File: //opt/.wp-cli/packages/vendor/wp-cli/extension-command/src/Plugin_Command.php
<?php
use WP_CLI\ParsePluginNameInput;
use WP_CLI\Utils;
use WP_CLI\WpOrgApi;
use function WP_CLI\Utils\normalize_path;
/**
* Manages plugins, including installs, activations, and updates.
*
* See the WordPress [Plugin Handbook](https://developer.wordpress.org/plugins/) developer resource for more information on plugins.
*
* ## EXAMPLES
*
* # Activate plugin
* $ wp plugin activate hello
* Plugin 'hello' activated.
* Success: Activated 1 of 1 plugins.
*
* # Deactivate plugin
* $ wp plugin deactivate hello
* Plugin 'hello' deactivated.
* Success: Deactivated 1 of 1 plugins.
*
* # Delete plugin
* $ wp plugin delete hello
* Deleted 'hello' plugin.
* Success: Deleted 1 of 1 plugins.
*
* # Install the latest version from wordpress.org and activate
* $ wp plugin install bbpress --activate
* Installing bbPress (2.5.9)
* Downloading install package from https://downloads.wordpress.org/plugin/bbpress.2.5.9.zip...
* Using cached file '/home/vagrant/.wp-cli/cache/plugin/bbpress-2.5.9.zip'...
* Unpacking the package...
* Installing the plugin...
* Plugin installed successfully.
* Activating 'bbpress'...
* Plugin 'bbpress' activated.
* Success: Installed 1 of 1 plugins.
*
* @package wp-cli
*/
class Plugin_Command extends \WP_CLI\CommandWithUpgrade {
use ParsePluginNameInput;
protected $item_type = 'plugin';
protected $upgrade_refresh = 'wp_update_plugins';
protected $upgrade_transient = 'update_plugins';
protected $check_wporg = [
'status' => false,
'last_updated' => false,
];
protected $check_headers = [
'tested_up_to' => false,
];
protected $obj_fields = array(
'name',
'status',
'update',
'version',
'update_version',
'auto_update',
);
/**
* Plugin fetcher instance.
*
* @var \WP_CLI\Fetchers\Plugin
*/
protected $fetcher;
public function __construct() {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
parent::__construct();
$this->fetcher = new WP_CLI\Fetchers\Plugin();
}
protected function get_upgrader_class( $force ) {
return $force ? '\\WP_CLI\\DestructivePluginUpgrader' : 'Plugin_Upgrader';
}
/**
* Reveals the status of one or all plugins.
*
* ## OPTIONS
*
* [<plugin>]
* : A particular plugin to show the status for.
*
* ## EXAMPLES
*
* # Displays status of all plugins
* $ wp plugin status
* 5 installed plugins:
* I akismet 3.1.11
* I easy-digital-downloads 2.5.16
* A theme-check 20160523.1
* I wen-logo-slider 2.0.3
* M ns-pack 1.0.0
* Legend: I = Inactive, A = Active, M = Must Use
*
* # Displays status of a plugin
* $ wp plugin status theme-check
* Plugin theme-check details:
* Name: Theme Check
* Status: Active
* Version: 20160523.1
* Author: Otto42, pross
* Description: A simple and easy way to test your theme for all the latest WordPress standards and practices. A great theme development tool!
*/
public function status( $args ) {
parent::status( $args );
}
/**
* Searches the WordPress.org plugin directory.
*
* Displays plugins in the WordPress.org plugin directory matching a given
* search query.
*
* ## OPTIONS
*
* <search>
* : The string to search for.
*
* [--page=<page>]
* : Optional page to display.
* ---
* default: 1
* ---
*
* [--per-page=<per-page>]
* : Optional number of results to display.
* ---
* default: 10
* ---
*
* [--field=<field>]
* : Prints the value of a single field for each plugin.
*
* [--fields=<fields>]
* : Ask for specific fields from the API. Defaults to name,slug,author_profile,rating. Acceptable values:
*
* **name**: Plugin Name
* **slug**: Plugin Slug
* **version**: Current Version Number
* **author**: Plugin Author
* **author_profile**: Plugin Author Profile
* **contributors**: Plugin Contributors
* **requires**: Plugin Minimum Requirements
* **tested**: Plugin Tested Up To
* **compatibility**: Plugin Compatible With
* **rating**: Plugin Rating in Percent and Total Number
* **ratings**: Plugin Ratings for each star (1-5)
* **num_ratings**: Number of Plugin Ratings
* **homepage**: Plugin Author's Homepage
* **description**: Plugin's Description
* **short_description**: Plugin's Short Description
* **sections**: Plugin Readme Sections: description, installation, FAQ, screenshots, other notes, and changelog
* **downloaded**: Plugin Download Count
* **last_updated**: Plugin's Last Update
* **added**: Plugin's Date Added to wordpress.org Repository
* **tags**: Plugin's Tags
* **versions**: Plugin's Available Versions with D/L Link
* **donate_link**: Plugin's Donation Link
* **banners**: Plugin's Banner Image Link
* **icons**: Plugin's Icon Image Link
* **active_installs**: Plugin's Number of Active Installs
* **contributors**: Plugin's List of Contributors
* **url**: Plugin's URL on wordpress.org
*
* [--format=<format>]
* : Render output in a particular format.
* ---
* default: table
* options:
* - table
* - csv
* - count
* - json
* - yaml
* ---
*
* ## EXAMPLES
*
* $ wp plugin search dsgnwrks --per-page=20 --format=json
* Success: Showing 3 of 3 plugins.
* [{"name":"DsgnWrks Instagram Importer Debug","slug":"dsgnwrks-instagram-importer-debug","rating":0},{"name":"DsgnWrks Instagram Importer","slug":"dsgnwrks-instagram-importer","rating":84},{"name":"DsgnWrks Twitter Importer","slug":"dsgnwrks-twitter-importer","rating":80}]
*
* $ wp plugin search dsgnwrks --fields=name,version,slug,rating,num_ratings
* Success: Showing 3 of 3 plugins.
* +-----------------------------------+---------+-----------------------------------+--------+-------------+
* | name | version | slug | rating | num_ratings |
* +-----------------------------------+---------+-----------------------------------+--------+-------------+
* | DsgnWrks Instagram Importer Debug | 0.1.6 | dsgnwrks-instagram-importer-debug | 0 | 0 |
* | DsgnWrks Instagram Importer | 1.3.7 | dsgnwrks-instagram-importer | 84 | 23 |
* | DsgnWrks Twitter Importer | 1.1.1 | dsgnwrks-twitter-importer | 80 | 1 |
* +-----------------------------------+---------+-----------------------------------+--------+-------------+
*/
public function search( $args, $assoc_args ) {
parent::_search( $args, $assoc_args );
}
protected function status_single( $args ) {
$plugin = $this->fetcher->get_check( $args[0] );
$file = $plugin->file;
$details = $this->get_details( $file );
$status = $this->format_status( $this->get_status( $file ), 'long' );
$version = $details['Version'];
if ( $this->has_update( $file ) ) {
$version .= ' (%gUpdate available%n)';
}
echo WP_CLI::colorize(
Utils\mustache_render(
self::get_template_path( 'plugin-status.mustache' ),
[
'slug' => Utils\get_plugin_name( $file ),
'status' => $status,
'version' => $version,
'name' => $details['Name'],
'author' => $details['Author'],
'description' => $details['Description'],
]
)
);
}
protected function get_all_items() {
$items = $this->get_item_list();
foreach ( get_mu_plugins() as $file => $mu_plugin ) {
$mu_version = '';
if ( ! empty( $mu_plugin['Version'] ) ) {
$mu_version = $mu_plugin['Version'];
}
$mu_title = '';
if ( ! empty( $mu_plugin['Name'] ) ) {
$mu_title = $mu_plugin['Title'];
}
$mu_description = '';
if ( ! empty( $mu_plugin['Description'] ) ) {
$mu_description = $mu_plugin['Description'];
}
$mu_name = Utils\get_plugin_name( $file );
$wporg_info = $this->get_wporg_data( $mu_name );
$items[ $file ] = array(
'name' => $mu_name,
'status' => 'must-use',
'update' => false,
'update_version' => null,
'update_package' => null,
'version' => $mu_version,
'update_id' => '',
'title' => $mu_title,
'description' => $mu_description,
'file' => $file,
'auto_update' => false,
'tested_up_to' => '',
'requires' => '',
'requires_php' => '',
'wporg_status' => $wporg_info['status'],
'wporg_last_updated' => $wporg_info['last_updated'],
);
}
$raw_items = get_dropins();
$raw_data = _get_dropins();
foreach ( $raw_items as $name => $item_data ) {
$description = ! empty( $raw_data[ $name ][0] ) ? $raw_data[ $name ][0] : '';
$items[ $name ] = [
'name' => $name,
'title' => $item_data['Title'],
'description' => $description,
'status' => 'dropin',
'update' => false,
'update_version' => null,
'update_package' => null,
'update_id' => '',
'file' => $name,
'auto_update' => false,
'author' => $item_data['Author'],
'tested_up_to' => '',
'requires' => '',
'requires_php' => '',
'wporg_status' => '',
'wporg_last_updated' => '',
];
}
return $items;
}
/**
* Activates one or more plugins.
*
* ## OPTIONS
*
* [<plugin>...]
* : One or more plugins to activate.
*
* [--all]
* : If set, all plugins will be activated.
*
* [--exclude=<name>]
* : Comma separated list of plugin slugs to be excluded from activation.
*
* [--network]
* : If set, the plugin will be activated for the entire multisite network.
*
* ## EXAMPLES
*
* # Activate plugin
* $ wp plugin activate hello
* Plugin 'hello' activated.
* Success: Activated 1 of 1 plugins.
*
* # Activate plugin in entire multisite network
* $ wp plugin activate hello --network
* Plugin 'hello' network activated.
* Success: Network activated 1 of 1 plugins.
*
* # Activate plugins that were recently active.
* $ wp plugin activate $(wp plugin list --recently-active --field=name)
* Plugin 'bbpress' activated.
* Plugin 'buddypress' activated.
* Success: Activated 2 of 2 plugins.
*
* # Activate plugins that were recently active on a multisite.
* $ wp plugin activate $(wp plugin list --recently-active --field=name) --network
* Plugin 'bbpress' network activated.
* Plugin 'buddypress' network activated.
* Success: Activated 2 of 2 plugins.
*/
public function activate( $args, $assoc_args = array() ) {
$network_wide = Utils\get_flag_value( $assoc_args, 'network', false );
$all = Utils\get_flag_value( $assoc_args, 'all', false );
$all_exclude = Utils\get_flag_value( $assoc_args, 'exclude' );
$args = $this->check_optional_args_and_all( $args, $all, 'activate', $all_exclude );
if ( ! $args ) {
return;
}
$successes = 0;
$errors = 0;
$plugins = $this->fetcher->get_many( $args );
if ( count( $plugins ) < count( $args ) ) {
$errors = count( $args ) - count( $plugins );
}
foreach ( $plugins as $plugin ) {
$status = $this->get_status( $plugin->file );
if ( $all && in_array( $status, [ 'active', 'active-network' ], true ) ) {
continue;
}
// Network-active is the highest level of activation status.
if ( 'active-network' === $status ) {
WP_CLI::warning( "Plugin '{$plugin->name}' is already network active." );
continue;
}
// Don't reactivate active plugins, but do let them become network-active.
if ( ! $network_wide && 'active' === $status ) {
WP_CLI::warning( "Plugin '{$plugin->name}' is already active." );
continue;
}
// Plugins need to be deactivated before being network activated.
if ( $network_wide && 'active' === $status ) {
deactivate_plugins( $plugin->file, false, false );
}
$result = activate_plugin( $plugin->file, '', $network_wide );
if ( is_wp_error( $result ) ) {
$message = $result->get_error_message();
$message = preg_replace( '/<a\s[^>]+>.*<\/a>/im', '', $message );
$message = wp_strip_all_tags( $message );
$message = str_replace( 'Error: ', '', $message );
WP_CLI::warning( "Failed to activate plugin. {$message}" );
++$errors;
} else {
$this->active_output( $plugin->name, $plugin->file, $network_wide, 'activate' );
++$successes;
}
}
if ( ! $this->chained_command ) {
$verb = $network_wide ? 'network activate' : 'activate';
Utils\report_batch_operation_results( 'plugin', $verb, count( $args ), $successes, $errors );
}
}
/**
* Deactivates one or more plugins.
*
* ## OPTIONS
*
* [<plugin>...]
* : One or more plugins to deactivate.
*
* [--uninstall]
* : Uninstall the plugin after deactivation.
*
* [--all]
* : If set, all plugins will be deactivated.
*
* [--exclude=<name>]
* : Comma separated list of plugin slugs that should be excluded from deactivation.
*
* [--network]
* : If set, the plugin will be deactivated for the entire multisite network.
*
* ## EXAMPLES
*
* # Deactivate plugin
* $ wp plugin deactivate hello
* Plugin 'hello' deactivated.
* Success: Deactivated 1 of 1 plugins.
*
* # Deactivate all plugins with exclusion
* $ wp plugin deactivate --all --exclude=hello,wordpress-seo
* Plugin 'contact-form-7' deactivated.
* Plugin 'ninja-forms' deactivated.
* Success: Deactivated 2 of 2 plugins.
*/
public function deactivate( $args, $assoc_args = array() ) {
$network_wide = Utils\get_flag_value( $assoc_args, 'network' );
$disable_all = Utils\get_flag_value( $assoc_args, 'all' );
$disable_all_exclude = Utils\get_flag_value( $assoc_args, 'exclude' );
$args = $this->check_optional_args_and_all( $args, $disable_all, 'deactivate', $disable_all_exclude );
if ( ! $args ) {
return;
}
$successes = 0;
$errors = 0;
$plugins = $this->fetcher->get_many( $args );
if ( count( $plugins ) < count( $args ) ) {
$errors = count( $args ) - count( $plugins );
}
foreach ( $plugins as $plugin ) {
$status = $this->get_status( $plugin->file );
if ( $disable_all && ! in_array( $status, [ 'active', 'active-network' ], true ) ) {
continue;
}
// Network active plugins must be explicitly deactivated.
if ( ! $network_wide && 'active-network' === $status ) {
WP_CLI::warning( "Plugin '{$plugin->name}' is network active and must be deactivated with --network flag." );
++$errors;
continue;
}
if ( ! in_array( $status, [ 'active', 'active-network' ], true ) ) {
WP_CLI::warning( "Plugin '{$plugin->name}' isn't active." );
continue;
}
deactivate_plugins( $plugin->file, false, $network_wide );
if ( ! is_network_admin() ) {
update_option(
'recently_activated',
array( $plugin->file => time() ) + (array) get_option( 'recently_activated' )
);
} else {
update_site_option(
'recently_activated',
array( $plugin->file => time() ) + (array) get_site_option( 'recently_activated' )
);
}
$this->active_output( $plugin->name, $plugin->file, $network_wide, 'deactivate' );
++$successes;
if ( Utils\get_flag_value( $assoc_args, 'uninstall' ) ) {
WP_CLI::log( "Uninstalling '{$plugin->name}'..." );
$this->chained_command = true;
$this->uninstall( array( $plugin->name ) );
$this->chained_command = false;
}
}
if ( ! $this->chained_command ) {
$verb = $network_wide ? 'network deactivate' : 'deactivate';
Utils\report_batch_operation_results( 'plugin', $verb, count( $args ), $successes, $errors );
}
}
/**
* Toggles a plugin's activation state.
*
* If the plugin is active, then it will be deactivated. If the plugin is
* inactive, then it will be activated.
*
* ## OPTIONS
*
* <plugin>...
* : One or more plugins to toggle.
*
* [--network]
* : If set, the plugin will be toggled for the entire multisite network.
*
* ## EXAMPLES
*
* # Akismet is currently activated
* $ wp plugin toggle akismet
* Plugin 'akismet' deactivated.
* Success: Toggled 1 of 1 plugins.
*
* # Akismet is currently deactivated
* $ wp plugin toggle akismet
* Plugin 'akismet' activated.
* Success: Toggled 1 of 1 plugins.
*/
public function toggle( $args, $assoc_args = array() ) {
$network_wide = Utils\get_flag_value( $assoc_args, 'network' );
$successes = 0;
$errors = 0;
$plugins = $this->fetcher->get_many( $args );
if ( count( $plugins ) < count( $args ) ) {
$errors = count( $args ) - count( $plugins );
}
$this->chained_command = true;
foreach ( $plugins as $plugin ) {
if ( $this->check_active( $plugin->file, $network_wide ) ) {
$this->deactivate( array( $plugin->name ), $assoc_args );
} else {
$this->activate( array( $plugin->name ), $assoc_args );
}
++$successes;
}
$this->chained_command = false;
Utils\report_batch_operation_results( 'plugin', 'toggle', count( $args ), $successes, $errors );
}
/**
* Gets the path to a plugin or to the plugin directory.
*
* ## OPTIONS
*
* [<plugin>]
* : The plugin to get the path to. If not set, will return the path to the
* plugins directory.
*
* [--dir]
* : If set, get the path to the closest parent directory, instead of the
* plugin file.
*
* ## EXAMPLES
*
* $ cd $(wp plugin path) && pwd
* /var/www/wordpress/wp-content/plugins
*/
public function path( $args, $assoc_args ) {
$path = untrailingslashit( WP_PLUGIN_DIR );
if ( ! empty( $args ) ) {
$plugin = $this->fetcher->get_check( $args[0] );
$path .= '/' . $plugin->file;
if ( Utils\get_flag_value( $assoc_args, 'dir' ) ) {
$path = dirname( $path );
}
}
WP_CLI::line( $path );
}
protected function install_from_repo( $slug, $assoc_args ) {
global $wp_version;
// Extract the major WordPress version (e.g., "6.3") from the full version string
list($wp_core_version) = explode( '-', $wp_version );
$wp_core_version = implode( '.', array_slice( explode( '.', $wp_core_version ), 0, 2 ) );
$api = plugins_api( 'plugin_information', array( 'slug' => $slug ) );
if ( is_wp_error( $api ) ) {
return $api;
}
if ( isset( $assoc_args['version'] ) ) {
self::alter_api_response( $api, $assoc_args['version'] );
} elseif ( ! Utils\get_flag_value( $assoc_args, 'ignore-requirements', false ) ) {
$requires_php = isset( $api->requires_php ) ? $api->requires_php : null;
$requires_wp = isset( $api->requires ) ? $api->requires : null;
$compatible_php = empty( $requires_php ) || version_compare( PHP_VERSION, $requires_php, '>=' );
$compatible_wp = empty( $requires_wp ) || version_compare( $wp_core_version, $requires_wp, '>=' );
if ( ! $compatible_wp ) {
return new WP_Error( 'requirements_not_met', "This plugin does not work with your version of WordPress. Minimum WordPress requirement is $requires_wp" );
}
if ( ! $compatible_php ) {
return new WP_Error( 'requirements_not_met', "This plugin does not work with your version of PHP. Minimum PHP required is $compatible_php" );
}
}
$status = install_plugin_install_status( $api );
if ( ! Utils\get_flag_value( $assoc_args, 'force' ) && 'install' !== $status['status'] ) {
// We know this will fail, so avoid a needless download of the package.
return new WP_Error( 'already_installed', 'Plugin already installed.' );
}
WP_CLI::log( sprintf( 'Installing %s (%s)', html_entity_decode( $api->name, ENT_QUOTES ), $api->version ) );
if ( Utils\get_flag_value( $assoc_args, 'version' ) !== 'dev' ) {
WP_CLI::get_http_cache_manager()->whitelist_package( $api->download_link, $this->item_type, $api->slug, $api->version );
}
// Ignore failures on accessing SSL "https://api.wordpress.org/plugins/update-check/1.1/" in `wp_update_plugins()` which seem to occur intermittently.
set_error_handler( array( __CLASS__, 'error_handler' ), E_USER_WARNING | E_USER_NOTICE );
$result = $this->get_upgrader( $assoc_args )->install( $api->download_link );
restore_error_handler();
return $result;
}
/**
* Updates one or more plugins.
*
* ## OPTIONS
*
* [<plugin>...]
* : One or more plugins to update.
*
* [--all]
* : If set, all plugins that have updates will be updated.
*
* [--exclude=<name>]
* : Comma separated list of plugin names that should be excluded from updating.
*
* [--minor]
* : Only perform updates for minor releases (e.g. from 1.3 to 1.4 instead of 2.0)
*
* [--patch]
* : Only perform updates for patch releases (e.g. from 1.3 to 1.3.3 instead of 1.4)
*
* [--format=<format>]
* : Render output in a particular format.
* ---
* default: table
* options:
* - table
* - csv
* - json
* - summary
* ---
*
* [--version=<version>]
* : If set, the plugin will be updated to the specified version.
*
* [--dry-run]
* : Preview which plugins would be updated.
*
* [--insecure]
* : Retry downloads without certificate validation if TLS handshake fails. Note: This makes the request vulnerable to a MITM attack.
*
* ## EXAMPLES
*
* $ wp plugin update bbpress --version=dev
* Installing bbPress (Development Version)
* Downloading install package from https://downloads.wordpress.org/plugin/bbpress.zip...
* Unpacking the package...
* Installing the plugin...
* Removing the old version of the plugin...
* Plugin updated successfully.
* Success: Updated 1 of 2 plugins.
*
* $ wp plugin update --all
* Enabling Maintenance mode...
* Downloading update from https://downloads.wordpress.org/plugin/akismet.3.1.11.zip...
* Unpacking the update...
* Installing the latest version...
* Removing the old version of the plugin...
* Plugin updated successfully.
* Downloading update from https://downloads.wordpress.org/plugin/nginx-champuru.3.2.0.zip...
* Unpacking the update...
* Installing the latest version...
* Removing the old version of the plugin...
* Plugin updated successfully.
* Disabling Maintenance mode...
* +------------------------+-------------+-------------+---------+
* | name | old_version | new_version | status |
* +------------------------+-------------+-------------+---------+
* | akismet | 3.1.3 | 3.1.11 | Updated |
* | nginx-cache-controller | 3.1.1 | 3.2.0 | Updated |
* +------------------------+-------------+-------------+---------+
* Success: Updated 2 of 2 plugins.
*
* $ wp plugin update --all --exclude=akismet
* Enabling Maintenance mode...
* Downloading update from https://downloads.wordpress.org/plugin/nginx-champuru.3.2.0.zip...
* Unpacking the update...
* Installing the latest version...
* Removing the old version of the plugin...
* Plugin updated successfully.
* Disabling Maintenance mode...
* +------------------------+-------------+-------------+---------+
* | name | old_version | new_version | status |
* +------------------------+-------------+-------------+---------+
* | nginx-cache-controller | 3.1.1 | 3.2.0 | Updated |
* +------------------------+-------------+-------------+---------+
*
* @alias upgrade
*/
public function update( $args, $assoc_args ) {
$all = Utils\get_flag_value( $assoc_args, 'all', false );
$args = $this->check_optional_args_and_all( $args, $all );
if ( ! $args ) {
return;
}
if ( isset( $assoc_args['version'] ) ) {
foreach ( $this->fetcher->get_many( $args ) as $plugin ) {
$assoc_args['force'] = 1;
$this->install( array( $plugin->name ), $assoc_args );
}
} else {
parent::update_many( $args, $assoc_args );
}
}
protected function get_item_list() {
global $wp_version;
$items = [];
$duplicate_names = [];
$auto_updates = get_site_option( Plugin_AutoUpdates_Command::SITE_OPTION );
if ( ! is_array( $auto_updates ) ) {
$auto_updates = [];
}
$recently_active = is_network_admin() ? get_site_option( 'recently_activated' ) : get_option( 'recently_activated' );
if ( false === $recently_active ) {
$recently_active = [];
}
foreach ( $this->get_all_plugins() as $file => $details ) {
$all_update_info = $this->get_update_info();
$update_info = ( isset( $all_update_info->response[ $file ] ) && null !== $all_update_info->response[ $file ] ) ? (array) $all_update_info->response[ $file ] : null;
$name = Utils\get_plugin_name( $file );
$wporg_info = $this->get_wporg_data( $name );
$plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $file, false, false );
if ( ! isset( $duplicate_names[ $name ] ) ) {
$duplicate_names[ $name ] = array();
}
$requires = isset( $update_info ) && isset( $update_info['requires'] ) ? $update_info['requires'] : null;
$requires_php = isset( $update_info ) && isset( $update_info['requires_php'] ) ? $update_info['requires_php'] : null;
// If an update has requires_php set, check to see if the local version of PHP meets that requirement
// The plugins update API already filters out plugins that don't meet WordPress requirements, but does not
// filter out plugins based on PHP requirements -- so we must do that here
$compatible_php = empty( $requires_php ) || version_compare( PHP_VERSION, $requires_php, '>=' );
if ( ! $compatible_php ) {
$update = 'unavailable';
$update_unavailable_reason = sprintf(
'This update requires PHP version %s, but the version installed is %s.',
$requires_php,
PHP_VERSION
);
} else {
$update = $update_info ? 'available' : 'none';
}
// requires and requires_php are only provided by the plugins update API in the case of an update available.
// For display consistency, get these values from the current plugin file if they aren't in this response
if ( null === $requires ) {
$requires = ! empty( $plugin_data['RequiresWP'] ) ? $plugin_data['RequiresWP'] : '';
}
if ( null === $requires_php ) {
$requires_php = ! empty( $plugin_data['RequiresPHP'] ) ? $plugin_data['RequiresPHP'] : '';
}
$duplicate_names[ $name ][] = $file;
$items[ $file ] = [
'name' => $name,
'status' => $this->get_status( $file ),
'update' => $update,
'update_version' => isset( $update_info ) && isset( $update_info['new_version'] ) ? $update_info['new_version'] : null,
'update_package' => isset( $update_info ) && isset( $update_info['package'] ) ? $update_info['package'] : null,
'version' => $details['Version'],
'update_id' => $file,
'title' => $details['Name'],
'description' => wordwrap( $details['Description'] ),
'file' => $file,
'auto_update' => in_array( $file, $auto_updates, true ),
'author' => $details['Author'],
'tested_up_to' => '',
'requires' => $requires,
'requires_php' => $requires_php,
'wporg_status' => $wporg_info['status'],
'wporg_last_updated' => $wporg_info['last_updated'],
'recently_active' => in_array( $file, array_keys( $recently_active ), true ),
'update_unavailable_reason' => isset( $update_unavailable_reason ) ? $update_unavailable_reason : '',
];
if ( $this->check_headers['tested_up_to'] ) {
$plugin_readme = normalize_path( dirname( WP_PLUGIN_DIR . '/' . $file ) . '/readme.txt' );
if ( file_exists( $plugin_readme ) && is_readable( $plugin_readme ) ) {
$readme_obj = new SplFileObject( $plugin_readme );
$readme_obj->setFlags( SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY );
$readme_line = 0;
// Reading the whole file can exhaust the memory, so only read the first 100 lines of the file,
// as the "Tested up to" header should be near the top.
while ( $readme_line < 100 && ! $readme_obj->eof() ) {
$line = $readme_obj->fgets();
// Similar to WP.org, it matches for both "Tested up to" and "Tested" header in the readme file.
preg_match( '/^tested(:| up to:) (.*)$/i', strtolower( $line ), $matches );
if ( isset( $matches[2] ) && ! empty( $matches[2] ) ) {
$items[ $file ]['tested_up_to'] = $matches[2];
break;
}
++$readme_line;
}
$file_obj = null;
}
}
if ( null === $update_info ) {
// Get info for all plugins that don't have an update.
$plugin_update_info = isset( $all_update_info->no_update[ $file ] ) ? $all_update_info->no_update[ $file ] : null;
// Check if local version is newer than what is listed upstream.
if ( null !== $plugin_update_info && version_compare( $details['Version'], $plugin_update_info->new_version, '>' ) ) {
$items[ $file ]['update'] = static::INVALID_VERSION_MESSAGE;
$items[ $file ]['requires'] = isset( $plugin_update_info->requires ) ? $plugin_update_info->requires : null;
$items[ $file ]['requires_php'] = isset( $plugin_update_info->requires_php ) ? $plugin_update_info->requires_php : null;
}
// If there is a plugin in no_update with a newer version than the local copy, it is because the plugins update api
// has already filtered it because the local WordPress version is too low
if ( null !== $plugin_update_info && version_compare( $details['Version'], $plugin_update_info->new_version, '<' ) ) {
$items[ $file ]['update'] = 'unavailable';
$items[ $file ]['update_version'] = $plugin_update_info->new_version;
$items[ $file ]['requires'] = isset( $plugin_update_info->requires ) ? $plugin_update_info->requires : null;
$items[ $file ]['requires_php'] = isset( $plugin_update_info->requires_php ) ? $plugin_update_info->requires_php : null;
$reason = "This update requires WordPress version $plugin_update_info->requires, but the version installed is $wp_version.";
$items[ $file ]['update_unavailable_reason'] = $reason;
}
}
}
foreach ( $duplicate_names as $name => $files ) {
if ( count( $files ) <= 1 ) {
continue;
}
foreach ( $files as $file ) {
$items[ $file ]['name'] = str_replace( '.' . pathinfo( $file, PATHINFO_EXTENSION ), '', $file );
}
}
return $items;
}
/**
* Get the wordpress.org status of a plugin.
*
* @param string $plugin_name The plugin slug.
*
* @return string The status of the plugin, includes the last update date.
*/
protected function get_wporg_data( $plugin_name ) {
$data = [
'status' => '',
'last_updated' => '',
];
if ( ! $this->check_wporg['status'] && ! $this->check_wporg['last_updated'] ) {
return $data;
}
if ( $this->check_wporg ) {
try {
$plugin_data = ( new WpOrgApi() )->get_plugin_info( $plugin_name );
} catch ( Exception $e ) {
// Request failed. The plugin is not (active) on .org.
$plugin_data = false;
}
if ( $plugin_data ) {
$data['status'] = 'active';
if ( ! $this->check_wporg['last_updated'] ) {
return $data; // The plugin is active on .org, but we don't need the date.
}
}
// Just because the plugin is not in the api, does not mean it was never on .org.
}
$request = wp_remote_get( "https://plugins.trac.wordpress.org/log/{$plugin_name}/?limit=1&mode=stop_on_copy&format=rss" );
$response_code = wp_remote_retrieve_response_code( $request );
if ( 404 === $response_code ) {
return $data; // This plugin was never on .org, there is no date to check.
}
if ( 'active' !== $data['status'] ) {
$data['status'] = 'closed'; // This plugin was on .org at some point, but not anymore.
}
if ( ! class_exists( 'SimpleXMLElement' ) ) {
WP_CLI::error( "The PHP extension 'SimpleXMLElement' is not available but is required for XML-formatted output." );
}
// Check the last update date.
$r_body = wp_remote_retrieve_body( $request );
if ( strpos( $r_body, 'pubDate' ) !== false ) {
// Very raw check, not validating the format or anything else.
$xml = simplexml_load_string( $r_body );
$xml_pub_date = $xml->xpath( '//pubDate' );
if ( $xml_pub_date ) {
$data['last_updated'] = wp_date( 'Y-m-d', (string) strtotime( $xml_pub_date[0] ) );
}
}
return $data;
}
protected function filter_item_list( $items, $args ) {
$basenames = wp_list_pluck( $this->fetcher->get_many( $args ), 'file' );
return Utils\pick_fields( $items, $basenames );
}
/**
* Installs one or more plugins.
*
* ## OPTIONS
*
* <plugin|zip|url>...
* : One or more plugins to install. Accepts a plugin slug, the path to a local zip file, or a URL to a remote zip file.
*
* [--version=<version>]
* : If set, get that particular version from wordpress.org, instead of the
* stable version.
*
* [--force]
* : If set, the command will overwrite any installed version of the plugin, without prompting
* for confirmation.
*
* [--ignore-requirements]
* :If set, the command will install the plugin while ignoring any WordPress or PHP version requirements
* specified by the plugin authors.
*
* [--activate]
* : If set, the plugin will be activated immediately after install.
*
* [--activate-network]
* : If set, the plugin will be network activated immediately after install
*
* [--insecure]
* : Retry downloads without certificate validation if TLS handshake fails. Note: This makes the request vulnerable to a MITM attack.
*
* ## EXAMPLES
*
* # Install the latest version from wordpress.org and activate
* $ wp plugin install bbpress --activate
* Installing bbPress (2.5.9)
* Downloading install package from https://downloads.wordpress.org/plugin/bbpress.2.5.9.zip...
* Using cached file '/home/vagrant/.wp-cli/cache/plugin/bbpress-2.5.9.zip'...
* Unpacking the package...
* Installing the plugin...
* Plugin installed successfully.
* Activating 'bbpress'...
* Plugin 'bbpress' activated.
* Success: Installed 1 of 1 plugins.
*
* # Install the development version from wordpress.org
* $ wp plugin install bbpress --version=dev
* Installing bbPress (Development Version)
* Downloading install package from https://downloads.wordpress.org/plugin/bbpress.zip...
* Unpacking the package...
* Installing the plugin...
* Plugin installed successfully.
* Success: Installed 1 of 1 plugins.
*
* # Install from a local zip file
* $ wp plugin install ../my-plugin.zip
* Unpacking the package...
* Installing the plugin...
* Plugin installed successfully.
* Success: Installed 1 of 1 plugins.
*
* # Install from a remote zip file
* $ wp plugin install http://s3.amazonaws.com/bucketname/my-plugin.zip?AWSAccessKeyId=123&Expires=456&Signature=abcdef
* Downloading install package from http://s3.amazonaws.com/bucketname/my-plugin.zip?AWSAccessKeyId=123&Expires=456&Signature=abcdef
* Unpacking the package...
* Installing the plugin...
* Plugin installed successfully.
* Success: Installed 1 of 1 plugins.
*
* # Update from a remote zip file
* $ wp plugin install https://github.com/envato/wp-envato-market/archive/master.zip --force
* Downloading install package from https://github.com/envato/wp-envato-market/archive/master.zip
* Unpacking the package...
* Installing the plugin...
* Renamed Github-based project from 'wp-envato-market-master' to 'wp-envato-market'.
* Plugin updated successfully
* Success: Installed 1 of 1 plugins.
*
* # Forcefully re-install all installed plugins
* $ wp plugin install $(wp plugin list --field=name) --force
* Installing Akismet (3.1.11)
* Downloading install package from https://downloads.wordpress.org/plugin/akismet.3.1.11.zip...
* Unpacking the package...
* Installing the plugin...
* Removing the old version of the plugin...
* Plugin updated successfully
* Success: Installed 1 of 1 plugins.
*/
public function install( $args, $assoc_args ) {
if ( ! is_dir( WP_PLUGIN_DIR ) ) {
wp_mkdir_p( WP_PLUGIN_DIR );
}
parent::install( $args, $assoc_args );
}
/**
* Gets details about an installed plugin.
*
* ## OPTIONS
*
* <plugin>
* : The plugin to get.
*
* [--field=<field>]
* : Instead of returning the whole plugin, returns the value of a single field.
*
* [--fields=<fields>]
* : Limit the output to specific fields. Defaults to all fields.
*
* [--format=<format>]
* : Render output in a particular format.
* ---
* default: table
* options:
* - table
* - csv
* - json
* - yaml
* ---
*
* ## AVAILABLE FIELDS
*
* These fields will be displayed by default for the plugin:
*
* * name
* * title
* * author
* * version
* * description
* * status
*
* These fields are optionally available:
*
* * requires_wp
* * requires_php
* * requires_plugins
*
* ## EXAMPLES
*
* # Get plugin details.
* $ wp plugin get bbpress --format=json
* {"name":"bbpress","title":"bbPress","author":"The bbPress Contributors","version":"2.6.9","description":"bbPress is forum software with a twist from the creators of WordPress.","status":"active"}
*/
public function get( $args, $assoc_args ) {
$default_fields = array(
'name',
'title',
'author',
'version',
'description',
'status',
);
$plugin = $this->fetcher->get_check( $args[0] );
$file = $plugin->file;
$plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $file, false, false );
$plugin_obj = (object) [
'name' => Utils\get_plugin_name( $file ),
'title' => $plugin_data['Name'],
'author' => $plugin_data['Author'],
'version' => $plugin_data['Version'],
'description' => wordwrap( $plugin_data['Description'] ),
'status' => $this->get_status( $file ),
'requires_wp' => ! empty( $plugin_data['RequiresWP'] ) ? $plugin_data['RequiresWP'] : '',
'requires_php' => ! empty( $plugin_data['RequiresPHP'] ) ? $plugin_data['RequiresPHP'] : '',
'requires_plugins' => ! empty( $plugin_data['RequiresPlugins'] ) ? $plugin_data['RequiresPlugins'] : '',
];
if ( empty( $assoc_args['fields'] ) ) {
$assoc_args['fields'] = $default_fields;
}
$formatter = $this->get_formatter( $assoc_args );
$formatter->display_item( $plugin_obj );
}
/**
* Uninstalls one or more plugins.
*
* ## OPTIONS
*
* [<plugin>...]
* : One or more plugins to uninstall.
*
* [--deactivate]
* : Deactivate the plugin before uninstalling. Default behavior is to warn and skip if the plugin is active.
*
* [--skip-delete]
* : If set, the plugin files will not be deleted. Only the uninstall procedure
* will be run.
*
* [--all]
* : If set, all plugins will be uninstalled.
*
* [--exclude=<name>]
* : Comma separated list of plugin slugs to be excluded from uninstall.
*
* ## EXAMPLES
*
* $ wp plugin uninstall hello
* Uninstalled and deleted 'hello' plugin.
* Success: Uninstalled 1 of 1 plugins.
*
* # Uninstall all plugins excluding specified ones
* $ wp plugin uninstall --all --exclude=hello-dolly,jetpack
* Uninstalled and deleted 'akismet' plugin.
* Uninstalled and deleted 'tinymce-templates' plugin.
* Success: Uninstalled 2 of 2 plugins.
*/
public function uninstall( $args, $assoc_args = array() ) {
$all = Utils\get_flag_value( $assoc_args, 'all', false );
$all_exclude = Utils\get_flag_value( $assoc_args, 'exclude', false );
// Check if plugin names or --all is passed.
$args = $this->check_optional_args_and_all( $args, $all, 'uninstall', $all_exclude );
if ( ! $args ) {
return;
}
$successes = 0;
$errors = 0;
$delete_errors = array();
$deleted_plugin_files = array();
$plugins = $this->fetcher->get_many( $args );
if ( count( $plugins ) < count( $args ) ) {
$errors = count( $args ) - count( $plugins );
}
foreach ( $plugins as $plugin ) {
if ( is_plugin_active( $plugin->file ) && ! WP_CLI\Utils\get_flag_value( $assoc_args, 'deactivate' ) ) {
WP_CLI::warning( "The '{$plugin->name}' plugin is active." );
++$errors;
continue;
}
if ( Utils\get_flag_value( $assoc_args, 'deactivate' ) ) {
WP_CLI::log( "Deactivating '{$plugin->name}'..." );
$this->chained_command = true;
$this->deactivate( array( $plugin->name ) );
$this->chained_command = false;
}
uninstall_plugin( $plugin->file );
$plugin_translations = wp_get_installed_translations( 'plugins' );
$plugin_slug = dirname( $plugin->file );
if ( 'hello.php' === $plugin->file ) {
$plugin_slug = 'hello-dolly';
}
// Remove language files, silently.
if ( '.' !== $plugin_slug && ! empty( $plugin_translations[ $plugin_slug ] ) ) {
$translations = $plugin_translations[ $plugin_slug ];
global $wp_filesystem;
require_once ABSPATH . '/wp-admin/includes/file.php';
WP_Filesystem();
foreach ( $translations as $translation => $data ) {
$wp_filesystem->delete( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.po' );
$wp_filesystem->delete( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.mo' );
$wp_filesystem->delete( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.l10n.php' );
$json_translation_files = glob( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '-*.json' );
if ( $json_translation_files ) {
array_map( array( $wp_filesystem, 'delete' ), $json_translation_files );
}
}
}
if ( ! Utils\get_flag_value( $assoc_args, 'skip-delete' ) ) {
if ( $this->delete_plugin( $plugin ) ) {
$deleted_plugin_files[] = $plugin->file;
WP_CLI::log( "Uninstalled and deleted '$plugin->name' plugin." );
} else {
$delete_errors[] = $plugin->file;
WP_CLI::log( "Ran uninstall procedure for '$plugin->name' plugin. Deletion of plugin files failed" );
++$errors;
continue;
}
} else {
WP_CLI::log( "Ran uninstall procedure for '$plugin->name' plugin without deleting." );
}
++$successes;
}
// Remove deleted plugins from the plugin updates list.
$current = get_site_transient( $this->upgrade_transient );
if ( $current ) {
// Don't remove the plugins that weren't deleted.
$deleted = array_diff( $deleted_plugin_files, $delete_errors );
foreach ( $deleted as $plugin_file ) {
unset( $current->response[ $plugin_file ] );
unset( $current->checked[ $plugin_file ] );
}
set_site_transient( $this->upgrade_transient, $current );
}
if ( ! $this->chained_command ) {
Utils\report_batch_operation_results( 'plugin', 'uninstall', count( $args ), $successes, $errors );
}
}
/**
* Checks if a given plugin is installed.
*
* Returns exit code 0 when installed, 1 when uninstalled.
*
* ## OPTIONS
*
* <plugin>
* : The plugin to check.
*
* ## EXAMPLES
*
* # Check whether plugin is installed; exit status 0 if installed, otherwise 1
* $ wp plugin is-installed hello
* $ echo $?
* 1
*
* @subcommand is-installed
*/
public function is_installed( $args, $assoc_args = array() ) {
if ( $this->fetcher->get( $args[0] ) ) {
WP_CLI::halt( 0 );
} else {
WP_CLI::halt( 1 );
}
}
/**
* Checks if a given plugin is active.
*
* Returns exit code 0 when active, 1 when not active.
*
* ## OPTIONS
*
* <plugin>
* : The plugin to check.
*
* [--network]
* : If set, check if plugin is network-activated.
*
* ## EXAMPLES
*
* # Check whether plugin is Active; exit status 0 if active, otherwise 1
* $ wp plugin is-active hello
* $ echo $?
* 1
*
* @subcommand is-active
*/
public function is_active( $args, $assoc_args = array() ) {
$network_wide = Utils\get_flag_value( $assoc_args, 'network' );
$plugin = $this->fetcher->get( $args[0] );
if ( ! $plugin ) {
WP_CLI::halt( 1 );
}
$this->check_active( $plugin->file, $network_wide ) ? WP_CLI::halt( 0 ) : WP_CLI::halt( 1 );
}
/**
* Deletes plugin files without deactivating or uninstalling.
*
* ## OPTIONS
*
* [<plugin>...]
* : One or more plugins to delete.
*
* [--all]
* : If set, all plugins will be deleted.
*
* [--exclude=<name>]
* : Comma separated list of plugin slugs to be excluded from deletion.
*
* ## EXAMPLES
*
* # Delete plugin
* $ wp plugin delete hello
* Deleted 'hello' plugin.
* Success: Deleted 1 of 1 plugins.
*
* # Delete inactive plugins
* $ wp plugin delete $(wp plugin list --status=inactive --field=name)
* Deleted 'tinymce-templates' plugin.
* Success: Deleted 1 of 1 plugins.
*
* # Delete all plugins excluding specified ones
* $ wp plugin delete --all --exclude=hello-dolly,jetpack
* Deleted 'akismet' plugin.
* Deleted 'tinymce-templates' plugin.
* Success: Deleted 2 of 2 plugins.
*/
public function delete( $args, $assoc_args = array() ) {
$all = Utils\get_flag_value( $assoc_args, 'all', false );
$all_exclude = Utils\get_flag_value( $assoc_args, 'exclude', false );
// Check if plugin names or --all is passed.
$args = $this->check_optional_args_and_all( $args, $all, 'delete', $all_exclude );
if ( ! $args ) {
return;
}
$successes = 0;
$errors = 0;
foreach ( $this->fetcher->get_many( $args ) as $plugin ) {
if ( $this->delete_plugin( $plugin ) ) {
WP_CLI::log( "Deleted '{$plugin->name}' plugin." );
++$successes;
} else {
WP_CLI::warning( "The '{$plugin->name}' plugin could not be deleted." );
++$errors;
}
}
if ( ! $this->chained_command ) {
Utils\report_batch_operation_results( 'plugin', 'delete', count( $args ), $successes, $errors );
}
}
/**
* Gets a list of plugins.
*
* Displays a list of the plugins installed on the site with activation
* status, whether or not there's an update available, etc.
*
* Use `--status=dropin` to list installed dropins (e.g. `object-cache.php`).
*
* ## OPTIONS
*
* [--<field>=<value>]
* : Filter results based on the value of a field.
*
* [--field=<field>]
* : Prints the value of a single field for each plugin.
*
* [--fields=<fields>]
* : Limit the output to specific object fields.
*
* [--format=<format>]
* : Render output in a particular format.
* ---
* default: table
* options:
* - table
* - csv
* - count
* - json
* - yaml
* ---
*
* [--status=<status>]
* : Filter the output by plugin status.
* ---
* options:
* - active
* - active-network
* - dropin
* - inactive
* - must-use
* ---
*
* [--skip-update-check]
* : If set, the plugin update check will be skipped.
*
* [--recently-active]
* : If set, only recently active plugins will be shown and the status filter will be ignored.
*
* ## AVAILABLE FIELDS
*
* These fields will be displayed by default for each plugin:
*
* * name
* * status
* * update
* * version
* * update_version
* * auto_update
*
* These fields are optionally available:
*
* * update_package
* * update_id
* * title
* * description
* * file
* * author
* * tested_up_to
* * requires
* * requires_php
* * wporg_status
* * wporg_last_updated
*
* ## EXAMPLES
*
* # List active plugins on the site.
* $ wp plugin list --status=active --format=json
* [{"name":"dynamic-hostname","status":"active","update":"none","version":"0.4.2","update_version":"","auto_update":"off"},{"name":"tinymce-templates","status":"active","update":"none","version":"4.8.1","update_version":"","auto_update":"off"},{"name":"wp-multibyte-patch","status":"active","update":"none","version":"2.9","update_version":"","auto_update":"off"},{"name":"wp-total-hacks","status":"active","update":"none","version":"4.7.2","update_version":"","auto_update":"off"}]
*
* # List plugins on each site in a network.
* $ wp site list --field=url | xargs -I % wp plugin list --url=%
* +---------+----------------+-----------+---------+-----------------+------------+
* | name | status | update | version | update_version | auto_update |
* +---------+----------------+-----------+---------+----------------+-------------+
* | akismet | active-network | none | 5.3.1 | | on |
* | hello | inactive | available | 1.6 | 1.7.2 | off |
* +---------+----------------+-----------+---------+----------------+-------------+
* +---------+----------------+-----------+---------+----------------+-------------+
* | name | status | update | version | update_version | auto_update |
* +---------+----------------+-----------+---------+----------------+-------------+
* | akismet | active-network | none | 5.3.1 | | on |
* | hello | inactive | available | 1.6 | 1.7.2 | off |
* +---------+----------------+-----------+---------+----------------+-------------+
*
* # Check whether plugins are still active on WordPress.org
* $ wp plugin list --fields=name,wporg_status,wporg_last_updated
* +--------------------+--------------+--------------------+
* | name | wporg_status | wporg_last_updated |
* +--------------------+--------------+--------------------+
* | akismet | active | 2023-12-11 |
* | user-switching | active | 2023-11-17 |
* | wordpress-importer | active | 2023-04-28 |
* | local | | |
* +--------------------+--------------+--------------------+
*
* # List recently active plugins on the site.
* $ wp plugin list --recently-active --field=name --format=json
* ["akismet","bbpress","buddypress"]
*
* @subcommand list
*/
public function list_( $_, $assoc_args ) {
$fields = Utils\get_flag_value( $assoc_args, 'fields' );
if ( ! empty( $fields ) ) {
$fields = explode( ',', $fields );
$this->check_wporg['status'] = in_array( 'wporg_status', $fields, true );
$this->check_wporg['last_updated'] = in_array( 'wporg_last_updated', $fields, true );
$this->check_headers['tested_up_to'] = in_array( 'tested_up_to', $fields, true );
}
$field = Utils\get_flag_value( $assoc_args, 'field' );
if ( 'wporg_status' === $field ) {
$this->check_wporg['status'] = true;
} elseif ( 'wporg_last_updated' === $field ) {
$this->check_wporg['last_updated'] = true;
}
$this->check_headers['tested_up_to'] = 'tested_up_to' === $field || $this->check_headers['tested_up_to'];
parent::_list( $_, $assoc_args );
}
/* PRIVATES */
private function check_active( $file, $network_wide ) {
$required = $network_wide ? 'active-network' : 'active';
return $required === $this->get_status( $file );
}
private function active_output( $name, $file, $network_wide, $action ) {
$network_wide = $network_wide || ( is_multisite() && is_network_only_plugin( $file ) );
$check = $this->check_active( $file, $network_wide );
if ( ( 'activate' === $action ) ? $check : ! $check ) {
if ( $network_wide ) {
WP_CLI::log( "Plugin '{$name}' network {$action}d." );
} else {
WP_CLI::log( "Plugin '{$name}' {$action}d." );
}
} else {
WP_CLI::warning( "Could not {$action} the '{$name}' plugin." );
}
}
protected function get_status( $file ) {
if ( is_plugin_active_for_network( $file ) ) {
return 'active-network';
}
if ( is_plugin_active( $file ) ) {
return 'active';
}
return 'inactive';
}
/**
* Gets the template path based on installation type.
*/
private static function get_template_path( $template ) {
$command_root = Utils\phar_safe_path( dirname( __DIR__ ) );
$template_path = "{$command_root}/templates/{$template}";
if ( ! file_exists( $template_path ) ) {
WP_CLI::error( "Couldn't find {$template}" );
}
return $template_path;
}
/**
* Gets the details of a plugin.
*
* @param object
* @return array
*/
private function get_details( $file ) {
$plugin_folder = get_plugins( '/' . plugin_basename( dirname( $file ) ) );
$plugin_file = Utils\basename( $file );
return $plugin_folder[ $plugin_file ];
}
/**
* Performs deletion of plugin files
*
* @param $plugin - Plugin fetcher object (name, file)
* @return bool - If plugin was deleted
*/
private function delete_plugin( $plugin ) {
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
do_action( 'delete_plugin', $plugin->file );
$plugin_dir = dirname( $plugin->file );
if ( '.' === $plugin_dir ) {
$plugin_dir = $plugin->file;
}
$path = path_join( WP_PLUGIN_DIR, $plugin_dir );
if ( Utils\is_windows() ) {
// Handles plugins that are not in own folders
// e.g. Hello Dolly -> plugins/hello.php
if ( is_file( $path ) ) {
$command = 'del /f /q ';
} else {
$command = 'rd /s /q ';
}
$path = str_replace( '/', '\\', $path );
} else {
$command = 'rm -rf ';
}
$result = ! WP_CLI::launch( $command . escapeshellarg( $path ), false );
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
do_action( 'deleted_plugin', $plugin->file, $result );
return $result;
}
}