diff options
Diffstat (limited to 'plugins/jetpack/class.jetpack-cli.php')
-rw-r--r-- | plugins/jetpack/class.jetpack-cli.php | 515 |
1 files changed, 468 insertions, 47 deletions
diff --git a/plugins/jetpack/class.jetpack-cli.php b/plugins/jetpack/class.jetpack-cli.php index 1f242377..9f82468a 100644 --- a/plugins/jetpack/class.jetpack-cli.php +++ b/plugins/jetpack/class.jetpack-cli.php @@ -6,7 +6,6 @@ WP_CLI::add_command( 'jetpack', 'Jetpack_CLI' ); * Control your local Jetpack installation. */ class Jetpack_CLI extends WP_CLI_Command { - // Aesthetics public $green_open = "\033[32m"; public $red_open = "\033[31m"; @@ -29,8 +28,9 @@ class Jetpack_CLI extends WP_CLI_Command { * */ public function status( $args, $assoc_args ) { + require_once( JETPACK__PLUGIN_DIR . 'class.jetpack-debugger.php' ); - WP_CLI::line( sprintf( __( 'Checking status for %s', 'jetpack' ), esc_url( get_site_url() ) ) ); + WP_CLI::line( sprintf( __( 'Checking status for %s', 'jetpack' ), esc_url( get_home_url() ) ) ); if ( ! Jetpack::is_active() ) { WP_CLI::error( __( 'Jetpack is not currently connected to WordPress.com', 'jetpack' ) ); @@ -43,6 +43,20 @@ class Jetpack_CLI extends WP_CLI_Command { $master_user_email = Jetpack::get_master_user_email(); + $jetpack_self_test = Jetpack_Debugger::run_self_test(); // Performs the same tests as jetpack.com/support/debug/ + + if ( ! $jetpack_self_test || ! wp_remote_retrieve_response_code( $jetpack_self_test ) ) { + WP_CLI::error( __( 'Jetpack connection status unknown.', 'jetpack' ) ); + } else if ( 200 == wp_remote_retrieve_response_code( $jetpack_self_test ) ) { + WP_CLI::success( __( 'Jetpack is currently connected to WordPress.com', 'jetpack' ) ); + } else { + WP_CLI::error( __( 'Jetpack connection is broken.', 'jetpack' ) ); + } + + WP_CLI::line( sprintf( __( 'The Jetpack Version is %s', 'jetpack' ), JETPACK__VERSION ) ); + WP_CLI::line( sprintf( __( 'The WordPress.com blog_id is %d', 'jetpack' ), Jetpack_Options::get_option( 'id' ) ) ); + WP_CLI::line( sprintf( __( 'The WordPress.com account for the primary connection is %s', 'jetpack' ), $master_user_email ) ); + /* * Are they asking for all data? * @@ -50,11 +64,6 @@ class Jetpack_CLI extends WP_CLI_Command { */ $all_data = ( isset( $args[0] ) && 'full' == $args[0] ) ? 'full' : false; if ( $all_data ) { - WP_CLI::success( __( 'Jetpack is currently connected to WordPress.com', 'jetpack' ) ); - WP_CLI::line( sprintf( __( "The Jetpack Version is %s", 'jetpack' ), JETPACK__VERSION ) ); - WP_CLI::line( sprintf( __( "The WordPress.com blog_id is %d", 'jetpack' ), Jetpack_Options::get_option( 'id' ) ) ); - WP_CLI::line( sprintf( __( 'The WordPress.com account for the primary connection is %s', 'jetpack' ), $master_user_email ) ); - // Heartbeat data WP_CLI::line( "\n" . __( 'Additional data: ', 'jetpack' ) ); @@ -86,10 +95,6 @@ class Jetpack_CLI extends WP_CLI_Command { } } else { // Just the basics - WP_CLI::success( __( 'Jetpack is currently connected to WordPress.com', 'jetpack' ) ); - WP_CLI::line( sprintf( __( 'The Jetpack Version is %s', 'jetpack' ), JETPACK__VERSION ) ); - WP_CLI::line( sprintf( __( 'The WordPress.com blog_id is %d', 'jetpack' ), Jetpack_Options::get_option( 'id' ) ) ); - WP_CLI::line( sprintf( __( 'The WordPress.com account for the primary connection is %s', 'jetpack' ), $master_user_email ) ); WP_CLI::line( "\n" . _x( "View full status with 'wp jetpack status full'", '"wp jetpack status full" is a command - do not translate', 'jetpack' ) ); } } @@ -302,62 +307,84 @@ class Jetpack_CLI extends WP_CLI_Command { * * ## OPTIONS * - * list : View all available modules, and their status. - * activate all : Activate all modules - * deactivate all: Deactivate all modules - * - * activate <module_slug> : Activate a module. - * deactivate <module_slug> : Deactivate a module. - * toggle <module_slug> : Toggle a module on or off. + * <list|activate|deactivate|toggle> + * : The action to take. + * --- + * default: list + * options: + * - list + * - activate + * - deactivate + * - toggle + * --- + * + * [<module_slug>] + * : The slug of the module to perform an action on. + * + * [--format=<format>] + * : Allows overriding the output of the command when listing modules. + * --- + * default: table + * options: + * - table + * - json + * - csv + * - yaml + * - ids + * - count + * --- * * ## EXAMPLES * * wp jetpack module list + * 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 - * - * @synopsis <list|activate|deactivate|toggle> [<module_name>] */ public function module( $args, $assoc_args ) { $action = isset( $args[0] ) ? $args[0] : 'list'; - if ( ! in_array( $action, array( 'list', 'activate', 'deactivate', 'toggle' ) ) ) { - /* translators: %s is a command like "prompt" */ - WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) ); - } - if ( in_array( $action, array( 'activate', 'deactivate', 'toggle' ) ) ) { - if ( isset( $args[1] ) ) { - $module_slug = $args[1]; - if ( 'all' !== $module_slug && ! Jetpack::is_module( $module_slug ) ) { - WP_CLI::error( sprintf( __( '%s is not a valid module.', 'jetpack' ), $module_slug ) ); - } - if ( 'toggle' == $action ) { - $action = Jetpack::is_module_active( $module_slug ) ? 'deactivate' : 'activate'; - } - // Bulk actions - if ( 'all' == $args[1] ) { - $action = ( 'deactivate' == $action ) ? 'deactivate_all' : 'activate_all'; - } - } else { - WP_CLI::line( __( 'Please specify a valid module.', 'jetpack' ) ); - $action = 'list'; + + if ( isset( $args[1] ) ) { + $module_slug = $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 ) + ? 'deactivate' + : 'activate'; } + if ( 'all' === $args[1] ) { + $action = ( 'deactivate' === $action ) + ? 'deactivate_all' + : 'activate_all'; + } + } elseif ( 'list' !== $action ) { + WP_CLI::line( __( 'Please specify a valid module.', 'jetpack' ) ); + $action = 'list'; } + switch ( $action ) { case 'list': - WP_CLI::line( __( 'Available Modules:', 'jetpack' ) ); - $modules = Jetpack::get_available_modules(); + $modules_list = array(); + $modules = Jetpack::get_available_modules(); sort( $modules ); - foreach( $modules as $module_slug ) { - if ( 'vaultpress' == $module_slug ) { + foreach ( (array) $modules as $module_slug ) { + if ( 'vaultpress' === $module_slug ) { continue; } - $active = Jetpack::is_module_active( $module_slug ) ? __( 'Active', 'jetpack' ) : __( 'Inactive', 'jetpack' ); - WP_CLI::line( "\t" . str_pad( $module_slug, 24 ) . $active ); + $modules_list[] = array( + 'slug' => $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' ) ); break; case 'activate': $module = Jetpack::get_module( $module_slug ); @@ -988,6 +1015,400 @@ class Jetpack_CLI extends WP_CLI_Command { $sitemap_builder->update_sitemap(); } + /** + * Allows authorizing a user via the command line and will activate + * + * ## EXAMPLES + * + * wp jetpack authorize_user --token=123456789abcdef + * + * @synopsis --token=<value> + */ + public function authorize_user( $args, $named_args ) { + if ( ! is_user_logged_in() ) { + WP_CLI::error( __( 'Please select a user to authorize via the --user global argument.', 'jetpack' ) ); + } + + if ( empty( $named_args['token'] ) ) { + WP_CLI::error( __( 'A non-empty token argument must be passed.', 'jetpack' ) ); + } + + $token = sanitize_text_field( $named_args['token'] ); + + $is_master_user = ! Jetpack::is_active(); + $current_user_id = get_current_user_id(); + + Jetpack::update_user_token( $current_user_id, sprintf( '%s.%d', $token, $current_user_id ), $is_master_user ); + + WP_CLI::log( wp_json_encode( $named_args ) ); + + if ( $is_master_user ) { + /** + * Auto-enable SSO module for new Jetpack Start connections + * + * @since 5.0.0 + * + * @param bool $enable_sso Whether to enable the SSO module. Default to true. + */ + $enable_sso = apply_filters( 'jetpack_start_enable_sso', true ); + Jetpack::handle_post_authorization_actions( $enable_sso, false ); + + /* translators: %d is a user ID */ + WP_CLI::success( sprintf( __( 'Authorized %d and activated default modules.', 'jetpack' ), $current_user_id ) ); + } else { + /* translators: %d is a user ID */ + WP_CLI::success( sprintf( __( 'Authorized %d.', 'jetpack' ), $current_user_id ) ); + } + } + + /** + * Allows calling a WordPress.com API endpoint using the current blog's token. + * + * ## OPTIONS + * --resource=<resource> + * : The resource to call with the current blog's token, where `%d` represents the current blog's ID. + * + * [--api_version=<api_version>] + * : The API version to query against. + * + * [--base_api_path=<base_api_path>] + * : The base API path to query. + * --- + * default: rest + * --- + * + * [--body=<body>] + * : A JSON encoded string representing arguments to send in the body. + * + * [--field=<value>] + * : Any number of arguments that should be passed to the resource. + * + * [--pretty] + * : Will pretty print the results of a successful API call. + * + * [--strip-success] + * : Will remove the green success label from successful API calls. + * + * ## EXAMPLES + * + * wp jetpack call_api --resource='/sites/%d' + */ + public function call_api( $args, $named_args ) { + if ( ! Jetpack::is_active() ) { + WP_CLI::error( __( 'Jetpack is not currently connected to WordPress.com', 'jetpack' ) ); + } + + $consumed_args = array( + 'resource', + 'api_version', + 'base_api_path', + 'body', + 'pretty', + ); + + // Get args that should be passed to resource. + $other_args = array_diff_key( $named_args, array_flip( $consumed_args ) ); + + $decoded_body = ! empty( $named_args['body'] ) + ? json_decode( $named_args['body'] ) + : false; + + $resource_url = ( false === strpos( $named_args['resource'], '%d' ) ) + ? $named_args['resource'] + : sprintf( $named_args['resource'], Jetpack_Options::get_option( 'id' ) ); + + $response = Jetpack_Client::wpcom_json_api_request_as_blog( + $resource_url, + empty( $named_args['api_version'] ) ? Jetpack_Client::WPCOM_JSON_API_VERSION : $named_args['api_version'], + $other_args, + empty( $decoded_body ) ? null : $decoded_body, + $named_args['base_api_path'] + ); + + if ( is_wp_error( $response ) ) { + WP_CLI::error( sprintf( + /* translators: %1$s is an endpoint route (ex. /sites/123456), %2$d is an error code, %3$s is an error message. */ + __( 'Request to %1$s returned an error: (%2$d) %3$s.', 'jetpack' ), + $resource_url, + $response->get_error_code(), + $response->get_error_message() + ) ); + } + + if ( 200 !== wp_remote_retrieve_response_code( $response ) ) { + WP_CLI::error( sprintf( + /* translators: %1$s is an endpoint route (ex. /sites/123456), %2$d is an HTTP status code. */ + __( 'Request to %1$s returned a non-200 response code: %2$d.', 'jetpack' ), + $resource_url, + wp_remote_retrieve_response_code( $response ) + ) ); + } + + $output = wp_remote_retrieve_body( $response ); + if ( isset( $named_args['pretty'] ) ) { + $decoded_output = json_decode( $output ); + if ( $decoded_output ) { + $output = wp_json_encode( $decoded_output, JSON_PRETTY_PRINT ); + } + } + + if ( isset( $named_args['strip-success'] ) ) { + WP_CLI::log( $output ); + WP_CLI::halt( 0 ); + } + + WP_CLI::success( $output ); + } + + /** + * API wrapper for getting stats from the WordPress.com API for the current site. + * + * ## OPTIONS + * + * [--quantity=<quantity>] + * : The number of units to include. + * --- + * default: 30 + * --- + * + * [--period=<period>] + * : The unit of time to query stats for. + * --- + * default: day + * options: + * - day + * - week + * - month + * - year + * --- + * + * [--date=<date>] + * : The latest date to return stats for. Ex. - 2018-01-01. + * + * [--pretty] + * : Will pretty print the results of a successful API call. + * + * [--strip-success] + * : Will remove the green success label from successful API calls. + * + * ## EXAMPLES + * + * wp jetpack get_stats + */ + public function get_stats( $args, $named_args ) { + $selected_args = array_intersect_key( + $named_args, + array_flip( array( + 'quantity', + 'date', + ) ) + ); + + // The API expects unit, but period seems to be more correct. + $selected_args['unit'] = $named_args['period']; + + $command = sprintf( + 'jetpack call_api --resource=/sites/%d/stats/%s', + Jetpack_Options::get_option( 'id' ), + add_query_arg( $selected_args, 'visits' ) + ); + + if ( isset( $named_args['pretty'] ) ) { + $command .= ' --pretty'; + } + + if ( isset( $named_args['strip-success'] ) ) { + $command .= ' --strip-success'; + } + + WP_CLI::runcommand( + $command, + array( + 'launch' => false, // Use the current process. + ) + ); + } + + /* + * Allows management of publicize connections. + * + * ## OPTIONS + * + * <list|disconnect> + * : The action to perform. + * --- + * options: + * - list + * - disconnect + * --- + * + * [<identifier>] + * : The connection ID or service to perform an action on. + * + * [--format=<format>] + * : Allows overriding the output of the command when listing connections. + * --- + * default: table + * options: + * - table + * - json + * - csv + * - yaml + * - ids + * - count + * --- + * + * ## EXAMPLES + * + * wp jetpack publicize list + * wp jetpack publicize list twitter + * wp --user=1 jetpack publicize list + * wp --user=1 jetpack publicize list twitter + * wp jetpack publicize list 123456 + * wp jetpack publicize disconnect 123456 + * wp jetpack publicize disconnect all + * wp jetpack publicize disconnect twitter + */ + public function publicize( $args, $named_args ) { + if ( ! Jetpack::is_active() ) { + WP_CLI::error( __( 'Jetpack is not currently connected to WordPress.com', 'jetpack' ) ); + } + + $action = $args[0]; + $publicize = new Publicize(); + $identifier = ! empty( $args[1] ) ? $args[1] : false; + $services = array_keys( $publicize->get_services() ); + $id_is_service = in_array( $identifier, $services, true ); + + switch ( $action ) { + case 'list': + $connections_to_return = array(); + + // For the CLI command, let's return all connections when a user isn't specified. This + // differs from the logic in the Publicize class. + $option_connections = is_user_logged_in() + ? (array) $publicize->get_all_connections_for_user() + : Jetpack_Options::get_option( 'publicize_connections' ); + + foreach ( $option_connections as $service_name => $connections ) { + foreach ( (array) $connections as $id => $connection ) { + $connection['id'] = $id; + $connection['service'] = $service_name; + $connections_to_return[] = $connection; + } + } + + if ( $id_is_service && ! empty( $identifier ) && ! empty( $connections_to_return ) ) { + $temp_connections = $connections_to_return; + $connections_to_return = array(); + + foreach ( $temp_connections as $connection ) { + if ( $identifier === $connection['service'] ) { + $connections_to_return[] = $connection; + } + } + } + + if ( $identifier && ! $id_is_service && ! empty( $connections_to_return ) ) { + $connections_to_return = wp_list_filter( $connections_to_return, array( 'id' => $identifier ) ); + } + + if ( empty( $connections_to_return ) ) { + return false; + } + + $expected_keys = array( + 'id', + 'service', + 'user_id', + 'provider', + 'issued', + 'expires', + 'external_id', + 'external_name', + 'external_display', + 'type', + 'connection_data', + ); + + WP_CLI\Utils\format_items( $named_args['format'], $connections_to_return, $expected_keys ); + break; // list. + case 'disconnect': + if ( ! $identifier ) { + WP_CLI::error( __( 'A connection ID must be passed in order to disconnect.', 'jetpack' ) ); + } + + // If the connection ID is 'all' then delete all connections. If the connection ID + // matches a service, delete all connections for that service. + if ( 'all' === $identifier || $id_is_service ) { + if ( 'all' === $identifier ) { + WP_CLI::log( __( "You're about to delete all publicize connections.", 'jetpack' ) ); + } else { + /* translators: %s is a lowercase string for a social network. */ + WP_CLI::log( sprintf( __( "You're about to delete all publicize connections to %s.", 'jetpack' ), $identifier ) ); + } + + jetpack_cli_are_you_sure(); + + $connections = array(); + $service = $identifier; + + $option_connections = is_user_logged_in() + ? (array) $publicize->get_all_connections_for_user() + : Jetpack_Options::get_option( 'publicize_connections' ); + + if ( 'all' === $service ) { + foreach ( (array) $option_connections as $service_name => $service_connections ) { + foreach ( $service_connections as $id => $connection ) { + $connections[ $id ] = $connection; + } + } + } elseif ( ! empty( $option_connections[ $service ] ) ) { + $connections = $option_connections[ $service ]; + } + + if ( ! empty( $connections ) ) { + $count = count( $connections ); + $progress = \WP_CLI\Utils\make_progress_bar( + /* translators: %s is a lowercase string for a social network. */ + sprintf( __( 'Disconnecting all connections to %s.', 'jetpack' ), $service ), + $count + ); + + foreach ( $connections as $id => $connection ) { + if ( false === $publicize->disconnect( false, $id ) ) { + WP_CLI::error( sprintf( + /* translators: %1$d is a numeric ID and %2$s is a lowercase string for a social network. */ + __( 'Publicize connection %d could not be disconnected', 'jetpack' ), + $id + ) ); + } + + $progress->tick(); + } + + $progress->finish(); + + if ( 'all' === $service ) { + WP_CLI::success( __( 'All publicize connections were successfully disconnected.', 'jetpack' ) ); + } else { + /* translators: %s is a lowercase string for a social network. */ + WP_CLI::success( __( 'All publicize connections to %s were successfully disconnected.', 'jetpack' ), $service ); + } + } + } else { + if ( false !== $publicize->disconnect( false, $identifier ) ) { + /* translators: %d is a numeric ID. Example: 1234. */ + WP_CLI::success( sprintf( __( 'Publicize connection %d has been disconnected.', 'jetpack' ), $identifier ) ); + } else { + /* translators: %d is a numeric ID. Example: 1234. */ + WP_CLI::error( sprintf( __( 'Publicize connection %d could not be disconnected.', 'jetpack' ), $identifier ) ); + } + } + break; // disconnect. + } + } + private function get_api_host() { $env_api_host = getenv( 'JETPACK_START_API_HOST', true ); return $env_api_host ? $env_api_host : JETPACK__WPCOM_JSON_API_HOST; |