'array', 'sanitize_callback' => array( $this, 'sanitize_options' ), 'default' => Airgentic::default_options(), ) ); add_settings_section( 'airgentic_main', __( 'Configuration', 'airgentic' ), array( $this, 'render_section_info' ), self::OPTION_GROUP ); add_settings_field( 'account_id', __( 'Account ID', 'airgentic' ) . ' *', array( $this, 'render_text_field' ), self::OPTION_GROUP, 'airgentic_main', array( 'label_for' => 'account_id', 'description' => __( 'Required. Your Airgentic account identifier.', 'airgentic' ), ) ); add_settings_field( 'service_id', __( 'Service ID', 'airgentic' ) . ' *', array( $this, 'render_text_field' ), self::OPTION_GROUP, 'airgentic_main', array( 'label_for' => 'service_id', 'description' => __( 'Required. Your service identifier (typically "website").', 'airgentic' ), ) ); add_settings_field( 'enable_on_front', __( 'Enable on Front-end', 'airgentic' ), array( $this, 'render_checkbox_field' ), self::OPTION_GROUP, 'airgentic_main', array( 'label_for' => 'enable_on_front', 'label' => __( 'Load Airgentic on all front-end pages', 'airgentic' ), ) ); add_settings_field( 'testing_mode', __( 'Testing Mode', 'airgentic' ), array( $this, 'render_testing_mode_field' ), self::OPTION_GROUP, 'airgentic_main', array( 'label_for' => 'testing_mode' ) ); add_settings_field( 'traffic_percent', __( 'Show to % of visitors', 'airgentic' ), array( $this, 'render_traffic_percent_field' ), self::OPTION_GROUP, 'airgentic_main', array( 'label_for' => 'traffic_percent' ) ); add_settings_section( 'airgentic_search', __( 'Site Search', 'airgentic' ), array( $this, 'render_search_section_info' ), self::OPTION_GROUP ); add_settings_field( 'search_mode', __( 'Search mode', 'airgentic' ), array( $this, 'render_search_mode_field' ), self::OPTION_GROUP, 'airgentic_search', array( 'label_for' => 'search_mode' ) ); add_settings_field( 'search_page_id', __( 'Search page', 'airgentic' ), array( $this, 'render_search_page_field' ), self::OPTION_GROUP, 'airgentic_search', array( 'label_for' => 'search_page_id' ) ); add_settings_field( 'rewrite_search_forms', __( 'Rewrite WordPress search forms', 'airgentic' ), array( $this, 'render_checkbox_field' ), self::OPTION_GROUP, 'airgentic_search', array( 'label_for' => 'rewrite_search_forms', 'label' => __( 'Point the Search block, Search widget, and theme search form at the Airgentic search page (inline mode)', 'airgentic' ), ) ); add_settings_field( 'redirect_s', __( 'Redirect WordPress searches', 'airgentic' ), array( $this, 'render_checkbox_field' ), self::OPTION_GROUP, 'airgentic_search', array( 'label_for' => 'redirect_s', 'label' => __( 'Redirect ?s= searches to the Airgentic search page (inline mode)', 'airgentic' ), ) ); add_settings_field( 'search_input_id', __( 'Non-WordPress search input', 'airgentic' ), array( $this, 'render_text_field' ), self::OPTION_GROUP, 'airgentic_search', array( 'label_for' => 'search_input_id', 'placeholder' => 'searchInput or .header-search', 'description' => __( 'Leave blank in inline mode. A WordPress search form (Search block, Search widget, or a theme form with an input named s) is found automatically. Fill this in only for a search box that is not a WordPress form: the HTML id of the text field, or a class name with a leading dot. Use the text field, not the icon that opens it. In overlay mode this field is required, and names the text field that opens the popup.', 'airgentic' ), ) ); add_settings_field( 'search_button_id', __( 'Non-WordPress search button', 'airgentic' ), array( $this, 'render_text_field' ), self::OPTION_GROUP, 'airgentic_search', array( 'label_for' => 'search_button_id', 'placeholder' => 'searchButton', 'description' => __( 'Leave blank in inline mode. A WordPress search button submits its form and needs no id. Fill this in only for a non-WordPress search box whose button does not submit the form, or in overlay mode when that button should open the popup. Not the icon that opens a search overlay.', 'airgentic' ), ) ); } /** * Render main section information */ public function render_section_info() { echo '

' . esc_html__( 'Configure your Airgentic account details below. You can find these in your Airgentic admin dashboard.', 'airgentic' ) . '

'; } /** * Render Site Search section information */ public function render_search_section_info() { echo '

' . esc_html__( 'Inline search (recommended) uses a results page on your site. Overlay search opens Airgentic in a popup instead. Existing installs that already set a search input ID keep overlay mode until you change it.', 'airgentic' ) . '

'; } /** * Render text field * * @param array $args Field arguments. */ public function render_text_field( $args ) { $options = Airgentic::get_plugin_options(); $id = $args['label_for']; $value = isset( $options[ $id ] ) ? $options[ $id ] : ''; $placeholder = isset( $args['placeholder'] ) ? $args['placeholder'] : ''; printf( '', esc_attr( $id ), esc_attr( self::OPTION_NAME ), esc_attr( $value ), esc_attr( $placeholder ) ); if ( ! empty( $args['description'] ) ) { echo '

' . esc_html( $args['description'] ) . '

'; } } /** * Render checkbox field * * @param array $args Field arguments. */ public function render_checkbox_field( $args ) { $options = Airgentic::get_plugin_options(); $id = $args['label_for']; $checked = ! empty( $options[ $id ] ); $label = isset( $args['label'] ) ? $args['label'] : ''; printf( '', esc_attr( $id ), esc_attr( self::OPTION_NAME ), checked( $checked, true, false ), esc_html( $label ) ); } /** * Render Testing Mode checkbox. */ public function render_testing_mode_field() { $options = Airgentic::get_plugin_options(); $checked = ! empty( $options['testing_mode'] ); printf( '', esc_attr( self::OPTION_NAME ), checked( $checked, true, false ), esc_html__( 'Only logged-in WordPress users can see Airgentic', 'airgentic' ) ); echo '

' . esc_html__( 'On by default for new installs. Log in to WordPress and browse the live site to preview chat and search. Turn Testing Mode off when you are ready for visitors. Show to % of visitors is ignored while Testing Mode is on (logged-in users always see Airgentic at 100%). Existing sites keep Testing Mode off until you turn it on.', 'airgentic' ) . '

'; } /** * Render the traffic-percent number field. */ public function render_traffic_percent_field() { $options = Airgentic::get_plugin_options(); $value = $options['traffic_percent']; $testing = ! empty( $options['testing_mode'] ); $name = self::OPTION_NAME . '[traffic_percent]'; printf( '', esc_attr( $name ), esc_attr( '' === $value ? '' : (string) $value ), disabled( $testing, true, false ) ); if ( $testing ) { printf( '', esc_attr( $name ), esc_attr( '' === $value ? '' : (string) $value ) ); } echo '

' . esc_html__( 'Applies after you turn Testing Mode off. Leave blank (or 100) to show Airgentic to every visitor. Set 20 to show the floating chat widget to about 20% of browsers, chosen once per visitor. Dedicated search and Service Hub pages always load so those pages are never empty. Staff can append ?airgentic=1 or ?airgentic=0 to a URL to force on or off.', 'airgentic' ) . '

'; } /** * Render search mode select */ public function render_search_mode_field() { $options = Airgentic::get_plugin_options(); $mode = $options['search_mode']; $choices = array( 'none' => __( 'Disabled', 'airgentic' ), 'inline' => __( 'Inline search (recommended) — a results page on your site', 'airgentic' ), 'overlay' => __( 'Overlay search — popup on the current page', 'airgentic' ), ); echo ''; echo '

' . esc_html__( 'Ask Airgentic support to confirm inline search is enabled for your service before choosing Inline search.', 'airgentic' ) . '

'; } /** * Render search page dropdown */ public function render_search_page_field() { $options = Airgentic::get_plugin_options(); $page_id = absint( $options['search_page_id'] ); wp_dropdown_pages( array( 'name' => self::OPTION_NAME . '[search_page_id]', 'id' => 'search_page_id', 'echo' => 1, 'show_option_none' => __( '— Select a page —', 'airgentic' ), 'option_none_value' => '0', 'selected' => $page_id, ) ); echo '

' . esc_html__( 'Used for inline search. The page should contain the [airgentic_search] shortcode. Use the button below to create one, or pick an existing page.', 'airgentic' ) . '

'; } /** * Sanitize options * * @param array $input Raw input values. * @return array Sanitized values. */ public function sanitize_options( $input ) { if ( ! is_array( $input ) ) { $input = array(); } $sanitized = array(); $sanitized['account_id'] = isset( $input['account_id'] ) ? sanitize_text_field( $input['account_id'] ) : ''; $sanitized['service_id'] = isset( $input['service_id'] ) ? sanitize_text_field( $input['service_id'] ) : ''; $sanitized['search_input_id'] = isset( $input['search_input_id'] ) ? $this->sanitize_selector( $input['search_input_id'] ) : ''; $sanitized['search_button_id'] = isset( $input['search_button_id'] ) ? $this->sanitize_selector( $input['search_button_id'] ) : ''; $sanitized['enable_on_front'] = ! empty( $input['enable_on_front'] ) ? 1 : 0; $sanitized['testing_mode'] = ! empty( $input['testing_mode'] ) ? 1 : 0; $sanitized['rewrite_search_forms'] = ! empty( $input['rewrite_search_forms'] ) ? 1 : 0; $sanitized['redirect_s'] = ! empty( $input['redirect_s'] ) ? 1 : 0; $sanitized['traffic_percent'] = Airgentic::normalise_traffic_percent( $input['traffic_percent'] ?? '' ); $mode = isset( $input['search_mode'] ) ? sanitize_key( $input['search_mode'] ) : 'none'; $sanitized['search_mode'] = in_array( $mode, array( 'none', 'inline', 'overlay' ), true ) ? $mode : 'none'; $sanitized['search_page_id'] = isset( $input['search_page_id'] ) ? absint( $input['search_page_id'] ) : 0; return $sanitized; } /** * Sanitize an element id, class name, or comma-separated list of either. * * @param string $value Raw value. * @return string Sanitized value. */ private function sanitize_selector( $value ) { $value = is_string( $value ) ? trim( $value ) : ''; return preg_replace( '/[^A-Za-z0-9_.,\s-]/', '', $value ); } /** * Create a Search page that contains the Site Search shortcode. */ public function handle_create_search_page() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'You do not have permission to create a search page.', 'airgentic' ) ); } check_admin_referer( 'airgentic_create_search_page' ); $options = Airgentic::get_plugin_options(); $status = 'created'; $page_id = absint( $options['search_page_id'] ); if ( $page_id && 'publish' === get_post_status( $page_id ) ) { $page = get_post( $page_id ); $status = $this->ensure_search_shortcode_on_page( $page ); $page_id = (int) $page->ID; } else { $existing = get_page_by_path( 'search' ); if ( $existing instanceof WP_Post && 'page' === $existing->post_type ) { $status = $this->ensure_search_shortcode_on_page( $existing ); $page_id = (int) $existing->ID; } else { $page_id = wp_insert_post( array( 'post_title' => __( 'Search', 'airgentic' ), 'post_name' => 'search', 'post_status' => 'publish', 'post_type' => 'page', 'post_content' => '[airgentic_search]', ), true ); if ( is_wp_error( $page_id ) || ! $page_id ) { $redirect = add_query_arg( array( 'page' => 'airgentic', 'airgentic-search-page' => 'error', ), admin_url( 'options-general.php' ) ); wp_safe_redirect( $redirect ); exit; } $status = 'created'; } } $options['search_page_id'] = $page_id; if ( 'none' === $options['search_mode'] ) { $options['search_mode'] = 'inline'; } update_option( self::OPTION_NAME, $options ); $redirect = add_query_arg( array( 'page' => 'airgentic', 'airgentic-search-page' => $status, ), admin_url( 'options-general.php' ) ); wp_safe_redirect( $redirect ); exit; } /** * Append the search shortcode to a page if it is missing. * * @param WP_Post $page Page object. * @return string Notice code: created or updated. */ private function ensure_search_shortcode_on_page( $page ) { if ( ! $page instanceof WP_Post ) { return 'error'; } if ( false !== strpos( $page->post_content, '[airgentic_search]' ) ) { return 'exists'; } $updated = wp_update_post( array( 'ID' => $page->ID, 'post_content' => rtrim( $page->post_content ) . "\n\n[airgentic_search]", ), true ); return is_wp_error( $updated ) ? 'error' : 'updated'; } /** * Show a notice after creating or updating the search page. */ public function maybe_search_page_notice() { if ( ! current_user_can( 'manage_options' ) ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display-only query flag after admin-post redirect. if ( empty( $_GET['page'] ) || 'airgentic' !== $_GET['page'] || empty( $_GET['airgentic-search-page'] ) ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended $code = sanitize_key( wp_unslash( $_GET['airgentic-search-page'] ) ); $messages = array( 'created' => array( 'updated', __( 'Search page created. It contains the [airgentic_search] shortcode. Choose Inline search and save settings if you have not already.', 'airgentic' ), ), 'updated' => array( 'updated', __( 'The selected Search page did not include the shortcode, so [airgentic_search] was added.', 'airgentic' ), ), 'exists' => array( 'updated', __( 'That page already contains the Site Search shortcode. It is now selected as your Search page.', 'airgentic' ), ), 'error' => array( 'error', __( 'The Search page could not be created. Create a page manually, add [airgentic_search], and select it above.', 'airgentic' ), ), ); if ( ! isset( $messages[ $code ] ) ) { return; } printf( '

%2$s

', esc_attr( $messages[ $code ][0] ), esc_html( $messages[ $code ][1] ) ); } /** * Render settings page */ public function render_settings_page() { if ( ! current_user_can( 'manage_options' ) ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Checking WordPress-set flag, Settings API handles nonces via settings_fields() if ( isset( $_GET['settings-updated'] ) ) { add_settings_error( 'airgentic_messages', 'airgentic_message', __( 'Settings saved successfully.', 'airgentic' ), 'updated' ); } settings_errors( 'airgentic_messages' ); ?>


[airgentic_service_hub]

<div id="airgentic"></div>

<div data-airgentic="search"></div>

' . esc_html__( 'Airgentic admin dashboard', 'airgentic' ) . '' ); ?>


www.airgentic.com/integrations/wordpress' ); ?>

help.airgentic.com/help/site-search-website-integration' ); ?>