robots = $robots; } /** * Returns the check identifier. * * @return string Identifier. */ public function get_id(): string { return 'robots_txt'; } /** * Whether a loopback request is required. * * @return bool False. */ public function needs_loopback(): bool { return false; } /** * Runs the check. * * @return Result The finding. */ public function run(): Result { $label = __( 'robots.txt rules', 'ai-blackout' ); $physical = $this->robots->has_physical_file(); $detail = array( 'physical_file' => $physical, 'physical_path' => $this->robots->get_physical_path(), 'would_generate' => $this->render_virtual(), 'physical_contents' => $physical ? (string) $this->robots->read_physical_file() : '', 'block_in_physical' => $physical && $this->robots->physical_file_has_block(), ); if ( aiblackout_is_disabled() ) { return new Result( $this->get_id(), $label, Result::INACTIVE, __( 'AI Blackout is switched off by the AIBLACKOUT_DISABLE setting in wp-config.php.', 'ai-blackout' ), __( 'No robots.txt rules are being published at all. Crawlers see nothing from this plugin.', 'ai-blackout' ), __( 'Remove the AIBLACKOUT_DISABLE line from wp-config.php to turn protection back on.', 'ai-blackout' ), $detail ); } if ( ! $this->robots->knows_site_root() ) { return new Result( $this->get_id(), $label, Result::UNKNOWN, __( 'We could not work out which folder your site is served from, so we cannot tell what robots.txt is being served.', 'ai-blackout' ), __( 'If a robots.txt file exists on disk it overrides WordPress entirely, and we cannot check for one without knowing where to look. Your rules may or may not be reaching crawlers.', 'ai-blackout' ), __( 'Check that the WordPress Address and Site Address in Settings → General match how your site is actually reached, then run this again. Opening /robots.txt in your browser will show you what is really being served.', 'ai-blackout' ), $detail ); } if ( ! $physical ) { return new Result( $this->get_id(), $label, Result::DECLARED, __( 'WordPress is serving robots.txt, and your AI crawler rules are included in it.', 'ai-blackout' ), __( 'Crawlers that check robots.txt and choose to obey it will stay away. This is a request, not a barrier. It does not stop a crawler that ignores the rules.', 'ai-blackout' ), '', $detail ); } if ( $this->robots->physical_file_has_block() ) { return new Result( $this->get_id(), $label, Result::DECLARED, __( 'A robots.txt file exists on disk and your AI crawler rules have been merged into it.', 'ai-blackout' ), __( 'Crawlers that check robots.txt and choose to obey it will stay away. Because the file is on disk, WordPress does not generate it -- so the plugin rewrites it every time you save.', 'ai-blackout' ), '', $detail ); } return new Result( $this->get_id(), $label, Result::BROKEN, __( 'A robots.txt file exists on disk and does not contain your AI crawler rules.', 'ai-blackout' ), __( 'None of your robots.txt rules are reaching crawlers. The web server serves this file directly and never asks WordPress, so the rules shown in your settings are not the rules being published.', 'ai-blackout' ), __( 'Save your choices again. That merges your rules into the existing file, leaving everything already in it untouched.', 'ai-blackout' ), $detail ); } /** * Renders the robots.txt WordPress would generate right now. * * @return string Generated robots.txt content. */ private function render_virtual(): string { $output = "User-agent: *\n"; $output .= 'Disallow: ' . wp_parse_url( admin_url(), PHP_URL_PATH ) . "\n"; $output .= 'Allow: ' . wp_parse_url( admin_url( 'admin-ajax.php' ), PHP_URL_PATH ) . "\n"; // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Core's filter, invoked for a read-only preview. return (string) apply_filters( 'robots_txt', $output, (bool) get_option( 'blog_public' ) ); } }