'string', 'sanitize_callback' => 'sanitize_text_field', ) ); } foreach ( array( 'geistm_contact_email', 'geistm_contact_ai_email', 'geistm_contact_privacy_email' ) as $field ) { register_setting( 'geistm_identity', $field, array( 'type' => 'string', 'sanitize_callback' => 'sanitize_email', ) ); } register_setting( 'geistm_identity', 'geistm_core_concepts', array( 'type' => 'string', 'sanitize_callback' => 'sanitize_textarea_field', ) ); register_setting( 'geistm_identity', 'geistm_ai_summary', array( 'type' => 'string', 'sanitize_callback' => 'sanitize_textarea_field', ) ); // Content tab settings. $content_checkboxes = array( 'geistm_enable_llms_txt', 'geistm_enable_llms_full', 'geistm_llms_include_excerpts', ); foreach ( $content_checkboxes as $field ) { register_setting( 'geistm_content', $field, array( 'type' => 'string', 'sanitize_callback' => array( $this, 'sanitize_checkbox' ), ) ); } foreach ( array( 'geistm_llms_posts_limit', 'geistm_llms_full_posts_limit', 'geistm_llms_pages_limit' ) as $field ) { register_setting( 'geistm_content', $field, array( 'type' => 'integer', 'sanitize_callback' => 'absint', ) ); } // Policies tab settings. register_setting( 'geistm_policies', 'geistm_content_license', array( 'type' => 'string', 'sanitize_callback' => array( $this, 'sanitize_license' ), ) ); foreach ( array( 'geistm_allow_quoting', 'geistm_allow_training' ) as $field ) { register_setting( 'geistm_policies', $field, array( 'type' => 'string', 'sanitize_callback' => array( $this, 'sanitize_checkbox' ), ) ); } // Adnet tab settings. foreach ( array( 'geistm_adnet_enabled', 'geistm_adnet_auto_enabled', 'geistm_adnet_auto_pages' ) as $field ) { register_setting( 'geistm_adnet', $field, array( 'type' => 'string', 'sanitize_callback' => array( $this, 'sanitize_checkbox' ), ) ); } register_setting( 'geistm_adnet', 'geistm_adnet_host', array( 'type' => 'string', 'sanitize_callback' => 'esc_url_raw', ) ); register_setting( 'geistm_adnet', 'geistm_adnet_auto_format', array( 'type' => 'string', 'sanitize_callback' => array( $this, 'sanitize_adnet_format' ), ) ); foreach ( array( 'geistm_adnet_auto_paragraph', 'geistm_adnet_auto_max' ) as $field ) { register_setting( 'geistm_adnet', $field, array( 'type' => 'string', 'sanitize_callback' => array( $this, 'sanitize_positive_int' ), ) ); } register_setting( 'geistm_content', 'geistm_enable_seo_tags', array( 'type' => 'string', 'sanitize_callback' => array( $this, 'sanitize_seo_checkbox' ), ) ); } /** * Sanitize the SEO tags checkbox, preserving the stored value when the * field wasn't part of the submitted form. * * @param mixed $value The submitted value. * @return string '1' or '0'. */ public function sanitize_seo_checkbox( $value ) { if ( null === $value ) { return get_option( 'geistm_enable_seo_tags', '1' ); } return $this->sanitize_checkbox( $value ); } /** * Handle auto-populate form submission. * Reads existing WordPress data and fills in empty AI Discovery fields. * Won't overwrite fields the user has already populated. */ public function handle_auto_populate() { if ( ! isset( $_POST['geistm_do_auto_populate'] ) || ! current_user_can( 'manage_options' ) ) { return; } if ( ! isset( $_POST['geistm_auto_populate_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['geistm_auto_populate_nonce'] ) ), 'geistm_auto_populate' ) ) { return; } // Gather site data for both basic and AI-enhanced populate. $site_name = get_bloginfo( 'name' ); $site_desc = get_bloginfo( 'description' ); $about_page = null; $about_text = ''; $about_html = ''; foreach ( array( 'about', 'about-us', 'who-we-are', 'company' ) as $slug ) { $p = get_page_by_path( $slug ); if ( $p && 'publish' === $p->post_status ) { $about_page = $p; $about_html = $p->post_content; $about_text = wp_strip_all_tags( $p->post_content ); break; } } // Also look for contact page text (both plain text and raw HTML for extraction). $contact_text = ''; $contact_html = ''; foreach ( array( 'contact', 'contact-us', 'get-in-touch' ) as $slug ) { $p = get_page_by_path( $slug ); if ( $p && 'publish' === $p->post_status ) { $contact_html = $p->post_content; $contact_text = wp_strip_all_tags( $p->post_content ); break; } } $categories = get_categories( array( 'orderby' => 'count', 'order' => 'DESC', 'number' => 15, 'hide_empty' => true, 'exclude' => array( 1 ), ) ); $cat_names = wp_list_pluck( $categories, 'name' ); $page_titles = get_pages( array( 'post_status' => 'publish', 'number' => 20, ) ); $page_titles = wp_list_pluck( $page_titles ? $page_titles : array(), 'post_title' ); // Check if AI generation is available. $ai = new Geistm_Ai_Generator(); $use_ai = $ai->is_available(); // Organization Name — from site title. if ( empty( get_option( 'geistm_organization_name', '' ) ) ) { update_option( 'geistm_organization_name', $site_name ); } // Tagline / Mission — from site description. if ( empty( get_option( 'geistm_organization_tagline', '' ) ) ) { update_option( 'geistm_organization_tagline', $site_desc ); } // AI Summary. if ( empty( get_option( 'geistm_ai_summary', '' ) ) ) { if ( $use_ai ) { // AI-generated summary from all available site content. $summary = $ai->generate_summary( $site_name, $site_desc, wp_trim_words( $about_text, 300 ), $page_titles ); if ( ! empty( $summary ) ) { update_option( 'geistm_ai_summary', $summary ); } } elseif ( ! empty( $about_text ) ) { // Basic fallback: first 80 words of about page. update_option( 'geistm_ai_summary', wp_trim_words( $about_text, 80 ) ); } } // Core Concepts. if ( empty( get_option( 'geistm_core_concepts', '' ) ) ) { if ( $use_ai ) { // AI-generated concepts from site content. $concepts = $ai->generate_concepts( $site_name, wp_trim_words( $about_text, 300 ), $cat_names, $page_titles ); if ( ! empty( $concepts ) ) { update_option( 'geistm_core_concepts', $concepts ); } } else { // Basic fallback: categories with descriptions. $concepts = array(); foreach ( $categories as $cat ) { if ( ! empty( $cat->description ) ) { $concepts[] = $cat->name . ': ' . $cat->description; } } if ( ! empty( $concepts ) ) { update_option( 'geistm_core_concepts', implode( "\n", $concepts ) ); } } } // Contact Email — from admin email. if ( empty( get_option( 'geistm_contact_email', '' ) ) ) { update_option( 'geistm_contact_email', get_option( 'admin_email' ) ); } // Contact URL — from contact page if it exists. if ( empty( get_option( 'geistm_contact_url', '' ) ) ) { foreach ( array( 'contact', 'contact-us', 'get-in-touch' ) as $slug ) { $p = get_page_by_path( $slug ); if ( $p && 'publish' === $p->post_status ) { update_option( 'geistm_contact_url', get_permalink( $p ) ); break; } } } // Identity fields — AI extraction from about + contact pages. $identity_fields = array( 'geistm_legal_name', 'geistm_sector', 'geistm_founded', 'geistm_headquarters' ); $any_identity_empty = false; foreach ( $identity_fields as $field ) { if ( empty( get_option( $field, '' ) ) ) { $any_identity_empty = true; break; } } if ( $any_identity_empty && ( ! empty( $about_text ) || ! empty( $contact_text ) ) ) { $identity = array(); // Try AI extraction first. if ( $use_ai ) { $identity = $ai->generate_identity( $site_name, $about_text, $contact_text, $page_titles ); } // Fall back to basic regex extraction if AI returned nothing. if ( empty( $identity ) ) { $identity = self::extract_identity_basic( $about_html . "\n" . $contact_html ); } $field_map = array( 'legalName' => 'geistm_legal_name', 'sector' => 'geistm_sector', 'founded' => 'geistm_founded', 'headquarters' => 'geistm_headquarters', ); foreach ( $field_map as $key => $option ) { if ( ! empty( $identity[ $key ] ) && empty( get_option( $option, '' ) ) ) { update_option( $option, sanitize_text_field( $identity[ $key ] ) ); } } } // Clear caches so viewer shows fresh data. geistm_ai_discovery_clear_all_caches(); // Redirect back to wherever the form lives (Overview setup or Identity tab). wp_safe_redirect( add_query_arg( array( 'page' => 'geistm-adnet', 'tab' => self::origin_tab( 'identity' ), 'auto-populate' => '1', ), admin_url( 'options-general.php' ) ) ); exit; } /** * Read the whitelisted origin tab posted by a form, so shared handlers can * redirect back to the Overview setup flow or the Account tab alike. * * @param string $fallback Tab to use when none was posted. * @return string The tab slug. */ private static function origin_tab( $fallback ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Read alongside the caller's nonce check; display routing only. $tab = isset( $_POST['geistm_origin_tab'] ) ? sanitize_key( wp_unslash( $_POST['geistm_origin_tab'] ) ) : ''; return in_array( $tab, array( 'viewer', 'account', 'adnet', 'identity' ), true ) ? $tab : $fallback; } /** * Handle signing key generation. */ public function handle_generate_key() { if ( ! isset( $_POST['geistm_do_generate_key'] ) || ! current_user_can( 'manage_options' ) ) { return; } if ( ! isset( $_POST['geistm_generate_key_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['geistm_generate_key_nonce'] ) ), 'geistm_generate_key' ) ) { return; } if ( ! Geistm_Signer::has_gmp() ) { return; } $signer = new Geistm_Signer(); if ( ! $signer->has_key() ) { $signer->generate_key(); } geistm_ai_discovery_clear_all_caches(); wp_safe_redirect( add_query_arg( array( 'page' => 'geistm-adnet', 'tab' => self::origin_tab( 'account' ), 'key-generated' => '1', ), admin_url( 'options-general.php' ) ) ); exit; } /** * Save the manual site description from the Overview setup flow. */ public function handle_save_description() { if ( ! isset( $_POST['geistm_do_save_description'] ) || ! current_user_can( 'manage_options' ) ) { return; } if ( ! isset( $_POST['geistm_save_description_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['geistm_save_description_nonce'] ) ), 'geistm_save_description' ) ) { return; } if ( isset( $_POST['geistm_organization_name'] ) ) { update_option( 'geistm_organization_name', sanitize_text_field( wp_unslash( $_POST['geistm_organization_name'] ) ) ); } if ( isset( $_POST['geistm_ai_summary'] ) ) { update_option( 'geistm_ai_summary', sanitize_textarea_field( wp_unslash( $_POST['geistm_ai_summary'] ) ) ); } geistm_ai_discovery_clear_all_caches(); wp_safe_redirect( add_query_arg( array( 'page' => 'geistm-adnet', 'tab' => 'viewer', 'described' => '1', ), admin_url( 'options-general.php' ) ) ); exit; } /** * Handle the "Turn on Adnet" / "Not now" choice in the Overview setup flow. * Turning it on also runs the idempotent network connect, so one click both * enables ad serving and registers the site with the matchmaker. */ public function handle_adnet_choice() { if ( ! current_user_can( 'manage_options' ) ) { return; } $enable = isset( $_POST['geistm_do_adnet_enable'] ); $decline = isset( $_POST['geistm_do_adnet_decline'] ); if ( ! $enable && ! $decline ) { return; } if ( ! isset( $_POST['geistm_adnet_choice_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['geistm_adnet_choice_nonce'] ) ), 'geistm_adnet_choice' ) ) { return; } $args = array( 'page' => 'geistm-adnet', 'tab' => 'viewer', ); if ( $enable ) { update_option( 'geistm_adnet_enabled', '1' ); delete_option( 'geistm_adnet_declined' ); Geistm_Network::connect(); $args['adnet-on'] = '1'; } else { update_option( 'geistm_adnet_declined', '1', false ); } wp_safe_redirect( add_query_arg( $args, admin_url( 'options-general.php' ) ) ); exit; } /** * Handle the placement choice in the Overview setup flow: automatic * placement on, or manual (which opens the copy-a-slot panel). */ public function handle_placement_choice() { if ( ! isset( $_POST['geistm_do_placement'] ) || ! current_user_can( 'manage_options' ) ) { return; } if ( ! isset( $_POST['geistm_placement_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['geistm_placement_nonce'] ) ), 'geistm_placement' ) ) { return; } $choice = sanitize_key( wp_unslash( $_POST['geistm_do_placement'] ) ); if ( 'auto' === $choice ) { update_option( 'geistm_adnet_auto_enabled', '1' ); update_option( 'geistm_adnet_placement_choice', 'auto', false ); } else { $choice = 'manual'; update_option( 'geistm_adnet_placement_choice', 'manual', false ); } wp_safe_redirect( add_query_arg( array( 'page' => 'geistm-adnet', 'tab' => 'viewer', 'placement' => $choice, ), admin_url( 'options-general.php' ) ) ); exit; } /** * Stream the wallet backup file. The private key travels only in this * admin-initiated HTTPS download — it is never written to disk on the * server or echoed into a page. */ public function handle_wallet_export() { if ( ! isset( $_POST['geistm_do_wallet_export'] ) || ! current_user_can( 'manage_options' ) ) { return; } if ( ! isset( $_POST['geistm_wallet_export_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['geistm_wallet_export_nonce'] ) ), 'geistm_wallet_export' ) ) { return; } $signer = new Geistm_Signer(); $key = $signer->export_key(); $address = Geistm_Signer::stored_address(); if ( '' === $key || '' === $address ) { wp_safe_redirect( add_query_arg( array( 'page' => 'geistm-adnet', 'tab' => 'account', 'wallet-backup' => 'error', ), admin_url( 'options-general.php' ) ) ); exit; } $domain = wp_parse_url( home_url(), PHP_URL_HOST ); nocache_headers(); header( 'Content-Type: application/json; charset=utf-8' ); header( 'Content-Disposition: attachment; filename="geistm-wallet-' . sanitize_file_name( $domain ) . '-' . gmdate( 'Ymd' ) . '.json"' ); echo wp_json_encode( array( 'format' => 'geistm-wallet-backup-v1', 'site' => home_url(), 'address' => $address, 'privateKey' => '0x' . $key, 'exportedAt' => gmdate( 'c' ), 'note' => 'Keep this file private — anyone who has it controls this wallet and its Credit. Restore it from the Account tab after reinstalling or moving your site.', ), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ); exit; } /** * Restore a wallet from a backup: accepts the backup file's contents or a * bare private key. Replaces the current wallet and flags a re-sign, since * everything published must now carry the restored identity's stamp. */ public function handle_wallet_import() { if ( ! isset( $_POST['geistm_do_wallet_import'] ) || ! current_user_can( 'manage_options' ) ) { return; } if ( ! isset( $_POST['geistm_wallet_import_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['geistm_wallet_import_nonce'] ) ), 'geistm_wallet_import' ) ) { return; } // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Key material must not be altered; it is strictly validated in import_key(). $raw = trim( (string) wp_unslash( $_POST['geistm_wallet_backup'] ?? '' ) ); $key = $raw; if ( '' !== $raw && '{' === $raw[0] ) { $parsed = json_decode( $raw, true ); $key = is_array( $parsed ) && ! empty( $parsed['privateKey'] ) ? (string) $parsed['privateKey'] : ''; } $signer = new Geistm_Signer(); $address = '' !== $key ? $signer->import_key( $key ) : ''; if ( '' !== $address ) { // The restored identity must sign everything anew. update_option( 'geistm_manifest_needs_signing', '1', false ); geistm_ai_discovery_clear_all_caches(); } wp_safe_redirect( add_query_arg( array( 'page' => 'geistm-adnet', 'tab' => self::origin_tab( 'account' ), 'wallet-restore' => '' !== $address ? 'ok' : 'error', ), admin_url( 'options-general.php' ) ) ); exit; } /** * Count published posts/pages that already carry an Adnet slot (block or * pasted snippet). Cached briefly — the setup flow uses it to recognize a * manual placement as "done". * * @return int */ public static function count_slot_posts() { $count = get_transient( 'geistm_adnet_slots_found' ); if ( false === $count ) { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Cached in the transient below. $count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_status = 'publish' AND ( post_content LIKE '%adnet-entry%' OR post_content LIKE '%wp:geistm-adnet/adnet-slot%' )" ); set_transient( 'geistm_adnet_slots_found', $count, 5 * MINUTE_IN_SECONDS ); } return (int) $count; } /** * Handle manifest refresh from the Viewer tab (cache clear only, no signing). */ public function handle_refresh_manifest() { if ( ! isset( $_GET['refresh'] ) || '1' !== $_GET['refresh'] ) { return; } if ( ! isset( $_GET['page'] ) || 'geistm-adnet' !== $_GET['page'] ) { return; } if ( ! current_user_can( 'manage_options' ) ) { return; } if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'geistm_refresh_manifest' ) ) { return; } geistm_ai_discovery_clear_all_caches(); } /** * Handle explicit manifest signing from admin notice or Viewer tab. * This is the admin's deliberate approval — the security gate. */ public function handle_sign_manifest() { if ( ! isset( $_GET['geistm_sign_manifest'] ) || '1' !== $_GET['geistm_sign_manifest'] ) { return; } if ( ! isset( $_GET['page'] ) || 'geistm-adnet' !== $_GET['page'] ) { return; } if ( ! current_user_can( 'manage_options' ) ) { return; } if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'geistm_sign_manifest' ) ) { wp_die( 'Security check failed.' ); } // Clear caches first so the manifest reflects current content. delete_transient( 'geistm_ai_json_cache' ); delete_transient( 'geistm_llms_txt_cache' ); delete_transient( 'geistm_llms_full_cache' ); delete_transient( 'geistm_discovered_policies' ); // Sign the manifest — this is the admin-approved action. geistm_ai_discovery_sign_manifest(); // Redirect back to Viewer tab with success flag. wp_safe_redirect( add_query_arg( array( 'page' => 'geistm-adnet', 'tab' => 'viewer', 'signed' => '1', ), admin_url( 'options-general.php' ) ) ); exit; } /** * Basic identity extraction from page content (no AI fallback). * Handles both HTML tables (LabelValue) and * plain text patterns ("Legal Name: X"). * * @param string $html Combined about + contact page HTML content. * @return array Extracted fields. */ private static function extract_identity_basic( $html ) { $identity = array(); // Pattern 1: HTML table rows — LabelValue. $table_patterns = array( 'legalName' => '/Legal\s*Name/i', 'sector' => '/(?:Sector|Industry)/i', 'founded' => '/(?:Founded|Established)/i', 'headquarters' => '/(?:Headquarters|Location|Located)/i', ); // Extract all table row label/value pairs. if ( preg_match_all( '/]*>\s*]*>(.*?)<\/td>\s*]*>(.*?)<\/td>/is', $html, $rows, PREG_SET_ORDER ) ) { foreach ( $rows as $row ) { $label = wp_strip_all_tags( $row[1] ); $value = wp_strip_all_tags( $row[2] ); if ( empty( $value ) ) { continue; } foreach ( $table_patterns as $key => $pattern ) { if ( ! isset( $identity[ $key ] ) && preg_match( $pattern, $label ) ) { $identity[ $key ] = trim( $value ); } } } } // Pattern 2: Plain text — "Label: Value" or "Label — Value". $text = wp_strip_all_tags( str_replace( array( '', '', '' ), "\n", $html ) ); if ( ! isset( $identity['legalName'] ) && preg_match( '/Legal\s+Name\s*[:\-–—]\s*(.+)/i', $text, $m ) ) { $identity['legalName'] = trim( $m[1] ); } if ( ! isset( $identity['sector'] ) && preg_match( '/(?:Sector|Industry)\s*[:\-–—]\s*(.+)/i', $text, $m ) ) { $identity['sector'] = trim( $m[1] ); } if ( ! isset( $identity['founded'] ) && preg_match( '/(?:Founded|Established)\s*[:\-–—]?\s*((?:19|20)\d{2})/i', $text, $m ) ) { $identity['founded'] = $m[1]; } if ( ! isset( $identity['headquarters'] ) && preg_match( '/(?:Headquarters|Located|Location)\s*[:\-–—]\s*(.+)/i', $text, $m ) ) { $identity['headquarters'] = trim( $m[1] ); } return $identity; } /** * Sanitize license select field value. * * @param string $value The submitted license value. * @return string The sanitized license value. */ public function sanitize_license( $value ) { $allowed = array( 'cc-by-4.0', 'cc-by-sa-4.0', 'cc-by-nc-4.0', 'cc-by-nc-sa-4.0', 'cc0', 'all-rights-reserved' ); return in_array( $value, $allowed, true ) ? $value : 'all-rights-reserved'; } /** * Sanitize checkbox field value. * * @param mixed $value The submitted checkbox value. * @return string '1' or '0'. */ public function sanitize_checkbox( $value ) { return in_array( $value, array( '1', 1, true, 'on', 'yes' ), true ) ? '1' : '0'; } /** * Sanitize an Adnet ad format. * * @param string $value The submitted format. * @return string One of banner|square|card|qr (defaults to banner). */ public function sanitize_adnet_format( $value ) { $value = sanitize_key( $value ); return in_array( $value, array( 'banner', 'square', 'card', 'qr' ), true ) ? $value : 'banner'; } /** * Sanitize a positive integer setting (stored as string, minimum 1). * * @param mixed $value The submitted value. * @return string The sanitized integer as a string. */ public function sanitize_positive_int( $value ) { return (string) max( 1, absint( $value ) ); } /** * Handle Quick Start Guide dismissal. */ public function handle_dismiss_quickstart() { if ( ! isset( $_GET['geistm_dismiss_quickstart'] ) || '1' !== $_GET['geistm_dismiss_quickstart'] ) { return; } if ( ! current_user_can( 'manage_options' ) ) { return; } if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'geistm_dismiss_quickstart' ) ) { return; } update_option( 'geistm_quickstart_dismissed', '1', false ); wp_safe_redirect( add_query_arg( array( 'page' => 'geistm-adnet', 'tab' => 'viewer', ), admin_url( 'options-general.php' ) ) ); exit; } /** * Handle the Check & Connect button on the Adnet tab: submit the site to * the discovery index, request a fresh review, register with the matcher, * and cache the resulting status. Everything runs server-side. */ public function handle_network_connect() { if ( ! isset( $_POST['geistm_do_network_connect'] ) || ! current_user_can( 'manage_options' ) ) { return; } if ( ! isset( $_POST['geistm_network_connect_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['geistm_network_connect_nonce'] ) ), 'geistm_network_connect' ) ) { return; } Geistm_Network::connect(); wp_safe_redirect( add_query_arg( array( 'page' => 'geistm-adnet', 'tab' => 'adnet', 'network-checked' => '1', ), admin_url( 'options-general.php' ) ) ); exit; } /** * Persist a browser-observed matcher status (the fallback path when the * server itself can't reach the network host). */ public function handle_network_browser_status() { if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( null, 403 ); } check_ajax_referer( 'geistm_network_browser_status', 'nonce' ); $status = Geistm_Network::cached(); if ( is_array( $status ) ) { $status['exchange'] = array( 'reachable' => true, 'registered' => true, 'matches' => isset( $_POST['matches'] ) ? absint( $_POST['matches'] ) : 0, 'via' => 'browser', ); set_transient( Geistm_Network::CACHE_KEY, $status, 10 * MINUTE_IN_SECONDS ); } wp_send_json_success(); } /** * Enqueue admin CSS and JavaScript assets. * * @param string $hook The current admin page hook suffix. */ public function enqueue_assets( $hook ) { if ( 'settings_page_geistm-adnet' !== $hook ) { return; } wp_enqueue_style( 'geistm-admin', GEISTM_AI_DISCOVERY_URL . 'admin/assets/admin.css', array(), GEISTM_AI_DISCOVERY_VERSION ); wp_enqueue_script( 'geistm-admin-help', GEISTM_AI_DISCOVERY_URL . 'admin/assets/admin-help.js', array(), GEISTM_AI_DISCOVERY_VERSION, true ); wp_enqueue_script( 'geistm-setup-guide', GEISTM_AI_DISCOVERY_URL . 'admin/assets/setup-guide.js', array(), GEISTM_AI_DISCOVERY_VERSION, true ); // Browser-assisted network status fallback (no-ops unless the panel // flags the matcher row as unreachable from the server). wp_enqueue_script( 'geistm-network-status', GEISTM_AI_DISCOVERY_URL . 'admin/assets/network-status.js', array(), GEISTM_AI_DISCOVERY_VERSION, true ); wp_localize_script( 'geistm-network-status', 'geistmNetworkStatus', array( 'matchUrl' => Geistm_Network::exchange_url() . '/api/match/' . rawurlencode( Geistm_Network::domain() ), 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'geistm_network_browser_status' ), 'l10n' => array( /* translators: %d: number of matched ad campaigns. */ 'matchedViaBrowser' => __( 'Registered with the matcher — %d campaigns matched (checked from your browser). Your server can\'t reach the network host directly; status checks may lag, but ads still work — they load in your visitors\' browsers.', 'geistm-adnet' ), 'unreachableBoth' => __( 'The ad network host couldn\'t be reached from your server or your browser — it may be down. Try again in a few minutes.', 'geistm-adnet' ), ), ) ); } /** * Where publishers see Adnet campaigns and balances. A plain link the admin * opens in their browser — the plugin itself makes no request. * * @return string */ public static function campaigns_url() { return apply_filters( 'geistm_campaigns_url', 'https://epistery.com/#/s/0xAb1844b1B11ab3eEfA3Ca50180Bf8000880bEedd/0xBD6A0847D61D7C5bf0bcdeC85936DF872B6A719B' ); } /** * Render a help tooltip button + hidden tip content. * * @param string $text Help text (HTML allowed via wp_kses_post). * @param string $id Unique identifier for accessibility. */ public static function help_tip( $text, $id = '' ) { $id_attr = $id ? ' id="geistm-help-' . esc_attr( $id ) . '"' : ''; $desc_attr = $id ? ' aria-describedby="geistm-help-' . esc_attr( $id ) . '"' : ''; ?>
role="note">
__( 'Overview', 'geistm-adnet' ), 'adnet' => __( 'Adnet', 'geistm-adnet' ), 'identity' => __( 'Identity', 'geistm-adnet' ), 'content' => __( 'Content', 'geistm-adnet' ), 'policies' => __( 'Policies', 'geistm-adnet' ), 'account' => __( 'Account', 'geistm-adnet' ), ); ?>

v