Lite/Traits/Options.php000064400000005230150212212220011040 0ustar00getDbOptions( $this->optionsName . '_lite' ); // Refactor options. $this->defaultsMerged = array_replace_recursive( $this->defaults, $this->liteDefaults ); $mergedDefaults = array_replace_recursive( $this->liteDefaults, $this->addValueToValuesArray( $this->liteDefaults, $dbOptions ) ); $cachedOptions = aioseo()->core->optionsCache->getOptions( $this->optionsName ); $dbOptions = array_replace_recursive( $cachedOptions, $mergedDefaults ); aioseo()->core->optionsCache->setOptions( $this->optionsName, $dbOptions ); } /** * Merge defaults with liteDefaults. * * @since 4.1.4 * * @return array An array of dafults. */ public function getDefaults() { return array_replace_recursive( parent::getDefaults(), $this->liteDefaults ); } /** * Updates the options in the database. * * @since 4.1.4 * * @param string $optionsName An optional option name to update. * @param string $defaults The defaults to filter the options by. * @param array|null $options An optional options array. * @return void */ public function update( $optionsName = null, $defaults = null, $options = null ) { $optionsName = empty( $optionsName ) ? $this->optionsName . '_lite' : $optionsName; $defaults = empty( $defaults ) ? $this->liteDefaults : $defaults; // We're creating a new array here because it was setting it by reference. $cachedOptions = aioseo()->core->optionsCache->getOptions( $this->optionsName ); $optionsBefore = json_decode( wp_json_encode( $cachedOptions ), true ); parent::update( $this->optionsName, $options ); parent::update( $optionsName, $defaults, $optionsBefore ); } /** * Updates the options in the database. * * @since 4.1.4 * * @param boolean $force Whether or not to force an immediate save. * @param string $optionsName An optional option name to update. * @param string $defaults The defaults to filter the options by. * @return void */ public function save( $force = false, $optionsName = null, $defaults = null ) { if ( ! $this->shouldSave && ! $force ) { return; } $optionsName = empty( $optionsName ) ? $this->optionsName . '_lite' : $optionsName; $defaults = empty( $defaults ) ? $this->liteDefaults : $defaults; parent::save( $force, $this->optionsName ); parent::save( $force, $optionsName, $defaults ); } }Lite/Utils/Helpers.php000064400000001314150212212220010640 0ustar00routes, $this->liteRoutes ); } }Lite/Api/Wizard.php000064400000002124150212212220010107 0ustar00get_json_params(); $section = ! empty( $body['section'] ) ? sanitize_text_field( $body['section'] ) : null; $wizard = ! empty( $body['wizard'] ) ? $body['wizard'] : null; // Save the smart recommendations section. if ( 'smartRecommendations' === $section && ! empty( $wizard['smartRecommendations'] ) ) { $smartRecommendations = $wizard['smartRecommendations']; if ( isset( $smartRecommendations['usageTracking'] ) ) { aioseo()->options->advanced->usageTracking = $smartRecommendations['usageTracking']; } } return $response; } }Lite/Options/InternalOptions.php000064400000002071150212212220012722 0ustar00 [ 'activated' => [ 'type' => 'number', 'default' => 0 ], 'firstActivated' => [ 'type' => 'number', 'default' => 0 ], 'installed' => [ 'type' => 'number', 'default' => 0 ], 'connect' => [ 'key' => [ 'type' => 'string' ], 'time' => [ 'type' => 'number', 'default' => 0 ], 'network' => [ 'type' => 'boolean', 'default' => false ], 'token' => [ 'type' => 'string' ] ] ] // phpcs:enable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound ]; }Lite/Options/Options.php000064400000002371150212212220011230 0ustar00 [ 'usageTracking' => [ 'type' => 'boolean', 'default' => false ] ] // phpcs:enable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound ]; /** * Sanitizes, then saves the options to the database. * * @since 4.7.2 * * @param array $options An array of options to sanitize, then save. * @return void */ public function sanitizeAndSave( $options ) { if ( isset( $options['advanced']['emailSummary']['recipients'] ) ) { $options['advanced']['emailSummary']['recipients'] = [ array_shift( $options['advanced']['emailSummary']['recipients'] ) ]; $options['advanced']['emailSummary']['recipients'][0]['frequency'] = 'monthly'; } parent::sanitizeAndSave( $options ); } }Lite/Main/Filters.php000064400000005774150212212220010450 0ustar00', 5 ); $actionLinks = [ 'suggest-feature' => [ // Translators: This is an action link users can click to open a feature request. 'label' => __( 'Suggest a Feature', 'all-in-one-seo-pack' ), 'url' => aioseo()->helpers->utmUrl( AIOSEO_MARKETING_URL . 'suggest-a-feature/', 'plugin-row-meta', 'feature' ), ], 'review' => [ 'label' => $reviewLabel, 'url' => aioseo()->helpers->utmUrl( AIOSEO_MARKETING_URL . 'review-aioseo', 'plugin-row-meta', 'review' ), 'title' => sprintf( // Translators: 1 - The plugin short name ("AIOSEO"). __( 'Rate %1$s', 'all-in-one-seo-pack' ), 'AIOSEO' ) ] ]; return $this->parseActionLinks( $actions, $pluginFile, $actionLinks ); } /** * Registers our action links for the plugins page. * * @since 4.0.0 * * @param array $actions List of existing actions. * @param string $pluginFile The plugin file. * @return array List of action links. */ public function pluginActionLinks( $actions, $pluginFile = '' ) { $actionLinks = [ 'settings' => [ 'label' => __( 'SEO Settings', 'all-in-one-seo-pack' ), 'url' => get_admin_url( null, 'admin.php?page=aioseo-settings' ), ], 'support' => [ // Translators: This is an action link users can click to open our premium support. 'label' => __( 'Support', 'all-in-one-seo-pack' ), 'url' => aioseo()->helpers->utmUrl( AIOSEO_MARKETING_URL . 'contact/', 'plugin-action-links', 'Support' ), ], 'docs' => [ // Translators: This is an action link users can click to open our general documentation page. 'label' => __( 'Documentation', 'all-in-one-seo-pack' ), 'url' => aioseo()->helpers->utmUrl( AIOSEO_MARKETING_URL . 'docs/', 'plugin-action-links', 'Documentation' ), ], 'proupgrade' => [ // Translators: This is an action link users can click to purchase a license for All in One SEO Pro. 'label' => __( 'Upgrade to Pro', 'all-in-one-seo-pack' ), 'url' => apply_filters( 'aioseo_upgrade_link', aioseo()->helpers->utmUrl( AIOSEO_MARKETING_URL . 'lite-upgrade/', 'plugin-action-links', 'Upgrade', false ) ), ] ]; if ( isset( $actions['edit'] ) ) { unset( $actions['edit'] ); } return $this->parseActionLinks( $actions, $pluginFile, $actionLinks, 'before' ); } }Lite/Views/taxonomy-upsell.php000064400000133476150212212220012432 0ustar00

