* <list|activate|deactivate|toggle>
* : The slug of the module to perform an action on.
* : Allows overriding the output of the command when listing modules.
* wp jetpack module list --format=json
* wp jetpack module activate stats
* wp jetpack module deactivate stats
* wp jetpack module toggle stats
* wp jetpack module activate all
* wp jetpack module deactivate all
* @param array $args Positional args.
* @param array $assoc_args Named args.
public function module( $args, $assoc_args ) {
$action = isset( $args[0] ) ? $args[0] : 'list';
if ( isset( $args[1] ) ) {
if ( 'all' !== $module_slug && ! Jetpack::is_module( $module_slug ) ) {
/* translators: %s is a module slug like "stats" */
WP_CLI::error( sprintf( __( '%s is not a valid module.', 'jetpack' ), $module_slug ) );
if ( 'toggle' === $action ) {
$action = Jetpack::is_module_active( $module_slug )
if ( 'all' === $args[1] ) {
$action = ( 'deactivate' === $action )
} elseif ( 'list' !== $action ) {
WP_CLI::line( __( 'Please specify a valid module.', 'jetpack' ) );
$modules = Jetpack::get_available_modules();
foreach ( (array) $modules as $module_slug ) {
if ( 'vaultpress' === $module_slug ) {
'status' => Jetpack::is_module_active( $module_slug )
? __( 'Active', 'jetpack' )
: __( 'Inactive', 'jetpack' ),
WP_CLI\Utils\format_items( $assoc_args['format'], $modules_list, array( 'slug', 'status' ) );
$module = Jetpack::get_module( $module_slug );
Jetpack::log( 'activate', $module_slug );
if ( Jetpack::activate_module( $module_slug, false, false ) ) {
/* translators: %s is the name of a Jetpack module */
WP_CLI::success( sprintf( __( '%s has been activated.', 'jetpack' ), $module['name'] ) );
/* translators: %s is the name of a Jetpack module */
WP_CLI::error( sprintf( __( '%s could not be activated.', 'jetpack' ), $module['name'] ) );
$modules = Jetpack::get_available_modules();
Jetpack::update_active_modules( $modules );
WP_CLI::success( __( 'All modules activated!', 'jetpack' ) );
$module = Jetpack::get_module( $module_slug );
Jetpack::log( 'deactivate', $module_slug );
Jetpack::deactivate_module( $module_slug );
/* translators: %s is the name of a Jetpack module */
WP_CLI::success( sprintf( __( '%s has been deactivated.', 'jetpack' ), $module['name'] ) );
Jetpack::delete_active_modules();
WP_CLI::success( __( 'All modules deactivated!', 'jetpack' ) );
// Will never happen, should have been handled above and changed to activate or deactivate.
* Manage Protect Settings
* allow: Add an IP address to an always allow list. You can also read or clear the allow list.
* wp jetpack protect allow <ip address>
* wp jetpack protect allow list
* wp jetpack protect allow clear
* @synopsis <allow> [<ip|ip_low-ip_high|list|clear>]
* @param array $args Positional args.
public function protect( $args ) {
$action = isset( $args[0] ) ? $args[0] : 'prompt';
if ( ! in_array( $action, array( 'whitelist', 'allow' ), true ) ) { // Still allow "whitelist" for legacy support.
/* translators: %s is a command like "prompt" */
WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
// Check if module is active.
if ( ! Jetpack::is_module_active( __FUNCTION__ ) ) {
/* translators: %s is a module name */
WP_CLI::error( sprintf( _x( '%1$s is not active. You can activate it with "wp jetpack module activate %2$s"', '"wp jetpack module activate" is a command - do not translate', 'jetpack' ), __FUNCTION__, __FUNCTION__ ) );
if ( in_array( $action, array( 'allow', 'whitelist' ), true ) ) {
if ( isset( $args[1] ) ) {
$current_allow = get_site_option( 'jetpack_protect_whitelist', array() ); // @todo Update the option name.
// Build array of IPs that are already on the allowed list.
// Re-build manually instead of using jetpack_protect_format_allow_list() so we can easily get
// low & high range params for IP_Utils::ip_address_is_in_range().
foreach ( $current_allow as $allowed ) {
// Is it already on the allowed list?
if ( IP_Utils::ip_address_is_in_range( $new_ip, $allowed->range_low, $allowed->range_high ) ) {
/* translators: %s is an IP address */
WP_CLI::error( sprintf( __( '%s is already on the always allow list.', 'jetpack' ), $new_ip ) );
$allow[] = $allowed->range_low . ' - ' . $allowed->range_high;
} else { // Individual IPs.
// Check if the IP is already on the allow list (single IP only).
if ( $new_ip === $allowed->ip_address ) {
/* translators: %s is an IP address */
WP_CLI::error( sprintf( __( '%s is already on the always allow list.', 'jetpack' ), $new_ip ) );
$allow[] = $allowed->ip_address;
* Done here because it's easier to read the $allow array after it's been rebuilt.
if ( isset( $args[1] ) && 'list' === $args[1] ) {
if ( ! empty( $allow ) ) {
WP_CLI::success( __( 'Here are your always allowed IPs:', 'jetpack' ) );
foreach ( $allow as $ip ) {
WP_CLI::line( "\t" . str_pad( $ip, 24 ) );
WP_CLI::line( __( 'Always allow list is empty.', 'jetpack' ) );
* Clear the always allow list.
if ( isset( $args[1] ) && 'clear' === $args[1] ) {
if ( ! empty( $allow ) ) {
Brute_Force_Protection_Shared_Functions::save_allow_list( $allow ); // @todo Need to update function name in the Protect module.
WP_CLI::success( __( 'Cleared all IPs from the always allow list.', 'jetpack' ) );
WP_CLI::line( __( 'Always allow list is empty.', 'jetpack' ) );
// Append new IP to allow array.
array_push( $allow, $new_ip );
// Save allow list if there are no errors.
$result = Brute_Force_Protection_Shared_Functions::save_allow_list( $allow ); // @todo Need to update function name in the Protect module.
if ( is_wp_error( $result ) ) {
WP_CLI::error( $result );
/* translators: %s is an IP address */
WP_CLI::success( sprintf( __( '%s has been added to the always allowed list.', 'jetpack' ), $new_ip ) );
__( 'No command found.', 'jetpack' ) . "\n" .
__( 'Please enter the IP address you want to always allow.', 'jetpack' ) . "\n" .
_x( 'You can save a range of IPs {low_range}-{high_range}. No spaces allowed. (example: 1.1.1.1-2.2.2.2)', 'Instructions on how to add IP ranges - low_range/high_range should be translated.', 'jetpack' ) . "\n" .
_x( "You can also 'list' or 'clear' the always allowed list.", "'list' and 'clear' are commands and should not be translated", 'jetpack' ) . "\n"
* list : List all jetpack options and their values
* delete : Delete an option
* - can only delete options that are white listed.
* update : update an option
* - can only update option strings
* get : get the value of an option
* wp jetpack options list
* wp jetpack options get <option_name>
* wp jetpack options delete <option_name>
* wp jetpack options update <option_name> [<option_value>]
* @synopsis <list|get|delete|update> [<option_name>] [<option_value>]
* @param array $args Positional args.
public function options( $args ) {
$action = isset( $args[0] ) ? $args[0] : 'list';
$safe_to_modify = Jetpack_Options::get_options_for_reset();
// Is the option flagged as unsafe?
$flagged = ! in_array( $args[1], $safe_to_modify, true );
if ( ! in_array( $action, array( 'list', 'get', 'delete', 'update' ), true ) ) {
/* translators: %s is a command like "prompt" */
WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
if ( isset( $args[0] ) ) {
if ( 'get' === $args[0] && isset( $args[1] ) ) {
} elseif ( 'delete' === $args[0] && isset( $args[1] ) ) {
} elseif ( 'update' === $args[0] && isset( $args[1] ) ) {
// Bail if the option isn't found.
$option = isset( $args[1] ) ? Jetpack_Options::get_option( $args[1] ) : false;
if ( isset( $args[1] ) && ! $option && 'update' !== $args[0] ) {
WP_CLI::error( __( 'Option not found or is empty. Use "list" to list option names', 'jetpack' ) );
// Let's print_r the option if it's an array.
// Used in the 'get' and 'list' actions.
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
$option = is_array( $option ) ? print_r( $option, true ) : $option;
WP_CLI::success( "\t" . $option );
jetpack_cli_are_you_sure( $flagged );
Jetpack_Options::delete_option( $args[1] );
/* translators: %s is the option name */
WP_CLI::success( sprintf( __( 'Deleted option: %s', 'jetpack' ), $args[1] ) );
jetpack_cli_are_you_sure( $flagged );
// Updating arrays would get pretty tricky...
$value = Jetpack_Options::get_option( $args[1] );
if ( $value && is_array( $value ) ) {
WP_CLI::error( __( 'Sorry, no updating arrays at this time', 'jetpack' ) );
Jetpack_Options::update_option( $args[1], $args[2] );
/* translators: %1$s is the previous value, %2$s is the new value */
WP_CLI::success( sprintf( _x( 'Updated option: %1$s to "%2$s"', 'Updating an option from "this" to "that".', 'jetpack' ), $args[1], $args[2] ) );
$options_compact = Jetpack_Options::get_option_names();
$options_non_compact = Jetpack_Options::get_option_names( 'non_compact' );
$options_private = Jetpack_Options::get_option_names( 'private' );
$options = array_merge( $options_compact, $options_non_compact, $options_private );
WP_CLI::line( "\t" . str_pad( __( 'Option', 'jetpack' ), 30 ) . __( 'Value', 'jetpack' ) );
// List out the options and their values.
// Tell them if the value is empty or not.
// Tell them if it's an array.
foreach ( $options as $option ) {
$value = Jetpack_Options::get_option( $option );
WP_CLI::line( "\t" . str_pad( $option, 30 ) . 'Empty' );
if ( ! is_array( $value ) ) {
WP_CLI::line( "\t" . str_pad( $option, 30 ) . $value );
} elseif ( is_array( $value ) ) {
WP_CLI::line( "\t" . str_pad( $option, 30 ) . 'Array - Use "get <option>" to read option array.' );
$option_text = '{' . _x( 'option', 'a variable command that a user can write, provided in the printed instructions', 'jetpack' ) . '}';
$value_text = '{' . _x( 'value', 'the value that they want to update the option to', 'jetpack' ) . '}';
_x( "Above are your options. You may 'get', 'delete', and 'update' them.", "'get', 'delete', and 'update' are commands - do not translate.", 'jetpack' ) . "\n" .
str_pad( 'wp jetpack options get', 26 ) . $option_text . "\n" .
str_pad( 'wp jetpack options delete', 26 ) . $option_text . "\n" .
str_pad( 'wp jetpack options update', 26 ) . "$option_text $value_text\n" .
_x( "Type 'wp jetpack options' for more info.", "'wp jetpack options' is a command - do not translate.", 'jetpack' ) . "\n"
* Get the status of or start a new Jetpack sync.
* status : Print the current sync status
* settings : Prints the current sync settings
* start : Start a full sync from this site to WordPress.com
* enable : Enables sync on the site
* disable : Disable sync on a site
* reset : Disables sync and Resets the sync queues on a site
* wp jetpack sync settings
* wp jetpack sync start --modules=functions --sync_wait_time=5
* wp jetpack sync disable
* wp jetpack sync reset --queue=full or regular
* @synopsis <status|start> [--<field>=<value>]
* @param array $args Positional args.
* @param array $assoc_args Named args.
public function sync( $args, $assoc_args ) {
$action = isset( $args[0] ) ? $args[0] : 'status';
$status = Actions::get_sync_status();
foreach ( $status as $key => $item ) {
'value' => is_scalar( $item ) ? $item : wp_json_encode( $item ),
WP_CLI::log( __( 'Sync Status:', 'jetpack' ) );
WP_CLI\Utils\format_items( 'table', $collection, array( 'option', 'value' ) );
WP_CLI::log( __( 'Sync Settings:', 'jetpack' ) );
foreach ( Settings::get_settings() as $setting => $item ) {
'value' => is_scalar( $item ) ? $item : wp_json_encode( $item ),
WP_CLI\Utils\format_items( 'table', $settings, array( 'setting', 'value' ) );
// Don't set it via the Settings since that also resets the queues.
update_option( 'jetpack_sync_settings_disable', 1 );
/* translators: %s is the site URL */
WP_CLI::log( sprintf( __( 'Sync Disabled on %s', 'jetpack' ), get_site_url() ) );
Settings::update_settings( array( 'disable' => 0 ) );
/* translators: %s is the site URL */
WP_CLI::log( sprintf( __( 'Sync Enabled on %s', 'jetpack' ), get_site_url() ) );
// Don't set it via the Settings since that also resets the queues.
update_option( 'jetpack_sync_settings_disable', 1 );
/* translators: %s is the site URL */
WP_CLI::log( sprintf( __( 'Sync Disabled on %s. Use `wp jetpack sync enable` to enable syncing again.', 'jetpack' ), get_site_url() ) );
$listener = Listener::get_instance();
if ( empty( $assoc_args['queue'] ) ) {
$listener->get_sync_queue()->reset();
$listener->get_full_sync_queue()->reset();
/* translators: %s is the site URL */
WP_CLI::log( sprintf( __( 'Reset Full Sync and Regular Queues Queue on %s', 'jetpack' ), get_site_url() ) );
if ( ! empty( $assoc_args['queue'] ) ) {
switch ( $assoc_args['queue'] ) {
$listener->get_sync_queue()->reset();
/* translators: %s is the site URL */
WP_CLI::log( sprintf( __( 'Reset Regular Sync Queue on %s', 'jetpack' ), get_site_url() ) );
$listener->get_full_sync_queue()->reset();
/* translators: %s is the site URL */
WP_CLI::log( sprintf( __( 'Reset Full Sync Queue on %s', 'jetpack' ), get_site_url() ) );
WP_CLI::error( __( 'Please specify what type of queue do you want to reset: `full` or `regular`.', 'jetpack' ) );
if ( ! Actions::sync_allowed() ) {
if ( Settings::get_setting( 'disable' ) ) {
WP_CLI::error( __( 'Jetpack sync is not currently allowed for this site. It is currently disabled. Run `wp jetpack sync enable` to enable it.', 'jetpack' ) );
$connection = new Connection_Manager();
if ( ! $connection->is_connected() ) {
if ( ! doing_action( 'jetpack_site_registered' ) ) {
WP_CLI::error( __( 'Jetpack sync is not currently allowed for this site. Jetpack is not connected.', 'jetpack' ) );
if ( $status->is_offline_mode() ) {
WP_CLI::error( __( 'Jetpack sync is not currently allowed for this site. The site is in offline mode.', 'jetpack' ) );
if ( $status->in_safe_mode() ) {
WP_CLI::error( __( 'Jetpack sync is not currently allowed for this site. The site is in safe mode.', 'jetpack' ) );
// Get the original settings so that we can restore them later.
$original_settings = Settings::get_settings();
// Initialize sync settigns so we can sync as quickly as possible.
$sync_settings = wp_parse_args(
array_intersect_key( $assoc_args, Settings::$valid_settings ),