PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.14.2
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.14.2
2.14.3 2.14.2 2.14.1 2.14.0 2.13.0 2.12.0 2.11.0 2.10.0 2.9.0 2.8.0 2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 All 58 releases
← 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',