Server : LiteSpeed
System : Linux server51.dnsbootclub.com 4.18.0-553.62.1.lve.el8.x86_64 #1 SMP Mon Jul 21 17:50:35 UTC 2025 x86_64
User : nandedex ( 1060)
PHP Version : 8.1.33
Disable Function : NONE
Directory :  /home/nandedex/public_html/s.nandedexpress.com/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]


Current File : //home/nandedex/public_html/s.nandedexpress.com/config.tar
wincher-client.php000064400000007531151216032540010175 0ustar00<?php

namespace Yoast\WP\SEO\Config;

use WPSEO_Utils;
use Yoast\WP\SEO\Exceptions\OAuth\Authentication_Failed_Exception;
use Yoast\WP\SEO\Exceptions\OAuth\Tokens\Empty_Property_Exception;
use Yoast\WP\SEO\Exceptions\OAuth\Tokens\Empty_Token_Exception;
use Yoast\WP\SEO\Helpers\Options_Helper;
use Yoast\WP\SEO\Values\OAuth\OAuth_Token;
use Yoast\WP\SEO\Wrappers\WP_Remote_Handler;
use YoastSEO_Vendor\GuzzleHttp\Client;
use YoastSEO_Vendor\League\OAuth2\Client\Provider\Exception\IdentityProviderException;

/**
 * Class Wincher_Client
 */
class Wincher_Client extends OAuth_Client {

	/**
	 * The option's key.
	 */
	public const TOKEN_OPTION = 'wincher_tokens';

	/**
	 * Name of the temporary PKCE cookie.
	 */
	public const PKCE_TRANSIENT_NAME = 'yoast_wincher_pkce';

	/**
	 * The WP_Remote_Handler instance.
	 *
	 * @var WP_Remote_Handler
	 */
	protected $wp_remote_handler;

	/**
	 * Wincher_Client constructor.
	 *
	 * @param Options_Helper    $options_helper    The Options_Helper instance.
	 * @param WP_Remote_Handler $wp_remote_handler The request handler.
	 *
	 * @throws Empty_Property_Exception Exception thrown if a token property is empty.
	 */
	public function __construct(
		Options_Helper $options_helper,
		WP_Remote_Handler $wp_remote_handler
	) {

		$provider = new Wincher_PKCE_Provider(
			[
				'clientId'                => 'yoast',
				'redirectUri'             => 'https://auth.wincher.com/yoast/setup',
				'urlAuthorize'            => 'https://auth.wincher.com/connect/authorize',
				'urlAccessToken'          => 'https://auth.wincher.com/connect/token',
				'urlResourceOwnerDetails' => 'https://api.wincher.com/beta/user',
				'scopes'                  => [ 'profile', 'account', 'websites:read', 'websites:write', 'offline_access' ],
				'scopeSeparator'          => ' ',
				'pkceMethod'              => 'S256',
			],
			[
				'httpClient' => new Client( [ 'handler' => $wp_remote_handler ] ),
			]
		);

		parent::__construct(
			self::TOKEN_OPTION,
			$provider,
			$options_helper
		);
	}

	/**
	 * Return the authorization URL.
	 *
	 * @return string The authentication URL.
	 */
	public function get_authorization_url() {
		$parsed_site_url = \wp_parse_url( \get_site_url() );

		$url = $this->provider->getAuthorizationUrl(
			[
				'state' => WPSEO_Utils::format_json_encode( [ 'domain' => $parsed_site_url['host'] ] ),
			]
		);

		$pkce_code = $this->provider->getPkceCode();

		// Store a transient value with the PKCE code that we need in order to
		// exchange the returned code for a token after authorization.
		\set_transient( self::PKCE_TRANSIENT_NAME, $pkce_code, \DAY_IN_SECONDS );

		return $url;
	}

	/**
	 * Requests the access token and refresh token based on the passed code.
	 *
	 * @param string $code The code to send.
	 *
	 * @return OAuth_Token The requested tokens.
	 *
	 * @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
	 */
	public function request_tokens( $code ) {
		$pkce_code = \get_transient( self::PKCE_TRANSIENT_NAME );
		if ( $pkce_code ) {
			$this->provider->setPkceCode( $pkce_code );
		}
		return parent::request_tokens( $code );
	}

	/**
	 * Performs the specified request.
	 *
	 * @codeCoverageIgnore
	 *
	 * @param string $method  The HTTP method to use.
	 * @param string $url     The URL to send the request to.
	 * @param array  $options The options to pass along to the request.
	 *
	 * @return mixed The parsed API response.
	 *
	 * @throws IdentityProviderException Exception thrown if there's something wrong with the identifying data.
	 * @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
	 * @throws Empty_Token_Exception Exception thrown if the token is empty.
	 */
	protected function do_request( $method, $url, array $options ) {
		$options['headers'] = [ 'Content-Type' => 'application/json' ];
		return parent::do_request( $method, $url, $options );
	}
}
oauth-client.php000064400000021617151216032540007657 0ustar00<?php

namespace Yoast\WP\SEO\Config;

use Exception;
use Yoast\WP\SEO\Exceptions\OAuth\Authentication_Failed_Exception;
use Yoast\WP\SEO\Exceptions\OAuth\Tokens\Empty_Property_Exception;
use Yoast\WP\SEO\Exceptions\OAuth\Tokens\Empty_Token_Exception;
use Yoast\WP\SEO\Exceptions\OAuth\Tokens\Failed_Storage_Exception;
use Yoast\WP\SEO\Helpers\Options_Helper;
use Yoast\WP\SEO\Values\OAuth\OAuth_Token;
use YoastSEO_Vendor\League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use YoastSEO_Vendor\League\OAuth2\Client\Provider\GenericProvider;

/**
 * Class OAuth_Client
 */
abstract class OAuth_Client {

	/**
	 * The option's key.
	 *
	 * @var string
	 */
	protected $token_option = null;

	/**
	 * The provider.
	 *
	 * @var Wincher_PKCE_Provider|GenericProvider
	 */
	protected $provider;

	/**
	 * The options helper.
	 *
	 * @var Options_Helper
	 */
	protected $options_helper;

	/**
	 * The token.
	 *
	 * @var OAuth_Token|null
	 */
	protected $token = null;

	/**
	 * OAuth_Client constructor.
	 *
	 * @param string                                $token_option   The option's name to save the token as.
	 * @param Wincher_PKCE_Provider|GenericProvider $provider       The provider.
	 * @param Options_Helper                        $options_helper The Options_Helper instance.
	 *
	 * @throws Empty_Property_Exception Exception thrown if a token property is empty.
	 */
	public function __construct(
		$token_option,
		$provider,
		Options_Helper $options_helper
	) {
		$this->provider       = $provider;
		$this->token_option   = $token_option;
		$this->options_helper = $options_helper;

		$tokens = $this->options_helper->get( $this->token_option );

		if ( ! empty( $tokens ) ) {
			$this->token = new OAuth_Token(
				$tokens['access_token'],
				$tokens['refresh_token'],
				$tokens['expires'],
				$tokens['has_expired'],
				$tokens['created_at'],
				( $tokens['error_count'] ?? 0 )
			);
		}
	}

	/**
	 * Requests the access token and refresh token based on the passed code.
	 *
	 * @param string $code The code to send.
	 *
	 * @return OAuth_Token The requested tokens.
	 *
	 * @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
	 */
	public function request_tokens( $code ) {
		try {
			$response = $this->provider
				->getAccessToken(
					'authorization_code',
					[
						'code' => $code,
					]
				);

			$token = OAuth_Token::from_response( $response );

			return $this->store_token( $token );
		} catch ( Exception $exception ) {
			throw new Authentication_Failed_Exception( $exception );
		}
	}

	/**
	 * Performs an authenticated GET request to the desired URL.
	 *
	 * @param string $url     The URL to send the request to.
	 * @param array  $options The options to pass along to the request.
	 *
	 * @return mixed The parsed API response.
	 *
	 * @throws IdentityProviderException Exception thrown if there's something wrong with the identifying data.
	 * @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
	 * @throws Empty_Token_Exception Exception thrown if the token is empty.
	 */
	public function get( $url, $options = [] ) {
		return $this->do_request( 'GET', $url, $options );
	}

	/**
	 * Performs an authenticated POST request to the desired URL.
	 *
	 * @param string $url     The URL to send the request to.
	 * @param mixed  $body    The data to send along in the request's body.
	 * @param array  $options The options to pass along to the request.
	 *
	 * @return mixed The parsed API response.
	 *
	 * @throws IdentityProviderException Exception thrown if there's something wrong with the identifying data.
	 * @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
	 * @throws Empty_Token_Exception Exception thrown if the token is empty.
	 */
	public function post( $url, $body, $options = [] ) {
		$options['body'] = $body;

		return $this->do_request( 'POST', $url, $options );
	}

	/**
	 * Performs an authenticated DELETE request to the desired URL.
	 *
	 * @param string $url     The URL to send the request to.
	 * @param array  $options The options to pass along to the request.
	 *
	 * @return mixed The parsed API response.
	 *
	 * @throws IdentityProviderException Exception thrown if there's something wrong with the identifying data.
	 * @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
	 * @throws Empty_Token_Exception Exception thrown if the token is empty.
	 */
	public function delete( $url, $options = [] ) {
		return $this->do_request( 'DELETE', $url, $options );
	}

