Your IP : 216.73.216.162


Current Path : /home/x/b/o/xbodynamge/namtation/wp-content/
Upload File :
Current File : /home/x/b/o/xbodynamge/namtation/wp-content/aioseo.tar

aioseo-replacevar-service.php000066600000006637151117035130012331 0ustar00<?php

// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Given it's a very specific case.
namespace Yoast\WP\SEO\Services\Importing\Aioseo;

/**
 * Replaces AISOEO replacevars with Yoast ones.
 */
class Aioseo_Replacevar_Service {

	/**
	 * Mapping between the AiOSEO replace vars and the Yoast replace vars.
	 *
	 * @var array
	 *
	 * @see https://yoast.com/help/list-available-snippet-variables-yoast-seo/
	 */
	protected $replace_vars_map = [
		// The key is the AiOSEO replace var, the value is the Yoast replace var (see class-wpseo-replace-vars).
		'#archive_title'             => '%%archive_title%%',
		'#archive_date'              => '%%date%%',
		'#attachment_caption'        => '%%caption%%',
		'#author_bio'                => '%%user_description%%',
		'#author_first_name'         => '%%author_first_name%%',
		'#author_last_name'          => '%%author_last_name%%',
		'#author_name'               => '%%name%%',
		'#blog_title'                => '%%sitename%%', // Same with #site_title.
		'#categories'                => '%%category%%',
		'#current_date'              => '%%currentdate%%',
		'#current_day'               => '%%currentday%%',
		'#current_month'             => '%%currentmonth%%',
		'#current_year'              => '%%currentyear%%',
		'#parent_title'              => '%%parent_title%%',
		'#page_number'               => '%%pagenumber%%',
		'#permalink'                 => '%%permalink%%',
		'#post_content'              => '%%post_content%%',
		'#post_date'                 => '%%date%%',
		'#post_day'                  => '%%post_day%%',
		'#post_month'                => '%%post_month%%',
		'#post_title'                => '%%title%%',
		'#post_year'                 => '%%post_year%%',
		'#post_excerpt_only'         => '%%excerpt_only%%',
		'#post_excerpt'              => '%%excerpt%%',
		'#search_term'               => '%%searchphrase%%',
		'#separator_sa'              => '%%sep%%',
		'#site_title'                => '%%sitename%%',
		'#tagline'                   => '%%sitedesc%%',
		'#taxonomy_title'            => '%%category_title%%',
		'#taxonomy_description'      => '%%term_description%%',
	];

	/**
	 * Edits the replace_vars map of the class.
	 *
	 * @param string $aioseo_var The AIOSEO replacevar.
	 * @param string $yoast_var  The Yoast replacevar.
	 *
	 * @return void
	 */
	public function compose_map( $aioseo_var, $yoast_var ) {
		$map = $this->replace_vars_map;

		$map[ $aioseo_var ] = $yoast_var;

		$this->replace_vars_map = $map;
	}

	/**
	 * Transforms AIOSEO replacevars into Yoast replacevars.
	 *
	 * @param string $aioseo_replacevar The AIOSEO replacevar.
	 *
	 * @return string The Yoast replacevar.
	 */
	public function transform( $aioseo_replacevar ) {
		$yoast_replacevar = \str_replace( \array_keys( $this->replace_vars_map ), \array_values( $this->replace_vars_map ), $aioseo_replacevar );

		// Transform the '#custom_field-<custom_field>' tags into '%%cf_<custom_field>%%' ones.
		$yoast_replacevar = \preg_replace_callback(
			'/#custom_field-([a-zA-Z0-9_-]+)/',
			static function ( $cf_matches ) {
				return '%%cf_' . $cf_matches[1] . '%%';
			},
			$yoast_replacevar
		);

		// Transform the '#tax_name-<custom-tax-name>' tags into '%%ct_<custom-tax-name>%%' ones.
		$yoast_replacevar = \preg_replace_callback(
			'/#tax_name-([a-zA-Z0-9_-]+)/',
			static function ( $ct_matches ) {
				return '%%ct_' . $ct_matches[1] . '%%';
			},
			$yoast_replacevar
		);

		return $yoast_replacevar;
	}
}
aioseo-robots-transformer-service.php000066600000003326151117035130014045 0ustar00<?php

// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Given it's a very specific case.
namespace Yoast\WP\SEO\Services\Importing\Aioseo;

/**
 * Transforms AISOEO search appearance robot settings.
 */
class Aioseo_Robots_Transformer_Service {

	/**
	 * The robots transfomer service.
	 *
	 * @var Aioseo_Robots_Provider_Service
	 */
	protected $robots_provider;

	/**
	 * Class constructor.
	 *
	 * @param Aioseo_Robots_Provider_Service $robots_provider The robots provider service.
	 */
	public function __construct(
		Aioseo_Robots_Provider_Service $robots_provider
	) {
		$this->robots_provider = $robots_provider;
	}

	/**
	 * Transforms the robot setting, taking into consideration whether they defer to global defaults.
	 *
	 * @param string $setting_name  The name of the robot setting, eg. noindex.
	 * @param bool   $setting_value The value of the robot setting.
	 * @param array  $mapping       The mapping of the setting we're working with.
	 *
	 * @return bool The transformed robot setting.
	 */
	public function transform_robot_setting( $setting_name, $setting_value, $mapping ) {
		$aioseo_settings = \json_decode( \get_option( $mapping['option_name'], '' ), true );

		// Let's check first if it defers to global robot settings.
		if ( empty( $aioseo_settings ) || ! isset( $aioseo_settings['searchAppearance'][ $mapping['type'] ][ $mapping['subtype'] ]['advanced']['robotsMeta']['default'] ) ) {
			return $setting_value;
		}

		$defers_to_defaults = $aioseo_settings['searchAppearance'][ $mapping['type'] ][ $mapping['subtype'] ]['advanced']['robotsMeta']['default'];

		if ( $defers_to_defaults ) {
			return $this->robots_provider->get_global_robot_settings( $setting_name );
		}

		return $setting_value;
	}
}
aioseo-robots-provider-service.php000066600000003254151117035130013335 0ustar00<?php

// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Given it's a very specific case.
namespace Yoast\WP\SEO\Services\Importing\Aioseo;

use Yoast\WP\SEO\Helpers\Aioseo_Helper;

/**
 * Provides AISOEO search appearance robot settings.
 */
class Aioseo_Robots_Provider_Service {

	/**
	 * The AIOSEO helper.
	 *
	 * @var Aioseo_Helper
	 */
	protected $aioseo_helper;

	/**
	 * Class constructor.
	 *
	 * @param Aioseo_Helper $aioseo_helper The AIOSEO helper.
	 */
	public function __construct(
		Aioseo_Helper $aioseo_helper
	) {
		$this->aioseo_helper = $aioseo_helper;
	}

	/**
	 * Retrieves the robot setting set globally in AIOSEO.
	 *
	 * @param string $setting_name The name of the robot setting, eg. noindex.
	 *
	 * @return bool Whether global robot settings enable or not the specific setting.
	 */
	public function get_global_robot_settings( $setting_name ) {
		$aioseo_settings = $this->aioseo_helper->get_global_option();
		if ( empty( $aioseo_settings ) ) {
			return false;
		}

		$global_robot_settings = $aioseo_settings['searchAppearance']['advanced']['globalRobotsMeta'];
		if ( $global_robot_settings['default'] === true ) {
			return false;
		}

		return $global_robot_settings[ $setting_name ];
	}

	/**
	 * Gets the subtype's robot setting from the db.
	 *
	 * @param array $mapping The mapping of the setting we're working with.
	 *
	 * @return bool The robot setting.
	 */
	public function get_subtype_robot_setting( $mapping ) {
		$aioseo_settings = \json_decode( \get_option( $mapping['option_name'], '' ), true );

		return $aioseo_settings['searchAppearance'][ $mapping['type'] ][ $mapping['subtype'] ]['advanced']['robotsMeta'][ $mapping['robot_type'] ];
	}
}
aioseo-social-images-provider-service.php000066600000010653151117035130014543 0ustar00<?php

// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Given it's a very specific case.
namespace Yoast\WP\SEO\Services\Importing\Aioseo;

use Yoast\WP\SEO\Helpers\Aioseo_Helper;
use Yoast\WP\SEO\Helpers\Image_Helper;

/**
 * Provides AISOEO social images urls.
 */
class Aioseo_Social_Images_Provider_Service {

	/**
	 * The AIOSEO helper.
	 *
	 * @var Aioseo_Helper
	 */
	protected $aioseo_helper;

	/**
	 * The image helper.
	 *
	 * @var Image_Helper
	 */
	protected $image;

	/**
	 * Class constructor.
	 *
	 * @param Aioseo_Helper $aioseo_helper The AIOSEO helper.
	 * @param Image_Helper  $image         The image helper.
	 */
	public function __construct(
		Aioseo_Helper $aioseo_helper,
		Image_Helper $image
	) {
		$this->aioseo_helper = $aioseo_helper;
		$this->image         = $image;
	}

	/**
	 * Retrieves the default source of social images.
	 *
	 * @param string $social_setting The social settings we're working with, eg. open-graph or twitter.
	 *
	 * @return string The default source of social images.
	 */
	public function get_default_social_image_source( $social_setting ) {
		return $this->get_social_defaults( 'source', $social_setting );
	}

	/**
	 * Retrieves the default custom social image if there is any.
	 *
	 * @param string $social_setting The social settings we're working with, eg. open-graph or twitter.
	 *
	 * @return string The global default social image.
	 */
	public function get_default_custom_social_image( $social_setting ) {
		return $this->get_social_defaults( 'custom_image', $social_setting );
	}

	/**
	 * Retrieves social defaults, be it Default Post Image Source or Default Post Image.
	 *
	 * @param string $setting        The setting we want, eg. source or custom image.
	 * @param string $social_setting The social settings we're working with, eg. open-graph or twitter.
	 *
	 * @return string The social default.
	 */
	public function get_social_defaults( $setting, $social_setting ) {
		switch ( $setting ) {
			case 'source':
				$setting_key = 'defaultImageSourcePosts';
				break;
			case 'custom_image':
				$setting_key = 'defaultImagePosts';
				break;
			default:
				return '';
		}

		$aioseo_settings = $this->aioseo_helper->get_global_option();

		if ( $social_setting === 'og' ) {
			$social_setting = 'facebook';
		}

		if ( ! isset( $aioseo_settings['social'][ $social_setting ]['general'][ $setting_key ] ) ) {
			return '';
		}

		return $aioseo_settings['social'][ $social_setting ]['general'][ $setting_key ];
	}

