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-aioseo-exporter.php +83 -12 2.12.0 → 2.14.2 View file →
@@ -273,10 +273,10 @@
273 273 'object_id' => $post_id,
274 274 'object_type' => 'post',
275 275 'source_plugin' => $this->plugin_slug,
276 276 'data' => [
277 - 'seo_title' => $this->convert_template_variables($this->safe_column($row, 'title'), $post_id),
278 - 'meta_description' => $this->convert_template_variables($this->safe_column($row, 'description'), $post_id),
277 + 'seo_title' => $this->convert_post_value($this->safe_column($row, 'title'), $post_id),
278 + 'meta_description' => $this->convert_post_value($this->safe_column($row, 'description'), $post_id),
279 279 'focus_keyword' => $focus_keyword,
280 280 'focus_keywords' => $focus_keywords,
281 281 'canonical_url' => $this->safe_column($row, 'canonical_url'),
282 282 'noindex' => $noindex,
@@ -286,13 +286,13 @@
286 286 'nosnippet' => $robots_default ? null : $this->safe_column_int($row, 'robots_nosnippet'),
287 287 'max_snippet' => $robots_default ? null : $this->robots_limit($row, 'robots_max_snippet'),
288 288 'max_video_preview' => $robots_default ? null : $this->robots_limit($row, 'robots_max_videopreview'),
289 289 'max_image_preview' => $max_image_preview,
290 - 'og_title' => $this->convert_template_variables($this->safe_column($row, 'og_title'), $post_id),
291 - 'og_description' => $this->convert_template_variables($this->safe_column($row, 'og_description'), $post_id),
290 + 'og_title' => $this->convert_post_value($this->safe_column($row, 'og_title'), $post_id),
291 + 'og_description' => $this->convert_post_value($this->safe_column($row, 'og_description'), $post_id),
292 292 'og_image' => $this->safe_column($row, 'og_image_custom_url'),
293 - 'twitter_title' => $this->convert_template_variables($this->safe_column($row, 'twitter_title'), $post_id),
294 - 'twitter_description' => $this->convert_template_variables($this->safe_column($row, 'twitter_description'), $post_id),
293 + 'twitter_title' => $this->convert_post_value($this->safe_column($row, 'twitter_title'), $post_id),
294 + 'twitter_description' => $this->convert_post_value($this->safe_column($row, 'twitter_description'), $post_id),
295 295 'twitter_image' => $this->safe_column($row, 'twitter_image_custom_url'),
296 296 'primary_category' => $this->extract_primary_category($row),
297 297 'schema_type' => $schema_type,
298 298 // AIOSEO pillar content maps directly to ThinkRank pillar content.
@@ -480,10 +480,10 @@
480 480 'object_id' => $term_id,
481 481 'object_type' => 'term',
482 482 'source_plugin' => $this->plugin_slug,
483 483 'data' => [
484 - 'seo_title' => $this->convert_template_variables((string) ($row['title'] ?? '')),
485 - 'meta_description' => $this->convert_template_variables((string) ($row['description'] ?? '')),
484 + 'seo_title' => $this->convert_term_value((string) ($row['title'] ?? '')),
485 + 'meta_description' => $this->convert_term_value((string) ($row['description'] ?? '')),
486 486 'focus_keyword' => $focus_keyword,
487 487 'focus_keywords' => array_values(array_filter(
488 488 array_merge([$focus_keyword], $additional),
489 489 static fn($keyword) => trim((string) $keyword) !== ''
@@ -490,13 +490,13 @@
490 490 )),
491 491 'canonical_url' => (string) ($row['canonical_url'] ?? ''),
492 492 'noindex' => $robots_default ? null : (int) ($row['robots_noindex'] ?? 0),
493 493 'nofollow' => $robots_default ? null : (int) ($row['robots_nofollow'] ?? 0),
494 - 'og_title' => $this->convert_template_variables((string) ($row['og_title'] ?? '')),
495 - 'og_description' => $this->convert_template_variables((string) ($row['og_description'] ?? '')),
494 + 'og_title' => $this->convert_term_value((string) ($row['og_title'] ?? '')),
495 + 'og_description' => $this->convert_term_value((string) ($row['og_description'] ?? '')),
496 496 'og_image' => (string) ($row['og_image_custom_url'] ?? ''),
497 - 'twitter_title' => $this->convert_template_variables((string) ($row['twitter_title'] ?? '')),
498 - 'twitter_description' => $this->convert_template_variables((string) ($row['twitter_description'] ?? '')),
497 + 'twitter_title' => $this->convert_term_value((string) ($row['twitter_title'] ?? '')),
498 + 'twitter_description' => $this->convert_term_value((string) ($row['twitter_description'] ?? '')),
499 499 'twitter_image' => (string) ($row['twitter_image_custom_url'] ?? ''),
500 500 ],
501 501 'extended' => [],
502 502 ];
@@ -1213,8 +1213,79 @@
1213 1213 ];
1214 1214 }
1215 1215
1216 1216 return $records;
1217 + }
1218 +
1219 + /**
1220 + * AIOSEO per-post tags ThinkRank resolves per request (#886): kept as
1221 + * ThinkRank tags rather than frozen into text. Mirrors
1222 + * convert_aioseo_global_template().
1223 + */
1224 + private const POST_TOKENS = [
1225 + '#post_title' => '%title%',
1226 + '#site_title' => '%sitename%',
1227 + '#separator_sa' => '%sep%',
1228 + '#post_excerpt' => '%excerpt%',
1229 + '#post_date' => '%date%',
1230 + '#author_name' => '%author%',
1231 + '#category_title' => '%category%',
1232 + ];
1233 +
1234 + /**
1235 + * AIOSEO per-term tags ThinkRank resolves on a term archive. The literal
1236 + * resolver only knows post context, so these were stripped.
1237 + */
1238 + private const TERM_TOKENS = [
1239 + '#taxonomy_title' => '%term%',
1240 + '#taxonomy_description' => '%excerpt%',
1241 + '#site_title' => '%sitename%',
1242 + '#separator_sa' => '%sep%',
1243 + ];
1244 +
1245 + /**
1246 + * A per-post AIOSEO value: mapped tags as ThinkRank tags, the rest literal.
1247 + *
1248 + * @param mixed $value Raw AIOSEO value
1249 + * @param int $post_id Post ID
1250 + * @return string
1251 + */
1252 + private function convert_post_value($value, int $post_id): string {
1253 + return $this->tokenize_object_template(
1254 + $this->stringify_template_value($value),
1255 + self::aioseo_token_patterns(self::POST_TOKENS),
1256 + fn(string $rest): string => $this->convert_template_variables($rest, $post_id)
1257 + );
1258 + }
1259 +
1260 + /**
1261 + * A per-term AIOSEO value: mapped tags as ThinkRank tags, the rest literal.
1262 + *
1263 + * @param mixed $value Raw AIOSEO value
1264 + * @return string
1265 + */
1266 + private function convert_term_value($value): string {
1267 + return $this->tokenize_object_template(
1268 + $this->stringify_template_value($value),
1269 + self::aioseo_token_patterns(self::TERM_TOKENS),
1270 + fn(string $rest): string => $this->convert_template_variables($rest)
1271 + );
1272 + }
1273 +
1274 + /**
1275 + * AIOSEO tags have no closing delimiter, so each one must end at a word
1276 + * boundary — `#post_title` is not the start of `#post_titles`.
1277 + *
1278 + * @param array<string,string> $map AIOSEO tag => ThinkRank tag
1279 + * @return array<string,string> Regex => ThinkRank tag
1280 + */
1281 + private static function aioseo_token_patterns(array $map): array {
1282 + $patterns = [];
1283 + foreach ($map as $tag => $thinkrank_tag) {
1284 + $patterns['/' . preg_quote((string) $tag, '/') . '\\b/'] = $thinkrank_tag;
1285 + }
1286 +
1287 + return $patterns;
1217 1288 }
1218 1289
1219 1290 /**
1220 1291 * {@inheritDoc}