	/**
	 * Determines whether there are valid tokens available.
	 *
	 * @return bool Whether there are valid tokens.
	 */
	public function has_valid_tokens() {
		return ! empty( $this->token ) && $this->token->has_expired() === false;
	}

	/**
	 * Gets the stored tokens and refreshes them if they've expired.
	 *
	 * @return OAuth_Token The stored tokens.
	 *
	 * @throws Empty_Token_Exception Exception thrown if the token is empty.
	 */
	public function get_tokens() {
		if ( empty( $this->token ) ) {
			throw new Empty_Token_Exception();
		}

		if ( $this->token->has_expired() ) {
			$this->token = $this->refresh_tokens( $this->token );
		}

		return $this->token;
	}

	/**
	 * Stores the passed token.
	 *
	 * @param OAuth_Token $token The token to store.
	 *
	 * @return OAuth_Token The stored token.
	 *
	 * @throws Failed_Storage_Exception Exception thrown if storing of the token fails.
	 */
	public function store_token( OAuth_Token $token ) {
		$saved = $this->options_helper->set( $this->token_option, $token->to_array() );

		if ( $saved === false ) {
			throw new Failed_Storage_Exception();
		}

		return $token;
	}

	/**
	 * Clears the stored token from storage.
	 *
	 * @return bool The stored token.
	 *
	 * @throws Failed_Storage_Exception Exception thrown if clearing of the token fails.
	 */
	public function clear_token() {
		$saved = $this->options_helper->set( $this->token_option, [] );

		if ( $saved === false ) {
			throw new Failed_Storage_Exception();
		}

		return true;
	}

	/**
	 * Performs the specified request.
	 *
	 * @param string $method  The HTTP method to use.
	 * @param string $url     The URL to send the request to.
	 * @param array  $options The options to pass along to the request.
	 *
	 * @return mixed The parsed API response.
	 *
	 * @throws IdentityProviderException Exception thrown if there's something wrong with the identifying data.
	 * @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
	 * @throws Empty_Token_Exception Exception thrown if the token is empty.
	 */
	protected function do_request( $method, $url, array $options ) {
		$defaults = [
			'headers' => $this->provider->getHeaders( $this->get_tokens()->access_token ),
		];

		$options = \array_merge_recursive( $defaults, $options );

		if ( \array_key_exists( 'params', $options ) ) {
			$url .= '?' . \http_build_query( $options['params'] );
			unset( $options['params'] );
		}

		$request = $this->provider
			->getAuthenticatedRequest( $method, $url, null, $options );

		return $this->provider->getParsedResponse( $request );
	}

	/**
	 * Refreshes the outdated tokens.
	 *
	 * @param OAuth_Token $tokens The outdated tokens.
	 *
	 * @return OAuth_Token The refreshed tokens.
	 *
	 * @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
	 */
	protected function refresh_tokens( OAuth_Token $tokens ) {
		// We do this dance with transients since we need to make sure we don't
		// delete valid tokens because of a race condition when two calls are
		// made simultaneously to this function and refresh token rotation is
		// turned on in the OAuth server. This is not 100% safe, but should at
		// least be much better than not having any lock at all.
		$lock_name = \sprintf( 'lock:%s', $this->token_option );
		$can_lock  = \get_transient( $lock_name ) === false;
		$has_lock  = $can_lock && \set_transient( $lock_name, true, 30 );

		try {
			$new_tokens = $this->provider->getAccessToken(
				'refresh_token',
				[
					'refresh_token' => $tokens->refresh_token,
				]
			);

			$token_obj = OAuth_Token::from_response( $new_tokens );

			return $this->store_token( $token_obj );
		} catch ( Exception $exception ) {
			// If we tried to refresh but the refresh token is invalid, delete
			// the tokens so that we don't try again. Only do this if we got the
			// lock at the beginning of the call.
			if ( $has_lock && $exception->getMessage() === 'invalid_grant' ) {
				try {
					// To protect from race conditions, only do this if we've
					// seen an error before with the same token.
					if ( $tokens->error_count >= 1 ) {
						$this->clear_token();
					}
					else {
						$tokens->error_count += 1;
						$this->store_token( $tokens );
					}
				} catch ( Exception $e ) {  // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
					// Pass through.
				}
			}

			throw new Authentication_Failed_Exception( $exception );
		} finally {
			\delete_transient( $lock_name );
		}
	}
}
indexing-reasons.php000064400000002443151216032540010534 0ustar00<?php

namespace Yoast\WP\SEO\Config;

/**
 * Class Indexing_Reasons. Contains constants that aren't context specific.
 */
class Indexing_Reasons {

	/**
	 * Represents the reason that the indexing process failed and should be tried again.
	 */
	public const REASON_INDEXING_FAILED = 'indexing_failed';

	/**
	 * Represents the reason that the permalink settings are changed.
	 */
	public const REASON_PERMALINK_SETTINGS = 'permalink_settings_changed';

	/**
	 * Represents the reason that the category base is changed.
	 */
	public const REASON_CATEGORY_BASE_PREFIX = 'category_base_changed';

	/**
	 * Represents the reason that the tag base is changed.
	 */
	public const REASON_TAG_BASE_PREFIX = 'tag_base_changed';

	/**
	 * Represents the reason that the home url option is changed.
	 */
	public const REASON_HOME_URL_OPTION = 'home_url_option_changed';

	/**
	 * Represents the reason that a post type has been made public.
	 */
	public const REASON_POST_TYPE_MADE_PUBLIC = 'post_type_made_public';

	/**
	 * Represents the reason that a post type has been made viewable.
	 */
	public const REASON_TAXONOMY_MADE_PUBLIC = 'taxonomy_made_public';

	/**
	 * Represents the reason that attachments have stopped being redirected.
	 */
	public const REASON_ATTACHMENTS_MADE_ENABLED = 'attachments_made_enabled';
}
schema-types.php000064400000010002151216032540007647 0ustar00<?php

namespace Yoast\WP\SEO\Config;

/**
 * Class Schema_Types.
 */
class Schema_Types {

	/**
	 * Holds the possible schema page types.
	 *
	 * Capitalized in this way so the value can be directly used in the schema output.
	 *
	 * @var string[]
	 */
	public const PAGE_TYPES = [
		'WebPage'           => '',
		'ItemPage'          => '',
		'AboutPage'         => '',
		'FAQPage'           => '',
		'QAPage'            => '',
		'ProfilePage'       => '',
		'ContactPage'       => '',
		'MedicalWebPage'    => '',
		'CollectionPage'    => '',
		'CheckoutPage'      => '',
		'RealEstateListing' => '',
		'SearchResultsPage' => '',
	];

	/**
	 * Holds the possible schema article types.
	 *
	 * Capitalized in this way so the value can be directly used in the schema output.
	 *
	 * @var string[]
	 */
	public const ARTICLE_TYPES = [
		'Article'                  => '',
		'BlogPosting'              => '',
		'SocialMediaPosting'       => '',
		'NewsArticle'              => '',
		'AdvertiserContentArticle' => '',
		'SatiricalArticle'         => '',
		'ScholarlyArticle'         => '',
		'TechArticle'              => '',
		'Report'                   => '',
		'None'                     => '',
	];

	/**
	 * Gets the page type options.
	 *
	 * @return array[] The schema page type options.
	 */
	public function get_page_type_options() {
		return [
			[
				'name'  => \__( 'Web Page', 'wordpress-seo' ),
				'value' => 'WebPage',
			],
			[
				'name'  => \__( 'Item Page', 'wordpress-seo' ),
				'value' => 'ItemPage',
			],
			[
				'name'  => \__( 'About Page', 'wordpress-seo' ),
				'value' => 'AboutPage',
			],
			[
				'name'  => \__( 'FAQ Page', 'wordpress-seo' ),
				'value' => 'FAQPage',
			],
			[
				'name'  => \__( 'QA Page', 'wordpress-seo' ),
				'value' => 'QAPage',
			],
			[
				'name'  => \__( 'Profile Page', 'wordpress-seo' ),
				'value' => 'ProfilePage',
			],
			[
				'name'  => \__( 'Contact Page', 'wordpress-seo' ),
				'value' => 'ContactPage',
			],
			[
				'name'  => \__( 'Medical Web Page', 'wordpress-seo' ),
				'value' => 'MedicalWebPage',
			],
			[
				'name'  => \__( 'Collection Page', 'wordpress-seo' ),
				'value' => 'CollectionPage',
			],
			[
				'name'  => \__( 'Checkout Page', 'wordpress-seo' ),
				'value' => 'CheckoutPage',
			],
			[
				'name'  => \__( 'Real Estate Listing', 'wordpress-seo' ),
				'value' => 'RealEstateListing',
			],
			[
				'name'  => \__( 'Search Results Page', 'wordpress-seo' ),
				'value' => 'SearchResultsPage',
			],
		];
	}

