PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.9.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.9.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 1.28.0 1.27.0 1.26.0 1.25.0 trunk 1.0.0 1.0.1 1.0.2 All 50 releases
← All changes | includes/seo/class-seo-analyzer-fixer.php +128 -0 2.3.0 → 2.9.0 View file →
@@ -72,8 +72,19 @@
72 72 'image_alt_text' => [
73 73 'label' => __('Fill in missing image alt text', 'thinkrank'),
74 74 'warning' => __('Runs in batches over your media library. With AI alt text enabled this uses your AI provider key.', 'thinkrank'),
75 75 ],
76 + 'ai_crawler_access' => [
77 + 'label' => __('Allow AI answer engines to crawl this site', 'thinkrank'),
78 + // Only the answer engines are unblocked. Saying so matters:
79 + // people block the training crawlers on purpose, and a fix
80 + // that quietly reversed that decision would be a betrayal.
81 + 'warning' => __('Sets the answer-engine crawlers (ChatGPT, Claude, Perplexity, Google-Extended) to Allow. Crawlers that only collect training data keep whatever setting you gave them.', 'thinkrank'),
82 + ],
83 + 'llms_txt' => [
84 + 'label' => __('Publish llms.txt', 'thinkrank'),
85 + 'warning' => __('Publishes /llms.txt from your saved LLMs.txt settings. It needs those fields filled in first.', 'thinkrank'),
86 + ],
76 87 ];
77 88
78 89 /**
79 90 * Filter the checks the analyzer can fix automatically.
@@ -118,8 +129,12 @@
118 129 case 'schema':
119 130 return $this->fix_schema();
120 131 case 'image_alt_text':
121 132 return $this->fix_image_alt_text();
133 + case 'ai_crawler_access':
134 + return $this->fix_ai_crawler_access();
135 + case 'llms_txt':
136 + return $this->fix_llms_txt();
122 137 }
123 138
124 139 /**
125 140 * Let a third party handle a fix it registered.
@@ -303,7 +318,120 @@
303 318 'data' => [
304 319 'updated' => $updated,
305 320 'remaining' => $remaining,
306 321 ],
322 + ];
323 + }
324 +
325 + /**
326 + * Set every AI answer-engine crawler to `allow`.
327 + *
328 + * Deliberately narrow: only the crawlers that decide whether the site can
329 + * be CITED are touched. GPTBot, ClaudeBot, CCBot and the other
330 + * training-only agents keep whatever the user chose for them — blocking
331 + * those is an editorial position, not a misconfiguration.
332 + *
333 + * @since 2.5.0
334 + * @return array
335 + * @throws \Exception When the rules cannot be saved, or when something
336 + * other than the rule map is still blocking them.
337 + */
338 + private function fix_ai_crawler_access(): array {
339 + $manager = new Site_Identity_Manager();
340 + $settings = $manager->get_settings('site');
341 + $settings = is_array($settings) ? $settings : [];
342 +
343 + $rules = isset($settings['ai_crawler_rules']) && is_array($settings['ai_crawler_rules'])
344 + ? $settings['ai_crawler_rules']
345 + : [];
346 +
347 + foreach (SEO_Analyzer::GEO_ANSWER_AGENTS as $slug) {
348 + if (AI_Crawlers::exists($slug)) {
349 + $rules[$slug] = 'allow';
350 + }
351 + }
352 +
353 + $settings['ai_crawler_rules'] = $rules;
354 +
355 + if (!$manager->save_settings('site', null, $settings)) {
356 + throw new \Exception(esc_html__('The AI crawler rules could not be saved. Check the PHP error log for the ThinkRank line naming the cause.', 'thinkrank'));
357 + }
358 +
359 + // The directives are composed into the served body at render time, but
360 + // a physical robots.txt in the web root is a copy the web server hands
361 + // out directly — saving the rules does not touch it. Re-sync from a
362 + // fresh manager (the one above holds the pre-save settings) or the fix
363 + // changes only the file nobody is reading.
364 + (new Site_Identity_Manager())->sync_robots_txt_file();
365 +
366 + // The rules are only one of the things that can disallow a crawler: a
367 + // hand-written robots.txt body, a physical robots.txt in the web root,
368 + // and the site-wide search block all outrank them. Ask the served file
369 + // again rather than reporting a success it contradicts.
370 + $still_blocked = (new SEO_Analyzer())->blocked_answer_agents();
371 +
372 + if (!empty($still_blocked)) {
373 + throw new \Exception(
374 + sprintf(
375 + /* translators: %s: comma-separated crawler names. */
376 + esc_html__('The crawler rules were saved, but your robots.txt still blocks %s. That block comes from your own robots.txt content, a robots.txt file in your site root, or the site-wide search-engine setting — edit it under Essential SEO → Crawling and AI Indexing.', 'thinkrank'),
377 + esc_html(implode(', ', $still_blocked))
378 + )
379 + );
380 + }
381 +
382 + return [
383 + 'fixed' => true,
384 + 'message' => __('AI answer engines can now crawl and cite your site.', 'thinkrank'),
385 + 'data' => [],
386 + ];
387 + }
388 +
389 + /**
390 + * Generate and publish /llms.txt from the saved LLMs.txt settings.
391 + *
392 + * The document is built from settings the user already wrote, so this is a
393 + * publish, not authorship. Incomplete settings produce an invalid document
394 + * and are refused rather than written — a half-empty llms.txt is worse
395 + * than none, because an assistant will read it and believe it.
396 + *
397 + * @since 2.5.0
398 + * @return array
399 + * @throws \Exception When the module is unavailable, the settings are
400 + * incomplete, or the write fails.
401 + */
402 + private function fix_llms_txt(): array {
403 + if (!class_exists('ThinkRank\\SEO\\LLMs_Txt_Manager')) {
404 + throw new \Exception(esc_html__('The llms.txt module is unavailable.', 'thinkrank'));
405 + }
406 +
407 + $manager = new LLMs_Txt_Manager();
408 + $generated = $manager->generate_llms_txt([], []);
409 +
410 + $validation = isset($generated['validation']) && is_array($generated['validation'])
411 + ? $generated['validation']
412 + : [];
413 +
414 + if (empty($validation['valid'])) {
415 + throw new \Exception(esc_html__('llms.txt could not be published because its settings are incomplete. Fill in Website Description, Key Features and Target Audience under Essential SEO → Crawling and AI Indexing → LLMs.txt, then try again.', 'thinkrank'));
416 + }
417 +
418 + $content = (string) ($generated['content'] ?? '');
419 + $write = $manager->write_llms_txt_to_file($content);
420 +
421 + if (empty($write['success'])) {
422 + $reason = (string) ($write['message'] ?? '');
423 +
424 + throw new \Exception(
425 + '' !== $reason
426 + ? esc_html($reason)
427 + : esc_html__('llms.txt could not be written. Check that your site root is writable.', 'thinkrank')
428 + );
429 + }
430 +
431 + return [
432 + 'fixed' => true,
433 + 'message' => __('Your llms.txt is now published at /llms.txt.', 'thinkrank'),
434 + 'data' => [],
307 435 ];
308 436 }
309 437 }