General
Social
Redirects
SEO Revisions
Advanced
Snippet Preview
https://aioseo.com/category/uncategorized/
Taxonomy Title | aioseo.com
Sample taxonomy description
Category Title
Click on the tags below to insert variables into your title.
Category Title
Separator
Site Title
View all tags →
Category Description
Category Description
Current or first category description.
Category Title
Category Title
Current or first category title.
Current Date
Current Date
The current date, localized.
Current Day
Current Day
The current day of the month, localized.
Current Month
Current Month
The current month, localized.
Current Year
Current Year
The current year, localized.
Custom Field
Custom Field
A custom field from the current page/post.
Permalink
Permalink
The permalink for the current page/post.
Separator
Separator
The separator defined in the search appearance settings.
Site Title
Site Title
Your site title.
Tagline
Tagline
The tagline for your site, set in the general settings.
Meta Description
Click on the tags below to insert variables into your meta description.
Category Title
Separator
Category Description
View all tags →
Category Description
Category Description
Current or first category description.
Category Title
Category Title
Current or first category title.
Current Date
Current Date
The current date, localized.
Current Day
Current Day
The current day of the month, localized.
Current Month
Current Month
The current month, localized.
Current Year
Current Year
The current year, localized.
Custom Field
Custom Field
A custom field from the current page/post.
Permalink
Permalink
The permalink for the current page/post.
Separator
Separator
The separator defined in the search appearance settings.
Site Title
Site Title
Your site title.
Tagline
Tagline
The tagline for your site, set in the general settings.
SEO Title/Description
Social Meta
SEO Revisions
Import/Export
🎁 .
Lite/Admin/PostSettings.php000064400000003366150212212220011645 0ustar00label ) || ! is_taxonomy_viewable( $taxObject ) ) { unset( $taxonomies[ $taxObject->name ] ); } } foreach ( $taxonomies as $taxonomy ) { add_action( $taxonomy->name . '_edit_form', [ $this, 'addTaxonomyUpsell' ] ); add_action( 'after-' . $taxonomy->name . '-table', [ $this, 'addTaxonomyUpsell' ] ); } } } /** * Add Taxonomy Upsell * * @since 4.0.0 * * @return void */ public function addTaxonomyUpsell() { $screen = aioseo()->helpers->getCurrentScreen(); if ( ! isset( $screen->parent_base ) || 'edit' !== $screen->parent_base || empty( $screen->taxonomy ) ) { return; } include_once AIOSEO_DIR . '/app/Lite/Views/taxonomy-upsell.php'; } }Lite/Admin/Connect.php000064400000027046150212212220010571 0ustar00loadConnectPro(); return; } $this->loadConnect(); // phpcs:enable } /** * 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( 'network_admin_notices' ); remove_all_actions( 'all_admin_notices' ); aioseo()->core->assets->load( 'src/vue/standalone/connect/main.js', [], 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( 'network_admin_notices' ); remove_all_actions( 'all_admin_notices' ); aioseo()->core->assets->load( 'src/vue/standalone/connect-pro/main.js', [], 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 ) ); ?> '; aioseo()->templates->getTemplate( 'admin/settings-page.php' ); echo ''; } /** * 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 ) ) { 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) sanitize_text_field( wp_unslash( $_POST['network'] ) ) : false; // phpcs:ignore HM.Security.ValidatedSanitizedInput.InputNotSanitized, HM.Security.NonceVerification.Missing, WordPress.Security.NonceVerification, Generic.Files.LineLength.MaxExceeded $network = ! empty( $network ); // Generate a hash that can be compared after the user is redirected back. $oth = hash( 'sha512', wp_rand() ); $hashedOth = hash_hmac( 'sha512', $oth, wp_salt() ); // 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 = $oth; $url = add_query_arg( [ 'key' => $key, 'network' => $network, 'token' => $hashedOth, '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()->core->cache->update( 'connect_active_user', get_current_user_id(), 15 * MINUTE_IN_SECONDS ); return [ 'url' => $url, ]; } /** * Process AIOSEO Connect. * * @since 1.0.0 * * @return array An array containing a valid response or an error message. */ public function process() { // phpcs:disable HM.Security.NonceVerification.Missing, WordPress.Security.NonceVerification $hashedOth = ! empty( $_POST['token'] ) ? sanitize_text_field( wp_unslash( $_POST['token'] ) ) : ''; $downloadUrl = ! empty( $_POST['file'] ) ? esc_url_raw( wp_unslash( $_POST['file'] ) ) : ''; // phpcs:enable $error = sprintf( // Translators: 1 - The marketing site domain ("aioseo.com"). 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' ); // Check if all required params are present. if ( empty( $downloadUrl ) || empty( $hashedOth ) ) { wp_send_json_error( $error ); } $oth = aioseo()->internalOptions->internal->connect->token; if ( empty( $oth ) ) { wp_send_json_error( $error ); } // Check if the stored hash matches the salted one that is sent back from the server. if ( hash_hmac( 'sha512', $oth, wp_salt() ) !== $hashedOth ) { 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(); 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 ); } $fs = aioseo()->core->fs->noConflict(); $fs->init( $creds ); if ( ! $fs->isWpfsValid() ) { 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(); wp_send_json_success( $success ); } }Lite/Admin/Notices/Notices.php000064400000004706150212212220012206 0ustar00wooUpsellNotice(); } /** * Validates the notification type. * * @since 4.0.0 * * @param string $type The notification type we are targeting. * @return boolean True if yes, false if no. */ public function validateType( $type ) { $validated = parent::validateType( $type ); // Any lite notification should pass here. if ( 'lite' === $type ) { $validated = true; } return $validated; } /** * Add a notice if WooCommerce is detected and not licensed or running Lite. * * @since 4.0.0 * * @return void */ private function wooUpsellNotice() { $notification = Models\Notification::getNotificationByName( 'woo-upsell' ); if ( ! class_exists( 'WooCommerce' ) ) { if ( $notification->exists() ) { Models\Notification::deleteNotificationByName( 'woo-upsell' ); } return; } if ( $notification->exists() ) { return; } Models\Notification::addNotification( [ 'slug' => uniqid(), 'notification_name' => 'woo-upsell', // Translators: 1 - "WooCommerce". 'title' => sprintf( __( 'Advanced %1$s Support', 'all-in-one-seo-pack' ), 'WooCommerce' ), // Translators: 1 - "WooCommerce", 2 - The plugin short name ("AIOSEO"). 'content' => sprintf( __( 'We have detected you are running %1$s. Upgrade to %2$s to unlock our advanced eCommerce SEO features, including SEO for Product Categories and more.', 'all-in-one-seo-pack' ), 'WooCommerce', AIOSEO_PLUGIN_SHORT_NAME . ' Pro' ), // phpcs:ignore Generic.Files.LineLength.MaxExceeded 'type' => 'info', 'level' => [ 'all' ], // Translators: 1 - "Pro". 'button1_label' => sprintf( __( 'Upgrade to %1$s', 'all-in-one-seo-pack' ), 'Pro' ), 'button1_action' => html_entity_decode( apply_filters( 'aioseo_upgrade_link', aioseo()->helpers->utmUrl( AIOSEO_MARKETING_URL . 'lite-upgrade/', 'woo-notification-upsell', false ) ) ), 'start' => gmdate( 'Y-m-d H:i:s' ) ] ); } }Lite/Admin/Usage.php000064400000001116150212212220010232 0ustar00enabled = aioseo()->options->advanced->usageTracking; } /** * Get the type for the request. * * @since 4.0.0 * * @return string The install type. */ public function getType() { return 'lite'; } }Lite/Admin/Admin.php000064400000005223150212212220010221 0ustar00connect = new Connect(); } /** * Actually adds the menu items to the admin bar. * * @since 4.0.0 * * @return void */ protected function addAdminBarMenuItems() { // Add an upsell to Pro. if ( current_user_can( $this->getPageRequiredCapability( '' ) ) ) { $this->adminBarMenuItems['aioseo-pro-upgrade'] = [ 'parent' => 'aioseo-main', 'title' => '' . __( 'Upgrade to Pro', 'all-in-one-seo-pack' ) . '', 'id' => 'aioseo-pro-upgrade', 'href' => apply_filters( 'aioseo_upgrade_link', esc_url( admin_url( 'admin.php?page=aioseo-tools&aioseo-redirect-upgrade=1' ) ) ), 'meta' => [ 'target' => '_blank' ], ]; } parent::addAdminBarMenuItems(); } /** * Add the menu inside of WordPress. * * @since 4.0.0 * * @return void */ public function addMenu() { parent::addMenu(); $capability = $this->getPageRequiredCapability( '' ); // We use the global submenu, because we are adding an external link here. if ( current_user_can( $capability ) ) { global $submenu; $submenu[ $this->pageSlug ][] = [ '' . esc_html__( 'Upgrade to Pro', 'all-in-one-seo-pack' ) . '', $capability, apply_filters( 'aioseo_upgrade_link', esc_url( admin_url( 'admin.php?page=aioseo-tools&aioseo-redirect-upgrade=1' ) ) ) ]; } } /** * Check the query args to see if we need to redirect to an external URL. * * @since 4.2.3 * * @return void */ protected function checkForRedirects() { $mappedUrls = [ // Added to resolve an issue with the open_basedir in the IIS. // https://github.com/awesomemotive/aioseo/issues/3243 'aioseo-redirect-upgrade' => apply_filters( 'aioseo_upgrade_link', aioseo()->helpers->utmUrl( AIOSEO_MARKETING_URL . 'lite-upgrade/', 'admin-bar', null, false ) ) ]; foreach ( $mappedUrls as $queryArg => $redirectUrl ) { if ( isset( $_GET[ $queryArg ] ) ) { // phpcs:ignore HM.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Recommended wp_redirect( $redirectUrl ); } } } }AIOSEO.php000064400000031211150212212220006217 0ustar00init(); // Load our addons on the action right after plugins_loaded. add_action( 'sanitize_comment_cookies', [ self::$instance, 'loadAddons' ] ); } return self::$instance; } /** * Initialize All in One SEO! * * @since 4.0.0 * * @return void */ private function init() { $this->constants(); $this->includes(); $this->preLoad(); if ( ! $this->core->isUninstalling() ) { $this->load(); } } /** * Setup plugin constants. * All the path/URL related constants are defined in main plugin file. * * @since 4.0.0 * * @return void */ private function constants() { $defaultHeaders = [ 'name' => 'Plugin Name', 'version' => 'Version', ]; $pluginData = get_file_data( AIOSEO_FILE, $defaultHeaders ); $constants = [ 'AIOSEO_PLUGIN_BASENAME' => plugin_basename( AIOSEO_FILE ), 'AIOSEO_PLUGIN_NAME' => $pluginData['name'], 'AIOSEO_PLUGIN_SHORT_NAME' => 'AIOSEO', 'AIOSEO_PLUGIN_URL' => plugin_dir_url( AIOSEO_FILE ), 'AIOSEO_VERSION' => $pluginData['version'], 'AIOSEO_MARKETING_URL' => 'https://aioseo.com/', 'AIOSEO_MARKETING_DOMAIN' => 'aioseo.com' ]; foreach ( $constants as $constant => $value ) { if ( ! defined( $constant ) ) { define( $constant, $value ); } } $this->version = AIOSEO_VERSION; } /** * Including the new files with PHP 5.3 style. * * @since 4.0.0 * * @return void */ private function includes() { $dependencies = [ '/vendor/autoload.php' => true, '/vendor/woocommerce/action-scheduler/action-scheduler.php' => true, '/vendor/jwhennessey/phpinsight/autoload.php' => false, '/vendor_prefixed/monolog/monolog/src/Monolog/Logger.php' => false ]; foreach ( $dependencies as $path => $shouldRequire ) { if ( ! file_exists( AIOSEO_DIR . $path ) ) { // Something is not right. status_header( 500 ); wp_die( esc_html__( 'Plugin is missing required dependencies. Please contact support for more information.', 'all-in-one-seo-pack' ) ); } if ( $shouldRequire ) { require_once AIOSEO_DIR . $path; } } $this->loadVersion(); } /** * Load the version of the plugin we are currently using. * * @since 4.1.9 * * @return void */ private function loadVersion() { $proDir = is_dir( plugin_dir_path( AIOSEO_FILE ) . 'app/Pro' ); if ( ! class_exists( '\Dotenv\Dotenv' ) || ! file_exists( AIOSEO_DIR . '/build/.env' ) ) { $this->pro = $proDir; $this->versionPath = $proDir ? 'Pro' : 'Lite'; return; } $dotenv = \Dotenv\Dotenv::createUnsafeImmutable( AIOSEO_DIR, '/build/.env' ); $dotenv->load(); $version = defined( 'AIOSEO_DEV_VERSION' ) ? strtolower( AIOSEO_DEV_VERSION ) : strtolower( getenv( 'VITE_VERSION' ) ); if ( ! empty( $version ) ) { $this->isDev = true; if ( file_exists( AIOSEO_DIR . '/build/filters.php' ) ) { require_once AIOSEO_DIR . '/build/filters.php'; } } if ( $proDir && 'pro' === $version ) { $this->pro = true; $this->versionPath = 'Pro'; } } /** * Runs before we load the plugin. * * @since 4.0.0 * * @return void */ private function preLoad() { $this->core = new Common\Core\Core(); $this->backwardsCompatibility(); // Internal Options. $this->helpers = $this->pro ? new Pro\Utils\Helpers() : new Lite\Utils\Helpers(); $this->internalNetworkOptions = ( $this->pro && $this->helpers->isPluginNetworkActivated() ) ? new Pro\Options\InternalNetworkOptions() : new Common\Options\InternalNetworkOptions(); $this->internalOptions = $this->pro ? new Pro\Options\InternalOptions() : new Lite\Options\InternalOptions(); $this->uninstall = new Common\Main\Uninstall(); // Run pre-updates. $this->preUpdates = $this->pro ? new Pro\Main\PreUpdates() : new Common\Main\PreUpdates(); } /** * To prevent errors and bugs from popping up, * we will run this backwards compatibility method. * * @since 4.1.9 * * @return void */ private function backwardsCompatibility() { $this->db = $this->core->db; $this->cache = $this->core->cache; $this->transients = $this->cache; $this->cachePrune = $this->core->cachePrune; $this->optionsCache = $this->core->optionsCache; } /** * To prevent errors and bugs from popping up, * we will run this backwards compatibility method. * * @since 4.2.0 * * @return void */ private function backwardsCompatibilityLoad() { $this->postSettings->integrations = $this->standalone->pageBuilderIntegrations; } /** * Load our classes. * * @since 4.0.0 * * @return void */ public function load() { // Load external translations if this is a Pro install. if ( $this->pro ) { $translations = new Pro\Main\Translations( 'plugin', 'all-in-one-seo-pack', 'https://aioseo.com/aioseo-plugin/all-in-one-seo-pack/packages.json' ); $translations->init(); $translations = new Pro\Main\Translations( 'plugin', 'aioseo-pro', 'https://aioseo.com/aioseo-plugin/aioseo-pro/packages.json' ); $translations->init(); } $this->addons = $this->pro ? new Pro\Utils\Addons() : new Common\Utils\Addons(); $this->features = $this->pro ? new Pro\Utils\Features() : new Common\Utils\Features(); $this->tags = $this->pro ? new Pro\Utils\Tags() : new Common\Utils\Tags(); $this->blocks = new Common\Utils\Blocks(); $this->badBotBlocker = new Common\Tools\BadBotBlocker(); $this->breadcrumbs = $this->pro ? new Pro\Breadcrumbs\Breadcrumbs() : new Common\Breadcrumbs\Breadcrumbs(); $this->dynamicBackup = $this->pro ? new Pro\Options\DynamicBackup() : new Common\Options\DynamicBackup(); $this->options = $this->pro ? new Pro\Options\Options() : new Lite\Options\Options(); $this->networkOptions = ( $this->pro && $this->helpers->isPluginNetworkActivated() ) ? new Pro\Options\NetworkOptions() : new Common\Options\NetworkOptions(); $this->dynamicOptions = $this->pro ? new Pro\Options\DynamicOptions() : new Common\Options\DynamicOptions(); $this->backup = new Common\Utils\Backup(); $this->access = $this->pro ? new Pro\Utils\Access() : new Common\Utils\Access(); $this->usage = $this->pro ? new Pro\Admin\Usage() : new Lite\Admin\Usage(); $this->siteHealth = $this->pro ? new Pro\Admin\SiteHealth() : new Common\Admin\SiteHealth(); $this->networkLicense = $this->pro && $this->helpers->isPluginNetworkActivated() ? new Pro\Admin\NetworkLicense() : null; $this->license = $this->pro ? new Pro\Admin\License() : null; $this->autoUpdates = $this->pro ? new Pro\Admin\AutoUpdates() : null; $this->updates = $this->pro ? new Pro\Main\Updates() : new Common\Main\Updates(); $this->meta = $this->pro ? new Pro\Meta\Meta() : new Common\Meta\Meta(); $this->social = $this->pro ? new Pro\Social\Social() : new Common\Social\Social(); $this->robotsTxt = new Common\Tools\RobotsTxt(); $this->htaccess = new Common\Tools\Htaccess(); $this->term = $this->pro ? new Pro\Admin\Term() : null; $this->notices = $this->pro ? new Pro\Admin\Notices\Notices() : new Lite\Admin\Notices\Notices(); $this->wpNotices = new Common\Admin\Notices\WpNotices(); $this->admin = $this->pro ? new Pro\Admin\Admin() : new Lite\Admin\Admin(); $this->networkAdmin = $this->helpers->isPluginNetworkActivated() ? ( $this->pro ? new Pro\Admin\NetworkAdmin() : new Common\Admin\NetworkAdmin() ) : null; $this->activate = $this->pro ? new Pro\Main\Activate() : new Common\Main\Activate(); $this->conflictingPlugins = $this->pro ? new Pro\Admin\ConflictingPlugins() : new Common\Admin\ConflictingPlugins(); $this->migration = $this->pro ? new Pro\Migration\Migration() : new Common\Migration\Migration(); $this->importExport = $this->pro ? new Pro\ImportExport\ImportExport() : new Common\ImportExport\ImportExport(); $this->sitemap = $this->pro ? new Pro\Sitemap\Sitemap() : new Common\Sitemap\Sitemap(); $this->htmlSitemap = new Common\Sitemap\Html\Sitemap(); $this->templates = $this->pro ? new Pro\Utils\Templates() : new Common\Utils\Templates(); $this->categoryBase = new Common\Main\CategoryBase(); $this->postSettings = $this->pro ? new Pro\Admin\PostSettings() : new Lite\Admin\PostSettings(); $this->standalone = new Common\Standalone\Standalone(); $this->searchStatistics = $this->pro ? new Pro\SearchStatistics\SearchStatistics() : new Common\SearchStatistics\SearchStatistics(); $this->slugMonitor = new Common\Admin\SlugMonitor(); $this->schema = $this->pro ? new Pro\Schema\Schema() : new Common\Schema\Schema(); $this->actionScheduler = new Common\Utils\ActionScheduler(); $this->seoRevisions = $this->pro ? new Pro\SeoRevisions\SeoRevisions() : new Common\SeoRevisions\SeoRevisions(); $this->ai = $this->pro ? new Pro\Ai\Ai() : null; $this->filters = $this->pro ? new Pro\Main\Filters() : new Lite\Main\Filters(); $this->crawlCleanup = new Common\QueryArgs\CrawlCleanup(); $this->searchCleanup = new Common\SearchCleanup\SearchCleanup(); $this->emailReports = new Common\EmailReports\EmailReports(); $this->thirdParty = new Common\ThirdParty\ThirdParty(); $this->writingAssistant = new Common\WritingAssistant\WritingAssistant(); if ( ! wp_doing_ajax() && ! wp_doing_cron() ) { $this->rss = new Common\Rss(); $this->main = $this->pro ? new Pro\Main\Main() : new Common\Main\Main(); $this->head = $this->pro ? new Pro\Main\Head() : new Common\Main\Head(); $this->dashboard = $this->pro ? new Pro\Admin\Dashboard() : new Common\Admin\Dashboard(); $this->api = $this->pro ? new Pro\Api\Api() : new Lite\Api\Api(); $this->help = new Common\Help\Help(); } $this->backwardsCompatibilityLoad(); add_action( 'init', [ $this, 'loadInit' ], 999 ); } /** * Things that need to load after init. * * @since 4.0.0 * * @return void */ public function loadInit() { $this->settings = new Common\Utils\VueSettings( '_aioseo_settings' ); $this->sitemap->init(); $this->badBotBlocker->init(); // We call this again to reset any post types/taxonomies that have not yet been set up. $this->dynamicOptions->refresh(); } /** * Loads our addons. * * Runs right after the plugins_loaded hook. * * @since 4.0.0 * * @return void */ public function loadAddons() { do_action( 'aioseo_loaded' ); } } } namespace { // Exit if accessed directly. if ( ! defined( 'ABSPATH' ) ) { exit; } /** * The function which returns the one AIOSEO instance. * * @since 4.0.0 * * @return AIOSEO\Plugin\AIOSEO The instance. */ function aioseo() { return AIOSEO\Plugin\AIOSEO::instance(); } }Common/Social/Image.php000064400000017435150212212220010740 0ustar00type = $type; $this->post = $post; $this->thumbnailSize = apply_filters( 'aioseo_thumbnail_size', 'fullsize' ); $hash = md5( wp_json_encode( [ $type, $imageSource, $post ] ) ); static $images = []; if ( isset( $images[ $hash ] ) ) { return $images[ $hash ]; } if ( 'auto' === $imageSource && aioseo()->helpers->getPostPageBuilderName( $post->ID ) ) { $imageSource = 'default'; } if ( is_a( $this->post, 'WP_Post' ) ) { switch ( $imageSource ) { case 'featured': $image = $this->getFeaturedImage(); break; case 'attach': $image = $this->getFirstAttachedImage(); break; case 'content': $image = $this->getFirstImageInContent(); break; case 'author': $image = $this->getAuthorAvatar(); break; case 'auto': $image = $this->getFirstAvailableImage(); break; case 'custom': $image = $this->getCustomFieldImage(); break; case 'custom_image': $metaData = aioseo()->meta->metaData->getMetaData( $post ); if ( empty( $metaData ) ) { break; } $image = 'facebook' === strtolower( $this->type ) ? $metaData->og_image_custom_url : $metaData->twitter_image_custom_url; break; case 'default': default: $image = aioseo()->options->social->{$this->type}->general->defaultImagePosts; } } if ( empty( $image ) ) { $image = aioseo()->options->social->{$this->type}->general->defaultImagePosts; } if ( is_array( $image ) ) { $images[ $hash ] = $image; return $images[ $hash ]; } $imageWithoutDimensions = aioseo()->helpers->removeImageDimensions( $image ); $attachmentId = aioseo()->helpers->attachmentUrlToPostId( $imageWithoutDimensions ); $images[ $hash ] = $attachmentId ? wp_get_attachment_image_src( $attachmentId, $this->thumbnailSize ) : $image; return $images[ $hash ]; } /** * Returns the Featured Image for the post. * * @since 4.0.0 * * @return array The image data. */ private function getFeaturedImage() { $cachedImage = $this->getCachedImage(); if ( $cachedImage ) { return $cachedImage; } $imageId = get_post_thumbnail_id( $this->post->ID ); return $imageId ? wp_get_attachment_image_src( $imageId, $this->thumbnailSize ) : ''; } /** * Returns the first attached image. * * @since 4.0.0 * * @return string The image data. */ private function getFirstAttachedImage() { $cachedImage = $this->getCachedImage(); if ( $cachedImage ) { return $cachedImage; } if ( 'attachment' === get_post_type( $this->post->ID ) ) { return wp_get_attachment_image_src( $this->post->ID, $this->thumbnailSize ); } $attachments = get_children( [ 'post_parent' => $this->post->ID, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', ] ); return $attachments && count( $attachments ) ? wp_get_attachment_image_src( array_values( $attachments )[0]->ID, $this->thumbnailSize ) : ''; } /** * Returns the first image found in the post content. * * @since 4.0.0 * * @return string The image URL. */ private function getFirstImageInContent() { $cachedImage = $this->getCachedImage(); if ( $cachedImage ) { return $cachedImage; } $postContent = aioseo()->helpers->getPostContent( $this->post ); preg_match_all( '||i', (string) $postContent, $matches ); // phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage // Ignore cover block background image - WP >= 5.7. if ( ! empty( $matches[0] ) && apply_filters( 'aioseo_social_image_ignore_cover_block', true, $this->post, $matches ) ) { foreach ( $matches[0] as $key => $match ) { if ( false !== stripos( $match, 'wp-block-cover__image-background' ) ) { unset( $matches[1][ $key ] ); } } } return ! empty( $matches[1] ) ? current( $matches[1] ) : ''; } /** * Returns the author avatar. * * @since 4.0.0 * * @return string The image URL. */ private function getAuthorAvatar() { $avatar = get_avatar( $this->post->post_author, 300 ); preg_match( "/src='(.*?)'/i", (string) $avatar, $matches ); return ! empty( $matches[1] ) ? $matches[1] : ''; } /** * Returns the first available image. * * @since 4.0.0 * * @return string The image URL. */ private function getFirstAvailableImage() { // Disable the cache. $this->useCache = false; $image = $this->getCustomFieldImage(); if ( ! $image ) { $image = $this->getFeaturedImage(); } if ( ! $image ) { $image = $this->getFirstAttachedImage(); } if ( ! $image ) { $image = $this->getFirstImageInContent(); } if ( ! $image && 'twitter' === strtolower( $this->type ) ) { $image = aioseo()->options->social->twitter->homePage->image; } // Enable the cache. $this->useCache = true; return $image ? $image : aioseo()->options->social->facebook->homePage->image; } /** * Returns the image from a custom field. * * @since 4.0.0 * * @return string The image URL. */ private function getCustomFieldImage() { $cachedImage = $this->getCachedImage(); if ( $cachedImage ) { return $cachedImage; } $prefix = 'facebook' === strtolower( $this->type ) ? 'og_' : 'twitter_'; $aioseoPost = Models\Post::getPost( $this->post->ID ); $customFields = ! empty( $aioseoPost->{ $prefix . 'image_custom_fields' } ) ? $aioseoPost->{ $prefix . 'image_custom_fields' } : aioseo()->options->social->{$this->type}->general->customFieldImagePosts; if ( ! $customFields ) { return ''; } $customFields = explode( ',', $customFields ); foreach ( $customFields as $customField ) { $image = get_post_meta( $this->post->ID, $customField, true ); if ( ! empty( $image ) ) { $image = is_array( $image ) ? $image[0] : $image; return is_numeric( $image ) ? wp_get_attachment_image_src( $image, $this->thumbnailSize ) : $image; } } return ''; } /** * Returns the cached image if there is one. * * @since 4.1.6.2 * * @param \WP_Term $object The object for which we need to get the cached image. * @return string|array The image URL or data. */ protected function getCachedImage( $object = null ) { if ( null === $object ) { // This isn't null if we call it from the Pro class. $object = $this->post; } $metaData = aioseo()->meta->metaData->getMetaData( $object ); switch ( $this->type ) { case 'facebook': if ( ! empty( $metaData->og_image_url ) && $this->useCache ) { return aioseo()->meta->metaData->getCachedOgImage( $metaData ); } break; case 'twitter': if ( ! empty( $metaData->twitter_image_url ) && $this->useCache ) { return $metaData->twitter_image_url; } break; default: break; } return ''; } }Common/Social/Social.php000064400000010213150212212220011113 0ustar00image = new Image(); if ( wp_doing_ajax() || wp_doing_cron() ) { return; } $this->facebook = new Facebook(); $this->twitter = new Twitter(); $this->output = new Output(); $this->hooks(); } /** * Registers our hooks. * * @since 4.0.0 */ protected function hooks() { add_action( $this->bustOgCacheActionName, [ $this, 'bustOgCachePost' ] ); // To avoid duplicate sets of meta tags. add_filter( 'jetpack_enable_open_graph', '__return_false' ); if ( ! is_admin() ) { add_filter( 'language_attributes', [ $this, 'addAttributes' ] ); return; } // Forces a refresh of the Facebook cache. add_action( 'post_updated', [ $this, 'scheduleBustOgCachePost' ], 10, 2 ); } /** * Adds our attributes to the registered language attributes. * * @since 4.0.0 * @version 4.4.5 Adds trim function the html tag removing empty spaces. * * @param string $htmlTag The 'html' tag as a string. * @return string The filtered 'html' tag as a string. */ public function addAttributes( $htmlTag ) { if ( ! aioseo()->options->social->facebook->general->enable ) { return $htmlTag; } $attributes = apply_filters( 'aioseo_opengraph_attributes', [ 'prefix="og: https://ogp.me/ns#"' ] ); foreach ( $attributes as $attr ) { if ( strpos( $htmlTag, $attr ) === false ) { $htmlTag .= " $attr "; } } return trim( $htmlTag ); } /** * Schedule a ping to bust the OG cache. * * @since 4.2.0 * * @param int $postId The post ID. * @param \WP_Post $post The post object. * @return void */ public function scheduleBustOgCachePost( $postId, $post = null ) { if ( ! aioseo()->helpers->isSbCustomFacebookFeedActive() || ! aioseo()->helpers->isValidPost( $post ) ) { return; } if ( aioseo()->actionScheduler->isScheduled( $this->bustOgCacheActionName, [ 'postId' => $postId ] ) ) { return; } // Schedule the new ping. aioseo()->actionScheduler->scheduleAsync( $this->bustOgCacheActionName, [ 'postId' => $postId ] ); } /** * Pings Facebook and asks them to bust the OG cache for a particular post. * * @since 4.2.0 * * @see https://developers.facebook.com/docs/sharing/opengraph/using-objects#update * * @param int $postId The post ID. * @return void */ public function bustOgCachePost( $postId ) { $post = get_post( $postId ); $customAccessToken = apply_filters( 'aioseo_facebook_access_token', '' ); if ( ! aioseo()->helpers->isValidPost( $post ) || ( ! aioseo()->helpers->isSbCustomFacebookFeedActive() && ! $customAccessToken ) ) { return; } $permalink = get_permalink( $postId ); $this->bustOgCacheHelper( $permalink ); } /** * Helper function for bustOgCache(). * * @since 4.2.0 * * @param string $permalink The permalink. * @return void */ protected function bustOgCacheHelper( $permalink ) { $accessToken = aioseo()->helpers->getSbAccessToken(); $accessToken = apply_filters( 'aioseo_facebook_access_token', $accessToken ); if ( ! $accessToken ) { return; } $url = sprintf( 'https://graph.facebook.com/?%s', http_build_query( [ 'id' => $permalink, 'scrape' => true, 'access_token' => $accessToken ] ) ); wp_remote_post( $url, [ 'blocking' => false ] ); } }Common/Social/Output.php000064400000011065150212212220011207 0ustar00helpers->isWooCommerceShopPage() ) { return false; } return true; } /** * Returns the Open Graph meta. * * @since 4.0.0 * * @return array The Open Graph meta. */ public function getFacebookMeta() { if ( ! $this->isAllowed() || ! aioseo()->options->social->facebook->general->enable ) { return []; } $meta = [ 'og:locale' => aioseo()->social->facebook->getLocale(), 'og:site_name' => aioseo()->helpers->encodeOutputHtml( aioseo()->social->facebook->getSiteName() ), 'og:type' => aioseo()->social->facebook->getObjectType(), 'og:title' => aioseo()->helpers->encodeOutputHtml( aioseo()->social->facebook->getTitle() ), 'og:description' => aioseo()->helpers->encodeOutputHtml( aioseo()->social->facebook->getDescription() ), 'og:url' => esc_url( aioseo()->helpers->canonicalUrl() ), 'fb:app_id' => aioseo()->options->social->facebook->advanced->appId, 'fb:admins' => implode( ',', array_map( 'trim', explode( ',', aioseo()->options->social->facebook->advanced->adminId ) ) ), ]; $image = aioseo()->social->facebook->getImage(); if ( $image ) { $image = is_array( $image ) ? $image[0] : $image; $image = aioseo()->helpers->makeUrlAbsolute( $image ); $image = set_url_scheme( esc_url( $image ) ); $meta += [ 'og:image' => $image, 'og:image:secure_url' => is_ssl() ? $image : '', 'og:image:width' => aioseo()->social->facebook->getImageWidth(), 'og:image:height' => aioseo()->social->facebook->getImageHeight(), ]; } $video = aioseo()->social->facebook->getVideo(); if ( $video ) { $video = set_url_scheme( esc_url( $video ) ); $meta += [ 'og:video' => $video, 'og:video:secure_url' => is_ssl() ? $video : '', 'og:video:width' => aioseo()->social->facebook->getVideoWidth(), 'og:video:height' => aioseo()->social->facebook->getVideoHeight(), ]; } if ( ! empty( $meta['og:type'] ) && 'article' === $meta['og:type'] ) { $meta += [ 'article:section' => aioseo()->social->facebook->getSection(), 'article:tag' => aioseo()->social->facebook->getArticleTags(), 'article:published_time' => aioseo()->social->facebook->getPublishedTime(), 'article:modified_time' => aioseo()->social->facebook->getModifiedTime(), 'article:publisher' => aioseo()->social->facebook->getPublisher(), 'article:author' => aioseo()->social->facebook->getAuthor() ]; } return array_filter( apply_filters( 'aioseo_facebook_tags', $meta ) ); } /** * Returns the Twitter meta. * * @since 4.0.0 * * @return array The Twitter meta. */ public function getTwitterMeta() { if ( ! $this->isAllowed() || ! aioseo()->options->social->twitter->general->enable ) { return []; } $meta = [ 'twitter:card' => aioseo()->social->twitter->getCardType(), 'twitter:site' => aioseo()->social->twitter->prepareUsername( aioseo()->social->twitter->getTwitterUrl() ), 'twitter:title' => aioseo()->helpers->encodeOutputHtml( aioseo()->social->twitter->getTitle() ), 'twitter:description' => aioseo()->helpers->encodeOutputHtml( aioseo()->social->twitter->getDescription() ), 'twitter:creator' => aioseo()->social->twitter->getCreator() ]; $image = aioseo()->social->twitter->getImage(); if ( $image ) { $image = is_array( $image ) ? $image[0] : $image; $image = aioseo()->helpers->makeUrlAbsolute( $image ); // Set the twitter image meta. $meta['twitter:image'] = $image; } if ( is_singular() ) { $additionalData = apply_filters( 'aioseo_social_twitter_additional_data', aioseo()->social->twitter->getAdditionalData() ); if ( $additionalData ) { $i = 1; foreach ( $additionalData as $data ) { $meta[ "twitter:label$i" ] = $data['label']; $meta[ "twitter:data$i" ] = $data['value']; $i++; } } } return array_filter( apply_filters( 'aioseo_twitter_tags', $meta ) ); } }Common/Social/Facebook.php000064400000040211150212212220011413 0ustar00helpers->getPost( $postId ); if ( is_home() && 'posts' === get_option( 'show_on_front' ) ) { $image = aioseo()->options->social->facebook->homePage->image; if ( empty( $image ) ) { $image = aioseo()->social->image->getImage( 'facebook', aioseo()->options->social->facebook->general->defaultImageSourcePosts, $post ); } return $image; } $metaData = aioseo()->meta->metaData->getMetaData( $post ); $image = ''; if ( ! empty( $metaData ) ) { $imageSource = ! empty( $metaData->og_image_type ) && 'default' !== $metaData->og_image_type ? $metaData->og_image_type : aioseo()->options->social->facebook->general->defaultImageSourcePosts; $image = aioseo()->social->image->getImage( 'facebook', $imageSource, $post ); } // Since we could be on an archive page, let's check again for that default image. if ( ! $image ) { $image = aioseo()->social->image->getImage( 'facebook', 'default' ); } if ( ! $image ) { $image = aioseo()->helpers->getSiteLogoUrl(); } // Allow users to control the default image per post type. return apply_filters( 'aioseo_opengraph_default_image', $image, [ $post, $this->getObjectType() ] ); } /** * Returns the width of the Open Graph image. * * @since 4.0.0 * * @return string The image width. */ public function getImageWidth() { if ( is_home() && 'posts' === get_option( 'show_on_front' ) ) { $width = aioseo()->options->social->facebook->homePage->imageWidth; return $width ? $width : aioseo()->options->social->facebook->general->defaultImagePostsWidth; } $metaData = aioseo()->meta->metaData->getMetaData(); if ( ! empty( $metaData->og_custom_image_width ) ) { return $metaData->og_custom_image_width; } $image = $this->getImage(); if ( is_array( $image ) ) { return $image[1]; } return aioseo()->options->social->facebook->general->defaultImagePostsWidth; } /** * Returns the height of the Open Graph image. * * @since 4.0.0 * * @return string The image height. */ public function getImageHeight() { if ( is_home() && 'posts' === get_option( 'show_on_front' ) ) { $height = aioseo()->options->social->facebook->homePage->imageHeight; return $height ? $height : aioseo()->options->social->facebook->general->defaultImagePostsHeight; } $metaData = aioseo()->meta->metaData->getMetaData(); if ( ! empty( $metaData->og_custom_image_height ) ) { return $metaData->og_custom_image_height; } $image = $this->getImage(); if ( is_array( $image ) ) { return $image[2]; } return aioseo()->options->social->facebook->general->defaultImagePostsHeight; } /** * Returns the Open Graph video URL. * * @since 4.0.0 * * @return string The video URL. */ public function getVideo() { $metaData = aioseo()->meta->metaData->getMetaData(); return ! empty( $metaData->og_video ) ? $metaData->og_video : ''; } /** * Returns the width of the video. * * @since 4.0.0 * * @return string The video width. */ public function getVideoWidth() { $metaData = aioseo()->meta->metaData->getMetaData(); return ! empty( $metaData->og_video_width ) ? $metaData->og_video_width : ''; } /** * Returns the height of the video. * * @since 4.0.0 * * @return string The video height. */ public function getVideoHeight() { $metaData = aioseo()->meta->metaData->getMetaData(); return ! empty( $metaData->og_video_height ) ? $metaData->og_video_height : ''; } /** * Returns the site name. * * @since 4.0.0 * * @return string The site name. */ public function getSiteName() { $title = aioseo()->helpers->decodeHtmlEntities( aioseo()->tags->replaceTags( aioseo()->options->social->facebook->general->siteName, get_the_ID() ) ); if ( ! $title ) { $title = aioseo()->helpers->decodeHtmlEntities( get_bloginfo( 'name' ) ); } return wp_strip_all_tags( $title ); } /** * Returns the Open Graph object type. * * @since 4.0.0 * * @return string The object type. */ public function getObjectType() { if ( is_home() && 'posts' === get_option( 'show_on_front' ) ) { $type = aioseo()->options->social->facebook->homePage->objectType; return $type ? $type : 'website'; } if ( is_post_type_archive() ) { return 'website'; } $post = aioseo()->helpers->getPost(); $metaData = aioseo()->meta->metaData->getMetaData( $post ); if ( ! empty( $metaData->og_object_type ) && 'default' !== $metaData->og_object_type ) { return $metaData->og_object_type; } $postType = get_post_type(); $dynamicOptions = aioseo()->dynamicOptions->noConflict(); $defaultObjectType = $dynamicOptions->social->facebook->general->postTypes->has( $postType ) ? $dynamicOptions->social->facebook->general->postTypes->$postType->objectType : ''; return ! empty( $defaultObjectType ) ? $defaultObjectType : 'article'; } /** * Returns the Open Graph title for the current page. * * @since 4.0.0 * * @param \WP_Post|integer $post The post object or ID (optional). * @return string The Open Graph title. */ public function getTitle( $post = null ) { if ( is_home() && 'posts' === get_option( 'show_on_front' ) ) { $title = aioseo()->meta->title->helpers->prepare( aioseo()->options->social->facebook->homePage->title ); return $title ? $title : aioseo()->meta->title->getTitle(); } $post = aioseo()->helpers->getPost( $post ); $metaData = aioseo()->meta->metaData->getMetaData( $post ); $title = ''; if ( ! empty( $metaData->og_title ) ) { $title = aioseo()->meta->title->helpers->prepare( $metaData->og_title ); } if ( is_post_type_archive() ) { $postType = get_queried_object(); if ( is_a( $postType, 'WP_Post_Type' ) ) { $dynamicOptions = aioseo()->dynamicOptions->noConflict(); if ( $dynamicOptions->searchAppearance->archives->has( $postType->name ) ) { $title = aioseo()->meta->title->helpers->prepare( aioseo()->dynamicOptions->searchAppearance->archives->{ $postType->name }->title ); } } } return $title ? $title : ( $post ? aioseo()->meta->title->getPostTitle( $post ) : $title ); } /** * Returns the Open Graph description. * * @since 4.0.0 * * @param \WP_Post|integer $post The post object or ID (optional). * @return string The Open Graph description. */ public function getDescription( $post = null ) { if ( is_home() && 'posts' === get_option( 'show_on_front' ) ) { $description = aioseo()->meta->description->helpers->prepare( aioseo()->options->social->facebook->homePage->description ); return $description ? $description : aioseo()->meta->description->getDescription(); } $post = aioseo()->helpers->getPost( $post ); $metaData = aioseo()->meta->metaData->getMetaData( $post ); $description = ''; if ( ! empty( $metaData->og_description ) ) { $description = aioseo()->meta->description->helpers->prepare( $metaData->og_description ); } if ( is_post_type_archive() ) { $postType = get_queried_object(); if ( is_a( $postType, 'WP_Post_Type' ) ) { $dynamicOptions = aioseo()->dynamicOptions->noConflict(); if ( $dynamicOptions->searchAppearance->archives->has( $postType->name ) ) { $description = aioseo()->meta->description->helpers->prepare( aioseo()->dynamicOptions->searchAppearance->archives->{ $postType->name }->metaDescription ); } } } return $description ? $description : ( $post ? aioseo()->meta->description->getPostDescription( $post ) : $description ); } /** * Returns the Open Graph article section name. * * @since 4.0.0 * * @return string The article section name. */ public function getSection() { $metaData = aioseo()->meta->metaData->getMetaData(); return ! empty( $metaData->og_article_section ) ? $metaData->og_article_section : ''; } /** * Returns the Open Graph publisher URL. * * @since 4.0.0 * * @return string The Open Graph publisher URL. */ public function getPublisher() { if ( ! aioseo()->options->social->profiles->sameUsername->enable ) { return aioseo()->options->social->profiles->urls->facebookPageUrl; } $username = aioseo()->options->social->profiles->sameUsername->username; return ( $username && in_array( 'facebookPageUrl', aioseo()->options->social->profiles->sameUsername->included, true ) ) ? 'https://facebook.com/' . $username : ''; } /** * Returns the published time. * * @since 4.0.0 * * @return string The published time. */ public function getPublishedTime() { $post = aioseo()->helpers->getPost(); return $post ? aioseo()->helpers->dateTimeToIso8601( $post->post_date_gmt ) : ''; } /** * Returns the last modified time. * * @since 4.0.0 * * @return string The last modified time. */ public function getModifiedTime() { $post = aioseo()->helpers->getPost(); return $post ? aioseo()->helpers->dateTimeToIso8601( $post->post_modified_gmt ) : ''; } /** * Returns the Open Graph author. * * @since 4.0.0 * * @return string The Open Graph author. */ public function getAuthor() { $post = aioseo()->helpers->getPost(); if ( ! is_a( $post, 'WP_Post' ) || ! aioseo()->options->social->facebook->general->showAuthor ) { return ''; } $author = ''; $userProfiles = $this->getUserProfiles( $post->post_author ); if ( ! empty( $userProfiles['facebookPageUrl'] ) ) { $author = $userProfiles['facebookPageUrl']; } if ( empty( $author ) ) { $author = aioseo()->options->social->facebook->advanced->authorUrl; } return $author; } /** * Returns the Open Graph article tags. * * @since 4.0.0 * * @return array An array of unique keywords. */ public function getArticleTags() { $post = aioseo()->helpers->getPost(); $metaData = aioseo()->meta->metaData->getMetaData( $post ); $tags = ! empty( $metaData->og_article_tags ) ? aioseo()->meta->keywords->extractMetaKeywords( $metaData->og_article_tags ) : []; if ( $post && aioseo()->options->social->facebook->advanced->enable && aioseo()->options->social->facebook->advanced->generateArticleTags ) { if ( aioseo()->options->social->facebook->advanced->useKeywordsInTags ) { $keywords = aioseo()->meta->keywords->getKeywords(); $keywords = aioseo()->tags->parseCustomFields( $keywords ); $keywords = aioseo()->meta->keywords->keywordStringToList( $keywords ); $tags = array_merge( $tags, $keywords ); } if ( aioseo()->options->social->facebook->advanced->useCategoriesInTags ) { $tags = array_merge( $tags, aioseo()->helpers->getAllCategories( $post->ID ) ); } if ( aioseo()->options->social->facebook->advanced->usePostTagsInTags ) { $tags = array_merge( $tags, aioseo()->helpers->getAllTags( $post->ID ) ); } } return aioseo()->meta->keywords->getUniqueKeywords( $tags, false ); } /** * Retreive the locale. * * @since 4.1.4 * * @return string The locale. */ public function getLocale() { $locale = get_locale(); // These are the locales FB supports. $validLocales = [ 'af_ZA', // Afrikaans. 'ak_GH', // Akan. 'am_ET', // Amharic. 'ar_AR', // Arabic. 'as_IN', // Assamese. 'ay_BO', // Aymara. 'az_AZ', // Azerbaijani. 'be_BY', // Belarusian. 'bg_BG', // Bulgarian. 'bp_IN', // Bhojpuri. 'bn_IN', // Bengali. 'br_FR', // Breton. 'bs_BA', // Bosnian. 'ca_ES', // Catalan. 'cb_IQ', // Sorani Kurdish. 'ck_US', // Cherokee. 'co_FR', // Corsican. 'cs_CZ', // Czech. 'cx_PH', // Cebuano. 'cy_GB', // Welsh. 'da_DK', // Danish. 'de_DE', // German. 'el_GR', // Greek. 'en_GB', // English (UK). 'en_PI', // English (Pirate). 'en_UD', // English (Upside Down). 'en_US', // English (US). 'em_ZM', 'eo_EO', // Esperanto. 'es_ES', // Spanish (Spain). 'es_LA', // Spanish. 'es_MX', // Spanish (Mexico). 'et_EE', // Estonian. 'eu_ES', // Basque. 'fa_IR', // Persian. 'fb_LT', // Leet Speak. 'ff_NG', // Fulah. 'fi_FI', // Finnish. 'fo_FO', // Faroese. 'fr_CA', // French (Canada). 'fr_FR', // French (France). 'fy_NL', // Frisian. 'ga_IE', // Irish. 'gl_ES', // Galician. 'gn_PY', // Guarani. 'gu_IN', // Gujarati. 'gx_GR', // Classical Greek. 'ha_NG', // Hausa. 'he_IL', // Hebrew. 'hi_IN', // Hindi. 'hr_HR', // Croatian. 'hu_HU', // Hungarian. 'ht_HT', // Haitian Creole. 'hy_AM', // Armenian. 'id_ID', // Indonesian. 'ig_NG', // Igbo. 'is_IS', // Icelandic. 'it_IT', // Italian. 'ik_US', 'iu_CA', 'ja_JP', // Japanese. 'ja_KS', // Japanese (Kansai). 'jv_ID', // Javanese. 'ka_GE', // Georgian. 'kk_KZ', // Kazakh. 'km_KH', // Khmer. 'kn_IN', // Kannada. 'ko_KR', // Korean. 'ks_IN', // Kashmiri. 'ku_TR', // Kurdish (Kurmanji). 'ky_KG', // Kyrgyz. 'la_VA', // Latin. 'lg_UG', // Ganda. 'li_NL', // Limburgish. 'ln_CD', // Lingala. 'lo_LA', // Lao. 'lt_LT', // Lithuanian. 'lv_LV', // Latvian. 'mg_MG', // Malagasy. 'mi_NZ', // Maori. 'mk_MK', // Macedonian. 'ml_IN', // Malayalam. 'mn_MN', // Mongolian. 'mr_IN', // Marathi. 'ms_MY', // Malay. 'mt_MT', // Maltese. 'my_MM', // Burmese. 'nb_NO', // Norwegian (bokmal). 'nd_ZW', // Ndebele. 'ne_NP', // Nepali. 'nl_BE', // Dutch (Belgie). 'nl_NL', // Dutch. 'nn_NO', // Norwegian (nynorsk). 'nr_ZA', // Southern Ndebele. 'ns_ZA', // Northern Sotho. 'ny_MW', // Chewa. 'om_ET', // Oromo. 'or_IN', // Oriya. 'pa_IN', // Punjabi. 'pl_PL', // Polish. 'ps_AF', // Pashto. 'pt_BR', // Portuguese (Brazil). 'pt_PT', // Portuguese (Portugal). 'qc_GT', // Quiché. 'qu_PE', // Quechua. 'qr_GR', 'qz_MM', // Burmese (Zawgyi). 'rm_CH', // Romansh. 'ro_RO', // Romanian. 'ru_RU', // Russian. 'rw_RW', // Kinyarwanda. 'sa_IN', // Sanskrit. 'sc_IT', // Sardinian. 'se_NO', // Northern Sami. 'si_LK', // Sinhala. 'su_ID', // Sundanese. 'sk_SK', // Slovak. 'sl_SI', // Slovenian. 'sn_ZW', // Shona. 'so_SO', // Somali. 'sq_AL', // Albanian. 'sr_RS', // Serbian. 'ss_SZ', // Swazi. 'st_ZA', // Southern Sotho. 'sv_SE', // Swedish. 'sw_KE', // Swahili. 'sy_SY', // Syriac. 'sz_PL', // Silesian. 'ta_IN', // Tamil. 'te_IN', // Telugu. 'tg_TJ', // Tajik. 'th_TH', // Thai. 'tk_TM', // Turkmen. 'tl_PH', // Filipino. 'tl_ST', // Klingon. 'tn_BW', // Tswana. 'tr_TR', // Turkish. 'ts_ZA', // Tsonga. 'tt_RU', // Tatar. 'tz_MA', // Tamazight. 'uk_UA', // Ukrainian. 'ur_PK', // Urdu. 'uz_UZ', // Uzbek. 've_ZA', // Venda. 'vi_VN', // Vietnamese. 'wo_SN', // Wolof. 'xh_ZA', // Xhosa. 'yi_DE', // Yiddish. 'yo_NG', // Yoruba. 'zh_CN', // Simplified Chinese (China). 'zh_HK', // Traditional Chinese (Hong Kong). 'zh_TW', // Traditional Chinese (Taiwan). 'zu_ZA', // Zulu. 'zz_TR', // Zazaki. ]; // Catch some weird locales served out by WP that are not easily doubled up. $fixLocales = [ 'ca' => 'ca_ES', 'en' => 'en_US', 'el' => 'el_GR', 'et' => 'et_EE', 'ja' => 'ja_JP', 'sq' => 'sq_AL', 'uk' => 'uk_UA', 'vi' => 'vi_VN', 'zh' => 'zh_CN', ]; if ( isset( $fixLocales[ $locale ] ) ) { $locale = $fixLocales[ $locale ]; } // Convert locales like "es" to "es_ES", in case that works for the given locale (sometimes it does). if ( 2 === strlen( $locale ) ) { $locale = strtolower( $locale ) . '_' . strtoupper( $locale ); } // Check to see if the locale is a valid FB one, if not, use en_US as a fallback. if ( ! in_array( $locale, $validLocales, true ) ) { $locale = strtolower( substr( $locale, 0, 2 ) ) . '_' . strtoupper( substr( $locale, 0, 2 ) ); if ( ! in_array( $locale, $validLocales, true ) ) { $locale = 'en_US'; } } return apply_filters( 'aioseo_og_locale', $locale ); } }Common/Social/Twitter.php000064400000017724150212212220011361 0ustar00options->social->profiles->sameUsername->enable ) { return aioseo()->options->social->profiles->urls->twitterUrl; } $userName = aioseo()->options->social->profiles->sameUsername->username; return ( $userName && in_array( 'twitterUrl', aioseo()->options->social->profiles->sameUsername->included, true ) ) ? 'https://x.com/' . $userName : ''; } /** * Returns the Twitter card type. * * @since 4.0.0 * * @return string $card The card type. */ public function getCardType() { if ( is_home() && 'posts' === get_option( 'show_on_front' ) ) { return aioseo()->options->social->twitter->homePage->cardType; } $metaData = aioseo()->meta->metaData->getMetaData(); return ! empty( $metaData->twitter_card ) && 'default' !== $metaData->twitter_card ? $metaData->twitter_card : aioseo()->options->social->twitter->general->defaultCardType; } /** * Returns the Twitter creator. * * @since 4.0.0 * * @return string The creator. */ public function getCreator() { $post = aioseo()->helpers->getPost(); if ( ! is_a( $post, 'WP_Post' ) || ! post_type_supports( $post->post_type, 'author' ) || ! aioseo()->options->social->twitter->general->showAuthor ) { return ''; } $author = ''; $userProfiles = $this->getUserProfiles( $post->post_author ); if ( ! empty( $userProfiles['twitterUrl'] ) ) { $author = $userProfiles['twitterUrl']; } if ( empty( $author ) ) { $author = aioseo()->social->twitter->getTwitterUrl(); } $author = aioseo()->social->twitter->prepareUsername( $author ); return $author; } /** * Returns the Twitter image URL. * * @since 4.0.0 * * @param int $postId The post ID (optional). * @return string The image URL. */ public function getImage( $postId = null ) { $post = aioseo()->helpers->getPost( $postId ); if ( is_home() && 'posts' === get_option( 'show_on_front' ) ) { $image = aioseo()->options->social->twitter->homePage->image; if ( empty( $image ) ) { $image = aioseo()->options->social->facebook->homePage->image; } if ( empty( $image ) ) { $image = aioseo()->social->image->getImage( 'twitter', aioseo()->options->social->twitter->general->defaultImageSourcePosts, $post ); } return $image ? $image : aioseo()->social->facebook->getImage(); } $metaData = aioseo()->meta->metaData->getMetaData( $post ); if ( ! empty( $metaData->twitter_use_og ) ) { return aioseo()->social->facebook->getImage(); } $image = ''; if ( ! empty( $metaData ) ) { $imageSource = ! empty( $metaData->twitter_image_type ) && 'default' !== $metaData->twitter_image_type ? $metaData->twitter_image_type : aioseo()->options->social->twitter->general->defaultImageSourcePosts; $image = aioseo()->social->image->getImage( 'twitter', $imageSource, $post ); } return $image ? $image : aioseo()->social->facebook->getImage(); } /** * Returns the Twitter title for the current page. * * @since 4.0.0 * * @param \WP_Post|integer $post The post object or ID (optional). * @return string The Twitter title. */ public function getTitle( $post = null ) { if ( is_home() && 'posts' === get_option( 'show_on_front' ) ) { $title = aioseo()->meta->title->helpers->prepare( aioseo()->options->social->twitter->homePage->title ); return $title ? $title : aioseo()->social->facebook->getTitle( $post ); } $post = aioseo()->helpers->getPost( $post ); $metaData = aioseo()->meta->metaData->getMetaData( $post ); if ( ! empty( $metaData->twitter_use_og ) ) { return aioseo()->social->facebook->getTitle( $post ); } $title = ''; if ( ! empty( $metaData->twitter_title ) ) { $title = aioseo()->meta->title->helpers->prepare( $metaData->twitter_title ); } return $title ? $title : aioseo()->social->facebook->getTitle( $post ); } /** * Returns the Twitter description for the current page. * * @since 4.0.0 * * @param \WP_Post|integer $post The post object or ID (optional). * @return string The Twitter description. */ public function getDescription( $post = null ) { if ( is_home() && 'posts' === get_option( 'show_on_front' ) ) { $description = aioseo()->meta->description->helpers->prepare( aioseo()->options->social->twitter->homePage->description ); return $description ? $description : aioseo()->social->facebook->getDescription( $post ); } $post = aioseo()->helpers->getPost( $post ); $metaData = aioseo()->meta->metaData->getMetaData( $post ); if ( ! empty( $metaData->twitter_use_og ) ) { return aioseo()->social->facebook->getDescription( $post ); } $description = ''; if ( ! empty( $metaData->twitter_description ) ) { $description = aioseo()->meta->description->helpers->prepare( $metaData->twitter_description ); } return $description ? $description : aioseo()->social->facebook->getDescription( $post ); } /** * Prepare twitter username for public display. * * We do things like strip out the URL, etc and return just (at)username. * At the moment, we'll check for 1 of 3 things... (at)username, username, and https://x.com/username. * * @since 4.0.0 * * @param string $profile Twitter username. * @param boolean $includeAt Whether or not ot include the @ sign. * @return string Full Twitter username. */ public function prepareUsername( $profile, $includeAt = true ) { if ( ! $profile ) { return $profile; } $profile = (string) $profile; if ( preg_match( '/^(\@)?[A-Za-z0-9_]+$/', (string) $profile ) ) { if ( '@' !== $profile[0] && $includeAt ) { $profile = '@' . $profile; } elseif ( '@' === $profile[0] && ! $includeAt ) { $profile = ltrim( $profile, '@' ); } } if ( strpos( $profile, 'twitter.com' ) || strpos( $profile, 'x.com' ) ) { $profile = esc_url( $profile ); // Extract the twitter username from the URL. $parsedTwitterProfile = wp_parse_url( $profile ); $path = $parsedTwitterProfile['path']; $pathParts = explode( '/', $path ); $profile = $pathParts[1]; if ( $profile ) { if ( '@' !== $profile[0] && $includeAt ) { $profile = '@' . $profile; } if ( '@' === $profile[0] && ! $includeAt ) { $profile = ltrim( $profile, '@' ); } } } return $profile; } /** * Get additional twitter data. * * @since 4.0.0 * * @return array An array of additional twitter data. */ public function getAdditionalData() { if ( ! aioseo()->options->social->twitter->general->additionalData ) { return []; } $data = []; $post = aioseo()->helpers->getPost(); if ( ! is_a( $post, 'WP_Post' ) ) { return $data; } if ( $post->post_author && post_type_supports( $post->post_type, 'author' ) ) { $data[] = [ 'label' => __( 'Written by', 'all-in-one-seo-pack' ), 'value' => get_the_author_meta( 'display_name', $post->post_author ) ]; } if ( ! empty( $post->post_content ) ) { $minutes = $this->getReadingTime( $post->post_content ); if ( ! empty( $minutes ) ) { $data[] = [ 'label' => __( 'Est. reading time', 'all-in-one-seo-pack' ), // Translators: 1 - The estimated reading time. 'value' => sprintf( _n( '%1$s minute', '%1$s minutes', $minutes, 'all-in-one-seo-pack' ), $minutes ) ]; } } return $data; } /** * Returns the estimated reading time for a string. * * @since 4.0.0 * * @param string $string The string to count. * @return integer The estimated reading time as an integer. */ private function getReadingTime( $string ) { $wpm = 200; $word = str_word_count( wp_strip_all_tags( $string ) ); return round( $word / $wpm ); } }Common/WritingAssistant/Utils/Helpers.php000064400000003036150212212220014513 0ustar00 get_the_ID(), 'report' => $keyword, 'keywordText' => ! empty( $keyword->keyword ) ? $keyword->keyword : '', 'contentAnalysis' => Models\WritingAssistantPost::getContentAnalysis( get_the_ID() ), 'seoBoost' => [ 'isLoggedIn' => aioseo()->writingAssistant->seoBoost->isLoggedIn(), 'loginUrl' => aioseo()->writingAssistant->seoBoost->getLoginUrl(), 'createAccountUrl' => aioseo()->writingAssistant->seoBoost->getCreateAccountUrl(), 'userOptions' => aioseo()->writingAssistant->seoBoost->getUserOptions() ] ]; } /** * Gets the data for vue. * * @since 4.7.4 * * @return array An array of data. */ public function getSettingsVueData() { return [ 'seoBoost' => [ 'isLoggedIn' => aioseo()->writingAssistant->seoBoost->isLoggedIn(), 'loginUrl' => aioseo()->writingAssistant->seoBoost->getLoginUrl(), 'createAccountUrl' => aioseo()->writingAssistant->seoBoost->getCreateAccountUrl(), 'userOptions' => aioseo()->writingAssistant->seoBoost->getUserOptions() ] ]; } }Common/WritingAssistant/SeoBoost/SeoBoost.php000064400000057732150212212220015317 0ustar00service = new Service(); $returnParam = isset( $_GET['aioseo-writing-assistant'] ) // phpcs:ignore HM.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Recommended ? sanitize_text_field( wp_unslash( $_GET['aioseo-writing-assistant'] ) ) // phpcs:ignore HM.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Recommended : null; if ( 'auth_return' === $returnParam ) { add_action( 'init', [ $this, 'checkToken' ], 50 ); } if ( 'ms_logged_in' === $returnParam ) { add_action( 'init', [ $this, 'marketingSiteCallback' ], 50 ); } add_action( 'init', [ $this, 'migrateUserData' ], 10 ); add_action( 'init', [ $this, 'refreshUserOptionsAfterError' ] ); } /** * Returns if the user has an access key. * * @since 4.7.4 * * @return bool */ public function isLoggedIn() { return $this->getAccessToken() !== ''; } /** * Gets the login URL. * * @since 4.7.4 * * @return string The login URL. */ public function getLoginUrl() { $url = $this->loginUrl; if ( defined( 'AIOSEO_WRITING_ASSISTANT_LOGIN_URL' ) ) { $url = AIOSEO_WRITING_ASSISTANT_LOGIN_URL; } $params = [ 'oauth' => true, 'redirect' => get_site_url() . '?' . build_query( [ 'aioseo-writing-assistant' => 'auth_return' ] ), 'domain' => aioseo()->helpers->getMultiSiteDomain() ]; return trailingslashit( $url ) . '?' . build_query( $params ); } /** * Gets the login URL. * * @since 4.7.4 * * @return string The login URL. */ public function getCreateAccountUrl() { $url = $this->createAccountUrl; if ( defined( 'AIOSEO_WRITING_ASSISTANT_CREATE_ACCOUNT_URL' ) ) { $url = AIOSEO_WRITING_ASSISTANT_CREATE_ACCOUNT_URL; } $params = [ 'url' => base64_encode( get_site_url() . '?' . build_query( [ 'aioseo-writing-assistant' => 'ms_logged_in' ] ) ), 'writing-assistant-checkout' => true ]; return trailingslashit( $url ) . '?' . build_query( $params ); } /** * Gets the user's access token. * * @since 4.7.4 * * @return string The access token. */ public function getAccessToken() { $metaKey = 'seoboost_access_token_' . get_current_blog_id(); return get_user_meta( get_current_user_id(), $metaKey, true ); } /** * Sets the user's access token. * * @since 4.7.4 * * @return void */ public function setAccessToken( $accessToken ) { $metaKey = 'seoboost_access_token_' . get_current_blog_id(); update_user_meta( get_current_user_id(), $metaKey, $accessToken ); $this->refreshUserOptions(); } /** * Refreshes user options from SEOBoost. * * @since 4.7.4 * * @return void */ public function refreshUserOptions() { $userOptions = $this->service->getUserOptions(); if ( is_wp_error( $userOptions ) || ! empty( $userOptions['error'] ) ) { $userOptions = $this->getDefaultUserOptions(); aioseo()->cache->update( 'seoboost_get_user_options_error', time() + DAY_IN_SECONDS, MONTH_IN_SECONDS ); } $this->setUserOptions( $userOptions ); } /** * Gets the user options. * * @since 4.7.4 * * @param bool $refresh Whether to refresh the user options. * @return array The user options. */ public function getUserOptions( $refresh = false ) { if ( ! $refresh ) { $metaKey = 'seoboost_user_options_' . get_current_blog_id(); $userOptions = get_user_meta( get_current_user_id(), $metaKey, true ); if ( ! empty( $userOptions ) ) { return json_decode( (string) $userOptions, true ) ?? []; } } // If there are no options or we need to refresh them, get them from SEOBoost. $this->refreshUserOptions(); $userOptions = $this->getUserOptions(); if ( empty( $userOptions ) ) { return $this->getDefaultUserOptions(); } return $userOptions; } /** * Gets the user options. * * @since 4.7.4 * * @param array $options The user options. * @return void */ public function setUserOptions( $options ) { if ( ! is_array( $options ) ) { return; } $userOptions = array_merge( $this->getDefaultUserOptions(), $options ); $metaKey = 'seoboost_user_options_' . get_current_blog_id(); update_user_meta( get_current_user_id(), $metaKey, wp_json_encode( $userOptions ) ); } /** * Gets the user info from SEOBoost. * * @since 4.7.4 * * @return array|\WP_Error The user info or a WP_Error. */ public function getUserInfo() { return $this->service->getUserInfo(); } /** * Checks the token. * * @since 4.7.4 * * @return void */ public function checkToken() { $authToken = isset( $_GET['token'] ) // phpcs:ignore HM.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Recommended ? sanitize_key( wp_unslash( $_GET['token'] ) ) // phpcs:ignore HM.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Recommended : null; if ( $authToken ) { $accessToken = $this->service->getAccessToken( $authToken ); if ( ! is_wp_error( $accessToken ) && ! empty( $accessToken['token'] ) ) { $this->setAccessToken( $accessToken['token'] ); ?> core->db->delete( 'usermeta' )->whereRaw( 'meta_key LIKE \'seoboost_access_token%\'' )->run(); aioseo()->core->db->delete( 'usermeta' )->where( 'meta_key', 'seoboost_user_options' )->run(); } /** * Gets the report history. * * @since 4.7.4 * * @return array|\WP_Error The report history. */ public function getReportHistory() { return $this->service->getReportHistory(); } /** * Migrate Writing Assistant access tokens. * This handles the fix for multisites where subsites all used the same workspace/account. * * @since 4.7.7 * * @return void */ public function migrateUserData() { $userToken = get_user_meta( get_current_user_id(), 'seoboost_access_token', true ); if ( ! empty( $userToken ) ) { $this->setAccessToken( $userToken ); delete_user_meta( get_current_user_id(), 'seoboost_access_token' ); } $userOptions = get_user_meta( get_current_user_id(), 'seoboost_user_options', true ); if ( ! empty( $userOptions ) ) { $this->setUserOptions( $userOptions ); delete_user_meta( get_current_user_id(), 'seoboost_user_options' ); } } /** * Refreshes user options after an error. * This needs to run on init since service class is not available in the constructor. * * @since 4.7.7.2 * * @return void */ public function refreshUserOptionsAfterError() { $userOptionsFetchError = aioseo()->cache->get( 'seoboost_get_user_options_error' ); if ( $userOptionsFetchError && time() > $userOptionsFetchError ) { aioseo()->cache->delete( 'seoboost_get_user_options_error' ); $this->refreshUserOptions(); } } /** * Returns the default user options. * * @since 4.7.7.1 * * @return array The default user options. */ private function getDefaultUserOptions() { return [ 'language' => 'en', 'country' => 'US', 'countries' => $this->getCountries(), 'languages' => $this->getLanguages(), 'searchEngines' => $this->getSearchEngines() ]; } /** * Returns the list of countries. * * @since 4.7.7.1 * * @return array The list of countries. */ private function getCountries() { $countries = [ 'AF' => __( 'Afghanistan', 'all-in-one-seo-pack' ), 'AL' => __( 'Albania', 'all-in-one-seo-pack' ), 'DZ' => __( 'Algeria', 'all-in-one-seo-pack' ), 'AS' => __( 'American Samoa', 'all-in-one-seo-pack' ), 'AD' => __( 'Andorra', 'all-in-one-seo-pack' ), 'AO' => __( 'Angola', 'all-in-one-seo-pack' ), 'AI' => __( 'Anguilla', 'all-in-one-seo-pack' ), 'AG' => __( 'Antigua & Barbuda', 'all-in-one-seo-pack' ), 'AR' => __( 'Argentina', 'all-in-one-seo-pack' ), 'AM' => __( 'Armenia', 'all-in-one-seo-pack' ), 'AU' => __( 'Australia', 'all-in-one-seo-pack' ), 'AT' => __( 'Austria', 'all-in-one-seo-pack' ), 'AZ' => __( 'Azerbaijan', 'all-in-one-seo-pack' ), 'BS' => __( 'Bahamas', 'all-in-one-seo-pack' ), 'BH' => __( 'Bahrain', 'all-in-one-seo-pack' ), 'BD' => __( 'Bangladesh', 'all-in-one-seo-pack' ), 'BY' => __( 'Belarus', 'all-in-one-seo-pack' ), 'BE' => __( 'Belgium', 'all-in-one-seo-pack' ), 'BZ' => __( 'Belize', 'all-in-one-seo-pack' ), 'BJ' => __( 'Benin', 'all-in-one-seo-pack' ), 'BT' => __( 'Bhutan', 'all-in-one-seo-pack' ), 'BO' => __( 'Bolivia', 'all-in-one-seo-pack' ), 'BA' => __( 'Bosnia & Herzegovina', 'all-in-one-seo-pack' ), 'BW' => __( 'Botswana', 'all-in-one-seo-pack' ), 'BR' => __( 'Brazil', 'all-in-one-seo-pack' ), 'VG' => __( 'British Virgin Islands', 'all-in-one-seo-pack' ), 'BN' => __( 'Brunei', 'all-in-one-seo-pack' ), 'BG' => __( 'Bulgaria', 'all-in-one-seo-pack' ), 'BF' => __( 'Burkina Faso', 'all-in-one-seo-pack' ), 'BI' => __( 'Burundi', 'all-in-one-seo-pack' ), 'KH' => __( 'Cambodia', 'all-in-one-seo-pack' ), 'CM' => __( 'Cameroon', 'all-in-one-seo-pack' ), 'CA' => __( 'Canada', 'all-in-one-seo-pack' ), 'CV' => __( 'Cape Verde', 'all-in-one-seo-pack' ), 'CF' => __( 'Central African Republic', 'all-in-one-seo-pack' ), 'TD' => __( 'Chad', 'all-in-one-seo-pack' ), 'CL' => __( 'Chile', 'all-in-one-seo-pack' ), 'CO' => __( 'Colombia', 'all-in-one-seo-pack' ), 'CG' => __( 'Congo - Brazzaville', 'all-in-one-seo-pack' ), 'CD' => __( 'Congo - Kinshasa', 'all-in-one-seo-pack' ), 'CK' => __( 'Cook Islands', 'all-in-one-seo-pack' ), 'CR' => __( 'Costa Rica', 'all-in-one-seo-pack' ), 'CI' => __( 'Côte d’Ivoire', 'all-in-one-seo-pack' ), 'HR' => __( 'Croatia', 'all-in-one-seo-pack' ), 'CU' => __( 'Cuba', 'all-in-one-seo-pack' ), 'CY' => __( 'Cyprus', 'all-in-one-seo-pack' ), 'CZ' => __( 'Czechia', 'all-in-one-seo-pack' ), 'DK' => __( 'Denmark', 'all-in-one-seo-pack' ), 'DJ' => __( 'Djibouti', 'all-in-one-seo-pack' ), 'DM' => __( 'Dominica', 'all-in-one-seo-pack' ), 'DO' => __( 'Dominican Republic', 'all-in-one-seo-pack' ), 'EC' => __( 'Ecuador', 'all-in-one-seo-pack' ), 'EG' => __( 'Egypt', 'all-in-one-seo-pack' ), 'SV' => __( 'El Salvador', 'all-in-one-seo-pack' ), 'EE' => __( 'Estonia', 'all-in-one-seo-pack' ), 'ET' => __( 'Ethiopia', 'all-in-one-seo-pack' ), 'FJ' => __( 'Fiji', 'all-in-one-seo-pack' ), 'FI' => __( 'Finland', 'all-in-one-seo-pack' ), 'FR' => __( 'France', 'all-in-one-seo-pack' ), 'GA' => __( 'Gabon', 'all-in-one-seo-pack' ), 'GM' => __( 'Gambia', 'all-in-one-seo-pack' ), 'GE' => __( 'Georgia', 'all-in-one-seo-pack' ), 'DE' => __( 'Germany', 'all-in-one-seo-pack' ), 'GH' => __( 'Ghana', 'all-in-one-seo-pack' ), 'GI' => __( 'Gibraltar', 'all-in-one-seo-pack' ), 'GR' => __( 'Greece', 'all-in-one-seo-pack' ), 'GL' => __( 'Greenland', 'all-in-one-seo-pack' ), 'GT' => __( 'Guatemala', 'all-in-one-seo-pack' ), 'GG' => __( 'Guernsey', 'all-in-one-seo-pack' ), 'GY' => __( 'Guyana', 'all-in-one-seo-pack' ), 'HT' => __( 'Haiti', 'all-in-one-seo-pack' ), 'HN' => __( 'Honduras', 'all-in-one-seo-pack' ), 'HK' => __( 'Hong Kong', 'all-in-one-seo-pack' ), 'HU' => __( 'Hungary', 'all-in-one-seo-pack' ), 'IS' => __( 'Iceland', 'all-in-one-seo-pack' ), 'IN' => __( 'India', 'all-in-one-seo-pack' ), 'ID' => __( 'Indonesia', 'all-in-one-seo-pack' ), 'IQ' => __( 'Iraq', 'all-in-one-seo-pack' ), 'IE' => __( 'Ireland', 'all-in-one-seo-pack' ), 'IM' => __( 'Isle of Man', 'all-in-one-seo-pack' ), 'IL' => __( 'Israel', 'all-in-one-seo-pack' ), 'IT' => __( 'Italy', 'all-in-one-seo-pack' ), 'JM' => __( 'Jamaica', 'all-in-one-seo-pack' ), 'JP' => __( 'Japan', 'all-in-one-seo-pack' ), 'JE' => __( 'Jersey', 'all-in-one-seo-pack' ), 'JO' => __( 'Jordan', 'all-in-one-seo-pack' ), 'KZ' => __( 'Kazakhstan', 'all-in-one-seo-pack' ), 'KE' => __( 'Kenya', 'all-in-one-seo-pack' ), 'KI' => __( 'Kiribati', 'all-in-one-seo-pack' ), 'KW' => __( 'Kuwait', 'all-in-one-seo-pack' ), 'KG' => __( 'Kyrgyzstan', 'all-in-one-seo-pack' ), 'LA' => __( 'Laos', 'all-in-one-seo-pack' ), 'LV' => __( 'Latvia', 'all-in-one-seo-pack' ), 'LB' => __( 'Lebanon', 'all-in-one-seo-pack' ), 'LS' => __( 'Lesotho', 'all-in-one-seo-pack' ), 'LY' => __( 'Libya', 'all-in-one-seo-pack' ), 'LI' => __( 'Liechtenstein', 'all-in-one-seo-pack' ), 'LT' => __( 'Lithuania', 'all-in-one-seo-pack' ), 'LU' => __( 'Luxembourg', 'all-in-one-seo-pack' ), 'MG' => __( 'Madagascar', 'all-in-one-seo-pack' ), 'MW' => __( 'Malawi', 'all-in-one-seo-pack' ), 'MY' => __( 'Malaysia', 'all-in-one-seo-pack' ), 'MV' => __( 'Maldives', 'all-in-one-seo-pack' ), 'ML' => __( 'Mali', 'all-in-one-seo-pack' ), 'MT' => __( 'Malta', 'all-in-one-seo-pack' ), 'MU' => __( 'Mauritius', 'all-in-one-seo-pack' ), 'MX' => __( 'Mexico', 'all-in-one-seo-pack' ), 'FM' => __( 'Micronesia', 'all-in-one-seo-pack' ), 'MD' => __( 'Moldova', 'all-in-one-seo-pack' ), 'MN' => __( 'Mongolia', 'all-in-one-seo-pack' ), 'ME' => __( 'Montenegro', 'all-in-one-seo-pack' ), 'MS' => __( 'Montserrat', 'all-in-one-seo-pack' ), 'MA' => __( 'Morocco', 'all-in-one-seo-pack' ), 'MZ' => __( 'Mozambique', 'all-in-one-seo-pack' ), 'MM' => __( 'Myanmar (Burma)', 'all-in-one-seo-pack' ), 'NA' => __( 'Namibia', 'all-in-one-seo-pack' ), 'NR' => __( 'Nauru', 'all-in-one-seo-pack' ), 'NP' => __( 'Nepal', 'all-in-one-seo-pack' ), 'NL' => __( 'Netherlands', 'all-in-one-seo-pack' ), 'NZ' => __( 'New Zealand', 'all-in-one-seo-pack' ), 'NI' => __( 'Nicaragua', 'all-in-one-seo-pack' ), 'NE' => __( 'Niger', 'all-in-one-seo-pack' ), 'NG' => __( 'Nigeria', 'all-in-one-seo-pack' ), 'NU' => __( 'Niue', 'all-in-one-seo-pack' ), 'MK' => __( 'North Macedonia', 'all-in-one-seo-pack' ), 'NO' => __( 'Norway', 'all-in-one-seo-pack' ), 'OM' => __( 'Oman', 'all-in-one-seo-pack' ), 'PK' => __( 'Pakistan', 'all-in-one-seo-pack' ), 'PS' => __( 'Palestine', 'all-in-one-seo-pack' ), 'PA' => __( 'Panama', 'all-in-one-seo-pack' ), 'PG' => __( 'Papua New Guinea', 'all-in-one-seo-pack' ), 'PY' => __( 'Paraguay', 'all-in-one-seo-pack' ), 'PE' => __( 'Peru', 'all-in-one-seo-pack' ), 'PH' => __( 'Philippines', 'all-in-one-seo-pack' ), 'PN' => __( 'Pitcairn Islands', 'all-in-one-seo-pack' ), 'PL' => __( 'Poland', 'all-in-one-seo-pack' ), 'PT' => __( 'Portugal', 'all-in-one-seo-pack' ), 'PR' => __( 'Puerto Rico', 'all-in-one-seo-pack' ), 'QA' => __( 'Qatar', 'all-in-one-seo-pack' ), 'RO' => __( 'Romania', 'all-in-one-seo-pack' ), 'RU' => __( 'Russia', 'all-in-one-seo-pack' ), 'RW' => __( 'Rwanda', 'all-in-one-seo-pack' ), 'WS' => __( 'Samoa', 'all-in-one-seo-pack' ), 'SM' => __( 'San Marino', 'all-in-one-seo-pack' ), 'ST' => __( 'São Tomé & Príncipe', 'all-in-one-seo-pack' ), 'SA' => __( 'Saudi Arabia', 'all-in-one-seo-pack' ), 'SN' => __( 'Senegal', 'all-in-one-seo-pack' ), 'RS' => __( 'Serbia', 'all-in-one-seo-pack' ), 'SC' => __( 'Seychelles', 'all-in-one-seo-pack' ), 'SL' => __( 'Sierra Leone', 'all-in-one-seo-pack' ), 'SG' => __( 'Singapore', 'all-in-one-seo-pack' ), 'SK' => __( 'Slovakia', 'all-in-one-seo-pack' ), 'SI' => __( 'Slovenia', 'all-in-one-seo-pack' ), 'SB' => __( 'Solomon Islands', 'all-in-one-seo-pack' ), 'SO' => __( 'Somalia', 'all-in-one-seo-pack' ), 'ZA' => __( 'South Africa', 'all-in-one-seo-pack' ), 'KR' => __( 'South Korea', 'all-in-one-seo-pack' ), 'ES' => __( 'Spain', 'all-in-one-seo-pack' ), 'LK' => __( 'Sri Lanka', 'all-in-one-seo-pack' ), 'SH' => __( 'St. Helena', 'all-in-one-seo-pack' ), 'VC' => __( 'St. Vincent & Grenadines', 'all-in-one-seo-pack' ), 'SR' => __( 'Suriname', 'all-in-one-seo-pack' ), 'SE' => __( 'Sweden', 'all-in-one-seo-pack' ), 'CH' => __( 'Switzerland', 'all-in-one-seo-pack' ), 'TW' => __( 'Taiwan', 'all-in-one-seo-pack' ), 'TJ' => __( 'Tajikistan', 'all-in-one-seo-pack' ), 'TZ' => __( 'Tanzania', 'all-in-one-seo-pack' ), 'TH' => __( 'Thailand', 'all-in-one-seo-pack' ), 'TL' => __( 'Timor-Leste', 'all-in-one-seo-pack' ), 'TG' => __( 'Togo', 'all-in-one-seo-pack' ), 'TO' => __( 'Tonga', 'all-in-one-seo-pack' ), 'TT' => __( 'Trinidad & Tobago', 'all-in-one-seo-pack' ), 'TN' => __( 'Tunisia', 'all-in-one-seo-pack' ), 'TR' => __( 'Turkey', 'all-in-one-seo-pack' ), 'TM' => __( 'Turkmenistan', 'all-in-one-seo-pack' ), 'VI' => __( 'U.S. Virgin Islands', 'all-in-one-seo-pack' ), 'UG' => __( 'Uganda', 'all-in-one-seo-pack' ), 'UA' => __( 'Ukraine', 'all-in-one-seo-pack' ), 'AE' => __( 'United Arab Emirates', 'all-in-one-seo-pack' ), 'GB' => __( 'United Kingdom', 'all-in-one-seo-pack' ), 'US' => __( 'United States', 'all-in-one-seo-pack' ), 'UY' => __( 'Uruguay', 'all-in-one-seo-pack' ), 'UZ' => __( 'Uzbekistan', 'all-in-one-seo-pack' ), 'VU' => __( 'Vanuatu', 'all-in-one-seo-pack' ), 'VE' => __( 'Venezuela', 'all-in-one-seo-pack' ), 'VN' => __( 'Vietnam', 'all-in-one-seo-pack' ), 'ZM' => __( 'Zambia', 'all-in-one-seo-pack' ), 'ZW' => __( 'Zimbabwe', 'all-in-one-seo-pack' ) ]; return $countries; } /** * Returns the list of languages. * * @since 4.7.7.1 * * @return array The list of languages. */ private function getLanguages() { $languages = [ 'ca' => __( 'Catalan', 'all-in-one-seo-pack' ), 'da' => __( 'Danish', 'all-in-one-seo-pack' ), 'nl' => __( 'Dutch', 'all-in-one-seo-pack' ), 'en' => __( 'English', 'all-in-one-seo-pack' ), 'fr' => __( 'French', 'all-in-one-seo-pack' ), 'de' => __( 'German', 'all-in-one-seo-pack' ), 'id' => __( 'Indonesian', 'all-in-one-seo-pack' ), 'it' => __( 'Italian', 'all-in-one-seo-pack' ), 'no' => __( 'Norwegian', 'all-in-one-seo-pack' ), 'pt' => __( 'Portuguese', 'all-in-one-seo-pack' ), 'ro' => __( 'Romanian', 'all-in-one-seo-pack' ), 'es' => __( 'Spanish', 'all-in-one-seo-pack' ), 'sv' => __( 'Swedish', 'all-in-one-seo-pack' ), 'tr' => __( 'Turkish', 'all-in-one-seo-pack' ) ]; return $languages; } /** * Returns the list of search engines. * * @since 4.7.7.1 * * @return array The list of search engines. */ private function getSearchEngines() { $searchEngines = [ 'AF' => 'google.com.af', 'AL' => 'google.al', 'DZ' => 'google.dz', 'AS' => 'google.as', 'AD' => 'google.ad', 'AO' => 'google.it.ao', 'AI' => 'google.com.ai', 'AG' => 'google.com.ag', 'AR' => 'google.com.ar', 'AM' => 'google.am', 'AU' => 'google.com.au', 'AT' => 'google.at', 'AZ' => 'google.az', 'BS' => 'google.bs', 'BH' => 'google.com.bh', 'BD' => 'google.com.bd', 'BY' => 'google.com.by', 'BE' => 'google.be', 'BZ' => 'google.com.bz', 'BJ' => 'google.bj', 'BT' => 'google.bt', 'BO' => 'google.com.bo', 'BA' => 'google.ba', 'BW' => 'google.co.bw', 'BR' => 'google.com.br', 'VG' => 'google.vg', 'BN' => 'google.com.bn', 'BG' => 'google.bg', 'BF' => 'google.bf', 'BI' => 'google.bi', 'KH' => 'google.com.kh', 'CM' => 'google.cm', 'CA' => 'google.ca', 'CV' => 'google.cv', 'CF' => 'google.cf', 'TD' => 'google.td', 'CL' => 'google.cl', 'CO' => 'google.com.co', 'CG' => 'google.cg', 'CD' => 'google.cd', 'CK' => 'google.co.ck', 'CR' => 'google.co.cr', 'CI' => 'google.ci', 'HR' => 'google.hr', 'CU' => 'google.com.cu', 'CY' => 'google.com.cy', 'CZ' => 'google.cz', 'DK' => 'google.dk', 'DJ' => 'google.dj', 'DM' => 'google.dm', 'DO' => 'google.com.do', 'EC' => 'google.com.ec', 'EG' => 'google.com.eg', 'SV' => 'google.com.sv', 'EE' => 'google.ee', 'ET' => 'google.com.et', 'FJ' => 'google.com.fj', 'FI' => 'google.fi', 'FR' => 'google.fr', 'GA' => 'google.ga', 'GM' => 'google.gm', 'GE' => 'google.ge', 'DE' => 'google.de', 'GH' => 'google.com.gh', 'GI' => 'google.com.gi', 'GR' => 'google.gr', 'GL' => 'google.gl', 'GT' => 'google.com.gt', 'GG' => 'google.gg', 'GY' => 'google.gy', 'HT' => 'google.ht', 'HN' => 'google.hn', 'HK' => 'google.com.hk', 'HU' => 'google.hu', 'IS' => 'google.is', 'IN' => 'google.co.in', 'ID' => 'google.co.id', 'IQ' => 'google.iq', 'IE' => 'google.ie', 'IM' => 'google.co.im', 'IL' => 'google.co.il', 'IT' => 'google.it', 'JM' => 'google.com.jm', 'JP' => 'google.co.jp', 'JE' => 'google.co.je', 'JO' => 'google.jo', 'KZ' => 'google.kz', 'KE' => 'google.co.ke', 'KI' => 'google.ki', 'KW' => 'google.com.kw', 'KG' => 'google.com.kg', 'LA' => 'google.la', 'LV' => 'google.lv', 'LB' => 'google.com.lb', 'LS' => 'google.co.ls', 'LY' => 'google.com.ly', 'LI' => 'google.li', 'LT' => 'google.lt', 'LU' => 'google.lu', 'MG' => 'google.mg', 'MW' => 'google.mw', 'MY' => 'google.com.my', 'MV' => 'google.mv', 'ML' => 'google.ml', 'MT' => 'google.com.mt', 'MU' => 'google.mu', 'MX' => 'google.com.mx', 'FM' => 'google.fm', 'MD' => 'google.md', 'MN' => 'google.mn', 'ME' => 'google.me', 'MS' => 'google.ms', 'MA' => 'google.co.ma', 'MZ' => 'google.co.mz', 'MM' => 'google.com.mm', 'NA' => 'google.com.na', 'NR' => 'google.nr', 'NP' => 'google.com.np', 'NL' => 'google.nl', 'NZ' => 'google.co.nz', 'NI' => 'google.com.ni', 'NE' => 'google.ne', 'NG' => 'google.com.ng', 'NU' => 'google.nu', 'MK' => 'google.mk', 'NO' => 'google.no', 'OM' => 'google.com.om', 'PK' => 'google.com.pk', 'PS' => 'google.ps', 'PA' => 'google.com.pa', 'PG' => 'google.com.pg', 'PY' => 'google.com.py', 'PE' => 'google.com.pe', 'PH' => 'google.com.ph', 'PN' => 'google.pn', 'PL' => 'google.pl', 'PT' => 'google.pt', 'PR' => 'google.com.pr', 'QA' => 'google.com.qa', 'RO' => 'google.ro', 'RU' => 'google.ru', 'RW' => 'google.rw', 'WS' => 'google.as', 'SM' => 'google.sm', 'ST' => 'google.st', 'SA' => 'google.com.sa', 'SN' => 'google.sn', 'RS' => 'google.rs', 'SC' => 'google.sc', 'SL' => 'google.com.sl', 'SG' => 'google.com.sg', 'SK' => 'google.sk', 'SI' => 'google.si', 'SB' => 'google.com.sb', 'SO' => 'google.so', 'ZA' => 'google.co.za', 'KR' => 'google.co.kr', 'ES' => 'google.es', 'LK' => 'google.lk', 'SH' => 'google.sh', 'VC' => 'google.com.vc', 'SR' => 'google.sr', 'SE' => 'google.se', 'CH' => 'google.ch', 'TW' => 'google.com.tw', 'TJ' => 'google.com.tj', 'TZ' => 'google.co.tz', 'TH' => 'google.co.th', 'TL' => 'google.tl', 'TG' => 'google.tg', 'TO' => 'google.to', 'TT' => 'google.tt', 'TN' => 'google.tn', 'TR' => 'google.com.tr', 'TM' => 'google.tm', 'VI' => 'google.co.vi', 'UG' => 'google.co.ug', 'UA' => 'google.com.ua', 'AE' => 'google.ae', 'GB' => 'google.co.uk', 'US' => 'google.com', 'UY' => 'google.com.uy', 'UZ' => 'google.co.uz', 'VU' => 'google.vu', 'VE' => 'google.co.ve', 'VN' => 'google.com.vn', 'ZM' => 'google.co.zm', 'ZW' => 'google.co.zw' ]; return $searchEngines; } }Common/WritingAssistant/SeoBoost/Service.php000064400000014005150212212220015144 0ustar00doRequest( 'waAddNewReport', [ 'params' => [ 'keyword' => $keyword, 'country' => $country, 'language' => $language ] ] ); if ( is_wp_error( $reportRequest ) ) { return $reportRequest; } if ( empty( $reportRequest ) || empty( $reportRequest['status'] ) ) { return new \WP_Error( 'service-error', __( 'Empty response from service', 'all-in-one-seo-pack' ) ); } if ( 'success' !== $reportRequest['status'] ) { return new \WP_Error( 'service-error', $reportRequest['msg'] ); } return $reportRequest; } /** * Sends a post content to be analyzed. * * @since 4.7.4 * * @param string $title The title. * @param string $description The description. * @param string $content The content. * @param string $reportSlug The report slug. * @return array|\WP_Error The response. */ public function getContentAnalysis( $title, $description, $content, $reportSlug ) { return $this->doRequest( 'waAnalyzeContent', [ 'title' => $title, 'description' => $description, 'content' => $content, 'slug' => $reportSlug ] ); } /** * Gets the progress for a keyword. * * @since 4.7.4 * * @param string $uuid The uuid. * @return array|\WP_Error The progress. */ public function getProgressAndResult( $uuid ) { $response = $this->doRequest( 'waGetReport', [ 'slug' => $uuid ] ); if ( is_wp_error( $response ) ) { return $response; } if ( empty( $response ) ) { return new \WP_Error( 'empty-progress-and-result', __( 'Empty progress and result.', 'all-in-one-seo-pack' ) ); } return $response; } /** * Gets the user options. * * @since 4.7.4 * * @return array|\WP_Error The user options. */ public function getUserOptions() { return $this->doRequest( 'waGetUserOptions' ); } /** * Gets the user information. * * @since 4.7.4 * * @return array|\WP_Error The user information. */ public function getUserInfo() { return $this->doRequest( 'waGetUserInfo' ); } /** * Gets the access token. * * @since 4.7.4 * * @param string $authToken The auth token. * @return array|\WP_Error The response. */ public function getAccessToken( $authToken ) { return $this->doRequest( 'oauthaccess', [ 'token' => $authToken ] ); } /** * Refreshes the access token. * * @since 4.7.4 * * @return bool Was the token refreshed? */ private function refreshAccessToken() { $newAccessToken = $this->doRequest( 'waRefreshAccessToken' ); if ( is_wp_error( $newAccessToken ) || 'success' !== $newAccessToken['status'] ) { aioseo()->writingAssistant->seoBoost->setAccessToken( '' ); return false; } aioseo()->writingAssistant->seoBoost->setAccessToken( $newAccessToken['token'] ); return true; } /** * Sends a POST request to the microservice. * * @since 4.7.4 * * @param string $path The path. * @param array $requestBody The request body. * @return array|\WP_Error Returns the response body or WP_Error if the request failed. */ private function doRequest( $path, $requestBody = [] ) { // Prevent API requests if no access token is present. if ( 'oauthaccess' !== $path && // Except if we're getting the access token. empty( aioseo()->writingAssistant->seoBoost->getAccessToken() ) ) { return new \WP_Error( 'service-error', __( 'Missing access token', 'all-in-one-seo-pack' ) ); } $requestData = [ 'headers' => [ 'X-SeoBoost-Access-Token' => aioseo()->writingAssistant->seoBoost->getAccessToken(), 'X-SeoBoost-Domain' => aioseo()->helpers->getMultiSiteDomain(), 'Content-Type' => 'application/json' ], 'timeout' => 60, 'method' => 'GET' ]; if ( ! empty( $requestBody ) ) { $requestData['method'] = 'POST'; $requestData['body'] = wp_json_encode( $requestBody ); } $path = trailingslashit( $this->getUrl() ) . trailingslashit( $path ); $response = wp_remote_request( $path, $requestData ); $responseBody = json_decode( wp_remote_retrieve_body( $response ), true ); if ( ! $responseBody ) { $response = new \WP_Error( 'service-failed', __( 'Error in the SeoBoost service. Please contact support.', 'all-in-one-seo-pack' ) ); } if ( is_wp_error( $response ) ) { return $response; } // Refresh access token if expired and redo the request. if ( isset( $responseBody['error'] ) && 'invalid-access-token' === $responseBody['error'] ) { if ( $this->refreshAccessToken() ) { return $this->doRequest( $path, $requestBody ); } } return $responseBody; } /** * Returns the URL for the Writing Assistant service. * * @since 4.7.4 * * @return string The URL. */ public function getUrl() { $url = $this->baseUrl; if ( defined( 'AIOSEO_WRITING_ASSISTANT_SERVICE_URL' ) ) { $url = AIOSEO_WRITING_ASSISTANT_SERVICE_URL; } return $url; } /** * Gets the report history. * * @since 4.7.4 * * @return array|\WP_Error */ public function getReportHistory() { return $this->doRequest( 'waGetReportHistory' ); } }Common/WritingAssistant/WritingAssistant.php000064400000001102150212212220015316 0ustar00helpers = new Utils\Helpers(); $this->seoBoost = new SeoBoost\SeoBoost(); } }Common/Breadcrumbs/Breadcrumbs.php000064400000051072150212212220013161 0ustar00frontend = new Frontend(); $this->shortcode = new Shortcode(); $this->block = new Block(); add_action( 'widgets_init', [ $this, 'registerWidget' ] ); // Init Tags class later as we need post types registered. add_action( 'init', [ $this, 'init' ], 50 ); } public function init() { $this->tags = new Tags(); } /** * Helper to add crumbs on the breadcrumb array. * * @since 4.1.1 * * @param array $crumbs A single crumb or an array of crumbs. * @return void */ public function addCrumbs( $crumbs ) { if ( empty( $crumbs ) || ! is_array( $crumbs ) ) { return; } // If it's a single crumb put it inside an array to merge. if ( isset( $crumbs['label'] ) ) { $crumbs = [ $crumbs ]; } $this->breadcrumbs = array_merge( $this->breadcrumbs, $crumbs ); } /** * Builds a crumb array based on a type and a reference. * * @since 4.1.1 * * @param string $type The type of breadcrumb ( post, single, page, category, tag, taxonomy, postTypeArchive, date, * author, search, notFound, blog ). * @param mixed $reference The reference can be an object ( WP_Post | WP_Term | WP_Post_Type | WP_User ), an array, an int or a string. * @param array $paged A reference for a paged crumb. * @return array An array of breadcrumbs with their label, link, type and reference. */ public function buildBreadcrumbs( $type, $reference, $paged = [] ) { // Clear the breadcrumb array and build a new one. $this->breadcrumbs = []; // Add breadcrumb prefix. $this->addCrumbs( $this->getPrefixCrumb( $type, $reference ) ); // Set a home page in the beginning of the breadcrumb. $this->addCrumbs( $this->maybeGetHomePageCrumb( $type, $reference ) ); // Woocommerce shop page support. $this->addCrumbs( $this->maybeGetWooCommerceShopCrumb() ); // Blog home. if ( aioseo()->options->breadcrumbs->showBlogHome && in_array( $type, [ 'category', 'tag', 'post', 'author', 'date' ], true ) ) { $this->addCrumbs( $this->getBlogCrumb() ); } switch ( $type ) { case 'post': case 'single': $this->addCrumbs( $this->getPostArchiveCrumb( $reference ) ); $this->addCrumbs( $this->getPostTaxonomyCrumbs( $reference ) ); $this->addCrumbs( $this->getPostParentCrumbs( $reference ) ); $this->addCrumbs( $this->getPostCrumb( $reference ) ); break; case 'page': $this->addCrumbs( $this->getPostParentCrumbs( $reference, 'page' ) ); $this->addCrumbs( $this->getPostCrumb( $reference, 'page' ) ); break; case 'category': case 'tag': case 'taxonomy': $this->addCrumbs( $this->getTermTaxonomyParentCrumbs( $reference ) ); $this->addCrumbs( $this->getTermTaxonomyCrumb( $reference ) ); break; case 'postTypeArchive': $this->addCrumbs( $this->getPostTypeArchiveCrumb( $reference ) ); break; case 'date': $this->addCrumbs( $this->getDateCrumb( $reference ) ); break; case 'author': $this->addCrumbs( $this->getAuthorCrumb( $reference ) ); break; case 'blog': $this->addCrumbs( $this->getBlogCrumb() ); break; case 'search': $this->addCrumbs( $this->getSearchCrumb( $reference ) ); break; case 'notFound': $this->addCrumbs( $this->getNotFoundCrumb() ); break; case 'preview': $this->addCrumbs( $this->getPreviewCrumb( $reference ) ); break; case 'wcProduct': $this->addCrumbs( $this->getPostTaxonomyCrumbs( $reference ) ); $this->addCrumbs( $this->getPostParentCrumbs( $reference ) ); $this->addCrumbs( $this->getPostCrumb( $reference ) ); break; case 'buddypress': $this->addCrumbs( aioseo()->standalone->buddyPress->component->getCrumbs() ); break; } // Paged crumb. if ( ! empty( $paged['paged'] ) ) { $this->addCrumbs( $this->getPagedCrumb( $paged ) ); } // Maybe remove the last crumb. if ( ! $this->showCurrentItem( $type, $reference ) ) { array_pop( $this->breadcrumbs ); } // Remove empty crumbs. $this->breadcrumbs = array_filter( $this->breadcrumbs ); return $this->breadcrumbs; } /** * Gets the prefix crumb. * * @since 4.1.1 * * @param string $type The type of breadcrumb. * @param mixed $reference The breadcrumb reference. * @return array A crumb. */ public function getPrefixCrumb( $type, $reference ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable if ( 0 === strlen( aioseo()->options->breadcrumbs->breadcrumbPrefix ) ) { return []; } return $this->makeCrumb( aioseo()->options->breadcrumbs->breadcrumbPrefix, '', 'prefix' ); } /** * Gets the 404 crumb. * * @since 4.1.1 * * @return array A crumb. */ public function getNotFoundCrumb() { return $this->makeCrumb( aioseo()->options->breadcrumbs->errorFormat404, '', 'notFound' ); } /** * Gets the search crumb. * * @since 4.1.1 * * @param string $searchQuery The search query for reference. * @return array A crumb. */ public function getSearchCrumb( $searchQuery ) { return $this->makeCrumb( aioseo()->options->breadcrumbs->searchResultFormat, get_search_link( $searchQuery ), 'search', $searchQuery ); } /** * Gets the preview crumb. * * @since 4.1.5 * * @param string $label The preview label. * @return array A crumb. */ public function getPreviewCrumb( $label ) { return $this->makeCrumb( $label, '', 'preview' ); } /** * Gets the post type archive crumb. * * @since 4.1.1 * * @param \WP_Post_Type $postType The post type object for reference. * @return array A crumb. */ public function getPostTypeArchiveCrumb( $postType ) { return $this->makeCrumb( aioseo()->options->breadcrumbs->archiveFormat, get_post_type_archive_link( $postType->name ), 'postTypeArchive', $postType ); } /** * Gets a post crumb. * * @since 4.1.1 * * @param \WP_Post $post A post object for reference. * @param string $type The breadcrumb type. * @param string $subType The breadcrumb subType. * @return array A crumb. */ public function getPostCrumb( $post, $type = 'single', $subType = '' ) { return $this->makeCrumb( get_the_title( $post ), get_permalink( $post ), $type, $post, $subType ); } /** * Gets the term crumb. * * @since 4.1.1 * * @param \WP_Term $term The term object for reference. * @param string $subType The breadcrumb subType. * @return array A crumb. */ public function getTermTaxonomyCrumb( $term, $subType = '' ) { return $this->makeCrumb( $term->name, get_term_link( $term ), 'taxonomy', $term, $subType ); } /** * Gets the paged crumb. * * @since 4.1.1 * * @param array $reference The paged array for reference. * @return array A crumb. */ public function getPagedCrumb( $reference ) { return $this->makeCrumb( sprintf( '%1$s %2$s', __( 'Page', 'all-in-one-seo-pack' ), $reference['paged'] ), $reference['link'], 'paged', $reference ); } /** * Gets the author crumb. * * @since 4.1.1 * * @param \WP_User $wpUser A WP_User object. * @return array A crumb. */ public function getAuthorCrumb( $wpUser ) { return $this->makeCrumb( $wpUser->display_name, get_author_posts_url( $wpUser->ID ), 'author', $wpUser ); } /** * Gets the date crumb. * * @since 4.1.1 * * @param array $reference An array of year, month and day values. * @return array A crumb. */ public function getDateCrumb( $reference ) { $dateCrumb = []; $addMonth = false; $addYear = false; if ( ! empty( $reference['day'] ) ) { $addMonth = true; $addYear = true; $dateCrumb[] = $this->makeCrumb( zeroise( (int) $reference['day'], 2 ), get_day_link( $reference['year'], $reference['month'], $reference['day'] ), 'day', $reference['day'] ); } if ( ! empty( $reference['month'] ) || $addMonth ) { $addYear = true; $dateCrumb[] = $this->makeCrumb( zeroise( (int) $reference['month'], 2 ), get_month_link( $reference['year'], $reference['month'] ), 'month', $reference['month'] ); } if ( ! empty( $reference['year'] ) || $addYear ) { $dateCrumb[] = $this->makeCrumb( $reference['year'], get_year_link( $reference['year'] ), 'year', $reference['year'] ); } return array_reverse( $dateCrumb ); } /** * Gets an array of crumbs parents for the term. * * @since 4.1.1 * * @param \WP_Term $term A WP_Term object. * @return array An array of parent crumbs. */ public function getTermTaxonomyParentCrumbs( $term ) { $crumbs = []; $termHierarchy = $this->getTermHierarchy( $term->term_id, $term->taxonomy ); if ( ! empty( $termHierarchy ) ) { foreach ( $termHierarchy as $parentTermId ) { $parentTerm = aioseo()->helpers->getTerm( $parentTermId, $term->taxonomy ); $crumbs[] = $this->getTermTaxonomyCrumb( $parentTerm, 'parent' ); } } return $crumbs; } /** * Helper function to create a standard crumb array. * * @since 4.1.1 * * @param string $label The crumb label. * @param string $link The crumb url. * @param null $type The crumb type. * @param null $reference The crumb reference. * @param null $subType The crumb subType ( single/parent ). * @return array A crumb array. */ public function makeCrumb( $label, $link = '', $type = null, $reference = null, $subType = null ) { return [ 'label' => $label, 'link' => $link, 'type' => $type, 'subType' => $subType, 'reference' => $reference ]; } /** * Gets a post archive crumb if it's post type has archives. * * @since 4.1.1 * * @param int|\WP_Post $post An ID or a WP_Post object. * @return array A crumb. */ public function getPostArchiveCrumb( $post ) { $postType = get_post_type_object( get_post_type( $post ) ); if ( ! $postType || ! $postType->has_archive ) { return []; } return $this->makeCrumb( $postType->labels->name, get_post_type_archive_link( $postType->name ), 'postTypeArchive', $postType ); } /** * Gets a post's taxonomy crumbs. * * @since 4.1.1 * * @param int|\WP_Post $post An ID or a WP_Post object. * @param null $taxonomy A taxonomy to use. If none is provided the first one with terms selected will be used. * @return array An array of term crumbs. */ public function getPostTaxonomyCrumbs( $post, $taxonomy = null ) { $crumbs = []; if ( $taxonomy && ! is_array( $taxonomy ) ) { $taxonomy = [ $taxonomy ]; } $termHierarchy = $this->getPostTaxTermHierarchy( $post, $taxonomy ); if ( ! empty( $termHierarchy['terms'] ) ) { foreach ( $termHierarchy['terms'] as $termId ) { $term = aioseo()->helpers->getTerm( $termId, $termHierarchy['taxonomy'] ); $crumbs[] = $this->makeCrumb( $term->name, get_term_link( $term, $termHierarchy['taxonomy'] ), 'taxonomy', $term, 'parent' ); } } return $crumbs; } /** * Gets the post's parent crumbs. * * @since 4.1.1 * * @param int|\WP_Post $post An ID or a WP_Post object. * @param string $type The crumb type. * @return array An array of the post parent crumbs. */ public function getPostParentCrumbs( $post, $type = 'single' ) { $crumbs = []; if ( ! is_post_type_hierarchical( get_post_type( $post ) ) ) { return $crumbs; } $postHierarchy = $this->getPostHierarchy( $post ); if ( ! empty( $postHierarchy ) ) { foreach ( $postHierarchy as $parentID ) { // Do not include the Home Page. if ( aioseo()->helpers->getHomePageId() === $parentID ) { continue; } $crumbs[] = $this->getPostCrumb( get_post( $parentID ), $type, 'parent' ); } } return $crumbs; } /** * Function to extend on pro for extra functionality. * * @since 4.1.1 * * @param string $type The type of breadcrumb. * @param mixed $reference The breadcrumb reference. * @return bool Show current item. */ public function showCurrentItem( $type = null, $reference = null ) { return apply_filters( 'aioseo_breadcrumbs_show_current_item', aioseo()->options->breadcrumbs->showCurrentItem, $type, $reference ); } /** * Gets a home page crumb. * * @since 4.1.1 * * @param string $type The type of breadcrumb. * @param mixed $reference The breadcrumb reference. * @return array|void The home crumb. */ public function maybeGetHomePageCrumb( $type = null, $reference = null ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable if ( aioseo()->options->breadcrumbs->homepageLink ) { return $this->getHomePageCrumb(); } } /** * Gets a home page crumb. * * @since 4.1.1 * * @return array The home crumb. */ public function getHomePageCrumb() { $homePageId = aioseo()->helpers->getHomePageId(); $label = ''; if ( $homePageId ) { $label = get_the_title( $homePageId ); } if ( 0 < strlen( aioseo()->options->breadcrumbs->homepageLabel ) ) { $label = aioseo()->options->breadcrumbs->homepageLabel; } // Label fallback. if ( empty( $label ) ) { $label = __( 'Home', 'all-in-one-seo-pack' ); } return $this->makeCrumb( $label, get_home_url(), 'homePage', aioseo()->helpers->getHomePage() ); } /** * Gets the blog crumb. * * @since 4.1.1 * * @return array The blog crumb. */ public function getBlogCrumb() { $crumb = []; $blogPage = aioseo()->helpers->getBlogPage(); if ( null !== $blogPage ) { $crumb = $this->makeCrumb( $blogPage->post_title, get_permalink( $blogPage ), 'blog', $blogPage ); } return $crumb; } /** * Maybe add the shop crumb to products and product categories. * * @since 4.5.5 * * @return array The shop crumb. */ public function maybeGetWooCommerceShopCrumb() { $crumb = []; if ( aioseo()->helpers->isWooCommerceShopPage() || aioseo()->helpers->isWooCommerceProductPage() || aioseo()->helpers->isWooCommerceTaxonomyPage() ) { $crumb = $this->getWooCommerceShopCrumb(); } return $crumb; } /** * Gets the shop crumb. * @see WC_Breadcrumb::prepend_shop_page() * * @since 4.5.5 * * @return array The shop crumb. */ public function getWooCommerceShopCrumb() { $crumb = []; if ( ! function_exists( 'wc_get_page_id' ) || apply_filters( 'aioseo_woocommerce_breadcrumb_hide_shop', false ) ) { return $crumb; } $shopPageId = wc_get_page_id( 'shop' ); $shopPage = get_post( $shopPageId ); // WC checks if the permalink contains the shop page in the URI, but we prefer to // always show the shop page as the first crumb if it exists and it's not the home page. if ( $shopPageId && $shopPage && aioseo()->helpers->getHomePageId() !== $shopPageId ) { $crumb = $this->makeCrumb( get_the_title( $shopPage ), get_permalink( $shopPage ), 'wcShop' ); } return $crumb; } /** * Gets a post's term hierarchy for a list of taxonomies selecting the one that has a lengthier hierarchy. * * @since 4.1.1 * * @param int|\WP_Post $post An ID or a WP_Post object. * @param array $taxonomies An array of taxonomy names. * @param false $skipUnselectedTerms Allow unselected terms to be filtered out from the crumbs. * @return array An array of the taxonomy name + a term hierarchy. */ public function getPostTaxTermHierarchy( $post, $taxonomies = [], $skipUnselectedTerms = false ) { // Get all taxonomies attached to the post. if ( empty( $taxonomies ) ) { $taxonomies = get_object_taxonomies( get_post_type( $post ), 'objects' ); $taxonomies = wp_filter_object_list( $taxonomies, [ 'public' => true ], 'and', 'name' ); } foreach ( $taxonomies as $taxonomy ) { $primaryTerm = aioseo()->standalone->primaryTerm->getPrimaryTerm( $post->ID, $taxonomy ); $terms = wp_get_object_terms( $post->ID, $taxonomy, [ 'orderby' => 'term_id', 'order' => 'ASC', ] ); // Use the first taxonomy with terms. if ( empty( $terms ) || is_wp_error( $terms ) ) { continue; } // Determines the lengthier term hierarchy. $termHierarchy = []; foreach ( $terms as $term ) { // Gets our filtered ancestors. $ancestors = $this->getFilteredTermHierarchy( $term->term_id, $term->taxonomy, $skipUnselectedTerms ? $terms : [] ); // Merge the current term to be used in the breadcrumbs. $ancestors = array_merge( $ancestors, [ $term->term_id ] ); // If the current term is the primary term, use it. if ( is_a( $primaryTerm, 'WP_Term' ) && $primaryTerm->term_id === $term->term_id ) { $termHierarchy = $ancestors; break; } $termHierarchy = ( count( $termHierarchy ) < count( $ancestors ) ) ? $ancestors : $termHierarchy; } // Return a top to bottom hierarchy. return [ 'taxonomy' => $taxonomy, 'terms' => $termHierarchy ]; } return []; } /** * Filters a term's parent hierarchy against other terms. * * @since 4.1.1 * * @param int $termId A term id. * @param string $taxonomy The taxonomy name. * @param array $termsToFilterAgainst Terms to filter out of the hierarchy. * @return array The term's parent hierarchy. */ public function getFilteredTermHierarchy( $termId, $taxonomy, $termsToFilterAgainst = [] ) { $ancestors = $this->getTermHierarchy( $termId, $taxonomy ); // Keep only selected terms in the hierarchy. if ( ! empty( $termsToFilterAgainst ) ) { // If it's a WP_Term array make it a term_id array. if ( is_a( current( $termsToFilterAgainst ), 'WP_Term' ) ) { $termsToFilterAgainst = wp_list_pluck( $termsToFilterAgainst, 'term_id' ); } $ancestors = array_intersect( $ancestors, $termsToFilterAgainst ); } return $ancestors; } /** * Gets a term's parent hierarchy. * * @since 4.1.1 * * @param int $termId A term id. * @param string $taxonomy A taxonomy name. * @return array The term parent hierarchy. */ public function getTermHierarchy( $termId, $taxonomy ) { // Return a top to bottom hierarchy. return array_reverse( get_ancestors( $termId, $taxonomy, 'taxonomy' ) ); } /** * Gets a post's parent hierarchy. * * @since 4.1.1 * * @param int|\WP_Post $post An ID or a WP_Post object. * @return array The post parent hierarchy. */ public function getPostHierarchy( $post ) { $postId = ! empty( $post->ID ) ? $post->ID : $post; // Return a top to bottom hierarchy. return array_reverse( get_ancestors( $postId, '', 'post_type' ) ); } /** * Register our breadcrumb widget. * * @since 4.1.1 * * @return void */ public function registerWidget() { if ( aioseo()->helpers->canRegisterLegacyWidget( 'aioseo-breadcrumb-widget' ) ) { register_widget( 'AIOSEO\Plugin\Common\Breadcrumbs\Widget' ); } } } } namespace { // Exit if accessed directly. if ( ! defined( 'ABSPATH' ) ) { exit; } if ( ! function_exists( 'aioseo_breadcrumbs' ) ) { /** * Global function for breadcrumbs output. * * @since 4.1.1 * * @param boolean $echo Echo or return the output. * @return string|void The output. */ function aioseo_breadcrumbs( $echo = true ) { return aioseo()->breadcrumbs->frontend->display( $echo ); } } }Common/Breadcrumbs/Widget.php000064400000006410150212212220012147 0ustar00defaults = [ 'title' => '' ]; // Widget Slug. $widgetSlug = 'aioseo-breadcrumb-widget'; // Widget basics. $widgetOps = [ 'classname' => $widgetSlug, 'description' => esc_html__( 'Display the current page breadcrumb.', 'all-in-one-seo-pack' ), ]; // Widget controls. $controlOps = [ 'id_base' => $widgetSlug, ]; // Translators: 1 - The plugin short name ("AIOSEO"). $name = sprintf( esc_html__( '%1$s - Breadcrumbs', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_SHORT_NAME ); $name .= ' ' . esc_html__( '(legacy)', 'all-in-one-seo-pack' ); parent::__construct( $widgetSlug, $name, $widgetOps, $controlOps ); } /** * Widget callback. * * @since 4.1.1 * * @param array $args Widget args. * @param array $instance The widget instance options. * @return void */ public function widget( $args, $instance ) { // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped // Merge with defaults. $instance = wp_parse_args( (array) $instance, $this->defaults ); echo $args['before_widget']; // Title. if ( ! empty( $instance['title'] ) ) { echo $args['before_title']; echo apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base ); echo $args['after_title']; } // If not being previewed in the Customizer maybe show the dummy preview. if ( ! is_customize_preview() && ( false !== strpos( wp_get_referer(), admin_url( 'widgets.php' ) ) || false !== strpos( wp_get_referer(), admin_url( 'customize.php' ) ) ) ) { aioseo()->breadcrumbs->frontend->preview(); } else { aioseo()->breadcrumbs->frontend->display(); } echo $args['after_widget']; // phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped } /** * Widget option update. * * @since 4.1.1 * * @param array $newInstance New instance options. * @param array $oldInstance Old instance options. * @return array Processed new instance options. */ public function update( $newInstance, $oldInstance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable $newInstance['title'] = wp_strip_all_tags( $newInstance['title'] ); return $newInstance; } /** * Widget options form. * * @since 4.1.1 * * @param array $instance The widget instance options. * @return void */ public function form( $instance ) { // Merge with defaults. $instance = wp_parse_args( (array) $instance, $this->defaults ); ?>