	/**
	 * Gets the article type options.
	 *
	 * @return array[] The schema article type options.
	 */
	public function get_article_type_options() {
		/**
		 * Filter: 'wpseo_schema_article_types_labels' - Allow developers to filter the available article types and their labels.
		 *
		 * Make sure when you filter this to also filter `wpseo_schema_article_types`.
		 *
		 * @param array $schema_article_types_labels The available schema article types and their labels.
		 */
		return \apply_filters(
			'wpseo_schema_article_types_labels',
			[
				[
					'name'  => \__( 'Article', 'wordpress-seo' ),
					'value' => 'Article',
				],
				[
					'name'  => \__( 'Blog Post', 'wordpress-seo' ),
					'value' => 'BlogPosting',
				],
				[
					'name'  => \__( 'Social Media Posting', 'wordpress-seo' ),
					'value' => 'SocialMediaPosting',
				],
				[
					'name'  => \__( 'News Article', 'wordpress-seo' ),
					'value' => 'NewsArticle',
				],
				[
					'name'  => \__( 'Advertiser Content Article', 'wordpress-seo' ),
					'value' => 'AdvertiserContentArticle',
				],
				[
					'name'  => \__( 'Satirical Article', 'wordpress-seo' ),
					'value' => 'SatiricalArticle',
				],
				[
					'name'  => \__( 'Scholarly Article', 'wordpress-seo' ),
					'value' => 'ScholarlyArticle',
				],
				[
					'name'  => \__( 'Tech Article', 'wordpress-seo' ),
					'value' => 'TechArticle',
				],
				[
					'name'  => \__( 'Report', 'wordpress-seo' ),
					'value' => 'Report',
				],
				[
					'name'  => \__( 'None', 'wordpress-seo' ),
					'value' => 'None',
				],
			]
		);
	}
}
migration-status.php000064400000012227151216032540010572 0ustar00<?php

namespace Yoast\WP\SEO\Config;

/**
 * Migration_Status class.
 *
 * Used to validate whether or not migrations have been run and whether or not they should be run again.
 */
class Migration_Status {

	/**
	 * The migration option key.
	 *
	 * @var string
	 */
	public const MIGRATION_OPTION_KEY = 'yoast_migrations_';

	/**
	 * The migration options.
	 *
	 * @var array
	 */
	protected $migration_options = [];

	/**
	 * Checks if a given migration should be run.
	 *
	 * @param string $name    The name of the migration.
	 * @param string $version The current version.
	 *
	 * @return bool Whether or not the migration should be run.
	 */
	public function should_run_migration( $name, $version = \WPSEO_VERSION ) {
		$migration_status = $this->get_migration_status( $name );

		// Check if we've attempted to run this migration in the past 10 minutes. If so, it may still be running.
		if ( \array_key_exists( 'lock', $migration_status ) ) {
			$timestamp = \strtotime( '-10 minutes' );

			return $timestamp > $migration_status['lock'];
		}

		// Is the migration version less than the current version.
		return \version_compare( $migration_status['version'], $version, '<' );
	}

	/**
	 * Checks whether or not the given migration is at least the given version, defaults to checking for the latest version.
	 *
	 * @param string $name    The name of the migration.
	 * @param string $version The version to check, defaults to the latest version.
	 *
	 * @return bool Whether or not the requested migration is at least the requested version.
	 */
	public function is_version( $name, $version = \WPSEO_VERSION ) {
		$migration_status = $this->get_migration_status( $name );

		return \version_compare( $version, $migration_status['version'], '<=' );
	}

	/**
	 * Gets the error of a given migration if it exists.
	 *
	 * @param string $name The name of the migration.
	 *
	 * @return bool|array False if there is no error, otherwise the error.
	 */
	public function get_error( $name ) {
		$migration_status = $this->get_migration_status( $name );

		if ( ! isset( $migration_status['error'] ) ) {
			return false;
		}

		return $migration_status['error'];
	}

	/**
	 * Sets an error for the migration.
	 *
	 * @param string $name    The name of the migration.
	 * @param string $message Message explaining the reason for the error.
	 * @param string $version The current version.
	 *
	 * @return void
	 */
	public function set_error( $name, $message, $version = \WPSEO_VERSION ) {
		$migration_status = $this->get_migration_status( $name );

		$migration_status['error'] = [
			'time'    => \strtotime( 'now' ),
			'version' => $version,
			'message' => $message,
		];

		$this->set_migration_status( $name, $migration_status );
	}

	/**
	 * Updates the migration version to the latest version.
	 *
	 * @param string $name    The name of the migration.
	 * @param string $version The current version.
	 *
	 * @return void
	 */
	public function set_success( $name, $version = \WPSEO_VERSION ) {
		$migration_status = $this->get_migration_status( $name );
		unset( $migration_status['lock'] );
		unset( $migration_status['error'] );
		$migration_status['version'] = $version;
		$this->set_migration_status( $name, $migration_status );
	}

	/**
	 * Locks the migration status.
	 *
	 * @param string $name The name of the migration.
	 *
	 * @return bool Whether or not the migration was succesfully locked.
	 */
	public function lock_migration( $name ) {
		$migration_status         = $this->get_migration_status( $name );
		$migration_status['lock'] = \strtotime( 'now' );

		return $this->set_migration_status( $name, $migration_status );
	}

	/**
	 * Retrieves the migration option.
	 *
	 * @param string $name The name of the migration.
	 *
	 * @return bool|array The status of the migration, false if no status exists.
	 */
	protected function get_migration_status( $name ) {
		$current_blog_id = \get_current_blog_id();
		if ( ! isset( $this->migration_options[ $current_blog_id ][ $name ] ) ) {
			$migration_status = \get_option( self::MIGRATION_OPTION_KEY . $name );

			if ( ! \is_array( $migration_status ) || ! isset( $migration_status['version'] ) ) {
				$migration_status = [ 'version' => '0.0' ];
			}

			if ( ! isset( $this->migration_options[ $current_blog_id ] ) ) {
				$this->migration_options[ $current_blog_id ] = [];
			}
			$this->migration_options[ $current_blog_id ][ $name ] = $migration_status;
		}

		return $this->migration_options[ $current_blog_id ][ $name ];
	}

	/**
	 * Retrieves the migration option.
	 *
	 * @param string $name             The name of the migration.
	 * @param array  $migration_status The migration status.
	 *
	 * @return bool True if the status was succesfully updated, false otherwise.
	 */
	protected function set_migration_status( $name, $migration_status ) {
		if ( ! \is_array( $migration_status ) || ! isset( $migration_status['version'] ) ) {
			return false;
		}
		$current_blog_id = \get_current_blog_id();

		if ( ! isset( $this->migration_options[ $current_blog_id ] ) ) {
			$this->migration_options[ $current_blog_id ] = [];
		}
		$this->migration_options[ $current_blog_id ][ $name ] = $migration_status;

		return \update_option( self::MIGRATION_OPTION_KEY . $name, $migration_status );
	}
}
migrations/20200420073606_AddColumnsToIndexables.php000064400000004124151216032540015423 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Class AddColumnsToIndexables.
 */
class AddColumnsToIndexables extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$tables  = $this->get_tables();
		$blog_id = \get_current_blog_id();
		foreach ( $tables as $table ) {
			$this->add_column(
				$table,
				'blog_id',
				'biginteger',
				[
					'null'    => false,
					'limit'   => 20,
					'default' => $blog_id,
				]
			);
		}

		$attr_limit_32 = [
			'null'  => true,
			'limit' => 32,
		];
		$attr_limit_64 = [
			'null'  => true,
			'limit' => 64,
		];

		$indexable_table = $this->get_indexable_table();
		$this->add_column( $indexable_table, 'language', 'string', $attr_limit_32 );
		$this->add_column( $indexable_table, 'region', 'string', $attr_limit_32 );
		$this->add_column( $indexable_table, 'schema_page_type', 'string', $attr_limit_64 );
		$this->add_column( $indexable_table, 'schema_article_type', 'string', $attr_limit_64 );
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		$tables = $this->get_tables();
		foreach ( $tables as $table ) {
			$this->remove_column( $table, 'blog_id' );
		}

		$indexable_table = $this->get_indexable_table();
		$this->remove_column( $indexable_table, 'language' );
		$this->remove_column( $indexable_table, 'region' );
		$this->remove_column( $indexable_table, 'schema_page_type' );
		$this->remove_column( $indexable_table, 'schema_article_type' );
	}

	/**
	 * Retrieves the Indexable table.
	 *
	 * @return string The Indexable table name.
	 */
	protected function get_indexable_table() {
		return Model::get_table_name( 'Indexable' );
	}

	/**
	 * Retrieves the table names to use.
	 *
	 * @return string[] The table names to use.
	 */
	protected function get_tables() {
		return [
			$this->get_indexable_table(),
			Model::get_table_name( 'Indexable_Hierarchy' ),
			Model::get_table_name( 'Primary_Term' ),
		];
	}
}
migrations/20230417083836_AddInclusiveLanguageScore.php000064400000001662151216032540016125 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * AddInclusiveLanguageScore class.
 */
class AddInclusiveLanguageScore extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$table_name = $this->get_table_name();

		$this->add_column(
			$table_name,
			'inclusive_language_score',
			'integer',
			[
				'null'  => true,
				'limit' => 3,
			]
		);
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		$table_name = $this->get_table_name();

		$this->remove_column( $table_name, 'inclusive_language_score' );
	}

	/**
	 * Retrieves the table name to use.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable' );
	}
}
migrations/20191011111109_WpYoastIndexableHierarchy.php000064400000003072151216032540016146 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Class WpYoastIndexableHierarchy.
 */
