HEX
Server: Apache
System: Linux 244.240.109.208.host.secureserver.net 5.14.0-611.11.1.el9_7.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Dec 3 09:47:37 EST 2025 x86_64
User: icsla (1002)
PHP: 8.1.34
Disabled: NONE
Upload Files
File: /home/icsla/public_html/wp-content/plugins/chatbot/qcld-wpwbot-search.php
<?php
if (!defined('ABSPATH')) exit; // Exit if accessed directly
/**
 * Product indexing, caching & searching features concept is taken from open source 'Advanced wp Search' Wp plugin by ILLID.
 */
//include_once( 'includes/class-wpwbot-cache.php' );

include_once( 'includes/class-wpwbot-table.php' );
include_once( 'includes/class-wpwbot-search.php' );

// Helper function to generate variations for each word in a keyword
if ( ! function_exists( '_wpbot_generate_word_variations' ) ) {
    function _wpbot_generate_word_variations($keyword) {
        $keyword = strtolower(trim($keyword));
        $words = preg_split('/\s+/', $keyword, -1, PREG_SPLIT_NO_EMPTY);
        $all_word_variations = [];

        foreach ($words as $word) {
            $variations = [$word];

            // Simple pluralization/singularization and common suffix handling
            if (strlen($word) > 1) { // Avoid stemming single letters
                // Remove 's' (e.g., 'inspections' -> 'inspection')
                if (substr($word, -1) === 's') {
                    $variations[] = substr($word, 0, -1);
                } else { // Add 's' (e.g., 'inspection' -> 'inspections')
                    $variations[] = $word . 's';
                }
                // Handle 'er' suffix (e.g., 'inspector' -> 'inspect')
                if (substr($word, -2) === 'er') {
                    $variations[] = substr($word, 0, -2);
                }
                // Handle 'ing' suffix (e.g., 'inspecting' -> 'inspect')
                if (substr($word, -3) === 'ing') {
                    $variations[] = substr($word, 0, -3);
                }
                // Add 'er' if the base word is 'inspect' and 'inspector' is not present
                if (strpos($word, 'inspect') !== false && strpos($word, 'inspector') === false) {
                     $variations[] = str_replace('inspect', 'inspector', $word);
                }
            }
            $all_word_variations[] = array_filter(array_unique($variations));
        }
        return $all_word_variations;
    }
}

// Filter function to modify WP_Query search for flexible matching
if ( ! function_exists( 'wpbot_flexible_search_filter' ) ) {
    function wpbot_flexible_search_filter($search, $wp_query) {
        global $wpdb, $wpbot_search_word_variations;

        // Only apply if it's the main search query and our variations are set
        if (empty($wpbot_search_word_variations) || !$wp_query->is_search || !$wp_query->is_main_query()) {
            return $search;
        }

        $search_parts_for_and = [];

        foreach ($wpbot_search_word_variations as $word_variations) {
            $search_parts_for_or = [];
            foreach ($word_variations as $term) {
                $term = $wpdb->esc_like($term);
                // Search in both post_title and post_content
                $search_parts_for_or[] = "(({$wpdb->posts}.post_title LIKE '%{$term}%') OR ({$wpdb->posts}.post_content LIKE '%{$term}%'))";
            }
            if (!empty($search_parts_for_or)) {
                $search_parts_for_and[] = '(' . implode(' OR ', $search_parts_for_or) . ')';
            }
        }

        if (!empty($search_parts_for_and)) {
            // Completely replace the default search clause generated by WP_Query's 's' parameter
            // This ensures our flexible matching takes precedence.
            $search = ' AND ' . implode(' AND ', $search_parts_for_and);
        }

        return $search;
    }
}