	/**
	 * Retrieves the url of the first image in content.
	 *
	 * @param int $post_id The post id to extract the image from.
	 *
	 * @return string The url of the first image in content.
	 */
	public function get_first_image_in_content( $post_id ) {
		$image = $this->image->get_gallery_image( $post_id );

		if ( ! $image ) {
			$image = $this->image->get_post_content_image( $post_id );
		}

		return $image;
	}

	/**
	 * Retrieves the url of the first attached image.
	 *
	 * @param int $post_id The post id to extract the image from.
	 *
	 * @return string The url of the first attached image.
	 */
	public function get_first_attached_image( $post_id ) {
		if ( \get_post_type( $post_id ) === 'attachment' ) {
			return $this->image->get_attachment_image_source( $post_id, 'fullsize' );
		}

		$attachments = \get_children(
			[
				'post_parent'    => $post_id,
				'post_status'    => 'inherit',
				'post_type'      => 'attachment',
				'post_mime_type' => 'image',
			]
		);

		if ( $attachments && ! empty( $attachments ) ) {
			return $this->image->get_attachment_image_source( \array_values( $attachments )[0]->ID, 'fullsize' );
		}

		return '';
	}

	/**
	 * Retrieves the url of the featured image.
	 *
	 * @param int $post_id The post id to extract the image from.
	 *
	 * @return string The url of the featured image.
	 */
	public function get_featured_image( $post_id ) {
		$feature_image_id = \get_post_thumbnail_id( $post_id );

		if ( $feature_image_id ) {
			return $this->image->get_attachment_image_source( $feature_image_id, 'fullsize' );
		}

		return '';
	}

	/**
	 * Retrieves the url of the first available image. Tries each image source to get one image.
	 *
	 * @param int $post_id The post id to extract the image from.
	 *
	 * @return string The url of the featured image.
	 */
	public function get_auto_image( $post_id ) {
		$image = $this->get_first_attached_image( $post_id );

		if ( ! $image ) {
			$image = $this->get_first_image_in_content( $post_id );
		}

		return $image;
	}
}
aioseo-validate-data-action.php000066600000021131151130630730012506 0ustar00<?php

// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Given it's a very specific case.
namespace Yoast\WP\SEO\Actions\Importing\Aioseo;

use wpdb;
use Yoast\WP\SEO\Actions\Importing\Abstract_Aioseo_Importing_Action;
use Yoast\WP\SEO\Exceptions\Importing\Aioseo_Validation_Exception;
use Yoast\WP\SEO\Helpers\Options_Helper;

/**
 * Importing action for validating AIOSEO data before the import occurs.
 */
class Aioseo_Validate_Data_Action extends Abstract_Aioseo_Importing_Action {

	/**
	 * The plugin of the action.
	 */
	const PLUGIN = 'aioseo';

	/**
	 * The type of the action.
	 */
	const TYPE = 'validate_data';

	/**
	 * The WordPress database instance.
	 *
	 * @var wpdb
	 */
	protected $wpdb;

	/**
	 * The Post Importing action.
	 *
	 * @var Aioseo_Posts_Importing_Action
	 */
	protected $post_importing_action;

	/**
	 * The settings importing actions.
	 *
	 * @var array
	 */
	protected $settings_importing_actions;

	/**
	 * Class constructor.
	 *
	 * @param wpdb                                               $wpdb                              The WordPress database instance.
	 * @param Options_Helper                                     $options                           The options helper.
	 * @param Aioseo_Custom_Archive_Settings_Importing_Action    $custom_archive_action             The Custom Archive Settings importing action.
	 * @param Aioseo_Default_Archive_Settings_Importing_Action   $default_archive_action            The Default Archive Settings importing action.
	 * @param Aioseo_General_Settings_Importing_Action           $general_settings_action           The General Settings importing action.
	 * @param Aioseo_Posttype_Defaults_Settings_Importing_Action $posttype_defaults_settings_action The Posttype Defaults Settings importing action.
	 * @param Aioseo_Taxonomy_Settings_Importing_Action          $taxonomy_settings_action          The Taxonomy Settings importing action.
	 * @param Aioseo_Posts_Importing_Action                      $post_importing_action             The Post importing action.
	 */
	public function __construct(
		wpdb $wpdb,
		Options_Helper $options,
		Aioseo_Custom_Archive_Settings_Importing_Action $custom_archive_action,
		Aioseo_Default_Archive_Settings_Importing_Action $default_archive_action,
		Aioseo_General_Settings_Importing_Action $general_settings_action,
		Aioseo_Posttype_Defaults_Settings_Importing_Action $posttype_defaults_settings_action,
		Aioseo_Taxonomy_Settings_Importing_Action $taxonomy_settings_action,
		Aioseo_Posts_Importing_Action $post_importing_action
	) {
		$this->wpdb                       = $wpdb;
		$this->options                    = $options;
		$this->post_importing_action      = $post_importing_action;
		$this->settings_importing_actions = [
			$custom_archive_action,
			$default_archive_action,
			$general_settings_action,
			$posttype_defaults_settings_action,
			$taxonomy_settings_action,
		];
	}

	/**
	 * Just checks if the action has been completed in the past.
	 *
	 * @return int 1 if it hasn't been completed in the past, 0 if it has.
	 */
	public function get_total_unindexed() {
		return ( ! $this->get_completed() ) ? 1 : 0;
	}

	/**
	 * Just checks if the action has been completed in the past.
	 *
	 * @param int $limit The maximum number of unimported objects to be returned. Not used, exists to comply with the interface.
	 *
	 * @return int 1 if it hasn't been completed in the past, 0 if it has.
	 */
	public function get_limited_unindexed_count( $limit ) {
		return ( ! $this->get_completed() ) ? 1 : 0;
	}

	/**
	 * Validates AIOSEO data.
	 *
	 * @return array An array of validated data or false if aioseo data did not pass validation.
	 *
	 * @throws Aioseo_Validation_Exception If the validation fails.
	 */
	public function index() {
		if ( $this->get_completed() ) {
			return [];
		}

		$validated_aioseo_table    = $this->validate_aioseo_table();
		$validated_aioseo_settings = $this->validate_aioseo_settings();
		$validated_robot_settings  = $this->validate_robot_settings();


		if ( $validated_aioseo_table === false || $validated_aioseo_settings === false || $validated_robot_settings === false ) {
			throw new Aioseo_Validation_Exception();
		}

		$this->set_completed( true );

		return [
			'validated_aioseo_table'    => $validated_aioseo_table,
			'validated_aioseo_settings' => $validated_aioseo_settings,
			'validated_robot_settings'  => $validated_robot_settings,
		];
	}

	/**
	 * Validates the AIOSEO indexable table.
	 *
	 * @return bool Whether the AIOSEO table exists and has the structure we expect.
	 */
	public function validate_aioseo_table() {
		if ( ! $this->aioseo_helper->aioseo_exists() ) {
			return false;
		}

		$table       = $this->aioseo_helper->get_table();
		$needed_data = $this->post_importing_action->get_needed_data();

		$aioseo_columns = $this->wpdb->get_col(
			"SHOW COLUMNS FROM {$table}", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reason: There is no unescaped user input.
			0
		);

		return $needed_data === \array_intersect( $needed_data, $aioseo_columns );
	}

	/**
	 * Validates the AIOSEO settings from the options table.
	 *
	 * @return bool Whether the AIOSEO settings from the options table exist and have the structure we expect.
	 */
	public function validate_aioseo_settings() {
		foreach ( $this->settings_importing_actions as $settings_import_action ) {
			$aioseo_settings = \json_decode( \get_option( $settings_import_action->get_source_option_name(), '' ), true );

			if ( ! $settings_import_action->isset_settings_tab( $aioseo_settings ) ) {
				return false;
			}
		}

		return true;
	}

	/**
	 * Validates the AIOSEO robots settings from the options table.
	 *
	 * @return bool Whether the AIOSEO robots settings from the options table exist and have the structure we expect.
	 */
	public function validate_robot_settings() {
		if ( $this->validate_post_robot_settings() && $this->validate_default_robot_settings() ) {
			return true;
		}

		return false;
	}

	/**
	 * Validates the post AIOSEO robots settings from the options table.
	 *
	 * @return bool Whether the post AIOSEO robots settings from the options table exist and have the structure we expect.
	 */
	public function validate_post_robot_settings() {
		$post_robot_mapping = $this->post_importing_action->enhance_mapping();
		// We're gonna validate against posttype robot settings only for posts, assuming the robot settings stay the same for other post types.
		$post_robot_mapping['subtype'] = 'post';

		// Let's get both the aioseo_options and the aioseo_options_dynamic options.
		$aioseo_global_settings = $this->aioseo_helper->get_global_option();
		$aioseo_posts_settings  = \json_decode( \get_option( $post_robot_mapping['option_name'], '' ), true );

		$needed_robots_data = $this->post_importing_action->get_needed_robot_data();
		\array_push( $needed_robots_data, 'default', 'noindex' );

		foreach ( $needed_robots_data as $robot_setting ) {
			// Validate against global settings.
			if ( ! isset( $aioseo_global_settings['searchAppearance']['advanced']['globalRobotsMeta'][ $robot_setting ] ) ) {
				return false;
			}

			// Validate against posttype settings.
			if ( ! isset( $aioseo_posts_settings['searchAppearance'][ $post_robot_mapping['type'] ][ $post_robot_mapping['subtype'] ]['advanced']['robotsMeta'][ $robot_setting ] ) ) {
				return false;
			}
		}

		return true;
	}

	/**
	 * Validates the default AIOSEO robots settings for search appearance settings from the options table.
	 *
	 * @return bool Whether the AIOSEO robots settings for search appearance settings from the options table exist and have the structure we expect.
	 */
	public function validate_default_robot_settings() {

		foreach ( $this->settings_importing_actions as $settings_import_action ) {
			$robot_setting_map = $settings_import_action->pluck_robot_setting_from_mapping();

			// Some actions return empty robot settings, let's not validate against those.
			if ( ! empty( $robot_setting_map ) ) {
				$aioseo_settings = \json_decode( \get_option( $robot_setting_map['option_name'], '' ), true );

				if ( ! isset( $aioseo_settings['searchAppearance'][ $robot_setting_map['type'] ][ $robot_setting_map['subtype'] ]['advanced']['robotsMeta']['default'] ) ) {
					return false;
				}
			}
		}

		return true;
	}