class WpYoastIndexableHierarchy extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$table_name = $this->get_table_name();

		$indexable_table = $this->create_table( $table_name, [ 'id' => false ] );

		$indexable_table->column(
			'indexable_id',
			'integer',
			[
				'primary_key' => true,
				'unsigned'    => true,
				'null'        => true,
				'limit'       => 11,
			]
		);
		$indexable_table->column(
			'ancestor_id',
			'integer',
			[
				'primary_key' => true,
				'unsigned'    => true,
				'null'        => true,
				'limit'       => 11,
			]
		);
		$indexable_table->column(
			'depth',
			'integer',
			[
				'unsigned' => true,
				'null'     => true,
				'limit'    => 11,
			]
		);
		$indexable_table->finish();

		$this->add_index( $table_name, 'indexable_id', [ 'name' => 'indexable_id' ] );
		$this->add_index( $table_name, 'ancestor_id', [ 'name' => 'ancestor_id' ] );
		$this->add_index( $table_name, 'depth', [ 'name' => 'depth' ] );
	}

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function down() {
		$this->drop_table( $this->get_table_name() );
	}

	/**
	 * Retrieves the table name to use.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable_Hierarchy' );
	}
}
migrations/20210817092415_AddVersionColumnToIndexables.php000064400000001547151216032540016626 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * AddVersionColumnToIndexables class.
 */
class AddVersionColumnToIndexables extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$this->add_column(
			$this->get_table_name(),
			'version',
			'integer',
			[
				'default'  => 1,
			]
		);
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		$this->remove_column(
			$this->get_table_name(),
			'version'
		);
	}

	/**
	 * Retrieves the table name to use for storing indexables.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable' );
	}
}
migrations/20200408101900_AddCollationToTables.php000064400000001676151216032540015067 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Class AddCollationToTables.
 */
class AddCollationToTables extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		global $wpdb;

		$charset_collate = $wpdb->get_charset_collate();
		if ( empty( $charset_collate ) ) {
			return;
		}

		$tables = [
			Model::get_table_name( 'migrations' ),
			Model::get_table_name( 'Indexable' ),
			Model::get_table_name( 'Indexable_Hierarchy' ),
			Model::get_table_name( 'Primary_Term' ),
		];

		foreach ( $tables as $table ) {
			$this->query( 'ALTER TABLE ' . $table . ' CONVERT TO ' . \str_replace( 'DEFAULT ', '', $charset_collate ) );
		}
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		// No down required.
	}
}
migrations/20200429105310_TruncateIndexableTables.php000064400000002065151216032540015623 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Class TruncateIndexableTables.
 */
class TruncateIndexableTables extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$this->query( 'TRUNCATE TABLE ' . $this->get_indexable_table_name() );
		$this->query( 'TRUNCATE TABLE ' . $this->get_indexable_hierarchy_table_name() );
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		// Nothing to do.
	}

	/**
	 * Retrieves the table name to use for storing indexables.
	 *
	 * @return string The table name to use.
	 */
	protected function get_indexable_table_name() {
		return Model::get_table_name( 'Indexable' );
	}

	/**
	 * Retrieves the table name to use.
	 *
	 * @return string The table name to use.
	 */
	protected function get_indexable_hierarchy_table_name() {
		return Model::get_table_name( 'Indexable_Hierarchy' );
	}
}
migrations/20200430075614_AddIndexableObjectIdAndTypeIndex.php000064400000001737151216032540017266 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Class AddIndexableObjectIdAndTypeIndex.
 */
class AddIndexableObjectIdAndTypeIndex extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$this->add_index(
			$this->get_table_name(),
			[
				'object_id',
				'object_type',
			],
			[
				'name' => 'object_id_and_type',
			]
		);
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		$this->remove_index(
			$this->get_table_name(),
			[
				'object_id',
				'object_type',
			],
			[
				'name' => 'object_id_and_type',
			]
		);
	}

	/**
	 * Retrieves the table name to use for storing indexables.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable' );
	}
}
migrations/20200507054848_DeleteDuplicateIndexables.php000064400000002113151216032540016135 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Class DeleteDuplicateIndexables.
 */
class DeleteDuplicateIndexables extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$table_name = $this->get_table_name();

		/*
		 * Deletes duplicate indexables that have the same object_id and object_type.
		 * The rows with a higher ID are deleted as those should be unused and could be outdated.
		 */
		$this->query( 'DELETE wyi FROM ' . $table_name . ' wyi INNER JOIN ' . $table_name . ' wyi2 WHERE wyi2.object_id = wyi.object_id AND wyi2.object_type = wyi.object_type AND wyi2.id < wyi.id;' );
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		// Nothing to do.
	}

	/**
	 * Retrieves the table name to use.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable' );
	}
}
migrations/20200616130143_ReplacePermalinkHashIndex.php000064400000004365151216032540016106 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * ReplacePermalinkHashIndex class.
 */
class ReplacePermalinkHashIndex extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$table_name = $this->get_table_name();
		$adapter    = $this->get_adapter();

		if ( ! $adapter->has_table( $table_name ) ) {
			return;
		}

		$this->change_column(
			$table_name,
			'permalink_hash',
			'string',
			[
				'null'  => true,
				'limit' => 40,
			]
		);

		if ( $adapter->has_index( $table_name, [ 'permalink_hash' ], [ 'name' => 'permalink_hash' ] ) ) {
			$this->remove_index(
				$table_name,
				[
					'permalink_hash',
				],
				[
					'name' => 'permalink_hash',
				]
			);
		}

		if ( ! $adapter->has_index( $table_name, [ 'permalink_hash', 'object_type' ], [ 'name' => 'permalink_hash_and_object_type' ] ) ) {
			$this->add_index(
				$table_name,
				[
					'permalink_hash',
					'object_type',
				],
				[
					'name' => 'permalink_hash_and_object_type',
				]
			);
		}
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		$table_name = $this->get_table_name();
		$adapter    = $this->get_adapter();

		if ( ! $adapter->has_table( $table_name ) ) {
			return;
		}

		if ( $adapter->has_index( $table_name, [ 'permalink_hash', 'object_type' ], [ 'name' => 'permalink_hash_and_object_type' ] ) ) {
			$this->remove_index(
				$table_name,
				[
					'permalink_hash',
					'object_type',
				],
				[
					'name' => 'permalink_hash_and_object_type',
				]
			);
		}

		$this->change_column(
			$table_name,
			'permalink_hash',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);

		if ( ! $adapter->has_index( $table_name, [ 'permalink_hash' ], [ 'name' => 'permalink_hash' ] ) ) {
			$this->add_index(
				$table_name,
				[
					'permalink_hash',
				],
				[
					'name' => 'permalink_hash',
				]
			);
		}
	}

	/**
	 * Retrieves the table name to use for storing indexables.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable' );
	}
}
migrations/20200513133401_ResetIndexableHierarchyTable.php000064400000001366151216032540016573 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Class ResetIndexableHierarchyTable.
 */
class ResetIndexableHierarchyTable extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$this->query( 'TRUNCATE TABLE ' . $this->get_table_name() );
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		// Nothing to do.
	}

	/**
	 * Retrieves the table name to use.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable_Hierarchy' );
	}
}
migrations/20200617122511_CreateSEOLinksTable.php000064400000004735151216032540014621 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * CreateSEOLinksTable class.
 */
class CreateSEOLinksTable extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$table_name = $this->get_table_name();
		$adapter    = $this->get_adapter();

		// The table may already have been created by legacy code.
		// If not, create it exactly as it was.
		if ( ! $adapter->table_exists( $table_name ) ) {
			$table = $this->create_table( $table_name, [ 'id' => false ] );
			$table->column(
				'id',
				'biginteger',
				[
					'primary_key'    => true,
					'limit'          => 20,
					'unsigned'       => true,
					'auto_increment' => true,
				]
			);
			$table->column( 'url', 'string', [ 'limit' => 255 ] );
			$table->column(
				'post_id',
				'biginteger',
				[
					'limit'    => 20,
					'unsigned' => true,
				]
			);
			$table->column(
				'target_post_id',
				'biginteger',
				[
					'limit'    => 20,
					'unsigned' => true,
				]
			);
			$table->column( 'type', 'string', [ 'limit' => 8 ] );
			$table->finish();
		}
		if ( ! $adapter->has_index( $table_name, [ 'post_id', 'type' ], [ 'name' => 'link_direction' ] ) ) {
			$this->add_index( $table_name, [ 'post_id', 'type' ], [ 'name' => 'link_direction' ] );
		}

		// Add these columns outside of the initial table creation as these did not exist on the legacy table.
		$this->add_column( $table_name, 'indexable_id', 'integer', [ 'unsigned' => true ] );
		$this->add_column( $table_name, 'target_indexable_id', 'integer', [ 'unsigned' => true ] );
		$this->add_column( $table_name, 'height', 'integer', [ 'unsigned' => true ] );
		$this->add_column( $table_name, 'width', 'integer', [ 'unsigned' => true ] );
		$this->add_column( $table_name, 'size', 'integer', [ 'unsigned' => true ] );
		$this->add_column( $table_name, 'language', 'string', [ 'limit' => 32 ] );
		$this->add_column( $table_name, 'region', 'string', [ 'limit' => 32 ] );

		$this->add_index( $table_name, [ 'indexable_id', 'type' ], [ 'name' => 'indexable_link_direction' ] );
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		$this->drop_table( $this->get_table_name() );
	}

	/**
	 * Returns the SEO Links table name.
	 *
	 * @return string
	 */
	private function get_table_name() {
		return Model::get_table_name( 'SEO_Links' );
	}
}
migrations/20171228151841_WpYoastPrimaryTerm.php000064400000003022151216032540014701 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Migration for the Primary Term.
 */