breadcrumbs->frontend->display( false ); } }Common/Breadcrumbs/Block.php000064400000007663150212212220011771 0ustar00register(); } /** * Registers the block. * * @since 4.1.1 * * @return void */ public function register() { aioseo()->blocks->registerBlock( 'aioseo/breadcrumbs', [ 'attributes' => [ 'primaryTerm' => [ 'type' => 'string', 'default' => null ] ], 'render_callback' => [ $this, 'render' ] ] ); } /** * Renders the block. * * @since 4.1.1 * * @param array $blockAttributes The block attributes. * @return string The output from the output buffering. */ public function render( $blockAttributes ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable // phpcs:disable HM.Security.ValidatedSanitizedInput.InputNotSanitized, HM.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Recommended $postId = ! empty( $_GET['post_id'] ) ? (int) sanitize_text_field( wp_unslash( $_GET['post_id'] ) ) : false; // phpcs:enable if ( ! empty( $blockAttributes['primaryTerm'] ) ) { $this->primaryTerm = json_decode( $blockAttributes['primaryTerm'], true ); } if ( aioseo()->blocks->isRenderingBlockInEditor() && ! empty( $postId ) ) { add_filter( 'aioseo_post_primary_term', [ $this, 'changePrimaryTerm' ], 10, 2 ); add_filter( 'get_object_terms', [ $this, 'temporarilyAddTerm' ], 10, 3 ); $breadcrumbs = aioseo()->breadcrumbs->frontend->sideDisplay( false, 'post' === get_post_type( $postId ) ? 'post' : 'single', get_post( $postId ) ); remove_filter( 'aioseo_post_primary_term', [ $this, 'changePrimaryTerm' ], 10 ); remove_filter( 'get_object_terms', [ $this, 'temporarilyAddTerm' ], 10 ); if ( in_array( 'breadcrumbsEnable', aioseo()->internalOptions->deprecatedOptions, true ) && ! aioseo()->options->deprecated->breadcrumbs->enable ) { return '

' . sprintf( // Translators: 1 - The plugin short name ("AIOSEO"), 2 - Opening HTML link tag, 3 - Closing HTML link tag. __( 'Breadcrumbs are currently disabled, so this block will be rendered empty. You can enable %1$s\'s breadcrumb functionality under %2$sGeneral Settings > Breadcrumbs%3$s.', 'all-in-one-seo-pack' ), // phpcs:ignore Generic.Files.LineLength.MaxExceeded AIOSEO_PLUGIN_SHORT_NAME, '', '' ) . '