	/**
	 * Used nowhere. Exists to comply with the interface.
	 *
	 * @return int The limit.
	 */
	public function get_limit() {
		/**
		 * Filter 'wpseo_aioseo_cleanup_limit' - Allow filtering the number of validations during each action pass.
		 *
		 * @api int The maximum number of validations.
		 */
		$limit = \apply_filters( 'wpseo_aioseo_validation_limit', 25 );

		if ( ! \is_int( $limit ) || $limit < 1 ) {
			$limit = 25;
		}

		return $limit;
	}
}
aioseo-posttype-defaults-settings-importing-action.php000066600000005513151130630730017354 0ustar00<?php

// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Given it's a very specific case.
namespace Yoast\WP\SEO\Actions\Importing\Aioseo;

/**
 * Importing action for AIOSEO posttype defaults settings data.
 *
 * @phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded
 */
class Aioseo_Posttype_Defaults_Settings_Importing_Action extends Abstract_Aioseo_Settings_Importing_Action {

	/**
	 * The plugin of the action.
	 */
	const PLUGIN = 'aioseo';

	/**
	 * The type of the action.
	 */
	const TYPE = 'posttype_default_settings';

	/**
	 * The option_name of the AIOSEO option that contains the settings.
	 */
	const SOURCE_OPTION_NAME = 'aioseo_options_dynamic';

	/**
	 * The map of aioseo_options to yoast settings.
	 *
	 * @var array
	 */
	protected $aioseo_options_to_yoast_map = [];

	/**
	 * The tab of the aioseo settings we're working with.
	 *
	 * @var string
	 */
	protected $settings_tab = 'postTypes';

	/**
	 * Builds the mapping that ties AOISEO option keys with Yoast ones and their data transformation method.
	 *
	 * @return void
	 */
	protected function build_mapping() {
		$post_type_objects = \get_post_types( [ 'public' => true ], 'objects' );

		foreach ( $post_type_objects as $pt ) {
			// Use all the custom post types that are public.
			$this->aioseo_options_to_yoast_map[ '/' . $pt->name . '/title' ]                       = [
				'yoast_name'       => 'title-' . $pt->name,
				'transform_method' => 'simple_import',
			];
			$this->aioseo_options_to_yoast_map[ '/' . $pt->name . '/metaDescription' ]             = [
				'yoast_name'       => 'metadesc-' . $pt->name,
				'transform_method' => 'simple_import',
			];
			$this->aioseo_options_to_yoast_map[ '/' . $pt->name . '/advanced/showMetaBox' ]        = [
				'yoast_name'       => 'display-metabox-pt-' . $pt->name,
				'transform_method' => 'simple_boolean_import',
			];
			$this->aioseo_options_to_yoast_map[ '/' . $pt->name . '/advanced/robotsMeta/noindex' ] = [
				'yoast_name'       => 'noindex-' . $pt->name,
				'transform_method' => 'import_noindex',
				'type'             => 'postTypes',
				'subtype'          => $pt->name,
				'option_name'      => 'aioseo_options_dynamic',
			];

			if ( $pt->name === 'attachment' ) {
				$this->aioseo_options_to_yoast_map['/attachment/redirectAttachmentUrls'] = [
					'yoast_name'       => 'disable-attachment',
					'transform_method' => 'import_redirect_attachment',
				];
			}
		}
	}

	/**
	 * Transforms the redirect_attachment setting.
	 *
	 * @param string $redirect_attachment The redirect_attachment setting.
	 *
	 * @return bool The transformed redirect_attachment setting.
	 */
	public function import_redirect_attachment( $redirect_attachment ) {
		switch ( $redirect_attachment ) {
			case 'disabled':
				return false;

			case 'attachment':
			case 'attachment_parent':
			default:
				return true;
		}
	}
}
aioseo-taxonomy-settings-importing-action.php000066600000007620151130630730015537 0ustar00<?php

// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Given it's a very specific case.
namespace Yoast\WP\SEO\Actions\Importing\Aioseo;

/**
 * Importing action for AIOSEO taxonomies settings data.
 */
class Aioseo_Taxonomy_Settings_Importing_Action extends Abstract_Aioseo_Settings_Importing_Action {

	/**
	 * The plugin of the action.
	 */
	const PLUGIN = 'aioseo';

	/**
	 * The type of the action.
	 */
	const TYPE = 'taxonomy_settings';

	/**
	 * The option_name of the AIOSEO option that contains the settings.
	 */
	const SOURCE_OPTION_NAME = 'aioseo_options_dynamic';

	/**
	 * The map of aioseo_options to yoast settings.
	 *
	 * @var array
	 */
	protected $aioseo_options_to_yoast_map = [];

	/**
	 * The tab of the aioseo settings we're working with.
	 *
	 * @var string
	 */
	protected $settings_tab = 'taxonomies';

	/**
	 * Additional mapping between AiOSEO replace vars and Yoast replace vars.
	 *
	 * @var array
	 *
	 * @see https://yoast.com/help/list-available-snippet-variables-yoast-seo/
	 */
	protected $replace_vars_edited_map = [
		'#breadcrumb_404_error_format'         => '', // Empty string, as AIOSEO shows nothing for that tag.
		'#breadcrumb_archive_post_type_format' => '', // Empty string, as AIOSEO shows nothing for that tag.
		'#breadcrumb_archive_post_type_name'   => '', // Empty string, as AIOSEO shows nothing for that tag.
		'#breadcrumb_author_display_name'      => '', // Empty string, as AIOSEO shows nothing for that tag.
		'#breadcrumb_author_first_name'        => '', // Empty string, as AIOSEO shows nothing for that tag.
		'#breadcrumb_blog_page_title'          => '', // Empty string, as AIOSEO shows nothing for that tag.
		'#breadcrumb_label'                    => '', // Empty string, as AIOSEO shows nothing for that tag.
		'#breadcrumb_link'                     => '', // Empty string, as AIOSEO shows nothing for that tag.
		'#breadcrumb_search_result_format'     => '', // Empty string, as AIOSEO shows nothing for that tag.
		'#breadcrumb_search_string'            => '', // Empty string, as AIOSEO shows nothing for that tag.
		'#breadcrumb_separator'                => '', // Empty string, as AIOSEO shows nothing for that tag.
		'#breadcrumb_taxonomy_title'           => '', // Empty string, as AIOSEO shows nothing for that tag.
		'#taxonomy_title'                      => '%%term_title%%',
	];

	/**
	 * Builds the mapping that ties AOISEO option keys with Yoast ones and their data transformation method.
	 *
	 * @return void
	 */
	protected function build_mapping() {
		$taxonomy_objects = \get_taxonomies( [ 'public' => true ], 'object' );

		foreach ( $taxonomy_objects as $tax ) {
			// Use all the public taxonomies.
			$this->aioseo_options_to_yoast_map[ '/' . $tax->name . '/title' ]                       = [
				'yoast_name'       => 'title-tax-' . $tax->name,
				'transform_method' => 'simple_import',
			];
			$this->aioseo_options_to_yoast_map[ '/' . $tax->name . '/metaDescription' ]             = [
				'yoast_name'       => 'metadesc-tax-' . $tax->name,
				'transform_method' => 'simple_import',
			];
			$this->aioseo_options_to_yoast_map[ '/' . $tax->name . '/advanced/robotsMeta/noindex' ] = [
				'yoast_name'       => 'noindex-tax-' . $tax->name,
				'transform_method' => 'import_noindex',
				'type'             => 'taxonomies',
				'subtype'          => $tax->name,
				'option_name'      => 'aioseo_options_dynamic',
			];
		}
	}

	/**
	 * Returns a setting map of the robot setting for post category taxonomies.
	 *
	 * @return array The setting map of the robot setting for post category taxonomies.
	 */
	public function pluck_robot_setting_from_mapping() {
		$this->build_mapping();

		foreach ( $this->aioseo_options_to_yoast_map as $setting ) {
			// Return the first archive setting map.
			if ( $setting['transform_method'] === 'import_noindex' && isset( $setting['subtype'] ) && $setting['subtype'] === 'category' ) {
				return $setting;
			}
		}

		return [];
	}
}
aioseo-default-archive-settings-importing-action.php000066600000005563151130630730016730 0ustar00<?php

// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Given it's a very specific case.
namespace Yoast\WP\SEO\Actions\Importing\Aioseo;

/**
 * Importing action for AIOSEO default archive settings data.
 *
 * @phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded
 */
class Aioseo_Default_Archive_Settings_Importing_Action extends Abstract_Aioseo_Settings_Importing_Action {

	/**
	 * The plugin of the action.
	 */
	const PLUGIN = 'aioseo';

	/**
	 * The type of the action.
	 */
	const TYPE = 'default_archive_settings';

	/**
	 * The option_name of the AIOSEO option that contains the settings.
	 */
	const SOURCE_OPTION_NAME = 'aioseo_options';

	/**
	 * The map of aioseo_options to yoast settings.
	 *
	 * @var array
	 */
	protected $aioseo_options_to_yoast_map = [];

	/**
	 * The tab of the aioseo settings we're working with.
	 *
	 * @var string
	 */
	protected $settings_tab = 'archives';

	/**
	 * Builds the mapping that ties AOISEO option keys with Yoast ones and their data transformation method.
	 *
	 * @return void
	 */
	protected function build_mapping() {
		$this->aioseo_options_to_yoast_map = [
			'/author/title'                       => [
				'yoast_name'       => 'title-author-wpseo',
				'transform_method' => 'simple_import',
			],
			'/author/metaDescription'             => [
				'yoast_name'       => 'metadesc-author-wpseo',
				'transform_method' => 'simple_import',
			],
			'/date/title'                         => [
				'yoast_name'       => 'title-archive-wpseo',
				'transform_method' => 'simple_import',
			],
			'/date/metaDescription'               => [
				'yoast_name'       => 'metadesc-archive-wpseo',
				'transform_method' => 'simple_import',
			],
			'/search/title'                       => [
				'yoast_name'       => 'title-search-wpseo',
				'transform_method' => 'simple_import',
			],
			'/author/advanced/robotsMeta/noindex' => [
				'yoast_name'       => 'noindex-author-wpseo',
				'transform_method' => 'import_noindex',
				'type'             => 'archives',
				'subtype'          => 'author',
				'option_name'      => 'aioseo_options',
			],
			'/date/advanced/robotsMeta/noindex'   => [
				'yoast_name'       => 'noindex-archive-wpseo',
				'transform_method' => 'import_noindex',
				'type'             => 'archives',
				'subtype'          => 'date',
				'option_name'      => 'aioseo_options',
			],
		];
	}

