installer = new Utils\PluginUpgraderSilentAjax( new Utils\PluginUpgraderSkin() ); } /** * Adds a dashboard page for our setup wizard. * * @since 4.0.0 * * @return void */ public function addDashboardPage() { add_dashboard_page( '', '', 'aioseo_manage_seo', 'aioseo-connect-pro', '' ); remove_submenu_page( 'index.php', 'aioseo-connect-pro' ); add_dashboard_page( '', '', 'aioseo_manage_seo', 'aioseo-connect', '' ); remove_submenu_page( 'index.php', 'aioseo-connect' ); } /** * Checks to see if we should load the setup wizard. * * @since 4.0.0 * * @return void */ public function maybeLoadConnect() { // Don't load the interface if doing an ajax call. if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { return; } // Check for connect-specific parameter // Allow plugins to disable the connect // Check if current user is allowed to save settings. if ( ! isset( $_GET['page'] ) || ( 'aioseo-connect-pro' !== $_GET['page'] && 'aioseo-connect' !== wp_unslash( $_GET['page'] ) ) || // phpcs:ignore HM.Security.ValidatedSanitizedInput.InputNotSanitized ! current_user_can( 'aioseo_manage_seo' ) ) { return; } set_current_screen(); // Remove an action in the Gutenberg plugin ( not core Gutenberg ) which throws an error. remove_action( 'admin_print_styles', 'gutenberg_block_editor_admin_print_styles' ); if ( 'aioseo-connect-pro' === wp_unslash( $_GET['page'] ) ) { // phpcs:ignore HM.Security.ValidatedSanitizedInput.InputNotSanitized $this->loadConnectPro(); return; } $this->loadConnect(); } /** * Load the Connect template. * * @since 4.0.0 * * @return void */ private function loadConnect() { $this->enqueueScripts(); $this->connectHeader(); $this->connectContent(); $this->connectFooter(); exit; } /** * Load the Connect Pro template. * * @since 4.0.0 * * @return void */ private function loadConnectPro() { $this->enqueueScriptsPro(); $this->connectHeader(); $this->connectContent(); $this->connectFooter( 'pro' ); exit; } /** * Enqueue's scripts for the setup wizard. * * @since 4.0.0 * * @return void */ public function enqueueScripts() { // We don't want any plugin adding notices to our screens. Let's clear them out here. remove_all_actions( 'admin_notices' ); remove_all_actions( 'all_admin_notices' ); // Scripts. aioseo()->helpers->enqueueScript( 'aioseo-vendors', 'js/chunk-vendors.js' ); aioseo()->helpers->enqueueScript( 'aioseo-common', 'js/chunk-common.js' ); aioseo()->helpers->enqueueScript( 'aioseo-connect-script', 'js/connect.js' ); // Styles. $rtl = is_rtl() ? '.rtl' : ''; aioseo()->helpers->enqueueStyle( 'aioseo-vendors', "css/chunk-vendors$rtl.css" ); aioseo()->helpers->enqueueStyle( 'aioseo-common', "css/chunk-common$rtl.css" ); // aioseo()->helpers->enqueueStyle( // 'aioseo-connect-style', // "css/connect$rtl.css" // ); // aioseo()->helpers->enqueueStyle( // 'aioseo-connect-vendors-style', // "css/chunk-connect-vendors$rtl.css" // ); wp_localize_script( 'aioseo-connect-script', 'aioseo', aioseo()->helpers->getVueData() ); } /** * Enqueue's scripts for the setup wizard. * * @since 4.0.0 * * @return void */ public function enqueueScriptsPro() { // We don't want any plugin adding notices to our screens. Let's clear them out here. remove_all_actions( 'admin_notices' ); remove_all_actions( 'all_admin_notices' ); // Scripts. aioseo()->helpers->enqueueScript( 'aioseo-vendors', 'js/chunk-vendors.js' ); aioseo()->helpers->enqueueScript( 'aioseo-common', 'js/chunk-common.js' ); aioseo()->helpers->enqueueScript( 'aioseo-connect-pro-script', 'js/connect-pro.js' ); // Styles. $rtl = is_rtl() ? '.rtl' : ''; aioseo()->helpers->enqueueStyle( 'aioseo-vendors', "css/chunk-vendors$rtl.css" ); aioseo()->helpers->enqueueStyle( 'aioseo-common', "css/chunk-common$rtl.css" ); // aioseo()->helpers->enqueueStyle( // 'aioseo-connect-pro-style', // "css/connect-pro$rtl.css" // ); // aioseo()->helpers->enqueueStyle( // 'aioseo-connect-pro-vendors-style', // "css/chunk-connect-pro-vendors$rtl.css" // ); wp_localize_script( 'aioseo-connect-pro-script', 'aioseo', aioseo()->helpers->getVueData() ); } /** * Outputs the simplified header used for the Onboarding Wizard. * * @since 4.0.0 * * @return void */ public function connectHeader() { ?> > <?php // Translators: 1 - The plugin name ("All in One SEO"). echo sprintf( esc_html__( '%1$s › Connect', 'all-in-one-seo-pack' ), esc_html( AIOSEO_PLUGIN_NAME ) ); ?> '; } /** * Outputs the simplified footer used for the Onboarding Wizard. * * @since 4.0.0 * * @return void */ public function connectFooter( $pro = '' ) { ?> esc_html__( 'You are not allowed to install plugins.', 'all-in-one-seo-pack' ) ]; } if ( empty( $key ) ) { return [ 'error' => esc_html__( 'Please enter your license key to connect.', 'all-in-one-seo-pack' ), ]; } // Verify pro version is not installed. $active = activate_plugin( 'all-in-one-seo-pack-pro/all_in_one_seo_pack_pro', false, false, true ); if ( ! is_wp_error( $active ) ) { // Deactivate plugin. deactivate_plugins( plugin_basename( AIOSEO_FILE ), false, false ); return [ 'error' => esc_html__( 'Pro version is already installed.', 'all-in-one-seo-pack' ) ]; } // Just check if network is set. $network = isset( $_POST['network'] ) ? (bool) wp_unslash( $_POST['network'] ) : false; // phpcs:ignore HM.Security.ValidatedSanitizedInput.InputNotSanitized, HM.Security.NonceVerification.Missing, Generic.Files.LineLength.MaxExceeded $network = ! empty( $network ); // Redirect. $token = hash( 'sha512', wp_rand() ); // Save the options. aioseo()->internalOptions->internal->connect->key = $key; aioseo()->internalOptions->internal->connect->time = time(); aioseo()->internalOptions->internal->connect->network = $network; aioseo()->internalOptions->internal->connect->token = $token; $url = add_query_arg( [ 'key' => $key, 'network' => $network, 'token' => $token, 'version' => aioseo()->version, 'siteurl' => admin_url(), 'homeurl' => home_url(), 'endpoint' => admin_url( 'admin-ajax.php' ), 'php' => PHP_VERSION, 'wp' => get_bloginfo( 'version' ), 'redirect' => rawurldecode( base64_encode( $redirect ? $redirect : admin_url( 'admin.php?page=aioseo-settings' ) ) ), 'v' => 1, ], defined( 'AIOSEO_UPGRADE_URL' ) ? AIOSEO_UPGRADE_URL : 'https://upgrade.aioseo.com' ); // We're storing the ID of the user who is installing Pro so that we can add capabilties for him after upgrading. aioseo()->cache->update( 'connect_active_user', get_current_user_id(), 15 * MINUTE_IN_SECONDS ); return [ 'url' => $url, ]; } /** * Process AIOSEO Connect. * * @since 1.0.0 * * @param string $downloadUrl The download URL. * @param string $postToken The token to validate. * @return array An array containing a valid response or an error message. */ public function process() { // Verify params present (oth & download link). $postToken = ! empty( $_POST['token'] ) ? sanitize_text_field( wp_unslash( $_POST['token'] ) ) : ''; // phpcs:ignore HM.Security.NonceVerification.Missing $downloadUrl = ! empty( $_POST['file'] ) ? esc_url_raw( wp_unslash( $_POST['file'] ) ) : ''; // phpcs:ignore HM.Security.NonceVerification.Missing // Translators: 1 - The marketing site anchor name ("aioseo.com"). $error = sprintf( esc_html__( 'Could not install upgrade. Please download from %1$s and install manually.', 'all-in-one-seo-pack' ), esc_html( AIOSEO_MARKETING_DOMAIN ) ); $success = esc_html__( 'Plugin installed & activated.', 'all-in-one-seo-pack' ); // verify params present (token & download link). if ( empty( $downloadUrl ) || empty( $postToken ) ) { wp_send_json_error( $error ); } // Verify token. $token = aioseo()->internalOptions->internal->connect->token; if ( empty( $token ) ) { wp_send_json_error( $error ); } // This function has been included in WP Core since 3.9.2. @see: https://developer.wordpress.org/reference/functions/hash_equals/ if ( ! hash_equals( $token, $postToken ) ) { // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.hash_equalsFound wp_send_json_error( $error ); } // Delete connect token so we don't replay. aioseo()->internalOptions->internal->connect->token = null; // Verify pro not activated. if ( aioseo()->pro ) { wp_send_json_success( $success ); } // Check license key. $licenseKey = aioseo()->internalOptions->internal->connect->key; if ( ! $licenseKey ) { wp_send_json_error( esc_html__( 'You are not licensed.', 'all-in-one-seo-pack' ) ); } // Set the license key in a new option so we can get it when Pro is activated. aioseo()->internalOptions->internal->validLicenseKey = $licenseKey; require_once ABSPATH . 'wp-admin/includes/file.php'; require_once ABSPATH . 'wp-admin/includes/class-wp-screen.php'; require_once ABSPATH . 'wp-admin/includes/screen.php'; // Set the current screen to avoid undefined notices. set_current_screen( 'toplevel_page_aioseo' ); // Prepare variables. $url = esc_url_raw( add_query_arg( [ 'page' => 'aioseo-settings', ], admin_url( 'admin.php' ) ) ); // Verify pro not installed. $network = aioseo()->internalOptions->internal->connect->network; $active = activate_plugin( 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php', $url, $network, true ); if ( ! is_wp_error( $active ) ) { aioseo()->internalOptions->internal->connect->reset(); // Because the regular activation hooks won't run, we need to add capabilities for the installing user so that he doesn't run into an error on the first request. aioseo()->activate->addCapabilitiesOnUpgrade(); deactivate_plugins( plugin_basename( AIOSEO_FILE ), false, $network ); wp_send_json_success( $success ); } $creds = request_filesystem_credentials( $url, '', false, false, null ); // Check for file system permissions. if ( false === $creds ) { wp_send_json_error( $error ); } if ( ! aioseo()->helpers->wpfs( $creds ) ) { wp_send_json_error( $error ); } // Do not allow WordPress to search/download translations, as this will break JS output. remove_action( 'upgrader_process_complete', [ 'Language_Pack_Upgrader', 'async_upgrade' ], 20 ); // Create the plugin upgrader with our custom skin. $installer = new Utils\PluginUpgraderSilentAjax( new Utils\PluginUpgraderSkin() ); // Error check. if ( ! method_exists( $installer, 'install' ) ) { wp_send_json_error( $error ); } $installer->install( $downloadUrl ); // Flush the cache and return the newly installed plugin basename. wp_cache_flush(); $pluginBasename = $installer->plugin_info(); if ( ! $pluginBasename ) { wp_send_json_error( $error ); } // Activate the plugin silently. $activated = activate_plugin( $pluginBasename, '', $network, true ); if ( is_wp_error( $activated ) ) { wp_send_json_error( esc_html__( 'The Pro version installed correctly, but it needs to be activated from the Plugins page inside your WordPress admin.', 'all-in-one-seo-pack' ) ); } aioseo()->internalOptions->internal->connect->reset(); // Because the regular activation hooks won't run, we need to add capabilities for the installing user so that he doesn't run into an error on the first request. aioseo()->activate->addCapabilitiesOnUpgrade(); deactivate_plugins( plugin_basename( AIOSEO_FILE ), false, $network ); wp_send_json_success( $success ); } }