'; } return $breadcrumbs; } return aioseo()->breadcrumbs->frontend->display( false ); } /** * Temporarily adds the primary term to the list of terms. * * @since 4.3.6 * * @param array $terms The list of terms. * @param array $objectIds The object IDs. * @param array $taxonomies The taxonomies. * @return array The list of terms. */ public function temporarilyAddTerm( $terms, $objectIds, $taxonomies ) { $taxonomy = $taxonomies[0]; if ( empty( $this->primaryTerm ) || empty( $this->primaryTerm[ $taxonomy ] ) ) { return $terms; } $term = aioseo()->helpers->getTerm( $this->primaryTerm[ $taxonomy ] ); if ( is_a( $term, 'WP_Term' ) ) { $terms[] = $term; } return $terms; } /** * Changes the primary term. * * @since 4.3.6 * * @param \WP_Term $term The term object. * @param string $taxonomy The taxonomy name. * @return \WP_Term The term object. */ public function changePrimaryTerm( $term, $taxonomy ) { if ( empty( $this->primaryTerm ) || empty( $this->primaryTerm[ $taxonomy ] ) ) { return $term; } return aioseo()->helpers->getTerm( $this->primaryTerm[ $taxonomy ], $taxonomy ); } }Common/Breadcrumbs/Frontend.php000064400000020557150212212220012513 0ustar00breadcrumbs ) ) { return apply_filters( 'aioseo_breadcrumbs_trail', $this->breadcrumbs ); } $reference = get_queried_object(); $type = ''; if ( BuddyPressIntegration::isComponentPage() ) { $type = 'buddypress'; } if ( ! $type ) { // These types need the queried object for reference. if ( is_object( $reference ) ) { if ( is_single() ) { $type = 'single'; } if ( is_singular( 'post' ) ) { $type = 'post'; } if ( is_page() && ! is_front_page() ) { $type = 'page'; } if ( is_category() || is_tag() ) { $type = 'category'; } if ( is_tax() ) { $type = 'taxonomy'; } if ( is_post_type_archive() ) { $type = 'postTypeArchive'; } if ( is_author() ) { $type = 'author'; } if ( is_home() ) { $type = 'blog'; } // Support WC shop page. if ( aioseo()->helpers->isWooCommerceShopPage() ) { $type = 'wcShop'; } // Support WC products. if ( aioseo()->helpers->isWooCommerceProductPage() ) { $type = 'wcProduct'; } } if ( is_date() ) { $type = 'date'; $reference = [ 'year' => get_query_var( 'year' ), 'month' => get_query_var( 'monthnum' ), 'day' => get_query_var( 'day' ) ]; } if ( is_search() ) { $type = 'search'; $reference = htmlspecialchars( sanitize_text_field( get_search_query() ) ); } if ( is_404() ) { $type = 'notFound'; } } $paged = false; if ( is_paged() || ( is_singular() && 1 < get_query_var( 'page' ) ) ) { global $wp; $paged = [ 'paged' => get_query_var( 'paged' ) ? get_query_var( 'paged' ) : get_query_var( 'page' ), 'link' => home_url( $wp->request ) ]; } return apply_filters( 'aioseo_breadcrumbs_trail', aioseo()->breadcrumbs->buildBreadcrumbs( $type, $reference, $paged ) ); } /** * Helper function to display breadcrumbs for a specific page. * * @since 4.1.1 * * @param bool $echo Print out the breadcrumb. * @param string $type The type for the breadcrumb. * @param string $reference A reference to be used for rendering the breadcrumb. * @return string|void A html breadcrumb. */ public function sideDisplay( $echo = true, $type = '', $reference = '' ) { // Save previously built breadcrumbs. $previousCrumbs = $this->breadcrumbs; // Build and run the sideDisplay. $this->breadcrumbs = aioseo()->breadcrumbs->buildBreadcrumbs( $type, $reference ); $sideDisplay = $this->display( $echo ); // Restore previously built breadcrumbs. $this->breadcrumbs = $previousCrumbs; return $sideDisplay; } /** * Display a generic breadcrumb preview. * * @since 4.1.5 * * @param bool $echo Print out the breadcrumb. * @param string $label The preview crumb label. * @return string|void A html breadcrumb. */ public function preview( $echo = true, $label = '' ) { // Translators: "Crumb" refers to a part of the breadcrumb trail. $label = empty( $label ) ? __( 'Sample Crumb', 'all-in-one-seo-pack' ) : $label; return $this->sideDisplay( $echo, 'preview', $label ); } /** * Display the breadcrumb in the frontend. * * @since 4.1.1 * * @param bool $echo Print out the breadcrumb. * @return string|void A html breadcrumb. */ public function display( $echo = true ) { if ( in_array( 'breadcrumbsEnable', aioseo()->internalOptions->deprecatedOptions, true ) && ! aioseo()->options->deprecated->breadcrumbs->enable ) { return; } if ( ! apply_filters( 'aioseo_breadcrumbs_output', true ) ) { return; } // We can only run after this action because we need all post types loaded. if ( ! did_action( 'init' ) ) { return; } $breadcrumbs = $this->getBreadcrumbs(); if ( empty( $breadcrumbs ) ) { return; } $breadcrumbsCount = count( $breadcrumbs ); $display = '
'; foreach ( $breadcrumbs as $breadcrumb ) { --$breadcrumbsCount; $breadcrumbDisplay = $this->breadcrumbToDisplay( $breadcrumb ); // Strip link from Last crumb. if ( 0 === $breadcrumbsCount && aioseo()->breadcrumbs->showCurrentItem() && ! $this->linkCurrentItem() && 'default' === $breadcrumbDisplay['templateType'] ) { $breadcrumbDisplay['template'] = $this->stripLink( $breadcrumbDisplay['template'] ); } $display .= $breadcrumbDisplay['template']; if ( 0 < $breadcrumbsCount ) { $display .= $this->getSeparator(); } } $display .= '
'; // Final security cleaning. $display = wp_kses_post( $display ); if ( $echo ) { echo $display; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } return $display; } /** * Turns a crumb array into a rendered html crumb. * * @since 4.1.1 * * @param array $item The crumb array. * @return string|void The crumb html. */ protected function breadcrumbToDisplay( $item ) { $templateItem = $this->getCrumbTemplate( $item ); if ( empty( $templateItem['template'] ) ) { return; } // Do tags. $templateItem['template'] = aioseo()->breadcrumbs->tags->replaceTags( $templateItem['template'], $item ); $templateItem['template'] = preg_replace_callback( '/>(?![^<]*>)(?![^>]*")([^<]*?)>/', function ( $matches ) { return '>' . $matches[1] . '>'; }, htmlentities( $templateItem['template'] ) ); // Restore html. $templateItem['template'] = aioseo()->helpers->decodeHtmlEntities( $templateItem['template'] ); // Remove html link if it comes back from the template but we passed no links to it. if ( empty( $item['link'] ) ) { $templateItem['template'] = $this->stripLink( $templateItem['template'] ); } // Allow shortcodes to run in the final html. $templateItem['template'] = do_shortcode( $templateItem['template'] ); return $templateItem; } /** * Helper function to get a crumb's template. * * @since 4.1.1 * * @param array $crumb The crumb array. * @return string The html template. */ protected function getTemplate( $crumb ) { return $this->getDefaultTemplate( $crumb ); } /** * Helper function to get a crumb's template. * * @since 4.1.1 * * @param array $crumb The crumb array. * @return array The template type and html. */ protected function getCrumbTemplate( $crumb ) { return [ 'templateType' => 'default', 'template' => $this->getTemplate( $crumb ) ]; } /** * Default html template. * * @since 4.1.1 * * @param string $type The crumb's type. * @param mixed $reference The crumb's reference. * @return string The default crumb template. */ public function getDefaultTemplate( $type = '', $reference = '' ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable return <<