← All changes
|
includes/admin/importers/class-rankmath-exporter.php
+89
-11
2.10.0
→
2.14.2
View file →
| @@ -262,10 +262,10 @@ | ||
| 262 | 262 | 'object_id' => $post_id, |
| 263 | 263 | 'object_type' => 'post', |
| 264 | 264 | 'source_plugin' => $this->plugin_slug, |
| 265 | 265 | 'data' => [ |
| 266 | - 'seo_title' => $this->convert_template_variables($meta['rank_math_title'] ?? '', $post_id), | |
| 267 | - 'meta_description' => $this->convert_template_variables($meta['rank_math_description'] ?? '', $post_id), | |
| 266 | + 'seo_title' => $this->convert_post_value($meta['rank_math_title'] ?? '', $post_id), | |
| 267 | + 'meta_description' => $this->convert_post_value($meta['rank_math_description'] ?? '', $post_id), | |
| 268 | 268 | 'focus_keyword' => $primary_keyword, |
| 269 | 269 | // Full keyword list; the migrator dedupes, drops empties and |
| 270 | 270 | // caps at the ThinkRank maximum via Focus_Keywords. |
| 271 | 271 | 'focus_keywords' => $focus_keywords, |
| @@ -277,16 +277,22 @@ | ||
| 277 | 277 | 'nosnippet' => isset($robots_flags['nosnippet']) ? 1 : 0, |
| 278 | 278 | 'max_snippet' => $this->advanced_robot_int($advanced_robots, 'max-snippet'), |
| 279 | 279 | 'max_video_preview' => $this->advanced_robot_int($advanced_robots, 'max-video-preview'), |
| 280 | 280 | 'max_image_preview' => $this->advanced_robot_string($advanced_robots, 'max-image-preview'), |
| 281 | - 'og_title' => $this->convert_template_variables($meta['rank_math_facebook_title'] ?? '', $post_id), | |
| 282 | - 'og_description' => $this->convert_template_variables($meta['rank_math_facebook_description'] ?? '', $post_id), | |
| 281 | + 'og_title' => $this->convert_post_value($meta['rank_math_facebook_title'] ?? '', $post_id), | |
| 282 | + 'og_description' => $this->convert_post_value($meta['rank_math_facebook_description'] ?? '', $post_id), | |
| 283 | 283 | 'og_image' => $meta['rank_math_facebook_image'] ?? '', |
| 284 | - 'twitter_title' => $this->convert_template_variables($meta['rank_math_twitter_title'] ?? '', $post_id), | |
| 285 | - 'twitter_description' => $this->convert_template_variables($meta['rank_math_twitter_description'] ?? '', $post_id), | |
| 284 | + 'twitter_title' => $this->convert_post_value($meta['rank_math_twitter_title'] ?? '', $post_id), | |
| 285 | + 'twitter_description' => $this->convert_post_value($meta['rank_math_twitter_description'] ?? '', $post_id), | |
| 286 | 286 | 'twitter_image' => $meta['rank_math_twitter_image'] ?? '', |
| 287 | 287 | 'primary_category' => (int) ($meta['rank_math_primary_category'] ?? 0), |
| 288 | 288 | 'schema_type' => $this->resolve_schema_type($meta), |
| 289 | + // WooCommerce product identifier. Rank Math keeps it in its | |
| 290 | + // own per-post meta; ThinkRank Pro reads the canonical field | |
| 291 | + // this maps to, so a migrating store keeps the identifier | |
| 292 | + // Google asks for instead of collecting a "missing | |
| 293 | + // identifier" warning on every product (#715). | |
| 294 | + 'product_identifier' => (string) ($meta['rank_math_gtin_code'] ?? ''), | |
| 289 | 295 | // Rank Math pillar content maps directly to ThinkRank pillar content. |
| 290 | 296 | 'pillar_content' => $this->normalize_pillar_content($meta['rank_math_pillar_content'] ?? ''), |
| 291 | 297 | ], |
| 292 | 298 | 'extended' => [ |
| @@ -343,16 +349,16 @@ | ||
| 343 | 349 | 'object_id' => $term_id, |
| 344 | 350 | 'object_type' => 'term', |
| 345 | 351 | 'source_plugin' => $this->plugin_slug, |
| 346 | 352 | 'data' => [ |
| 347 | - 'seo_title' => $this->convert_term_template_variables($meta['rank_math_title'] ?? '', $term_id), | |
| 348 | - 'meta_description' => $this->convert_term_template_variables($meta['rank_math_description'] ?? '', $term_id), | |
| 353 | + 'seo_title' => $this->convert_term_value($meta['rank_math_title'] ?? '', $term_id), | |
| 354 | + 'meta_description' => $this->convert_term_value($meta['rank_math_description'] ?? '', $term_id), | |
| 349 | 355 | 'focus_keyword' => $primary_keyword, |
| 350 | 356 | 'canonical_url' => $meta['rank_math_canonical_url'] ?? '', |
| 351 | 357 | 'noindex' => $robots['noindex'], |
| 352 | 358 | 'nofollow' => $robots['nofollow'], |
| 353 | - 'og_title' => $this->convert_term_template_variables($meta['rank_math_facebook_title'] ?? '', $term_id), | |
| 354 | - 'og_description' => $this->convert_term_template_variables($meta['rank_math_facebook_description'] ?? '', $term_id), | |
| 359 | + 'og_title' => $this->convert_term_value($meta['rank_math_facebook_title'] ?? '', $term_id), | |
| 360 | + 'og_description' => $this->convert_term_value($meta['rank_math_facebook_description'] ?? '', $term_id), | |
| 355 | 361 | ], |
| 356 | 362 | 'extended' => [ |
| 357 | 363 | 'og_image' => $meta['rank_math_facebook_image'] ?? '', |
| 358 | 364 | 'twitter_title' => $meta['rank_math_twitter_title'] ?? '', |
| @@ -990,8 +996,68 @@ | ||
| 990 | 996 | return self::MODERN_SCHEMA_TYPE_MAP[$key] ?? ''; |
| 991 | 997 | } |
| 992 | 998 | |
| 993 | 999 | /** |
| 1000 | + * Rank Math per-post tokens ThinkRank resolves per request (#886): kept | |
| 1001 | + * as tags rather than frozen into text. Same vocabulary as | |
| 1002 | + * convert_template_tokens(). | |
| 1003 | + */ | |
| 1004 | + private const POST_TOKENS = [ | |
| 1005 | + '%title%' => '%title%', | |
| 1006 | + '%sitename%' => '%sitename%', | |
| 1007 | + '%sep%' => '%sep%', | |
| 1008 | + '%excerpt%' => '%excerpt%', | |
| 1009 | + '%date%' => '%date%', | |
| 1010 | + '%modified%' => '%modified%', | |
| 1011 | + '%name%' => '%author%', | |
| 1012 | + '%category%' => '%category%', | |
| 1013 | + '%wc_price%' => '%price%', | |
| 1014 | + '%wc_sku%' => '%sku%', | |
| 1015 | + '%wc_shortdesc%' => '%short_description%', | |
| 1016 | + '%wc_brand%' => '%brand%', | |
| 1017 | + ]; | |
| 1018 | + | |
| 1019 | + /** | |
| 1020 | + * Rank Math per-term tokens ThinkRank resolves on a term archive. | |
| 1021 | + */ | |
| 1022 | + private const TERM_TOKENS = [ | |
| 1023 | + '%term%' => '%term%', | |
| 1024 | + '%term_description%' => '%excerpt%', | |
| 1025 | + '%sitename%' => '%sitename%', | |
| 1026 | + '%sep%' => '%sep%', | |
| 1027 | + ]; | |
| 1028 | + | |
| 1029 | + /** | |
| 1030 | + * A per-post Rank Math value: mapped tokens as ThinkRank tags, the rest literal. | |
| 1031 | + * | |
| 1032 | + * @param mixed $value Raw Rank Math value | |
| 1033 | + * @param int $post_id Post ID | |
| 1034 | + * @return string | |
| 1035 | + */ | |
| 1036 | + private function convert_post_value($value, int $post_id): string { | |
| 1037 | + return $this->tokenize_object_template( | |
| 1038 | + $this->stringify_template_value($value), | |
| 1039 | + self::literal_token_patterns(self::POST_TOKENS), | |
| 1040 | + fn(string $rest): string => $this->convert_template_variables($rest, $post_id) | |
| 1041 | + ); | |
| 1042 | + } | |
| 1043 | + | |
| 1044 | + /** | |
| 1045 | + * A per-term Rank Math value: mapped tokens as ThinkRank tags, the rest literal. | |
| 1046 | + * | |
| 1047 | + * @param mixed $value Raw Rank Math value | |
| 1048 | + * @param int $term_id Term ID | |
| 1049 | + * @return string | |
| 1050 | + */ | |
| 1051 | + private function convert_term_value($value, int $term_id): string { | |
| 1052 | + return $this->tokenize_object_template( | |
| 1053 | + $this->stringify_template_value($value), | |
| 1054 | + self::literal_token_patterns(self::TERM_TOKENS), | |
| 1055 | + fn(string $rest): string => $this->convert_term_template_variables($rest, $term_id) | |
| 1056 | + ); | |
| 1057 | + } | |
| 1058 | + | |
| 1059 | + /** | |
| 994 | 1060 | * Resolve a Rank Math TERM title/description value, replacing the |
| 995 | 1061 | * term-context tokens (%term%, %term_description%) Rank Math uses for term |
| 996 | 1062 | * archives before delegating to the shared variable resolver. Without this |
| 997 | 1063 | * the term name is stripped and titles render as "Archives - Site". |
| @@ -1101,13 +1167,25 @@ | ||
| 1101 | 1167 | |
| 1102 | 1168 | // Rank Math token => ThinkRank Global SEO token (structure preserved). |
| 1103 | 1169 | $token_map = [ |
| 1104 | 1170 | '%name%' => '%author%', // Rank Math author display name token |
| 1171 | + // WooCommerce product variables. Rank Math lets a product title or | |
| 1172 | + // description carry the price, SKU and stock status; every one of | |
| 1173 | + // them used to be stripped by the pass below, so "Buy %title% for | |
| 1174 | + // %wc_price%" imported as "Buy %title% for" (#715). | |
| 1175 | + '%wc_price%' => '%price%', | |
| 1176 | + '%wc_sku%' => '%sku%', | |
| 1177 | + '%wc_shortdesc%' => '%short_description%', | |
| 1178 | + '%wc_brand%' => '%brand%', | |
| 1105 | 1179 | ]; |
| 1106 | 1180 | $template = str_replace(array_keys($token_map), array_values($token_map), $template); |
| 1107 | 1181 | |
| 1108 | 1182 | // Tokens ThinkRank's Global SEO engine resolves natively — keep as-is. |
| 1109 | - $supported = ['%title%', '%sitename%', '%sep%', '%excerpt%', '%date%', '%modified%', '%author%', '%category%']; | |
| 1183 | + $supported = [ | |
| 1184 | + '%title%', '%sitename%', '%sep%', '%excerpt%', '%date%', '%modified%', '%author%', '%category%', | |
| 1185 | + // Resolved by Pattern_Resolver on a product, empty elsewhere. | |
| 1186 | + '%price%', '%sale_price%', '%sku%', '%stock_status%', '%short_description%', '%brand%', | |
| 1187 | + ]; | |
| 1110 | 1188 | |
| 1111 | 1189 | // Strip any token ThinkRank cannot resolve so it does not render literally. |
| 1112 | 1190 | $template = preg_replace_callback( |
| 1113 | 1191 | '/%[a-z0-9_]+%/i', |