Initial version of plugin installer version 2.
This commit is contained in:
parent
f7572b4361
commit
943e9f1869
|
@ -63,6 +63,8 @@ class WC_WCCOM_Site_Installer {
|
||||||
'activate_product',
|
'activate_product',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
private static $wp_upgrader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the product install state.
|
* Get the product install state.
|
||||||
*
|
*
|
||||||
|
@ -150,15 +152,7 @@ class WC_WCCOM_Site_Installer {
|
||||||
* element is install args.
|
* element is install args.
|
||||||
*/
|
*/
|
||||||
public static function install( $products ) {
|
public static function install( $products ) {
|
||||||
require_once ABSPATH . 'wp-admin/includes/file.php';
|
$upgrader = self::get_wp_upgrader();
|
||||||
require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
|
|
||||||
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
|
|
||||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
|
||||||
|
|
||||||
WP_Filesystem();
|
|
||||||
$upgrader = new WP_Upgrader( new Automatic_Upgrader_Skin() );
|
|
||||||
$upgrader->init();
|
|
||||||
wp_clean_plugins_cache();
|
|
||||||
|
|
||||||
foreach ( $products as $product_id => $install_args ) {
|
foreach ( $products as $product_id => $install_args ) {
|
||||||
self::install_product( $product_id, $install_args, $upgrader );
|
self::install_product( $product_id, $install_args, $upgrader );
|
||||||
|
@ -424,7 +418,7 @@ class WC_WCCOM_Site_Installer {
|
||||||
* @param int $product_id Product ID.
|
* @param int $product_id Product ID.
|
||||||
* @return \WP_Error|null
|
* @return \WP_Error|null
|
||||||
*/
|
*/
|
||||||
private static function activate_plugin( $product_id ) {
|
public static function activate_plugin( $product_id ) {
|
||||||
// Clear plugins cache used in `WC_Helper::get_local_woo_plugins`.
|
// Clear plugins cache used in `WC_Helper::get_local_woo_plugins`.
|
||||||
wp_clean_plugins_cache();
|
wp_clean_plugins_cache();
|
||||||
$filename = false;
|
$filename = false;
|
||||||
|
@ -520,7 +514,7 @@ class WC_WCCOM_Site_Installer {
|
||||||
* @param string $dir Directory name of the plugin.
|
* @param string $dir Directory name of the plugin.
|
||||||
* @return bool|string
|
* @return bool|string
|
||||||
*/
|
*/
|
||||||
private static function get_wporg_plugin_main_file( $dir ) {
|
public static function get_wporg_plugin_main_file( $dir ) {
|
||||||
// Ensure that exact dir name is used.
|
// Ensure that exact dir name is used.
|
||||||
$dir = trailingslashit( $dir );
|
$dir = trailingslashit( $dir );
|
||||||
|
|
||||||
|
@ -546,7 +540,7 @@ class WC_WCCOM_Site_Installer {
|
||||||
* @param string $dir Directory name of the plugin.
|
* @param string $dir Directory name of the plugin.
|
||||||
* @return bool|array
|
* @return bool|array
|
||||||
*/
|
*/
|
||||||
private static function get_plugin_info( $dir ) {
|
public static function get_plugin_info( $dir ) {
|
||||||
$plugin_folder = basename( $dir );
|
$plugin_folder = basename( $dir );
|
||||||
|
|
||||||
if ( ! function_exists( 'get_plugins' ) ) {
|
if ( ! function_exists( 'get_plugins' ) ) {
|
||||||
|
@ -574,4 +568,22 @@ class WC_WCCOM_Site_Installer {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function get_wp_upgrader() {
|
||||||
|
if (!empty(self::$wp_upgrader)) {
|
||||||
|
return self::$wp_upgrader;
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once ABSPATH . 'wp-admin/includes/file.php';
|
||||||
|
require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
|
||||||
|
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
|
||||||
|
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||||
|
|
||||||
|
WP_Filesystem();
|
||||||
|
self::$wp_upgrader = new WP_Upgrader( new Automatic_Upgrader_Skin() );
|
||||||
|
self::$wp_upgrader->init();
|
||||||
|
wp_clean_plugins_cache();
|
||||||
|
|
||||||
|
return self::$wp_upgrader;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
* @since 3.7.0
|
* @since 3.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;
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -63,11 +66,7 @@ class WC_WCCOM_Site {
|
||||||
add_filter(
|
add_filter(
|
||||||
self::AUTH_ERROR_FILTER_NAME,
|
self::AUTH_ERROR_FILTER_NAME,
|
||||||
function() {
|
function() {
|
||||||
return new WP_Error(
|
return new Installer_Error( Installer_Error_Codes::NO_ACCESS_TOKEN );
|
||||||
WC_REST_WCCOM_Site_Installer_Errors::NO_ACCESS_TOKEN_CODE,
|
|
||||||
WC_REST_WCCOM_Site_Installer_Errors::NO_ACCESS_TOKEN_MESSAGE,
|
|
||||||
array( 'status' => WC_REST_WCCOM_Site_Installer_Errors::NO_ACCESS_TOKEN_HTTP_CODE )
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
|
@ -81,11 +80,7 @@ class WC_WCCOM_Site {
|
||||||
add_filter(
|
add_filter(
|
||||||
self::AUTH_ERROR_FILTER_NAME,
|
self::AUTH_ERROR_FILTER_NAME,
|
||||||
function() {
|
function() {
|
||||||
return new WP_Error(
|
return new Installer_Error( Installer_Error_Codes::NO_SIGNATURE );
|
||||||
WC_REST_WCCOM_Site_Installer_Errors::NO_SIGNATURE_CODE,
|
|
||||||
WC_REST_WCCOM_Site_Installer_Errors::NO_SIGNATURE_MESSAGE,
|
|
||||||
array( 'status' => WC_REST_WCCOM_Site_Installer_Errors::NO_SIGNATURE_HTTP_CODE )
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
|
@ -98,11 +93,7 @@ class WC_WCCOM_Site {
|
||||||
add_filter(
|
add_filter(
|
||||||
self::AUTH_ERROR_FILTER_NAME,
|
self::AUTH_ERROR_FILTER_NAME,
|
||||||
function() {
|
function() {
|
||||||
return new WP_Error(
|
return new Installer_Error( Installer_Error_Codes::SITE_NOT_CONNECTED );
|
||||||
WC_REST_WCCOM_Site_Installer_Errors::SITE_NOT_CONNECTED_CODE,
|
|
||||||
WC_REST_WCCOM_Site_Installer_Errors::SITE_NOT_CONNECTED_MESSAGE,
|
|
||||||
array( 'status' => WC_REST_WCCOM_Site_Installer_Errors::SITE_NOT_CONNECTED_HTTP_CODE )
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
|
@ -112,11 +103,7 @@ class WC_WCCOM_Site {
|
||||||
add_filter(
|
add_filter(
|
||||||
self::AUTH_ERROR_FILTER_NAME,
|
self::AUTH_ERROR_FILTER_NAME,
|
||||||
function() {
|
function() {
|
||||||
return new WP_Error(
|
return new Installer_Error( Installer_Error_Codes::INVALID_TOKEN );
|
||||||
WC_REST_WCCOM_Site_Installer_Errors::INVALID_TOKEN_CODE,
|
|
||||||
WC_REST_WCCOM_Site_Installer_Errors::INVALID_TOKEN_MESSAGE,
|
|
||||||
array( 'status' => WC_REST_WCCOM_Site_Installer_Errors::INVALID_TOKEN_HTTP_CODE )
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
|
@ -128,11 +115,7 @@ class WC_WCCOM_Site {
|
||||||
add_filter(
|
add_filter(
|
||||||
self::AUTH_ERROR_FILTER_NAME,
|
self::AUTH_ERROR_FILTER_NAME,
|
||||||
function() {
|
function() {
|
||||||
return new WP_Error(
|
return new Installer_Error( Installer_Error_Codes::REQUEST_VERIFICATION_FAILED );
|
||||||
WC_REST_WCCOM_Site_Installer_Errors::REQUEST_VERIFICATION_FAILED_CODE,
|
|
||||||
WC_REST_WCCOM_Site_Installer_Errors::REQUEST_VERIFICATION_FAILED_MESSAGE,
|
|
||||||
array( 'status' => WC_REST_WCCOM_Site_Installer_Errors::REQUEST_VERIFICATION_FAILED_HTTP_CODE )
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
|
@ -143,11 +126,7 @@ class WC_WCCOM_Site {
|
||||||
add_filter(
|
add_filter(
|
||||||
self::AUTH_ERROR_FILTER_NAME,
|
self::AUTH_ERROR_FILTER_NAME,
|
||||||
function() {
|
function() {
|
||||||
return new WP_Error(
|
return new Installer_Error( Installer_Error_Codes::USER_NOT_FOUND );
|
||||||
WC_REST_WCCOM_Site_Installer_Errors::USER_NOT_FOUND_CODE,
|
|
||||||
WC_REST_WCCOM_Site_Installer_Errors::USER_NOT_FOUND_MESSAGE,
|
|
||||||
array( 'status' => WC_REST_WCCOM_Site_Installer_Errors::USER_NOT_FOUND_HTTP_CODE )
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
|
@ -239,13 +218,31 @@ class WC_WCCOM_Site {
|
||||||
* @return array Registered namespaces.
|
* @return array Registered namespaces.
|
||||||
*/
|
*/
|
||||||
public static function register_rest_namespace( $namespaces ) {
|
public static function register_rest_namespace( $namespaces ) {
|
||||||
require_once WC_ABSPATH . 'includes/wccom-site/rest-api/class-wc-rest-wccom-site-installer-errors.php';
|
|
||||||
require_once WC_ABSPATH . 'includes/wccom-site/rest-api/endpoints/class-wc-rest-wccom-site-installer-controller.php';
|
|
||||||
|
|
||||||
$namespaces['wccom-site/v1'] = array(
|
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/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/installation/class-wc-wccom-site-installation-state.php';
|
||||||
|
require_once WC_ABSPATH . 'includes/wccom-site/installation/class-wc-wccom-site-installation-state-storage.php';
|
||||||
|
require_once WC_ABSPATH . 'includes/wccom-site/installation/class-wc-wccom-site-installation-manager.php';
|
||||||
|
|
||||||
|
require_once WC_ABSPATH . 'includes/wccom-site/installation/installation-steps/interface-installaton-step.php';
|
||||||
|
require_once WC_ABSPATH . 'includes/wccom-site/installation/installation-steps/class-installation-step-product-info.php';
|
||||||
|
require_once WC_ABSPATH . 'includes/wccom-site/installation/installation-steps/class-installation-step-download-product.php';
|
||||||
|
require_once WC_ABSPATH . 'includes/wccom-site/installation/installation-steps/class-installation-step-unpack-product.php';
|
||||||
|
require_once WC_ABSPATH . 'includes/wccom-site/installation/installation-steps/class-installation-step-move-product.php';
|
||||||
|
require_once WC_ABSPATH . 'includes/wccom-site/installation/installation-steps/class-installation-step-activate-product.php';
|
||||||
|
|
||||||
|
$namespaces['wccom-site/v1'] = array(
|
||||||
'installer' => 'WC_REST_WCCOM_Site_Installer_Controller',
|
'installer' => 'WC_REST_WCCOM_Site_Installer_Controller',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$namespaces['wccom-site/v2'] = array(
|
||||||
|
'installer' => 'WC_REST_WCCOM_Site_Installer_Controller_V2',
|
||||||
|
);
|
||||||
|
|
||||||
return $namespaces;
|
return $namespaces;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
class WC_WCCOM_Site_Installation_Manager {
|
||||||
|
|
||||||
|
const STEPS = [
|
||||||
|
'get_product_info',
|
||||||
|
'download_product',
|
||||||
|
'unpack_product',
|
||||||
|
'move_product',
|
||||||
|
'activate_product',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function __construct(int $product_id, string $idempotency_key) {
|
||||||
|
$this->product_id = $product_id;
|
||||||
|
$this->idempotency_key = $idempotency_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run_installation(string $run_until_step) : bool {
|
||||||
|
$state = WC_WCCOM_Site_Installation_State_Storage::get_state( $this->product_id );
|
||||||
|
|
||||||
|
if ( $state && $state->get_idempotency_key() !== $this->idempotency_key ) {
|
||||||
|
throw new Installer_Error(Installer_Error_Codes::IDEMPOTENCY_KEY_MISMATCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! $state ) {
|
||||||
|
$state = WC_WCCOM_Site_Installation_State::initiate_new( $this->product_id, $this->idempotency_key );
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->can_run_installation( $run_until_step, $state );
|
||||||
|
|
||||||
|
$next_step = $this->get_next_step($state);
|
||||||
|
$installation_steps = $this->get_installation_steps( $next_step, $run_until_step );
|
||||||
|
|
||||||
|
array_walk($installation_steps, function($step_name) use ($state) {
|
||||||
|
$this->run_step($step_name, $state);
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reset_installation() : bool {
|
||||||
|
$state = WC_WCCOM_Site_Installation_State_Storage::get_state( $this->product_id );
|
||||||
|
|
||||||
|
if ( ! $state ) {
|
||||||
|
throw new Installer_Error( Installer_Error_Codes::NO_INITIATED_INSTALLATION_FOUND );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $state->get_idempotency_key() !== $this->idempotency_key ) {
|
||||||
|
throw new Installer_Error(Installer_Error_Codes::IDEMPOTENCY_KEY_MISMATCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = WC_WCCOM_Site_Installation_State_Storage::delete_state( $state );
|
||||||
|
if ( ! $result) {
|
||||||
|
throw new Installer_Error(Installer_Error_Codes::FAILED_TO_RESET_INSTALLATION_STATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function can_run_installation( $run_until_step, $state ) {
|
||||||
|
// if ( $this->already_installed( $product_id )) {
|
||||||
|
// throw new Installer_Error(Installer_Error_Codes::PLUGIN_ALREADY_INSTALLED_MESSAGE );
|
||||||
|
// }
|
||||||
|
|
||||||
|
if ( $state->get_last_step_status() === \WC_WCCOM_Site_Installation_State::STEP_STATUS_IN_PROGRESS) {
|
||||||
|
throw new Installer_Error(Installer_Error_Codes::INSTALLATION_ALREADY_RUNNING );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $state->get_last_step_status() === \WC_WCCOM_Site_Installation_State::STEP_STATUS_FAILED) {
|
||||||
|
throw new Installer_Error(Installer_Error_Codes::INSTALLATION_FAILED );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $state->get_last_step_name() === self::STEPS[ count( self::STEPS ) - 1 ] ) {
|
||||||
|
throw new Installer_Error(Installer_Error_Codes::ALL_INSTALLATION_STEPS_RUN );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array_search($state->get_last_step_name(), self::STEPS) >= array_search($run_until_step, self::STEPS)) {
|
||||||
|
throw new Installer_Error(Installer_Error_Codes::REQUESTED_STEP_ALREADY_RUN );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! is_writable( WP_CONTENT_DIR ) ) {
|
||||||
|
throw new Installer_Error(Installer_Error_Codes::FILESYSTEM_REQUIREMENTS_NOT_MET );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function get_next_step($state) : string {
|
||||||
|
$last_executed_step = $state->get_last_step_name();
|
||||||
|
|
||||||
|
if (! $last_executed_step) {
|
||||||
|
return self::STEPS[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
$last_executed_step_index = array_search($last_executed_step, self::STEPS);
|
||||||
|
|
||||||
|
return self::STEPS[ $last_executed_step_index + 1 ];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function get_installation_steps(string $start_step, string $end_step) {
|
||||||
|
$start_step_offset = array_search($start_step, self::STEPS);
|
||||||
|
$end_step_index = array_search($end_step, self::STEPS);
|
||||||
|
$length = $end_step_index - $start_step_offset + 1;
|
||||||
|
|
||||||
|
return array_slice(self::STEPS, $start_step_offset, $length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected function run_step($step_name, $state ) {
|
||||||
|
$state->initiate_step( $step_name );
|
||||||
|
WC_WCCOM_Site_Installation_State_Storage::save_state( $state );
|
||||||
|
|
||||||
|
try {
|
||||||
|
$class_name = "WC_WCCOM_Site_Installation_Step_$step_name";
|
||||||
|
$current_step = new $class_name( $state );
|
||||||
|
$current_step->run();
|
||||||
|
|
||||||
|
} catch(Installer_Error $exception) {
|
||||||
|
|
||||||
|
$state->capture_failure( $step_name, $exception->get_error_code() );
|
||||||
|
WC_WCCOM_Site_Installation_State_Storage::save_state( $state );
|
||||||
|
|
||||||
|
throw $exception;
|
||||||
|
|
||||||
|
} catch (Throwable $error) {
|
||||||
|
|
||||||
|
$state->capture_failure( $step_name, Installer_Error_Codes::UNEXPECTED_ERROR );
|
||||||
|
WC_WCCOM_Site_Installation_State_Storage::save_state( $state );
|
||||||
|
|
||||||
|
throw new Installer_Error(Installer_Error_Codes::UNEXPECTED_ERROR, $error->getMessage() );
|
||||||
|
}
|
||||||
|
|
||||||
|
$state->complete_step( $step_name );
|
||||||
|
WC_WCCOM_Site_Installation_State_Storage::save_state( $state );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
class WC_WCCOM_Site_Installation_State_Storage {
|
||||||
|
|
||||||
|
public static function get_state( $product_id ) : ?WC_WCCOM_Site_Installation_State {
|
||||||
|
$storage_key = self::get_storage_key( $product_id );
|
||||||
|
$data = get_option($storage_key);
|
||||||
|
|
||||||
|
if ( ! is_array( $data ) ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$installation_state = WC_WCCOM_Site_Installation_State::initiate_existing(
|
||||||
|
$product_id,
|
||||||
|
$data['idempotency_key'],
|
||||||
|
$data['last_step_name'],
|
||||||
|
$data['last_step_status'],
|
||||||
|
$data['last_step_error'],
|
||||||
|
$data['started_date']
|
||||||
|
);
|
||||||
|
|
||||||
|
$installation_state->set_product_type( $data['product_type'] ?? null );
|
||||||
|
$installation_state->set_product_name( $data['product_name'] ?? null);
|
||||||
|
$installation_state->set_download_url( $data['download_url'] ?? null);
|
||||||
|
$installation_state->set_download_path( $data['download_path'] ?? null);
|
||||||
|
$installation_state->set_unpacked_path( $data['unpacked_path'] ?? null);
|
||||||
|
$installation_state->set_installed_path( $data['installed_path'] ?? null );
|
||||||
|
$installation_state->set_already_installed_plugin_info( $data['already_installed_plugin_info'] ?? null );
|
||||||
|
|
||||||
|
return $installation_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function save_state( WC_WCCOM_Site_Installation_State $state) : bool {
|
||||||
|
$storage_key = self::get_storage_key( $state->get_product_id() );
|
||||||
|
|
||||||
|
return update_option($storage_key, [
|
||||||
|
'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(),
|
||||||
|
'download_url' => $state->get_download_url(),
|
||||||
|
'download_path' => $state->get_download_path(),
|
||||||
|
'unpacked_path' => $state->get_unpacked_path(),
|
||||||
|
'installed_path' => $state->get_installed_path(),
|
||||||
|
'already_installed_plugin_info' => $state->get_already_installed_plugin_info(),
|
||||||
|
'started_date' => $state->get_started_date(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function delete_state( WC_WCCOM_Site_Installation_State $state ) : bool {
|
||||||
|
$storage_key = self::get_storage_key( $state->get_product_id() );
|
||||||
|
|
||||||
|
return delete_option($storage_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function get_storage_key( $product_id ) : string {
|
||||||
|
return sprintf('wccom-product-installation-state-%d', $product_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
class WC_WCCOM_Site_Installation_State {
|
||||||
|
protected $product_id;
|
||||||
|
protected $idempotency_key;
|
||||||
|
protected $last_step_name;
|
||||||
|
protected $last_step_status;
|
||||||
|
protected $last_step_error;
|
||||||
|
|
||||||
|
protected $product_type;
|
||||||
|
protected $product_name;
|
||||||
|
protected $download_url;
|
||||||
|
protected $download_path;
|
||||||
|
protected $unpacked_path;
|
||||||
|
protected $installed_path;
|
||||||
|
protected $already_installed_plugin_info;
|
||||||
|
|
||||||
|
protected $started_date;
|
||||||
|
const STEP_STATUS_IN_PROGRESS = 'in-progress';
|
||||||
|
const STEP_STATUS_FAILED = 'failed';
|
||||||
|
const STEP_STATUS_COMPLETED = 'completed';
|
||||||
|
|
||||||
|
protected function __construct( $product_id ) {
|
||||||
|
$this->product_id = $product_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function initiate_existing( $product_id, $idempotency_key, $last_step_name, $last_step_status, $last_step_error, $started_date ) {
|
||||||
|
$instance = new self( $product_id );
|
||||||
|
$instance->idempotency_key = $idempotency_key;
|
||||||
|
$instance->last_step_name = $last_step_name;
|
||||||
|
$instance->last_step_status = $last_step_status;
|
||||||
|
$instance->last_step_error = $last_step_error;
|
||||||
|
$instance->started_date = $started_date;
|
||||||
|
|
||||||
|
return $instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function initiate_new( $product_id, $idempotency_key ) {
|
||||||
|
$instance = new self( $product_id );
|
||||||
|
$instance->idempotency_key = $idempotency_key;
|
||||||
|
$instance->started_date = time();
|
||||||
|
|
||||||
|
return $instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_product_id() {
|
||||||
|
return $this->product_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_idempotency_key() {
|
||||||
|
return $this->idempotency_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_last_step_name() {
|
||||||
|
return $this->last_step_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_last_step_status( ) {
|
||||||
|
return $this->last_step_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_last_step_error( ) {
|
||||||
|
return $this->last_step_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function initiate_step( $step_name ) {
|
||||||
|
$this->last_step_name = $step_name;
|
||||||
|
$this->last_step_status = self::STEP_STATUS_IN_PROGRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function complete_step( $step_name ) {
|
||||||
|
$this->last_step_name = $step_name;
|
||||||
|
$this->last_step_status = self::STEP_STATUS_COMPLETED;
|
||||||
|
}
|
||||||
|
public function capture_failure( $step_name, $error_code ) {
|
||||||
|
$this->last_step_name = $step_name;
|
||||||
|
$this->last_step_error = $error_code;
|
||||||
|
$this->last_step_status = self::STEP_STATUS_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_product_type() {
|
||||||
|
return $this->product_type;
|
||||||
|
}
|
||||||
|
public function set_product_type( $product_type ) {
|
||||||
|
$this->product_type = $product_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_product_name() {
|
||||||
|
return $this->product_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set_product_name( $product_name ) {
|
||||||
|
$this->product_name = $product_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_download_url() {
|
||||||
|
return $this->download_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set_download_url( $download_url ) {
|
||||||
|
$this->download_url = $download_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_download_path( ) {
|
||||||
|
return $this->download_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set_download_path( $download_path ) {
|
||||||
|
$this->download_path = $download_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_unpacked_path() {
|
||||||
|
return $this->unpacked_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set_unpacked_path( $unpacked_path ) {
|
||||||
|
$this->unpacked_path = $unpacked_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_installed_path( ) {
|
||||||
|
return $this->installed_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set_installed_path( $installed_path ) {
|
||||||
|
$this->installed_path = $installed_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_already_installed_plugin_info() {
|
||||||
|
return $this->already_installed_plugin_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set_already_installed_plugin_info( $plugin_info ) {
|
||||||
|
$this->already_installed_plugin_info = $plugin_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_started_date() {
|
||||||
|
return $this->started_date;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
class WC_WCCOM_Site_Installation_Step_Activate_Product implements WC_WCCOM_Site_Installation_Step {
|
||||||
|
public function __construct($state) {
|
||||||
|
$this->state = $state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run( ) {
|
||||||
|
$product_id = $this->state->get_product_id();
|
||||||
|
|
||||||
|
if ( 'plugin' === $this->state->get_product_type() ) {
|
||||||
|
$this->activate_plugin( $product_id );
|
||||||
|
} else {
|
||||||
|
$this->activate_theme( $product_id );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->state;
|
||||||
|
}
|
||||||
|
|
||||||
|
private 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 = $this->get_wporg_product_dir_name( );
|
||||||
|
if ( false !== $dir_name ) {
|
||||||
|
$filename = \WC_WCCOM_Site_Installer::get_wporg_plugin_main_file( $dir_name );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( false === $filename ) {
|
||||||
|
$plugins = wp_list_filter(
|
||||||
|
WC_Helper::get_local_woo_plugins(),
|
||||||
|
[
|
||||||
|
'_product_id' => $product_id,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$filename = is_array( $plugins ) && ! empty( $plugins ) ? key( $plugins ) : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( empty( $filename ) ) {
|
||||||
|
return 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() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private 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 = $this->get_wporg_product_dir_name( );
|
||||||
|
if ( false !== $dir_name ) {
|
||||||
|
$theme_slug = basename( $dir_name );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( false === $theme_slug ) {
|
||||||
|
$themes = wp_list_filter(
|
||||||
|
WC_Helper::get_local_woo_themes(),
|
||||||
|
[
|
||||||
|
'_product_id' => $product_id,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$theme_slug = is_array( $themes ) && ! empty( $themes ) ? dirname( key( $themes ) ) : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( empty( $theme_slug ) ) {
|
||||||
|
return new Installer_Error( Installer_Error_Codes::UNKNOWN_FILENAME );
|
||||||
|
}
|
||||||
|
|
||||||
|
switch_theme( $theme_slug );
|
||||||
|
}
|
||||||
|
|
||||||
|
private function get_wporg_product_dir_name( ) {
|
||||||
|
if ( empty($this->state->get_installed_path() ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether product was downloaded from WordPress.org.
|
||||||
|
$download_url = $this->state->get_download_url();
|
||||||
|
$parsed_url = wp_parse_url( $download_url );
|
||||||
|
if ( ! empty( $parsed_url['host'] ) && 'downloads.wordpress.org' !== $parsed_url['host'] ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return basename( $this->state->get_installed_path() );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
class WC_WCCOM_Site_Installation_Step_Download_Product implements WC_WCCOM_Site_Installation_Step {
|
||||||
|
public function __construct($state) {
|
||||||
|
$this->state = $state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run( ) {
|
||||||
|
$upgrader = WC_WCCOM_Site_Installer::get_wp_upgrader();
|
||||||
|
|
||||||
|
$download_path = $upgrader->download_package( $this->state->get_download_url() );
|
||||||
|
|
||||||
|
if (empty($download_path)) {
|
||||||
|
throw new Installer_Error( Installer_Error_Codes::MISSING_DOWNLOAD_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->state->set_download_path($download_path);
|
||||||
|
|
||||||
|
return $this->state;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
class WC_WCCOM_Site_Installation_Step_Move_Product implements WC_WCCOM_Site_Installation_Step {
|
||||||
|
public function __construct($state) {
|
||||||
|
$this->state = $state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run( ) {
|
||||||
|
$upgrader = WC_WCCOM_Site_Installer::get_wp_upgrader();
|
||||||
|
|
||||||
|
$destination = 'plugin' === $this->state->get_product_type()
|
||||||
|
? WP_PLUGIN_DIR
|
||||||
|
: get_theme_root();
|
||||||
|
|
||||||
|
$package = array(
|
||||||
|
'source' => $this->state->get_unpacked_path(),
|
||||||
|
'destination' => $destination,
|
||||||
|
'clear_working' => true,
|
||||||
|
'hook_extra' => array(
|
||||||
|
'type' => $this->state->get_product_type(),
|
||||||
|
'action' => 'install',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$result = $upgrader->install_package( $package );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If install package returns error 'folder_exists' treat as success.
|
||||||
|
*/
|
||||||
|
if ( is_wp_error( $result ) && array_key_exists( 'folder_exists', $result->errors ) ) {
|
||||||
|
$existing_folder_path = $result->error_data[ 'folder_exists' ];
|
||||||
|
$plugin_info = WC_WCCOM_Site_Installer::get_plugin_info( $existing_folder_path );
|
||||||
|
|
||||||
|
$this->state->set_installed_path( $existing_folder_path );
|
||||||
|
$this->state->set_already_installed_plugin_info( $plugin_info );
|
||||||
|
return $this->state;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->state->set_installed_path( $result['destination'] );
|
||||||
|
return $this->state;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
class WC_WCCOM_Site_Installation_Step_Get_Product_Info implements WC_WCCOM_Site_Installation_Step {
|
||||||
|
public function __construct($state) {
|
||||||
|
$this->state = $state;
|
||||||
|
}
|
||||||
|
public function run() {
|
||||||
|
|
||||||
|
$product_id = $this->state->get_product_id();
|
||||||
|
|
||||||
|
// Get product info from woocommerce.com.
|
||||||
|
$request = WC_Helper_API::get(
|
||||||
|
add_query_arg(
|
||||||
|
[ 'product_id' => $product_id ],
|
||||||
|
'info'
|
||||||
|
),
|
||||||
|
[
|
||||||
|
'authenticated' => true,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( 200 !== wp_remote_retrieve_response_code( $request ) ) {
|
||||||
|
throw new Installer_Error( Installer_Error_Codes::FAILED_GETTING_PRODUCT_INFO );
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = json_decode( wp_remote_retrieve_body( $request ), true );
|
||||||
|
|
||||||
|
if (!isset($result['_product_type'], $result['name'])) {
|
||||||
|
throw new Installer_Error( Installer_Error_Codes::INVALID_PRODUCT_INFO_RESPONSE );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ( ! empty( $result['_wporg_product'] )) {
|
||||||
|
$download_url = $this->get_wporg_download_url( $result );
|
||||||
|
} else {
|
||||||
|
$download_url = $this->get_wccom_download_url( $product_id );
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->state->set_product_type( $result['_product_type'] );
|
||||||
|
$this->state->set_product_name ($result['name'] );
|
||||||
|
$this->state->set_download_url ($download_url );
|
||||||
|
|
||||||
|
return $this->state;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected function get_wporg_download_url ( $data ) {
|
||||||
|
if ( empty( $data['_wporg_product'] ) ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( empty( $data['download_link'] ) ) {
|
||||||
|
throw new Installer_Error( Installer_Error_Codes::WPORG_PRODUCT_MISSING_DOWNLOAD_LINK );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data['download_link'];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function get_wccom_download_url( $product_id ) {
|
||||||
|
WC_Helper::_flush_subscriptions_cache();
|
||||||
|
|
||||||
|
if ( ! WC_Helper::has_product_subscription( $product_id ) ) {
|
||||||
|
throw new Installer_Error( Installer_Error_Codes::WCCOM_PRODUCT_MISSING_SUBSCRIPTION );
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 Installer_Error( Installer_Error_Codes::WCCOM_PRODUCT_MISSING_PACKAGE );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $updates[ $product_id ]['package'];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
class WC_WCCOM_Site_Installation_Step_Unpack_Product implements WC_WCCOM_Site_Installation_Step {
|
||||||
|
public function __construct($state) {
|
||||||
|
$this->state = $state;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->state->set_unpacked_path($unpacked_path);
|
||||||
|
|
||||||
|
return $this->state;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
interface WC_WCCOM_Site_Installation_Step {
|
||||||
|
public function __construct( $state );
|
||||||
|
|
||||||
|
public function run() ;
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WCCOM Site Installer Error Codes Class
|
||||||
|
*
|
||||||
|
* @package WooCommerce\WCCom\API
|
||||||
|
* @since 7.7.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WCCOM Site Installer Error Codes Class
|
||||||
|
*
|
||||||
|
* Stores data for errors, returned by installer API.
|
||||||
|
*/
|
||||||
|
class WC_REST_WCCOM_Site_Installer_Error_Codes {
|
||||||
|
|
||||||
|
const NOT_AUTHENTICATED = 'not_authenticated';
|
||||||
|
const NO_ACCESS_TOKEN = 'no_access_token';
|
||||||
|
const NO_SIGNATURE = 'no_signature';
|
||||||
|
const SITE_NOT_CONNECTED = 'site_not_connnected';
|
||||||
|
const INVALID_TOKEN = 'invalid_token';
|
||||||
|
const REQUEST_VERIFICATION_FAILED = 'request_verification_failed';
|
||||||
|
const USER_NOT_FOUND = 'user_not_found';
|
||||||
|
const NO_PERMISSION = 'forbidden';
|
||||||
|
const IDEMPOTENCY_KEY_MISMATCH = 'idempotency_key_mismatch';
|
||||||
|
const NO_INITIATED_INSTALLATION_FOUND = 'no_initiated_installation_found';
|
||||||
|
const ALL_INSTALLATION_STEPS_RUN = 'all_installation_steps_run';
|
||||||
|
const REQUESTED_STEP_ALREADY_RUN = 'requested_step_already_run';
|
||||||
|
const PLUGIN_ALREADY_INSTALLED = 'plugin_already_installed';
|
||||||
|
const INSTALLATION_ALREADY_RUNNING = 'installation_already_running';
|
||||||
|
const INSTALLATION_FAILED = 'installation_failed';
|
||||||
|
const FILESYSTEM_REQUIREMENTS_NOT_MET = 'filesystem_requirements_not_met';
|
||||||
|
const FAILED_GETTING_PRODUCT_INFO = 'product_info_failed';
|
||||||
|
const INVALID_PRODUCT_INFO_RESPONSE = 'invalid_product_info_response';
|
||||||
|
const WCCOM_PRODUCT_MISSING_SUBSCRIPTION = 'wccom_product_missing_subscription';
|
||||||
|
const WCCOM_PRODUCT_MISSING_PACKAGE = 'wccom_product_missing_package';
|
||||||
|
const WPORG_PRODUCT_MISSING_DOWNLOAD_LINK = 'wporg_product_missing_download_link';
|
||||||
|
const MISSING_DOWNLOAD_PATH = 'missing_download_path';
|
||||||
|
const MISSING_UNPACKED_PATH = 'missing_unpacked_path';
|
||||||
|
const UNKNOWN_FILENAME = 'unknown_filename';
|
||||||
|
const PLUGIN_ACTIVATION_ERROR = 'plugin_activation_error';
|
||||||
|
const UNEXPECTED_ERROR = 'unexpected_error';
|
||||||
|
const FAILED_TO_RESET_INSTALLATION_STATE = 'failed_to_reset_installation_state';
|
||||||
|
|
||||||
|
const ERROR_MESSAGES = [
|
||||||
|
self::NOT_AUTHENTICATED => 'Authentication required',
|
||||||
|
self::NO_ACCESS_TOKEN => 'No access token provided',
|
||||||
|
self::NO_SIGNATURE => 'No signature provided',
|
||||||
|
self::SITE_NOT_CONNECTED => 'Site not connected to WooCommerce.com',
|
||||||
|
self::INVALID_TOKEN => 'Invalid access token provided',
|
||||||
|
self::REQUEST_VERIFICATION_FAILED => 'Request verification by signature failed',
|
||||||
|
self::USER_NOT_FOUND => 'Token owning user not found',
|
||||||
|
self::NO_PERMISSION => 'You do not have permission to install plugin or theme',
|
||||||
|
self::IDEMPOTENCY_KEY_MISMATCH => 'Idempotency key mismatch',
|
||||||
|
self::NO_INITIATED_INSTALLATION_FOUND => 'No initiated installation for the product found',
|
||||||
|
self::ALL_INSTALLATION_STEPS_RUN => 'All installation steps have been run',
|
||||||
|
self::REQUESTED_STEP_ALREADY_RUN => 'Requested step has already been run',
|
||||||
|
self::PLUGIN_ALREADY_INSTALLED => 'The plugin has already been installed',
|
||||||
|
self::INSTALLATION_ALREADY_RUNNING => 'The installation of the plugin is already running',
|
||||||
|
self::INSTALLATION_FAILED => 'The installation of the plugin failed',
|
||||||
|
self::FILESYSTEM_REQUIREMENTS_NOT_MET => 'The filesystem requirements are not met',
|
||||||
|
self::FAILED_GETTING_PRODUCT_INFO => 'Failed to retrieve product info from woocommerce.com',
|
||||||
|
self::INVALID_PRODUCT_INFO_RESPONSE => 'Invalid product info response from woocommerce.com',
|
||||||
|
self::WCCOM_PRODUCT_MISSING_SUBSCRIPTION => 'Product subscription is missing',
|
||||||
|
self::WCCOM_PRODUCT_MISSING_PACKAGE => 'Could not find product package',
|
||||||
|
self::MISSING_DOWNLOAD_PATH => 'Download path is missing',
|
||||||
|
self::MISSING_UNPACKED_PATH => 'Unpacked path is missing',
|
||||||
|
self::UNKNOWN_FILENAME => 'Unknown product filename',
|
||||||
|
self::PLUGIN_ACTIVATION_ERROR => 'Plugin activation error',
|
||||||
|
self::UNEXPECTED_ERROR => 'Unexpected error',
|
||||||
|
self::FAILED_TO_RESET_INSTALLATION_STATE => 'Failed to reset installation state',
|
||||||
|
];
|
||||||
|
|
||||||
|
const HTTP_CODES = [
|
||||||
|
self::NOT_AUTHENTICATED => 401,
|
||||||
|
self::NO_ACCESS_TOKEN => 400,
|
||||||
|
self::NO_SIGNATURE => 400,
|
||||||
|
self::SITE_NOT_CONNECTED => 401,
|
||||||
|
self::INVALID_TOKEN => 401,
|
||||||
|
self::REQUEST_VERIFICATION_FAILED => 400,
|
||||||
|
self::USER_NOT_FOUND => 401,
|
||||||
|
self::NO_PERMISSION => 403,
|
||||||
|
self::IDEMPOTENCY_KEY_MISMATCH => 400,
|
||||||
|
self::NO_INITIATED_INSTALLATION_FOUND => 400,
|
||||||
|
self::ALL_INSTALLATION_STEPS_RUN => 400,
|
||||||
|
self::REQUESTED_STEP_ALREADY_RUN => 400,
|
||||||
|
self::UNEXPECTED_ERROR => 500,
|
||||||
|
];
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WCCOM Site Installer Error Class
|
||||||
|
*
|
||||||
|
* @package WooCommerce\WCCom\API
|
||||||
|
* @since 7.7.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WCCOM Site Installer Error Class
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class WC_REST_WCCOM_Site_Installer_Error extends Exception {
|
||||||
|
|
||||||
|
public function __construct($error_code, $error_message = null, $http_code = null) {
|
||||||
|
$this->error_code = $error_code;
|
||||||
|
$this->error_message = $error_message ?? WC_REST_WCCOM_Site_Installer_Error_Codes::ERROR_MESSAGES[ $error_code ] ?? '';
|
||||||
|
$this->http_code = $http_code ?? WC_REST_WCCOM_Site_Installer_Error_Codes::HTTP_CODES[ $error_code ] ?? 400;
|
||||||
|
|
||||||
|
parent::__construct( $error_code );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_error_code( ) {
|
||||||
|
return $this->error_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_error_message( ) {
|
||||||
|
return $this->error_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_http_code( ) {
|
||||||
|
return $this->http_code;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,73 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* WCCOM Site Installer Errors Class
|
|
||||||
*
|
|
||||||
* @package WooCommerce\WCCom\API
|
|
||||||
* @since 3.9.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
defined( 'ABSPATH' ) || exit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* WCCOM Site Installer Errors Class
|
|
||||||
*
|
|
||||||
* Stores data for errors, returned by installer API.
|
|
||||||
*/
|
|
||||||
class WC_REST_WCCOM_Site_Installer_Errors {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Not unauthenticated generic error
|
|
||||||
*/
|
|
||||||
const NOT_AUTHENTICATED_CODE = 'not_authenticated';
|
|
||||||
const NOT_AUTHENTICATED_MESSAGE = 'Authentication required';
|
|
||||||
const NOT_AUTHENTICATED_HTTP_CODE = 401;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* No access token provided
|
|
||||||
*/
|
|
||||||
const NO_ACCESS_TOKEN_CODE = 'no_access_token';
|
|
||||||
const NO_ACCESS_TOKEN_MESSAGE = 'No access token provided';
|
|
||||||
const NO_ACCESS_TOKEN_HTTP_CODE = 400;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* No signature provided
|
|
||||||
*/
|
|
||||||
const NO_SIGNATURE_CODE = 'no_signature';
|
|
||||||
const NO_SIGNATURE_MESSAGE = 'No signature provided';
|
|
||||||
const NO_SIGNATURE_HTTP_CODE = 400;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Site not connected to WooCommerce.com
|
|
||||||
*/
|
|
||||||
const SITE_NOT_CONNECTED_CODE = 'site_not_connnected';
|
|
||||||
const SITE_NOT_CONNECTED_MESSAGE = 'Site not connected to WooCommerce.com';
|
|
||||||
const SITE_NOT_CONNECTED_HTTP_CODE = 401;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provided access token is not valid
|
|
||||||
*/
|
|
||||||
const INVALID_TOKEN_CODE = 'invalid_token';
|
|
||||||
const INVALID_TOKEN_MESSAGE = 'Invalid access token provided';
|
|
||||||
const INVALID_TOKEN_HTTP_CODE = 401;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request verification by provided signature failed
|
|
||||||
*/
|
|
||||||
const REQUEST_VERIFICATION_FAILED_CODE = 'request_verification_failed';
|
|
||||||
const REQUEST_VERIFICATION_FAILED_MESSAGE = 'Request verification by signature failed';
|
|
||||||
const REQUEST_VERIFICATION_FAILED_HTTP_CODE = 400;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User doesn't exist
|
|
||||||
*/
|
|
||||||
const USER_NOT_FOUND_CODE = 'user_not_found';
|
|
||||||
const USER_NOT_FOUND_MESSAGE = 'Token owning user not found';
|
|
||||||
const USER_NOT_FOUND_HTTP_CODE = 401;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* No permissions error
|
|
||||||
*/
|
|
||||||
const NO_PERMISSION_CODE = 'forbidden';
|
|
||||||
const NO_PERMISSION_MESSAGE = 'You do not have permission to install plugin or theme';
|
|
||||||
const NO_PERMISSION_HTTP_CODE = 403;
|
|
||||||
}
|
|
|
@ -0,0 +1,207 @@
|
||||||
|
<?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,
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'methods' => WP_REST_Server::EDITABLE,
|
||||||
|
'callback' => [ $this, 'install' ],
|
||||||
|
'permission_callback' => [ $this, 'check_permission' ],
|
||||||
|
'args' => [
|
||||||
|
'product-id' => [
|
||||||
|
'required' => true,
|
||||||
|
'type' => 'integer',
|
||||||
|
],
|
||||||
|
'run-until-step' => [
|
||||||
|
'required' => true,
|
||||||
|
'type' => 'string',
|
||||||
|
'enum' => WC_WCCOM_Site_Installation_Manager::STEPS,
|
||||||
|
],
|
||||||
|
'idempotency-key' => [
|
||||||
|
'required' => true,
|
||||||
|
'type' => 'string',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'methods' => WP_REST_Server::DELETABLE,
|
||||||
|
'callback' => [ $this, 'reset_install' ],
|
||||||
|
'permission_callback' => [ $this, 'check_permission' ],
|
||||||
|
'args' => [
|
||||||
|
'product-id' => [
|
||||||
|
'required' => true,
|
||||||
|
'type' => 'integer',
|
||||||
|
],
|
||||||
|
'idempotency-key' => [
|
||||||
|
'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() ) ) {
|
||||||
|
$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 WooCommerce.com products.
|
||||||
|
*
|
||||||
|
* @since 7.7.0
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
* @return bool|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;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function success_response( $product_id ) {
|
||||||
|
$state = WC_WCCOM_Site_Installation_State_Storage::get_state( $product_id );
|
||||||
|
$response = rest_ensure_response([
|
||||||
|
'success' => true,
|
||||||
|
'state' => $state ? $this->map_state_to_response( $state ) : null,
|
||||||
|
]);
|
||||||
|
$response->set_status(200);
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function failure_response( $product_id, $exception ) {
|
||||||
|
$state = WC_WCCOM_Site_Installation_State_Storage::get_state( $product_id );
|
||||||
|
$response = rest_ensure_response([
|
||||||
|
'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;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function map_state_to_response( $state ) {
|
||||||
|
return [
|
||||||
|
'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(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,9 @@
|
||||||
* @since 3.7.0
|
* @since 3.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;
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,30 +76,32 @@ class WC_REST_WCCOM_Site_Installer_Controller extends WC_REST_Controller {
|
||||||
* @param WP_REST_Request $request Full details about the request.
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
* @return bool|WP_Error
|
* @return bool|WP_Error
|
||||||
*/
|
*/
|
||||||
public function check_permission( $request ) {
|
public function check_permission( $request ) {
|
||||||
$current_user = wp_get_current_user();
|
$current_user = wp_get_current_user();
|
||||||
|
|
||||||
if ( empty( $current_user ) || ( $current_user instanceof WP_User && ! $current_user->exists() ) ) {
|
if ( empty( $current_user ) || ( $current_user instanceof WP_User && ! $current_user->exists() ) ) {
|
||||||
return apply_filters(
|
$error = apply_filters(
|
||||||
WC_WCCOM_Site::AUTH_ERROR_FILTER_NAME,
|
WC_WCCOM_Site::AUTH_ERROR_FILTER_NAME,
|
||||||
new WP_Error(
|
new Installer_Error( Installer_Error_Codes::NOT_AUTHENTICATED )
|
||||||
WC_REST_WCCOM_Site_Installer_Errors::NOT_AUTHENTICATED_CODE,
|
);
|
||||||
WC_REST_WCCOM_Site_Installer_Errors::NOT_AUTHENTICATED_MESSAGE,
|
return new WP_Error(
|
||||||
array( 'status' => WC_REST_WCCOM_Site_Installer_Errors::NOT_AUTHENTICATED_HTTP_CODE )
|
$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' ) ) {
|
if ( ! user_can( $current_user, 'install_plugins' ) || ! user_can( $current_user, 'install_themes' ) ) {
|
||||||
return new WP_Error(
|
$error = new Installer_Error( Installer_Error_Codes::NO_PERMISSION );
|
||||||
WC_REST_WCCOM_Site_Installer_Errors::NO_PERMISSION_CODE,
|
return new WP_Error(
|
||||||
WC_REST_WCCOM_Site_Installer_Errors::NO_PERMISSION_MESSAGE,
|
$error->get_error_code(),
|
||||||
array( 'status' => WC_REST_WCCOM_Site_Installer_Errors::NO_PERMISSION_HTTP_CODE )
|
$error->get_error_message(),
|
||||||
);
|
array( 'status' => $error->get_http_code() )
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get installation state.
|
* Get installation state.
|
||||||
|
|
Loading…
Reference in New Issue