plugin updates

This commit is contained in:
Tony Volpe
2024-02-21 16:19:46 +00:00
parent c72f206574
commit 21d4c85c00
1214 changed files with 102269 additions and 179257 deletions

View File

@@ -1,62 +0,0 @@
<?php
/**
* Woo.com Product Installation Requirements Check.
*
* @package WooCommerce\WCCom
* @since 3.8.0
*/
use Automattic\Jetpack\Constants;
defined( 'ABSPATH' ) || exit;
/**
* WC_WCCOM_Site_Installer_Requirements_Check Class
* Contains functionality to check the necessary requirements for the installer.
*/
class WC_WCCOM_Site_Installer_Requirements_Check {
/**
* Check if the site met the requirements
*
* @version 3.8.0
* @return bool|WP_Error Does the site met the requirements?
*/
public static function met_requirements() {
$errs = array();
if ( ! self::met_wp_cron_requirement() ) {
$errs[] = 'wp-cron';
}
if ( ! self::met_filesystem_requirement() ) {
$errs[] = 'filesystem';
}
if ( ! empty( $errs ) ) {
// translators: %s: Requirements unmet.
return new WP_Error( 'requirements_not_met', sprintf( __( 'Server requirements not met, missing requirement(s): %s.', 'woocommerce' ), implode( ', ', $errs ) ), array( 'status' => 400 ) );
}
return true;
}
/**
* Validates if WP CRON is enabled.
*
* @since 3.8.0
* @return bool
*/
private static function met_wp_cron_requirement() {
return ! Constants::is_true( 'DISABLE_WP_CRON' );
}
/**
* Validates if `WP_CONTENT_DIR` is writable.
*
* @since 3.8.0
* @return bool
*/
private static function met_filesystem_requirement() {
return is_writable( WP_CONTENT_DIR );
}
}

View File

