* REST API Onboarding Profile Controller
* Handles requests to /onboarding/profile
namespace Automattic\WooCommerce\Admin\API;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\PluginsHelper;
use Automattic\WooCommerce\Internal\Jetpack\JetpackConnection;
use WC_REST_Data_Controller;
* Onboarding Plugins controller.
* @extends WC_REST_Data_Controller
class OnboardingPlugins extends WC_REST_Data_Controller {
protected $namespace = 'wc-admin';
protected $rest_base = 'onboarding/plugins';
public function register_routes() {
'/' . $this->rest_base . '/install-and-activate-async',
'callback' => array( $this, 'install_and_activate_async' ),
'permission_callback' => array( $this, 'can_install_and_activate_plugins' ),
'description' => 'A list of plugins to install',
'sanitize_callback' => function ( $value ) {
return sanitize_text_field( $value );
'description' => 'The source of the request',
'sanitize_callback' => 'sanitize_text_field',
'schema' => array( $this, 'get_install_async_schema' ),
'/' . $this->rest_base . '/install-and-activate',
'callback' => array( $this, 'install_and_activate' ),
'permission_callback' => array( $this, 'can_install_and_activate_plugins' ),
'schema' => array( $this, 'get_install_activate_schema' ),
'/' . $this->rest_base . '/scheduled-installs/(?P<job_id>\w+)',
'callback' => array( $this, 'get_scheduled_installs' ),
'permission_callback' => array( $this, 'can_install_plugins' ),
'schema' => array( $this, 'get_install_async_schema' ),
// This is an experimental endpoint and is subject to change in the future.
'/' . $this->rest_base . '/jetpack-authorization-url',
'callback' => array( $this, 'get_jetpack_authorization_url' ),
'permission_callback' => array( $this, 'can_install_plugins' ),
'description' => 'The URL to redirect to after authorization',
'sanitize_callback' => 'sanitize_text_field',
'description' => 'from value for the jetpack authorization page',
'sanitize_callback' => 'sanitize_text_field',
'default' => 'woocommerce-onboarding',
add_action( 'woocommerce_plugins_install_error', array( $this, 'log_plugins_install_error' ), 10, 4 );
add_action( 'woocommerce_plugins_install_api_error', array( $this, 'log_plugins_install_api_error' ), 10, 2 );
* Install and activate a plugin.
* @param WP_REST_Request $request WP Request object.
* @return WP_REST_Response
public function install_and_activate( WP_REST_Request $request ) {
$response['install'] = PluginsHelper::install_plugins( $request->get_param( 'plugins' ) );
$response['activate'] = PluginsHelper::activate_plugins( $response['install']['installed'] );
return new WP_REST_Response( $response );
* Queue plugin install request.
* @param WP_REST_Request $request WP_REST_Request object.
public function install_and_activate_async( WP_REST_Request $request ) {
$plugins = $request->get_param( 'plugins' );
$source = $request->get_param( 'source' );
WC()->queue()->add( 'woocommerce_plugins_install_and_activate_async_callback', array( $plugins, $job_id, $source ) );
$plugin_status = array();
foreach ( $plugins as $plugin ) {
$plugin_status[ $plugin ] = array(
'plugins' => $plugin_status,
* Returns current status of given job.
* @param WP_REST_Request $request WP_REST_Request object.
* @return array|WP_REST_Response
public function get_scheduled_installs( WP_REST_Request $request ) {
$job_id = $request->get_param( 'job_id' );
$actions = WC()->queue()->search(
'hook' => 'woocommerce_plugins_install_and_activate_async_callback',
PluginsHelper::get_action_data( $actions ),
function ( $action ) use ( $job_id ) {
return $action['job_id'] === $job_id;
if ( empty( $actions ) ) {
return new WP_REST_Response( null, 404 );
'job_id' => $actions[0]['job_id'],
'status' => $actions[0]['status'],
$option = get_option( 'woocommerce_onboarding_plugins_install_and_activate_async_' . $job_id );
if ( isset( $option['plugins'] ) ) {
$response['plugins'] = $option['plugins'];
* Return Jetpack authorization URL.
* @param WP_REST_Request $request WP_REST_Request object.
public function get_jetpack_authorization_url( WP_REST_Request $request ) {
return JetpackConnection::get_authorization_url(
$request->get_param( 'redirect_url' ),
$request->get_param( 'from' )
* Check whether the current user has permission to install plugins
* @return WP_Error|boolean
public function can_install_plugins() {
if ( ! current_user_can( 'install_plugins' ) ) {
'woocommerce_rest_cannot_update',
__( 'Sorry, you cannot manage plugins.', 'woocommerce' ),
array( 'status' => rest_authorization_required_code() )
* Check whether the current user has permission to install and activate plugins
* @return WP_Error|boolean
public function can_install_and_activate_plugins() {
if ( ! current_user_can( 'install_plugins' ) || ! current_user_can( 'activate_plugins' ) ) {
'woocommerce_rest_cannot_update',
__( 'Sorry, you cannot manage plugins.', 'woocommerce' ),
array( 'status' => rest_authorization_required_code() )
* JSON Schema for both install-async and scheduled-installs endpoints.
public function get_install_async_schema() {
'$schema' => 'http://json-schema.org/draft-04/schema#',
'title' => 'Install Async Schema',
'enum' => array( 'pending', 'complete', 'failed' ),
* JSON Schema for install-and-activate endpoint.
public function get_install_activate_schema() {
'patternProperties' => array(
'errors' => $error_schema,
'error_data' => $error_schema,
$activate_schema = array(
'errors' => $error_schema,
'error_data' => $error_schema,
'$schema' => 'http://json-schema.org/draft-04/schema#',
'title' => 'Install and Activate Schema',
'install' => $install_schema,
'activate' => $activate_schema,
public function log_plugins_install_error( $slug, $api, $result, $upgrader ) {
'error_message' => sprintf(
/* translators: %s: plugin slug (example: woocommerce-services) */
'The requested plugin `%s` could not be installed.',
'type' => 'plugin_info_api_error',
'api_version' => $api->version,
'api_download_link' => $api->download_link,
'upgrader_skin_message' => implode( ',', $upgrader->skin->get_upgrade_messages() ),
'result' => is_wp_error( $result ) ? $result->get_error_message() : 'null',
wc_admin_record_tracks_event( 'coreprofiler_install_plugin_error', $properties );
public function log_plugins_install_api_error( $slug, $api ) {
'error_message' => sprintf(
// translators: %s: plugin slug (example: woocommerce-services).
'The requested plugin `%s` could not be installed. Plugin API call failed.',
'type' => 'plugin_install_error',
'api_error_message' => $api->get_error_message(),
wc_admin_record_tracks_event( 'coreprofiler_install_plugin_error', $properties );