class WpYoastPrimaryTerm extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$table_name = $this->get_table_name();

		$indexable_table = $this->create_table( $table_name );

		$indexable_table->column(
			'post_id',
			'integer',
			[
				'unsigned' => true,
				'null'     => false,
				'limit'    => 11,
			]
		);
		$indexable_table->column(
			'term_id',
			'integer',
			[
				'unsigned' => true,
				'null'     => false,
				'limit'    => 11,
			]
		);
		$indexable_table->column(
			'taxonomy',
			'string',
			[
				'null'  => false,
				'limit' => 32,
			]
		);

		// Executes the SQL to create the table.
		$indexable_table->finish();

		$this->add_index(
			$table_name,
			[
				'post_id',
				'taxonomy',
			],
			[
				'name' => 'post_taxonomy',
			]
		);

		$this->add_index(
			$table_name,
			[
				'post_id',
				'term_id',
			],
			[
				'name' => 'post_term',
			]
		);

		$this->add_timestamps( $table_name );
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		$this->drop_table( $this->get_table_name() );
	}

	/**
	 * Retrieves the table name to use.
	 *
	 * @return string Table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Primary_Term' );
	}
}
migrations/20200430150130_ClearIndexableTables.php000064400000002057151216032540015055 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Class ClearIndexableTables.
 */
class ClearIndexableTables extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$this->query( 'TRUNCATE TABLE ' . $this->get_indexable_table_name() );
		$this->query( 'TRUNCATE TABLE ' . $this->get_indexable_hierarchy_table_name() );
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		// Nothing to do.
	}

	/**
	 * Retrieves the table name to use for storing indexables.
	 *
	 * @return string The table name to use.
	 */
	protected function get_indexable_table_name() {
		return Model::get_table_name( 'Indexable' );
	}

	/**
	 * Retrieves the table name to use.
	 *
	 * @return string The table name to use.
	 */
	protected function get_indexable_hierarchy_table_name() {
		return Model::get_table_name( 'Indexable_Hierarchy' );
	}
}
migrations/20211020091404_AddObjectTimestamps.php000064400000003007151216032540014747 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * AddObjectTimestamps class.
 */
class AddObjectTimestamps extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$this->add_column(
			$this->get_table_name(),
			'object_last_modified',
			'datetime',
			[
				'null'    => true,
				'default' => null,
			]
		);
		$this->add_column(
			$this->get_table_name(),
			'object_published_at',
			'datetime',
			[
				'null'    => true,
				'default' => null,
			]
		);
		$this->add_index(
			$this->get_table_name(),
			[
				'object_published_at',
				'is_robots_noindex',
				'object_type',
				'object_sub_type',
			],
			[
				'name' => 'published_sitemap_index',
			]
		);
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		$this->remove_column( $this->get_table_name(), 'object_last_modified' );
		$this->remove_column( $this->get_table_name(), 'object_published_at' );
		$this->remove_index(
			$this->get_table_name(),
			[
				'object_published_at',
				'is_robots_noindex',
				'object_type',
				'object_sub_type',
			],
			[
				'name' => 'published_sitemap_index',
			]
		);
	}

	/**
	 * Retrieves the table name to use for storing indexables.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable' );
	}
}
migrations/20201216124002_ExpandIndexableIDColumnLengths.php000064400000002023151216032540017026 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * ExpandIndexableIDColumnLengths class.
 */
class ExpandIndexableIDColumnLengths extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * The columns to change the column type and length of.
	 *
	 * @var string[]
	 */
	protected static $columns_to_change = [
		'object_id',
		'author_id',
		'post_parent',
	];

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		foreach ( self::$columns_to_change as $column ) {
			$this->change_column(
				$this->get_table_name(),
				$column,
				'biginteger',
				[ 'limit' => 20 ]
			);
		}
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
	}

	/**
	 * Retrieves the table name to use for storing indexables.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable' );
	}
}
migrations/20200428123747_BreadcrumbTitleAndHierarchyReset.php000064400000002356151216032540017444 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Class BreadcrumbTitleAndHierarchyReset.
 */
class BreadcrumbTitleAndHierarchyReset extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$this->change_column( $this->get_indexable_table_name(), 'breadcrumb_title', 'text', [ 'null' => true ] );
		$this->query( 'DELETE FROM ' . $this->get_indexable_hierarchy_table_name() );
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		$this->change_column(
			$this->get_indexable_table_name(),
			'breadcrumb_title',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);
	}

	/**
	 * Retrieves the table name to use for storing indexables.
	 *
	 * @return string The table name to use.
	 */
	protected function get_indexable_table_name() {
		return Model::get_table_name( 'Indexable' );
	}

	/**
	 * Retrieves the table name to use.
	 *
	 * @return string The table name to use.
	 */
	protected function get_indexable_hierarchy_table_name() {
		return Model::get_table_name( 'Indexable_Hierarchy' );
	}
}
migrations/20190529075038_WpYoastDropIndexableMetaTableIfExists.php000064400000001573151216032540020425 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Class WpYoastDropIndexableMetaTableIfExists.
 */
class WpYoastDropIndexableMetaTableIfExists extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$table_name = $this->get_table_name();

		// This can be done safely as it executes a DROP IF EXISTS.
		$this->drop_table( $table_name );
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		// No down required. This specific table should never exist.
	}

	/**
	 * Retrieves the table name to use.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable_Meta' );
	}
}
migrations/20200428194858_ExpandIndexableColumnLengths.php000064400000003413151216032540016653 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Class ExpandIndexableColumnLengths.
 */
class ExpandIndexableColumnLengths extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$this->change_column( $this->get_table_name(), 'title', 'text', [ 'null' => true ] );
		$this->change_column( $this->get_table_name(), 'open_graph_title', 'text', [ 'null' => true ] );
		$this->change_column( $this->get_table_name(), 'twitter_title', 'text', [ 'null' => true ] );
		$this->change_column( $this->get_table_name(), 'open_graph_image_source', 'text', [ 'null' => true ] );
		$this->change_column( $this->get_table_name(), 'twitter_image_source', 'text', [ 'null' => true ] );
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		$attr_limit_191 = [
			'null'  => true,
			'limit' => 191,
		];

		$this->change_column(
			$this->get_table_name(),
			'title',
			'string',
			$attr_limit_191
		);
		$this->change_column(
			$this->get_table_name(),
			'opengraph_title',
			'string',
			$attr_limit_191
		);
		$this->change_column(
			$this->get_table_name(),
			'twitter_title',
			'string',
			$attr_limit_191
		);
		$this->change_column(
			$this->get_table_name(),
			'open_graph_image_source',
			'string',
			$attr_limit_191
		);
		$this->change_column(
			$this->get_table_name(),
			'twitter_image_source',
			'string',
			$attr_limit_191
		);
	}

	/**
	 * Retrieves the table name to use for storing indexables.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable' );
	}
}
migrations/20171228151840_WpYoastIndexable.php000064400000014256151216032540014333 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * Indexable migration.
 */