	/**
	 * Returns a setting map of the robot setting for author archives.
	 *
	 * @return array The setting map of the robot setting for author archives.
	 */
	public function pluck_robot_setting_from_mapping() {
		$this->build_mapping();

		foreach ( $this->aioseo_options_to_yoast_map as $setting ) {
			// Return the first archive setting map.
			if ( $setting['transform_method'] === 'import_noindex' && isset( $setting['subtype'] ) && $setting['subtype'] === 'author' ) {
				return $setting;
			}
		}

		return [];
	}
}
aioseo-posts-importing-action.php000066600000054622151130630730013177 0ustar00<?php

// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Given it's a very specific case.
namespace Yoast\WP\SEO\Actions\Importing\Aioseo;

use wpdb;
use Yoast\WP\SEO\Actions\Importing\Abstract_Aioseo_Importing_Action;
use Yoast\WP\SEO\Helpers\Image_Helper;
use Yoast\WP\SEO\Helpers\Import_Cursor_Helper;
use Yoast\WP\SEO\Helpers\Indexable_Helper;
use Yoast\WP\SEO\Helpers\Indexable_To_Postmeta_Helper;
use Yoast\WP\SEO\Helpers\Options_Helper;
use Yoast\WP\SEO\Helpers\Sanitization_Helper;
use Yoast\WP\SEO\Models\Indexable;
use Yoast\WP\SEO\Repositories\Indexable_Repository;
use Yoast\WP\SEO\Services\Importing\Aioseo\Aioseo_Replacevar_Service;
use Yoast\WP\SEO\Services\Importing\Aioseo\Aioseo_Robots_Provider_Service;
use Yoast\WP\SEO\Services\Importing\Aioseo\Aioseo_Robots_Transformer_Service;
use Yoast\WP\SEO\Services\Importing\Aioseo\Aioseo_Social_Images_Provider_Service;

/**
 * Importing action for AIOSEO post data.
 */
class Aioseo_Posts_Importing_Action extends Abstract_Aioseo_Importing_Action {

	/**
	 * The plugin of the action.
	 */
	const PLUGIN = 'aioseo';

	/**
	 * The type of the action.
	 */
	const TYPE = 'posts';

	/**
	 * The map of aioseo to yoast meta.
	 *
	 * @var array
	 */
	protected $aioseo_to_yoast_map = [
		'title'               => [
			'yoast_name'       => 'title',
			'transform_method' => 'simple_import_post',
		],
		'description'         => [
			'yoast_name'       => 'description',
			'transform_method' => 'simple_import_post',
		],
		'og_title'            => [
			'yoast_name'       => 'open_graph_title',
			'transform_method' => 'simple_import_post',
		],
		'og_description'      => [
			'yoast_name'       => 'open_graph_description',
			'transform_method' => 'simple_import_post',
		],
		'twitter_title'       => [
			'yoast_name'       => 'twitter_title',
			'transform_method' => 'simple_import_post',
			'twitter_import'   => true,
		],
		'twitter_description' => [
			'yoast_name'       => 'twitter_description',
			'transform_method' => 'simple_import_post',
			'twitter_import'   => true,
		],
		'canonical_url'       => [
			'yoast_name'       => 'canonical',
			'transform_method' => 'url_import_post',
		],
		'keyphrases'          => [
			'yoast_name'       => 'primary_focus_keyword',
			'transform_method' => 'keyphrase_import',
		],
		'og_image_url'        => [
			'yoast_name'                   => 'open_graph_image',
			'social_image_import'          => true,
			'social_setting_prefix_aioseo' => 'og_',
			'social_setting_prefix_yoast'  => 'open_graph_',
			'transform_method'             => 'social_image_url_import',
		],
		'twitter_image_url'   => [
			'yoast_name'                   => 'twitter_image',
			'social_image_import'          => true,
			'social_setting_prefix_aioseo' => 'twitter_',
			'social_setting_prefix_yoast'  => 'twitter_',
			'transform_method'             => 'social_image_url_import',
		],
		'robots_noindex'      => [
			'yoast_name'       => 'is_robots_noindex',
			'transform_method' => 'post_robots_noindex_import',
			'robots_import'    => true,
		],
		'robots_nofollow'     => [
			'yoast_name'       => 'is_robots_nofollow',
			'transform_method' => 'post_general_robots_import',
			'robots_import'    => true,
			'robot_type'       => 'nofollow',
		],
		'robots_noarchive'    => [
			'yoast_name'       => 'is_robots_noarchive',
			'transform_method' => 'post_general_robots_import',
			'robots_import'    => true,
			'robot_type'       => 'noarchive',
		],
		'robots_nosnippet'    => [
			'yoast_name'       => 'is_robots_nosnippet',
			'transform_method' => 'post_general_robots_import',
			'robots_import'    => true,
			'robot_type'       => 'nosnippet',
		],
		'robots_noimageindex' => [
			'yoast_name'       => 'is_robots_noimageindex',
			'transform_method' => 'post_general_robots_import',
			'robots_import'    => true,
			'robot_type'       => 'noimageindex',
		],
	];

	/**
	 * Represents the indexables repository.
	 *
	 * @var Indexable_Repository
	 */
	protected $indexable_repository;

	/**
	 * The WordPress database instance.
	 *
	 * @var wpdb
	 */
	protected $wpdb;

	/**
	 * The image helper.
	 *
	 * @var Image_Helper
	 */
	protected $image;

	/**
	 * The indexable_to_postmeta helper.
	 *
	 * @var Indexable_To_Postmeta_Helper
	 */
	protected $indexable_to_postmeta;

	/**
	 * The indexable helper.
	 *
	 * @var Indexable_Helper
	 */
	protected $indexable_helper;

	/**
	 * The social images provider service.
	 *
	 * @var Aioseo_Social_Images_Provider_Service
	 */
	protected $social_images_provider;

	/**
	 * Class constructor.
	 *
	 * @param Indexable_Repository                  $indexable_repository   The indexables repository.
	 * @param wpdb                                  $wpdb                   The WordPress database instance.
	 * @param Import_Cursor_Helper                  $import_cursor          The import cursor helper.
	 * @param Indexable_Helper                      $indexable_helper       The indexable helper.
	 * @param Indexable_To_Postmeta_Helper          $indexable_to_postmeta  The indexable_to_postmeta helper.
	 * @param Options_Helper                        $options                The options helper.
	 * @param Image_Helper                          $image                  The image helper.
	 * @param Sanitization_Helper                   $sanitization           The sanitization helper.
	 * @param Aioseo_Replacevar_Service             $replacevar_handler     The replacevar handler.
	 * @param Aioseo_Robots_Provider_Service        $robots_provider        The robots provider service.
	 * @param Aioseo_Robots_Transformer_Service     $robots_transformer     The robots transfomer service.
	 * @param Aioseo_Social_Images_Provider_Service $social_images_provider The social images provider service.
	 */
	public function __construct(
		Indexable_Repository $indexable_repository,
		wpdb $wpdb,
		Import_Cursor_Helper $import_cursor,
		Indexable_Helper $indexable_helper,
		Indexable_To_Postmeta_Helper $indexable_to_postmeta,
		Options_Helper $options,
		Image_Helper $image,
		Sanitization_Helper $sanitization,
		Aioseo_Replacevar_Service $replacevar_handler,
		Aioseo_Robots_Provider_Service $robots_provider,
		Aioseo_Robots_Transformer_Service $robots_transformer,
		Aioseo_Social_Images_Provider_Service $social_images_provider ) {
		parent::__construct( $import_cursor, $options, $sanitization, $replacevar_handler, $robots_provider, $robots_transformer );

		$this->indexable_repository   = $indexable_repository;
		$this->wpdb                   = $wpdb;
		$this->image                  = $image;
		$this->indexable_helper       = $indexable_helper;
		$this->indexable_to_postmeta  = $indexable_to_postmeta;
		$this->social_images_provider = $social_images_provider;
	}

	// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared -- Reason: They are already prepared.

	/**
	 * Returns the total number of unimported objects.
	 *
	 * @return int The total number of unimported objects.
	 */
	public function get_total_unindexed() {
		if ( ! $this->aioseo_helper->aioseo_exists() ) {
			return 0;
		}

		$limit                = false;
		$just_detect          = true;
		$indexables_to_create = $this->wpdb->get_col( $this->query( $limit, $just_detect ) );

		$number_of_indexables_to_create = \count( $indexables_to_create );
		$completed                      = $number_of_indexables_to_create === 0;
		$this->set_completed( $completed );

		return $number_of_indexables_to_create;
	}

	/**
	 * Returns the limited number of unimported objects.
	 *
	 * @param int $limit The maximum number of unimported objects to be returned.
	 *
	 * @return int|false The limited number of unindexed posts. False if the query fails.
	 */
	public function get_limited_unindexed_count( $limit ) {
		if ( ! $this->aioseo_helper->aioseo_exists() ) {
			return 0;
		}

		$just_detect          = true;
		$indexables_to_create = $this->wpdb->get_col( $this->query( $limit, $just_detect ) );

		$number_of_indexables_to_create = \count( $indexables_to_create );
		$completed                      = $number_of_indexables_to_create === 0;
		$this->set_completed( $completed );

		return $number_of_indexables_to_create;
	}

