PluginProbe ʕ •ᴥ•ʔ
Reviews Feed – Add Testimonials and Customer Reviews From Google Reviews, Yelp, TripAdvisor, and More / 2.7.0
Reviews Feed – Add Testimonials and Customer Reviews From Google Reviews, Yelp, TripAdvisor, and More v2.7.0
2.11.0 2.10.0 2.9.0 2.8.0 2.7.0 2.6.7 2.6.8 2.6.5 2.6.4 2.6.3 2.6.2 2.6.0 2.5.5 2.5.4 2.5.3 2.5.2 trunk 1.0 1.0.1 1.0.2 1.0.3 1.1 1.1.1 1.1.2 1.2.0 2.0 2.1.0 2.1.1 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.5.0 2.5.1
reviews-feed / class / Common / Customizer / DB.php
reviews-feed / class / Common / Customizer Last commit date
Tabs 1 month ago Config.php 1 month ago DB.php 1 month ago ProxyProvider.php 1 month ago ShortcodePreviewProvider.php 1 month ago
DB.php
959 lines
1 <?php
2
3 // phpcs:disable WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL, WordPress.DB.PreparedSQLPlaceholders, WordPress.Security.EscapeOutput.OutputNotEscaped
4 // Note: Legacy file with pre-existing PHPCS issues. Direct DB queries and LIKE wildcards required for customizer functionality.
5
6 namespace SmashBalloon\Reviews\Common\Customizer;
7
8 use SmashBalloon\Reviews\Common\Builder\SBR_Sources;
9 use SmashBalloon\Reviews\Common\SBR_Settings;
10 use SmashBalloon\Reviews\Common\Helpers\Data_Encryption;
11
12 class DB extends \Smashballoon\Customizer\V2\DB{
13 /**
14 * Number of feeds per page (override parent for testing: set to 2)
15 * Production value: 20
16 */
17 const RESULTS_PER_PAGE = 20;
18
19 /**
20 * Number of sources per page (for testing: set to 2)
21 * Production value: 40
22 */
23 const SOURCES_PER_PAGE = 20;
24
25 protected $feeds_table = SBR_FEEDS_TABLE;
26 protected $sources_table = SBR_SOURCES_TABLE;
27 protected $caches_table = SBR_FEED_CACHES_TABLE;
28 protected $post_tables = POSTS_TABLE_NAME;
29 protected $custom_source_table = true;
30
31 public function __construct()
32 {
33 global $wpdb;
34 $this->feeds_table = $wpdb->prefix . SBR_FEEDS_TABLE;
35 $this->sources_table = $wpdb->prefix . SBR_SOURCES_TABLE;
36 $this->caches_table = $wpdb->prefix . SBR_FEED_CACHES_TABLE;
37 $this->post_tables = $wpdb->prefix . POSTS_TABLE_NAME;
38 $this->custom_source_table = true;
39 }
40
41 /**
42 * Query the feeds table
43 * Porcess to define the name of the feed when adding new
44 *
45 * @param array $args
46 *
47 * @return array|bool
48 *
49 * @since 1.0
50 */
51 public static function feeds_query_name($feedname)
52 {
53 global $wpdb;
54 $feeds_table_name = $wpdb->prefix . SBR_FEEDS_TABLE;
55 $sql = $wpdb->prepare(
56 "SELECT * FROM $feeds_table_name
57 WHERE feed_name LIKE %s;",
58 $wpdb->esc_like($feedname) . '%'
59 );
60 $count = sizeof($wpdb->get_results($sql, ARRAY_A));
61 return ($count == 0) ? $feedname : $feedname . ' (' . ($count + 1) . ')';
62 }
63
64 /**
65 * Query to Duplicate a Single Feed
66 *
67 * @param array $args
68 *
69 * @return array|bool
70 *
71 * @since 1.0
72 */
73 public static function duplicate_feed_query($feed_id)
74 {
75 global $wpdb;
76 $feeds_table_name = $wpdb->prefix . SBR_FEEDS_TABLE;
77 $wpdb->query(
78 $wpdb->prepare(
79 "INSERT INTO $feeds_table_name (feed_name, settings, author, status)
80 SELECT CONCAT(feed_name, ' (copy)'), settings, author, status
81 FROM $feeds_table_name
82 WHERE id = %d; ",
83 $feed_id
84 )
85 );
86
87 echo sbr_json_encode(
88 [
89 'feedsList' => DB::get_feeds_list(),
90 'feedsCount' => DB::feeds_list_count()
91 ]
92 );
93 wp_die();
94 }
95
96 /**
97 * Query to Remove Feeds from Database
98 *
99 * @param array $args
100 *
101 * @return array|bool
102 *
103 * @since 1.0
104 */
105 public static function delete_feeds_query($feed_ids_array)
106 {
107 global $wpdb;
108 $feeds_table_name = $wpdb->prefix . SBR_FEEDS_TABLE;
109 $feed_caches_table_name = $wpdb->prefix . SBR_FEED_CACHES_TABLE;
110
111 // Sanitize IDs - ensure they are integers
112 $feed_ids_array = array_map('absint', $feed_ids_array);
113 $feed_ids_array = array_filter($feed_ids_array);
114
115 if (empty($feed_ids_array)) {
116 echo sbr_json_encode([
117 'feedsList' => DB::get_feeds_list(),
118 'feedsCount' => DB::feeds_list_count()
119 ]);
120 wp_die();
121 }
122
123 $feed_ids_string = implode(',', $feed_ids_array);
124
125 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- IDs are sanitized with absint()
126 $wpdb->query("DELETE FROM $feeds_table_name WHERE id IN ($feed_ids_string)");
127 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- IDs are sanitized with absint()
128 $wpdb->query("DELETE FROM $feed_caches_table_name WHERE feed_id IN ($feed_ids_string)");
129
130 echo sbr_json_encode(
131 [
132 'feedsList' => DB::get_feeds_list(),
133 'feedsCount' => DB::feeds_list_count()
134 ]
135 );
136 wp_die();
137 }
138
139 /**
140 * Creates Sources Table
141 *
142 *
143 *
144 * @since 1.0
145 */
146 public function create_sources_table()
147 {
148 if (!function_exists('dbDelta')) {
149 require_once ABSPATH . '/wp-admin/includes/upgrade.php';
150 }
151 global $wpdb;
152 $max_index_length = 191;
153 $charset_collate = '';
154 if (method_exists($wpdb, 'get_charset_collate')) { // get_charset_collate introduced in WP3.5
155 $charset_collate = $wpdb->get_charset_collate();
156 }
157 if ($wpdb->get_var("show tables like '$this->sources_table'") !== $this->sources_table) {
158 $sql = '
159 CREATE TABLE ' . $this->sources_table . " (
160 id bigint(20) unsigned NOT NULL auto_increment,
161 account_id varchar(255) NOT NULL default '',
162 provider varchar(255) NOT NULL default '',
163 access_token varchar(1000) NOT NULL default '',
164 name varchar(255) NOT NULL default '',
165 info text NOT NULL default '',
166 error text NOT NULL default '',
167 expires datetime NOT NULL,
168 last_updated datetime NOT NULL,
169 author bigint(20) unsigned NOT NULL default '1',
170 PRIMARY KEY (id),
171 KEY author (author)
172 ) $charset_collate;";
173 $wpdb->query($sql);
174 }
175 }
176
177
178 /**
179 * Query the sbi_sources table
180 *
181 * @param array $args
182 *
183 * @return array|bool
184 *
185 * @since 1.0
186 */
187 public function source_query($args = array())
188 {
189 global $wpdb;
190
191 $page = 0;
192 if (isset($args['page'])) {
193 $page = (int) $args['page'] - 1;
194 unset($args['page']);
195 }
196
197 $limit = self::SOURCES_PER_PAGE;
198 $offset = max(0, $page * $limit);
199
200 // Handle search parameter
201 $search = '';
202 if (isset($args['search'])) {
203 $search = sanitize_text_field($args['search']);
204 unset($args['search']);
205 }
206
207 if (empty($args)) {
208 // Build WHERE clause for search
209 $where_clause = '';
210 if (! empty($search)) {
211 $search_term = '%' . $wpdb->esc_like($search) . '%';
212 $where_clause = $wpdb->prepare(
213 " WHERE s.name LIKE %s OR s.provider LIKE %s ",
214 $search_term,
215 $search_term
216 );
217 }
218
219 $sql = "SELECT s.id, s.account_id, s.provider, s.access_token, s.name, s.info, s.error, s.expires, count(f.id) as used_in,
220 (SELECT count(p.id) FROM $this->post_tables p WHERE s.account_id = p.provider_id) as reviews_number
221 FROM $this->sources_table s
222 LEFT JOIN $this->feeds_table f ON f.settings LIKE CONCAT('%', s.account_id, '%')
223 {$where_clause}
224 GROUP BY s.account_id
225 LIMIT $limit
226 OFFSET $offset;
227 ";
228
229 $results = $wpdb->get_results($sql, ARRAY_A);
230
231 if (empty($results)) {
232 return array();
233 }
234
235 $i = 0;
236 foreach ($results as $result) {
237 if ((int) $result['used_in'] > 0) {
238 $results[ $i ]['instances'] = $wpdb->get_results($wpdb->prepare(
239 "SELECT *
240 FROM $this->feeds_table
241 WHERE settings LIKE CONCAT('%', %s, '%')
242 GROUP BY id
243 LIMIT 100;
244 ",
245 $result['account_id']
246 ), ARRAY_A);
247 }
248 $i++;
249 }
250
251 return $results;
252 }
253
254
255 if (! empty($args['name'])) {
256 return $wpdb->get_results(
257 $wpdb->prepare(
258 "
259 SELECT * FROM $this->sources_table
260 WHERE name = %s;
261 ",
262 $args['name']
263 ),
264 ARRAY_A
265 );
266 }
267
268
269 if (! isset($args['id'])) {
270 return false;
271 }
272
273 if (is_array($args['id'])) {
274 $id_array = array();
275 foreach ($args['id'] as $id) {
276 $id_array[] = esc_sql($id);
277 }
278 } elseif (strpos($args['id'], ',') !== false) {
279 $id_array = explode(',', str_replace(' ', '', esc_sql($args['id'])));
280 }
281
282 if (isset($id_array)) {
283 $id_array = array_filter($id_array, function ($value) {
284 return !is_null($value) && $value !== '' && !empty($value) && !is_array($value);
285 });
286 $id_string = "'" . implode("' , '", array_map('esc_sql', $id_array)) . "'";
287 }
288
289 $privilege = '';
290 if (isset($id_string)) {
291 $sql = "
292 SELECT * FROM $this->sources_table
293 WHERE account_id IN ($id_string);
294 ";
295 } else {
296 $sql = $wpdb->prepare(
297 "
298 SELECT * FROM $this->sources_table
299 WHERE account_id = %s;
300 ",
301 $args['id']
302 );
303 }
304 return $wpdb->get_results($sql, ARRAY_A);
305 }
306
307
308 /**
309 * Query the sbi_sources table to get number of sources
310 *
311 * @param array $args Optional args with 'search' parameter
312 *
313 * @return int
314 *
315 * @since 1.0
316 */
317 public function source_query_count($args = array())
318 {
319 global $wpdb;
320
321 $where_clause = '';
322 if (! empty($args['search'])) {
323 $search_term = '%' . $wpdb->esc_like(sanitize_text_field($args['search'])) . '%';
324 $where_clause = $wpdb->prepare(
325 " WHERE name LIKE %s OR provider LIKE %s ",
326 $search_term,
327 $search_term
328 );
329 }
330
331 $source_count = $wpdb->get_var("SELECT count(*) FROM $this->sources_table {$where_clause}");
332 return (int) $source_count;
333 }
334
335 /**
336 * Query the feeds table with search support
337 *
338 * @param array $args
339 *
340 * @return array|bool
341 *
342 * @since 2.3.0
343 */
344 public function feeds_query($args = array())
345 {
346 global $wpdb;
347
348 $page = 0;
349 if (isset($args['page'])) {
350 $page = (int) $args['page'] - 1;
351 unset($args['page']);
352 }
353
354 $offset = max(0, $page * self::RESULTS_PER_PAGE);
355
356 // Handle search parameter
357 $search = '';
358 if (isset($args['search'])) {
359 $search = sanitize_text_field($args['search']);
360 unset($args['search']);
361 }
362
363 if (isset($args['id'])) {
364 $sql = $wpdb->prepare(
365 "SELECT * FROM $this->feeds_table WHERE id = %d;",
366 $args['id']
367 );
368 } else {
369 // Build WHERE clause for search
370 $where_clause = '';
371 if (! empty($search)) {
372 $search_term = '%' . $wpdb->esc_like($search) . '%';
373 $where_clause = $wpdb->prepare(
374 " WHERE feed_name LIKE %s ",
375 $search_term
376 );
377 }
378
379 $sql = $wpdb->prepare(
380 "SELECT * FROM $this->feeds_table {$where_clause} ORDER BY last_modified DESC LIMIT %d OFFSET %d;",
381 self::RESULTS_PER_PAGE,
382 $offset
383 );
384 }
385
386 return $wpdb->get_results($sql, ARRAY_A);
387 }
388 /**
389 * New source (connected account) data is added to the
390 * sbr_sources table and the new insert ID is returned
391 *
392 * @param array $to_insert
393 *
394 * @return false|int
395 *
396 * @since 1.0
397 */
398 public function source_insert($to_insert)
399 {
400 global $wpdb;
401 $data = array();
402 $format = array();
403 $where = array();
404 $where_format = array();
405 if (isset($to_insert['account_id'])) {
406 $data['account_id'] = $to_insert['account_id'];
407 $format[] = '%s';
408 }
409 if (isset($to_insert['provider'])) {
410 $data['provider'] = $to_insert['provider'];
411 $format[] = '%s';
412 }
413 if (isset($to_insert['name'])) {
414 $data['name'] = $to_insert['name'];
415 $format[] = '%s';
416 }
417 if (isset($to_insert['info'])) {
418 $data['info'] = $to_insert['info'];
419 $format[] = '%s';
420 }
421 if (isset($to_insert['access_token'])) {
422 $data['access_token'] = $to_insert['access_token'];
423 $format[] = '%s';
424 }
425 if (isset($to_insert['error'])) {
426 $data['error'] = $to_insert['error'];
427 $format[] = '%s';
428 }
429 if (isset($to_insert['expires'])) {
430 $data['expires'] = $to_insert['expires'];
431 $format[] = '%s';
432 } else {
433 $data['expires'] = '2100-12-30 00:00:00';
434 $format[] = '%s';
435 }
436 $data['last_updated'] = gmdate('Y-m-d H:i:s');
437 $format[] = '%s';
438 if (isset($to_insert['author'])) {
439 $data['author'] = $to_insert['author'];
440 $format[] = '%d';
441 } else {
442 $data['author'] = get_current_user_id();
443 $format[] = '%d';
444 }
445 $affected = $wpdb->insert($this->sources_table, $data, $format);
446
447 return $affected;
448 }
449
450 /**
451 * Update a source (connected account)
452 *
453 * @param array $to_update
454 * @param array $where_data
455 *
456 * @return false|int
457 *
458 * @since 1.0
459 */
460 public function source_update($to_update, $where_data)
461 {
462 global $wpdb;
463
464 $data = array();
465 $where = array();
466 $format = array();
467 $where_format = array();
468
469 if (isset($to_update['name'])) {
470 $data['name'] = $to_update['name'];
471 $format[] = '%s';
472 }
473 if (isset($to_update['info'])) {
474 $data['info'] = $to_update['info'];
475 $format[] = '%s';
476 }
477 if (isset($to_update['last_updated'])) {
478 $data['last_updated'] = $to_update['last_updated'];
479 $format[] = '%s';
480 }
481 if (isset($to_update['access_token'])) {
482 $data['access_token'] = $to_update['access_token'];
483 $format[] = '%s';
484 }
485 if (isset($where_data['id'])) {
486 $where['account_id'] = $where_data['id'];
487 $where_format[] = '%s';
488 }
489 if (isset($where_data['provider'])) {
490 $where['provider'] = $where_data['provider'];
491 $where_format[] = '%s';
492 }
493
494
495 $affected = $wpdb->update($this->sources_table, $data, $where, $format, $where_format);
496 return $affected;
497 }
498
499 /**
500 * Update a source (connected account)
501 *
502 * @param array $to_update
503 * @param array $where_data
504 *
505 * @return false|int
506 *
507 * @since 1.0
508 */
509 public function get_single_source($args)
510 {
511 global $wpdb;
512 $query = "SELECT s.*, count(f.id) as used_in FROM $this->sources_table as s LEFT JOIN $this->feeds_table f ON f.settings LIKE CONCAT('%', s.account_id, '%') WHERE s.account_id = %s AND s.provider = %s";
513 $sql = $wpdb->prepare(
514 $query,
515 $args['id'],
516 $args['provider']
517 );
518 $affected = $wpdb->get_results($sql, ARRAY_A);
519 $i = 0;
520 foreach ($affected as $result) {
521 if ((int) $result['used_in'] > 0) {
522 $affected[ $i ]['instances'] = $wpdb->get_results($wpdb->prepare(
523 "SELECT id
524 FROM $this->feeds_table
525 WHERE settings LIKE CONCAT('%', %s, '%')
526 GROUP BY id
527 LIMIT 100;
528 ",
529 $result['account_id']
530 ), ARRAY_A);
531 }
532 $i++;
533 }
534 return !isset($affected[0]['account_id']) || is_null($affected[0]['account_id']) ? [] : $affected;
535 }
536
537
538 /**
539 * Get feeds list with optional location data.
540 *
541 * @param array $feeds_args Query arguments.
542 * @param bool $skip_locations Skip expensive shortcode location queries. Default false.
543 *
544 * @return array
545 */
546 public static function get_feeds_list($feeds_args = array(), $skip_locations = false)
547 {
548
549 if (! empty($_GET['feed_id'])) {
550 return array();
551 }
552 $db = new DB();
553 $feeds_data = $db->feeds_query($feeds_args);
554
555 $i = 0;
556 foreach ($feeds_data as $single_feed) {
557 if ($skip_locations) {
558 $feeds_data[ $i ]['instance_count'] = 0;
559 $feeds_data[ $i ]['location_summary'] = array();
560 } else {
561 // Use direct content scanning to find shortcode usage
562 // This finds shortcodes in drafts, unpublished content, and all post types
563 $locations = self::get_feed_shortcode_locations($single_feed['id'], DB::RESULTS_PER_PAGE);
564 $count = self::count_feed_shortcode_locations($single_feed['id']);
565 $feeds_data[ $i ]['instance_count'] = $count;
566 $feeds_data[ $i ]['location_summary'] = $locations;
567 }
568
569 $settings = json_decode($feeds_data[ $i ]['settings'], true);
570
571 $settings['feed'] = $single_feed['id'];
572
573 $reviews_feed_settings = new SBR_Settings($settings, sbr_settings_defaults());
574
575 $feeds_data[ $i ]['settings'] = $reviews_feed_settings->get_settings();
576 $feeds_data[ $i ]['sourcesList'] = SBR_Sources::get_sources_list([
577 'id' => $feeds_data[ $i ]['settings']['sources']
578 ]);
579 $i++;
580 }
581 return $feeds_data;
582 }
583
584 /**
585 * Query to Remove Source from Database
586 *
587 * @param array $source_id
588 *
589 * @since 6.0
590 */
591 public function delete_source($source_id)
592 {
593 global $wpdb;
594 return $wpdb->query(
595 $wpdb->prepare(
596 "DELETE FROM $this->sources_table WHERE id = %d; ",
597 $source_id
598 )
599 );
600 }
601
602 /**
603 * Count the sbr_feeds table
604 *
605 * @param array $args Optional args with 'search' parameter
606 *
607 * @return int
608 *
609 * @since 4.0
610 */
611 public static function feeds_list_count($args = array())
612 {
613 global $wpdb;
614 $feeds_table_name = $wpdb->prefix . SBR_FEEDS_TABLE;
615
616 $where_clause = '';
617 if (! empty($args['search'])) {
618 $search_term = '%' . $wpdb->esc_like(sanitize_text_field($args['search'])) . '%';
619 $where_clause = $wpdb->prepare(
620 " WHERE feed_name LIKE %s ",
621 $search_term
622 );
623 }
624
625 $results = $wpdb->get_results(
626 "SELECT COUNT(*) AS num_entries FROM $feeds_table_name {$where_clause}",
627 ARRAY_A
628 );
629 return isset($results[0]['num_entries']) ? (int)$results[0]['num_entries'] : 0;
630 }
631
632 /**
633 * Get Facebook Sources List
634 *
635 * @return array
636 *
637 * @since X.X
638 */
639 public static function get_facebook_sources()
640 {
641 global $wpdb;
642 $source_table = $wpdb->prefix . SBR_SOURCES_TABLE;
643 $query = "SELECT * FROM $source_table as s WHERE s.provider = %s";
644 $sql = $wpdb->prepare(
645 $query,
646 'facebook'
647 );
648 $results = $wpdb->get_results($sql, ARRAY_A);
649 return $results;
650 }
651
652 /**
653 * Query to Remove Source from Database
654 *
655 * @param int $source_id
656 *
657 * @return array|bool
658 *
659 * @since X.X
660 */
661 public static function delete_source_by_id($source_id)
662 {
663 global $wpdb;
664 $sources_table_name = $wpdb->prefix . SBR_SOURCES_TABLE;
665 $wpdb->query(
666 $wpdb->prepare(
667 "DELETE FROM $sources_table_name WHERE id = %d; ",
668 $source_id
669 )
670 );
671 }
672
673 /**
674 * Remove ALL Facebook Posts
675 *
676 * @return void
677 *
678 * @since X.X
679 */
680 public static function clear_facebook_feed_posts()
681 {
682 global $wpdb;
683 $posts_table_name = $wpdb->prefix . SBR_POSTS_TABLE;
684
685 if ($wpdb->get_var("show tables like '$posts_table_name'") === $posts_table_name) {
686 $wpdb->query("DELETE FROM $posts_table_name WHERE provider = 'facebook'");
687 }
688 }
689
690 /**
691 * Remove ALL Facebook Sources
692 *
693 * @return void
694 *
695 * @since X.X
696 */
697 public static function clear_facebook_sources()
698 {
699 global $wpdb;
700 $sources_table_name = $wpdb->prefix . SBR_SOURCES_TABLE;
701
702 if ($wpdb->get_var("show tables like '$sources_table_name'") === $sources_table_name) {
703 $wpdb->query("DELETE FROM $sources_table_name WHERE provider = 'facebook'");
704 }
705 }
706
707
708
709 /**
710 * Get Facebook Cached Posts List
711 *
712 * @return array
713 *
714 * @since X.X
715 */
716 public static function get_facebook_cached_posts()
717 {
718 global $wpdb;
719 $posts_table = $wpdb->prefix . SBR_POSTS_TABLE;
720 $query = "SELECT * FROM $posts_table as s WHERE s.provider = %s";
721 $sql = $wpdb->prepare(
722 $query,
723 'facebook'
724 );
725 $results = $wpdb->get_results($sql, ARRAY_A);
726 return $results;
727 }
728
729
730 /**
731 * Update a single Post
732 *
733 * @param array $to_update
734 * @param array $where_data
735 *
736 * @return void
737 *
738 * @since 1.0
739 */
740 public static function single_post_update($to_update, $where_data)
741 {
742 global $wpdb;
743 $data = [];
744 $where = [];
745 $format = [];
746 $where_format = [];
747 if (isset($to_update['json_data'])) {
748 $data['json_data'] = $to_update['json_data'];
749 $format[] = '%s';
750 }
751 if (isset($to_update['post_content'])) {
752 $data['post_content'] = $to_update['post_content'];
753 $format[] = '%s';
754 }
755 if (isset($where_data['id'])) {
756 $where['id'] = $where_data['id'];
757 $where_format[] = '%s';
758 }
759 if (isset($where_data['provider'])) {
760 $where['provider'] = $where_data['provider'];
761 $where_format[] = '%s';
762 }
763 $feeds_posts_table_name = $wpdb->prefix . 'sbr_reviews_posts';
764 $wpdb->update($feeds_posts_table_name, $data, $where, $format, $where_format);
765 }
766
767 /**
768 * Get Collections List
769 *
770 * @return array
771 *
772 * @since X.X
773 */
774 public static function get_collections_list()
775 {
776 global $wpdb;
777 $sources_table = $wpdb->prefix . SBR_SOURCES_TABLE;
778 $query = "SELECT * FROM $sources_table as s WHERE s.provider = %s";
779 $sql = $wpdb->prepare(
780 $query,
781 'collection'
782 );
783 $results = $wpdb->get_results($sql, ARRAY_A);
784 return $results;
785 }
786
787 /**
788 * Find all posts/pages containing a specific feed shortcode.
789 *
790 * Scans post_content directly in the database to find shortcode usage,
791 * including drafts and unpublished content that may not have been rendered yet.
792 *
793 * @param int|string $feed_id The feed ID to search for.
794 * @param int $limit Maximum number of results to return.
795 *
796 * @return array Array of locations where the feed shortcode is used.
797 *
798 * @since 2.3.0
799 */
800 public static function get_feed_shortcode_locations($feed_id, $limit = 20)
801 {
802 global $wpdb;
803
804 if (empty($feed_id)) {
805 return array();
806 }
807
808 $feed_id = (int) $feed_id;
809
810 // The query below is a leading-wildcard LIKE over the post_content LONGTEXT
811 // column, which is unindexable and forces a full-table scan + filesort.
812 // Running it on every editor/admin page load hangs large sites, so cache the
813 // result for a short window. See SMASH-1591.
814 $cache_key = 'sbr_feed_loc_' . $feed_id . '_' . (int) $limit;
815 $cached = get_transient($cache_key);
816 if (false !== $cached) {
817 return $cached;
818 }
819
820 // Search for shortcode patterns in post_content
821 // Matches: [reviews-feed feed=123] or [reviews-feed feed="123"] or [reviews-feed feed='123']
822 $results = $wpdb->get_results(
823 $wpdb->prepare(
824 "SELECT ID, post_title, post_type, post_status
825 FROM {$wpdb->posts}
826 WHERE post_content LIKE %s
827 AND post_type NOT IN ('revision', 'nav_menu_item', 'custom_css', 'customize_changeset', 'oembed_cache', 'user_request', 'wp_block', 'wp_template', 'wp_template_part', 'wp_global_styles', 'wp_navigation')
828 AND post_status NOT IN ('auto-draft', 'inherit', 'trash')
829 ORDER BY post_date DESC
830 LIMIT %d",
831 '%[reviews-feed%feed=' . $wpdb->esc_like((string) $feed_id) . '%',
832 $limit
833 ),
834 ARRAY_A
835 );
836
837 $locations = array();
838 foreach ((array) $results as $row) {
839 $page_text = ! empty($row['post_title']) ? $row['post_title'] : __('(no title)', 'reviews-feed');
840
841 // Add status indicator for non-published posts
842 if ($row['post_status'] !== 'publish') {
843 $status_labels = array(
844 'draft' => __('Draft', 'reviews-feed'),
845 'pending' => __('Pending', 'reviews-feed'),
846 'private' => __('Private', 'reviews-feed'),
847 'future' => __('Scheduled', 'reviews-feed'),
848 );
849 $status_label = isset($status_labels[ $row['post_status'] ])
850 ? $status_labels[ $row['post_status'] ]
851 : ucfirst($row['post_status']);
852 $page_text .= '' . $status_label;
853 }
854
855 $locations[] = array(
856 'link' => esc_url(get_the_permalink($row['ID'])),
857 'page_text' => $page_text,
858 'post_id' => $row['ID'],
859 'post_type' => $row['post_type'],
860 );
861 }
862
863 set_transient($cache_key, $locations, 5 * MINUTE_IN_SECONDS);
864
865 return $locations;
866 }
867
868 /**
869 * Count posts/pages containing a specific feed shortcode.
870 *
871 * @param int|string $feed_id The feed ID to search for.
872 *
873 * @return int Number of posts containing the shortcode.
874 *
875 * @since 2.3.0
876 */
877 public static function count_feed_shortcode_locations($feed_id)
878 {
879 global $wpdb;
880
881 if (empty($feed_id)) {
882 return 0;
883 }
884
885 $feed_id = (int) $feed_id;
886
887 // Same unindexable full-table LONGTEXT scan as get_feed_shortcode_locations();
888 // cache for a short window so it does not run on every page load. See SMASH-1591.
889 $cache_key = 'sbr_feed_loc_count_' . $feed_id;
890 $cached = get_transient($cache_key);
891 if (false !== $cached) {
892 return (int) $cached;
893 }
894
895 $count = $wpdb->get_var(
896 $wpdb->prepare(
897 "SELECT COUNT(*)
898 FROM {$wpdb->posts}
899 WHERE post_content LIKE %s
900 AND post_type NOT IN ('revision', 'nav_menu_item', 'custom_css', 'customize_changeset', 'oembed_cache', 'user_request', 'wp_block', 'wp_template', 'wp_template_part', 'wp_global_styles', 'wp_navigation')
901 AND post_status NOT IN ('auto-draft', 'inherit', 'trash')",
902 '%[reviews-feed%feed=' . $wpdb->esc_like((string) $feed_id) . '%'
903 )
904 );
905
906 set_transient($cache_key, (int) $count, 5 * MINUTE_IN_SECONDS);
907
908 return (int) $count;
909 }
910
911 /**
912 * Get All Collection Reviews
913 *
914 * @return array
915 *
916 * @since X.X
917 */
918
919 public static function get_collections_reviews($account_id)
920 {
921 global $wpdb;
922 $post_table = $wpdb->prefix . SBR_POSTS_TABLE;
923 $source_table = $wpdb->prefix . SBR_SOURCES_TABLE;
924
925 $collection = $wpdb->get_row(
926 $wpdb->prepare(
927 "SELECT * FROM $source_table WHERE account_id = %s",
928 $account_id
929 ),
930 ARRAY_A
931 );
932
933 if (!isset($collection['account_id'])) {
934 return [];
935 }
936
937 $posts_list = $wpdb->get_results(
938 $wpdb->prepare(
939 "SELECT * FROM $post_table WHERE provider_id = %s",
940 $collection['account_id']
941 ),
942 ARRAY_A
943 );
944
945 $posts_list_result = [];
946 //Loop over all posts to decrypt Facebook Posts
947 $encryption = new Data_Encryption();
948
949 foreach ($posts_list as $post) {
950 $post['post_content'] = $post['provider'] === 'facebook' ? $encryption->maybe_decrypt($post['post_content']) : $post['post_content'];
951 $post['json_data'] = $post['provider'] === 'facebook' ? $encryption->maybe_decrypt($post['json_data']) : $post['json_data'];
952 array_push($posts_list_result, $post);
953 }
954
955 $collection['reviewsList'] = $posts_list_result;
956 return $collection;
957 }
958 }
959