| Current File : /home/abedqjib/krishanfoundation.com/wp-content/plugins/surerank/inc/importers/aioseo/aioseo.php |
<?php
/**
* AIOSEO Importer Class
*
* Handles importing data from All in One SEO plugin.
*
* @package SureRank\Inc\Importers
* @since 1.7.0
*/
namespace SureRank\Inc\Importers\Aioseo;
use Exception;
use SureRank\Inc\API\Onboarding;
use SureRank\Inc\Functions\Settings;
use SureRank\Inc\Importers\BaseImporter;
use SureRank\Inc\Importers\ImporterUtils;
use SureRank\Inc\Traits\Logger;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Implements AIOSEO to SureRank migration.
*
* @since 1.7.0
*/
class Aioseo extends BaseImporter {
use Logger;
/**
* AIOSEO global robots settings.
*
* @since 1.7.0
*
* @var array<string, string>
*/
private array $aioseo_global_robots = [];
/**
* AIOSEO options from database.
*
* @since 1.7.0
*
* @var array<string, mixed>
*/
private array $aioseo_options = [];
/**
* Get the source plugin name.
*
* @since 1.7.0
*
* @return string
*/
public function get_plugin_name(): string {
return Constants::PLUGIN_NAME;
}
/**
* Get the source plugin file.
*
* @since 1.7.0
*
* @return string
*/
public function get_plugin_file(): string {
if ( defined( 'AIOSEO_FILE' ) ) {
return plugin_basename( AIOSEO_FILE );
}
return Constants::PLUGIN_FILE;
}
/**
* Check if AIOSEO plugin is active.
*
* @since 1.7.0
*
* @return bool
*/
public function is_plugin_active(): bool {
return defined( 'AIOSEO_VERSION' ) || function_exists( 'aioseo' );
}
/**
* Detect whether the source plugin has data for the given post.
*
* @since 1.7.0
*
* @param int $post_id Post ID.
* @return array{success: bool, message: string}
*/
public function detect_post( int $post_id ): array {
$tables = Constants::tables_exist();
if ( $tables['posts'] ) {
global $wpdb;
$exists = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->prepare(
"SELECT post_id FROM {$wpdb->prefix}aioseo_posts WHERE post_id = %d",
$post_id
)
);
if ( $exists ) {
return ImporterUtils::build_response(
sprintf(
/* translators: %d: post ID */
__( 'AIOSEO data detected for post %d.', 'surerank' ),
$post_id
),
true
);
}
}
return parent::detect_post( $post_id );
}
/**
* Detect whether the source plugin has data for the given term.
*
* @since 1.7.0
*
* @param int $term_id Term ID.
* @return array{success: bool, message: string}
*/
public function detect_term( int $term_id ): array {
$term = \get_term( $term_id );
if ( ! $term || \is_wp_error( $term ) ) {
return ImporterUtils::build_response(
sprintf(
/* translators: %d: term ID */
__( 'Invalid term ID %d.', 'surerank' ),
$term_id
),
false,
[],
true
);
}
$this->type = $term->taxonomy && in_array( $term->taxonomy, array_keys( $this->taxonomies ), true ) ? $term->taxonomy : '';
$tables = Constants::tables_exist();
if ( $tables['terms'] ) {
global $wpdb;
$exists = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->prepare(
"SELECT term_id FROM {$wpdb->prefix}aioseo_terms WHERE term_id = %d",
$term_id
)
);
if ( $exists ) {
return ImporterUtils::build_response(
sprintf(
/* translators: %d: term ID */
__( 'AIOSEO data detected for term %d.', 'surerank' ),
$term_id
),
true
);
}
}
$meta = get_term_meta( $term_id );
$excluded_keys = $this->get_excluded_meta_keys();
if ( $this->has_source_meta( $meta, $excluded_keys ) ) {
return ImporterUtils::build_response(
sprintf(
/* translators: %d: term ID */
__( 'AIOSEO data detected for term %d.', 'surerank' ),
$term_id
),
true
);
}
ImporterUtils::update_surerank_migrated( $term_id, false );
return ImporterUtils::build_response(
sprintf(
/* translators: %d: term ID */
__( 'No AIOSEO data found for term %d.', 'surerank' ),
$term_id
),
false,
[],
true
);
}
/**
* Import meta-robots settings for a post.
*
* @since 1.7.0
*
* @param int $post_id Post ID.
* @return array{success: bool, message: string}
*/
public function import_post_meta_robots( int $post_id ): array {
return $this->import_post_taxo_robots( $post_id, false );
}
/**
* Import meta-robots settings for a term.
*
* @since 1.7.0
*
* @param int $term_id Term ID.
* @return array{success: bool, message: string}
*/
public function import_term_meta_robots( int $term_id ): array {
return $this->import_post_taxo_robots( $term_id, true );
}
/**
* Import general SEO settings for a post.
*
* @since 1.7.0
*
* @param int $post_id Post ID.
* @return array{success: bool, message: string}
*/
public function import_post_general_settings( int $post_id ): array {
return $this->import_post_taxo_general_settings( $post_id, false );
}
/**
* Import general SEO settings for a term.
*
* @since 1.7.0
*
* @param int $term_id Term ID.
* @return array{success: bool, message: string}
*/
public function import_term_general_settings( int $term_id ): array {
return $this->import_post_taxo_general_settings( $term_id, true );
}
/**
* Import social metadata for a post.
*
* @since 1.7.0
*
* @param int $post_id Post ID.
* @return array{success: bool, message: string}
*/
public function import_post_social( int $post_id ): array {
return $this->import_post_taxo_social( $post_id, false );
}
/**
* Import social metadata for a term.
*
* @since 1.7.0
*
* @param int $term_id Term ID.
* @return array{success: bool, message: string}
*/
public function import_term_social( int $term_id ): array {
return $this->import_post_taxo_social( $term_id, true );
}
/**
* Import global settings from AIOSEO.
*
* @since 1.7.0
*
* @return array{success: bool, message: string}
*/
public function import_global_settings(): array {
$this->aioseo_options = Constants::get_aioseo_options();
if ( empty( $this->aioseo_options ) ) {
return ImporterUtils::build_response(
__( 'No AIOSEO global settings found to import.', 'surerank' ),
false
);
}
$this->surerank_settings = Settings::get();
$this->update_global_robot_settings();
$this->update_robot_settings();
$this->update_homepage_robots();
$this->update_description_and_title();
$this->update_archive_settings();
$this->update_twitter_card_type();
$this->update_social_profiles();
$this->update_sitemap_settings();
$this->update_site_details();
$this->update_webmaster_tools();
try {
ImporterUtils::update_global_settings( $this->surerank_settings );
return ImporterUtils::build_response(
__( 'AIOSEO global settings imported successfully.', 'surerank' ),
true
);
} catch ( Exception $e ) {
self::log(
sprintf(
/* translators: %s: error message */
__( 'Error importing AIOSEO global settings: %s', 'surerank' ),
$e->getMessage()
)
);
return ImporterUtils::build_response( $e->getMessage(), false );
}
}
/**
* Get count and posts for migration.
*
* @since 1.7.0
*
* @param array<string> $post_types Post types to check.
* @param int $batch_size Number of posts per batch.
* @param int $offset Offset for pagination.
* @return array{total_items: int, post_ids: array<int>}
*/
public function get_count_and_posts( $post_types, $batch_size, $offset ) {
global $wpdb;
$tables = Constants::tables_exist();
if ( ! $tables['posts'] ) {
$result = parent::get_count_and_posts( $post_types, $batch_size, $offset );
$result['post_ids'] = array_map( 'intval', $result['post_ids'] );
return $result;
}
$placeholders = implode( ',', array_fill( 0, count( $post_types ), '%s' ) );
$post_types_values = array_values( $post_types );
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
$total_query = $wpdb->prepare(
"SELECT COUNT(DISTINCT ap.post_id)
FROM {$wpdb->prefix}aioseo_posts ap
INNER JOIN {$wpdb->posts} p ON ap.post_id = p.ID
LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = 'surerank_migration'
WHERE p.post_type IN ({$placeholders})
AND p.post_status != 'auto-draft'
AND pm.meta_id IS NULL",
...$post_types_values
);
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
$total_items = (int) $wpdb->get_var( $total_query ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
$ids_query = $wpdb->prepare(
"SELECT DISTINCT ap.post_id
FROM {$wpdb->prefix}aioseo_posts ap
INNER JOIN {$wpdb->posts} p ON ap.post_id = p.ID
LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = 'surerank_migration'
WHERE p.post_type IN ({$placeholders})
AND p.post_status != 'auto-draft'
AND pm.meta_id IS NULL
ORDER BY ap.post_id
LIMIT %d OFFSET %d",
...array_merge( $post_types_values, [ $batch_size, $offset ] )
);
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
$post_ids = $wpdb->get_col( $ids_query ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
return [
'total_items' => $total_items,
'post_ids' => array_map( 'intval', $post_ids ),
];
}
/**
* Get count and terms for migration.
*
* @since 1.7.0
*
* @param array<string> $taxonomies Taxonomies to check.
* @param array<string, \WP_Taxonomy> $taxonomies_objects Taxonomy objects.
* @param int $batch_size Number of terms per batch.
* @param int $offset Offset for pagination.
* @return array{total_items: int, term_ids: array<int>}
*/
public function get_count_and_terms( $taxonomies, $taxonomies_objects, $batch_size, $offset ) {
global $wpdb;
$tables = Constants::tables_exist();
if ( ! $tables['terms'] ) {
$parent_result = parent::get_count_and_terms( $taxonomies, $taxonomies_objects, $batch_size, $offset );
return [
'total_items' => (int) ( $parent_result['total_items'] ?? 0 ),
'term_ids' => array_map( 'intval', $parent_result['term_ids'] ?? [] ),
];
}
$taxonomies_values = array_values( $taxonomies );
$placeholders = implode( ',', array_fill( 0, count( $taxonomies_values ), '%s' ) );
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
$total_query = $wpdb->prepare(
"SELECT COUNT(DISTINCT at.term_id)
FROM {$wpdb->prefix}aioseo_terms at
INNER JOIN {$wpdb->term_taxonomy} tt ON at.term_id = tt.term_id
LEFT JOIN {$wpdb->termmeta} tm ON at.term_id = tm.term_id AND tm.meta_key = 'surerank_migration'
WHERE tt.taxonomy IN ({$placeholders})
AND tm.meta_id IS NULL",
...$taxonomies_values
);
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
$total_items = (int) $wpdb->get_var( $total_query ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
$ids_query = $wpdb->prepare(
"SELECT DISTINCT at.term_id
FROM {$wpdb->prefix}aioseo_terms at
INNER JOIN {$wpdb->term_taxonomy} tt ON at.term_id = tt.term_id
LEFT JOIN {$wpdb->termmeta} tm ON at.term_id = tm.term_id AND tm.meta_key = 'surerank_migration'
WHERE tt.taxonomy IN ({$placeholders})
AND tm.meta_id IS NULL
ORDER BY at.term_id
LIMIT %d OFFSET %d",
...array_merge( $taxonomies_values, [ $batch_size, $offset ] )
);
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
$term_ids = $wpdb->get_col( $ids_query ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
return [
'total_items' => $total_items,
'term_ids' => array_map( 'intval', $term_ids ),
];
}
/**
* {@inheritDoc}
*
* @since 1.7.0
*/
protected function get_not_allowed_types(): array {
return Constants::NOT_ALLOWED_TYPES;
}
/**
* Get the source meta data for a post or term.
*
* @since 1.7.0
*
* @param int $id The ID of the post or term.
* @param bool $is_taxonomy Whether it is a taxonomy.
* @param string $type The type of post or term.
* @return array<string, mixed>
*/
protected function get_source_meta_data( int $id, bool $is_taxonomy, string $type = '' ): array {
return Constants::aioseo_meta_data( $id, $is_taxonomy, $type );
}
/**
* Get the meta key prefix for the importer.
*
* @since 1.7.0
*
* @return string
*/
protected function get_meta_key_prefix(): string {
return Constants::META_KEY_PREFIX;
}
/**
* {@inheritDoc}
*
* @since 1.7.0
*/
protected function get_excluded_meta_keys(): array {
return Constants::EXCLUDED_META_KEYS;
}
/**
* Import meta-robots settings for a post or term.
*
* @since 1.7.0
*
* @param int $id Post or Term ID.
* @param bool $is_taxonomy Whether the ID is for a taxonomy term.
* @return array{success: bool, message: string}
*/
private function import_post_taxo_robots( int $id, bool $is_taxonomy = false ): array {
try {
if ( ! empty( $this->source_meta['robots_default'] ) ) {
return ImporterUtils::build_response(
sprintf(
/* translators: 1: type (post/term), 2: ID */
__( 'Using default robots for %1$s %2$d.', 'surerank' ),
$is_taxonomy ? 'term' : 'post',
$id
),
true
);
}
$robot_data = Constants::get_mapped_robots( $this->source_meta );
foreach ( $robot_data as $key => $value ) {
if ( isset( Constants::ROBOTS_MAPPING[ $key ] ) ) {
$this->default_surerank_meta[ Constants::ROBOTS_MAPPING[ $key ] ] = $value;
}
}
return ImporterUtils::build_response(
sprintf(
/* translators: 1: type (post/term), 2: ID */
__( 'Meta-robots imported for %1$s %2$d.', 'surerank' ),
$is_taxonomy ? 'term' : 'post',
$id
),
true
);
} catch ( Exception $e ) {
self::log(
sprintf(
/* translators: 1: ID, 2: type (post/term), 3: error message */
__( 'Error importing meta-robots for %2$s %1$d: %3$s', 'surerank' ),
$id,
$is_taxonomy ? 'term' : 'post',
$e->getMessage()
)
);
return ImporterUtils::build_response( $e->getMessage(), false );
}
}
/**
* Import general SEO settings for a post or term.
*
* @since 1.7.0
*
* @param int $id Post or Term ID.
* @param bool $is_taxonomy Whether the ID is for a taxonomy term.
* @return array{success: bool, message: string}
*/
private function import_post_taxo_general_settings( int $id, bool $is_taxonomy = false ): array {
$page_title_description = Constants::get_page_title_description( $this->type, $is_taxonomy );
$separator = $this->source_meta['separator'] ?? '-';
$imported = false;
if ( ! empty( $this->source_meta['title'] ) ) {
$this->default_surerank_meta['page_title'] = Constants::replace_placeholders(
$this->source_meta['title'],
$separator
);
$imported = true;
} elseif ( ! empty( $page_title_description['page_title'] ) ) {
$this->default_surerank_meta['page_title'] = Constants::replace_placeholders(
$page_title_description['page_title'],
$separator
);
$imported = true;
}
if ( ! empty( $this->source_meta['description'] ) ) {
$this->default_surerank_meta['page_description'] = Constants::replace_placeholders(
$this->source_meta['description'],
$separator
);
$imported = true;
} elseif ( ! empty( $page_title_description['page_description'] ) ) {
$this->default_surerank_meta['page_description'] = Constants::replace_placeholders(
$page_title_description['page_description'],
$separator
);
$imported = true;
}
if ( ! empty( $this->source_meta['canonical_url'] ) ) {
$this->default_surerank_meta['canonical_url'] = esc_url_raw( $this->source_meta['canonical_url'] );
$imported = true;
}
$message = $imported
/* translators: 1: type (post/term), 2: ID */
? __( 'General settings imported for %1$s %2$d.', 'surerank' )
/* translators: 1: type (post/term), 2: ID */
: __( 'No general settings to import for %1$s %2$d.', 'surerank' );
return ImporterUtils::build_response(
sprintf(
$message,
$is_taxonomy ? 'term' : 'post',
$id
),
$imported
);
}
/**
* Import social metadata for a post or term.
*
* @since 1.7.0
*
* @param int $id Post or Term ID.
* @param bool $is_taxonomy Whether the ID is for a taxonomy term.
* @return array{success: bool, message: string}
*/
private function import_post_taxo_social( int $id, bool $is_taxonomy = false ): array {
$twitter_use_og = ! empty( $this->source_meta['twitter_use_og'] );
$this->default_surerank_meta['twitter_same_as_facebook'] = $twitter_use_og;
$imported = false;
$separator = $this->source_meta['separator'] ?? '-';
$social_mapping = Constants::get_social_mapping();
foreach ( $social_mapping as $aioseo_key => $surerank_data ) {
$surerank_key = $surerank_data[1];
if ( ! empty( $this->source_meta[ $aioseo_key ] ) ) {
$value = $this->source_meta[ $aioseo_key ];
if ( strpos( $surerank_key, 'image' ) === false ) {
$value = Constants::replace_placeholders( $value, $separator );
}
$this->default_surerank_meta[ $surerank_key ] = $value;
$imported = true;
}
}
$message = $imported
/* translators: 1: type (post/term), 2: ID */
? __( 'Social metadata imported for %1$s %2$d.', 'surerank' )
/* translators: 1: type (post/term), 2: ID */
: __( 'No social metadata to import for %1$s %2$d.', 'surerank' );
return ImporterUtils::build_response(
sprintf(
$message,
$is_taxonomy ? 'term' : 'post',
$id
),
$imported
);
}
/**
* Update global robot settings.
*
* @since 1.7.0
*
* @return void
*/
private function update_global_robot_settings(): void {
$global_robots = $this->aioseo_options['searchAppearance']['advanced']['globalRobotsMeta'] ?? [];
$this->aioseo_global_robots = Constants::GLOBAL_ROBOTS;
foreach ( [ 'noindex', 'nofollow', 'noarchive' ] as $robot ) {
if ( isset( $global_robots[ $robot ] ) ) {
$this->aioseo_global_robots[ $robot ] = ! empty( $global_robots[ $robot ] ) ? 'yes' : 'no';
}
}
}
/**
* Update robot settings for post types and taxonomies.
*
* @since 1.7.0
*
* @return void
*/
private function update_robot_settings(): void {
// Get dynamic options - AIOSEO stores post type/taxonomy settings in aioseo_options_dynamic.
$dynamic_options = Constants::get_aioseo_dynamic_options();
$search_appearance = $dynamic_options['searchAppearance'] ?? [];
foreach ( $this->post_types as $post_type ) {
$pt_settings = $search_appearance['postTypes'][ $post_type ]['advanced']['robotsMeta'] ?? [];
$this->process_robot_settings_for_type( $pt_settings, $post_type );
}
foreach ( $this->taxonomies as $taxonomy => $object ) {
$tax_settings = $search_appearance['taxonomies'][ $taxonomy ]['advanced']['robotsMeta'] ?? [];
$this->process_robot_settings_for_type( $tax_settings, $taxonomy );
}
$this->migrate_redirect_attachment_setting( $search_appearance );
}
/**
* Migrate redirect attachment pages setting.
*
* AIOSEO stores this at searchAppearance.postTypes.attachment.redirectAttachmentUrls
* Values: 'attachment' (redirect to parent), 'attachment_url' (redirect to file), 'disabled' (no redirect)
*
* @since 1.7.0
*
* @param array<string, mixed> $search_appearance AIOSEO search appearance settings.
* @return void
*/
private function migrate_redirect_attachment_setting( array $search_appearance ): void {
$attachment_settings = $search_appearance['postTypes']['attachment'] ?? [];
$redirect_setting = $attachment_settings['redirectAttachmentUrls'] ?? '';
// SureRank: true = redirect to parent, false = no redirect.
if ( ! empty( $redirect_setting ) ) {
$this->surerank_settings['redirect_attachment_pages_to_post_parent'] = 'disabled' !== $redirect_setting;
}
}
/**
* Process robot settings for a specific type.
*
* @since 1.7.0
*
* @param array<string, mixed> $robot_settings Robot settings from AIOSEO.
* @param string $type Post type or taxonomy name.
* @return void
*/
private function process_robot_settings_for_type( array $robot_settings, string $type ): void {
if ( ! empty( $robot_settings['default'] ) ) {
$robot_rules = $this->aioseo_global_robots;
} else {
$robot_rules = Constants::GLOBAL_ROBOTS;
foreach ( [ 'noindex', 'nofollow', 'noarchive' ] as $robot ) {
if ( isset( $robot_settings[ $robot ] ) ) {
$robot_rules[ $robot ] = ! empty( $robot_settings[ $robot ] ) ? 'yes' : 'no';
}
}
}
$robot_mapping = [
'noindex' => 'no_index',
'nofollow' => 'no_follow',
'noarchive' => 'no_archive',
];
foreach ( $robot_mapping as $aioseo_key => $surerank_key ) {
if ( ! isset( $this->surerank_settings[ $surerank_key ] ) || ! is_array( $this->surerank_settings[ $surerank_key ] ) ) {
$this->surerank_settings[ $surerank_key ] = [];
}
$is_present = in_array( $type, $this->surerank_settings[ $surerank_key ], true );
if ( 'yes' === $robot_rules[ $aioseo_key ] && ! $is_present ) {
$this->surerank_settings[ $surerank_key ][] = $type;
} elseif ( 'no' === $robot_rules[ $aioseo_key ] && $is_present ) {
$this->surerank_settings[ $surerank_key ] = array_values(
array_diff( $this->surerank_settings[ $surerank_key ], [ $type ] )
);
}
}
}
/**
* Update homepage robots.
*
* @since 1.7.0
*
* @return void
*/
private function update_homepage_robots(): void {
$home_robots = $this->aioseo_options['searchAppearance']['advanced']['globalRobotsMeta'] ?? [];
if ( ! isset( $this->surerank_settings['home_page_robots']['general'] ) ) {
$this->surerank_settings['home_page_robots']['general'] = [];
}
foreach ( [ 'noindex', 'nofollow', 'noarchive' ] as $robot ) {
$value = ! empty( $home_robots[ $robot ] ) ? 'yes' : 'no';
$in_array = in_array( $robot, $this->surerank_settings['home_page_robots']['general'], true );
if ( 'yes' === $value && ! $in_array ) {
$this->surerank_settings['home_page_robots']['general'][] = $robot;
} elseif ( 'no' === $value && $in_array ) {
$this->surerank_settings['home_page_robots']['general'] = array_values(
array_diff( $this->surerank_settings['home_page_robots']['general'], [ $robot ] )
);
}
}
}
/**
* Update description and title settings.
*
* @since 1.7.0
*
* @return void
*/
private function update_description_and_title(): void {
$global = $this->aioseo_options['searchAppearance']['global'] ?? [];
$separator = $global['separator'] ?? '-';
if ( ! empty( $global['siteTitle'] ) ) {
$this->surerank_settings['home_page_title'] = Constants::replace_placeholders(
$global['siteTitle'],
$separator
);
}
if ( ! empty( $global['metaDescription'] ) ) {
$this->surerank_settings['home_page_description'] = Constants::replace_placeholders(
$global['metaDescription'],
$separator
);
}
$fb_general = $this->aioseo_options['social']['facebook']['general'] ?? [];
if ( ! empty( $fb_general['defaultImagePosts'] ) ) {
$this->surerank_settings['fallback_image'] = $fb_general['defaultImagePosts'];
}
$fb_home = $this->aioseo_options['social']['facebook']['homePage'] ?? [];
if ( ! empty( $fb_home['title'] ) ) {
$this->surerank_settings['home_page_facebook_title'] = Constants::replace_placeholders(
$fb_home['title'],
$separator
);
}
if ( ! empty( $fb_home['description'] ) ) {
$this->surerank_settings['home_page_facebook_description'] = Constants::replace_placeholders(
$fb_home['description'],
$separator
);
}
if ( ! empty( $fb_home['image'] ) ) {
$this->surerank_settings['home_page_facebook_image_url'] = $fb_home['image'];
}
$twitter_home = $this->aioseo_options['social']['twitter']['homePage'] ?? [];
$twitter_general = $this->aioseo_options['social']['twitter']['general'] ?? [];
// When "Use Data from Facebook Tab" is enabled (useOgData), Twitter homePage
// fields are empty and AIOSEO inherits from Facebook. Fall back accordingly.
$use_og_data = ! empty( $twitter_general['useOgData'] );
$twitter_title = ! empty( $twitter_home['title'] ) ? $twitter_home['title'] : ( $use_og_data ? ( $fb_home['title'] ?? '' ) : '' );
$twitter_desc = ! empty( $twitter_home['description'] ) ? $twitter_home['description'] : ( $use_og_data ? ( $fb_home['description'] ?? '' ) : '' );
$twitter_image = ! empty( $twitter_home['image'] ) ? $twitter_home['image'] : ( $use_og_data ? ( $fb_home['image'] ?? '' ) : '' );
if ( ! empty( $twitter_title ) ) {
$this->surerank_settings['home_page_twitter_title'] = Constants::replace_placeholders(
$twitter_title,
$separator
);
}
if ( ! empty( $twitter_desc ) ) {
$this->surerank_settings['home_page_twitter_description'] = Constants::replace_placeholders(
$twitter_desc,
$separator
);
}
if ( ! empty( $twitter_image ) ) {
$this->surerank_settings['home_page_twitter_image_url'] = $twitter_image;
}
// Get dynamic options (contains post type/taxonomy templates).
// AIOSEO stores these in a separate option called 'aioseo_options_dynamic'.
$dynamic_options = Constants::get_aioseo_dynamic_options();
$search_appearance = $dynamic_options['searchAppearance'] ?? [];
$post_types = $search_appearance['postTypes'] ?? [];
foreach ( $post_types as $post_type => $settings ) {
if ( ! empty( $settings['title'] ) ) {
$this->surerank_settings[ "{$post_type}_page_title" ] = Constants::replace_placeholders(
$settings['title'],
$separator
);
}
if ( ! empty( $settings['metaDescription'] ) ) {
$this->surerank_settings[ "{$post_type}_page_description" ] = Constants::replace_placeholders(
$settings['metaDescription'],
$separator
);
}
}
$taxonomies = $search_appearance['taxonomies'] ?? [];
foreach ( $taxonomies as $taxonomy => $settings ) {
if ( ! empty( $settings['title'] ) ) {
$this->surerank_settings[ "{$taxonomy}_taxonomy_title" ] = Constants::replace_placeholders(
$settings['title'],
$separator
);
}
if ( ! empty( $settings['metaDescription'] ) ) {
$this->surerank_settings[ "{$taxonomy}_taxonomy_description" ] = Constants::replace_placeholders(
$settings['metaDescription'],
$separator
);
}
}
$archives = $this->aioseo_options['searchAppearance']['archives'] ?? [];
if ( ! empty( $archives['author']['title'] ) ) {
$this->surerank_settings['author_archive_title'] = Constants::replace_placeholders(
$archives['author']['title'],
$separator
);
}
if ( ! empty( $archives['author']['metaDescription'] ) ) {
$this->surerank_settings['author_archive_description'] = Constants::replace_placeholders(
$archives['author']['metaDescription'],
$separator
);
}
if ( ! empty( $archives['date']['title'] ) ) {
$this->surerank_settings['date_archive_title'] = Constants::replace_placeholders(
$archives['date']['title'],
$separator
);
}
if ( ! empty( $archives['date']['metaDescription'] ) ) {
$this->surerank_settings['date_archive_description'] = Constants::replace_placeholders(
$archives['date']['metaDescription'],
$separator
);
}
// Import CPT archive title/description from dynamic options.
$cpt_archives = $search_appearance['archives'] ?? [];
foreach ( $cpt_archives as $post_type => $settings ) {
if ( ! empty( $settings['title'] ) ) {
$this->surerank_settings[ "cpt_{$post_type}_archive_title" ] = Constants::replace_placeholders(
$settings['title'],
$separator
);
}
if ( ! empty( $settings['metaDescription'] ) ) {
$this->surerank_settings[ "cpt_{$post_type}_archive_description" ] = Constants::replace_placeholders(
$settings['metaDescription'],
$separator
);
}
}
}
/**
* Update archive settings.
*
* @since 1.7.0
*
* @return void
*/
private function update_archive_settings(): void {
$archives = $this->aioseo_options['searchAppearance']['archives'] ?? [];
if ( isset( $archives['author']['show'] ) ) {
$this->surerank_settings['author_archive'] = $archives['author']['show'] ? 1 : 0;
}
if ( isset( $archives['date']['show'] ) ) {
$this->surerank_settings['date_archive'] = $archives['date']['show'] ? 1 : 0;
}
}
/**
* Update Twitter card type.
*
* @since 1.7.0
*
* @return void
*/
private function update_twitter_card_type(): void {
$twitter_settings = $this->aioseo_options['social']['twitter']['general'] ?? [];
if ( ! empty( $twitter_settings['defaultCardType'] ) ) {
$card_type = $twitter_settings['defaultCardType'];
$this->surerank_settings['twitter_card_type'] = 'summary' === $card_type ? 'summary' : 'summary_large_image';
}
}
/**
* Update social profiles.
*
* @since 1.7.0
*
* @return void
*/
private function update_social_profiles(): void {
$profiles = $this->aioseo_options['social']['profiles'] ?? [];
$urls = $profiles['urls'] ?? [];
// When "Use the same username" is enabled, AIOSEO stores only the username
// and constructs full URLs dynamically. Resolve them here before mapping.
$same_username = $profiles['sameUsername'] ?? [];
if ( ! empty( $same_username['enable'] ) && ! empty( $same_username['username'] ) ) {
$username = $same_username['username'];
$included = $same_username['included'] ?? [];
$base_urls = [
'facebookPageUrl' => 'https://facebook.com/',
'twitterUrl' => 'https://x.com/',
'instagramUrl' => 'https://instagram.com/',
'tiktokUrl' => 'https://tiktok.com/@',
'pinterestUrl' => 'https://pinterest.com/',
'youtubeUrl' => 'https://youtube.com/',
'linkedinUrl' => 'https://linkedin.com/in/',
'yelpPageUrl' => 'https://yelp.com/biz/',
'blueskyUrl' => 'https://bsky.app/profile/',
];
foreach ( $base_urls as $platform_key => $base_url ) {
if ( in_array( $platform_key, $included, true ) ) {
$urls[ $platform_key ] = $base_url . $username;
}
}
}
if ( ! empty( $urls['facebookPageUrl'] ) ) {
$this->surerank_settings['facebook_page_url'] = $urls['facebookPageUrl'];
}
if ( ! empty( $urls['twitterUrl'] ) ) {
$this->surerank_settings['twitter_profile_username'] = $urls['twitterUrl'];
}
// Direct mapping for AIOSEO URL keys → SureRank social_profiles keys.
$social_mapping = [
'instagramUrl' => 'instagram',
'pinterestUrl' => 'pinterest',
'youtubeUrl' => 'youtube',
'linkedinUrl' => 'linkedin',
'tiktokUrl' => 'tiktok',
'yelpPageUrl' => 'yelp',
'blueskyUrl' => 'bluesky',
];
if ( ! isset( $this->surerank_settings['social_profiles'] ) ) {
$this->surerank_settings['social_profiles'] = [];
}
foreach ( $social_mapping as $aioseo_key => $surerank_key ) {
if ( ! empty( $urls[ $aioseo_key ] ) ) {
$this->surerank_settings['social_profiles'][ $surerank_key ] = $urls[ $aioseo_key ];
}
}
// Import additionalUrls via domain-based matching for platforms like
// WhatsApp (wa.me), Telegram (t.me), etc.
if ( ! empty( $profiles['additionalUrls'] ) ) {
$additional = array_filter( array_map( 'trim', explode( "\n", $profiles['additionalUrls'] ) ) );
// Ensure special platform keys exist for domain-based matching.
// get_mapped_social_profiles() only iterates over existing keys,
// so platforms like whatsapp/telegram must be pre-initialized.
$special_platforms = [ 'whatsapp', 'telegram', 'bluesky' ];
foreach ( $special_platforms as $platform ) {
if ( ! isset( $this->surerank_settings['social_profiles'][ $platform ] ) ) {
$this->surerank_settings['social_profiles'][ $platform ] = '';
}
}
$this->surerank_settings['social_profiles'] = ImporterUtils::get_mapped_social_profiles(
$additional,
$this->surerank_settings['social_profiles']
);
}
}
/**
* Update sitemap settings.
*
* @since 1.7.0
*
* @return void
*/
private function update_sitemap_settings(): void {
$sitemap = $this->aioseo_options['sitemap']['general'] ?? [];
if ( isset( $sitemap['enable'] ) ) {
$this->surerank_settings['enable_xml_sitemap'] = $sitemap['enable'] ? 1 : 0;
}
// Migrate image sitemap setting (AIOSEO uses inverted "excludeImages").
$advanced = $sitemap['advancedSettings'] ?? [];
if ( isset( $advanced['excludeImages'] ) ) {
$this->surerank_settings['enable_xml_image_sitemap'] = empty( $advanced['excludeImages'] );
}
// Migrate sitemap post types exclusions.
$this->migrate_sitemap_post_types( $sitemap );
// Migrate sitemap taxonomies exclusions.
$this->migrate_sitemap_taxonomies( $sitemap );
}
/**
* Migrate sitemap post types exclusions.
*
* AIOSEO uses inclusion model (select what to include).
* SureRank uses exclusion model (include all, select what to exclude).
*
* @since 1.7.0
*
* @param array<string, mixed> $sitemap AIOSEO sitemap settings.
* @return void
*/
private function migrate_sitemap_post_types( array $sitemap ): void {
$post_types_settings = $sitemap['postTypes'] ?? [];
if ( ! empty( $post_types_settings['all'] ) ) {
$this->surerank_settings['sitemap_excluded_post_types'] = [];
return;
}
$included = $post_types_settings['included'] ?? [];
if ( empty( $included ) || ! is_array( $included ) ) {
return;
}
$all_public = get_post_types( [ 'public' => true ], 'names' );
$excluded = array_values( array_diff( $all_public, $included ) );
$this->surerank_settings['sitemap_excluded_post_types'] = $excluded;
}
/**
* Migrate sitemap taxonomies exclusions.
*
* AIOSEO uses inclusion model (select what to include).
* SureRank uses exclusion model (include all, select what to exclude).
*
* @since 1.7.0
*
* @param array<string, mixed> $sitemap AIOSEO sitemap settings.
* @return void
*/
private function migrate_sitemap_taxonomies( array $sitemap ): void {
$taxonomies_settings = $sitemap['taxonomies'] ?? [];
// If all taxonomies are included, exclude nothing.
if ( ! empty( $taxonomies_settings['all'] ) ) {
$this->surerank_settings['sitemap_excluded_taxonomies'] = [];
return;
}
$included = $taxonomies_settings['included'] ?? [];
if ( empty( $included ) || ! is_array( $included ) ) {
return;
}
$all_public = get_taxonomies( [ 'public' => true ], 'names' );
$excluded = array_values( array_diff( $all_public, $included ) );
$this->surerank_settings['sitemap_excluded_taxonomies'] = $excluded;
}
/**
* Update site details (organization/person info).
*
* @since 1.7.0
*
* @return void
*/
private function update_site_details(): void {
$schema = $this->aioseo_options['searchAppearance']['global']['schema'] ?? [];
$site_represents = $schema['siteRepresents'] ?? 'organization';
$website_name = $schema['websiteName'] ?? '';
// AIOSEO uses '#site_title' as a placeholder meaning "use the WordPress site title".
// Resolve it to the current blogname so onboarding/schema data keeps a valid website name instead of persisting the literal placeholder or an empty string.
if ( '#site_title' === trim( $website_name ) ) {
$website_name = (string) get_option( 'blogname', '' );
}
$site_data = [
'website_name' => $website_name,
'organization_type' => 'organization' === $site_represents ? 'Organization' : 'Person',
'website_logo' => $schema['organizationLogo'] ?? $schema['personLogo'] ?? '',
'website_type' => $site_represents,
];
Onboarding::update_common_onboarding_data( $site_data );
}
/**
* Update webmaster tools verification codes.
*
* @since 1.7.0
*
* @return void
*/
private function update_webmaster_tools(): void {
$webmaster = $this->aioseo_options['webmasterTools'] ?? [];
$mapping = [
'google' => 'google_verify',
'bing' => 'bing_verify',
'baidu' => 'baidu_verify',
'yandex' => 'yandex_verify',
'pinterest' => 'pinterest_verify',
];
foreach ( $mapping as $aioseo_key => $surerank_key ) {
if ( ! empty( $webmaster[ $aioseo_key ] ) ) {
$this->surerank_settings[ $surerank_key ] = $webmaster[ $aioseo_key ];
}
}
}
}