	/**
	 * Imports AIOSEO meta data and creates the respective Yoast indexables and postmeta.
	 *
	 * @return Indexable[]|false An array of created indexables or false if aioseo data was not found.
	 */
	public function index() {
		if ( ! $this->aioseo_helper->aioseo_exists() ) {
			return false;
		}

		$limit              = $this->get_limit();
		$aioseo_indexables  = $this->wpdb->get_results( $this->query( $limit ), \ARRAY_A );
		$created_indexables = [];

		$completed = \count( $aioseo_indexables ) === 0;
		$this->set_completed( $completed );

		// Let's build the list of fields to check their defaults, to identify whether we're gonna import AIOSEO data in the indexable or not.
		$check_defaults_fields = [];
		foreach ( $this->aioseo_to_yoast_map as $yoast_mapping ) {
			// We don't want to check all the imported fields.
			if ( ! \in_array( $yoast_mapping['yoast_name'], [ 'open_graph_image', 'twitter_image' ], true ) ) {
				$check_defaults_fields[] = $yoast_mapping['yoast_name'];
			}
		}

		$last_indexed_aioseo_id = 0;
		foreach ( $aioseo_indexables as $aioseo_indexable ) {
			$last_indexed_aioseo_id = $aioseo_indexable['id'];

			$indexable = $this->indexable_repository->find_by_id_and_type( $aioseo_indexable['post_id'], 'post' );

			// Let's ensure that the current post id represents something that we want to index (eg. *not* shop_order).
			if ( ! \is_a( $indexable, 'Yoast\WP\SEO\Models\Indexable' ) ) {
				continue;
			}

			if ( $this->indexable_helper->check_if_default_indexable( $indexable, $check_defaults_fields ) ) {
				$indexable = $this->map( $indexable, $aioseo_indexable );
				$indexable->save();

				// To ensure that indexables can be rebuild after a reset, we have to store the data in the postmeta table too.
				$this->indexable_to_postmeta->map_to_postmeta( $indexable );
			}

			$last_indexed_aioseo_id = $aioseo_indexable['id'];

			$created_indexables[] = $indexable;
		}

		$cursor_id = $this->get_cursor_id();
		$this->import_cursor->set_cursor( $cursor_id, $last_indexed_aioseo_id );

		return $created_indexables;
	}

	// phpcs:enable WordPress.DB.PreparedSQL.NotPrepared

	/**
	 * Maps AIOSEO meta data to Yoast meta data.
	 *
	 * @param Indexable $indexable        The Yoast indexable.
	 * @param array     $aioseo_indexable The AIOSEO indexable.
	 *
	 * @return Indexable The created indexables.
	 */
	public function map( $indexable, $aioseo_indexable ) {
		foreach ( $this->aioseo_to_yoast_map as $aioseo_key => $yoast_mapping ) {
			// For robots import.
			if ( isset( $yoast_mapping['robots_import'] ) && $yoast_mapping['robots_import'] ) {
				$yoast_mapping['subtype']                  = $indexable->object_sub_type;
				$indexable->{$yoast_mapping['yoast_name']} = $this->transform_import_data( $yoast_mapping['transform_method'], $aioseo_indexable, $aioseo_key, $yoast_mapping, $indexable );

				continue;
			}

			// For social images, like open graph and twitter image.
			if ( isset( $yoast_mapping['social_image_import'] ) && $yoast_mapping['social_image_import'] ) {
				$image_url = $this->transform_import_data( $yoast_mapping['transform_method'], $aioseo_indexable, $aioseo_key, $yoast_mapping, $indexable );

				// Update the indexable's social image only where there's actually a url to import, so as not to lose the social images that we came up with when we originally built the indexable.
				if ( ! empty( $image_url ) ) {
					$indexable->{$yoast_mapping['yoast_name']} = $image_url;

					$image_source_key             = $yoast_mapping['social_setting_prefix_yoast'] . 'image_source';
					$indexable->$image_source_key = 'imported';

					$image_id_key             = $yoast_mapping['social_setting_prefix_yoast'] . 'image_id';
					$indexable->$image_id_key = $this->image->get_attachment_by_url( $image_url );

					if ( $yoast_mapping['yoast_name'] === 'open_graph_image' ) {
						$indexable->open_graph_image_meta = null;
					}
				}
				continue;
			}

			// For twitter import, take the respective open graph data if the appropriate setting is enabled.
			if ( isset( $yoast_mapping['twitter_import'] ) && $yoast_mapping['twitter_import'] && $aioseo_indexable['twitter_use_og'] ) {
				$aioseo_indexable['twitter_title']       = $aioseo_indexable['og_title'];
				$aioseo_indexable['twitter_description'] = $aioseo_indexable['og_description'];
			}

			if ( ! empty( $aioseo_indexable[ $aioseo_key ] ) ) {
				$indexable->{$yoast_mapping['yoast_name']} = $this->transform_import_data( $yoast_mapping['transform_method'], $aioseo_indexable, $aioseo_key, $yoast_mapping, $indexable );
			}
		}

		return $indexable;
	}

	/**
	 * Transforms the data to be imported.
	 *
	 * @param string    $transform_method The method that is going to be used for transforming the data.
	 * @param array     $aioseo_indexable The data of the AIOSEO indexable data that is being imported.
	 * @param string    $aioseo_key       The name of the specific set of data that is going to be transformed.
	 * @param array     $yoast_mapping    Extra details for the import of the specific data that is going to be transformed.
	 * @param Indexable $indexable        The Yoast indexable that we are going to import the transformed data into.
	 *
	 * @return string|bool|null The transformed data to be imported.
	 */
	protected function transform_import_data( $transform_method, $aioseo_indexable, $aioseo_key, $yoast_mapping, $indexable ) {
		return \call_user_func( [ $this, $transform_method ], $aioseo_indexable, $aioseo_key, $yoast_mapping, $indexable );
	}

	/**
	 * Returns the number of objects that will be imported in a single importing pass.
	 *
	 * @return int The limit.
	 */
	public function get_limit() {
		/**
		 * Filter 'wpseo_aioseo_post_indexation_limit' - Allow filtering the number of posts indexed during each indexing pass.
		 *
		 * @api int The maximum number of posts indexed.
		 */
		$limit = \apply_filters( 'wpseo_aioseo_post_indexation_limit', 25 );

		if ( ! \is_int( $limit ) || $limit < 1 ) {
			$limit = 25;
		}

		return $limit;
	}

	/**
	 * Populates the needed data array based on which columns we use from the AIOSEO indexable table.
	 *
	 * @return array The needed data array that contains all the needed columns.
	 */
	public function get_needed_data() {
		$needed_data = \array_keys( $this->aioseo_to_yoast_map );
		\array_push( $needed_data, 'id', 'post_id', 'robots_default', 'og_image_custom_url', 'og_image_type', 'twitter_image_custom_url', 'twitter_image_type', 'twitter_use_og' );

		return $needed_data;
	}

	/**
	 * Populates the needed robot data array to be used in validating against its structure.
	 *
	 * @return array The needed data array that contains all the needed columns.
	 */
	public function get_needed_robot_data() {
		$needed_robot_data = [];

		foreach ( $this->aioseo_to_yoast_map as $yoast_mapping ) {
			if ( isset( $yoast_mapping['robot_type'] ) ) {
				$needed_robot_data[] = $yoast_mapping['robot_type'];
			}
		}

		return $needed_robot_data;
	}

	/**
	 * Creates a query for gathering AiOSEO data from the database.
	 *
	 * @param int  $limit       The maximum number of unimported objects to be returned.
	 * @param bool $just_detect Whether we want to just detect if there are unimported objects. If false, we want to actually import them too.
	 *
	 * @return string The query to use for importing or counting the number of items to import.
	 */
	public function query( $limit = false, $just_detect = false ) {
		$table = $this->aioseo_helper->get_table();

		$select_statement = 'id';
		if ( ! $just_detect ) {
			// If we want to import too, we need the actual needed data from AIOSEO indexables.
			$needed_data = $this->get_needed_data();

			$select_statement = \implode( ', ', $needed_data );
		}

		$cursor_id = $this->get_cursor_id();
		$cursor    = $this->import_cursor->get_cursor( $cursor_id );

		/**
		 * Filter 'wpseo_aioseo_post_cursor' - Allow filtering the value of the aioseo post import cursor.
		 *
		 * @api int The value of the aioseo post import cursor.
		 */
		$cursor = \apply_filters( 'wpseo_aioseo_post_import_cursor', $cursor );

		$replacements = [ $cursor ];

		$limit_statement = '';
		if ( ! empty( $limit ) ) {
			$replacements[]  = $limit;
			$limit_statement = ' LIMIT %d';
		}

		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reason: There is no unescaped user input.
		return $this->wpdb->prepare(
			"SELECT {$select_statement} FROM {$table} WHERE id > %d ORDER BY id{$limit_statement}",
			$replacements
		);
		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
	}

	/**
	 * Minimally transforms data to be imported.
	 *
	 * @param array  $aioseo_data All of the AIOSEO data to be imported.
	 * @param string $aioseo_key  The AIOSEO key that contains the setting we're working with.
	 *
	 * @return string The transformed meta data.
	 */
	public function simple_import_post( $aioseo_data, $aioseo_key ) {
		return $this->simple_import( $aioseo_data[ $aioseo_key ] );
	}

	/**
	 * Transforms URL to be imported.
	 *
	 * @param array  $aioseo_data All of the AIOSEO data to be imported.
	 * @param string $aioseo_key  The AIOSEO key that contains the setting we're working with.
	 *
	 * @return string The transformed URL.
	 */
	public function url_import_post( $aioseo_data, $aioseo_key ) {
		return $this->url_import( $aioseo_data[ $aioseo_key ] );
	}

	/**
	 * Plucks the keyphrase to be imported from the AIOSEO array of keyphrase meta data.
	 *
	 * @param array  $aioseo_data All of the AIOSEO data to be imported.
	 * @param string $aioseo_key  The AIOSEO key that contains the setting we're working with, aka keyphrases.
	 *
	 * @return string|null The plucked keyphrase.
	 */
	public function keyphrase_import( $aioseo_data, $aioseo_key ) {
		$meta_data = \json_decode( $aioseo_data[ $aioseo_key ], true );
		if ( ! isset( $meta_data['focus']['keyphrase'] ) ) {
			return null;
		}

		return $this->sanitization->sanitize_text_field( $meta_data['focus']['keyphrase'] );
	}

	/**
	 * Imports the post's noindex setting.
	 *
	 * @param bool $aioseo_robots_settings AIOSEO's set of robot settings for the post.
	 *
	 * @return bool|null The value of Yoast's noindex setting for the post.
	 */
	public function post_robots_noindex_import( $aioseo_robots_settings ) {
		// If robot settings defer to default settings, we have null in the is_robots_noindex field.
		if ( $aioseo_robots_settings['robots_default'] ) {
			return null;
		}

		return $aioseo_robots_settings['robots_noindex'];
	}

