PluginProbe
WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services / 8.7.7
WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services v8.7.7
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 8.5.4 8.5.3 8.5.2 All 533 releases
chatbot / qcld-wpwbot-search.php

qcld-wpwbot-search.php in WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services 8.7.7, at qcld-wpwbot-search.php

1,128 lines 46.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) exit; // Exit if accessed directly
3 /**
4 * Product indexing, caching & searching features concept is taken from open source 'Advanced wp Search' Wp plugin by ILLID.
5 */
6 //include_once( 'includes/class-wpwbot-cache.php' );
7
8 include_once( 'includes/class-wpwbot-table.php' );
9 include_once( 'includes/class-wpwbot-search.php' );
10
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 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
94
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', 'product' );
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.NotPrepared, 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", // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
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(wp_strip_all_tags($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
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 ) && ! empty( $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();
373 $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';
411 }
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.NotPrepared, 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", // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
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;
541 }
542
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 die();
626 }
627
628 }
629
630
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
636 global $wpdb;
637
638 $keyword = isset( $_POST['keyword'] ) ? sanitize_text_field(wp_unslash($_POST['keyword'])) : '';
639
640 $table = $wpdb->prefix . 'wpbot_response';
641 $table_sql = '`' . esc_sql( $table ) . '`';
642
643 $result = $wpdb->get_row( $wpdb->prepare( "SELECT `response` FROM {$table_sql} WHERE 1 AND `intent` = %s", $keyword ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
644
645 $response = array('status'=>'fail');
646
647 if(!empty($result)){
648
649 $response['status'] = 'success';
650 $response['html'] = $result->response;
651
652 }
653
654 echo wp_json_encode($response);
655
656 die();
657
658 }
659 function qcld_wb_chatbot_email_subscription() {
660
661 global $wpdb;
662 $table = $wpdb->prefix . 'wpbot_subscription';
663 $table_sql = '`' . esc_sql( $table ) . '`';
664
665 $name = sanitize_text_field( $_POST['name'] );// phpcs:ignore WordPress.Security.NonceVerification.Missing
666 $email = sanitize_email( $_POST['email'] );// phpcs:ignore WordPress.Security.NonceVerification.Missing
667 $url = esc_url_raw( $_POST['url'] );// phpcs:ignore WordPress.Security.NonceVerification.Missing
668 $user_agent = sanitize_text_field( $_SERVER['HTTP_USER_AGENT'] );// phpcs:ignore WordPress.Security.NonceVerification.Missing
669
670 if ( isset( $_POST['phone'] ) && $_POST['phone'] != '' ) {// phpcs:ignore WordPress.Security.NonceVerification.Missing
671
672 $phone = sanitize_text_field( $_POST['phone'] );// phpcs:ignore WordPress.Security.NonceVerification.Missing
673 if ( $email != '' ) {
674
675 $email_exists = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$table_sql} WHERE 1 AND email = %s", $email ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
676 if ( ! empty( $email_exists ) ) {
677 $wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
678 $table,
679 array(
680 'phone' => $phone,
681 ),
682 array( 'email' => $email ),
683 array(
684 '%s',
685 ),
686 array( '%s' )
687 );
688 } else {
689 $wpdb->insert( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
690 $table,
691 array(
692 'date' => current_time( 'mysql' ),
693 'name' => $name,
694 'email' => $email,
695 'phone' => $phone,
696 'url' => $url,
697 'user_agent' => $user_agent,
698 )
699 );
700 }
701 } else {
702 $wpdb->insert( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
703 $table,
704 array(
705 'date' => current_time( 'mysql' ),
706 'name' => $name,
707 'email' => $email,
708 'phone' => $phone,
709 'url' => $url,
710 'user_agent' => $user_agent,
711 )
712 );
713 }
714 $response['status'] = 'success';
715 echo json_encode( $response );
716 die();
717
718 } else {
719
720 $response = array();
721 $response['status'] = 'fail';
722
723 $email_exists = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$table_sql} WHERE 1 AND email = %s", $email ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
724 if ( empty( $email_exists ) ) {
725
726 $wpdb->insert( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
727 $table,
728 array(
729 'date' => current_time( 'mysql' ),
730 'name' => $name,
731 'email' => $email,
732 'url' => $url,
733 'user_agent' => $user_agent,
734 )
735 );
736 $response['status'] = 'success';
737 $texts = maybe_unserialize( get_option( 'qlcd_wp_email_subscription_success' ) );
738 if ( is_array( $texts ) && isset( $texts[ get_wpbot_locale() ] ) ) {
739 $texts = $texts[ get_wpbot_locale() ];
740 }
741 if ( is_array( $texts ) && ! empty( $texts ) ) {
742 $response['msg'] = $texts[ array_rand( $texts ) ];
743 } elseif ( is_string( $texts ) && ! empty( $texts ) ) {
744 $response['msg'] = $texts;
745 } else {
746 $response['msg'] = 'Thank you for subscribing.';
747 }
748
749 } else {
750 $texts = maybe_unserialize( get_option( 'qlcd_wp_email_already_subscribe' ) );
751
752 if ( is_array( $texts ) && isset( $texts[ get_wpbot_locale() ] ) ) {
753 $texts = $texts[ get_wpbot_locale() ];
754 }
755
756 if ( is_array( $texts ) && ! empty( $texts ) ) {
757 $response['msg'] = $texts[ array_rand( $texts ) ];
758 } elseif ( is_string( $texts ) && ! empty( $texts ) ) {
759 $response['msg'] = $texts;
760 } else {
761 $response['msg'] = 'You have already subscribed!';
762 }
763 }
764
765 do_action( 'qcld_mailing_list_subscription_success', $name, $email );
766
767 if ( get_option( 'qc_email_subscription_offer' ) == 1 ) {
768
769 $response['status'] = 'success';
770
771 if ( get_option( 'qlcd_wp_email_subscription_offer_subject' ) ) {
772 $offertextss = maybe_unserialize( get_option( 'qlcd_wp_email_subscription_offer_subject' ) );
773 if ( is_array( $offertextss ) && isset( $offertextss[ get_wpbot_locale() ] ) ) {
774 $offertextss = $offertextss[ get_wpbot_locale() ];
775 }
776 if ( is_array( $offertextss ) && ! empty( $offertextss ) ) {
777 $subject = str_replace( '%%username%%', $name, $offertextss[ array_rand( $offertextss ) ] );
778 } elseif ( is_string( $offertextss ) && ! empty( $offertextss ) ) {
779 $subject = str_replace( '%%username%%', $name, $offertextss );
780 } else {
781 $subject = 'Email subscription offer';
782 }
783
784 } else {
785 $subject = 'Email subscription offer';
786 }
787
788 // Extract Domain
789 $url = get_site_url();
790 $url = wp_parse_url( $url );
791 $domain = isset( $url['host'] ) ? $url['host'] : '';
792 $toEmail = $email;
793 $fromEmail = 'wordpress@' . $domain;
794 $fromname = ( get_option( 'qlcd_wp_chatbot_from_name' ) ? get_option( 'qlcd_wp_chatbot_from_name' ) : 'WordPress' );
795
796 if ( get_option( 'qlcd_wp_chatbot_from_email' ) && get_option( 'qlcd_wp_chatbot_from_email' ) != '' ) {
797 $fromEmail = get_option( 'qlcd_wp_chatbot_from_email' );
798 }
799
800 $replyto = $fromEmail;
801
802 if ( get_option( 'qlcd_wp_chatbot_reply_to_email' ) && get_option( 'qlcd_wp_chatbot_reply_to_email' ) != '' ) {
803 $replyto = get_option( 'qlcd_wp_chatbot_reply_to_email' );
804 }
805
806 // Starting messaging and status.
807 $offertexts = maybe_unserialize( get_option( 'qlcd_wp_email_subscription_offer' ) );
808 if ( is_array( $offertexts ) && isset( $offertexts[ get_wpbot_locale() ] ) ) {
809 $offertexts = $offertexts[ get_wpbot_locale() ];
810 }
811 // build email body.
812 $bodyContent = '';
813 $bodyContent .= '<p><strong>' . esc_html__( 'Offer Details', 'chatbot' ) . ':</strong></p><hr>';
814 if ( is_array( $offertexts ) && ! empty( $offertexts ) ) {
815 $bodyContent .= '<p>' . str_replace( '%%username%%', $name, $offertexts[ array_rand( $offertexts ) ] ) . '</p>';
816 } elseif ( is_string( $offertexts ) && ! empty( $offertexts ) ) {
817 $bodyContent .= '<p>' . str_replace( '%%username%%', $name, $offertexts ) . '</p>';
818 } else {
819 $bodyContent .= '<p></p>';
820 }
821 $bodyContent .= '<p>' . esc_html__( 'Mail Generated on', 'chatbot' ) . ': ' . current_time( 'F j, Y, g:i a' ) . '</p>';
822 $to = $toEmail;
823 $body = $bodyContent;
824
825 $headers = array();
826 $headers[] = 'Content-Type: text/html; charset=UTF-8';
827 $headers[] = 'From: ' . $fromname . ' <' . $fromEmail . '>';
828 $headers[] = 'Reply-To: ' . $fromname . ' <' . ( $replyto ) . '>';
829 wp_mail( $to, $subject, $body, $headers );
830 $response['email'] = 'Send! to ' . $to . ' from ' . $fromEmail;
831
832 }
833
834 echo json_encode( $response );
835
836 die();
837 }
838 }
839
840 add_action( 'wp_ajax_qcld_wb_chatbot_email_subscription', 'qcld_wb_chatbot_email_subscription' );
841 add_action( 'wp_ajax_nopriv_qcld_wb_chatbot_email_subscription', 'qcld_wb_chatbot_email_subscription' );
842 add_action( 'admin_post_wpbprint.csv', 'qcld_wpb_export_email_csv' );
843
844 if ( ! function_exists( 'qcld_wpbd_array2csv' ) ) {
845 function qcld_wpbd_array2csv( array &$array ) {
846 if ( count( $array ) == 0 ) {
847 return null;
848 }
849 ob_start();
850 // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fopen, WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- php://output memory stream for CSV export.
851 $df = fopen( 'php://output', 'w' );
852 fputcsv( $df, array( 'Name', 'Email' ), ',', '"', '\\' );
853 foreach ( $array as $row ) {
854 fputcsv( $df, $row, ',', '"', '\\' );
855 }
856 fclose( $df );
857 // phpcs:enable WordPress.WP.AlternativeFunctions.file_system_operations_fopen, WordPress.WP.AlternativeFunctions.file_system_operations_fclose
858 return ob_get_clean();
859 }
860 }
861
862 function qcld_wpb_export_email_csv() {
863 global $wpdb;
864 $table = $wpdb->prefix . 'wpbot_subscription';
865 $table_sql = '`' . esc_sql( $table ) . '`';
866
867 if ( ! current_user_can( 'manage_options' ) ) {
868 return;
869 }
870
871 $emails = $wpdb->get_results( "SELECT * FROM {$table_sql}" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
872 $childArray = array();
873 foreach ( $emails as $email ) {
874 $innerArray = array();
875 $innerArray[0] = $email->name;
876 $innerArray[1] = $email->email;
877 array_push( $childArray, $innerArray );
878 }
879 qcld_wpbd_download_send_headers( 'wpb_email_lists_' . current_time( 'Y-m-d' ) . '.csv' );
880
881 $result = qcld_wpbd_array2csv( $childArray );
882
883 print $result; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
884 die();
885 }
886 function qcld_wpbd_download_send_headers( $filename ) {
887 // disable caching
888 $now = gmdate( 'D, d M Y H:i:s' );
889 header( 'Expires: Tue, 03 Jul 2001 06:00:00 GMT' );
890 header( 'Cache-Control: max-age=0, no-cache, must-revalidate, proxy-revalidate' );
891 header( "Last-Modified: {$now} GMT" );
892
893 // force download
894 header( 'Content-Type: application/force-download' );
895
896 // disposition / encoding on response body
897 header( "Content-Disposition: attachment;filename={$filename}" );
898 header( 'Content-Transfer-Encoding: binary' );
899 }
900 add_action( 'wp_ajax_wpbo_search_response_catlist', 'wpbo_search_response_catlist' );
901 add_action( 'wp_ajax_nopriv_wpbo_search_response_catlist', 'wpbo_search_response_catlist' );
902
903 if( !function_exists( 'wpbo_search_response_catlist' )){
904 function wpbo_search_response_catlist(){
905 global $wpdb;
906 $table = $wpdb->prefix . 'wpbot_response_category';
907 $table_sql = '`' . esc_sql( $table ) . '`';
908 $status = array( 'status' => 'fail' );
909 $results = $wpdb->get_results( "SELECT * FROM {$table_sql}" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
910 $response_result = array();
911
912 if(!empty($results)){
913 foreach($results as $result){
914
915 $response_result[] = array('name'=>$result->name);
916
917 }
918 }
919
920 if(!empty($response_result)){
921
922 $status = array('status'=>'success', 'data'=>$response_result);
923
924
925 }
926
927 echo wp_json_encode($status);
928
929 die();
930
931 }
932 }
933 add_action( 'wp_ajax_wpbo_search_response', 'qcld_wpbo_search_response' );
934 add_action( 'wp_ajax_nopriv_wpbo_search_response', 'qcld_wpbo_search_response' );
935
936
937
938 function qcld_wpbo_search_response(){
939
940 global $wpdb;
941 $keyword = isset( $_POST['keyword'] ) ? ( sanitize_text_field( wp_unslash( $_POST['keyword'] ) ) ) : '';
942 $strid = isset( $_POST['strid'] ) ? ( sanitize_text_field( wp_unslash( $_POST['strid'] ) ) ) : '';
943 $table = $wpdb->prefix . 'wpbot_response';
944 $table_sql = '`' . esc_sql( $table ) . '`';
945
946 $response_result = array();
947
948 $status = array( 'status' => 'fail', 'multiple' => false );
949 $field = 'ID';
950 if ( ( $strid != '' ) && empty( $response_result ) ) {
951 $results = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table_sql} WHERE `ID` = %d", $strid ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
952 if(!empty($results)){
953 foreach($results as $result){
954
955 $response_result[] = array('id'=>$result->id,'query'=>$result->query, 'response'=>$result->response, 'score'=>1);
956
957 }
958 }
959 }
960 $field = 'query';
961 $results = $wpdb->get_results( $wpdb->prepare( "SELECT `id`, `query`, `response` FROM {$table_sql} WHERE 1 AND `query` = %s", $keyword ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
962
963
964 if(!empty($results)){
965 foreach($results as $result){
966
967 $response_result[] = array('id'=>$result->id,'query'=>$result->query, 'response'=>$result->response, 'score'=>1);
968
969 }
970 }
971
972 $field = 'category';
973 if ( empty( $response_result ) ) {
974 $results = $wpdb->get_results( $wpdb->prepare( "SELECT `id`, `query`, `response` FROM {$table_sql} WHERE 1 AND `category` = %s", $keyword ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
975
976
977 if(!empty($results)){
978 foreach($results as $result){
979 $response_result[] = array('id'=>$result->id,'query'=>$result->query, 'response'=>$result->response, 'score'=>1);
980 }
981 if(count($response_result)>1){
982 $status = array('status'=>'success','category'=> true, 'multiple'=>true, 'data'=>$response_result);
983 }else{
984 $status = array('status'=>'success', 'category'=> true, 'multiple'=>false, 'data'=>$response_result);
985 }
986
987 echo wp_json_encode($status);
988
989 die();
990 }
991
992 }
993
994 if(class_exists('Qcld_str_pro')){
995 if(get_option('qc_bot_str_remove_stopwords') && get_option('qc_bot_str_remove_stopwords')==1){
996 $keyword = qcld_strpro_remove_stopwords($keyword);
997 }
998 }
999
1000
1001 if(empty($response_result)){
1002
1003 $fields = get_option('qc_bot_str_fields');
1004
1005 $allowed_fields = array('query', 'keyword', 'response');
1006 $valid_fields = array();
1007
1008 if($fields && !empty($fields) && is_array($fields)){
1009 foreach($fields as $field){
1010 if(in_array($field, $allowed_fields)){
1011 $valid_fields[] = '`' . $field . '`';
1012 }
1013 }
1014 }
1015
1016 if(!empty($valid_fields)){
1017 $qfields = implode(', ', $valid_fields);
1018 }else{
1019 $qfields = '`query`,`keyword`,`response`';
1020 }
1021
1022
1023
1024 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
1025 $results = $wpdb->get_results( $wpdb->prepare( "SELECT `id`, `query`, `response`, MATCH({$qfields}) AGAINST(%s IN NATURAL LANGUAGE MODE) as score FROM {$table_sql} WHERE MATCH({$qfields}) AGAINST(%s IN NATURAL LANGUAGE MODE) order by score desc limit 15", $keyword, $keyword ) );
1026
1027 $weight = get_option('qc_bot_str_weight')!=''?get_option('qc_bot_str_weight'):'0.4';
1028
1029 if(!empty($results)){
1030 $max_score = max(array_column($results, 'score'));
1031 if ($max_score <= 0) {
1032 $max_score = 1; // Set to 1 to avoid division by zero
1033 }
1034 foreach($results as $result){
1035 if(($result->score/$max_score) >= $weight){
1036 $response_result[] = array('id'=>$result->id,'query'=>$result->query, 'response'=>$result->response, 'score'=>$result->score);
1037 }
1038 }
1039 }
1040 }
1041 $field = 'keyword';
1042 if ( empty( $response_result ) ) {
1043
1044 $results = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table_sql} WHERE `keyword` REGEXP %s", $keyword ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
1045
1046
1047 if(!empty($results)){
1048 foreach($results as $result){
1049 $response_result[] = array('id'=>$result->id,'query'=>$result->query, 'response'=>$result->response, 'score'=>1);
1050 }
1051 }
1052 }
1053 if(!empty($response_result)){
1054
1055 if(count($response_result)>1){
1056 $status = array('status'=>'success', 'multiple'=>true, 'data'=>$response_result);
1057 }else{
1058 $status = array('status'=>'success', 'multiple'=>false, 'data'=>$response_result);
1059 }
1060
1061 }
1062 if(empty($result->query)){
1063 $status = array('status'=>'fail', 'multiple'=>false, 'data'=>$response_result);
1064 }
1065 if(empty($status['data']) || (isset($status['status']) && $status['status']==='fail')){
1066 // Check for space before question mark and try again.
1067 if(preg_match('/ \?$/', $keyword)){
1068 $keyword2 = preg_replace('/ \?$/', '?', $keyword);
1069 // Try again with new keyword.
1070 // Repeat the main search logic with $keyword2.
1071 $response_result = array();
1072 $field = 'query';
1073 $results = $wpdb->get_results( $wpdb->prepare( "SELECT `id`, `query`, `response` FROM {$table_sql} WHERE 1 AND `query` = %s", $keyword2 ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
1074 if(!empty($results)){
1075 foreach($results as $result){
1076 $response_result[] = array('id'=>$result->id,'query'=>$result->query, 'response'=>$result->response, 'score'=>1);
1077 }
1078 if(count($response_result)>1){
1079 $status = array('status'=>'success', 'multiple'=>true, 'data'=>$response_result);
1080 }else{
1081 $status = array('status'=>'success', 'multiple'=>false, 'data'=>$response_result);
1082 }
1083 }else{
1084 $status = array('status'=>'fail', 'multiple'=>false, 'data'=>[]);
1085 }
1086 }
1087 }
1088 if(empty($status['data']) || (isset($status['status']) && $status['status']==='fail')){
1089 // Try a partial match if still nothing found.
1090 if(empty($status['data'])) {
1091 $keyword_like = '%' . preg_replace('/[\\s\\?]+/', '%', $keyword) . '%';
1092 $results = $wpdb->get_results( $wpdb->prepare( "SELECT `id`, `query`, `response` FROM {$table_sql} WHERE `query` LIKE %s", $keyword_like ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
1093 $response_result = array();
1094 if(!empty($results)){
1095 foreach($results as $result){
1096 $response_result[] = array('id'=>$result->id,'query'=>$result->query, 'response'=>$result->response, 'score'=>1);
1097 }
1098 if(count($response_result)>1){
1099 $status = array('status'=>'success', 'multiple'=>true, 'data'=>$response_result);
1100 }else{
1101 $status = array('status'=>'success', 'multiple'=>false, 'data'=>$response_result);
1102 }
1103 } else {
1104 $status = array('status'=>'fail', 'multiple'=>false, 'data'=>[], 'message'=>'Sorry, I found nothing');
1105 }
1106 }
1107 }
1108 if(empty($status['data'])){
1109 $status = array('status'=>'fail', 'multiple'=>false, 'data'=>[], 'message'=>'no result found');
1110 }
1111 echo wp_json_encode($status);
1112
1113 die();
1114
1115 }
1116
1117 function qcld_strpro_remove_stopwords($keyword){
1118
1119 if(get_option('qlcd_wp_chatbot_stop_words') && get_option('qlcd_wp_chatbot_stop_words')!=''){
1120 $commonWords = explode(',', get_option('qlcd_wp_chatbot_stop_words'));
1121 return preg_replace('/\b('.implode('|',$commonWords).')\b/','',$keyword);
1122 }else{
1123 return $keyword;
1124 }
1125
1126
1127
1128 }