class WpYoastIndexable extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$this->add_table();
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		$this->drop_table( $this->get_table_name() );
	}

	/**
	 * Creates the indexable table.
	 *
	 * @return void
	 */
	private function add_table() {
		$table_name = $this->get_table_name();

		$indexable_table = $this->create_table( $table_name );

		// Permalink.
		$indexable_table->column( 'permalink', 'mediumtext', [ 'null' => true ] );
		$indexable_table->column(
			'permalink_hash',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);

		// Object information.
		$indexable_table->column(
			'object_id',
			'integer',
			[
				'unsigned' => true,
				'null'     => true,
				'limit'    => 11,
			]
		);
		$indexable_table->column(
			'object_type',
			'string',
			[
				'null'  => false,
				'limit' => 32,
			]
		);
		$indexable_table->column(
			'object_sub_type',
			'string',
			[
				'null'  => true,
				'limit' => 32,
			]
		);

		// Ownership.
		$indexable_table->column(
			'author_id',
			'integer',
			[
				'unsigned' => true,
				'null'     => true,
				'limit'    => 11,
			]
		);
		$indexable_table->column(
			'post_parent',
			'integer',
			[
				'unsigned' => true,
				'null'     => true,
				'limit'    => 11,
			]
		);

		// Title and description.
		$indexable_table->column(
			'title',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);
		$indexable_table->column( 'description', 'text', [ 'null' => true ] );
		$indexable_table->column(
			'breadcrumb_title',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);

		// Post metadata: status, public, protected.
		$indexable_table->column(
			'post_status',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);
		$indexable_table->column(
			'is_public',
			'boolean',
			[
				'null'    => true,
				'default' => null,
			]
		);
		$indexable_table->column( 'is_protected', 'boolean', [ 'default' => false ] );
		$indexable_table->column(
			'has_public_posts',
			'boolean',
			[
				'null'    => true,
				'default' => null,
			]
		);

		$indexable_table->column(
			'number_of_pages',
			'integer',
			[
				'unsigned' => true,
				'null'     => true,
				'default'  => null,
				'limit'    => 11,
			]
		);

		$indexable_table->column( 'canonical', 'mediumtext', [ 'null' => true ] );

		// SEO and readability analysis.
		$indexable_table->column(
			'primary_focus_keyword',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);
		$indexable_table->column(
			'primary_focus_keyword_score',
			'integer',
			[
				'null'  => true,
				'limit' => 3,
			]
		);
		$indexable_table->column(
			'readability_score',
			'integer',
			[
				'null'  => true,
				'limit' => 3,
			]
		);
		$indexable_table->column( 'is_cornerstone', 'boolean', [ 'default' => false ] );

		// Robots.
		$indexable_table->column(
			'is_robots_noindex',
			'boolean',
			[
				'null'    => true,
				'default' => false,
			]
		);
		$indexable_table->column(
			'is_robots_nofollow',
			'boolean',
			[
				'null'    => true,
				'default' => false,
			]
		);
		$indexable_table->column(
			'is_robots_noarchive',
			'boolean',
			[
				'null'    => true,
				'default' => false,
			]
		);
		$indexable_table->column(
			'is_robots_noimageindex',
			'boolean',
			[
				'null'    => true,
				'default' => false,
			]
		);
		$indexable_table->column(
			'is_robots_nosnippet',
			'boolean',
			[
				'null'    => true,
				'default' => false,
			]
		);

		// Twitter.
		$indexable_table->column(
			'twitter_title',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);
		$indexable_table->column( 'twitter_image', 'mediumtext', [ 'null' => true ] );
		$indexable_table->column( 'twitter_description', 'mediumtext', [ 'null' => true ] );
		$indexable_table->column(
			'twitter_image_id',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);
		$indexable_table->column(
			'twitter_image_source',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);

		// Open-Graph.
		$indexable_table->column(
			'open_graph_title',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);
		$indexable_table->column( 'open_graph_description', 'mediumtext', [ 'null' => true ] );
		$indexable_table->column( 'open_graph_image', 'mediumtext', [ 'null' => true ] );
		$indexable_table->column(
			'open_graph_image_id',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);
		$indexable_table->column(
			'open_graph_image_source',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);
		$indexable_table->column( 'open_graph_image_meta', 'text', [ 'null' => true ] );

		// Link count.
		$indexable_table->column(
			'link_count',
			'integer',
			[
				'null'  => true,
				'limit' => 11,
			]
		);
		$indexable_table->column(
			'incoming_link_count',
			'integer',
			[
				'null'  => true,
				'limit' => 11,
			]
		);

		// Prominent words.
		$indexable_table->column(
			'prominent_words_version',
			'integer',
			[
				'null'     => true,
				'limit'    => 11,
				'unsigned' => true,
				'default'  => null,
			]
		);

		$indexable_table->finish();

		$this->add_indexes( $table_name );

		$this->add_timestamps( $table_name );
	}

	/**
	 * Adds indexes to the indexable table.
	 *
	 * @param string $indexable_table_name The name of the indexable table.
	 *
	 * @return void
	 */
	private function add_indexes( $indexable_table_name ) {
		$this->add_index(
			$indexable_table_name,
			[
				'object_type',
				'object_sub_type',
			],
			[
				'name' => 'object_type_and_sub_type',
			]
		);

		$this->add_index(
			$indexable_table_name,
			'permalink_hash',
			[
				'name' => 'permalink_hash',
			]
		);
	}

	/**
	 * Retrieves the table name to use for storing indexables.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable' );
	}
}
migrations/20200728095334_AddIndexesForProminentWordsOnIndexables.php000064400000002265151216032540020777 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * AddIndexesForProminentWordsOnIndexables class.
 */
class AddIndexesForProminentWordsOnIndexables extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * The columns on which an index should be added.
	 *
	 * @var string[]
	 */
	private $columns_with_index = [
		'prominent_words_version',
		'object_type',
		'object_sub_type',
		'post_status',
	];

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$table_name = $this->get_table_name();
		$adapter    = $this->get_adapter();

		if ( ! $adapter->has_index( $table_name, $this->columns_with_index, [ 'name' => 'prominent_words' ] ) ) {
			$this->add_index(
				$table_name,
				$this->columns_with_index,
				[
					'name' => 'prominent_words',
				]
			);
		}
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
	}

	/**
	 * Retrieves the table name to use.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable' );
	}
}
migrations/20200702141921_CreateIndexableSubpagesIndex.php000064400000002354151216032540016573 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * CreateIndexableSubpagesIndex class.
 */
class CreateIndexableSubpagesIndex extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$this->change_column(
			$this->get_table_name(),
			'post_status',
			'string',
			[
				'null'  => true,
				'limit' => 20,
			]
		);
		$this->add_index(
			$this->get_table_name(),
			[ 'post_parent', 'object_type', 'post_status', 'object_id' ],
			[ 'name' => 'subpages' ]
		);
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		$this->change_column(
			$this->get_table_name(),
			'post_status',
			'string',
			[
				'null'  => true,
				'limit' => 191,
			]
		);
		$this->remove_index(
			$this->get_table_name(),
			[ 'post_parent', 'object_type', 'post_status', 'object_id' ],
			[ 'name' => 'subpages' ]
		);
	}

	/**
	 * Retrieves the table name to use for storing indexables.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable' );
	}
}
migrations/20201202144329_AddEstimatedReadingTime.php000064400000001703151216032540015531 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * AddEstimatedReadingTime class.
 */
class AddEstimatedReadingTime extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$table_name = $this->get_table_name();

		$this->add_column(
			$table_name,
			'estimated_reading_time_minutes',
			'integer',
			[
				'null'     => true,
				'default'  => null,
			]
		);
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		$table_name = $this->get_table_name();

		$this->remove_column( $table_name, 'estimated_reading_time_minutes' );
	}

	/**
	 * Retrieves the table name to use.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Indexable' );
	}
}
migrations/20200609154515_AddHasAncestorsColumn.php000064400000001661151216032540015267 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;
use Yoast\WP\SEO\WordPress\Wrapper;

/**
 * Class AddHasAncestorsColumn.
 */
class AddHasAncestorsColumn extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		$this->add_column(
			Model::get_table_name( 'Indexable' ),
			'has_ancestors',
			'boolean',
			[
				'default' => false,
			]
		);

		Wrapper::get_wpdb()->query(
			'
			UPDATE ' . Model::get_table_name( 'Indexable' ) . '
			SET has_ancestors = 1
			WHERE id IN ( SELECT indexable_id FROM ' . Model::get_table_name( 'Indexable_Hierarchy' ) . ' )
			'
		);
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
		$this->remove_column( Model::get_table_name( 'Indexable' ), 'has_ancestors' );
	}
}
migrations/20201216141134_ExpandPrimaryTermIDColumnLengths.php000064400000002005151216032540017413 0ustar00<?php

namespace Yoast\WP\SEO\Config\Migrations;

use Yoast\WP\Lib\Migrations\Migration;
use Yoast\WP\Lib\Model;

/**
 * ExpandPrimaryTermIDColumnLengths class.
 */
class ExpandPrimaryTermIDColumnLengths extends Migration {

	/**
	 * The plugin this migration belongs to.
	 *
	 * @var string
	 */
	public static $plugin = 'free';

	/**
	 * The columns to change the column type and length of.
	 *
	 * @var string[]
	 */
	protected static $columns_to_change = [
		'post_id',
		'term_id',
	];

	/**
	 * Migration up.
	 *
	 * @return void
	 */
	public function up() {
		foreach ( self::$columns_to_change as $column ) {
			$this->change_column(
				$this->get_table_name(),
				$column,
				'biginteger',
				[ 'limit' => 20 ]
			);
		}
	}

	/**
	 * Migration down.
	 *
	 * @return void
	 */
	public function down() {
	}

	/**
	 * Retrieves the table name to use for storing indexables.
	 *
	 * @return string The table name to use.
	 */
	protected function get_table_name() {
		return Model::get_table_name( 'Primary_Term' );
	}
}
researcher-languages.php000064400000000523151216032540011343 0ustar00<?php

namespace Yoast\WP\SEO\Config;

/**
 * Holds all languages supported with specific researches for our readability analysis.
 */
class Researcher_Languages {

	public const SUPPORTED_LANGUAGES = [ 'ar', 'ca', 'de', 'en', 'es', 'fa', 'fr', 'he', 'hu', 'id', 'it', 'nb', 'nl', 'pl', 'pt', 'ru', 'sv', 'tr', 'cs', 'sk', 'el', 'ja' ];
}
badge-group-names.php000064400000002771151216032540010560 0ustar00<?php

namespace Yoast\WP\SEO\Config;

/**
 * Class Badge_Group_Names.
 *
 * This class defines groups for "new" badges, with the version in which those groups are no longer considered
 * to be "new".
 */
class Badge_Group_Names {

	public const GROUP_GLOBAL_TEMPLATES = 'global-templates';

	/**
	 * Constant describing when certain groups of new badges will no longer be shown.
	 */
	public const GROUP_NAMES = [
		self::GROUP_GLOBAL_TEMPLATES => '16.7-beta0',
	];

	/**
	 * The current plugin version.
	 *
	 * @var string
	 */
	protected $version;

	/**
	 * Badge_Group_Names constructor.
	 *
	 * @param string|null $version Optional: the current plugin version.
	 */
	public function __construct( $version = null ) {
		if ( ! $version ) {
			$version = \WPSEO_VERSION;
		}
		$this->version = $version;
	}

