$post Sanitized POST fields (Request::post()). * @return string */ public static function render( array $post ): string { $html = '

' . esc_html__( 'Connection', 'flitagent' ) . '

' . '

' . esc_html__( 'Test connection checks, from this server to itself, the host settings that most often break MCP connections: a public HTTPS address, permalinks, the MCP endpoint and custom request headers. No credentials are sent and nothing is stored.', 'flitagent' ) . ' ' . '' . esc_html__( 'Site Health', 'flitagent' ) . ' ' . esc_html__( 'covers general loopback and REST API problems.', 'flitagent' ) . '

' . '
' . '' . wp_nonce_field( self::ACTION, '_wpnonce', true, false ) . '

'; if ( ! isset( $post[ self::ACTION ] ) ) { return $html . '
'; } if ( ! current_user_can( Admin::CAPABILITY ) ) { return $html . self::error( __( 'You are not allowed to do this.', 'flitagent' ) ) . ''; } $nonce = isset( $post['_wpnonce'] ) && is_string( $post['_wpnonce'] ) ? $post['_wpnonce'] : ''; if ( ! wp_verify_nonce( $nonce, self::ACTION ) ) { return $html . self::error( __( 'The link expired. Reload the page and try again.', 'flitagent' ) ) . ''; } return $html . self::results( self::run() ) . ''; } /** * Runs every check against this site (#148: also from the setup guide). The caller checks access. * * @return array> */ public static function run(): array { return self::diagnostics()->run( self::token() ); } /** * Diagnostics fed with this site's URLs and the loopback HTTP function. * * @return Diagnostics */ private static function diagnostics(): Diagnostics { return new Diagnostics( array( 'home' => home_url( '/' ), 'mcp' => rest_url( RestController::REST_NAMESPACE . RestController::ROUTE ), 'probe' => rest_url( RestController::REST_NAMESPACE . DiagnosticsRoute::ROUTE ), 'pretty_permalinks' => '' !== (string) get_option( 'permalink_structure', '' ), 'gateway' => Config::url(), ), array( self::class, 'http' ) ); } /** * A fresh per-run token for the custom header check. * * @return string */ private static function token(): string { return DiagnosticsToken::mint( DiagnosticsRoute::site_secret(), time(), bin2hex( random_bytes( 16 ) ) ); } /** * Loopback HTTP for Diagnostics: core's safe request API (which allows only the home host's own port among private * addresses), a short timeout, no redirects, no cookies, and core's `https_local_ssl_verify` setting as Site Health * uses it. Diagnostics already refuses URLs outside the home URL. * * @param string $method HTTP method. * @param string $url URL. * @param array $headers Headers. * @param string $body Body. * @return array{code: int, type: string, body: string}|string */ public static function http( string $method, string $url, array $headers, string $body ) { $response = wp_safe_remote_request( $url, array( 'method' => $method, 'headers' => $headers, 'body' => '' === $body ? null : $body, 'timeout' => self::TIMEOUT, 'redirection' => 0, 'cookies' => array(), // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- core's own loopback filter, applied as Site Health applies it. 'sslverify' => apply_filters( 'https_local_ssl_verify', false, $url ), 'limit_response_size' => self::MAX_BODY, 'user-agent' => 'FlitAgent diagnostics', ) ); if ( is_wp_error( $response ) ) { return $response->get_error_message(); } return array( 'code' => (int) wp_remote_retrieve_response_code( $response ), 'type' => (string) wp_remote_retrieve_header( $response, 'content-type' ), 'body' => (string) wp_remote_retrieve_body( $response ), ); } /** * Results list: each check with its status chip, message and fix. * * @param array> $results Results. * @return string */ public static function results( array $results ): string { $html = '
    '; foreach ( $results as $result ) { $html .= '
  • ' . esc_html( $result['label'] ) . '' . self::chip( $result ) . '' . self::details( $result ) . '
  • '; } return $html . '
'; } /** * The setup guide's check step (the design's step 2 board): the same results as a table with Check, Result and * Details columns. * * @param array> $results Results. * @return string */ public static function table( array $results ): string { $html = '
' . '' . '' . ''; foreach ( $results as $result ) { $html .= ''; } return $html . '
' . esc_html__( 'Check', 'flitagent' ) . '' . esc_html__( 'Result', 'flitagent' ) . '' . esc_html__( 'Details', 'flitagent' ) . '
' . esc_html( $result['label'] ) . '' . self::chip( $result ) . '' . self::details( $result ) . '
'; } /** * A result's status chip (Ui::chip); an unknown status reads as a warning. * * @param array $result A result. * @return string */ private static function chip( array $result ): string { $statuses = array( Diagnostics::PASS => 'pass', Diagnostics::WARNING => 'warning', Diagnostics::FAIL => 'fail', Diagnostics::SKIPPED => 'skipped', ); return Ui::chip( $statuses[ $result['status'] ] ?? 'warning' ); } /** * A result's message, then its fix if it has one. Escaped. * * @param array $result A result. * @return string */ private static function details( array $result ): string { $html = esc_html( $result['message'] ); if ( '' !== $result['fix'] ) { $html .= '
' . esc_html__( 'Fix:', 'flitagent' ) . ' ' . esc_html( $result['fix'] ); } return $html; } /** * Inline error notice. * * @param string $message Message. * @return string */ private static function error( string $message ): string { return '

' . esc_html( $message ) . '

'; } }