function wpbo_search_site() {
    // Verify nonce for security
    $nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : (isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '');
    if ( ! wp_verify_nonce( $nonce, 'wp_chatbot' ) && ! wp_verify_nonce( $nonce, 'qcsecretbotnonceval123qc' ) ) {
        wp_send_json_error( array( 'status' => 'fail', 'message' => 'Security check failed.' ) );
        wp_die();
    }
    
    global $wpdb;
    // Limit results to 5 items.
    $limit = 5;
    $response = array('status' => 'fail', 'html' => ''); // Initialize response array

    // Get default language for load more button text and other language-specific checks.
    $default_language = get_locale();

    if(get_option('enable_wp_chatbot_post_content') == 1){
        $keyword            = isset( $_POST['keyword'] )    ? sanitize_text_field(wp_unslash($_POST['keyword'])) : '';
        $all_word_variations = _wpbot_generate_word_variations($keyword);

        // Temporarily store the variations for the filter
        global $wpbot_search_word_variations;
        $wpbot_search_word_variations = $all_word_variations;

        // Add the custom search filter
        add_filter('posts_search', 'wpbot_flexible_search_filter', 10, 2);

        // $enable_post_types   = array( 'post', 'page'); // This line is commented out, so post_type is not restricted here.
        $total_items        = $limit; 
        $query_arg          = array(
        //  'post_type'     => $enable_post_types,
            'post_status'   => 'publish',
            'posts_per_page'=> $total_items,
            's'             => stripslashes( $keyword ), // Keep original for WP_Query to initiate search, filter will override
            'paged'         => 1,
            'suppress_filters' => false // Crucial for filters to run
        );
        $resultss = new WP_Query( $query_arg );
        $results = $resultss->posts;

        // Remove the filter after the query to avoid affecting other queries
        remove_filter('posts_search', 'wpbot_flexible_search_filter', 10);
        unset($wpbot_search_word_variations); // Clean up global
    }else{
        $keyword    = isset( $_POST['keyword'] )    ? sanitize_text_field(wp_unslash($_POST['keyword'])) : '';
        $all_word_variations = _wpbot_generate_word_variations($keyword);

        $sql_parts_for_and = [];
        $sql_params = [];

        foreach ($all_word_variations as $word_variations) {
            $sql_parts_for_or = [];
            foreach ($word_variations as $term) {
                $sql_parts_for_or[] = "post_title LIKE %s";
                $sql_params[] = '%' . $wpdb->esc_like($term) . '%';
            }
            if (!empty($sql_parts_for_or)) {
                $sql_parts_for_and[] = '(' . implode(' OR ', $sql_parts_for_or) . ')';
            }
        }

        $where_clause = '';
        if (!empty($sql_parts_for_and)) {
            $where_clause = ' AND ' . implode(' AND ', $sql_parts_for_and);
        } else {
            // Fallback to original behavior if no variations generated (e.g., empty keyword)
            $where_clause = " AND (post_title LIKE %s)";
            $sql_params[] = '%' . $wpdb->esc_like($keyword) . '%';
        }

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
        $results    = $wpdb->get_results( $wpdb->prepare(
            "SELECT * FROM " . $wpdb->prefix . "posts WHERE post_status = %s " . $where_clause . " ORDER BY ID DESC LIMIT %d",
            array_merge(['publish'], $sql_params, [$limit])
        ) );
    }
    
    if(!empty( $results )){

        $response['status'] = 'success';
        $response['html']   = '<div class="wpb-search-result">';
        $total_post         = 0;
        $responses          = '';
        
        foreach ( $results as $result ) {
            $featured_img_url = get_the_post_thumbnail_url( $result->ID, 'full' );
            $excerpt = '';
            if ( isset( $result->ID ) ) {
                $post_obj = get_post( $result->ID );
                if ( $post_obj ) {
					if ( has_excerpt( $result->ID ) ) {
						$excerpt = get_the_excerpt( $result->ID );
					} else {
						$content = $post_obj->post_content;

						// Remove ALL WPBakery shortcodes (paired + self-closing)
						$content = preg_replace( '/\[vc_[^\]]*\](.*?)\[\/vc_[^\]]*\]/s', '$1', $content ); // paired
						$content = preg_replace( '/\[vc_[^\]]*\]/s', '', $content ); // self-closing
						$content = preg_replace('/\[\/?[\w\-]+[^\]]*\]/', '', $content);
						// Extra: remove any leftover [] shortcodes (just in case)
						$content = strip_shortcodes( $content );

						// Run through normal WP content filters
						$content_filtered = apply_filters( 'the_content', $content );

						// Strip HTML tags, then trim
						$excerpt = wp_trim_words( wp_strip_all_tags( $content_filtered ), 20, '...' );
					}


                }
            }
        
            $total_post = $total_post + 1;
            $responses .='<div class="wpbot_card_wraper">';
            $responses .=   '<div class="wpbot_card_image '.($result->post_type=='product'?'wp-chatbot-product':'').' '.( empty($featured_img_url) ?'wpbot_card_image_saas':'').'"><a href="'.esc_url(get_permalink($result->ID)).'" target="_blank" '.($result->post_type=='product'?'wp-chatbot-pid="'.$result->ID.'"':'').'>';
            if( !empty($featured_img_url) ){
                $responses .=       '<img src="'.esc_url_raw($featured_img_url).'" />';
            }
            $responses .=       '<div class="wpbot_card_caption '.( empty($featured_img_url) ?'wpbot_card_caption_saas':'').'">';
            $responses .=           '<p><span style="padding: 0 5px;color: #1d73b4;display: inline-block;margin: 0 5px 0 0;width: 18px;height: 18px;border-radius: 50%;font-size: 20px;line-height: 22px;"> ✓ </span> '.esc_html($result->post_title).'</p>';
            $responses .=           '<p>'.esc_html($excerpt).'</p>';
            if($result->post_type=='product'){
                if ( class_exists( 'WooCommerce' ) ) {
                    if ( $result->ID ) {
                        $product = wc_get_product( $result->ID );
                        $responses .=           '<p class="wpbot_product_price">'.get_woocommerce_currency_symbol().$product->get_price_html().'</p>';
                    }
                }
            }
            $responses .=       '</div>';
            $responses .=   '</a></div>';
                $responses .='</div>';
            
        }
        $response['html'] .= $responses;
        $response['html'] .='</div>';
        if($total_post >= $limit ){ // Use $limit for consistency
            $load_more = maybe_unserialize(get_option('qlcd_wp_chatbot_load_more_search'));
        
            $response['html'] .='<button type="button" class="wp-chatbot-loadmore" data-search-type="default-wp-search" data-keyword="'.esc_attr($keyword).'" data-page="2">'. ( !empty($load_more) && isset($load_more[$default_language]) ? $load_more[$default_language] : 'Load More').'  <span id="wp-chatbot-loadmore-loader" class="wp-chatbot-loadmore-loader"></span></button>';
            
        }
    }else{
        // Fuzzy search if initial search yields no results
        $response['status'] = 'success'; 
        
        // Use the same word variation logic for fuzzy search
        $all_word_variations_for_fuzzy = _wpbot_generate_word_variations($keyword);
        
        $unique_posts = array(); // Store unique post objects
        $seen_ids = array(); // Keep track of seen post IDs

        // Iterate through each word's variations to find matching posts
        foreach ( $all_word_variations_for_fuzzy as $word_variations ) {
            foreach ($word_variations as $term) {
                $term = $wpdb->esc_like( $term );
                
                $term_results = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
                    $wpdb->prepare("SELECT * FROM ". $wpdb->prefix."posts WHERE post_type IN (%s, %s) AND post_status = %s AND (post_title LIKE %s) ORDER BY ID DESC", 'page', 'post', 'publish', '%'. $term .'%')
                );
                
                foreach ($term_results as $res) {
                    if (!in_array($res->ID, $seen_ids)) {
                        $unique_posts[] = $res;
                        $seen_ids[] = $res->ID;
                    }
                }
            }
        }
        $results = $unique_posts; // Now $results contains unique WP_Post objects

        if(  !empty( $results) ){
            $response['html'] = '<div class="wpb-search-result">';
            $total_post = 0;
            $responses = '';
            $selected_lan = get_option('qlcd_wp_chatbot_default_language'); 

            foreach ($results as $value) { // $value is a single post object here
                if(!empty($value->guid)){
                    $post_id = $value->ID;
                    $current_featured_img_url = get_the_post_thumbnail_url( $post_id, 'full' );

                    // Corrected URL segment parsing for language check
                    $url_path = wp_parse_url(get_permalink($post_id), PHP_URL_PATH);
                    $url_segments = array_filter(explode('/',$url_path));
                    
                    // Assuming the language slug is the first non-empty segment of the URL path.
                    $first_segment = !empty($url_segments) ? reset($url_segments) : '';
                    
                    // If $selected_lan is empty, the language check is effectively skipped.
                    $language_match = empty($selected_lan) || ($first_segment == $selected_lan);

                    if($language_match){
                        $total_post = $total_post + 1;
                        $responses .='<div class="wpbot_card_wraper">';
                        $responses .=   '<div class="wpbot_card_image '.(empty($current_featured_img_url)?'wpbot_card_image_saas':'').'"><a href="'.esc_url(get_permalink($post_id)).'" target="_blank">';
                        if(!empty($current_featured_img_url)){
                            $responses .=       '<img src="'.esc_url_raw($current_featured_img_url).'" />';
                        }
                        $responses .=       '<div class="wpbot_card_caption '.(empty($current_featured_img_url)?'wpbot_card_caption_saas':'').'">';
                        $responses .=           '<p><span style="padding: 0 5px;color: #1d73b4;display: inline-block;margin: 0 5px 0 0;width: 18px;height: 18px;border-radius: 50%;font-size: 20px;line-height: 22px;"> ✓ </span>'.esc_html($value->post_title).'</p>';
                        $responses .=       '</div>';
                        $responses .=   '</a></div>';
                        $responses .='</div>';
                    }       
                }
            }
            if($total_post > 2 ){ // This condition is different from the first block ($total_post >= $limit)
                $load_more = maybe_unserialize(get_option('qlcd_wp_chatbot_load_more_search'));
                $response['html'] .='<button type="button" class="wp-chatbot-loadmore2" data-search-type="default-wp-search" data-keyword="'.esc_attr($keyword).'" data-page="2">'. ( !empty($load_more) && isset($load_more[$default_language]) ? $load_more[$default_language] : 'Load More').'  <span id="wp-chatbot-loadmore-loader" class="wp-chatbot-loadmore-loader"></span></button>';
                $response['status'] = 'success';
            }else{
                $response['status'] = 'fail';
            }
            
            $response['html'] .= $responses;
            $response['html'] .='</div>';
        } else {
            $response['status'] = 'fail'; // No results from fuzzy search either
        }
    }
    echo wp_json_encode($response);
    wp_die();
}