	/**
	 * Check whether a group of badges is still eligible for a "new" badge.
	 *
	 * @param string      $group           One of the GROUP_* constants.
	 * @param string|null $current_version The current version of the plugin that's being checked.
	 *
	 * @return bool Whether a group of badges is still eligible for a "new" badge.
	 */
	public function is_still_eligible_for_new_badge( $group, $current_version = null ) {
		if ( ! \array_key_exists( $group, $this::GROUP_NAMES ) ) {
			return false;
		}

		$group_version = $this::GROUP_NAMES[ $group ];

		if ( \is_null( $current_version ) ) {
			$current_version = $this->version;
		}

		return (bool) \version_compare( $group_version, $current_version, '>' );
	}
}
wincher-pkce-provider.php000064400000016020151216032540011462 0ustar00<?php

namespace Yoast\WP\SEO\Config;

use Exception;
use UnexpectedValueException;
use YoastSEO_Vendor\GuzzleHttp\Exception\BadResponseException;
use YoastSEO_Vendor\League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use YoastSEO_Vendor\League\OAuth2\Client\Provider\GenericProvider;
use YoastSEO_Vendor\League\OAuth2\Client\Token\AccessToken;
use YoastSEO_Vendor\League\OAuth2\Client\Token\AccessTokenInterface;
use YoastSEO_Vendor\League\OAuth2\Client\Tool\BearerAuthorizationTrait;
use YoastSEO_Vendor\Psr\Http\Message\RequestInterface;
use YoastSEO_Vendor\Psr\Log\InvalidArgumentException;

/**
 * Class Wincher_PKCE_Provider
 *
 * @codeCoverageIgnore Ignoring as this class is purely a temporary wrapper until https://github.com/thephpleague/oauth2-client/pull/901 is merged.
 *
 * @phpcs:disable WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase -- This class extends an external class.
 * @phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- This class extends an external class.
 */
class Wincher_PKCE_Provider extends GenericProvider {

	use BearerAuthorizationTrait;

	/**
	 * The method to use.
	 *
	 * @var string
	 */
	protected $pkceMethod = null;

	/**
	 * The PKCE code.
	 *
	 * @var string
	 */
	protected $pkceCode;

	/**
	 * Set the value of the pkceCode parameter.
	 *
	 * When using PKCE this should be set before requesting an access token.
	 *
	 * @param string $pkce_code The value for the pkceCode.
	 * @return self
	 */
	public function setPkceCode( $pkce_code ) {
		$this->pkceCode = $pkce_code;
		return $this;
	}

	/**
	 * Returns the current value of the pkceCode parameter.
	 *
	 * This can be accessed by the redirect handler during authorization.
	 *
	 * @return string
	 */
	public function getPkceCode() {
		return $this->pkceCode;
	}

	/**
	 * Returns a new random string to use as PKCE code_verifier and
	 * hashed as code_challenge parameters in an authorization flow.
	 * Must be between 43 and 128 characters long.
	 *
	 * @param int $length Length of the random string to be generated.
	 *
	 * @return string
	 *
	 * @throws Exception Throws exception if an invalid value is passed to random_bytes.
	 */
	protected function getRandomPkceCode( $length = 64 ) {
		return \substr(
			\strtr(
				// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
				\base64_encode( \random_bytes( $length ) ),
				'+/',
				'-_'
			),
			0,
			$length
		);
	}

	/**
	 * Returns the current value of the pkceMethod parameter.
	 *
	 * @return string|null
	 */
	protected function getPkceMethod() {
		return $this->pkceMethod;
	}

	/**
	 * Returns authorization parameters based on provided options.
	 *
	 * @param array $options The options to use in the authorization parameters.
	 *
	 * @return array The authorization parameters
	 *
	 * @throws InvalidArgumentException Throws exception if an invalid PCKE method is passed in the options.
	 * @throws Exception                When something goes wrong with generating the PKCE code.
	 */
	protected function getAuthorizationParameters( array $options ) {
		if ( empty( $options['state'] ) ) {
			$options['state'] = $this->getRandomState();
		}

		if ( empty( $options['scope'] ) ) {
			$options['scope'] = $this->getDefaultScopes();
		}

		$options += [
			'response_type'   => 'code',
		];

		if ( \is_array( $options['scope'] ) ) {
			$separator        = $this->getScopeSeparator();
			$options['scope'] = \implode( $separator, $options['scope'] );
		}

		// Store the state as it may need to be accessed later on.
		$this->state = $options['state'];

		$pkce_method = $this->getPkceMethod();
		if ( ! empty( $pkce_method ) ) {
			$this->pkceCode = $this->getRandomPkceCode();
			if ( $pkce_method === 'S256' ) {
				$options['code_challenge'] = \trim(
					\strtr(
						// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
						\base64_encode( \hash( 'sha256', $this->pkceCode, true ) ),
						'+/',
						'-_'
					),
					'='
				);
			}
			elseif ( $pkce_method === 'plain' ) {
				$options['code_challenge'] = $this->pkceCode;
			}
			else {
				throw new InvalidArgumentException( 'Unknown PKCE method "' . $pkce_method . '".' );
			}
			$options['code_challenge_method'] = $pkce_method;
		}

		// Business code layer might set a different redirect_uri parameter.
		// Depending on the context, leave it as-is.
		if ( ! isset( $options['redirect_uri'] ) ) {
			$options['redirect_uri'] = $this->redirectUri;
		}

		$options['client_id'] = $this->clientId;

		return $options;
	}

	/**
	 * Requests an access token using a specified grant and option set.
	 *
	 * @param mixed $grant   The grant to request access for.
	 * @param array $options The options to use with the current request.
	 *
	 * @return AccessToken|AccessTokenInterface The access token.
	 *
	 * @throws UnexpectedValueException Exception thrown if the provider response contains errors.
	 */
	public function getAccessToken( $grant, array $options = [] ) {
		$grant = $this->verifyGrant( $grant );

		$params = [
			'client_id'     => $this->clientId,
			'client_secret' => $this->clientSecret,
			'redirect_uri'  => $this->redirectUri,
		];

		if ( ! empty( $this->pkceCode ) ) {
			$params['code_verifier'] = $this->pkceCode;
		}

		$params   = $grant->prepareRequestParameters( $params, $options );
		$request  = $this->getAccessTokenRequest( $params );
		$response = $this->getParsedResponse( $request );

		if ( \is_array( $response ) === false ) {
			throw new UnexpectedValueException(
				'Invalid response received from Authorization Server. Expected JSON.'
			);
		}

		$prepared = $this->prepareAccessTokenResponse( $response );
		$token    = $this->createAccessToken( $prepared, $grant );

		return $token;
	}

	/**
	 * Returns all options that can be configured.
	 *
	 * @return array The configurable options.
	 */
	protected function getConfigurableOptions() {
		return \array_merge(
			$this->getRequiredOptions(),
			[
				'accessTokenMethod',
				'accessTokenResourceOwnerId',
				'scopeSeparator',
				'responseError',
				'responseCode',
				'responseResourceOwnerId',
				'scopes',
				'pkceMethod',
			]
		);
	}

	/**
	 * Parses the request response.
	 *
	 * @param RequestInterface $request The request interface.
	 *
	 * @return array The parsed response.
	 *
	 * @throws IdentityProviderException Exception thrown if there is no proper identity provider.
	 */
	public function getParsedResponse( RequestInterface $request ) {
		try {
			$response = $this->getResponse( $request );
		} catch ( BadResponseException $e ) {
			$response = $e->getResponse();
		}

		$parsed = $this->parseResponse( $response );

		$this->checkResponse( $response, $parsed );

		// We always expect an array from the API except for on DELETE requests.
		// We convert to an array here to prevent problems with array_key_exists on PHP8.
		if ( ! \is_array( $parsed ) ) {
			$parsed = [ 'data' => [] ];
		}

		// Add the response code as this is omitted from Winchers API.
		if ( ! \array_key_exists( 'status', $parsed ) ) {
			$parsed['status'] = $response->getStatusCode();
		}

		return $parsed;
	}
}
conflicting-plugins.php000064400000012443151216032540011236 0ustar00<?php

namespace Yoast\WP\SEO\Config;

/**
 * Conflicting_Plugins class that holds all known conflicting plugins.
 */
class Conflicting_Plugins {