	/**
	 * Imports the post's robots setting.
	 *
	 * @param bool   $aioseo_robots_settings AIOSEO's set of robot settings for the post.
	 * @param string $aioseo_key             The AIOSEO key that contains the robot setting we're working with.
	 * @param array  $mapping                The mapping of the setting we're working with.
	 *
	 * @return bool|null The value of Yoast's noindex setting for the post.
	 */
	public function post_general_robots_import( $aioseo_robots_settings, $aioseo_key, $mapping ) {
		$mapping = $this->enhance_mapping( $mapping );

		if ( $aioseo_robots_settings['robots_default'] ) {
			// Let's first get the subtype's setting value and then transform it taking into consideration whether it defers to global defaults.
			$subtype_setting = $this->robots_provider->get_subtype_robot_setting( $mapping );
			return $this->robots_transformer->transform_robot_setting( $mapping['robot_type'], $subtype_setting, $mapping );
		}

		return $aioseo_robots_settings[ $aioseo_key ];
	}

	/**
	 * Enhances the mapping of the setting we're working with, with type and the option name, so that we can retrieve the settings for the object we're working with.
	 *
	 * @param array $mapping The mapping of the setting we're working with.
	 *
	 * @return array The enhanced mapping.
	 */
	public function enhance_mapping( $mapping = [] ) {
		$mapping['type']        = 'postTypes';
		$mapping['option_name'] = 'aioseo_options_dynamic';

		return $mapping;
	}

	/**
	 * Imports the og and twitter image url.
	 *
	 * @param bool      $aioseo_social_image_settings AIOSEO's set of social image settings for the post.
	 * @param string    $aioseo_key                   The AIOSEO key that contains the robot setting we're working with.
	 * @param array     $mapping                      The mapping of the setting we're working with.
	 * @param Indexable $indexable                    The Yoast indexable we're importing into.
	 *
	 * @return bool|null The url of the social image we're importing, null if there's none.
	 */
	public function social_image_url_import( $aioseo_social_image_settings, $aioseo_key, $mapping, $indexable ) {
		if ( $mapping['social_setting_prefix_aioseo'] === 'twitter_' && $aioseo_social_image_settings['twitter_use_og'] ) {
			$mapping['social_setting_prefix_aioseo'] = 'og_';
		}

		$social_setting = \rtrim( $mapping['social_setting_prefix_aioseo'], '_' );

		$image_type = $aioseo_social_image_settings[ $mapping['social_setting_prefix_aioseo'] . 'image_type' ];

		if ( $image_type === 'default' ) {
			$image_type = $this->social_images_provider->get_default_social_image_source( $social_setting );
		}

		switch ( $image_type ) {
			case 'attach':
				$image_url = $this->social_images_provider->get_first_attached_image( $indexable->object_id );
				break;
			case 'auto':
				if ( $this->social_images_provider->get_featured_image( $indexable->object_id ) ) {
					// If there's a featured image, lets not import it, as our indexable calculation has already set that as active social image. That way we achieve dynamicality.
					return null;
				}
				$image_url = $this->social_images_provider->get_auto_image( $indexable->object_id );
				break;
			case 'content':
				$image_url = $this->social_images_provider->get_first_image_in_content( $indexable->object_id );
				break;
			case 'custom_image':
				$image_url = $aioseo_social_image_settings[ $mapping['social_setting_prefix_aioseo'] . 'image_custom_url' ];
				break;
			case 'featured':
				return null; // Our auto-calculation when the indexable was built/updated has taken care of it, so it's not needed to transfer any data now.
			case 'author':
				return null;
			case 'custom':
				return null;
			case 'default':
				$image_url = $this->social_images_provider->get_default_custom_social_image( $social_setting );
				break;
			default:
				$image_url = $aioseo_social_image_settings[ $mapping['social_setting_prefix_aioseo'] . 'image_url' ];
				break;
		}

		if ( empty( $image_url ) ) {
			$image_url = $this->social_images_provider->get_default_custom_social_image( $social_setting );
		}

		if ( empty( $image_url ) ) {
			return null;
		}

		return $this->sanitization->sanitize_url( $image_url, null );
	}
}
aioseo-custom-archive-settings-importing-action.php000066600000007341151130630730016612 0ustar00<?php

// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Given it's a very specific case.
namespace Yoast\WP\SEO\Actions\Importing\Aioseo;

use Yoast\WP\SEO\Helpers\Import_Cursor_Helper;
use Yoast\WP\SEO\Helpers\Options_Helper;
use Yoast\WP\SEO\Helpers\Post_Type_Helper;
use Yoast\WP\SEO\Helpers\Sanitization_Helper;
use Yoast\WP\SEO\Services\Importing\Aioseo\Aioseo_Replacevar_Service;
use Yoast\WP\SEO\Services\Importing\Aioseo\Aioseo_Robots_Provider_Service;
use Yoast\WP\SEO\Services\Importing\Aioseo\Aioseo_Robots_Transformer_Service;

/**
 * Importing action for AIOSEO custom archive settings data.
 *
 * @phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded
 */
class Aioseo_Custom_Archive_Settings_Importing_Action extends Abstract_Aioseo_Settings_Importing_Action {

	/**
	 * The plugin of the action.
	 */
	const PLUGIN = 'aioseo';

	/**
	 * The type of the action.
	 */
	const TYPE = 'custom_archive_settings';

	/**
	 * The option_name of the AIOSEO option that contains the settings.
	 */
	const SOURCE_OPTION_NAME = 'aioseo_options_dynamic';

	/**
	 * The map of aioseo_options to yoast settings.
	 *
	 * @var array
	 */
	protected $aioseo_options_to_yoast_map = [];

	/**
	 * The tab of the aioseo settings we're working with.
	 *
	 * @var string
	 */
	protected $settings_tab = 'archives';

	/**
	 * The post type helper.
	 *
	 * @var Post_Type_Helper
	 */
	protected $post_type;

	/**
	 * Aioseo_Custom_Archive_Settings_Importing_Action constructor.
	 *
	 * @param Import_Cursor_Helper              $import_cursor      The import cursor helper.
	 * @param Options_Helper                    $options            The options helper.
	 * @param Sanitization_Helper               $sanitization       The sanitization helper.
	 * @param Post_Type_Helper                  $post_type          The post type helper.
	 * @param Aioseo_Replacevar_Service         $replacevar_handler The replacevar handler.
	 * @param Aioseo_Robots_Provider_Service    $robots_provider    The robots provider service.
	 * @param Aioseo_Robots_Transformer_Service $robots_transformer The robots transfomer service.
	 */
	public function __construct(
		Import_Cursor_Helper $import_cursor,
		Options_Helper $options,
		Sanitization_Helper $sanitization,
		Post_Type_Helper $post_type,
		Aioseo_Replacevar_Service $replacevar_handler,
		Aioseo_Robots_Provider_Service $robots_provider,
		Aioseo_Robots_Transformer_Service $robots_transformer
	) {
		parent::__construct( $import_cursor, $options, $sanitization, $replacevar_handler, $robots_provider, $robots_transformer );

		$this->post_type = $post_type;
	}

	/**
	 * Builds the mapping that ties AOISEO option keys with Yoast ones and their data transformation method.
	 *
	 * @return void
	 */
	protected function build_mapping() {
		$post_type_objects = \get_post_types( [ 'public' => true ], 'objects' );

		foreach ( $post_type_objects as $pt ) {
			// Use all the custom post types that have archives.
			if ( ! $pt->_builtin && $this->post_type->has_archive( $pt ) ) {
				$this->aioseo_options_to_yoast_map[ '/' . $pt->name . '/title' ]                       = [
					'yoast_name'       => 'title-ptarchive-' . $pt->name,
					'transform_method' => 'simple_import',
				];
				$this->aioseo_options_to_yoast_map[ '/' . $pt->name . '/metaDescription' ]             = [
					'yoast_name'       => 'metadesc-ptarchive-' . $pt->name,
					'transform_method' => 'simple_import',
				];
				$this->aioseo_options_to_yoast_map[ '/' . $pt->name . '/advanced/robotsMeta/noindex' ] = [
					'yoast_name'       => 'noindex-ptarchive-' . $pt->name,
					'transform_method' => 'import_noindex',
					'type'             => 'archives',
					'subtype'          => $pt->name,
					'option_name'      => 'aioseo_options_dynamic',
				];
			}
		}
	}
}
aioseo-general-settings-importing-action.php000066600000013725151130630730015301 0ustar00<?php

// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Given it's a very specific case.
namespace Yoast\WP\SEO\Actions\Importing\Aioseo;

use Yoast\WP\SEO\Helpers\Image_Helper;
use Yoast\WP\SEO\Helpers\Import_Cursor_Helper;
use Yoast\WP\SEO\Helpers\Options_Helper;
use Yoast\WP\SEO\Helpers\Sanitization_Helper;
use Yoast\WP\SEO\Services\Importing\Aioseo\Aioseo_Replacevar_Service;
use Yoast\WP\SEO\Services\Importing\Aioseo\Aioseo_Robots_Provider_Service;
use Yoast\WP\SEO\Services\Importing\Aioseo\Aioseo_Robots_Transformer_Service;

/**
 * Importing action for AIOSEO general settings.
 */
class Aioseo_General_Settings_Importing_Action extends Abstract_Aioseo_Settings_Importing_Action {

	/**
	 * The plugin of the action.
	 */
	const PLUGIN = 'aioseo';

	/**
	 * The type of the action.
	 */
	const TYPE = 'general_settings';

	/**
	 * The option_name of the AIOSEO option that contains the settings.
	 */
	const SOURCE_OPTION_NAME = 'aioseo_options';

	/**
	 * The map of aioseo_options to yoast settings.
	 *
	 * @var array
	 */
	protected $aioseo_options_to_yoast_map = [];

	/**
	 * The tab of the aioseo settings we're working with.
	 *
	 * @var string
	 */
	protected $settings_tab = 'global';

	/**
	 * The image helper.
	 *
	 * @var Image_Helper
	 */
	protected $image;

