WP_REST_Server::READABLE, // Intentionally public: called by the FocalScore server, not a logged-in user. It reveals nothing. 'permission_callback' => '__return_true', 'callback' => 'focalscore_rest_verify', 'args' => array( 'challenge' => array( 'description' => __( 'A random challenge from FocalScore: 1 to 64 lowercase hexadecimal characters.', 'focalscore' ), 'type' => 'string', 'required' => true, 'validate_callback' => 'focalscore_valid_challenge', 'sanitize_callback' => 'sanitize_text_field', ), ), ) ); } add_action( 'rest_api_init', 'focalscore_rest_routes' ); /** * Whether a challenge is 1 to 64 lowercase hexadecimal characters. * * @param mixed $value The challenge parameter. * @return bool */ function focalscore_valid_challenge( $value ) { return is_string( $value ) && 1 === preg_match( '/^[a-f0-9]{1,64}$/D', $value ); } /** * The proof that this site holds the one-time token, for FocalScore's * challenge. The token itself is never returned. * * @param WP_REST_Request $request The request, with a validated challenge. * @return WP_REST_Response */ function focalscore_rest_verify( $request ) { $token = get_transient( 'focalscore_verify_token' ); if ( ! is_string( $token ) || '' === $token ) { $response = new WP_REST_Response( null, 404 ); } else { $challenge = (string) $request->get_param( 'challenge' ); $response = new WP_REST_Response( array( 'proof' => hash_hmac( 'sha256', $challenge, $token ) ), 200 ); } $response->header( 'Cache-Control', 'no-store' ); return $response; } /** * A JSON call to the FocalScore service. * * @param string $method GET or POST. * @param string $path The API path after /api/plugin. * @param array|null $body The JSON body for a POST. * @return array{ok: bool, status: int, data: array} */ function focalscore_api( $method, $path, $body = null ) { $args = array( 'timeout' => 30, 'headers' => array( 'Content-Type' => 'application/json', 'Accept' => 'application/json', ), ); if ( null !== $body ) { $args['body'] = wp_json_encode( $body ); } $url = FOCALSCORE_API . $path; $response = 'POST' === $method ? wp_remote_post( $url, $args ) : wp_remote_get( $url, $args ); if ( is_wp_error( $response ) ) { return array( 'ok' => false, 'status' => 0, 'data' => array( 'error' => __( 'We could not reach FocalScore. Check your connection and try again.', 'focalscore' ) ), ); } $status = (int) wp_remote_retrieve_response_code( $response ); $data = json_decode( wp_remote_retrieve_body( $response ), true ); return array( 'ok' => $status >= 200 && $status < 300, 'status' => $status, 'data' => is_array( $data ) ? $data : array(), ); } /** * The form's handler: no consent, no scan. Only then are the site's address * and the email sent, and only to FocalScore. */ function focalscore_handle_scan() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'You are not allowed to do that.', 'focalscore' ) ); } check_admin_referer( 'focalscore_scan' ); $back = admin_url( 'tools.php?page=focalscore' ); $consent = isset( $_POST['focalscore_consent'] ) && '1' === sanitize_text_field( wp_unslash( $_POST['focalscore_consent'] ) ); if ( ! $consent ) { // The founder's rule: without the box ticked, nothing is sent. focalscore_notice( 'error', __( 'Tick the box to agree before scanning. Nothing was sent to FocalScore.', 'focalscore' ) ); wp_safe_redirect( $back ); exit; } $email = isset( $_POST['focalscore_email'] ) ? sanitize_email( wp_unslash( $_POST['focalscore_email'] ) ) : ''; if ( ! is_email( $email ) ) { focalscore_notice( 'error', __( 'Enter a valid email for your report. Nothing was sent to FocalScore.', 'focalscore' ) ); wp_safe_redirect( $back ); exit; } $given = gmdate( 'c' ); update_option( 'focalscore_consent', array( 'version' => FOCALSCORE_CONSENT_VERSION, 'at' => $given, 'email' => $email, ), false ); $start = focalscore_api( 'POST', '/start', array( 'site' => home_url( '/' ), 'email' => $email, 'consent' => array( 'given' => true, 'version' => FOCALSCORE_CONSENT_VERSION, 'at' => $given, ), 'plugin' => FOCALSCORE_VERSION, ) ); if ( ! $start['ok'] || empty( $start['data']['token'] ) ) { focalscore_notice( 'error', focalscore_error_text( $start ) ); wp_safe_redirect( $back ); exit; } // Hold the one-time token privately while FocalScore checks (focalscore_rest_verify answers its challenge), then drop it. set_transient( 'focalscore_verify_token', sanitize_text_field( $start['data']['token'] ), 5 * MINUTE_IN_SECONDS ); $verify = focalscore_api( 'POST', '/verify', array( 'token' => $start['data']['token'] ) ); delete_transient( 'focalscore_verify_token' ); if ( ! $verify['ok'] || empty( $verify['data']['scanId'] ) ) { focalscore_notice( 'error', focalscore_error_text( $verify ) ); wp_safe_redirect( $back ); exit; } update_option( 'focalscore_scan', array( 'id' => sanitize_text_field( $verify['data']['scanId'] ), 'key' => sanitize_text_field( $verify['data']['key'] ), 'unsubscribe' => isset( $verify['data']['unsubscribe'] ) ? sanitize_text_field( $verify['data']['unsubscribe'] ) : '', 'email' => $email, 'at' => $given, ), false ); delete_transient( 'focalscore_result' ); wp_safe_redirect( $back ); exit; } add_action( 'admin_post_focalscore_scan', 'focalscore_handle_scan' ); /** * Forget the scan and the consent: the plugin sends nothing more until the box is ticked again. */ function focalscore_handle_forget() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'You are not allowed to do that.', 'focalscore' ) ); } check_admin_referer( 'focalscore_forget' ); delete_option( 'focalscore_consent' ); delete_option( 'focalscore_scan' ); delete_transient( 'focalscore_result' ); focalscore_notice( 'success', __( 'Cleared from WordPress. The plugin will send nothing to FocalScore until you agree and scan again. This did not change anything at FocalScore.', 'focalscore' ) ); wp_safe_redirect( admin_url( 'tools.php?page=focalscore' ) ); exit; } add_action( 'admin_post_focalscore_forget', 'focalscore_handle_forget' ); /** * Stop marketing emails: tell FocalScore, and say "done" only when it confirms. */ function focalscore_handle_unsubscribe() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'You are not allowed to do that.', 'focalscore' ) ); } check_admin_referer( 'focalscore_unsubscribe' ); $scan = get_option( 'focalscore_scan' ); if ( is_array( $scan ) && ! empty( $scan['unsubscribe'] ) ) { $res = focalscore_api( 'POST', '/unsubscribe', array( 'token' => $scan['unsubscribe'] ) ); if ( $res['ok'] ) { focalscore_notice( 'success', __( 'Done. FocalScore will send no more tips or offers to this email. Your report stays available.', 'focalscore' ) ); } else { focalscore_notice( 'error', focalscore_error_text( $res ) ); } } wp_safe_redirect( admin_url( 'tools.php?page=focalscore' ) ); exit; } add_action( 'admin_post_focalscore_unsubscribe', 'focalscore_handle_unsubscribe' ); /** * A message for the next page load. * * @param string $type success or error. * @param string $message The text. */ function focalscore_notice( $type, $message ) { set_transient( 'focalscore_notice_' . get_current_user_id(), array( 'type' => $type, 'message' => $message, ), MINUTE_IN_SECONDS ); } /** * The service's own words for a refusal, or a plain fallback. * * @param array $res A focalscore_api() result. * @return string */ function focalscore_error_text( $res ) { if ( ! empty( $res['data']['error'] ) && is_string( $res['data']['error'] ) ) { return $res['data']['error']; } return __( 'FocalScore could not start the scan. Try again in a minute.', 'focalscore' ); } /** * The scan's summary from FocalScore: cached briefly while it runs, kept once done. * * @param array $scan The saved scan (id and key). * @return array */ function focalscore_result( $scan ) { $cached = get_transient( 'focalscore_result' ); if ( is_array( $cached ) && isset( $cached['id'] ) && $cached['id'] === $scan['id'] ) { return $cached['data']; } $res = focalscore_api( 'GET', '/result?scan=' . rawurlencode( $scan['id'] ) . '&key=' . rawurlencode( $scan['key'] ) ); $data = $res['ok'] ? $res['data'] : array( 'status' => 'unavailable' ); $keep = ( isset( $data['status'] ) && 'done' === $data['status'] ) ? DAY_IN_SECONDS : 15; set_transient( 'focalscore_result', array( 'id' => $scan['id'], 'data' => $data, ), $keep ); return $data; } /** * The plugin's page. */ function focalscore_render_page() { if ( ! current_user_can( 'manage_options' ) ) { return; } $scan = get_option( 'focalscore_scan' ); $consent = get_option( 'focalscore_consent' ); $email = is_array( $consent ) && ! empty( $consent['email'] ) ? $consent['email'] : get_option( 'admin_email' ); $notice = get_transient( 'focalscore_notice_' . get_current_user_id() ); delete_transient( 'focalscore_notice_' . get_current_user_id() ); $site = wp_parse_url( home_url( '/' ), PHP_URL_HOST ); ?>
info@focalscore.com' ); ?>
' . esc_html__( 'This usually takes a few minutes. This page refreshes on its own; you can also leave it and come back.', 'focalscore' ) . '
'; } elseif ( 'done' === $status ) { $score = isset( $r['score'] ) && null !== $r['score'] ? (int) $r['score'] : null; echo '' . esc_html__( 'Your full report, with every fix, is in your inbox.', 'focalscore' ) . ' '; /* translators: %s: the email the report was sent to */ echo esc_html( sprintf( __( 'Confirm the email we sent to %s to open it.', 'focalscore' ), $scan['email'] ) ) . '
'; if ( ! empty( $r['reportUrl'] ) ) { echo '' . esc_html__( 'Open the report', 'focalscore' ) . '
'; } echo '' . esc_html__( 'Want to know what ChatGPT says about your business? The AI Advanced Report is $49.', 'focalscore' ) . ' '; if ( ! empty( $r['reportUrl'] ) ) { echo '' . esc_html__( 'See what it includes', 'focalscore' ) . ''; } echo '
'; if ( null !== $score ) { echo '' . esc_html__( 'Found this useful?', 'focalscore' ) . ' ' . esc_html__( 'Leave a review on WordPress.org', 'focalscore' ) . '
'; } } elseif ( 'failed' === $status ) { echo '' . esc_html( isset( $r['error'] ) ? $r['error'] : __( 'Try again later.', 'focalscore' ) ) . '
'; } else { echo '' . esc_html__( 'We could not read the scan from FocalScore just now. Reload this page in a minute.', 'focalscore' ) . '
'; } echo ''; }