	public const OPEN_GRAPH_PLUGINS = [
		'2-click-socialmedia-buttons/2-click-socialmedia-buttons.php',
		// 2 Click Social Media Buttons.
		'add-link-to-facebook/add-link-to-facebook.php',         // Add Link to Facebook.
		'add-meta-tags/add-meta-tags.php',                       // Add Meta Tags.
		'easy-facebook-share-thumbnails/esft.php',               // Easy Facebook Share Thumbnail.
		'facebook/facebook.php',                                 // Facebook (official plugin).
		'facebook-awd/AWD_facebook.php',                         // Facebook AWD All in one.
		'facebook-featured-image-and-open-graph-meta-tags/fb-featured-image.php',
		// Facebook Featured Image & OG Meta Tags.
		'facebook-meta-tags/facebook-metatags.php',              // Facebook Meta Tags.
		'wonderm00ns-simple-facebook-open-graph-tags/wonderm00n-open-graph.php',
		// Facebook Open Graph Meta Tags for WordPress.
		'facebook-revised-open-graph-meta-tag/index.php',        // Facebook Revised Open Graph Meta Tag.
		'facebook-thumb-fixer/_facebook-thumb-fixer.php',        // Facebook Thumb Fixer.
		'facebook-and-digg-thumbnail-generator/facebook-and-digg-thumbnail-generator.php',
		// Fedmich's Facebook Open Graph Meta.
		'network-publisher/networkpub.php',                      // Network Publisher.
		'nextgen-facebook/nextgen-facebook.php',                 // NextGEN Facebook OG.
		'opengraph/opengraph.php',                               // Open Graph.
		'open-graph-protocol-framework/open-graph-protocol-framework.php',
		// Open Graph Protocol Framework.
		'seo-facebook-comments/seofacebook.php',                 // SEO Facebook Comments.
		'sexybookmarks/sexy-bookmarks.php',                      // Shareaholic.
		'shareaholic/sexy-bookmarks.php',                        // Shareaholic.
		'sharepress/sharepress.php',                             // SharePress.
		'simple-facebook-connect/sfc.php',                       // Simple Facebook Connect.
		'social-discussions/social-discussions.php',             // Social Discussions.
		'social-sharing-toolkit/social_sharing_toolkit.php',     // Social Sharing Toolkit.
		'socialize/socialize.php',                               // Socialize.
		'only-tweet-like-share-and-google-1/tweet-like-plusone.php',
		// Tweet, Like, Google +1 and Share.
		'wordbooker/wordbooker.php',                             // Wordbooker.
		'wpsso/wpsso.php',                                       // WordPress Social Sharing Optimization.
		'wp-caregiver/wp-caregiver.php',                         // WP Caregiver.
		'wp-facebook-like-send-open-graph-meta/wp-facebook-like-send-open-graph-meta.php',
		// WP Facebook Like Send & Open Graph Meta.
		'wp-facebook-open-graph-protocol/wp-facebook-ogp.php',   // WP Facebook Open Graph protocol.
		'wp-ogp/wp-ogp.php',                                     // WP-OGP.
		'zoltonorg-social-plugin/zosp.php',                      // Zolton.org Social Plugin.
	];

	public const XML_SITEMAPS_PLUGINS = [
		'google-sitemap-plugin/google-sitemap-plugin.php',
		// Google Sitemap (BestWebSoft).
		'xml-sitemaps/xml-sitemaps.php',
		// XML Sitemaps (Denis de Bernardy and Mike Koepke).
		'bwp-google-xml-sitemaps/bwp-simple-gxs.php',
		// Better WordPress Google XML Sitemaps (Khang Minh).
		'google-sitemap-generator/sitemap.php',
		// Google XML Sitemaps (Arne Brachhold).
		'xml-sitemap-feed/xml-sitemap.php',
		// XML Sitemap & Google News feeds (RavanH).
		'google-monthly-xml-sitemap/monthly-xml-sitemap.php',
		// Google Monthly XML Sitemap (Andrea Pernici).
		'simple-google-sitemap-xml/simple-google-sitemap-xml.php',
		// Simple Google Sitemap XML (iTx Technologies).
		'another-simple-xml-sitemap/another-simple-xml-sitemap.php',
		// Another Simple XML Sitemap.
		'xml-maps/google-sitemap.php',
		// Xml Sitemap (Jason Martens).
		'google-xml-sitemap-generator-by-anton-dachauer/adachauer-google-xml-sitemap.php',
		// Google XML Sitemap Generator by Anton Dachauer (Anton Dachauer).
		'wp-xml-sitemap/wp-xml-sitemap.php',
		// WP XML Sitemap (Team Vivacity).
		'sitemap-generator-for-webmasters/sitemap.php',
		// Sitemap Generator for Webmasters (iwebslogtech).
		'xml-sitemap-xml-sitemapcouk/xmls.php',
		// XML Sitemap - XML-Sitemap.co.uk (Simon Hancox).
		'sewn-in-xml-sitemap/sewn-xml-sitemap.php',
		// Sewn In XML Sitemap (jcow).
		'rps-sitemap-generator/rps-sitemap-generator.php',
		// RPS Sitemap Generator (redpixelstudios).
	];

	public const CLOAKING_PLUGINS = [
		'rs-head-cleaner/rs-head-cleaner.php',
		// RS Head Cleaner Plus https://wordpress.org/plugins/rs-head-cleaner/.
		'rs-head-cleaner-lite/rs-head-cleaner-lite.php',
		// RS Head Cleaner Lite https://wordpress.org/plugins/rs-head-cleaner-lite/.
	];

	public const SEO_PLUGINS = [
		'all-in-one-seo-pack/all_in_one_seo_pack.php',           // All in One SEO Pack.
		'seo-ultimate/seo-ultimate.php',                         // SEO Ultimate.
		'seo-by-rank-math/rank-math.php',                        // Rank Math.
	];

	/**
	 * Returns the list of all conflicting plugins.
	 *
	 * @return array The list of all conflicting plugins.
	 */
	public static function all_plugins() {
		return \array_merge(
			self::OPEN_GRAPH_PLUGINS,
			self::XML_SITEMAPS_PLUGINS,
			self::CLOAKING_PLUGINS,
			self::SEO_PLUGINS
		);
	}
}
semrush-client.php000064400000005172151216032540010223 0ustar00<?php

namespace Yoast\WP\SEO\Config;

use Yoast\WP\SEO\Exceptions\OAuth\Authentication_Failed_Exception;
use Yoast\WP\SEO\Exceptions\OAuth\Tokens\Empty_Property_Exception;
use Yoast\WP\SEO\Exceptions\OAuth\Tokens\Empty_Token_Exception;
use Yoast\WP\SEO\Helpers\Options_Helper;
use Yoast\WP\SEO\Wrappers\WP_Remote_Handler;
use YoastSEO_Vendor\GuzzleHttp\Client;
use YoastSEO_Vendor\League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use YoastSEO_Vendor\League\OAuth2\Client\Provider\GenericProvider;

/**
 * Class SEMrush_Client
 */
class SEMrush_Client extends OAuth_Client {

	/**
	 * The option's key.
	 */
	public const TOKEN_OPTION = 'semrush_tokens';

	/**
	 * SEMrush_Client constructor.
	 *
	 * @param Options_Helper    $options_helper    The Options_Helper instance.
	 * @param WP_Remote_Handler $wp_remote_handler The request handler.
	 *
	 * @throws Empty_Property_Exception Throws when one of the required properties is empty.
	 */
	public function __construct(
		Options_Helper $options_helper,
		WP_Remote_Handler $wp_remote_handler
	) {
		$provider = new GenericProvider(
			[
				'clientId'                => 'yoast',
				'clientSecret'            => 'YdqNsWwnP4vE54WO1ugThKEjGMxMAHJt',
				'redirectUri'             => 'https://oauth.semrush.com/oauth2/yoast/success',
				'urlAuthorize'            => 'https://oauth.semrush.com/oauth2/authorize',
				'urlAccessToken'          => 'https://oauth.semrush.com/oauth2/access_token',
				'urlResourceOwnerDetails' => 'https://oauth.semrush.com/oauth2/resource',
			],
			[
				'httpClient' => new Client( [ 'handler' => $wp_remote_handler ] ),
			]
		);

		parent::__construct(
			self::TOKEN_OPTION,
			$provider,
			$options_helper
		);
	}

	/**
	 * Performs the specified request.
	 *
	 * @codeCoverageIgnore
	 *
	 * @param string $method  The HTTP method to use.
	 * @param string $url     The URL to send the request to.
	 * @param array  $options The options to pass along to the request.
	 *
	 * @return mixed The parsed API response.
	 *
	 * @throws IdentityProviderException Exception thrown if there's something wrong with the identifying data.
	 * @throws Authentication_Failed_Exception Exception thrown if authentication has failed.
	 * @throws Empty_Token_Exception Exception thrown if the token is empty.
	 */
	public function do_request( $method, $url, array $options ) {
		// Add the access token to the GET parameters as well since this is what
		// the SEMRush API expects.
		$options = \array_merge_recursive(
			$options,
			[
				'params' => [
					'access_token' => $this->get_tokens()->access_token,
				],
			]
		);

		return parent::do_request( $method, $url, $options );
	}
}
schema-ids.php000064400000002103151216032540007265 0ustar00<?php

namespace Yoast\WP\SEO\Config;

/**
 * Class Schema_IDs.
 */
class Schema_IDs {

	/**
	 * Hash used for the Author `@id`.
	 */
	public const AUTHOR_HASH = '#author';

	/**
	 * Hash used for the Author Logo's `@id`.
	 */
	public const AUTHOR_LOGO_HASH = '#authorlogo';

	/**
	 * Hash used for the Breadcrumb's `@id`.
	 */
	public const BREADCRUMB_HASH = '#breadcrumb';

	/**
	 * Hash used for the Person `@id`.
	 */
	public const PERSON_HASH = '#/schema/person/';

	/**
	 * Hash used for the Article `@id`.
	 */
	public const ARTICLE_HASH = '#article';

	/**
	 * Hash used for the Organization `@id`.
	 */
	public const ORGANIZATION_HASH = '#organization';

	/**
	 * Hash used for the Organization `@id`.
	 */
	public const ORGANIZATION_LOGO_HASH = '#/schema/logo/image/';

	/**
	 * Hash used for the logo `@id`.
	 */
	public const PERSON_LOGO_HASH = '#/schema/person/image/';

	/**
	 * Hash used for an Article's primary image `@id`.
	 */
	public const PRIMARY_IMAGE_HASH = '#primaryimage';

	/**
	 * Hash used for the Website's `@id`.
	 */
	public const WEBSITE_HASH = '#website';
}

F1le Man4ger