← Back
Editing: Connect.php
<?php namespace AIOSEO\Plugin\Lite\Admin; // Exit if accessed directly. if ( ! defined( 'ABSPATH' ) ) { exit; } use AIOSEO\Plugin\Common\Utils; /** * Connect to AIOSEO Pro Worker Service to connect with our Premium Services. * * @since 4.0.0 */ class Connect { /** * Class constructor. * * @since 4.0.0 */ public function __construct() { add_action( 'wp_ajax_nopriv_aioseo_connect_process', [ $this, 'process' ] ); add_action( 'admin_menu', [ $this, 'addDashboardPage' ] ); add_action( 'admin_init', [ $this, 'maybeLoadConnect' ] ); // Create the plugin upgrader with our custom skin. $this->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() { ?> <!DOCTYPE html> <html <?php language_attributes(); ?>> <head> <meta name="viewport" content="width=device-width"/> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title> <?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 ) ); ?> </title> <?php do_action( 'admin_print_scripts' ); ?> <?php do_action( 'admin_print_styles' ); ?> <?php do_action( 'admin_head' ); ?> </head> <body class="aioseo-connect"> <?php } /** * Outputs the content of the current step. * * @since 4.0.0 * * @return void */ public function connectContent() { echo '<div id="aioseo-app"></div>'; } /** * Outputs the simplified footer used for the Onboarding Wizard. * * @since 4.0.0 * * @return void */ public function connectFooter( $pro = '' ) { ?> <?php wp_print_scripts( 'aioseo-vendors' ); wp_print_scripts( 'aioseo-common' ); wp_print_scripts( "aioseo-connect-$pro-script" ); ?> </body> </html> <?php } /** * Generates and returns the AIOSEO Connect URL. * * @since 4.0.0 * * @return array The AIOSEO Connect URL or an error message inside an array. */ public function generateConnectUrl( $key, $redirect = null ) { // Check for permissions. if ( ! current_user_can( 'install_plugins' ) ) { return [ 'error' => 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()->transients->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(); $this->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(); $this->addCapabilitiesOnUpgrade(); deactivate_plugins( plugin_basename( AIOSEO_FILE ), false, $network ); wp_send_json_success( $success ); } /** * Adds our capabilities to all roles on the next request and the installing user on the current request after upgrading to Pro. * See #2267 and #2288 for context. * * @since 4.1.4.4 * * @return void */ private function addCapabilitiesOnUpgrade() { // We need to set this transient here because the regular activation hooks won't run and Pro otherwise won't clear the cache and add the required capabilities. aioseo()->transients->update( 'pro_just_deactivated_lite', true ); // Doing the above isn't sufficient because the logged in user will be lacking the capabilities on the first request and see an error. Therefore, we add them manually just for him. $userId = aioseo()->transients->get( 'connect_active_user' ); $user = get_userdata( $userId ); if ( is_object( $user ) ) { $capabilities = aioseo()->access->getCapabilityList(); foreach ( $capabilities as $capability ) { $user->add_cap( $capability ); } } aioseo()->transients->delete( 'connect_active_user' ); } }
Save File
Cancel