	/**
	 * Aioseo_General_Settings_Importing_Action constructor.
	 *
	 * @param Import_Cursor_Helper              $import_cursor      The import cursor helper.
	 * @param Options_Helper                    $options            The options helper.
	 * @param Sanitization_Helper               $sanitization       The sanitization helper.
	 * @param Image_Helper                      $image              The image helper.
	 * @param Aioseo_Replacevar_Service         $replacevar_handler The replacevar handler.
	 * @param Aioseo_Robots_Provider_Service    $robots_provider    The robots provider service.
	 * @param Aioseo_Robots_Transformer_Service $robots_transformer The robots transfomer service.
	 */
	public function __construct(
		Import_Cursor_Helper $import_cursor,
		Options_Helper $options,
		Sanitization_Helper $sanitization,
		Image_Helper $image,
		Aioseo_Replacevar_Service $replacevar_handler,
		Aioseo_Robots_Provider_Service $robots_provider,
		Aioseo_Robots_Transformer_Service $robots_transformer
	) {
		parent::__construct( $import_cursor, $options, $sanitization, $replacevar_handler, $robots_provider, $robots_transformer );

		$this->image = $image;
	}

	/**
	 * Builds the mapping that ties AOISEO option keys with Yoast ones and their data transformation method.
	 *
	 * @return void
	 */
	protected function build_mapping() {
		$this->aioseo_options_to_yoast_map = [
			'/separator'               => [
				'yoast_name'       => 'separator',
				'transform_method' => 'transform_separator',
			],
			'/siteTitle'               => [
				'yoast_name'       => 'title-home-wpseo',
				'transform_method' => 'simple_import',
			],
			'/metaDescription'         => [
				'yoast_name'       => 'metadesc-home-wpseo',
				'transform_method' => 'simple_import',
			],
			'/schema/siteRepresents'   => [
				'yoast_name'       => 'company_or_person',
				'transform_method' => 'transform_site_represents',
			],
			'/schema/person'           => [
				'yoast_name'       => 'company_or_person_user_id',
				'transform_method' => 'simple_import',
			],
			'/schema/organizationName' => [
				'yoast_name'       => 'company_name',
				'transform_method' => 'simple_import',
			],
			'/schema/organizationLogo' => [
				'yoast_name'       => 'company_logo',
				'transform_method' => 'import_company_logo',
			],
			'/schema/personLogo'       => [
				'yoast_name'       => 'person_logo',
				'transform_method' => 'import_person_logo',
			],
		];
	}

	/**
	 * Imports the organization logo while also accounting for the id of the log to be saved in the separate Yoast option.
	 *
	 * @param string $logo_url The company logo url coming from AIOSEO settings.
	 *
	 * @return string The transformed company logo url.
	 */
	public function import_company_logo( $logo_url ) {
		$logo_id = $this->image->get_attachment_by_url( $logo_url );
		$this->options->set( 'company_logo_id', $logo_id );

		$this->options->set( 'company_logo_meta', false );
		$logo_meta = $this->image->get_attachment_meta_from_settings( 'company_logo' );
		$this->options->set( 'company_logo_meta', $logo_meta );

		return $this->url_import( $logo_url );
	}

	/**
	 * Imports the person logo while also accounting for the id of the log to be saved in the separate Yoast option.
	 *
	 * @param string $logo_url The person logo url coming from AIOSEO settings.
	 *
	 * @return string The transformed person logo url.
	 */
	public function import_person_logo( $logo_url ) {
		$logo_id = $this->image->get_attachment_by_url( $logo_url );
		$this->options->set( 'person_logo_id', $logo_id );

		$this->options->set( 'person_logo_meta', false );
		$logo_meta = $this->image->get_attachment_meta_from_settings( 'person_logo' );
		$this->options->set( 'person_logo_meta', $logo_meta );

		return $this->url_import( $logo_url );
	}

	/**
	 * Transforms the site represents setting.
	 *
	 * @param string $site_represents The site represents setting.
	 *
	 * @return string The transformed site represents setting.
	 */
	public function transform_site_represents( $site_represents ) {
		switch ( $site_represents ) {
			case 'person':
				return 'person';

			case 'organization':
			default:
				return 'company';
		}
	}

	/**
	 * Transforms the separator setting.
	 *
	 * @param string $separator The separator setting.
	 *
	 * @return string The transformed separator.
	 */
	public function transform_separator( $separator ) {
		switch ( $separator ) {
			case '&#45;':
				return 'sc-dash';

			case '&ndash;':
				return 'sc-ndash';

			case '&mdash;':
				return 'sc-mdash';

			case '&raquo;':
				return 'sc-raquo';

			case '&laquo;':
				return 'sc-laquo';

			case '&gt;':
				return 'sc-gt';

			case '&bull;':
				return 'sc-bull';

			case '&#124;':
				return 'sc-pipe';

			default:
				return 'sc-dash';
		}
	}
}
abstract-aioseo-settings-importing-action.php000066600000024500151130630730015460 0ustar00<?php

// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Given it's a very specific case.
namespace Yoast\WP\SEO\Actions\Importing\Aioseo;

use Exception;
use Yoast\WP\SEO\Actions\Importing\Abstract_Aioseo_Importing_Action;
use Yoast\WP\SEO\Helpers\Import_Helper;

/**
 * Abstract class for importing AIOSEO settings.
 */
abstract class Abstract_Aioseo_Settings_Importing_Action extends Abstract_Aioseo_Importing_Action {

	/**
	 * The plugin the class deals with.
	 *
	 * @var string
	 */
	const PLUGIN = null;

	/**
	 * The type the class deals with.
	 *
	 * @var string
	 */
	const TYPE = null;

	/**
	 * The option_name of the AIOSEO option that contains the settings.
	 */
	const SOURCE_OPTION_NAME = null;

	/**
	 * The map of aioseo_options to yoast settings.
	 *
	 * @var array
	 */
	protected $aioseo_options_to_yoast_map = [];

	/**
	 * The tab of the aioseo settings we're working with, eg. taxonomies, posttypes.
	 *
	 * @var string
	 */
	protected $settings_tab = '';

	/**
	 * Additional mapping between AiOSEO replace vars and Yoast replace vars.
	 *
	 * @var array
	 *
	 * @see https://yoast.com/help/list-available-snippet-variables-yoast-seo/
	 */
	protected $replace_vars_edited_map = [];

	/**
	 * The import helper.
	 *
	 * @var Import_Helper
	 */
	protected $import_helper;

	/**
	 * Builds the mapping that ties AOISEO option keys with Yoast ones and their data transformation method.
	 *
	 * @return void
	 */
	abstract protected function build_mapping();

	/**
	 * Sets the import helper.
	 *
	 * @required
	 *
	 * @param Import_Helper $import_helper The import helper.
	 */
	public function set_import_helper( Import_Helper $import_helper ) {
		$this->import_helper = $import_helper;
	}

	/**
	 * Retrieves the source option_name.
	 *
	 * @return string The source option_name.
	 *
	 * @throws Exception If the SOURCE_OPTION_NAME constant is not set in the child class.
	 */
	public function get_source_option_name() {
		$source_option_name = static::SOURCE_OPTION_NAME;

		if ( empty( $source_option_name ) ) {
			throw new Exception( 'Importing settings action without explicit source option_name' );
		}

		return $source_option_name;
	}

	/**
	 * Returns the total number of unimported objects.
	 *
	 * @return int The total number of unimported objects.
	 */
	public function get_total_unindexed() {
		return $this->get_unindexed_count();
	}

	/**
	 * Returns the limited number of unimported objects.
	 *
	 * @param int $limit The maximum number of unimported objects to be returned.
	 *
	 * @return int The limited number of unindexed posts.
	 */
	public function get_limited_unindexed_count( $limit ) {
		return $this->get_unindexed_count( $limit );
	}

	/**
	 * Returns the number of unimported objects (limited if limit is applied).
	 *
	 * @param int|null $limit The maximum number of unimported objects to be returned.
	 *
	 * @return int The number of unindexed posts.
	 */
	protected function get_unindexed_count( $limit = null ) {
		if ( ! \is_int( $limit ) || $limit < 1 ) {
			$limit = null;
		}

		$settings_to_create = $this->query( $limit );

		$number_of_settings_to_create = \count( $settings_to_create );
		$completed                    = $number_of_settings_to_create === 0;
		$this->set_completed( $completed );

		return $number_of_settings_to_create;
	}

	/**
	 * Imports AIOSEO settings.
	 *
	 * @return array|false An array of the AIOSEO settings that were imported or false if aioseo data was not found.
	 */
	public function index() {
		$limit            = $this->get_limit();
		$aioseo_settings  = $this->query( $limit );
		$created_settings = [];

		$completed = \count( $aioseo_settings ) === 0;
		$this->set_completed( $completed );

		// Prepare the setting keys mapping.
		$this->build_mapping();

		// Prepare the replacement var mapping.
		foreach ( $this->replace_vars_edited_map as $aioseo_var => $yoast_var ) {
			$this->replacevar_handler->compose_map( $aioseo_var, $yoast_var );
		}

		$last_imported_setting = '';
		try {
			foreach ( $aioseo_settings as $setting => $setting_value ) {
				// Map and import the values of the setting we're working with (eg. post, book-category, etc.) to the respective Yoast option.
				$this->map( $setting_value, $setting );

				// Save the type of the settings that were just imported, so that we can allow chunked imports.
				$last_imported_setting = $setting;

				$created_settings[] = $setting;
			}
		}
		finally {
			$cursor_id = $this->get_cursor_id();
			$this->import_cursor->set_cursor( $cursor_id, $last_imported_setting );
		}

		return $created_settings;
	}

	/**
	 * Checks if the settings tab subsetting is set in the AIOSEO option.
	 *
	 * @param string $aioseo_settings The AIOSEO option.
	 *
	 * @return bool Whether the settings are set.
	 */
	public function isset_settings_tab( $aioseo_settings ) {
		return isset( $aioseo_settings['searchAppearance'][ $this->settings_tab ] );
	}