add_action( 'wp_ajax_wpbo_search_site',        'wpbo_search_site' );
add_action( 'wp_ajax_nopriv_wpbo_search_site', 'wpbo_search_site' );

if ( ! function_exists( 'qcld_wpbot_modified_keyword' ) ) {
	function qcld_wpbot_modified_keyword( $keyword ) {
		$keyword = rtrim( $keyword, '!' );
		$pattern = '/[?\/]/';
		$strings = preg_split( $pattern, $keyword );
		$strings = array_filter( array_map( 'trim', $strings ) );
		$keyword = rtrim( $strings[0], '!' );
		return htmlspecialchars_decode( $keyword );
	}
}

add_action( 'wp_ajax_wpbo_search_responseby_intent',        'qcld_wpbo_search_responseby_intent' );
add_action( 'wp_ajax_nopriv_wpbo_search_responseby_intent', 'qcld_wpbo_search_responseby_intent' );



function wpbo_search_site_pagination() {
	global $wpdb;

	// Verify nonce for security 
	$p_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
	if ( ! wp_verify_nonce( $p_nonce, 'wp_chatbot' ) && ! wp_verify_nonce( $p_nonce, 'qcsecretbotnonceval123qc' ) ) {
		wp_send_json_error( array( 'message' => 'Security check failed' ) );
		wp_die();
	}

	// Sanitize and validate inputs
	$keyword           = isset( $_POST['keyword'] ) ? sanitize_text_field(wp_unslash($_POST['keyword'])) : '';
	$post_type         = isset( $_POST['type'] ) ? sanitize_text_field(wp_unslash($_POST['type'])) : 'post';
	$page              = isset($_POST['page']) ? absint( wp_unslash($_POST['page']) ) : 0;
	
	// Validate post type against allowed types
	$allowed_post_types = array( 'post', 'page', 'product' );
	if ( ! in_array( $post_type, $allowed_post_types, true ) ) {
		$post_type = 'post';
	}

	$enable_post_types = get_option( 'wppt_post_types' );
	$load_more         = maybe_unserialize( get_option( 'qlcd_wp_chatbot_load_more' ) );

	if ( is_array( $load_more ) && isset( $load_more[ get_locale() ] ) ) {
		$load_more = $load_more[ get_locale() ];
	}
	if ( is_array( $load_more ) ) {
		$load_more = $load_more[ array_rand( $load_more ) ];
	}
	$searchlimit = ( get_option( 'wppt_number_of_result' ) == '' ? 5 : absint( get_option( 'wppt_number_of_result' ) ) );
	$orderby     = ( get_option( 'wppt_result_orderby' ) == '' ? 'none' : get_option( 'wppt_result_orderby' ) );
	$order       = ( get_option( 'wppt_result_order' ) == '' ? 'ASC' : get_option( 'wppt_result_order' ) );
	$thumb       = ( get_option( 'wpbot_search_image_size' ) ? get_option( 'wpbot_search_image_size' ) : 'thumbnail' );
	// order by setup
	$new_window = get_option( 'wpbot_search_result_new_window' );

	$total_items = absint( get_option( 'wppt_number_of_result' ) );
	if ( $total_items < 1 ) {
		$total_items = 5;
	}

	$searchkeyword = qcld_wpbot_modified_keyword( $keyword );

	$response           = array();
	$response['status'] = 'fail';
	$response['html']   = '';

	// Use prepared statements to prevent SQL injection
	if ( get_option( 'active_advance_query' ) != '1' ) {
		// Simple query - search in post_title only
		$total_results = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->prepare(
			"SELECT * FROM {$wpdb->prefix}posts 
			WHERE post_type = %s 
			AND post_status = 'publish' 
			AND post_title LIKE %s 
			ORDER BY ID DESC",
			$post_type,
			'%' . $wpdb->esc_like( $searchkeyword ) . '%'
		));
	} else {
		// Advanced query - search in both post_title and post_content
		$total_results = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->prepare(
			"SELECT * FROM " . $wpdb->prefix . "posts 
			WHERE post_type = %s 
			AND post_status = %s 
			AND (post_title REGEXP %s OR post_content REGEXP %s) 
			ORDER BY ID DESC",
			$post_type,
			'publish',
			'[[:<:]]' . $searchkeyword . '[[:>:]]',
			'[[:<:]]' . $searchkeyword . '[[:>:]]'
		));
	}

	if ( ! empty( $total_results ) ) {

		// Validate and sanitize orderby parameter
		$valid_orderby = array( 'title', 'date', 'modified', 'none', 'rand' );
		if ( ! in_array( $orderby, $valid_orderby, true ) ) {
			$orderby = 'none';
		}

		if ( $orderby == 'title' ) {
			$orderby = 'post_title';
		}
		if ( $orderby == 'date' ) {
			$orderby = 'post_date';
		}
		if ( $orderby == 'modified' ) {
			$orderby = 'post_modified';
		}

		// Validate order parameter
		$order = strtoupper( $order );
		if ( ! in_array( $order, array( 'ASC', 'DESC' ), true ) ) {
			$order = 'ASC';
		}

		// Build query with pagination
		$offset = absint( $total_items * $page );
		
		if ( get_option( 'active_advance_query' ) != '1' ) {
			if ( $orderby != 'none' && $orderby != 'rand' ) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
				$results = $wpdb->get_results( $wpdb->prepare(
					"SELECT * FROM {$wpdb->prefix}posts 
					WHERE post_type = %s 
					AND post_status = 'publish' 
					AND post_title LIKE %s 
					ORDER BY {$orderby} {$order}
					LIMIT %d, %d",
					$post_type,
					'%' . $wpdb->esc_like( $searchkeyword ) . '%',
					$offset,
					$total_items
				) );
			} else {
				$results = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
					$wpdb->prepare(
					"SELECT * FROM {$wpdb->prefix}posts 
					WHERE post_type = %s 
					AND post_status = 'publish' 
					AND post_title LIKE %s 
					ORDER BY ID DESC
					LIMIT %d, %d",
					$post_type,
					'%' . $wpdb->esc_like( $searchkeyword ) . '%',
					$offset,
					$total_items
				));
			}
		} else {
			if ( $orderby != 'none' && $orderby != 'rand' ) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
				$results = $wpdb->get_results( $wpdb->prepare(
					"SELECT * FROM " . $wpdb->prefix . "posts 
					WHERE post_type = %s 
					AND post_status = %s 
					AND (post_title REGEXP %s OR post_content REGEXP %s) 
					ORDER BY " . $orderby . " " . $order . "
					LIMIT %d, %d",
					$post_type,
					'publish',
					'[[:<:]]' . $searchkeyword . '[[:>:]]',
					'[[:<:]]' . $searchkeyword . '[[:>:]]',
					$offset,
					$total_items
				) );
			} else {
				$results = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
					$wpdb->prepare(
					"SELECT * FROM " . $wpdb->prefix . "posts 
					WHERE post_type = %s 
					AND post_status = %s 
					AND (post_title REGEXP %s OR post_content REGEXP %s) 
					ORDER BY ID DESC
					LIMIT %d, %d",
					$post_type,
					'publish',
					'[[:<:]]' . $searchkeyword . '[[:>:]]',
					'[[:<:]]' . $searchkeyword . '[[:>:]]',
					$offset,
					$total_items
				));
			}
		}
	} else {
		if ( class_exists( 'SitePress' ) ) {
			global $sitepress;
			$selected_lan = isset( $_POST['language'] ) ? sanitize_text_field(wp_unslash($_POST['language'])) : '';
			$selected_lan = explode( '_', $selected_lan );
			if ( ! empty( $selected_lan[0] ) ) {
				$sitepress->switch_lang( $selected_lan[0], true );
			}
		}
		
		$query_arg = array(
			'post_type'      => $post_type,
			'post_status'    => 'publish',
			'posts_per_page' => $total_items,
			's'              => stripslashes( $keyword ),
			'paged'          => ( $page + 1 ),
			'orderby'        => $orderby,
		);
		
		if ( class_exists( 'SitePress' ) ) {
			global $sitepress;
			$selected_lan = isset( $_POST['language'] ) ? sanitize_text_field(wp_unslash($_POST['language'])) : '';
			$selected_lan = explode( '_', $selected_lan );
			if ( ! empty( $selected_lan[0] ) ) {
				$sitepress->switch_lang( $selected_lan[0], true );
			}
		}

		$query_arg['suppress_filters'] = true;
		if ( $orderby != 'none' && $orderby != 'rand' ) {
			$query_arg['order'] = $order;
		}

		$totalresults = new WP_Query(
			array(
				'post_type'   => $post_type,
				'post_status' => 'publish',
				's'           => stripslashes( $keyword ),
			)
		);
		$resultss      = new WP_Query( $query_arg );
		$total_results = $totalresults->posts;
		$resultss      = new WP_Query( $query_arg );
		$results       = $resultss->posts;
	}

	if ( ! empty( $total_results ) ) {

		$selected_lan     = isset( $_POST['language'] ) ? sanitize_text_field(wp_unslash($_POST['language'])) : '';
		$urlss            = get_option( 'wpbotml_url_urls' ) ? get_option( 'wpbotml_url_urls' ) : '';
		$imagesize        = ( get_option( 'wpbot_search_image_size' ) != '' ? get_option( 'wpbot_search_image_size' ) : 'thumbnail' );

		$response['html'] .= '<div class="wpb-search-result">';

		foreach ( $total_results as $result ) {

			if ( $result->post_type == 'product' ) {
				if ( ! class_exists( 'WooCommerce' ) ) {
					continue;
				}
			}

			$featured_img_url = get_the_post_thumbnail_url( $result->ID, $thumb );
			$excerpt = '';
			if ( isset( $result->ID ) ) {
				$post_obj = get_post( $result->ID );
				if ( $post_obj ) {
					if ( has_excerpt( $result->ID ) ) {
						$excerpt = get_the_excerpt( $result->ID );
					} else {
						$content = $post_obj->post_content;

						// Remove ALL WPBakery shortcodes (paired + self-closing)
						$content = preg_replace( '/\[vc_[^\]]*\](.*?)\[\/vc_[^\]]*\]/s', '$1', $content ); // paired
						$content = preg_replace( '/\[vc_[^\]]*\]/s', '', $content ); // self-closing
						$content = preg_replace('/\[\/?[\w\-]+[^\]]*\]/', '', $content);
						// Extra: remove any leftover [] shortcodes (just in case)
						$content = strip_shortcodes( $content );

						// Run through normal WP content filters
						$content_filtered = apply_filters( 'the_content', $content );

						// Strip HTML tags, then trim
						$excerpt = wp_trim_words( wp_strip_all_tags( $content_filtered ), 20, '...' );
					}
				}
			}
			
			
			$response['html'] .= '<div class="wpbot_card_wraper">';
			$response['html'] .= '<div class="wpbot_card_image ' . ( $result->post_type == 'product' ? 'wp-chatbot-product' : '' ) . ' ' . ( $featured_img_url == '' ? 'wpbot_card_image_saas' : '' ) . '"><a href="' . esc_url( get_permalink( $result->ID ) ) . '" ' . ( $new_window == 1 ? 'target="_blank"' : '' ) . ' ' . ( $result->post_type == 'product' ? 'wp-chatbot-pid="' . absint( $result->ID ) . '"' : '' ) . '>';
			if ( $featured_img_url != '' ) {
				$response['html'] .= '<img src="' . esc_url_raw( $featured_img_url ) . '" />';
			}

			$response['html'] .= '<div class="wpbot_card_caption ' . ( $featured_img_url == '' ? 'wpbot_card_caption_saas' : '' ) . '">';
			$response['html'] .= '<p class="wpbot_card_caption_title"><span style="padding: 0 5px;color: #1d73b4;display: inline-block;margin: 0 5px 0 0;width: 18px;height: 18px;border-radius: 50%;font-size: 20px;line-height: 22px;"> ✓ </span> ' . esc_html( $result->post_title ) . '</p>';
			$response['html'] .= '<p class="wpbot_card_description">' . esc_html( $excerpt ) . '</p>';
			if ( $result->post_type == 'product' ) {
				if ( class_exists( 'WooCommerce' ) ) {
					$product           = wc_get_product( $result->ID );
					$response['html'] .= '<p class="wpbot_product_price">' . get_woocommerce_currency_symbol() . $product->get_price_html() . '</p>';
				}
			}
			$response['html'] .= '</div>';
			$response['html'] .= '</a></div>';
			$response['html'] .= '</div>';
			
		}
		
		
		$response['html']  .= '</div>';
		$response['status'] = 'success';

	}
	wp_reset_query();

	if ( $response['status'] != 'success' ) {
		$texts            = maybe_unserialize( get_option( 'qlcd_wp_chatbot_no_result' ) );
		$selected_lan     = isset( $_POST['language'] ) ? sanitize_text_field(wp_unslash($_POST['language'])) : '';
		if ( ! empty( $texts ) && is_array( $texts ) && isset( $texts[ $selected_lan ][0] ) ) {
			$texts            = str_replace( "\'", "'", $texts[ $selected_lan ][0] );
			$response['html'] = array( $texts );
		} else {
			$response['html'] = array( 'No results found' );
		}
	}
	wp_send_json( $response );
	die();
}

