1, 'cookie_days' => 90, 'consent_mode' => 'none', 'capture_selector' => '', 'track_clicks' => 1, 'count_visits' => 1, 'data_layer' => 0, 'geo_cities' => 0, 'maxmind_account' => '', 'maxmind_key' => '', 'live_view' => 1, 'ai_enabled' => 0, 'ai_spam' => 1, 'ai_confirm' => 0, 'team_lang' => '', 'remind_waiting' => 1, 'form_stats' => 1, 'woocommerce_orders' => 1, 'found_forms' => 1, 'purchase_orders' => 1, 'currency' => self::default_currency( $country ), 'phone_country' => isset( Phone::CALLING_CODES[ $country ] ) ? $country : '', 'telegram_enabled' => 0, 'telegram_token' => '', 'telegram_chat_id' => '', 'telegram_weekly' => 1, 'telegram_morning' => 1, 'form_alarm' => 1, 'smart_form' => 1, 'learn_style' => 1, 'mail_html' => 1, 'mail_from_name' => '', 'mail_from' => '', 'mail_reply_to' => '', 'mail_logo' => '', 'mail_color' => '#0e4a37', 'mail_signature' => '', 'notify_new' => 0, 'webhook_url' => '', 'webhook_secret' => '', 'digest_enabled' => 1, 'digest_recipients' => '', 'gads_conversion' => 'Dealroot - Won', 'msads_conversion' => 'Dealroot - Won', 'site_icons' => '', 'replies' => array(), 'retention_days' => 0, 'delete_on_uninstall' => 0, ); } return self::$defaults; } /** * All settings merged with defaults. * * @return array */ public static function all() { $saved = get_option( self::OPTION, array() ); $saved = is_array( $saved ) ? $saved : array(); // Before Microsoft had its own name, both exports used the Google Ads name. if ( ! isset( $saved['msads_conversion'] ) && isset( $saved['gads_conversion'] ) ) { $saved['msads_conversion'] = $saved['gads_conversion']; } return wp_parse_args( $saved, self::defaults() ); } /** * Single setting. * * @param string $key Setting key. * @param mixed $fallback Fallback. * @return mixed */ public static function get( $key, $fallback = null ) { $all = self::all(); return array_key_exists( $key, $all ) ? $all[ $key ] : $fallback; } /** * Sanitizes raw input from the settings form. * * @param array $input Raw values. * @return array */ public static function sanitize( $input ) { $input = is_array( $input ) ? $input : array(); $current = self::all(); $out = self::defaults(); $out['tracking_enabled'] = empty( $input['tracking_enabled'] ) ? 0 : 1; $out['cookie_days'] = max( 1, min( 730, absint( $input['cookie_days'] ?? 90 ) ) ); $out['consent_mode'] = in_array( $input['consent_mode'] ?? '', array( 'none', 'wp_consent_api', 'custom' ), true ) ? $input['consent_mode'] : 'none'; $out['capture_selector'] = mb_substr( trim( wp_strip_all_tags( $input['capture_selector'] ?? '' ) ), 0, 500 ); $out['track_clicks'] = empty( $input['track_clicks'] ) ? 0 : 1; $out['count_visits'] = empty( $input['count_visits'] ) ? 0 : 1; $out['data_layer'] = empty( $input['data_layer'] ) ? 0 : 1; $out['geo_cities'] = empty( $input['geo_cities'] ) ? 0 : 1; $out['maxmind_account'] = (string) preg_replace( '/\D/', '', (string) ( $input['maxmind_account'] ?? $current['maxmind_account'] ) ); $out['live_view'] = empty( $input['live_view'] ) ? 0 : 1; $out['ai_enabled'] = empty( $input['ai_enabled'] ) ? 0 : 1; $out['ai_spam'] = empty( $input['ai_spam'] ) ? 0 : 1; $out['ai_confirm'] = empty( $input['ai_confirm'] ) ? 0 : 1; $out['team_lang'] = isset( $input['team_lang'] ) && preg_match( '/^[a-z]{2}$/', (string) $input['team_lang'] ) ? (string) $input['team_lang'] : ''; $out['remind_waiting'] = empty( $input['remind_waiting'] ) ? 0 : 1; $out['form_stats'] = empty( $input['form_stats'] ) ? 0 : 1; $out['digest_enabled'] = empty( $input['digest_enabled'] ) ? 0 : 1; $out['digest_recipients'] = implode( ', ', array_filter( array_map( 'sanitize_email', preg_split( '/[\s,;]+/', (string) ( $input['digest_recipients'] ?? '' ) ) ), 'is_email' ) ); $out['found_forms'] = empty( $input['found_forms'] ) ? 0 : 1; $out['purchase_orders'] = empty( $input['purchase_orders'] ) ? 0 : 1; $out['woocommerce_orders'] = class_exists( '\\WooCommerce' ) ? ( empty( $input['woocommerce_orders'] ) ? 0 : 1 ) : $current['woocommerce_orders']; $out['currency'] = self::sanitize_currency( $input['currency'] ?? $out['currency'] ); $out['phone_country'] = isset( Phone::CALLING_CODES[ strtoupper( $input['phone_country'] ?? '' ) ] ) ? strtoupper( $input['phone_country'] ) : ''; $out['telegram_enabled'] = empty( $input['telegram_enabled'] ) ? 0 : 1; $out['telegram_chat_id'] = sanitize_text_field( $input['telegram_chat_id'] ?? '' ); $out['telegram_weekly'] = empty( $input['telegram_weekly'] ) ? 0 : 1; $out['telegram_morning'] = empty( $input['telegram_morning'] ) ? 0 : 1; $out['form_alarm'] = empty( $input['form_alarm'] ) ? 0 : 1; $out['smart_form'] = empty( $input['smart_form'] ) ? 0 : 1; $out['learn_style'] = empty( $input['learn_style'] ) ? 0 : 1; $out['mail_html'] = empty( $input['mail_html'] ) ? 0 : 1; $out['notify_new'] = empty( $input['notify_new'] ) ? 0 : 1; $out['mail_from_name'] = mb_substr( sanitize_text_field( (string) ( $input['mail_from_name'] ?? '' ) ), 0, 80 ); $out['mail_from'] = sanitize_email( (string) ( $input['mail_from'] ?? '' ) ); $out['mail_reply_to'] = sanitize_email( (string) ( $input['mail_reply_to'] ?? '' ) ); $out['mail_logo'] = esc_url_raw( trim( (string) ( $input['mail_logo'] ?? '' ) ), array( 'https', 'http' ) ); $out['mail_color'] = sanitize_hex_color( (string) ( $input['mail_color'] ?? '' ) ) ? sanitize_hex_color( (string) $input['mail_color'] ) : '#0e4a37'; $out['mail_signature'] = mb_substr( sanitize_textarea_field( (string) ( $input['mail_signature'] ?? '' ) ), 0, 500 ); $out['webhook_url'] = esc_url_raw( trim( $input['webhook_url'] ?? '' ), array( 'https', 'http' ) ); $out['gads_conversion'] = self::conversion_name( $input['gads_conversion'] ?? '' ); $out['msads_conversion'] = self::conversion_name( $input['msads_conversion'] ?? $current['gads_conversion'] ); $out['site_icons'] = empty( $input['site_icons'] ) ? 0 : 1; $out['replies'] = isset( $input['replies'] ) ? Replies::sanitize( $input['replies'] ) : $current['replies']; $out['retention_days'] = min( 3650, absint( $input['retention_days'] ?? 0 ) ); $out['delete_on_uninstall'] = empty( $input['delete_on_uninstall'] ) ? 0 : 1; // Away mode is kept apart from the settings, with its own start date. Away::from_settings( $input ); if ( ! empty( $input['forget_style'] ) || ! $out['learn_style'] ) { Reply_Style::forget(); } // Secret fields show a mask; an unchanged mask means "keep the stored value". foreach ( self::SECRETS as $key ) { $value = trim( sanitize_text_field( $input[ $key ] ?? '' ) ); $out[ $key ] = ( '' !== $value && self::masked_token( $current[ $key ] ) === $value ) ? $current[ $key ] : $value; } // Advice written in the language before is written again. if ( (string) ( $current['team_lang'] ?? '' ) !== $out['team_lang'] ) { delete_option( Ai_Coach::OPTION ); } return $out; } /** * Name of a conversion in an ad platform. An empty field falls back to the default name, * so the export never has rows without a conversion. * * @param string $name Raw name. * @return string */ private static function conversion_name( $name ) { $name = mb_substr( trim( sanitize_text_field( (string) $name ) ), 0, 100 ); return '' !== $name ? $name : 'Dealroot - Won'; } /** * Uppercase ISO 4217 code. * * @param string $code Raw code. * @return string */ public static function sanitize_currency( $code ) { $code = strtoupper( preg_replace( '/[^A-Za-z]/', '', (string) $code ) ); return 3 === strlen( $code ) ? $code : 'USD'; } /** * Store currency from WooCommerce, otherwise the currency of the site's country. * * @param string $country ISO country code. * @return string */ public static function default_currency( $country ) { if ( function_exists( 'get_woocommerce_currency' ) ) { return self::sanitize_currency( get_woocommerce_currency() ); } if ( class_exists( '\NumberFormatter' ) && preg_match( '/^[A-Z]{2}$/', $country ) ) { $formatter = new \NumberFormatter( 'en_' . $country, \NumberFormatter::CURRENCY ); $code = $formatter->getTextAttribute( \NumberFormatter::CURRENCY_CODE ); if ( is_string( $code ) && preg_match( '/^[A-Z]{3}$/', $code ) && 'XXX' !== $code ) { return $code; } } return 'USD'; } /** * Display value that keeps a secret out of the page source. * * @param string $token Secret. * @return string */ public static function masked_token( $token ) { if ( '' === $token ) { return ''; } return str_repeat( '•', 8 ) . substr( $token, -4 ); } }