@@ -15,54 +15,6 @@ defined( 'ABSPATH' ) || exit;
*/
class WC_WCCOM_Site_Installer {
/**
* Error message returned install_package if the folder already exists.
*
* @var string
*/
private static $folder_exists = 'folder_exists';
/**
* Default state.
*
* @var array
*/
private static $default_state = array(
'status' => 'idle',
'steps' => array(),
'current_step' => null,
);
/**
* Represents product step state.
*
* @var array
*/
private static $default_step_state = array(
'download_url' => '',
'product_type' => '',
'last_step' => '',
'last_error' => '',
'download_path' => '',
'unpacked_path' => '',
'installed_path' => '',
'activate' => false,
);
/**
* Product install steps. Each step is a method name in this class that
* will be passed with product ID arg \WP_Upgrader instance.
*
* @var array
*/
private static $install_steps = array(
'get_product_info',
'download_product',
'unpack_product',
'move_product',
'activate_product',
);
/**
* An instance of the WP_Upgrader class to be used for installation.
*
@@ -70,447 +22,6 @@ class WC_WCCOM_Site_Installer {
*/
private static $wp_upgrader;
/**
* Get the product install state.
*
* @since 3.7.0
* @param string $key Key in state data. If empty key is passed array of
* state will be returned.
* @return array Product install state.
*/
public static function get_state( $key = '' ) {
$state = WC_Helper_Options::get( 'product_install', self::$default_state );
if ( ! empty( $key ) ) {
return isset( $state[ $key ] ) ? $state[ $key ] : null;
}
return $state;
}
/**
* Update the product install state.
*
* @since 3.7.0
* @param string $key Key in state data.
* @param mixed $value Value.
*/
public static function update_state( $key, $value ) {
$state = WC_Helper_Options::get( 'product_install', self::$default_state );
$state[ $key ] = $value;
WC_Helper_Options::update( 'product_install', $state );
}
/**
* Reset product install state.
*
* @since 3.7.0
* @param array $products List of product IDs.
*/
public static function reset_state( $products = array() ) {
WC()->queue()->cancel_all( 'woocommerce_wccom_install_products' );
WC_Helper_Options::update( 'product_install', self::$default_state );
}
/**
* Schedule installing given list of products.
*
* @since 3.7.0
* @param array $products Array of products where key is product ID and
* element is install args.
* @return array State.
*/
public static function schedule_install( $products ) {
$state = self::get_state();
$status = ! empty( $state['status'] ) ? $state['status'] : '';
if ( 'in-progress' === $status ) {
return $state;
}
self::update_state( 'status', 'in-progress' );
$steps = array_fill_keys( array_keys( $products ), self::$default_step_state );
self::update_state( 'steps', $steps );
self::update_state( 'current_step', null );
$args = array(
'products' => $products,
);
// Clear the cache of customer's subscription before asking for them.
// Thus, they will be re-fetched from Woo.com after a purchase.
WC_Helper::_flush_subscriptions_cache();
WC()->queue()->cancel_all( 'woocommerce_wccom_install_products', $args );
WC()->queue()->add( 'woocommerce_wccom_install_products', $args );
return self::get_state();
}
/**
* Install a given product IDs.
*
* Run via `woocommerce_wccom_install_products` hook.
*
* @since 3.7.0
* @param array $products Array of products where key is product ID and
* element is install args.
*/
public static function install( $products ) {
$upgrader = self::get_wp_upgrader();
foreach ( $products as $product_id => $install_args ) {
self::install_product( $product_id, $install_args, $upgrader );
}
self::finish_installation();
}
/**
* Finish installation by updating the state.
*
* @since 3.7.0
*/
private static function finish_installation() {
$state = self::get_state();
if ( empty( $state['steps'] ) ) {
return;
}
foreach ( $state['steps'] as $step ) {
if ( ! empty( $step['last_error'] ) ) {
$state['status'] = 'has_error';
break;
}
}
if ( 'has_error' !== $state['status'] ) {
$state['status'] = 'finished';
}
WC_Helper_Options::update( 'product_install', $state );
}
/**
* Install a single product given its ID.
*
* @since 3.7.0
* @param int $product_id Product ID.
* @param array $install_args Install args.
* @param \WP_Upgrader $upgrader Core class to handle installation.
*/
private static function install_product( $product_id, $install_args, $upgrader ) {
foreach ( self::$install_steps as $step ) {
self::do_install_step( $product_id, $install_args, $step, $upgrader );
}
}
/**
* Perform product installation step.
*
* @since 3.7.0
* @param int $product_id Product ID.
* @param array $install_args Install args.
* @param string $step Installation step.
* @param \WP_Upgrader $upgrader Core class to handle installation.
*/
private static function do_install_step( $product_id, $install_args, $step, $upgrader ) {
$state_steps = self::get_state( 'steps' );
if ( empty( $state_steps[ $product_id ] ) ) {
$state_steps[ $product_id ] = self::$default_step_state;
}
if ( ! empty( $state_steps[ $product_id ]['last_error'] ) ) {
return;
}
$state_steps[ $product_id ]['last_step'] = $step;
if ( ! empty( $install_args['activate'] ) ) {
$state_steps[ $product_id ]['activate'] = true;
}
self::update_state(
'current_step',
array(
'product_id' => $product_id,
'step' => $step,
)
);
$result = call_user_func( array( __CLASS__, $step ), $product_id, $upgrader );
if ( is_wp_error( $result ) ) {
$state_steps[ $product_id ]['last_error'] = $result->get_error_message();
} else {
switch ( $step ) {
case 'get_product_info':
$state_steps[ $product_id ]['download_url'] = $result['download_url'];
$state_steps[ $product_id ]['product_type'] = $result['product_type'];
$state_steps[ $product_id ]['product_name'] = $result['product_name'];
break;
case 'download_product':
$state_steps[ $product_id ]['download_path'] = $result;
break;
case 'unpack_product':
$state_steps[ $product_id ]['unpacked_path'] = $result;
break;
case 'move_product':
$state_steps[ $product_id ]['installed_path'] = $result['destination'];
if ( isset( $result[ self::$folder_exists ] ) ) {
$state_steps[ $product_id ]['warning'] = array(
'message' => self::$folder_exists,
'plugin_info' => self::get_plugin_info( $state_steps[ $product_id ]['installed_path'] ),
);
}
break;
}
}
self::update_state( 'steps', $state_steps );
}
/**
* Get product info from its ID.
*
* @since 3.7.0
* @param int $product_id Product ID.
* @return array|\WP_Error
*/
private static function get_product_info( $product_id ) {
$product_info = array(
'download_url' => '',
'product_type' => '',
);
// Get product info from Woo.com.
$request = WC_Helper_API::get(
add_query_arg(
array( 'product_id' => absint( $product_id ) ),
'info'
),
array(
'authenticated' => true,
)
);
if ( 200 !== wp_remote_retrieve_response_code( $request ) ) {
return new WP_Error( 'product_info_failed', __( 'Failed to retrieve product info from Woo.com', 'woocommerce' ) );
}
$result = json_decode( wp_remote_retrieve_body( $request ), true );
$product_info['product_type'] = $result['_product_type'];
$product_info['product_name'] = $result['name'];
if ( ! empty( $result['_wporg_product'] ) && ! empty( $result['download_link'] ) ) {
// For wporg product, download is set already from info response.
$product_info['download_url'] = $result['download_link'];
} elseif ( ! WC_Helper::has_product_subscription( $product_id ) ) {
// Non-wporg product needs subscription.
return new WP_Error( 'missing_subscription', __( 'Missing product subscription', 'woocommerce' ) );
} else {
// Retrieve download URL for non-wporg product.
WC_Helper_Updater::flush_updates_cache();
$updates = WC_Helper_Updater::get_update_data();
if ( empty( $updates[ $product_id ]['package'] ) ) {
return new WP_Error( 'missing_product_package', __( 'Could not find product package.', 'woocommerce' ) );
}
$product_info['download_url'] = $updates[ $product_id ]['package'];
}
return $product_info;
}
/**
* Download product by its ID and returns the path of the zip package.
*
* @since 3.7.0
* @param int $product_id Product ID.
* @param \WP_Upgrader $upgrader Core class to handle installation.
* @return \WP_Error|string
*/
private static function download_product( $product_id, $upgrader ) {
$steps = self::get_state( 'steps' );
if ( empty( $steps[ $product_id ]['download_url'] ) ) {
return new WP_Error( 'missing_download_url', __( 'Could not find download url for the product.', 'woocommerce' ) );
}
return $upgrader->download_package( $steps[ $product_id ]['download_url'] );
}
/**
* Unpack downloaded product.
*
* @since 3.7.0
* @param int $product_id Product ID.
* @param \WP_Upgrader $upgrader Core class to handle installation.
* @return \WP_Error|string
*/
private static function unpack_product( $product_id, $upgrader ) {
$steps = self::get_state( 'steps' );
if ( empty( $steps[ $product_id ]['download_path'] ) ) {
return new WP_Error( 'missing_download_path', __( 'Could not find download path.', 'woocommerce' ) );
}
return $upgrader->unpack_package( $steps[ $product_id ]['download_path'], true );
}
/**
* Move product to plugins directory.
*
* @since 3.7.0
* @param int $product_id Product ID.
* @param \WP_Upgrader $upgrader Core class to handle installation.
* @return array|\WP_Error
*/
private static function move_product( $product_id, $upgrader ) {
$steps = self::get_state( 'steps' );
if ( empty( $steps[ $product_id ]['unpacked_path'] ) ) {
return new WP_Error( 'missing_unpacked_path', __( 'Could not find unpacked path.', 'woocommerce' ) );
}
$destination = 'plugin' === $steps[ $product_id ]['product_type']
? WP_PLUGIN_DIR
: get_theme_root();
$package = array(
'source' => $steps[ $product_id ]['unpacked_path'],
'destination' => $destination,
'clear_working' => true,
'hook_extra' => array(
'type' => $steps[ $product_id ]['product_type'],
'action' => 'install',
),
);
$result = $upgrader->install_package( $package );
/**
* If install package returns error 'folder_exists' threat as success.
*/
if ( is_wp_error( $result ) && array_key_exists( self::$folder_exists, $result->errors ) ) {
return array(
self::$folder_exists => true,
'destination' => $result->error_data[ self::$folder_exists ],
);
}
return $result;
}
/**
* Activate product given its product ID.
*
* @since 3.7.0
* @param int $product_id Product ID.
* @return \WP_Error|null
*/
private static function activate_product( $product_id ) {
$steps = self::get_state( 'steps' );
if ( ! $steps[ $product_id ]['activate'] ) {
return null;
}
if ( 'plugin' === $steps[ $product_id ]['product_type'] ) {
return self::activate_plugin( $product_id );
}
return self::activate_theme( $product_id );
}
/**
* Activate plugin given its product ID.
*
* @since 3.7.0
* @param int $product_id Product ID.
* @return \WP_Error|null
*/
public static function activate_plugin( $product_id ) {
// Clear plugins cache used in `WC_Helper::get_local_woo_plugins`.
wp_clean_plugins_cache();
$filename = false;
// If product is WP.org one, find out its filename.
$dir_name = self::get_wporg_product_dir_name( $product_id );
if ( false !== $dir_name ) {
$filename = self::get_wporg_plugin_main_file( $dir_name );
}
if ( false === $filename ) {
$plugins = wp_list_filter(
WC_Helper::get_local_woo_plugins(),
array(
'_product_id' => $product_id,
)
);
$filename = is_array( $plugins ) && ! empty( $plugins ) ? key( $plugins ) : '';
}
if ( empty( $filename ) ) {
return new WP_Error( 'unknown_filename', __( 'Unknown product filename.', 'woocommerce' ) );
}
return activate_plugin( $filename );
}
/**
* Activate theme given its product ID.
*
* @since 3.7.0
* @param int $product_id Product ID.
* @return \WP_Error|null
*/
private static function activate_theme( $product_id ) {
// Clear plugins cache used in `WC_Helper::get_local_woo_themes`.
wp_clean_themes_cache();
$theme_slug = false;
// If product is WP.org theme, find out its slug.
$dir_name = self::get_wporg_product_dir_name( $product_id );
if ( false !== $dir_name ) {
$theme_slug = basename( $dir_name );
}
if ( false === $theme_slug ) {
$themes = wp_list_filter(
WC_Helper::get_local_woo_themes(),
array(
'_product_id' => $product_id,
)
);
$theme_slug = is_array( $themes ) && ! empty( $themes ) ? dirname( key( $themes ) ) : '';
}
if ( empty( $theme_slug ) ) {
return new WP_Error( 'unknown_filename', __( 'Unknown product filename.', 'woocommerce' ) );
}
return switch_theme( $theme_slug );
}
/**
* Get installed directory of WP.org product.
*
* @since 3.7.0
* @param int $product_id Product ID.
* @return bool|string
*/
private static function get_wporg_product_dir_name( $product_id ) {
$steps = self::get_state( 'steps' );
$product = $steps[ $product_id ];
if ( empty( $product['download_url'] ) || empty( $product['installed_path'] ) ) {
return false;
}
// Check whether product was downloaded from WordPress.org.
$parsed_url = wp_parse_url( $product['download_url'] );
if ( ! empty( $parsed_url['host'] ) && 'downloads.wordpress.org' !== $parsed_url['host'] ) {
return false;
}
return basename( $product['installed_path'] );
}
/**
* Get WP.org plugin's main file.

View File

@@ -41,7 +41,6 @@ class WC_WCCOM_Site {
protected static function includes() {
require_once WC_ABSPATH . 'includes/admin/helper/class-wc-helper.php';
require_once WC_ABSPATH . 'includes/wccom-site/class-wc-wccom-site-installer.php';
require_once WC_ABSPATH . 'includes/wccom-site/class-wc-wccom-site-installer-requirements-check.php';
}
/**
@@ -221,8 +220,8 @@ class WC_WCCOM_Site {
require_once WC_ABSPATH . 'includes/wccom-site/rest-api/class-wc-rest-wccom-site-installer-error-codes.php';
require_once WC_ABSPATH . 'includes/wccom-site/rest-api/class-wc-rest-wccom-site-installer-error.php';
require_once WC_ABSPATH . 'includes/wccom-site/rest-api/endpoints/abstract-wc-rest-wccom-site-controller.php';
require_once WC_ABSPATH . 'includes/wccom-site/rest-api/endpoints/class-wc-rest-wccom-site-installer-controller.php';
require_once WC_ABSPATH . 'includes/wccom-site/rest-api/endpoints/class-wc-rest-wccom-site-installer-controller-v2.php';
require_once WC_ABSPATH . 'includes/wccom-site/rest-api/endpoints/class-wc-rest-wccom-site-ssr-controller.php';
require_once WC_ABSPATH . 'includes/wccom-site/installation/class-wc-wccom-site-installation-state.php';
@@ -236,15 +235,11 @@ class WC_WCCOM_Site {
require_once WC_ABSPATH . 'includes/wccom-site/installation/installation-steps/class-wc-wccom-site-installation-step-move-product.php';
require_once WC_ABSPATH . 'includes/wccom-site/installation/installation-steps/class-wc-wccom-site-installation-step-activate-product.php';
$namespaces['wccom-site/v1'] = array(
$namespaces['wccom-site/v2'] = array(
'installer' => 'WC_REST_WCCOM_Site_Installer_Controller',
'ssr' => 'WC_REST_WCCOM_Site_SSR_Controller',
);
$namespaces['wccom-site/v2'] = array(
'installer' => 'WC_REST_WCCOM_Site_Installer_Controller_V2',
);
return $namespaces;
}
}

View File

@@ -50,6 +50,8 @@ class WC_WCCOM_Site_Installation_Step_Activate_Product implements WC_WCCOM_Site_
* Activate plugin.
*
* @param int $product_id Product ID.
* @return void
* @throws WC_REST_WCCOM_Site_Installer_Error If plugin activation failed.
*/
private function activate_plugin( $product_id ) {
// Clear plugins cache used in `WC_Helper::get_local_woo_plugins`.
@@ -74,13 +76,13 @@ class WC_WCCOM_Site_Installation_Step_Activate_Product implements WC_WCCOM_Site_
}
if ( empty( $filename ) ) {
return new Installer_Error( Installer_Error_Codes::UNKNOWN_FILENAME );
throw new Installer_Error( Installer_Error_Codes::UNKNOWN_FILENAME );
}
$result = activate_plugin( $filename );
if ( is_wp_error( $result ) ) {
return new Installer_Error( Installer_Error_Codes::PLUGIN_ACTIVATION_ERROR, $result->get_error_message() );
throw new Installer_Error( Installer_Error_Codes::PLUGIN_ACTIVATION_ERROR, $result->get_error_message() );
}
}
@@ -88,6 +90,8 @@ class WC_WCCOM_Site_Installation_Step_Activate_Product implements WC_WCCOM_Site_
* Activate theme.
*
* @param int $product_id Product ID.
* @return void
* @throws WC_REST_WCCOM_Site_Installer_Error If theme activation failed.
*/
private function activate_theme( $product_id ) {
// Clear plugins cache used in `WC_Helper::get_local_woo_themes`.
@@ -112,7 +116,7 @@ class WC_WCCOM_Site_Installation_Step_Activate_Product implements WC_WCCOM_Site_
}
if ( empty( $theme_slug ) ) {
return new Installer_Error( Installer_Error_Codes::UNKNOWN_FILENAME );
throw new Installer_Error( Installer_Error_Codes::UNKNOWN_FILENAME );
}
switch_theme( $theme_slug );

