isset( $cfg['forms'] ) && is_array( $cfg['forms'] ) ? $cfg['forms'] : array(), 'builtin' => array_merge( array( 'newsletter_signup' => 1, 'push_subscribe' => 1, 'course_enroll' => 1, 'lesson_complete' => 1, 'user_register' => 0, 'product_request' => 1, 'waitlist_signup' => 1, 'product_request_item' => 0 ), isset( $cfg['builtin'] ) && is_array( $cfg['builtin'] ) ? array_map( 'intval', $cfg['builtin'] ) : array() ), 'builtin_values' => isset( $cfg['builtin_values'] ) && is_array( $cfg['builtin_values'] ) ? array_map( 'floatval', $cfg['builtin_values'] ) : array(), ); } public static function save_config( array $forms, array $builtin, array $builtin_values = array() ) { $cur = self::config(); $clean = array( 'forms' => array(), 'builtin' => array(), 'builtin_values' => array() ); foreach ( $forms as $key => $f ) { $key = sanitize_key( (string) $key ); if ( '' === $key ) { continue; } $prev = isset( $cur['forms'][ $key ] ) ? $cur['forms'][ $key ] : array(); $clean['forms'][ $key ] = array( 'on' => empty( $f['on'] ) ? 0 : 1, 'label' => sanitize_text_field( (string) ( $f['label'] ?? ( $prev['label'] ?? '' ) ) ), 'context' => sanitize_textarea_field( (string) ( $f['context'] ?? ( $prev['context'] ?? '' ) ) ), 'value' => isset( $f['value'] ) ? max( 0, (float) $f['value'] ) : (float) ( $prev['value'] ?? 0 ), ); } foreach ( self::builtin_keys() as $b ) { $clean['builtin'][ $b ] = empty( $builtin[ $b ] ) ? 0 : 1; $v = $builtin_values[ $b ] ?? ( $builtin[ $b . '_value' ] ?? ( $cur['builtin_values'][ $b ] ?? 0 ) ); $clean['builtin_values'][ $b ] = max( 0, (float) $v ); } $clean['gravity_attribution'] = empty( $builtin['gravity_attribution'] ) ? 0 : 1; update_option( self::CONFIG_OPT, $clean, false ); } public static function builtin_keys() { return array( 'newsletter_signup', 'push_subscribe', 'course_enroll', 'lesson_complete', 'user_register', 'product_request', 'waitlist_signup', 'product_request_item', ); } public static function goal_label( $goal_key, $form_id = 0 ) { $goal_key = (string) $goal_key; if ( 'form_submit_gravity' === $goal_key && (int) $form_id > 0 ) { $cfg = self::config(); $key = sanitize_key( 'gravity_' . (int) $form_id ); $lbl = (string) ( $cfg['forms'][ $key ]['label'] ?? '' ); return '' !== $lbl ? $lbl : sprintf( __( 'Gravity form #%d', 'ai-eshop-optimizer' ), (int) $form_id ); } $map = array( 'newsletter_signup' => __( 'Newsletter signup', 'ai-eshop-optimizer' ), 'push_subscribe' => __( 'Web push subscription', 'ai-eshop-optimizer' ), 'course_enroll' => __( 'Course enrolment', 'ai-eshop-optimizer' ), 'lesson_complete' => __( 'Lesson completed', 'ai-eshop-optimizer' ), 'user_register' => __( 'Account registration', 'ai-eshop-optimizer' ), 'product_request' => __( 'Quote request', 'ai-eshop-optimizer' ), 'product_request_item' => __( 'Product added to a quote', 'ai-eshop-optimizer' ), 'waitlist_signup' => __( 'Back-in-stock request', 'ai-eshop-optimizer' ), 'form_submit_gravity' => __( 'Form submission', 'ai-eshop-optimizer' ), ); return isset( $map[ $goal_key ] ) ? $map[ $goal_key ] : $goal_key; } public static function channel( $a = null ) { return self::channel_guess( is_array( $a ) ? array_merge( self::attribution(), $a ) : self::attribution() ); } public static function goal_value( $goal_key, $meta = array() ) { $cfg = self::config(); $goal_key = (string) $goal_key; if ( 'form_submit_gravity' === $goal_key ) { $fid = (int) ( is_array( $meta ) ? ( $meta['form'] ?? 0 ) : 0 ); $key = sanitize_key( 'gravity_' . $fid ); return max( 0.0, (float) ( $cfg['forms'][ $key ]['value'] ?? 0 ) ); } return max( 0.0, (float) ( $cfg['builtin_values'][ $goal_key ] ?? 0 ) ); } public static function gravity_attribution_on() { $raw = get_option( self::CONFIG_OPT, array() ); return is_array( $raw ) && ! empty( $raw['gravity_attribution'] ); } private static function first_touch_on() { if ( self::gravity_attribution_on() ) { return true; } $cfg = self::config(); foreach ( $cfg['builtin'] as $on ) { if ( ! empty( $on ) ) { return true; } } foreach ( $cfg['forms'] as $f ) { if ( ! empty( $f['on'] ) ) { return true; } } return false; } private static function form_on( $provider, $form_id ) { $cfg = self::config(); $key = sanitize_key( $provider . '_' . (int) $form_id ); return ! empty( $cfg['forms'][ $key ]['on'] ); } private static function builtin_on( $key ) { $cfg = self::config(); return ! empty( $cfg['builtin'][ $key ] ); } public static function available_forms() { $out = array(); if ( class_exists( 'GFAPI' ) ) { foreach ( (array) GFAPI::get_forms( true ) as $f ) { if ( isset( $f['id'] ) ) { $out[ 'gravity_' . (int) $f['id'] ] = array( 'provider' => 'gravity', 'form_id' => (int) $f['id'], 'title' => (string) ( $f['title'] ?? ( '#' . $f['id'] ) ) ); } } } return $out; } public static function register() { static $done = false; if ( $done ) { return; } $done = true; if ( class_exists( 'GFForms' ) ) { add_action( 'gform_after_submission', static function ( $entry, $form ) { $id = isset( $form['id'] ) ? (int) $form['id'] : 0; if ( self::form_on( 'gravity', $id ) ) { $attr = array_filter( array_intersect_key( self::attribution(), array( 'utm_source' => 1, 'utm_medium' => 1, 'utm_campaign' => 1 ) ) ); self::record( 'form_submit_gravity', array_merge( array( 'form' => $id, 'entry' => (int) rgar( $entry, 'id' ) ), $attr ) ); } }, 10, 2 ); foreach ( array( 'utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term' ) as $p ) { add_filter( "gform_field_value_{$p}", static function ( $value ) use ( $p ) { $a = self::attribution(); return '' !== $a[ $p ] ? $a[ $p ] : $value; } ); } add_filter( 'gform_field_value_handl_landing_page', static function ( $value ) { $a = self::attribution(); return '' !== $a['landing_page'] ? $a['landing_page'] : $value; } ); add_filter( 'gform_field_value_handl_original_ref', static function ( $value ) { $a = self::attribution(); return '' !== $a['referrer'] ? $a['referrer'] : $value; } ); add_filter( 'gform_notification', array( __CLASS__, 'append_attribution_to_notification' ), 10, 3 ); add_filter( 'gform_hubspot_submission_data', static function ( $submission_data ) { if ( self::gravity_attribution_on() && isset( $submission_data['context']['pageUri'] ) && is_string( $submission_data['context']['pageUri'] ) ) { $parts = explode( '?', $submission_data['context']['pageUri'] ); $submission_data['context']['pageUri'] = $parts[0]; } return $submission_data; } ); } if ( self::first_touch_on() ) { add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_first_touch' ), 30 ); add_action( 'template_redirect', array( __CLASS__, 'server_first_touch' ), 1 ); add_filter( 'rooster/third_party/handles', static function ( $handles ) { $handles = is_array( $handles ) ? $handles : array(); $handles['aieo-first-touch'] = array( 'trigger' => 'human', 'consent' => 'analytics' ); return $handles; } ); add_filter( 'rocket_excluded_inline_js_content', static function ( $excluded ) { $excluded[] = 'aieo_attr'; return $excluded; } ); add_filter( 'rocket_delay_js_exclusions', static function ( $excluded ) { $excluded[] = 'aieo-first-touch'; $excluded[] = 'aieo_attr'; return $excluded; } ); } if ( defined( 'NEWSLETTER_VERSION' ) || class_exists( 'Newsletter' ) ) { add_action( 'newsletter_user_confirmed', static function () { if ( self::builtin_on( 'newsletter_signup' ) ) { self::record( 'newsletter_signup' ); } } ); } if ( defined( 'LEARNDASH_VERSION' ) ) { add_action( 'learndash_update_course_access', static function ( $user_id, $course_id, $access_list, $remove ) { if ( ! $remove && self::builtin_on( 'course_enroll' ) ) { self::record( 'course_enroll', array( 'course' => (int) $course_id ) ); } }, 10, 4 ); add_action( 'learndash_lesson_completed', static function ( $data ) { if ( self::builtin_on( 'lesson_complete' ) ) { $lesson = isset( $data['lesson'] ) && is_object( $data['lesson'] ) ? (int) $data['lesson']->ID : 0; self::record( 'lesson_complete', array( 'lesson' => $lesson ) ); } } ); } add_action( 'user_register', static function () { if ( self::builtin_on( 'user_register' ) ) { self::record( 'user_register' ); } } ); add_action( 'aieo_product_request_created', static function ( $request_id, $context = array() ) { if ( self::builtin_on( 'product_request' ) ) { self::record( 'product_request', array( 'request' => (int) $request_id, 'product' => (int) ( is_array( $context ) ? ( $context['product_id'] ?? 0 ) : 0 ), ) ); } }, 10, 2 ); add_action( 'aieo_product_request_item_added', static function ( $request_id, $product_id = 0 ) { if ( self::builtin_on( 'product_request_item' ) ) { self::record( 'product_request_item', array( 'request' => (int) $request_id, 'product' => (int) $product_id ) ); } }, 10, 2 ); add_action( 'aieo_waitlist_subscribed', static function ( $row_id, $email = '', $product_id = 0, $variation_id = 0 ) { if ( self::builtin_on( 'waitlist_signup' ) ) { self::record( 'waitlist_signup', array( 'waitlist' => (int) $row_id, 'product' => (int) $product_id, 'variation' => (int) $variation_id ) ); } }, 10, 4 ); add_action( 'aieo_goal', array( __CLASS__, 'record' ), 10, 2 ); } public static function server_first_touch() { if ( is_admin() || wp_doing_ajax() || wp_doing_cron() || headers_sent() || is_feed() || is_robots() ) { return; } if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { return; } if ( empty( $_SERVER['REQUEST_METHOD'] ) || 'GET' !== $_SERVER['REQUEST_METHOD'] ) { return; } if ( class_exists( 'AIEO_Session_Tracker' ) && method_exists( 'AIEO_Session_Tracker', 'consent_required_rule' ) && AIEO_Session_Tracker::consent_required_rule() ) { return; } if ( class_exists( 'AIEO_Consent' ) && method_exists( 'AIEO_Consent', 'banner_active' ) && AIEO_Consent::banner_active() && ! ( AIEO_Consent::has_choice() && AIEO_Consent::allows( 'analytics' ) ) ) { return; } $query = isset( $_SERVER['QUERY_STRING'] ) ? (string) wp_unslash( $_SERVER['QUERY_STRING'] ) : ''; if ( '' === $query || ! preg_match( '/(?:^|&)(utm_[a-z]+|gclid|gbraid|wbraid|fbclid|msclkid|ttclid|dclid|gad_source|srsltid)=/i', $query ) ) { return; } $referrer = self::external_referrer( isset( $_SERVER['HTTP_REFERER'] ) ? (string) wp_unslash( $_SERVER['HTTP_REFERER'] ) : '' ); $internal = isset( $_SERVER['HTTP_REFERER'] ) && '' !== $_SERVER['HTTP_REFERER'] && '' === $referrer; if ( $internal ) { return; } $landing = home_url( esc_url_raw( wp_unslash( isset( $_SERVER['REQUEST_URI'] ) ? (string) $_SERVER['REQUEST_URI'] : '/' ) ) ); $value = rawurlencode( (string) wp_json_encode( array( 'r' => substr( $referrer, 0, 500 ), 'l' => substr( $landing, 0, 800 ) ) ) ); setcookie( 'aieo_attr', $value, array( 'expires' => time() + 7776000, 'path' => '/', 'secure' => is_ssl(), 'httponly' => false, 'samesite' => 'Lax' ) ); $_COOKIE['aieo_attr'] = $value; } public static function enqueue_first_touch() { $dir = dirname( __DIR__ ) . '/'; $rel = 'assets/js/aieo-first-touch.js'; if ( ! file_exists( $dir . $rel ) ) { return; } $deps = wp_script_is( 'aieo-session-loader', 'registered' ) ? array( 'aieo-session-loader' ) : array(); wp_enqueue_script( 'aieo-first-touch', plugins_url( $rel, $dir . 'plugin.php' ), $deps, (string) filemtime( $dir . $rel ), array( 'in_footer' => true, 'strategy' => 'defer' ) ); } private static function external_referrer( $url ) { $url = trim( (string) $url ); if ( '' === $url ) { return ''; } $host = preg_replace( '/^www\./', '', strtolower( (string) wp_parse_url( $url, PHP_URL_HOST ) ) ); $home = preg_replace( '/^www\./', '', strtolower( (string) wp_parse_url( home_url(), PHP_URL_HOST ) ) ); return ( '' === $host || $host === $home ) ? '' : $url; } public static function attribution() { static $memo = null; if ( null !== $memo ) { return $memo; } $out = array( 'utm_source' => '', 'utm_medium' => '', 'utm_campaign' => '', 'utm_content' => '', 'utm_term' => '', 'gclid' => '', 'fbclid' => '', 'landing_page' => '', 'referrer' => '', ); $st = array( 'landing' => '', 'referrer' => '' ); $session = ''; foreach ( array( 'aieo_attr_sid', 'aieo_session_id' ) as $c ) { if ( isset( $_COOKIE[ $c ] ) && '' !== $_COOKIE[ $c ] ) { $session = sanitize_text_field( wp_unslash( $_COOKIE[ $c ] ) ); break; } } if ( '' !== $session ) { global $wpdb; $table = $wpdb->prefix . 'aieo_sessions_statics'; if ( (string) $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ) === $table ) { $row = $wpdb->get_row( $wpdb->prepare( "SELECT landing_page, referrer FROM `{$table}` WHERE session_id = %s LIMIT 1", $session ), ARRAY_A ); if ( $row ) { $st['landing'] = (string) $row['landing_page']; $st['referrer'] = (string) $row['referrer']; } } } $ft = array( 'landing' => '', 'referrer' => '' ); if ( isset( $_COOKIE['aieo_attr'] ) ) { $c = json_decode( urldecode( (string) wp_unslash( $_COOKIE['aieo_attr'] ) ), true ); if ( is_array( $c ) ) { if ( ! empty( $c['l'] ) && is_string( $c['l'] ) ) { $ft['landing'] = esc_url_raw( $c['l'] ); } if ( ! empty( $c['r'] ) && is_string( $c['r'] ) ) { $ft['referrer'] = esc_url_raw( $c['r'] ); } } } if ( '' !== $ft['landing'] ) { $win = $ft; $other = $st; } else { $win = $st; $other = $ft; } $out['landing_page'] = '' !== $win['landing'] ? $win['landing'] : $other['landing']; $out['referrer'] = '' !== $win['referrer'] ? $win['referrer'] : $other['referrer']; $query = (string) wp_parse_url( $out['landing_page'], PHP_URL_QUERY ); if ( '' !== $query ) { parse_str( $query, $params ); foreach ( array( 'utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term', 'gclid', 'fbclid' ) as $p ) { if ( isset( $params[ $p ] ) && is_string( $params[ $p ] ) ) { $out[ $p ] = sanitize_text_field( $params[ $p ] ); } } } foreach ( array( 'utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term', 'gclid', 'fbclid' ) as $p ) { if ( '' === $out[ $p ] && isset( $_COOKIE[ $p ] ) && '' !== $_COOKIE[ $p ] ) { $out[ $p ] = sanitize_text_field( wp_unslash( $_COOKIE[ $p ] ) ); } } if ( '' === $out['landing_page'] && isset( $_COOKIE['handl_landing_page'] ) ) { $out['landing_page'] = esc_url_raw( wp_unslash( $_COOKIE['handl_landing_page'] ) ); } if ( '' === $out['referrer'] && isset( $_COOKIE['handl_original_ref'] ) ) { $out['referrer'] = esc_url_raw( wp_unslash( $_COOKIE['handl_original_ref'] ) ); } foreach ( array( 'utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term', 'gclid', 'fbclid' ) as $p ) { if ( '' === $out[ $p ] && isset( $_GET[ $p ] ) && is_string( $_GET[ $p ] ) ) { $out[ $p ] = sanitize_text_field( wp_unslash( $_GET[ $p ] ) ); } } $out['referrer'] = self::external_referrer( $out['referrer'] ); if ( class_exists( 'AIEO_Url_Redaction' ) ) { $out['landing_page'] = AIEO_Url_Redaction::clean( $out['landing_page'] ); $out['referrer'] = AIEO_Url_Redaction::clean( $out['referrer'] ); } $memo = $out; return $memo; } public static function append_attribution_to_notification( $notification, $form, $entry ) { if ( ! self::gravity_attribution_on() || ! class_exists( 'AIEO_Attribution_Block' ) ) { return $notification; } if ( ! self::notification_is_staff( $notification ) ) { return $notification; } $entry_a = self::entry_attribution( $form, $entry ); $a = self::attribution(); foreach ( $entry_a as $k => $v ) { if ( 'landing_page' === $k || 'referrer' === $k ) { if ( '' === (string) $a[ $k ] ) { $a[ $k ] = $v; } } else { $a[ $k ] = $v; } } $a['referrer'] = self::external_referrer( $a['referrer'] ); foreach ( $a as $k => $v ) { $a[ $k ] = str_replace( array( '{', '}' ), '', (string) $v ); } $rows = array( __( 'Channel', 'ai-eshop-optimizer' ) => AIEO_Attribution_Block::channel_label( self::channel_guess( $a ) ) ); $rows += AIEO_Attribution_Block::utm_rows( $a ); $rows['gclid'] = isset( $a['gclid'] ) && '' !== $a['gclid'] ? $a['gclid'] : '—'; $rows[ __( 'Landing page', 'ai-eshop-optimizer' ) ] = '' !== $a['landing_page'] ? $a['landing_page'] : '—'; $rows[ __( 'Original referrer', 'ai-eshop-optimizer' ) ] = '' !== $a['referrer'] ? $a['referrer'] : '—'; $ip = is_array( $entry ) && ! empty( $entry['ip'] ) ? sanitize_text_field( (string) $entry['ip'] ) : ( isset( $a['ip'] ) ? $a['ip'] : '' ); $rows['IP'] = '' !== $ip ? $ip : '—'; $title = __( 'Campaign attribution', 'ai-eshop-optimizer' ); $fmt = isset( $notification['message_format'] ) ? strtolower( (string) $notification['message_format'] ) : 'html'; $block = ( 'text' === $fmt ) ? "\n\n" . AIEO_Attribution_Block::render_plain( $title, $rows ) : AIEO_Attribution_Block::render_html( $title, $rows ); $notification['message'] = (string) ( $notification['message'] ?? '' ) . $block; return $notification; } private static function notification_is_staff( $notification ) { $to_type = isset( $notification['toType'] ) ? (string) $notification['toType'] : 'email'; if ( 'field' === $to_type || ! empty( $notification['toField'] ) ) { return false; } $candidates = array( isset( $notification['to'] ) ? (string) $notification['to'] : '' ); if ( 'routing' === $to_type ) { if ( empty( $notification['routing'] ) || ! is_array( $notification['routing'] ) ) { return false; } foreach ( $notification['routing'] as $rule ) { $candidates[] = isset( $rule['email'] ) ? (string) $rule['email'] : ''; } } foreach ( $candidates as $c ) { $c = trim( $c ); if ( '' !== $c && ctype_digit( $c ) ) { return false; } if ( false !== strpos( str_replace( '{admin_email}', '', $c ), '{' ) ) { return false; } } return true; } public static function entry_attribution( $form, $entry ) { $map = array( 'utm_source' => 'utm_source', 'utm_medium' => 'utm_medium', 'utm_campaign' => 'utm_campaign', 'utm_content' => 'utm_content', 'utm_term' => 'utm_term', 'gclid' => 'gclid', 'fbclid' => 'fbclid', 'handl_landing_page' => 'landing_page', 'handl_original_ref' => 'referrer', 'handl_ip' => 'ip', ); $out = array(); if ( empty( $form['fields'] ) || ! is_array( $entry ) ) { return $out; } foreach ( $form['fields'] as $field ) { $param = is_object( $field ) ? (string) ( $field->inputName ?? '' ) : (string) ( $field['inputName'] ?? '' ); if ( '' === $param || ! isset( $map[ $param ] ) ) { continue; } $id = is_object( $field ) ? (string) $field->id : (string) ( $field['id'] ?? '' ); $val = isset( $entry[ $id ] ) ? trim( (string) $entry[ $id ] ) : ''; if ( '' !== $val ) { $out[ $map[ $param ] ] = sanitize_text_field( $val ); } } return $out; } private static function channel_guess( array $a ) { $source = strtolower( $a['utm_source'] ); $medium = strtolower( $a['utm_medium'] ); if ( false !== strpos( $medium, 'cpc' ) || false !== strpos( $medium, 'paid' ) || false !== strpos( $medium, 'ppc' ) ) { if ( false !== strpos( $source, 'google' ) ) { return 'paid_google'; } if ( false !== strpos( $source, 'facebook' ) || false !== strpos( $source, 'instagram' ) || false !== strpos( $source, 'meta' ) || false !== strpos( $source, 'fb' ) ) { return 'paid_facebook'; } if ( false !== strpos( $source, 'tiktok' ) ) { return 'paid_tiktok'; } if ( false !== strpos( $source, 'bing' ) || false !== strpos( $source, 'microsoft' ) ) { return 'paid_microsoft'; } return '' !== $source ? 'paid_' . preg_replace( '/[^a-z0-9]+/', '', $source ) : 'paid'; } if ( '' !== (string) ( isset( $a['gclid'] ) ? $a['gclid'] : '' ) ) { return 'paid_google'; } if ( false !== strpos( $medium, 'email' ) || false !== strpos( $source, 'newsletter' ) || false !== strpos( $source, 'klaviyo' ) ) { return 'newsletter'; } if ( false !== strpos( $medium, 'social' ) || in_array( $source, array( 'facebook', 'instagram', 'tiktok', 'x', 'twitter', 'linkedin' ), true ) ) { return 'social'; } if ( '' !== (string) ( isset( $a['fbclid'] ) ? $a['fbclid'] : '' ) ) { return 'social'; } if ( '' !== $source || '' !== $medium || '' !== $a['utm_campaign'] ) { return trim( $source . ' / ' . $medium, ' /' ); } $ref_host = strtolower( (string) wp_parse_url( $a['referrer'], PHP_URL_HOST ) ); if ( '' !== $ref_host ) { foreach ( array( 'google.', 'bing.', 'yahoo.', 'duckduckgo.', 'ecosia.' ) as $se ) { if ( false !== strpos( $ref_host, $se ) ) { return 'organic_search'; } } foreach ( array( 'facebook.', 'instagram.', 'tiktok.', 'twitter.', 'x.com', 'linkedin.', 'lnkd.in', 'pinterest.', 'youtube.', 'youtu.be', 'reddit.' ) as $social_host ) { if ( false !== strpos( $ref_host, $social_host ) ) { return 'social'; } } if ( 't.co' === $ref_host ) { return 'social'; } $site_host = strtolower( (string) wp_parse_url( home_url(), PHP_URL_HOST ) ); if ( $ref_host !== $site_host ) { return 'referral'; } } return 'direct'; } public static function record( $goal_key, $meta = array() ) { global $wpdb; $goal_key = sanitize_key( str_replace( ':', '_', (string) $goal_key ) ); if ( '' === $goal_key ) { return; } $session = ''; foreach ( array( 'aieo_attr_sid', 'aieo_session_id' ) as $c ) { if ( isset( $_COOKIE[ $c ] ) && '' !== $_COOKIE[ $c ] ) { $session = sanitize_text_field( wp_unslash( $_COOKIE[ $c ] ) ); break; } } if ( '' === $session ) { return; } $t = $wpdb->prefix . 'aieo_sessions'; if ( (string) $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $t ) ) !== $t ) { return; } $page = (string) wp_get_referer(); if ( '' === $page && isset( $_SERVER['HTTP_REFERER'] ) ) { $page = esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) ); } if ( class_exists( 'AIEO_Url_Redaction' ) ) { $page = AIEO_Url_Redaction::clean( $page ); } $page = substr( $page, 0, 2048 ); $now_ms = (int) round( microtime( true ) * 1000 ); $data = wp_json_encode( array_merge( array( 'goal_key' => $goal_key ), is_array( $meta ) ? array_map( 'strval', array_filter( $meta, 'is_scalar' ) ) : array() ) ); $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO `{$t}` (event_id, session_id, user_id, event_type, event_data, page_url, timestamp) VALUES (%s, %s, %d, 'goal', %s, %s, %d)", substr( md5( $session . '|' . $now_ms . '|goal|' . $goal_key . '|' . wp_rand() ), 0, 36 ), $session, get_current_user_id(), $data, $page, $now_ms ) ); do_action( 'aieo_goal_recorded', $goal_key, is_array( $meta ) ? $meta : array(), array( 'session_id' => $session, 'page_url' => $page, 'attribution' => self::attribution(), 'ip' => isset( $_SERVER['REMOTE_ADDR'] ) ? (string) $_SERVER['REMOTE_ADDR'] : '', 'ua' => isset( $_SERVER['HTTP_USER_AGENT'] ) ? (string) $_SERVER['HTTP_USER_AGENT'] : '', ) ); } public static function form_substance( $provider, $form_id ) { $form_id = (int) $form_id; $lines = array(); if ( 'gravity' === $provider && class_exists( 'GFAPI' ) ) { $f = GFAPI::get_form( $form_id ); if ( is_array( $f ) ) { $lines[] = 'FORM TITLE: ' . (string) ( $f['title'] ?? '' ); if ( ! empty( $f['description'] ) ) { $lines[] = 'FORM DESCRIPTION: ' . (string) $f['description']; } foreach ( (array) ( $f['fields'] ?? array() ) as $fl ) { $label = is_object( $fl ) ? (string) ( $fl->label ?? '' ) : (string) ( $fl['label'] ?? '' ); $type = is_object( $fl ) ? (string) ( $fl->type ?? '' ) : (string) ( $fl['type'] ?? '' ); if ( '' !== $label ) { $lines[] = "FIELD: {$label} ({$type})"; } } if ( ! empty( $f['button']['text'] ) ) { $lines[] = 'SUBMIT BUTTON: ' . (string) $f['button']['text']; } } } return implode( "\n", array_filter( $lines ) ); } public static function analyze_form( $provider, $form_id ) { if ( ! class_exists( 'AIEO_SEO_AI_Assist' ) ) { require_once AIEO_DIR . 'includes/seo/class-aieo-seo-ai-assist.php'; } if ( ! AIEO_SEO_AI_Assist::available() ) { return new WP_Error( 'aieo_ai_unavailable', __( 'AI is not configured — paste the Rooster Content Connector token first.', 'ai-eshop-optimizer' ) ); } $substance = self::form_substance( $provider, $form_id ); if ( '' === $substance ) { return new WP_Error( 'aieo_form_empty', __( 'Could not read that form’s fields.', 'ai-eshop-optimizer' ) ); } $api = AI_eShop_Chat_Anthropic_API::get_instance(); $resp = $api->chat_completion( array( array( 'role' => 'user', 'content' => "Analyse this web form and identify the CONVERSION GOAL it captures.\n\n{$substance}\n\n" . 'Return ONLY minified JSON: {"label": "<2-4 word goal name>", "context": "<1-2 sentences: what the visitor is trying to achieve by submitting, and what business outcome a submission represents. Same language as the form.>"}', ) ), array( 'system' => 'You analyse web forms to identify conversion goals for SEO copywriting. Be concrete about visitor intent. Return only the JSON object.', 'max_tokens' => 300, 'temperature' => 0.3, 'source' => AIEO_AI_Sources::SEO_GOAL, ) ); if ( is_wp_error( $resp ) ) { return $resp; } $text = ''; if ( isset( $resp['content'] ) && is_array( $resp['content'] ) ) { foreach ( $resp['content'] as $blk ) { if ( is_array( $blk ) && 'text' === ( $blk['type'] ?? '' ) && isset( $blk['text'] ) ) { $text .= (string) $blk['text']; } } } $json = json_decode( trim( preg_replace( '/^```(?:json)?|```$/m', '', $text ) ), true ); if ( ! is_array( $json ) || empty( $json['context'] ) ) { return new WP_Error( 'aieo_ai_parse', __( 'The AI returned an unexpected format.', 'ai-eshop-optimizer' ) ); } $label = sanitize_text_field( (string) ( $json['label'] ?? '' ) ); $context = sanitize_textarea_field( (string) $json['context'] ); $cfg = self::config(); $key = sanitize_key( $provider . '_' . (int) $form_id ); if ( ! isset( $cfg['forms'][ $key ] ) ) { $cfg['forms'][ $key ] = array( 'on' => 0, 'label' => '', 'context' => '' ); } $cfg['forms'][ $key ]['label'] = $label; $cfg['forms'][ $key ]['context'] = $context; update_option( self::CONFIG_OPT, $cfg, false ); return array( 'label' => $label, 'context' => $context ); } public static function goal_context_for( $post ) { if ( ! $post instanceof WP_Post ) { return array(); } $cfg = self::config(); $content = (string) $post->post_content; $out = array(); foreach ( $cfg['forms'] as $key => $f ) { if ( empty( $f['on'] ) || '' === (string) ( $f['context'] ?? '' ) ) { continue; } if ( ! preg_match( '/^gravity_(\d+)$/', (string) $key, $m ) ) { continue; } $id = (int) $m[1]; $present = (bool) preg_match( '/\[gravityforms?\s[^\]]*id=["\']?' . $id . '["\']?/', $content ) || false !== strpos( $content, '"formId":"' . $id . '"' ) || false !== strpos( $content, '"formId":' . $id ); if ( $present ) { $label = '' !== (string) ( $f['label'] ?? '' ) ? $f['label'] : $key; $out[] = $label . ': ' . $f['context']; } } return $out; } } }