product_name = sanitize_text_field( $product_name ); $this->product_shortname = sanitize_text_field( $product_shortname ); $this->product_version = sanitize_text_field( $product_version ); if ( ! $options = get_site_option( 'et_automatic_updates_options' ) ) { $options = get_option( 'et_automatic_updates_options' ); } $this->api_username = isset( $options['username'] ) ? sanitize_text_field( $options['username'] ) : ''; $this->api_key = isset( $options['api_key'] ) ? sanitize_text_field( $options['api_key'] ) : ''; } /** * Enqueue assets. * * @since 3.10 */ public function assets() { wp_enqueue_style( 'et-core-version-rollback', ET_CORE_URL . 'admin/css/version-rollback.css', array( 'et-core-admin', ), ET_CORE_VERSION ); wp_enqueue_script( 'et-core-version-rollback', ET_CORE_URL . 'admin/js/version-rollback.js', array( 'jquery', 'jquery-ui-tabs', 'jquery-form', 'et-core-admin', ), ET_CORE_VERSION ); wp_localize_script( 'et-core-version-rollback', 'etCoreVersionRollbackI18n', array( 'unknownError' => esc_html__( 'An unknown error has occurred. Please try again later.', 'et-core' ), ) ); } /** * Get previous installed version, if any. * * @since 3.10 * * @return string */ protected function _get_previous_installed_version() { return et_get_option( "{$this->product_shortname}_previous_installed_version", '' ); } /** * Set previous installed version. * * @since 3.10 * * @param string $version * * @return void */ protected function _set_previous_installed_version( $version ) { et_update_option( "{$this->product_shortname}_previous_installed_version", sanitize_text_field( $version ) ); } /** * Get latest installed version, if any. * * @since 3.10 * * @return string */ protected function _get_latest_installed_version() { return et_get_option( "{$this->product_shortname}_latest_installed_version", '' ); } /** * Set latest installed version. * * @since 3.10 * * @param string $version * * @return void */ protected function _set_latest_installed_version( $version ) { et_update_option( "{$this->product_shortname}_latest_installed_version", sanitize_text_field( $version ) ); } /** * Check if the product has already been rolled back. * * @since 3.10 * * @return bool */ protected function _is_rolled_back() { return version_compare( $this->_get_latest_installed_version(), $this->_get_previous_installed_version(), '<=' ); } /** * Get unique ajax action. * * @since 3.10 * * @return string */ protected function _get_ajax_action() { return 'et_core_version_rollback'; } /** * Enable update rollback. * * @since 3.10 * * @return void */ public function enable() { if ( $this->enabled ) { return; } $this->enabled = true; add_action( 'admin_enqueue_scripts', array( $this, 'assets' ) ); add_action( 'wp_ajax_' . $this->_get_ajax_action(), array( $this, 'ajax_rollback' ) ); // Update version number when theme is manually replaced. add_action( 'admin_init', array( $this, 'store_previous_version_number' ) ); // Update version number when theme is activated. add_action( 'after_switch_theme', array( $this, 'store_previous_version_number' ) ); // Update version number when theme is updated. add_action( 'upgrader_process_complete', array( $this, 'store_previous_version_number' ), 10, 0 ); } /** * Handle REST API requests to rollback. * * @since 3.10 * * @return void */ public function ajax_rollback() { if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'], $this->_get_ajax_action() ) ) { wp_send_json_error( array( 'errorCode' => 'et_unknown', 'error' => esc_html__( 'Security check failed. Please refresh and try again.', 'et-core' ), ), 400 ); } if ( ! current_user_can( 'install_themes' ) ) { wp_send_json_error( array( 'errorCode' => 'et_unknown', 'error' => esc_html__( 'You don\'t have sufficient permissions to access this page.', 'et-core' ), ), 400 ); } if ( $this->_is_rolled_back() ) { $error = '
' . et_get_safe_localization( sprintf( __( 'You\'re currently rolled back to Version %1$s from Version %2$s.', 'et-core' ), esc_html( $this->_get_latest_installed_version() ), esc_html( $this->_get_previous_installed_version() ) ) ) . '
' . et_get_safe_localization( sprintf( __( 'Update to the latest version to unlock the full power of %1$s. Learn more here.', 'et-core' ), esc_html( $this->product_name ), esc_url( $this->_get_update_documentation_url() ) ) ) . '
'; wp_send_json_error( array( 'errorCode' => 'et_unknown', 'error' => $error, ), 400 ); } $success = $this->rollback(); if ( is_wp_error( $success ) ) { $error = $success->get_error_message(); if ( $success->get_error_code() === 'et_version_rollback_blacklisted' ) { $error = '' . et_get_safe_localization( sprintf( __( 'For privacy and security reasons, you cannot rollback to Version %1$s.', 'et-core' ), esc_html( $this->_get_previous_installed_version() ) ) ) . '
'; } wp_send_json_error( array( 'errorIsUnrecoverable' => in_array( $success->get_error_code(), array( 'et_version_rollback_not_available', 'et_version_rollback_blacklisted' ) ), 'errorCode' => $success->get_error_code(), 'error' => $error, ), 400 ); } wp_send_json_success(); } /** * Execute a version rollback. * * @since 3.10 * * @return bool|WP_Error */ public function rollback() { // Load versions before rollback so they are not affected. $previous_version = $this->_get_previous_installed_version(); $latest_version = $this->_get_latest_installed_version(); $api = new ET_Core_API_ElegantThemes( $this->api_username, $this->api_key ); $available = $api->is_product_available( $this->product_name, $previous_version ); if ( is_wp_error( $available ) ) { return $available; } $download_url = $api->get_download_url( $this->product_name, $previous_version ); // Buffer and discard output as upgrader classes still output content even if the upgrader skin is silent. $buffer_started = ob_start(); $result = $this->_install_theme( $download_url ); if ( $buffer_started ) { ob_end_clean(); } if ( is_wp_error( $result ) ) { return $result; } if ( true !== $result ) { return new WP_Error( 'et_unknown', esc_html__( 'An unknown error has occurred. Please try again later.', 'et-core' ) ); } // Swap version numbers after a successful rollback. $this->_set_previous_installed_version( $latest_version ); $this->_set_latest_installed_version( $previous_version ); } /** * Install a theme overwriting it if it already exists. * Copied from Theme_Upgrader::install() due to lack of control over the clear_desination argument. * * @see Theme_Upgrader::install() @ WordPress 4.9.4 * * @since 3.10 * * @param string $package * * @return bool|WP_Error */ protected function _install_theme( $package ) { require_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' ); $upgrader = new Theme_Upgrader( new ET_Core_LIB_SilentThemeUpgraderSkin() ); $defaults = array( 'clear_update_cache' => true, ); $parsed_args = wp_parse_args( array(), $defaults ); $upgrader->init(); $upgrader->install_strings(); add_filter('upgrader_source_selection', array( $upgrader, 'check_package' ) ); add_filter('upgrader_post_install', array( $upgrader, 'check_parent_theme_filter' ), 10, 3 ); if ( $parsed_args['clear_update_cache'] ) { // Clear cache so wp_update_themes() knows about the new theme. add_action( 'upgrader_process_complete', 'wp_clean_themes_cache', 9, 0 ); } $upgrader->run( array( 'package' => $package, 'destination' => get_theme_root(), 'clear_destination' => true, // Overwrite theme. 'clear_working' => true, 'hook_extra' => array( 'type' => 'theme', 'action' => 'install', ), ) ); remove_action( 'upgrader_process_complete', 'wp_clean_themes_cache', 9 ); remove_filter( 'upgrader_source_selection', array( $upgrader, 'check_package' ) ); remove_filter( 'upgrader_post_install', array( $upgrader, 'check_parent_theme_filter' ) ); if ( ! $upgrader->result || is_wp_error( $upgrader->result ) ) { return $upgrader->result; } // Refresh the Theme Update information. wp_clean_themes_cache( $parsed_args['clear_update_cache'] ); return true; } /** * Get update documentation url for the product. * * @since 3.10 * * @return string */ protected function _get_update_documentation_url() { return "https://www.elegantthemes.com/documentation/{$this->product_shortname}/update-{$this->product_shortname}/"; } /** * Return ePanel option. * * @since 3.10 * * @return array */ public function get_epanel_option() { return array( 'name' => esc_html__( 'Version Rollback', ET_CORE_VERSION ), 'id' => 'et_version_rollback', 'type' => 'callback_function', 'function_name' => array( $this, 'render_epanel_option' ), 'desc' => et_get_safe_localization( __( 'Before you can receive product updates, you must first authenticate your Elegant Themes subscription. To do this, you need to enter both your Elegant Themes Username and your Elegant Themes API Key into the Updates Tab in your theme and plugin settings. To locate your API Key, log in to your Elegant Themes account and navigate to the Account > API Key page. Learn more here. If you still get this message, please make sure that your Username and API Key have been entered correctly', ET_CORE_VERSION ) ), ); } /** * Render ePanel option. * * @since 3.10 * * @return void */ public function render_epanel_option() { $previous = $this->_get_previous_installed_version(); $modal_renderer = array( $this, 'render_epanel_no_previous_version_modal' ); if ( ! empty( $previous ) ) { $modal_renderer = array( $this, 'render_epanel_confirm_rollback_modal' ); if ( $this->_is_rolled_back() ) { $modal_renderer = array( $this, 'render_epanel_already_rolled_back_modal' ); } } add_action( 'admin_footer', $modal_renderer ); ?> _get_ajax_action(); $url = add_query_arg( array( 'action' => $action, 'nonce' => wp_create_nonce( $action ), ), admin_url( 'admin-ajax.php' ) ); ?> _get_previous_installed_version(); $latest_installed_version = $this->_get_latest_installed_version(); // Get the theme version since the files may have changed but // we are still executing old code from memory. $theme_version = et_get_theme_version(); if ( $latest_installed_version === $theme_version ) { return; } if ( empty( $latest_installed_version ) ) { $latest_installed_version = $theme_version; } if ( version_compare( $theme_version, $latest_installed_version, '!=') ) { $previous_installed_version = $latest_installed_version; $latest_installed_version = $theme_version; } $this->_set_previous_installed_version( $previous_installed_version ); $this->_set_latest_installed_version( $latest_installed_version ); } } endif;