View File

@@ -114,7 +114,7 @@ class WC_WCCOM_Site_Installation_Step_Get_Product_Info implements WC_WCCOM_Site_
$updates = WC_Helper_Updater::get_update_data();
if ( empty( $updates[ $product_id ]['package'] ) ) {
return new Installer_Error( Installer_Error_Codes::WCCOM_PRODUCT_MISSING_PACKAGE );
throw new Installer_Error( Installer_Error_Codes::WCCOM_PRODUCT_MISSING_PACKAGE );
}
return $updates[ $product_id ]['package'];

View File

@@ -33,13 +33,16 @@ class WC_WCCOM_Site_Installation_Step_Unpack_Product implements WC_WCCOM_Site_In
/**
* Run the step installation process.
*
* @return WC_WCCOM_Site_Installation_State
* @throws WC_REST_WCCOM_Site_Installer_Error If the package unpacked path is not returned.
*/
public function run() {
$upgrader = WC_WCCOM_Site_Installer::get_wp_upgrader();
$unpacked_path = $upgrader->unpack_package( $this->state->get_download_path(), true );
if ( empty( $unpacked_path ) ) {
return new Installer_Error( Installer_Error_Codes::MISSING_UNPACKED_PATH );
throw new Installer_Error( Installer_Error_Codes::MISSING_UNPACKED_PATH );
}
$this->state->set_unpacked_path( $unpacked_path );

View File

@@ -0,0 +1,78 @@
<?php
/**
* WCCOM Site Base REST API Controller
*
* Handles requests to /ssr.
*
* @package WooCommerce\WCCom\API
* @since 8.6.0
*/
use WC_REST_WCCOM_Site_Installer_Error_Codes as Installer_Error_Codes;
use WC_REST_WCCOM_Site_Installer_Error as Installer_Error;
defined( 'ABSPATH' ) || exit;
/**
* REST API WCCOM Site Base REST API Controller Astract Class.
*
* @extends WC_REST_Controller
*/
abstract class WC_REST_WCCOM_Site_Controller extends WC_REST_Controller {
/**
* Endpoint namespace.
*
* @var string
*/
protected $namespace = 'wccom-site/v3';
/**
* Check whether user has permission to access controller's endpoints.
*
* @since 8.6.0
* @param WP_USER $user User object.
* @return bool
*/
abstract protected function user_has_permission( $user ) : bool;
/**
* Check permissions.
*
* Please note that access to this endpoint is also governed by the WC_WCCOM_Site::authenticate_wccom() method.
*
* @since 7.8.0
* @return bool|WP_Error
*/
public function check_permission() {
$current_user = wp_get_current_user();
if ( empty( $current_user ) || ( $current_user instanceof WP_User && ! $current_user->exists() ) ) {
/**
* This filter allows to provide a custom error message when the user is not authenticated.
*
* @since 3.7.0
*/
$error = apply_filters(
WC_WCCOM_Site::AUTH_ERROR_FILTER_NAME,
new Installer_Error( Installer_Error_Codes::NOT_AUTHENTICATED )
);
return new WP_Error(
$error->get_error_code(),
$error->get_error_message(),
array( 'status' => $error->get_http_code() )
);
}
if ( ! $this->user_has_permission( $current_user ) ) {
$error = new Installer_Error( Installer_Error_Codes::NO_PERMISSION );
return new WP_Error(
$error->get_error_code(),
$error->get_error_message(),
array( 'status' => $error->get_http_code() )
);
}
return true;
}
}

View File

@@ -1,234 +0,0 @@
<?php
/**
* WCCOM Site Installer REST API Controller Version 2
*
* Handles requests to /installer.
*
* @package WooCommerce\WCCom\API
* @since 7.7.0
*/
use WC_REST_WCCOM_Site_Installer_Error_Codes as Installer_Error_Codes;
use WC_REST_WCCOM_Site_Installer_Error as Installer_Error;
defined( 'ABSPATH' ) || exit;
/**
* REST API WCCOM Site Installer Controller Class.
*
* @extends WC_REST_Controller
*/
class WC_REST_WCCOM_Site_Installer_Controller_V2 extends WC_REST_Controller {
/**
* Endpoint namespace.
*
* @var string
*/
protected $namespace = 'wccom-site/v2';
/**
* Route base.
*
* @var string
*/
protected $rest_base = 'installer';
/**
* Register the routes for product reviews.
*
* @since 7.7.0
*/
public function register_routes() {
register_rest_route(
$this->namespace,
'/' . $this->rest_base,
array(
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'install' ),
'permission_callback' => array( $this, 'check_permission' ),
'args' => array(
'product-id' => array(
'required' => true,
'type' => 'integer',
),
'run-until-step' => array(
'required' => true,
'type' => 'string',
'enum' => WC_WCCOM_Site_Installation_Manager::STEPS,
),
'idempotency-key' => array(
'required' => true,
'type' => 'string',
),
),
),
array(
'methods' => WP_REST_Server::DELETABLE,
'callback' => array( $this, 'reset_install' ),
'permission_callback' => array( $this, 'check_permission' ),
'args' => array(
'product-id' => array(
'required' => true,
'type' => 'integer',
),
'idempotency-key' => array(
'required' => true,
'type' => 'string',
),
),
),
)
);
}
/**
* Check permissions.
*
* @since 7.7.0
* @param WP_REST_Request $request Full details about the request.
* @return bool|WP_Error
*/
public function check_permission( $request ) {
$current_user = wp_get_current_user();
if ( empty( $current_user ) || ( $current_user instanceof WP_User && ! $current_user->exists() ) ) {
/**
* This filter allows to provide a custom error message when the user is not authenticated.
*
* @since 3.7.0
*/
$error = apply_filters(
WC_WCCOM_Site::AUTH_ERROR_FILTER_NAME,
new Installer_Error( Installer_Error_Codes::NOT_AUTHENTICATED )
);
return new WP_Error(
$error->get_error_code(),
$error->get_error_message(),
array( 'status' => $error->get_http_code() )
);
}
if ( ! user_can( $current_user, 'install_plugins' ) || ! user_can( $current_user, 'install_themes' ) ) {
$error = new Installer_Error( Installer_Error_Codes::NO_PERMISSION );
return new WP_Error(
$error->get_error_code(),
$error->get_error_message(),
array( 'status' => $error->get_http_code() )
);
}
return true;
}
/**
* Install Woo.com products.
*
* @since 7.7.0
* @param WP_REST_Request $request Full details about the request.
* @return WP_REST_Response|WP_Error
*/
public function install( $request ) {
try {
$product_id = $request['product-id'];
$run_until_step = $request['run-until-step'];
$idempotency_key = $request['idempotency-key'];
$installation_manager = new WC_WCCOM_Site_Installation_Manager( $product_id, $idempotency_key );
$installation_manager->run_installation( $run_until_step );
$response = $this->success_response( $product_id );
} catch ( Installer_Error $exception ) {
$response = $this->failure_response( $product_id, $exception );
}
return $response;
}
/**
* Reset installation state.
*
* @since 7.7.0
* @param WP_REST_Request $request Full details about the request.
* @return WP_REST_Response|WP_Error
*/
public function reset_install( $request ) {
try {
$product_id = $request['product-id'];
$idempotency_key = $request['idempotency-key'];
$installation_manager = new WC_WCCOM_Site_Installation_Manager( $product_id, $idempotency_key );
$installation_manager->reset_installation();
$response = $this->success_response( $product_id );
} catch ( Installer_Error $exception ) {
$response = $this->failure_response( $product_id, $exception );
}
return $response;
}
/**
* Generate a standardized response for a successful request.
*
* @param int $product_id Product ID.
* @return WP_REST_Response|WP_Error
*/
protected function success_response( $product_id ) {
$state = WC_WCCOM_Site_Installation_State_Storage::get_state( $product_id );
$response = rest_ensure_response(
array(
'success' => true,
'state' => $state ? $this->map_state_to_response( $state ) : null,
)
);
$response->set_status( 200 );
return $response;
}
/**
* Generate a standardized response for a failed request.
*
* @param int $product_id Product ID.
* @param Installer_Error $exception The exception.
* @return WP_REST_Response|WP_Error
*/
protected function failure_response( $product_id, $exception ) {
$state = WC_WCCOM_Site_Installation_State_Storage::get_state( $product_id );
$response = rest_ensure_response(
array(
'success' => false,
'error_code' => $exception->get_error_code(),
'error_message' => $exception->get_error_message(),
'state' => $state ? $this->map_state_to_response( $state ) : null,
)
);
$response->set_status( $exception->get_http_code() );
return $response;
}
/**
* Map the installation state to a response.
*
* @param WC_WCCOM_Site_Installation_State $state The installation state.
* @return array
*/
protected function map_state_to_response( $state ) {
return array(
'product_id' => $state->get_product_id(),
'idempotency_key' => $state->get_idempotency_key(),
'last_step_name' => $state->get_last_step_name(),
'last_step_status' => $state->get_last_step_status(),
'last_step_error' => $state->get_last_step_error(),
'product_type' => $state->get_product_type(),
'product_name' => $state->get_product_name(),
'already_installed_plugin_info' => $state->get_already_installed_plugin_info(),
'started_seconds_ago' => time() - $state->get_started_date(),
);
}
}