	/**
	 * Queries the database and retrieves unimported AiOSEO settings (in chunks if a limit is applied).
	 *
	 * @param int|null $limit The maximum number of unimported objects to be returned.
	 *
	 * @return array The (maybe chunked) unimported AiOSEO settings to import.
	 */
	protected function query( $limit = null ) {
		$aioseo_settings = \json_decode( \get_option( $this->get_source_option_name(), '' ), true );

		if ( empty( $aioseo_settings ) ) {
			return [];
		}

		// We specifically want the setttings of the tab we're working with, eg. postTypes, taxonomies, etc.
		$settings_values = $aioseo_settings['searchAppearance'][ $this->settings_tab ];
		if ( ! \is_array( $settings_values ) ) {
			return [];
		}

		$flattened_settings = $this->import_helper->flatten_settings( $settings_values );

		return $this->get_unimported_chunk( $flattened_settings, $limit );
	}

	/**
	 * Retrieves (a chunk of, if limit is applied) the unimported AIOSEO settings.
	 * To apply a chunk, we manipulate the cursor to the keys of the AIOSEO settings.
	 *
	 * @param array $importable_data All of the available AIOSEO settings.
	 * @param int   $limit           The maximum number of unimported objects to be returned.
	 *
	 * @return array The (chunk of, if limit is applied)) unimported AIOSEO settings.
	 */
	protected function get_unimported_chunk( $importable_data, $limit ) {
		\ksort( $importable_data );

		$cursor_id = $this->get_cursor_id();
		$cursor    = $this->import_cursor->get_cursor( $cursor_id, '' );

		/**
		 * Filter 'wpseo_aioseo_<identifier>_import_cursor' - Allow filtering the value of the aioseo settings import cursor.
		 *
		 * @api int The value of the aioseo posttype default settings import cursor.
		 */
		$cursor = \apply_filters( 'wpseo_aioseo_' . $this->get_type() . '_import_cursor', $cursor );

		if ( $cursor === '' ) {
			return \array_slice( $importable_data, 0, $limit, true );
		}

		// Let's find the position of the cursor in the alphabetically sorted importable data, so we can return only the unimported data.
		$keys = \array_flip( \array_keys( $importable_data ) );
		// If the stored cursor now no longer exists in the data, we have no choice but to start over.
		$position = ( isset( $keys[ $cursor ] ) ) ? ( $keys[ $cursor ] + 1 ) : 0;

		return \array_slice( $importable_data, $position, $limit, true );
	}

	/**
	 * Returns the number of objects that will be imported in a single importing pass.
	 *
	 * @return int The limit.
	 */
	public function get_limit() {
		/**
		 * Filter 'wpseo_aioseo_<identifier>_indexation_limit' - Allow filtering the number of settings imported during each importing pass.
		 *
		 * @api int The maximum number of posts indexed.
		 */
		$limit = \apply_filters( 'wpseo_aioseo_' . $this->get_type() . '_indexation_limit', 25 );

		if ( ! \is_int( $limit ) || $limit < 1 ) {
			$limit = 25;
		}

		return $limit;
	}

	/**
	 * Maps/imports AIOSEO settings into the respective Yoast settings.
	 *
	 * @param string|array $setting_value The value of the AIOSEO setting at hand.
	 * @param string       $setting       The setting at hand, eg. post or movie-category, separator etc.
	 *
	 * @return void
	 */
	protected function map( $setting_value, $setting ) {
		$aioseo_options_to_yoast_map = $this->aioseo_options_to_yoast_map;

		if ( isset( $aioseo_options_to_yoast_map[ $setting ] ) ) {
			$this->import_single_setting( $setting, $setting_value, $aioseo_options_to_yoast_map[ $setting ] );
		}
	}

	/**
	 * Imports a single setting in the db after transforming it to adhere to Yoast conventions.
	 *
	 * @param string $setting         The name of the setting.
	 * @param string $setting_value   The values of the setting.
	 * @param array  $setting_mapping The mapping of the setting to Yoast formats.
	 *
	 * @return void
	 */
	protected function import_single_setting( $setting, $setting_value, $setting_mapping ) {
		$yoast_key = $setting_mapping['yoast_name'];

		// Check if we're supposed to save the setting.
		if ( $this->options->get_default( 'wpseo_titles', $yoast_key ) !== null ) {
			// Then, do any needed data transfomation before actually saving the incoming data.
			$transformed_data = \call_user_func( [ $this, $setting_mapping['transform_method'] ], $setting_value, $setting_mapping );

			$this->options->set( $yoast_key, $transformed_data );
		}
	}

	/**
	 * Minimally transforms boolean data to be imported.
	 *
	 * @param bool $meta_data The boolean meta data to be imported.
	 *
	 * @return bool The transformed boolean meta data.
	 */
	public function simple_boolean_import( $meta_data ) {
		return $meta_data;
	}

	/**
	 * Imports the noindex setting, taking into consideration whether they defer to global defaults.
	 *
	 * @param bool  $noindex The noindex of the type, without taking into consideration whether the type defers to global defaults.
	 * @param array $mapping The mapping of the setting we're working with.
	 *
	 * @return bool The noindex setting.
	 */
	public function import_noindex( $noindex, $mapping ) {
		return $this->robots_transformer->transform_robot_setting( 'noindex', $noindex, $mapping );
	}

	/**
	 * Returns a setting map of the robot setting for one subset of post types/taxonomies/archives.
	 * For custom archives, it returns an empty array because AIOSEO excludes some custom archives from this option structure, eg. WooCommerce's products and we don't want to raise a false alarm.
	 *
	 * @return array The setting map of the robot setting for one subset of post types/taxonomies/archives or an empty array.
	 */
	public function pluck_robot_setting_from_mapping() {
		return [];
	}
}
aioseo-cleanup-action.php000066600000010770151130630730011444 0ustar00<?php

// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Given it's a very specific case.
namespace Yoast\WP\SEO\Actions\Importing\Aioseo;

use wpdb;
use Yoast\WP\SEO\Actions\Importing\Abstract_Aioseo_Importing_Action;
use Yoast\WP\SEO\Helpers\Options_Helper;

/**
 * Importing action for cleaning up AIOSEO data.
 */
class Aioseo_Cleanup_Action extends Abstract_Aioseo_Importing_Action {

	/**
	 * The plugin of the action.
	 */
	const PLUGIN = 'aioseo';

	/**
	 * The type of the action.
	 */
	const TYPE = 'cleanup';

	/**
	 * The AIOSEO meta_keys to be cleaned up.
	 *
	 * @var array
	 */
	protected $aioseo_postmeta_keys = [
		'_aioseo_title',
		'_aioseo_description',
		'_aioseo_og_title',
		'_aioseo_og_description',
		'_aioseo_twitter_title',
		'_aioseo_twitter_description',
	];

	/**
	 * The WordPress database instance.
	 *
	 * @var wpdb
	 */
	protected $wpdb;

	/**
	 * Class constructor.
	 *
	 * @param wpdb           $wpdb    The WordPress database instance.
	 * @param Options_Helper $options The options helper.
	 */
	public function __construct(
		wpdb $wpdb,
		Options_Helper $options
	) {
		$this->wpdb    = $wpdb;
		$this->options = $options;
	}

	/**
	 * Retrieves the postmeta along with the db prefix.
	 *
	 * @return string The postmeta table name along with the db prefix.
	 */
	protected function get_postmeta_table() {
		return $this->wpdb->prefix . 'postmeta';
	}

	/**
	 * Just checks if the cleanup has been completed in the past.
	 *
	 * @return int The total number of unimported objects.
	 */
	public function get_total_unindexed() {
		if ( ! $this->aioseo_helper->aioseo_exists() ) {
			return 0;
		}

		return ( ! $this->get_completed() ) ? 1 : 0;
	}

	/**
	 * Just checks if the cleanup has been completed in the past.
	 *
	 * @param int $limit The maximum number of unimported objects to be returned.
	 *
	 * @return int|false The limited number of unindexed posts. False if the query fails.
	 */
	public function get_limited_unindexed_count( $limit ) {
		if ( ! $this->aioseo_helper->aioseo_exists() ) {
			return 0;
		}

		return ( ! $this->get_completed() ) ? 1 : 0;
	}

	/**
	 * Cleans up AIOSEO data.
	 *
	 * @return Indexable[]|false An array of created indexables or false if aioseo data was not found.
	 */
	public function index() {
		if ( $this->get_completed() ) {
			return [];
		}

		// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared -- Reason: There is no unescaped user input.
		$meta_data                  = $this->wpdb->query( $this->cleanup_postmeta_query() );
		$aioseo_table_truncate_done = $this->wpdb->query( $this->truncate_query() );
		// phpcs:enable WordPress.DB.PreparedSQL.NotPrepared


		if ( $meta_data === false && $aioseo_table_truncate_done === false ) {
			return false;
		}

		$this->set_completed( true );

		return [
			'metadata_cleanup'   => $meta_data,
			'indexables_cleanup' => $aioseo_table_truncate_done,
		];
	}

	/**
	 * Creates a DELETE query string for deleting AIOSEO postmeta data.
	 *
	 * @return string The query to use for importing or counting the number of items to import.
	 */
	public function cleanup_postmeta_query() {
		$table               = $this->get_postmeta_table();
		$meta_keys_to_delete = $this->aioseo_postmeta_keys;

		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reason: There is no unescaped user input.
		return $this->wpdb->prepare(
			"DELETE FROM {$table} WHERE meta_key IN (" . \implode( ', ', \array_fill( 0, \count( $meta_keys_to_delete ), '%s' ) ) . ')',
			$meta_keys_to_delete
		);
		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
	}

	/**
	 * Creates a TRUNCATE query string for emptying the AIOSEO indexable table, if it exists.
	 *
	 * @return string The query to use for importing or counting the number of items to import.
	 */
	public function truncate_query() {
		if ( ! $this->aioseo_helper->aioseo_exists() ) {
			// If the table doesn't exist, we need a string that will amount to a quick query that doesn't return false when ran.
			return 'SELECT 1';
		}

		$table = $this->aioseo_helper->get_table();

		return "TRUNCATE TABLE {$table}";
	}

	/**
	 * Used nowhere. Exists to comply with the interface.
	 *
	 * @return int The limit.
	 */
	public function get_limit() {
		/**
		 * Filter 'wpseo_aioseo_cleanup_limit' - Allow filtering the number of posts indexed during each indexing pass.
		 *
		 * @api int The maximum number of posts cleaned up.
		 */
		$limit = \apply_filters( 'wpseo_aioseo_cleanup_limit', 25 );

		if ( ! \is_int( $limit ) || $limit < 1 ) {
			$limit = 25;
		}

		return $limit;
	}
}
.htaccess000066600000000424151150073440006346 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>