add_action( 'wp_ajax_wpbo_search_site_pagination', 'wpbo_search_site_pagination' );
add_action( 'wp_ajax_nopriv_wpbo_search_site_pagination', 'wpbo_search_site_pagination' );
function qcld_wpbo_search_responseby_intent(){

	global $wpdb;

	$keyword 	= isset( $_POST['keyword'] )    ? sanitize_text_field(wp_unslash($_POST['keyword'])) : '';

	$table 		= $wpdb->prefix.'wpbot_response';

	$result = $wpdb->get_row( $wpdb->prepare("SELECT `response` FROM %i WHERE 1 and `intent` = %s", $table, $keyword) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
	
	$response = array('status'=>'fail');
	
	if(!empty($result)){

		$response['status'] = 'success';
		$response['html'] = $result->response;

	}

	echo wp_json_encode($response);

	die();

}

add_action( 'wp_ajax_wpbo_search_response_catlist',        'wpbo_search_response_catlist' );
add_action( 'wp_ajax_nopriv_wpbo_search_response_catlist', 'wpbo_search_response_catlist' );

function wpbo_search_response_catlist(){
	global $wpdb;
	$table 		= $wpdb->prefix.'wpbot_response_category';
	$status 	= array('status'=>'fail');
	$results 	= $wpdb->get_results($wpdb->prepare("SELECT * FROM %i", $table)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
	$response_result = array();
	
	if(!empty($results)){
		foreach($results as $result){
			
			$response_result[] = array('name'=>$result->name);
			
		}
	}
	
	if(!empty($response_result)){

		$status = array('status'=>'success', 'data'=>$response_result);
		

	}
	
	echo wp_json_encode($status);

	die();
	
}

add_action( 'wp_ajax_wpbo_search_response',        'qcld_wpbo_search_response' );
add_action( 'wp_ajax_nopriv_wpbo_search_response', 'qcld_wpbo_search_response' );



function qcld_wpbo_search_response(){

	global $wpdb;
	$keyword 	= isset( $_POST['keyword'] )    ? (sanitize_text_field(wp_unslash($_POST['keyword']))) : '';
	$strid 		= isset( $_POST['strid'] )    	? (sanitize_text_field(wp_unslash($_POST['strid']))) : '';
	$table 		= $wpdb->prefix.'wpbot_response';
	

	$response_result = array();

	$status = array('status'=>'fail', 'multiple'=>false);
	$field = "ID";
	if(($strid != '') && empty($response_result)){
		$results = $wpdb->get_results($wpdb->prepare("SELECT * FROM %i WHERE %i = %d",$table,$field,$strid)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		if(!empty($results)){
			foreach($results as $result){
				
				$response_result[] = array('id'=>$result->id,'query'=>$result->query, 'response'=>$result->response, 'score'=>1);
				
			}
		}
	}
	$field = "query";
	$results = $wpdb->get_results( $wpdb->prepare("SELECT `id`, `query`, `response` FROM %i WHERE 1 and %i =  %s", $table, $field,$keyword) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
	
	
	if(!empty($results)){
		foreach($results as $result){
			
			$response_result[] = array('id'=>$result->id,'query'=>$result->query, 'response'=>$result->response, 'score'=>1);
			
		}
	}

	$field = "category";
	if(empty($response_result)){
		$results = $wpdb->get_results( $wpdb->prepare("SELECT `id`, `query`, `response` FROM %i  WHERE 1 and %i = %s", $table,$field, $keyword) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		
		
		if(!empty($results)){
			foreach($results as $result){
				$response_result[] = array('id'=>$result->id,'query'=>$result->query, 'response'=>$result->response, 'score'=>1);
			}
			if(count($response_result)>1){
				$status = array('status'=>'success','category'=> true, 'multiple'=>true, 'data'=>$response_result);
			}else{
				$status = array('status'=>'success', 'category'=> true, 'multiple'=>false, 'data'=>$response_result);
			}
			
			echo wp_json_encode($status);

			die();
		}
		
	}
	
	if(class_exists('Qcld_str_pro')){
		if(get_option('qc_bot_str_remove_stopwords') && get_option('qc_bot_str_remove_stopwords')==1){
			$keyword = qcld_strpro_remove_stopwords($keyword);
		}
	}
	
	
	if(empty($response_result)){

		$fields = get_option('qc_bot_str_fields');

		if($fields && !empty($fields)){
			$qfields = implode(', ', $fields);
		}else{
			$qfields = '`query`,`keyword`,`response`';
		}


		
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
		$results = $wpdb->get_results( $wpdb->prepare("SELECT `id`, `query`, `response`, MATCH($qfields) AGAINST(%s IN NATURAL LANGUAGE MODE) as score FROM %i WHERE MATCH($qfields) AGAINST(%s IN NATURAL LANGUAGE MODE) order by score desc limit 15",$keyword,$table,$keyword) );
		
		$weight = get_option('qc_bot_str_weight')!=''?get_option('qc_bot_str_weight'):'0.4';
		
		if(!empty($results)){
			$max_score = max(array_column($results, 'score'));
			if ($max_score <= 0) {
				$max_score = 1; // Set to 1 to avoid division by zero
			}
			foreach($results as $result){
				if(($result->score/$max_score) >= $weight){
					$response_result[] = array('id'=>$result->id,'query'=>$result->query, 'response'=>$result->response, 'score'=>$result->score);
				}
			}
		}
	}
	$field = "keyword";
	if( empty( $response_result ) ){
		
		$results = $wpdb->get_results($wpdb->prepare("SELECT * FROM %i WHERE %i REGEXP %s", $table,$field,$keyword)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		
		
		if(!empty($results)){
			foreach($results as $result){
				$response_result[] = array('id'=>$result->id,'query'=>$result->query, 'response'=>$result->response, 'score'=>1);
			}
		}
	}
	if(!empty($response_result)){
		
		if(count($response_result)>1){
			$status = array('status'=>'success', 'multiple'=>true, 'data'=>$response_result);
		}else{
			$status = array('status'=>'success', 'multiple'=>false, 'data'=>$response_result);
		}

	}
	if(empty($result->query)){
		$status = array('status'=>'fail', 'multiple'=>false, 'data'=>$response_result);
	}
	if(empty($status['data']) || (isset($status['status']) && $status['status']==='fail')){
		// Check for space before question mark and try again.
		if(preg_match('/ \?$/', $keyword)){
			$keyword2 = preg_replace('/ \?$/', '?', $keyword);
			// Try again with new keyword.
			// Repeat the main search logic with $keyword2.
			$response_result = array();
			$field = "query";
			$results = $wpdb->get_results( $wpdb->prepare("SELECT `id`, `query`, `response` FROM %i WHERE 1 and %i =  %s", $table, $field, $keyword2) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			if(!empty($results)){
				foreach($results as $result){
					$response_result[] = array('id'=>$result->id,'query'=>$result->query, 'response'=>$result->response, 'score'=>1);
				}
				if(count($response_result)>1){
					$status = array('status'=>'success', 'multiple'=>true, 'data'=>$response_result);
				}else{
					$status = array('status'=>'success', 'multiple'=>false, 'data'=>$response_result);
				}
			}else{
				$status = array('status'=>'fail', 'multiple'=>false, 'data'=>[]);
			}
		}
	}
	if(empty($status['data']) || (isset($status['status']) && $status['status']==='fail')){
		// Try a partial match if still nothing found.
		if(empty($status['data'])) {
			$keyword_like = '%' . preg_replace('/[\\s\\?]+/', '%', $keyword) . '%';
			$results = $wpdb->get_results( $wpdb->prepare("SELECT `id`, `query`, `response` FROM %i WHERE `query` LIKE %s", $table, $keyword_like) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$response_result = array();
			if(!empty($results)){
				foreach($results as $result){
					$response_result[] = array('id'=>$result->id,'query'=>$result->query, 'response'=>$result->response, 'score'=>1);
				}
				if(count($response_result)>1){
					$status = array('status'=>'success', 'multiple'=>true, 'data'=>$response_result);
				}else{
					$status = array('status'=>'success', 'multiple'=>false, 'data'=>$response_result);
				}
			} else {
				$status = array('status'=>'fail', 'multiple'=>false, 'data'=>[], 'message'=>'Sorry, I found nothing');
			}
		}
	}
	if(empty($status['data'])){
		$status = array('status'=>'fail', 'multiple'=>false, 'data'=>[], 'message'=>'no result found');
	}
	echo wp_json_encode($status);

	die();

}

function qcld_strpro_remove_stopwords($keyword){
	
	if(get_option('qlcd_wp_chatbot_stop_words') && get_option('qlcd_wp_chatbot_stop_words')!=''){
		$commonWords = explode(',', get_option('qlcd_wp_chatbot_stop_words'));
		return preg_replace('/\b('.implode('|',$commonWords).')\b/','',$keyword);
	}else{
		return $keyword;
	}
	
 
	
}