From ea99620f9614b8d40465d0360d288f3beb6ce43b Mon Sep 17 00:00:00 2001 From: Robert O'Rourke Date: Thu, 8 Jul 2021 17:34:41 +0100 Subject: [PATCH 1/4] Handle migrating from WP SEO to Yoast SEO Fixes #56 Handles the title options settings and post meta. --- inc/namespace.php | 182 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/inc/namespace.php b/inc/namespace.php index 9be152c..948e905 100644 --- a/inc/namespace.php +++ b/inc/namespace.php @@ -9,6 +9,9 @@ use Altis; use Altis\Module; +use WP_CLI; +use WP_Query; +use WP_Site_Query; /** * Bootstrap SEO Module. @@ -67,6 +70,9 @@ function bootstrap( Module $module ) { add_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\\enqueue_yoast_css_overrides', 11 ); add_action( 'wpseo_configuration_wizard_head', __NAMESPACE__ . '\\override_wizard_styles' ); add_action( 'admin_head', __NAMESPACE__ . '\\hide_yoast_premium_social_previews' ); + + // Migrations. + add_action( 'altis.migrate', __NAMESPACE__ . '\\do_migrations' ); } /** @@ -368,3 +374,179 @@ function hide_yoast_premium_social_previews() { echo ""; // phpcs:ignore HM.Security.EscapeOutput.OutputNotEscaped } + +/** + * Run migration functions. + * + * @return void + */ +function do_migrations() : void { + migrate_wpseo_to_yoast(); +} + +/** + * Copy WPSEO settings to Yoast. + * + * Migration for Altis 7 or lower to 8+. + * + * @return void + */ +function migrate_wpseo_to_yoast() : void { + $version = Altis\get_version(); + if ( $version && version_compare( '8', $version, 'lt' ) ) { + return; + } + + // WP SEO to Yoast data mappings. + $title_tag_mapping = [ + '#site_name#' => '%%sitename%%', + '#site_description#' => '%%sitedesc%%', + '#date_published#' => '%%date%%', + '#date_modified#' => '%%modified%%', + '#author#' => '%%name%%', + '#categories#' => '%%category%%', + '#tags#' => '%%tag%%', + '#term_name#' => '%%term_title%%', + '#post_type_singular_name#' => '%%pt_single%%', + '#post_type_plural_name#' => '%%pt_plural%%', + '#archive_date#' => '%%archive_title%%', + '#search_term#' => '%%searchphrase%%', + '#thumbnail_url#' => '', + ]; + + $options_mapping = [ + 'home_title' => 'title-home-wpseo', + 'home_description' => 'metadesc-home-wpseo', + 'archive_author_title' => 'title-author-wpseo', + 'archive_author_description' => 'metadesc-author-wpseo', + 'archive_date_title' => 'title-archive-wpseo', + 'archive_date_description' => 'metadesc-archive-wpseo', + 'search_title' => 'title-search-wpseo', + '404_title' => 'title-404-wpseo', + 'single_post_title' => 'title-post', + 'single_post_description' => 'metadesc-post', + 'single_page_title' => 'title-page', + 'single_page_description' => 'metadesc-page', + 'single_attachment_title' => 'title-attachment', + 'single_attachment_description' => 'metadesc-attachment', + ]; + + $meta_mapping = [ + '_meta_title' => '_yoast_wpseo_title', + '_meta_description' => '_yoast_wpseo_metadesc', + '_meta_keywords' => '_yoast_wpseo_focuskw', + ]; + + // Begin processing. + $sites_count = new WP_Site_Query( [ 'count' => true ] ); + $sites_processed = 0; + $sites_page = 0; + + while ( $sites_processed < $sites_count ) { + + // Get sites collection. + $sites = new WP_Site_Query( [ + 'number' => 100, + 'offset' => $sites_page * 100, + 'fields' => 'ids', + ] ); + + // Set next page. + $sites_page++; + + foreach ( $sites as $site_id ) { + + switch_to_blog( $site_id ); + + // Handle options. + $options = get_option( 'wp-seo' ); + if ( ! empty( $options ) && is_array( $options ) ) { + WP_CLI::log( sprintf( 'Migrating legacy SEO settings for site %d...', $site_id ) ); + + $yoast_options = get_option( 'wpseo_titles' ); + if ( empty( $yoast_options ) || ! is_array( $yoast_options ) ) { + $yoast_options = []; + } + + foreach ( $options as $name => $value ) { + // Ignore if empty. + if ( empty( $value ) ) { + continue; + } + + // Replace variables with Yoast equivalents. + $value = str_replace( + array_keys( $title_tag_mapping ), + array_values( $title_tag_mapping ), + $value + ); + + // Replace remaining hash delimited words with double percents (tag names are the same). + $value = preg_replace( '/(?:#([^#\s])+#)/', '%%$1%%', $value ); + + // Check how to map this value. + if ( isset( $options_mapping[ $name ] ) ) { + $yoast_options[ $options_mapping[ $name ] ] = $value; + } else { + // Dynamic item. + preg_match( '/(single|archive)_([a-z_-])_(title|description)/', $name, $matches ); + $prefix = $matches[2] === 'title' ? 'title' : 'metadesc'; + if ( $matches[0] === 'single' ) { + $yoast_options[ $prefix . '-' . $matches[1] ] = $value; + } else { + if ( taxonomy_exists( $matches[1] ) ) { + $yoast_options[ $prefix . '-tax-' . $matches[1] ] = $value; + } + if ( post_type_exists( $matches[1] ) ) { + $yoast_options[ $prefix . '-ptarchive-' . $matches[1] ] = $value; + } + } + } + } + + // Update Yoast options. + update_option( 'wpseo_titles', $yoast_options ); + } + + // Handle post meta. + $posts_query_args = [ + 'post_type' => 'any', + 'post_status' => 'any', + 'posts_per_page' => 0, + 'meta_key' => '_meta_title', + 'meta_compare' => 'EXISTS', + 'fields' => 'ids', + ]; + $posts = new WP_Query( $posts_query_args ); + + $posts_query_args['posts_per_page'] = 100; + $posts_query_args['paged'] = 1; + + WP_CLI::log( sprintf( 'Migrating legacy SEO metadata for %d posts...', $posts->found_posts ) ); + + while ( $posts_query_args['paged'] < $posts->max_num_pages ) { + + $posts = new WP_Query( $posts_query_args ); + $posts_query_args['paged']++; + + foreach ( $posts->posts as $post_id ) { + foreach ( $meta_mapping as $old => $new ) { + // Copy post meta data over. + $value = get_post_meta( $post_id, $old, true ); + update_post_meta( $post_id, $new, $value ); + // Remove old meta data. + delete_post_meta( $post_id, $old ); + } + } + + // Prevent objecct cache consuming too much memory. + WP_CLI\Utils\wp_clear_object_cache(); + } + + restore_current_blog(); + + // Site processed. + $sites_processed++; + } + } +} From 75bb2469085df12c7fd87c28383202a21264a5f8 Mon Sep 17 00:00:00 2001 From: Robert O'Rourke Date: Fri, 9 Jul 2021 10:31:18 +0100 Subject: [PATCH 2/4] Fix regexes --- inc/namespace.php | 46 ++++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/inc/namespace.php b/inc/namespace.php index 948e905..471eb36 100644 --- a/inc/namespace.php +++ b/inc/namespace.php @@ -392,11 +392,6 @@ function do_migrations() : void { * @return void */ function migrate_wpseo_to_yoast() : void { - $version = Altis\get_version(); - if ( $version && version_compare( '8', $version, 'lt' ) ) { - return; - } - // WP SEO to Yoast data mappings. $title_tag_mapping = [ '#site_name#' => '%%sitename%%', @@ -423,12 +418,6 @@ function migrate_wpseo_to_yoast() : void { 'archive_date_description' => 'metadesc-archive-wpseo', 'search_title' => 'title-search-wpseo', '404_title' => 'title-404-wpseo', - 'single_post_title' => 'title-post', - 'single_post_description' => 'metadesc-post', - 'single_page_title' => 'title-page', - 'single_page_description' => 'metadesc-page', - 'single_attachment_title' => 'title-attachment', - 'single_attachment_description' => 'metadesc-attachment', ]; $meta_mapping = [ @@ -458,11 +447,11 @@ function migrate_wpseo_to_yoast() : void { switch_to_blog( $site_id ); + WP_CLI::log( sprintf( 'Migrating legacy SEO settings for site %d...', $site_id ) ); + // Handle options. $options = get_option( 'wp-seo' ); if ( ! empty( $options ) && is_array( $options ) ) { - WP_CLI::log( sprintf( 'Migrating legacy SEO settings for site %d...', $site_id ) ); - $yoast_options = get_option( 'wpseo_titles' ); if ( empty( $yoast_options ) || ! is_array( $yoast_options ) ) { $yoast_options = []; @@ -482,23 +471,22 @@ function migrate_wpseo_to_yoast() : void { ); // Replace remaining hash delimited words with double percents (tag names are the same). - $value = preg_replace( '/(?:#([^#\s])+#)/', '%%$1%%', $value ); + $value = preg_replace( '/(?:#([^#\s]+)#)/', '%%$1%%', $value ); // Check how to map this value. if ( isset( $options_mapping[ $name ] ) ) { $yoast_options[ $options_mapping[ $name ] ] = $value; - } else { + } elseif ( preg_match( '/^(single|archive)_([a-z_-]+)_(title|description)$/', $name, $matches ) ) { // Dynamic item. - preg_match( '/(single|archive)_([a-z_-])_(title|description)/', $name, $matches ); - $prefix = $matches[2] === 'title' ? 'title' : 'metadesc'; - if ( $matches[0] === 'single' ) { - $yoast_options[ $prefix . '-' . $matches[1] ] = $value; + $prefix = $matches[3] === 'title' ? 'title' : 'metadesc'; + if ( $matches[1] === 'single' ) { + $yoast_options[ $prefix . '-' . $matches[2] ] = $value; } else { - if ( taxonomy_exists( $matches[1] ) ) { - $yoast_options[ $prefix . '-tax-' . $matches[1] ] = $value; + if ( taxonomy_exists( $matches[2] ) ) { + $yoast_options[ $prefix . '-tax-' . $matches[2] ] = $value; } - if ( post_type_exists( $matches[1] ) ) { - $yoast_options[ $prefix . '-ptarchive-' . $matches[1] ] = $value; + if ( post_type_exists( $matches[2] ) ) { + $yoast_options[ $prefix . '-ptarchive-' . $matches[2] ] = $value; } } } @@ -522,7 +510,7 @@ function migrate_wpseo_to_yoast() : void { $posts_query_args['posts_per_page'] = 100; $posts_query_args['paged'] = 1; - WP_CLI::log( sprintf( 'Migrating legacy SEO metadata for %d posts...', $posts->found_posts ) ); + $progress = WP_CLI\Utils\make_progress_bar( 'Copying post meta data', $posts->found_posts ); while ( $posts_query_args['paged'] < $posts->max_num_pages ) { @@ -533,16 +521,22 @@ function migrate_wpseo_to_yoast() : void { foreach ( $meta_mapping as $old => $new ) { // Copy post meta data over. $value = get_post_meta( $post_id, $old, true ); - update_post_meta( $post_id, $new, $value ); + if ( ! empty( $value ) ) { + update_post_meta( $post_id, $new, $value ); + } // Remove old meta data. delete_post_meta( $post_id, $old ); } + + $progress->tick(); } - // Prevent objecct cache consuming too much memory. + // Prevent object cache consuming too much memory. WP_CLI\Utils\wp_clear_object_cache(); } + $progress->finish(); + restore_current_blog(); // Site processed. From 0028793ad3a466bab5fee37ff219fe8d38977c6f Mon Sep 17 00:00:00 2001 From: Robert O'Rourke Date: Fri, 9 Jul 2021 11:51:52 +0100 Subject: [PATCH 3/4] Fix posts loop --- inc/namespace.php | 54 +++++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/inc/namespace.php b/inc/namespace.php index 471eb36..0435969 100644 --- a/inc/namespace.php +++ b/inc/namespace.php @@ -427,14 +427,17 @@ function migrate_wpseo_to_yoast() : void { ]; // Begin processing. - $sites_count = new WP_Site_Query( [ 'count' => true ] ); + $sites_count = get_sites( [ + 'count' => true, + 'number' => -1, + ] ); $sites_processed = 0; $sites_page = 0; while ( $sites_processed < $sites_count ) { // Get sites collection. - $sites = new WP_Site_Query( [ + $sites = get_sites( [ 'number' => 100, 'offset' => $sites_page * 100, 'fields' => 'ids', @@ -494,6 +497,7 @@ function migrate_wpseo_to_yoast() : void { // Update Yoast options. update_option( 'wpseo_titles', $yoast_options ); + delete_option( 'wp-seo' ); } // Handle post meta. @@ -507,36 +511,44 @@ function migrate_wpseo_to_yoast() : void { ]; $posts = new WP_Query( $posts_query_args ); - $posts_query_args['posts_per_page'] = 100; - $posts_query_args['paged'] = 1; + if ( $posts->found_posts > 0 ) { + $posts_query_args['posts_per_page'] = 100; + $posts_query_args['paged'] = 1; - $progress = WP_CLI\Utils\make_progress_bar( 'Copying post meta data', $posts->found_posts ); + $progress = WP_CLI\Utils\make_progress_bar( 'Copying post meta data', $posts->found_posts ); - while ( $posts_query_args['paged'] < $posts->max_num_pages ) { + while ( $posts_query_args['paged'] <= $posts->max_num_pages ) { - $posts = new WP_Query( $posts_query_args ); - $posts_query_args['paged']++; + $posts = new WP_Query( $posts_query_args ); + $posts_query_args['paged']++; - foreach ( $posts->posts as $post_id ) { - foreach ( $meta_mapping as $old => $new ) { - // Copy post meta data over. - $value = get_post_meta( $post_id, $old, true ); - if ( ! empty( $value ) ) { - update_post_meta( $post_id, $new, $value ); + foreach ( $posts->posts as $post_id ) { + foreach ( $meta_mapping as $old => $new ) { + // Copy post meta data over. + $value = get_post_meta( $post_id, $old, true ); + // Handle keywords, Yoast only accepts 1 focus keyword by default so use the 1st. + if ( $old === '_meta_keywords' ) { + $value = explode( ',', $value ); + $value = array_map( 'trim', $value ); + $value = array_shift( $value ); + } + if ( ! empty( $value ) ) { + update_post_meta( $post_id, $new, $value ); + } + // Remove old meta data to prevent reprocessing. + delete_post_meta( $post_id, $old ); } - // Remove old meta data. - delete_post_meta( $post_id, $old ); + + $progress->tick(); } - $progress->tick(); + // Prevent object cache consuming too much memory. + WP_CLI\Utils\wp_clear_object_cache(); } - // Prevent object cache consuming too much memory. - WP_CLI\Utils\wp_clear_object_cache(); + $progress->finish(); } - $progress->finish(); - restore_current_blog(); // Site processed. From 3d7c7c75ccc30e7b0e4b394124694b80be9d6a8c Mon Sep 17 00:00:00 2001 From: Robert O'Rourke Date: Fri, 9 Jul 2021 11:53:28 +0100 Subject: [PATCH 4/4] Remove unused use query --- inc/namespace.php | 1 - 1 file changed, 1 deletion(-) diff --git a/inc/namespace.php b/inc/namespace.php index 0435969..931426a 100644 --- a/inc/namespace.php +++ b/inc/namespace.php @@ -11,7 +11,6 @@ use Altis\Module; use WP_CLI; use WP_Query; -use WP_Site_Query; /** * Bootstrap SEO Module.