View File

@@ -1,14 +1,13 @@
<?php
/**
* WCCOM Site Installer REST API Controller
* WCCOM Site Installer REST API Controller Version
*
* Handles requests to /installer.
*
* @package WooCommerce\WCCom\API
* @since 3.7.0
* @since 7.7.0
*/
use WC_REST_WCCOM_Site_Installer_Error_Codes as Installer_Error_Codes;
use WC_REST_WCCOM_Site_Installer_Error as Installer_Error;
defined( 'ABSPATH' ) || exit;
@@ -16,16 +15,9 @@ defined( 'ABSPATH' ) || exit;
/**
* REST API WCCOM Site Installer Controller Class.
*
* @extends WC_REST_Controller
* @extends WC_REST_WCCOM_Site_Controller
*/
class WC_REST_WCCOM_Site_Installer_Controller extends WC_REST_Controller {
/**
* Endpoint namespace.
*
* @var string
*/
protected $namespace = 'wccom-site/v1';
class WC_REST_WCCOM_Site_Installer_Controller extends WC_REST_WCCOM_Site_Controller {
/**
* Route base.
@@ -35,159 +27,179 @@ class WC_REST_WCCOM_Site_Installer_Controller extends WC_REST_Controller {
protected $rest_base = 'installer';
/**
* Register the routes for WCCCOM Installer Controller.
* Register the routes for plugin auto-installer.
*
* @since 3.7.0
* @since 7.7.0
*/
public function register_routes() {
register_rest_route(
$this->namespace,
'/' . $this->rest_base,
array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_install_state' ),
'permission_callback' => array( $this, 'check_permission' ),
),
array(
'methods' => WP_REST_Server::CREATABLE,
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'install' ),
'permission_callback' => array( $this, 'check_permission' ),
'args' => array(
'products' => array(
'product-id' => array(
'required' => true,
'type' => 'object',
'type' => 'integer',
),
'run-until-step' => array(
'required' => true,
'type' => 'string',
'enum' => WC_WCCOM_Site_Installation_Manager::STEPS,
),
'idempotency-key' => array(
'required' => true,
'type' => 'string',
),
),
),
)
);
register_rest_route(
$this->namespace,
'/' . $this->rest_base . '/reset',
array(
array(
'methods' => WP_REST_Server::DELETABLE,
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'reset_install' ),
'permission_callback' => array( $this, 'check_permission' ),
'args' => array(
'product-id' => array(
'required' => true,
'type' => 'integer',
),
'idempotency-key' => array(
'required' => true,
'type' => 'string',
),
),
),
)
);
}
/**
* Check permissions.
* Check whether user has permission to access controller's endpoints.
*
* @since 3.7.0
* @param WP_REST_Request $request Full details about the request.
* @return bool|WP_Error
* @since 8.6.0
* @param WP_USER $user User object.
* @return bool
*/
public function check_permission( $request ) {
$current_user = wp_get_current_user();
if ( empty( $current_user ) || ( $current_user instanceof WP_User && ! $current_user->exists() ) ) {
/**
* This filter allows to provide a custom error message when the user is not authenticated.
*
* @since 3.7.0
*/
$error = apply_filters(
WC_WCCOM_Site::AUTH_ERROR_FILTER_NAME,
new Installer_Error( Installer_Error_Codes::NOT_AUTHENTICATED )
);
return new WP_Error(
$error->get_error_code(),
$error->get_error_message(),
array( 'status' => $error->get_http_code() )
);
}
if ( ! user_can( $current_user, 'install_plugins' ) || ! user_can( $current_user, 'install_themes' ) ) {
$error = new Installer_Error( Installer_Error_Codes::NO_PERMISSION );
return new WP_Error(
$error->get_error_code(),
$error->get_error_message(),
array( 'status' => $error->get_http_code() )
);
}
return true;
}
/**
* Get installation state.
*
* @since 3.7.0
* @param WP_REST_Request $request Full details about the request.
* @return bool|WP_Error
*/
public function get_install_state( $request ) {
$requirements_met = WC_WCCOM_Site_Installer_Requirements_Check::met_requirements();
if ( is_wp_error( $requirements_met ) ) {
return $requirements_met;
}
return rest_ensure_response( WC_WCCOM_Site_Installer::get_state() );
public function user_has_permission( $user ) : bool {
return user_can( $user, 'install_plugins' ) && user_can( $user, 'install_themes' );
}
/**
* Install Woo.com products.
*
* @since 3.7.0
* @since 7.7.0
* @param WP_REST_Request $request Full details about the request.
* @return bool|WP_Error
* @return WP_REST_Response|WP_Error
*/
public function install( $request ) {
$requirements_met = WC_WCCOM_Site_Installer_Requirements_Check::met_requirements();
if ( is_wp_error( $requirements_met ) ) {
return $requirements_met;
try {
$product_id = $request['product-id'];
$run_until_step = $request['run-until-step'];
$idempotency_key = $request['idempotency-key'];
$installation_manager = new WC_WCCOM_Site_Installation_Manager( $product_id, $idempotency_key );
$installation_manager->run_installation( $run_until_step );
$response = $this->success_response( $product_id );
} catch ( Installer_Error $exception ) {
$response = $this->failure_response( $product_id, $exception );
}
if ( empty( $request['products'] ) ) {
return new WP_Error( 'missing_products', __( 'Missing products in request body.', 'woocommerce' ), array( 'status' => 400 ) );
}
$validation_result = $this->validate_products( $request['products'] );
if ( is_wp_error( $validation_result ) ) {
return $validation_result;
}
return rest_ensure_response( WC_WCCOM_Site_Installer::schedule_install( $request['products'] ) );
return $response;
}
/**
* Reset installation state.
*
* @since 3.7.0
* @since 7.7.0
* @param WP_REST_Request $request Full details about the request.
* @return bool|WP_Error
* @return WP_REST_Response|WP_Error
*/
public function reset_install( $request ) {
$resp = rest_ensure_response( WC_WCCOM_Site_Installer::reset_state() );
$resp->set_status( 204 );
try {
$product_id = $request['product-id'];
$idempotency_key = $request['idempotency-key'];
return $resp;
$installation_manager = new WC_WCCOM_Site_Installation_Manager( $product_id, $idempotency_key );
$installation_manager->reset_installation();
$response = $this->success_response( $product_id );
} catch ( Installer_Error $exception ) {
$response = $this->failure_response( $product_id, $exception );
}
return $response;
}
/**
* Validate products from request body.
* Generate a standardized response for a successful request.
*
* @since 3.7.0
* @param array $products Array of products where key is product ID and
* element is install args.
* @return bool|WP_Error
* @param int $product_id Product ID.
* @return WP_REST_Response|WP_Error
*/
protected function validate_products( $products ) {
$err = new WP_Error( 'invalid_products', __( 'Invalid products in request body.', 'woocommerce' ), array( 'status' => 400 ) );
protected function success_response( $product_id ) {
$state = WC_WCCOM_Site_Installation_State_Storage::get_state( $product_id );
$response = rest_ensure_response(
array(
'success' => true,
'state' => $state ? $this->map_state_to_response( $state ) : null,
)
);
$response->set_status( 200 );
return $response;
}
if ( ! is_array( $products ) ) {
return $err;
}
/**
* Generate a standardized response for a failed request.
*
* @param int $product_id Product ID.
* @param Installer_Error $exception The exception.
* @return WP_REST_Response|WP_Error
*/
protected function failure_response( $product_id, $exception ) {
$state = WC_WCCOM_Site_Installation_State_Storage::get_state( $product_id );
$response = rest_ensure_response(
array(
'success' => false,
'error_code' => $exception->get_error_code(),
'error_message' => $exception->get_error_message(),
'state' => $state ? $this->map_state_to_response( $state ) : null,
)
);
$response->set_status( $exception->get_http_code() );
return $response;
}
foreach ( $products as $product_id => $install_args ) {
if ( ! absint( $product_id ) ) {
return $err;
}
if ( empty( $install_args ) || ! is_array( $install_args ) ) {
return $err;
}
}
return true;
/**
* Map the installation state to a response.
*
* @param WC_WCCOM_Site_Installation_State $state The installation state.
* @return array
*/
protected function map_state_to_response( $state ) {
return array(
'product_id' => $state->get_product_id(),
'idempotency_key' => $state->get_idempotency_key(),
'last_step_name' => $state->get_last_step_name(),
'last_step_status' => $state->get_last_step_status(),
'last_step_error' => $state->get_last_step_error(),
'product_type' => $state->get_product_type(),
'product_name' => $state->get_product_name(),
'already_installed_plugin_info' => $state->get_already_installed_plugin_info(),
'started_seconds_ago' => time() - $state->get_started_date(),
);
}
}

View File

@@ -8,24 +8,14 @@
* @since 7.8.0
*/
use WC_REST_WCCOM_Site_Installer_Error_Codes as Installer_Error_Codes;
use WC_REST_WCCOM_Site_Installer_Error as Installer_Error;
defined( 'ABSPATH' ) || exit;
/**
* REST API WCCOM System Status Report Controller Class.
*
* @extends WC_REST_Controller
* @extends WC_REST_WCCOM_Site_Controller
*/
class WC_REST_WCCOM_Site_SSR_Controller extends WC_REST_Controller {
/**
* Endpoint namespace.
*
* @var string
*/
protected $namespace = 'wccom-site/v1';
class WC_REST_WCCOM_Site_SSR_Controller extends WC_REST_WCCOM_Site_Controller {
/**
* Route base.
@@ -54,44 +44,14 @@ class WC_REST_WCCOM_Site_SSR_Controller extends WC_REST_Controller {
}
/**
* Check permissions.
* Check whether user has permission to access controller's endpoints.
*
* Please note that access to this endpoint is also governed by the WC_WCCOM_Site::authenticate_wccom() method.
*
* @since 7.8.0
* @param WP_REST_Request $request Full details about the request.
* @return bool|WP_Error
* @since 8.6.0
* @param WP_USER $user User object.
* @return bool
*/
public function check_permission( $request ) {
$current_user = wp_get_current_user();
if ( empty( $current_user ) || ( $current_user instanceof WP_User && ! $current_user->exists() ) ) {
/**
* This filter allows to provide a custom error message when the user is not authenticated.
*
* @since 7.8.0
*/
$error = apply_filters(
WC_WCCOM_Site::AUTH_ERROR_FILTER_NAME,
new Installer_Error( Installer_Error_Codes::NOT_AUTHENTICATED )
);
return new WP_Error(
$error->get_error_code(),
$error->get_error_message(),
array( 'status' => $error->get_http_code() )
);
}
if ( ! user_can( $current_user, 'manage_woocommerce' ) ) {
$error = new Installer_Error( Installer_Error_Codes::NO_PERMISSION );
return new WP_Error(
$error->get_error_code(),
$error->get_error_message(),
array( 'status' => $error->get_http_code() )
);
}
return true;
public function user_has_permission( $user ) : bool {
return user_can( $user, 'manage_woocommerce' );
}
/**