PluginProbe
WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services / 4.7.7
WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services v4.7.7
8.8.0 8.7.9 8.7.8 8.7.7 8.7.6 8.7.5 8.7.4 8.7.3 8.7.2 8.7.1 8.7.0 8.6.9 8.6.8 8.6.7 8.6.6 8.6.5 8.6.4 8.6.2 8.6.1 8.6.0 8.5.9 8.5.8 8.5.7 8.5.6 8.5.5 All 536 releases
← All changes | qcld-wpwbot-search.php +77 -739 8.5.4 → 4.7.7 View file →
@@ -1,6 +1,5 @@
1 1 <?php
2 -if (!defined('ABSPATH')) exit; // Exit if accessed directly
3 2 /**
4 3 * Product indexing, caching & searching features concept is taken from open source 'Advanced wp Search' Wp plugin by ILLID.
5 4 */
6 5 //include_once( 'includes/class-wpwbot-cache.php' );
@@ -7,651 +6,62 @@
7 6
8 7 include_once( 'includes/class-wpwbot-table.php' );
9 8 include_once( 'includes/class-wpwbot-search.php' );
10 9
11 -// Helper function to generate variations for each word in a keyword
12 -if ( ! function_exists( '_wpbot_generate_word_variations' ) ) {
13 - function _wpbot_generate_word_variations($keyword) {
14 - $keyword = strtolower(trim($keyword));
15 - $words = preg_split('/\s+/', $keyword, -1, PREG_SPLIT_NO_EMPTY);
16 - $all_word_variations = [];
17 -
18 - foreach ($words as $word) {
19 - $variations = [$word];
20 -
21 - // Simple pluralization/singularization and common suffix handling
22 - if (strlen($word) > 1) { // Avoid stemming single letters
23 - // Remove 's' (e.g., 'inspections' -> 'inspection')
24 - if (substr($word, -1) === 's') {
25 - $variations[] = substr($word, 0, -1);
26 - } else { // Add 's' (e.g., 'inspection' -> 'inspections')
27 - $variations[] = $word . 's';
28 - }
29 - // Handle 'er' suffix (e.g., 'inspector' -> 'inspect')
30 - if (substr($word, -2) === 'er') {
31 - $variations[] = substr($word, 0, -2);
32 - }
33 - // Handle 'ing' suffix (e.g., 'inspecting' -> 'inspect')
34 - if (substr($word, -3) === 'ing') {
35 - $variations[] = substr($word, 0, -3);
36 - }
37 - // Add 'er' if the base word is 'inspect' and 'inspector' is not present
38 - if (strpos($word, 'inspect') !== false && strpos($word, 'inspector') === false) {
39 - $variations[] = str_replace('inspect', 'inspector', $word);
40 - }
41 - }
42 - $all_word_variations[] = array_filter(array_unique($variations));
43 - }
44 - return $all_word_variations;
45 - }
46 -}
47 -
48 -// Filter function to modify WP_Query search for flexible matching
49 -if ( ! function_exists( 'wpbot_flexible_search_filter' ) ) {
50 - function wpbot_flexible_search_filter($search, $wp_query) {
51 - global $wpdb, $wpbot_search_word_variations;
52 -
53 - // Only apply if it's the main search query and our variations are set
54 - if (empty($wpbot_search_word_variations) || !$wp_query->is_search || !$wp_query->is_main_query()) {
55 - return $search;
56 - }
57 -
58 - $search_parts_for_and = [];
59 -
60 - foreach ($wpbot_search_word_variations as $word_variations) {
61 - $search_parts_for_or = [];
62 - foreach ($word_variations as $term) {
63 - $term = $wpdb->esc_like($term);
64 - // Search in both post_title and post_content
65 - $search_parts_for_or[] = "(({$wpdb->posts}.post_title LIKE '%{$term}%') OR ({$wpdb->posts}.post_content LIKE '%{$term}%'))";
66 - }
67 - if (!empty($search_parts_for_or)) {
68 - $search_parts_for_and[] = '(' . implode(' OR ', $search_parts_for_or) . ')';
69 - }
70 - }
71 -
72 - if (!empty($search_parts_for_and)) {
73 - // Completely replace the default search clause generated by WP_Query's 's' parameter
74 - // This ensures our flexible matching takes precedence.
75 - $search = ' AND ' . implode(' AND ', $search_parts_for_and);
76 - }
77 -
78 - return $search;
79 - }
80 -}
81 -
82 10 function wpbo_search_site() {
83 - // Verify nonce for security
84 - $nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : (isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '');
85 - if ( ! wp_verify_nonce( $nonce, 'wp_chatbot' ) && ! wp_verify_nonce( $nonce, 'qcsecretbotnonceval123qc' ) ) {
86 - wp_send_json_error( array( 'status' => 'fail', 'message' => 'Security check failed.' ) );
87 - wp_die();
88 - }
89 -
90 - global $wpdb;
91 - // Limit results to 5 items.
92 - $limit = 5;
93 - $response = array('status' => 'fail', 'html' => ''); // Initialize response array
11 +
12 + $results = new WP_Query( array(
13 + 'post_type' => array( 'post', 'page' ),
14 + 'post_status' => 'publish',
15 + 'nopaging' => true,
16 + 'posts_per_page'=> 10,
17 + 's' => stripslashes( $_POST['keyword'] ),
18 + ) );
19 +
94 20
95 - // Get default language for load more button text and other language-specific checks.
96 - $default_language = get_locale();
97 -
98 - if(get_option('enable_wp_chatbot_post_content') == 1){
99 - $keyword = isset( $_POST['keyword'] ) ? sanitize_text_field(wp_unslash($_POST['keyword'])) : '';
100 - $all_word_variations = _wpbot_generate_word_variations($keyword);
101 -
102 - // Temporarily store the variations for the filter
103 - global $wpbot_search_word_variations;
104 - $wpbot_search_word_variations = $all_word_variations;
105 -
106 - // Add the custom search filter
107 - add_filter('posts_search', 'wpbot_flexible_search_filter', 10, 2);
108 -
109 - // $enable_post_types = array( 'post', 'page'); // This line is commented out, so post_type is not restricted here.
110 - $total_items = $limit;
111 - $query_arg = array(
112 - // 'post_type' => $enable_post_types,
113 - 'post_status' => 'publish',
114 - 'posts_per_page'=> $total_items,
115 - 's' => stripslashes( $keyword ), // Keep original for WP_Query to initiate search, filter will override
116 - 'paged' => 1,
117 - 'suppress_filters' => false // Crucial for filters to run
118 - );
119 - $resultss = new WP_Query( $query_arg );
120 - $results = $resultss->posts;
121 -
122 - // Remove the filter after the query to avoid affecting other queries
123 - remove_filter('posts_search', 'wpbot_flexible_search_filter', 10);
124 - unset($wpbot_search_word_variations); // Clean up global
125 - }else{
126 - $keyword = isset( $_POST['keyword'] ) ? sanitize_text_field(wp_unslash($_POST['keyword'])) : '';
127 - $all_word_variations = _wpbot_generate_word_variations($keyword);
128 -
129 - $sql_parts_for_and = [];
130 - $sql_params = [];
131 -
132 - foreach ($all_word_variations as $word_variations) {
133 - $sql_parts_for_or = [];
134 - foreach ($word_variations as $term) {
135 - $sql_parts_for_or[] = "post_title LIKE %s";
136 - $sql_params[] = '%' . $wpdb->esc_like($term) . '%';
137 - }
138 - if (!empty($sql_parts_for_or)) {
139 - $sql_parts_for_and[] = '(' . implode(' OR ', $sql_parts_for_or) . ')';
140 - }
141 - }
142 -
143 - $where_clause = '';
144 - if (!empty($sql_parts_for_and)) {
145 - $where_clause = ' AND ' . implode(' AND ', $sql_parts_for_and);
146 - } else {
147 - // Fallback to original behavior if no variations generated (e.g., empty keyword)
148 - $where_clause = " AND (post_title LIKE %s)";
149 - $sql_params[] = '%' . $wpdb->esc_like($keyword) . '%';
150 - }
151 -
152 - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
153 - $results = $wpdb->get_results( $wpdb->prepare(
154 - "SELECT * FROM " . $wpdb->prefix . "posts WHERE post_status = %s " . $where_clause . " ORDER BY ID DESC LIMIT %d",
155 - array_merge(['publish'], $sql_params, [$limit])
156 - ) );
157 - }
158 -
159 - if(!empty( $results )){
160 -
161 - $response['status'] = 'success';
162 - $response['html'] = '<div class="wpb-search-result">';
163 - $total_post = 0;
164 - $responses = '';
165 -
166 - foreach ( $results as $result ) {
167 - $featured_img_url = get_the_post_thumbnail_url( $result->ID, 'full' );
168 - $excerpt = '';
169 - if ( isset( $result->ID ) ) {
170 - $post_obj = get_post( $result->ID );
171 - if ( $post_obj ) {
172 - if ( has_excerpt( $result->ID ) ) {
173 - $excerpt = get_the_excerpt( $result->ID );
174 - } else {
175 - $content = $post_obj->post_content;
176 -
177 - // Remove ALL WPBakery shortcodes (paired + self-closing)
178 - $content = preg_replace( '/\[vc_[^\]]*\](.*?)\[\/vc_[^\]]*\]/s', '$1', $content ); // paired
179 - $content = preg_replace( '/\[vc_[^\]]*\]/s', '', $content ); // self-closing
180 - $content = preg_replace('/\[\/?[\w\-]+[^\]]*\]/', '', $content);
181 - // Extra: remove any leftover [] shortcodes (just in case)
182 - $content = strip_shortcodes( $content );
183 -
184 - // Run through normal WP content filters
185 - $content_filtered = apply_filters( 'the_content', $content );
186 -
187 - // Strip HTML tags, then trim
188 - $excerpt = wp_trim_words( wp_strip_all_tags( $content_filtered ), 20, '...' );
189 - }
190 -
191 -
192 - }
193 - }
194 -
195 - $total_post = $total_post + 1;
196 - $responses .='<div class="wpbot_card_wraper">';
197 - $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.'"':'').'>';
198 - if( !empty($featured_img_url) ){
199 - $responses .= '<img src="'.esc_url_raw($featured_img_url).'" />';
200 - }
201 - $responses .= '<div class="wpbot_card_caption '.( empty($featured_img_url) ?'wpbot_card_caption_saas':'').'">';
202 - $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>';
203 - $responses .= '<p>'.esc_html($excerpt).'</p>';
204 - if($result->post_type=='product'){
205 - if ( class_exists( 'WooCommerce' ) ) {
206 - if ( $result->ID ) {
207 - $product = wc_get_product( $result->ID );
208 - $responses .= '<p class="wpbot_product_price">'.get_woocommerce_currency_symbol().$product->get_price_html().'</p>';
209 - }
210 - }
211 - }
212 - $responses .= '</div>';
213 - $responses .= '</a></div>';
214 - $responses .='</div>';
215 -
216 - }
217 - $response['html'] .= $responses;
218 - $response['html'] .='</div>';
219 - if($total_post >= $limit ){ // Use $limit for consistency
220 - $load_more = maybe_unserialize(get_option('qlcd_wp_chatbot_load_more_search'));
221 -
222 - $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>';
223 -
224 - }
225 - }else{
226 - // Fuzzy search if initial search yields no results
227 - $response['status'] = 'success';
228 -
229 - // Use the same word variation logic for fuzzy search
230 - $all_word_variations_for_fuzzy = _wpbot_generate_word_variations($keyword);
231 -
232 - $unique_posts = array(); // Store unique post objects
233 - $seen_ids = array(); // Keep track of seen post IDs
234 -
235 - // Iterate through each word's variations to find matching posts
236 - foreach ( $all_word_variations_for_fuzzy as $word_variations ) {
237 - foreach ($word_variations as $term) {
238 - $term = $wpdb->esc_like( $term );
239 -
240 - $term_results = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
241 - $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 .'%')
242 - );
243 -
244 - foreach ($term_results as $res) {
245 - if (!in_array($res->ID, $seen_ids)) {
246 - $unique_posts[] = $res;
247 - $seen_ids[] = $res->ID;
248 - }
249 - }
250 - }
251 - }
252 - $results = $unique_posts; // Now $results contains unique WP_Post objects
253 -
254 - if( !empty( $results) ){
255 - $response['html'] = '<div class="wpb-search-result">';
256 - $total_post = 0;
257 - $responses = '';
258 - $selected_lan = get_option('qlcd_wp_chatbot_default_language');
259 -
260 - foreach ($results as $value) { // $value is a single post object here
261 - if(!empty($value->guid)){
262 - $post_id = $value->ID;
263 - $current_featured_img_url = get_the_post_thumbnail_url( $post_id, 'full' );
264 -
265 - // Corrected URL segment parsing for language check
266 - $url_path = wp_parse_url(get_permalink($post_id), PHP_URL_PATH);
267 - $url_segments = array_filter(explode('/',$url_path));
268 -
269 - // Assuming the language slug is the first non-empty segment of the URL path.
270 - $first_segment = !empty($url_segments) ? reset($url_segments) : '';
271 -
272 - // If $selected_lan is empty, the language check is effectively skipped.
273 - $language_match = empty($selected_lan) || ($first_segment == $selected_lan);
274 -
275 - if($language_match){
276 - $total_post = $total_post + 1;
277 - $responses .='<div class="wpbot_card_wraper">';
278 - $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">';
279 - if(!empty($current_featured_img_url)){
280 - $responses .= '<img src="'.esc_url_raw($current_featured_img_url).'" />';
281 - }
282 - $responses .= '<div class="wpbot_card_caption '.(empty($current_featured_img_url)?'wpbot_card_caption_saas':'').'">';
283 - $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>';
284 - $responses .= '</div>';
285 - $responses .= '</a></div>';
286 - $responses .='</div>';
287 - }
288 - }
289 - }
290 - if($total_post > 2 ){ // This condition is different from the first block ($total_post >= $limit)
291 - $load_more = maybe_unserialize(get_option('qlcd_wp_chatbot_load_more_search'));
292 - $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>';
293 - $response['status'] = 'success';
294 - }else{
295 - $response['status'] = 'fail';
296 - }
297 -
298 - $response['html'] .= $responses;
299 - $response['html'] .='</div>';
300 - } else {
301 - $response['status'] = 'fail'; // No results from fuzzy search either
302 - }
303 - }
304 - echo wp_json_encode($response);
305 - wp_die();
306 -}
307 -
308 -
309 -add_action( 'wp_ajax_wpbo_search_site', 'wpbo_search_site' );
310 -add_action( 'wp_ajax_nopriv_wpbo_search_site', 'wpbo_search_site' );
311 -
312 -if ( ! function_exists( 'qcld_wpbot_modified_keyword' ) ) {
313 - function qcld_wpbot_modified_keyword( $keyword ) {
314 - $keyword = rtrim( $keyword, '!' );
315 - $pattern = '/[?\/]/';
316 - $strings = preg_split( $pattern, $keyword );
317 - $strings = array_filter( array_map( 'trim', $strings ) );
318 - $keyword = rtrim( $strings[0], '!' );
319 - return htmlspecialchars_decode( $keyword );
320 - }
321 -}
322 -
323 -add_action( 'wp_ajax_wpbo_search_responseby_intent', 'qcld_wpbo_search_responseby_intent' );
324 -add_action( 'wp_ajax_nopriv_wpbo_search_responseby_intent', 'qcld_wpbo_search_responseby_intent' );
325 -
326 -if( !function_exists( 'wpbo_search_site_pagination' )){
327 -
328 - function wpbo_search_site_pagination() {
329 - global $wpdb;
330 -
331 - // Verify nonce for security
332 - $p_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
333 - if ( ! wp_verify_nonce( $p_nonce, 'wp_chatbot' ) && ! wp_verify_nonce( $p_nonce, 'qcsecretbotnonceval123qc' ) ) {
334 - wp_send_json_error( array( 'message' => 'Security check failed' ) );
335 - wp_die();
336 - }
337 -
338 - // Sanitize and validate inputs
339 - $keyword = isset( $_POST['keyword'] ) ? sanitize_text_field(wp_unslash($_POST['keyword'])) : '';
340 - $post_type = isset( $_POST['type'] ) ? sanitize_text_field(wp_unslash($_POST['type'])) : 'post';
341 - $page = isset($_POST['page']) ? absint( wp_unslash($_POST['page']) ) : 0;
342 21
343 - // Validate post type against allowed types
344 - $allowed_post_types = array( 'post', 'page', 'product' );
345 - if ( ! in_array( $post_type, $allowed_post_types, true ) ) {
346 - $post_type = 'post';
347 - }
348 -
349 - $enable_post_types = get_option( 'wppt_post_types' );
350 - $load_more = maybe_unserialize( get_option( 'qlcd_wp_chatbot_load_more' ) );
351 -
352 - if ( is_array( $load_more ) && isset( $load_more[ get_locale() ] ) ) {
353 - $load_more = $load_more[ get_locale() ];
354 - }
355 - if ( is_array( $load_more ) ) {
356 - $load_more = $load_more[ array_rand( $load_more ) ];
357 - }
358 - $searchlimit = ( get_option( 'wppt_number_of_result' ) == '' ? 5 : absint( get_option( 'wppt_number_of_result' ) ) );
359 - $orderby = ( get_option( 'wppt_result_orderby' ) == '' ? 'none' : get_option( 'wppt_result_orderby' ) );
360 - $order = ( get_option( 'wppt_result_order' ) == '' ? 'ASC' : get_option( 'wppt_result_order' ) );
361 - $thumb = ( get_option( 'wpbot_search_image_size' ) ? get_option( 'wpbot_search_image_size' ) : 'thumbnail' );
362 - // order by setup
363 - $new_window = get_option( 'wpbot_search_result_new_window' );
364 -
365 - $total_items = absint( get_option( 'wppt_number_of_result' ) );
366 - if ( $total_items < 1 ) {
367 - $total_items = 5;
368 - }
369 -
370 - $searchkeyword = qcld_wpbot_modified_keyword( $keyword );
371 -
372 - $response = array();
22 + $response = array();
373 23 $response['status'] = 'fail';
374 - $response['html'] = '';
375 -
376 - // Use prepared statements to prevent SQL injection
377 - if ( get_option( 'active_advance_query' ) != '1' ) {
378 - // Simple query - search in post_title only
379 - $total_results = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
380 - $wpdb->prepare(
381 - "SELECT * FROM {$wpdb->prefix}posts
382 - WHERE post_type = %s
383 - AND post_status = 'publish'
384 - AND post_title LIKE %s
385 - ORDER BY ID DESC",
386 - $post_type,
387 - '%' . $wpdb->esc_like( $searchkeyword ) . '%'
388 - ));
389 - } else {
390 - // Advanced query - search in both post_title and post_content
391 - $total_results = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
392 - $wpdb->prepare(
393 - "SELECT * FROM " . $wpdb->prefix . "posts
394 - WHERE post_type = %s
395 - AND post_status = %s
396 - AND (post_title REGEXP %s OR post_content REGEXP %s)
397 - ORDER BY ID DESC",
398 - $post_type,
399 - 'publish',
400 - '[[:<:]]' . $searchkeyword . '[[:>:]]',
401 - '[[:<:]]' . $searchkeyword . '[[:>:]]'
402 - ));
403 - }
404 -
405 - if ( ! empty( $total_results ) ) {
406 -
407 - // Validate and sanitize orderby parameter
408 - $valid_orderby = array( 'title', 'date', 'modified', 'none', 'rand' );
409 - if ( ! in_array( $orderby, $valid_orderby, true ) ) {
410 - $orderby = 'none';
24 +
25 + if ( !empty( $results->posts ) ) {
26 + $response['status'] = 'success';
27 + $response['html'] = '<div class="wpb-search-result">';
28 + $response['html'] .= esc_html_e('<p>We have found '.count($results->posts).' results for <b>'.$_POST['keyword'].'</b></p>.','wpbot');
29 + foreach ( $results->posts as $result ) {
30 + $response['html'] .= '<a href="'.$result->guid.'" target="_blank">'.$result->post_title.'</a>';
411 31 }
412 -
413 - if ( $orderby == 'title' ) {
414 - $orderby = 'post_title';
415 - }
416 - if ( $orderby == 'date' ) {
417 - $orderby = 'post_date';
418 - }
419 - if ( $orderby == 'modified' ) {
420 - $orderby = 'post_modified';
421 - }
422 -
423 - // Validate order parameter
424 - $order = strtoupper( $order );
425 - if ( ! in_array( $order, array( 'ASC', 'DESC' ), true ) ) {
426 - $order = 'ASC';
427 - }
428 -
429 - // Build query with pagination
430 - $offset = absint( $total_items * $page );
431 -
432 - if ( get_option( 'active_advance_query' ) != '1' ) {
433 - if ( $orderby != 'none' && $orderby != 'rand' ) {
434 - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
435 - $results = $wpdb->get_results( $wpdb->prepare(
436 - "SELECT * FROM {$wpdb->prefix}posts
437 - WHERE post_type = %s
438 - AND post_status = 'publish'
439 - AND post_title LIKE %s
440 - ORDER BY {$orderby} {$order}
441 - LIMIT %d, %d",
442 - $post_type,
443 - '%' . $wpdb->esc_like( $searchkeyword ) . '%',
444 - $offset,
445 - $total_items
446 - ) );
447 - } else {
448 - $results = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
449 - $wpdb->prepare(
450 - "SELECT * FROM {$wpdb->prefix}posts
451 - WHERE post_type = %s
452 - AND post_status = 'publish'
453 - AND post_title LIKE %s
454 - ORDER BY ID DESC
455 - LIMIT %d, %d",
456 - $post_type,
457 - '%' . $wpdb->esc_like( $searchkeyword ) . '%',
458 - $offset,
459 - $total_items
460 - ));
461 - }
462 - } else {
463 - if ( $orderby != 'none' && $orderby != 'rand' ) {
464 - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
465 - $results = $wpdb->get_results( $wpdb->prepare(
466 - "SELECT * FROM " . $wpdb->prefix . "posts
467 - WHERE post_type = %s
468 - AND post_status = %s
469 - AND (post_title REGEXP %s OR post_content REGEXP %s)
470 - ORDER BY " . $orderby . " " . $order . "
471 - LIMIT %d, %d",
472 - $post_type,
473 - 'publish',
474 - '[[:<:]]' . $searchkeyword . '[[:>:]]',
475 - '[[:<:]]' . $searchkeyword . '[[:>:]]',
476 - $offset,
477 - $total_items
478 - ) );
479 - } else {
480 - $results = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
481 - $wpdb->prepare(
482 - "SELECT * FROM " . $wpdb->prefix . "posts
483 - WHERE post_type = %s
484 - AND post_status = %s
485 - AND (post_title REGEXP %s OR post_content REGEXP %s)
486 - ORDER BY ID DESC
487 - LIMIT %d, %d",
488 - $post_type,
489 - 'publish',
490 - '[[:<:]]' . $searchkeyword . '[[:>:]]',
491 - '[[:<:]]' . $searchkeyword . '[[:>:]]',
492 - $offset,
493 - $total_items
494 - ));
495 - }
496 - }
497 - } else {
498 - if ( class_exists( 'SitePress' ) ) {
499 - global $sitepress;
500 - $selected_lan = isset( $_POST['language'] ) ? sanitize_text_field(wp_unslash($_POST['language'])) : '';
501 - $selected_lan = explode( '_', $selected_lan );
502 - if ( ! empty( $selected_lan[0] ) ) {
503 - $sitepress->switch_lang( $selected_lan[0], true );
504 - }
505 - }
506 -
507 - $query_arg = array(
508 - 'post_type' => $post_type,
509 - 'post_status' => 'publish',
510 - 'posts_per_page' => $total_items,
511 - 's' => stripslashes( $keyword ),
512 - 'paged' => ( $page + 1 ),
513 - 'orderby' => $orderby,
514 - );
515 -
516 - if ( class_exists( 'SitePress' ) ) {
517 - global $sitepress;
518 - $selected_lan = isset( $_POST['language'] ) ? sanitize_text_field(wp_unslash($_POST['language'])) : '';
519 - $selected_lan = explode( '_', $selected_lan );
520 - if ( ! empty( $selected_lan[0] ) ) {
521 - $sitepress->switch_lang( $selected_lan[0], true );
522 - }
523 - }
524 -
525 - $query_arg['suppress_filters'] = true;
526 - if ( $orderby != 'none' && $orderby != 'rand' ) {
527 - $query_arg['order'] = $order;
528 - }
529 -
530 - $totalresults = new WP_Query(
531 - array(
532 - 'post_type' => $post_type,
533 - 'post_status' => 'publish',
534 - 's' => stripslashes( $keyword ),
535 - )
536 - );
537 - $resultss = new WP_Query( $query_arg );
538 - $total_results = $totalresults->posts;
539 - $resultss = new WP_Query( $query_arg );
540 - $results = $resultss->posts;
32 + $response['html'] .='</div>';
33 + }else{
34 + $texts = unserialize(get_option('qlcd_wp_chatbot_no_result'));
35 + $response['html'] = esc_html($texts[array_rand($texts)]);
541 36 }
37 +
38 + echo json_encode($response);
542 39
543 - if ( ! empty( $total_results ) ) {
544 -
545 - $selected_lan = isset( $_POST['language'] ) ? sanitize_text_field(wp_unslash($_POST['language'])) : '';
546 - $urlss = get_option( 'wpbotml_url_urls' ) ? get_option( 'wpbotml_url_urls' ) : '';
547 - $imagesize = ( get_option( 'wpbot_search_image_size' ) != '' ? get_option( 'wpbot_search_image_size' ) : 'thumbnail' );
548 -
549 - $response['html'] .= '<div class="wpb-search-result">';
550 -
551 - foreach ( $total_results as $result ) {
552 -
553 - if ( $result->post_type == 'product' ) {
554 - if ( ! class_exists( 'WooCommerce' ) ) {
555 - continue;
556 - }
557 - }
558 -
559 - $featured_img_url = get_the_post_thumbnail_url( $result->ID, $thumb );
560 - $excerpt = '';
561 - if ( isset( $result->ID ) ) {
562 - $post_obj = get_post( $result->ID );
563 - if ( $post_obj ) {
564 - if ( has_excerpt( $result->ID ) ) {
565 - $excerpt = get_the_excerpt( $result->ID );
566 - } else {
567 - $content = $post_obj->post_content;
568 -
569 - // Remove ALL WPBakery shortcodes (paired + self-closing)
570 - $content = preg_replace( '/\[vc_[^\]]*\](.*?)\[\/vc_[^\]]*\]/s', '$1', $content ); // paired
571 - $content = preg_replace( '/\[vc_[^\]]*\]/s', '', $content ); // self-closing
572 - $content = preg_replace('/\[\/?[\w\-]+[^\]]*\]/', '', $content);
573 - // Extra: remove any leftover [] shortcodes (just in case)
574 - $content = strip_shortcodes( $content );
575 -
576 - // Run through normal WP content filters
577 - $content_filtered = apply_filters( 'the_content', $content );
578 -
579 - // Strip HTML tags, then trim
580 - $excerpt = wp_trim_words( wp_strip_all_tags( $content_filtered ), 20, '...' );
581 - }
582 - }
583 - }
584 -
585 -
586 - $response['html'] .= '<div class="wpbot_card_wraper">';
587 - $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 ) . '"' : '' ) . '>';
588 - if ( $featured_img_url != '' ) {
589 - $response['html'] .= '<img src="' . esc_url_raw( $featured_img_url ) . '" />';
590 - }
591 -
592 - $response['html'] .= '<div class="wpbot_card_caption ' . ( $featured_img_url == '' ? 'wpbot_card_caption_saas' : '' ) . '">';
593 - $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>';
594 - $response['html'] .= '<p class="wpbot_card_description">' . esc_html( $excerpt ) . '</p>';
595 - if ( $result->post_type == 'product' ) {
596 - if ( class_exists( 'WooCommerce' ) ) {
597 - $product = wc_get_product( $result->ID );
598 - $response['html'] .= '<p class="wpbot_product_price">' . get_woocommerce_currency_symbol() . $product->get_price_html() . '</p>';
599 - }
600 - }
601 - $response['html'] .= '</div>';
602 - $response['html'] .= '</a></div>';
603 - $response['html'] .= '</div>';
604 -
605 - }
606 -
607 -
608 - $response['html'] .= '</div>';
609 - $response['status'] = 'success';
610 -
611 - }
612 - wp_reset_query();
613 -
614 - if ( $response['status'] != 'success' ) {
615 - $texts = maybe_unserialize( get_option( 'qlcd_wp_chatbot_no_result' ) );
616 - $selected_lan = isset( $_POST['language'] ) ? sanitize_text_field(wp_unslash($_POST['language'])) : '';
617 - if ( ! empty( $texts ) && is_array( $texts ) && isset( $texts[ $selected_lan ][0] ) ) {
618 - $texts = str_replace( "\'", "'", $texts[ $selected_lan ][0] );
619 - $response['html'] = array( $texts );
620 - } else {
621 - $response['html'] = array( 'No results found' );
622 - }
623 - }
624 - wp_send_json( $response );
625 40 die();
626 41 }
627 42
628 -}
43 +add_action( 'wp_ajax_wpbo_search_site', 'wpbo_search_site' );
44 +add_action( 'wp_ajax_nopriv_wpbo_search_site', 'wpbo_search_site' );
629 45
46 +add_action( 'wp_ajax_wpbo_search_responseby_intent', 'qc_wpbo_search_responseby_intent' );
47 +add_action( 'wp_ajax_nopriv_wpbo_search_responseby_intent', 'qc_wpbo_search_responseby_intent' );
630 48
631 -
632 -add_action( 'wp_ajax_wpbo_search_site_pagination', 'wpbo_search_site_pagination' );
633 -add_action( 'wp_ajax_nopriv_wpbo_search_site_pagination', 'wpbo_search_site_pagination' );
634 -function qcld_wpbo_search_responseby_intent(){
635 -
49 +function qc_wpbo_search_responseby_intent(){
636 50 global $wpdb;
51 + $keyword = sanitize_text_field($_POST['keyword']);
52 + $table = $wpdb->prefix.'wpbot_response';
637 53
638 - $keyword = isset( $_POST['keyword'] ) ? sanitize_text_field(wp_unslash($_POST['keyword'])) : '';
639 -
640 - $table = $wpdb->prefix.'wpbot_response';
641 -
642 - $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
54 + $result = $wpdb->get_row("SELECT `response` FROM `$table` WHERE 1 and `intent` = '".$keyword."'");
643 55
644 56 $response = array('status'=>'fail');
645 57
646 58 if(!empty($result)){
647 -
648 59 $response['status'] = 'success';
649 60 $response['html'] = $result->response;
650 -
651 61 }
652 62
653 - echo wp_json_encode($response);
63 + echo json_encode($response);
654 64
655 65 die();
656 66
657 67 }
@@ -658,56 +68,54 @@
658 68
659 69 add_action( 'wp_ajax_wpbo_search_response_catlist', 'wpbo_search_response_catlist' );
660 70 add_action( 'wp_ajax_nopriv_wpbo_search_response_catlist', 'wpbo_search_response_catlist' );
661 71
662 -if( !function_exists( 'wpbo_search_response_catlist' )){
663 - function wpbo_search_response_catlist(){
664 - global $wpdb;
665 - $table = $wpdb->prefix.'wpbot_response_category';
666 - $status = array('status'=>'fail');
667 - $results = $wpdb->get_results($wpdb->prepare("SELECT * FROM %i", $table)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
668 - $response_result = array();
669 -
670 - if(!empty($results)){
671 - foreach($results as $result){
672 -
673 - $response_result[] = array('name'=>$result->name);
674 -
675 - }
72 +function wpbo_search_response_catlist(){
73 + global $wpdb;
74 + $table = $wpdb->prefix.'wpbot_response_category';
75 +
76 + $status = array('status'=>'fail');
77 + $results = $wpdb->get_results("SELECT * FROM `$table` WHERE 1");
78 + $response_result = array();
79 +
80 + if(!empty($results)){
81 + foreach($results as $result){
82 +
83 + $response_result[] = array('name'=>$result->name);
84 +
676 85 }
677 -
678 - if(!empty($response_result)){
86 + }
87 +
88 + if(!empty($response_result)){
679 89
680 - $status = array('status'=>'success', 'data'=>$response_result);
681 -
682 -
683 - }
90 + $status = array('status'=>'success', 'data'=>$response_result);
684 91
685 - echo wp_json_encode($status);
686 92
687 - die();
688 -
689 93 }
94 +
95 + echo json_encode($status);
96 +
97 + die();
98 +
690 99 }
691 -add_action( 'wp_ajax_wpbo_search_response', 'qcld_wpbo_search_response' );
692 -add_action( 'wp_ajax_nopriv_wpbo_search_response', 'qcld_wpbo_search_response' );
693 100
101 +add_action( 'wp_ajax_wpbo_search_response', 'qc_wpbo_search_response' );
102 +add_action( 'wp_ajax_nopriv_wpbo_search_response', 'qc_wpbo_search_response' );
694 103
695 104
696 -function qcld_wpbo_search_response(){
697 105
106 +function qc_wpbo_search_response(){
698 107 global $wpdb;
699 - $keyword = isset( $_POST['keyword'] ) ? (sanitize_text_field(wp_unslash($_POST['keyword']))) : '';
700 - $strid = isset( $_POST['strid'] ) ? (sanitize_text_field(wp_unslash($_POST['strid']))) : '';
701 - $table = $wpdb->prefix.'wpbot_response';
108 + $keyword = (sanitize_text_field($_POST['keyword']));
109 + $strid = (sanitize_text_field($_POST['strid']));
110 + $table = $wpdb->prefix.'wpbot_response';
702 111
703 112
704 113 $response_result = array();
705 114
706 115 $status = array('status'=>'fail', 'multiple'=>false);
707 - $field = "ID";
708 116 if(($strid != '') && empty($response_result)){
709 - $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
117 + $results = $wpdb->get_results("SELECT * FROM `$table` WHERE `ID` = ".$strid);
710 118 if(!empty($results)){
711 119 foreach($results as $result){
712 120
713 121 $response_result[] = array('id'=>$result->id,'query'=>$result->query, 'response'=>$result->response, 'score'=>1);
@@ -714,11 +122,10 @@
714 122
715 123 }
716 124 }
717 125 }
718 - $field = "query";
719 - $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
720 126
127 + $results = $wpdb->get_results("SELECT `id`, `query`, `response` FROM `$table` WHERE 1 and `query` = '".$keyword."'");
721 128
722 129 if(!empty($results)){
723 130 foreach($results as $result){
724 131
@@ -725,12 +132,10 @@
725 132 $response_result[] = array('id'=>$result->id,'query'=>$result->query, 'response'=>$result->response, 'score'=>1);
726 133
727 134 }
728 135 }
729 -
730 - $field = "category";
731 136 if(empty($response_result)){
732 - $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
137 + $results = $wpdb->get_results("SELECT `id`, `query`, `response` FROM `$table` WHERE 1 and `category` = '".$keyword."'");
733 138
734 139
735 140 if(!empty($results)){
736 141 foreach($results as $result){
@@ -741,9 +146,9 @@
741 146 }else{
742 147 $status = array('status'=>'success', 'category'=> true, 'multiple'=>false, 'data'=>$response_result);
743 148 }
744 149
745 - echo wp_json_encode($status);
150 + echo json_encode($status);
746 151
747 152 die();
748 153 }
749 154
@@ -750,9 +155,9 @@
750 155 }
751 156
752 157 if(class_exists('Qcld_str_pro')){
753 158 if(get_option('qc_bot_str_remove_stopwords') && get_option('qc_bot_str_remove_stopwords')==1){
754 - $keyword = qcld_strpro_remove_stopwords($keyword);
159 + $keyword = qc_strpro_remove_stopwords($keyword);
755 160 }
756 161 }
757 162
758 163
@@ -758,51 +163,30 @@
758 163
759 164 if(empty($response_result)){
760 165
761 166 $fields = get_option('qc_bot_str_fields');
762 -
763 - $allowed_fields = array('query', 'keyword', 'response');
764 - $valid_fields = array();
765 -
766 - if($fields && !empty($fields) && is_array($fields)){
767 - foreach($fields as $field){
768 - if(in_array($field, $allowed_fields)){
769 - $valid_fields[] = '`' . $field . '`';
770 - }
771 - }
772 - }
773 -
774 - if(!empty($valid_fields)){
775 - $qfields = implode(', ', $valid_fields);
167 + if($fields && !empty($fields)){
168 + $qfields = implode(', ', $fields);
776 169 }else{
777 170 $qfields = '`query`,`keyword`,`response`';
778 171 }
779 -
780 -
172 + $sql = "ALTER TABLE `{$table}` ADD FULLTEXT($qfields);";
173 + $wpdb->query( $sql );
174 + $sql_text = "SELECT `id`, `query`, `response`, MATCH($qfields) AGAINST('".$keyword."' IN NATURAL LANGUAGE MODE) as score FROM $table WHERE MATCH($qfields) AGAINST('".$keyword."' IN NATURAL LANGUAGE MODE) order by score desc limit 15";
175 + $results = $wpdb->get_results($sql_text);
781 176
782 - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
783 - $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) );
784 -
785 177 $weight = get_option('qc_bot_str_weight')!=''?get_option('qc_bot_str_weight'):'0.4';
786 -
787 178 if(!empty($results)){
788 - $max_score = max(array_column($results, 'score'));
789 - if ($max_score <= 0) {
790 - $max_score = 1; // Set to 1 to avoid division by zero
791 - }
792 179 foreach($results as $result){
793 - if(($result->score/$max_score) >= $weight){
180 + if(($result->score) >= ($weight)){
794 181 $response_result[] = array('id'=>$result->id,'query'=>$result->query, 'response'=>$result->response, 'score'=>$result->score);
795 182 }
796 183 }
797 184 }
798 185 }
799 - $field = "keyword";
186 +
800 187 if( empty( $response_result ) ){
801 -
802 - $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
803 -
804 -
188 + $results = $wpdb->get_results("SELECT * FROM `$table` WHERE `keyword` REGEXP '".$keyword."'");
805 189 if(!empty($results)){
806 190 foreach($results as $result){
807 191 $response_result[] = array('id'=>$result->id,'query'=>$result->query, 'response'=>$result->response, 'score'=>1);
808 192 }
@@ -819,61 +203,15 @@
819 203 }
820 204 if(empty($result->query)){
821 205 $status = array('status'=>'fail', 'multiple'=>false, 'data'=>$response_result);
822 206 }
823 - if(empty($status['data']) || (isset($status['status']) && $status['status']==='fail')){
824 - // Check for space before question mark and try again.
825 - if(preg_match('/ \?$/', $keyword)){
826 - $keyword2 = preg_replace('/ \?$/', '?', $keyword);
827 - // Try again with new keyword.
828 - // Repeat the main search logic with $keyword2.
829 - $response_result = array();
830 - $field = "query";
831 - $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
832 - if(!empty($results)){
833 - foreach($results as $result){
834 - $response_result[] = array('id'=>$result->id,'query'=>$result->query, 'response'=>$result->response, 'score'=>1);
835 - }
836 - if(count($response_result)>1){
837 - $status = array('status'=>'success', 'multiple'=>true, 'data'=>$response_result);
838 - }else{
839 - $status = array('status'=>'success', 'multiple'=>false, 'data'=>$response_result);
840 - }
841 - }else{
842 - $status = array('status'=>'fail', 'multiple'=>false, 'data'=>[]);
843 - }
844 - }
845 - }
846 - if(empty($status['data']) || (isset($status['status']) && $status['status']==='fail')){
847 - // Try a partial match if still nothing found.
848 - if(empty($status['data'])) {
849 - $keyword_like = '%' . preg_replace('/[\\s\\?]+/', '%', $keyword) . '%';
850 - $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
851 - $response_result = array();
852 - if(!empty($results)){
853 - foreach($results as $result){
854 - $response_result[] = array('id'=>$result->id,'query'=>$result->query, 'response'=>$result->response, 'score'=>1);
855 - }
856 - if(count($response_result)>1){
857 - $status = array('status'=>'success', 'multiple'=>true, 'data'=>$response_result);
858 - }else{
859 - $status = array('status'=>'success', 'multiple'=>false, 'data'=>$response_result);
860 - }
861 - } else {
862 - $status = array('status'=>'fail', 'multiple'=>false, 'data'=>[], 'message'=>'Sorry, I found nothing');
863 - }
864 - }
865 - }
866 - if(empty($status['data'])){
867 - $status = array('status'=>'fail', 'multiple'=>false, 'data'=>[], 'message'=>'no result found');
868 - }
869 - echo wp_json_encode($status);
207 + echo json_encode($status);
870 208
871 209 die();
872 210
873 211 }
874 212
875 -function qcld_strpro_remove_stopwords($keyword){
213 +function qc_strpro_remove_stopwords($keyword){
876 214
877 215 if(get_option('qlcd_wp_chatbot_stop_words') && get_option('qlcd_wp_chatbot_stop_words')!=''){
878 216 $commonWords = explode(',', get_option('qlcd_wp_chatbot_stop_words'));
879 217 return preg_replace('/\b('.implode('|',$commonWords).')\b/','',$keyword);
@@ -882,5 +220,5 @@
882 220 }
883 221
884 222
885 223
886 -}
224 +}