8889841cPK[B,class-wc-wccom-site-installation-manager.phpnu[product_id = $product_id; $this->idempotency_key = $idempotency_key; } /** * Run the installation. * * @param string $run_until_step The step to run until. * @return bool * @throws WC_REST_WCCOM_Site_Installer_Error If installation failed to run. */ 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; } /** * Get the next step to run. * * @return bool * @throws WC_REST_WCCOM_Site_Installer_Error If the installation cannot be rest. */ 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; } /** * Check if the installation can be run. * * @param string $run_until_step Run until this step. * @param WC_WCCOM_Site_Installation_State $state Installation state. * @return void * @throws WC_REST_WCCOM_Site_Installer_Error If the installation cannot be run. */ protected function can_run_installation( $run_until_step, $state ) { 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, true ) >= array_search( $run_until_step, self::STEPS, true ) ) { 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 ); } } /** * Get the next step to run. * * @param WC_WCCOM_Site_Installation_State $state Installation state. * @return string */ 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, true ); return self::STEPS[ $last_executed_step_index + 1 ]; } /** * Get the steps to run. * * @param string $start_step The step to start from. * @param string $end_step The step to end at. * @return string[] */ protected function get_installation_steps( string $start_step, string $end_step ) { $start_step_offset = array_search( $start_step, self::STEPS, true ); $end_step_index = array_search( $end_step, self::STEPS, true ); $length = $end_step_index - $start_step_offset + 1; return array_slice( self::STEPS, $start_step_offset, $length ); } /** * Run the step. * * @param string $step_name Step name. * @param WC_WCCOM_Site_Installation_State $state Installation state. * @return void * @throws WC_REST_WCCOM_Site_Installer_Error If the step fails. */ 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 ); } } PK[>B 2class-wc-wccom-site-installation-state-storage.phpnu[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; } /** * Save state to storage. * * @param WC_WCCOM_Site_Installation_State $state The state to save. * @return bool */ 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, 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(), '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(), ) ); } /** * Delete state from storage. * * @param WC_WCCOM_Site_Installation_State $state The state to delete. * @return bool */ 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 ); } /** * Get the storage key for a product ID. * * @param int $product_id The product ID. * @return string */ protected static function get_storage_key( $product_id ) : string { return sprintf( 'wccom-product-installation-state-%d', $product_id ); } } PK[lJJ*class-wc-wccom-site-installation-state.phpnu[product_id = $product_id; } /** * Initiate an existing installation state. * * @param int $product_id The product ID. * @param string $idempotency_key The idempotency key. * @param string $last_step_name The last step name. * @param string $last_step_status The last step status. * @param string $last_step_error The last step error. * @param int $started_date The timestamp of the installation start. * @return WC_WCCOM_Site_Installation_State The instance. */ 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; } /** * Initiate a new installation state. * * @param init $product_id The product ID. * @param string $idempotency_key The idempotency key. * @return WC_WCCOM_Site_Installation_State The 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; } /** * Get the product ID. * * @return string */ public function get_product_id() { return $this->product_id; } /** * Get the idempotency key. * * @return string */ public function get_idempotency_key() { return $this->idempotency_key; } /** * Get the timestamp of the installation start. * * @return int */ public function get_last_step_name() { return $this->last_step_name; } /** * Get the last step status. * * @return string */ public function get_last_step_status() { return $this->last_step_status; } /** * Get the last step error. * * @return int */ public function get_last_step_error() { return $this->last_step_error; } /** * Initiate a step. * * @param string $step_name Step name. * @return void */ public function initiate_step( $step_name ) { $this->last_step_name = $step_name; $this->last_step_status = self::STEP_STATUS_IN_PROGRESS; } /** * Capture a successful installation of a step. * * @param string $step_name The step name. */ public function complete_step( $step_name ) { $this->last_step_name = $step_name; $this->last_step_status = self::STEP_STATUS_COMPLETED; } /** * Capture an installation failure. * * @param string $step_name The step name. * @param string $error_code The error code. */ 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; } /** * Get the product type. * * @return string */ public function get_product_type() { return $this->product_type; } /** * Set the product type. * * @param string $product_type The product type. */ public function set_product_type( $product_type ) { $this->product_type = $product_type; } /** * Get the product name. * * @return string */ public function get_product_name() { return $this->product_name; } /** * Set the product name. * * @param string $product_name The product name. */ public function set_product_name( $product_name ) { $this->product_name = $product_name; } /** * Get the download URL. * * @return string */ public function get_download_url() { return $this->download_url; } /** * Set the download URL. * * @param string $download_url The download URL. */ public function set_download_url( $download_url ) { $this->download_url = $download_url; } /** * Get the path to the downloaded file. * * @return string */ public function get_download_path() { return $this->download_path; } /** * Set the path to the downloaded file. * * @param string $download_path The path to the downloaded file. */ public function set_download_path( $download_path ) { $this->download_path = $download_path; } /** * Get the path to the unpacked file. * * @return string */ public function get_unpacked_path() { return $this->unpacked_path; } /** * Set the path to the unpacked file. * * @param string $unpacked_path The path to the unpacked file. */ public function set_unpacked_path( $unpacked_path ) { $this->unpacked_path = $unpacked_path; } /** * Get the path to the installed file. * * @return string */ public function get_installed_path() { return $this->installed_path; } /** * Set the path to the installed file. * * @param string $installed_path The path to the installed file. */ public function set_installed_path( $installed_path ) { $this->installed_path = $installed_path; } /** * Get the plugin info for the already installed plugin. * * @return array */ public function get_already_installed_plugin_info() { return $this->already_installed_plugin_info; } /** * Set the plugin info for the already installed plugin. * * @param array $plugin_info The plugin info. */ public function set_already_installed_plugin_info( $plugin_info ) { $this->already_installed_plugin_info = $plugin_info; } /** * Get the timestamp of the installation start. * * @return int */ public function get_started_date() { return $this->started_date; } } PK[}}1installation-steps/interface-installaton-step.phpnu[state = $state; } /** * Run the step installation process. * * @throws Installer_Error Installer Error. */ 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; } } PK[DYYKinstallation-steps/class-wc-wccom-site-installation-step-unpack-product.phpnu[state = $state; } /** * Run the step installation process. */ 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; } } PK[Iinstallation-steps/class-wc-wccom-site-installation-step-move-product.phpnu[state = $state; } /** * Run the step installation process. */ 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; } } PK[$: : Minstallation-steps/class-wc-wccom-site-installation-step-activate-product.phpnu[state = $state; } /** * Run the step installation process. */ 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; } /** * Activate plugin. * * @param int $product_id Product ID. */ 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(), array( '_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() ); } } /** * Activate theme. * * @param int $product_id Product ID. */ 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(), array( '_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 ); } /** * Get WP.org product directory name. * * @return string|false */ 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() ); } } PK[O Minstallation-steps/class-wc-wccom-site-installation-step-get-product-info.phpnu[state = $state; } /** * Run the step installation process. * * @throws Installer_Error Installer Error. * @return array */ public function run() { $product_id = $this->state->get_product_id(); // Get product info from Woo.com. $request = WC_Helper_API::get( add_query_arg( array( 'product_id' => $product_id ), 'info' ), array( '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; } /** * Get download URL for wporg product. * * @param array $data Product data. * * @return string|null * @throws Installer_Error Installer Error. */ 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']; } /** * Get download URL for wccom product. * * @param int $product_id Product ID. * * @return string * @throws Installer_Error Installer Error. */ 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']; } } PK[B,class-wc-wccom-site-installation-manager.phpnu[PK[>B 2)class-wc-wccom-site-installation-state-storage.phpnu[PK[lJJ*v%class-wc-wccom-site-installation-state.phpnu[PK[}}1Binstallation-steps/interface-installaton-step.phpnu[PK[%+MCinstallation-steps/class-wc-wccom-site-installation-step-download-product.phpnu[PK[DYYKHinstallation-steps/class-wc-wccom-site-installation-step-unpack-product.phpnu[PK[IMinstallation-steps/class-wc-wccom-site-installation-step-move-product.phpnu[PK[$: : MTinstallation-steps/class-wc-wccom-site-installation-step-activate-product.phpnu[PK[O Mbinstallation-steps/class-wc-wccom-site-installation-step-get-product-info.phpnu[PK to