PluginProbe
SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent / 3.5.4
SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent v3.5.4
3.5.4 3.5.3 3.5.2 3.5.1 3.4.9 3.5.0 3.4.8 3.4.7 trunk 2.3.1 3.3.6 3.3.7 3.3.8 3.3.9 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.6
supportcandy / includes / class-wpsc-functions.php

class-wpsc-functions.php in SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent 3.5.4, at includes/class-wpsc-functions.php

1,523 lines 40.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 exit; // Exit if accessed directly!
4 }
5
6 if ( ! class_exists( 'WPSC_Functions' ) ) :
7
8 final class WPSC_Functions {
9
10 /**
11 * Referance classes. Used to provide objects on runtime.
12 *
13 * @var string
14 */
15 public static $ref_classes;
16
17 /**
18 * Initialize class
19 */
20 public static function init() {
21
22 // Load ref classes.
23 add_action( 'init', array( __CLASS__, 'load_ref_classes' ), 1 );
24
25 // Refresh paid customer option when plugins state changes.
26 add_action( 'activated_plugin', array( __CLASS__, 'update_paid_customer_status_option' ) );
27 add_action( 'deactivated_plugin', array( __CLASS__, 'update_paid_customer_status_option' ), 99 );
28 }
29
30 /**
31 * Load ref classes
32 */
33 public static function load_ref_classes() {
34
35 self::$ref_classes = apply_filters( 'wpsc_load_ref_classes', array() );
36 }
37
38 /**
39 * Return an object for ref class
40 *
41 * @param string $ref_class - reference class.
42 * @param string $value - value.
43 * @return object
44 */
45 public static function get_object( $ref_class, $value ) {
46
47 $object = null;
48
49 switch ( $ref_class ) {
50
51 case 'wp_user':
52 $object = $value ? get_user_by( 'ID', $value ) : null;
53 break;
54
55 case 'datetime':
56 $object = $value && $value !== '0000-00-00 00:00:00' ? DateTime::createFromFormat( 'Y-m-d H:i:s', $value ) : '';
57 break;
58
59 case 'dateinterval':
60 $object = $value ? new DateInterval( $value ) : new DateInterval( 'PT0M' );
61 break;
62
63 case 'wpsc_cft':
64 $object = isset( self::$ref_classes[ $value ] ) ? self::$ref_classes[ $value ]['class'] : null;
65 break;
66
67 case 'wpsc_customer':
68 if ( $value && ! is_object( $object ) && isset( self::$ref_classes[ $ref_class ] ) ) {
69 $class = self::$ref_classes[ $ref_class ]['class'];
70 $object = new $class( $value );
71 } else {
72 $object = WPSC_Customer::get_anonymous_customer();
73 }
74 break;
75
76 case 'wpsc_ticket':
77 if ( $value && isset( self::$ref_classes[ $ref_class ] ) ) {
78 $class = self::$ref_classes[ $ref_class ]['class'];
79 $object = new $class( $value );
80 // A ticket referenced from another record (a todo, a thread, a timer log, ...)
81 // may have since been archived - archived tickets are moved out of the live
82 // tickets table entirely, so the lookup above resolves to an empty object. Fall
83 // back to the archived-ticket table rather than silently returning a blank
84 // ticket (blank id, blank subject, blank link) to every caller.
85 if ( ! $object->id ) {
86 $object = new WPSC_Archive_Ticket( $value );
87 }
88 }
89 break;
90
91 default:
92 $object = apply_filters( 'wpsc_fun_get_object', $value, $ref_class );
93 if ( ! is_object( $object ) && isset( self::$ref_classes[ $ref_class ] ) ) {
94 $class = self::$ref_classes[ $ref_class ]['class'];
95 $object = new $class( $value );
96 }
97 }
98
99 return $object ? $object : $value;
100 }
101
102 /**
103 * Return value to be saved in model db for an abject
104 *
105 * @param string $ref_class - reference class.
106 * @param string $value - value.
107 * @return object
108 */
109 public static function set_object( $ref_class, $value ) {
110
111 $save_val = '';
112 switch ( $ref_class ) {
113
114 case 'wp_user':
115 $save_val = $value->ID;
116 break;
117
118 case 'datetime':
119 $save_val = $value->format( 'Y-m-d H:i:s' );
120 break;
121
122 case 'dateinterval':
123 $save_val = self::date_interval_to_string( $value );
124 break;
125
126 default:
127 if ( isset( self::$ref_classes[ $ref_class ] ) ) {
128 $key = self::$ref_classes[ $ref_class ]['save-key'];
129 $save_val = $value->$key;
130 }
131 }
132
133 return $save_val ? $save_val : $value;
134 }
135
136 /**
137 * Return search string given in model filters array
138 *
139 * @param array $filter - filter array.
140 * @return string
141 */
142 public static function get_filter_search_str( $filter ) {
143
144 return isset( $filter['search'] ) ? addslashes( trim( $filter['search'] ) ) : '';
145 }
146
147 /**
148 * Parse user filter for models which has static schema
149 *
150 * @param string $class_name - Class name of the model.
151 * @param array $filters - User filters array.
152 * @return string
153 */
154 public static function parse_user_filters( $class_name, $filters ) {
155
156 global $wpdb;
157
158 // Invalid filter.
159 if ( ! isset( $filters['relation'] ) || count( $filters ) < 2 ) {
160 return '1=1';
161 }
162
163 $relation = $filters['relation'];
164 $filter_str = array();
165
166 foreach ( $filters as $key => $filter ) {
167
168 // Skip if current element is relation indicator.
169 if ( $key === 'relation' ) {
170 continue;
171 }
172
173 // Invalid filter if it is not an array.
174 if ( ! is_array( $filter ) ) {
175 return '1=1';
176 }
177
178 // Call recursively if there is multi-layer filter detected.
179 if ( isset( $filter['relation'] ) ) {
180 $filter_str[] = self::parse_user_filters( $class_name, $filter );
181 continue;
182 }
183
184 // Invalid filter if it does not contain slug, compare and val indexes.
185 $slug = isset( $filter['slug'] ) ? self::sanitize_sql_key( $filter['slug'] ) : false;
186 $compare = isset( $filter['compare'] ) ? $filter['compare'] : false;
187 $val = isset( $filter['val'] ) || $filter['val'] == null ? $filter['val'] : false;
188 if ( ! $slug || ! $compare || $val === false ) {
189 return '1=1';
190 }
191
192 // custom filter.
193 if ( $slug === 'custom_query' ) {
194
195 $filter_str[] = $val;
196
197 } else {
198 switch ( $compare ) {
199
200 case '<':
201 case '=':
202 case '>':
203 case '<=':
204 case '>=':
205 if ( $class_name::$schema[ $slug ]['has_multiple_val'] ) {
206 $filter_str[] = '1=1';
207 break;
208 }
209 $filter_str[] = $slug . ' ' . $compare . ' \'' . esc_sql( $val ) . '\'';
210 break;
211
212 case 'BETWEEN':
213 $filter_str[] = $slug . ' BETWEEN \'' . esc_sql( $val[0] ) . '\' AND \'' . esc_sql( $val[1] ) . '\'';
214 break;
215
216 case 'IN':
217 if ( $class_name::$schema[ $slug ]['has_multiple_val'] ) {
218
219 $rlike = array();
220 foreach ( $val as $match ) {
221
222 $rlike[] = $slug . ' RLIKE \'(^|[|])' . esc_sql( $match ) . '($|[|])\'';
223 }
224 $filter_str[] = '( ' . implode( ' OR ', $rlike ) . ' )';
225
226 } else {
227
228 $filter_str[] = $slug . ' IN ( \'' . implode( '\', \'', esc_sql( $val ) ) . '\' )';
229 }
230 break;
231
232 case 'NOT IN':
233 if ( $class_name::$schema[ $slug ]['has_multiple_val'] ) {
234
235 $rlike = array();
236 foreach ( $val as $match ) {
237
238 $rlike[] = $slug . ' NOT RLIKE \'(^|[|])' . esc_sql( $match ) . '($|[|])\'';
239 }
240 $filter_str[] = '( ' . implode( ' OR ', $rlike ) . ' )';
241
242 } else {
243
244 $filter_str[] = $slug . ' NOT IN ( \'' . implode( '\', \'', esc_sql( $val ) ) . '\' )';
245 }
246 break;
247
248 case 'IS':
249 $filter_str[] = $slug . ' IS NULL';
250 break;
251
252 case 'IS NOT':
253 $filter_str[] = $slug . ' IS NOT NULL';
254 break;
255
256 case 'LIKE':
257 $filter_str[] = $slug . ' ' . $compare . ' \'%' . esc_sql( $wpdb->esc_like( $val ) ) . '%\'';
258 break;
259 }
260 }
261 }
262
263 return count( $filter_str ) > 1 ?
264 '( ' . implode( ' ' . $relation . ' ', $filter_str ) . ' )' :
265 $filter_str[0];
266 }
267
268 /**
269 * Get order for find method of models
270 *
271 * @param array $filter - filter array.
272 * @return string
273 */
274 public static function parse_order( $filter ) {
275
276 $orderby = isset( $filter['orderby'] ) && $filter['orderby'] ?
277 $filter['orderby'] : '';
278
279 if ( ! $orderby ) {
280 return '';
281 }
282
283 $order = isset( $filter['order'] ) && $filter['order'] && in_array( $filter['order'], array( 'ASC', 'DESC' ) ) ?
284 $filter['order'] : 'ASC';
285
286 $orderby_slug = isset( $filter['orderby_slug'] ) && $filter['orderby_slug'] ? $filter['orderby_slug'] : '';
287 $cf = WPSC_Custom_Field::get_cf_by_slug( $orderby_slug );
288
289 if ( $cf && $cf->type::$slug == 'cf_number' ) {
290
291 return 'ORDER BY CAST(' . $orderby . ' AS SIGNED ) ' . $order . ' ';
292 } else {
293
294 return 'ORDER BY ' . $orderby . ' ' . $order . ' ';
295 }
296 }
297
298 /**
299 * Get order for find method of models
300 *
301 * @param array $filter - filter array.
302 * @return string
303 */
304 public static function parse_limit( $filter ) {
305
306 $items_per_page = isset( $filter['items_per_page'] ) ?
307 intval( $filter['items_per_page'] ) : 0;
308
309 if ( $items_per_page == 0 ) {
310 return '';
311 }
312
313 $page_no = isset( $filter['page_no'] ) && is_numeric( $filter['page_no'] ) ?
314 intval( $filter['page_no'] ) : 1;
315
316 $offset = ( $page_no - 1 ) * $items_per_page;
317
318 return 'LIMIT ' . $offset . ', ' . $items_per_page;
319 }
320
321 /**
322 * Calculate total pages, has_next_page, results, etc. for models
323 *
324 * @param string $results - page result.
325 * @param int $total_items - total page items.
326 * @param array $filter - filter items.
327 * @return array
328 */
329 public static function parse_response( $results, int $total_items, $filter ) {
330
331 $items_per_page = isset( $filter['items_per_page'] ) ?
332 intval( $filter['items_per_page'] ) : 0;
333
334 if ( ! $items_per_page ) {
335 return array(
336 'total_items' => $total_items,
337 'results' => $results,
338 );
339 }
340
341 $page_no = isset( $filter['page_no'] ) && is_numeric( $filter['page_no'] ) ?
342 intval( $filter['page_no'] ) : 1;
343
344 $total_pages = ceil( $total_items / $items_per_page );
345
346 $has_next_page = $page_no < $total_pages ? true : false;
347
348 return array(
349 'total_items' => $total_items,
350 'items_per_page' => $items_per_page,
351 'current_page' => $page_no,
352 'total_pages' => $total_pages,
353 'has_next_page' => $has_next_page,
354 'results' => $results,
355 );
356 }
357
358 /**
359 * Check whether current page is supportcandy page or not
360 * Used for loading framework.
361 * Pages like dashboard pages and where wpsc shortcode is present are considered true.
362 *
363 * @return boolean
364 */
365 public static function is_wpsc_page() {
366
367 if ( is_admin() ) {
368
369 return isset( $_REQUEST['page'] ) && preg_match( '/wpsc-/', $_REQUEST['page'] ) ? true : false; // phpcs:ignore
370
371 } else {
372
373 return true;
374 }
375 }
376
377 /**
378 * Check whether current use is site admin or not
379 *
380 * @return boolean
381 */
382 public static function is_site_admin() {
383
384 global $current_user;
385 return $current_user && $current_user->ID && $current_user->has_cap( 'manage_options' ) ? true : false;
386 }
387
388 /**
389 * Get default filter auto-increament number
390 *
391 * @return integer
392 */
393 public static function get_tl_df_auto_increament() {
394
395 $index = intval( get_option( 'wpsc-tl-df-auto-increament', 0 ) );
396 update_option( 'wpsc-tl-df-auto-increament', ++$index );
397 return $index;
398 }
399
400 /**
401 * Get user custom filter auto-increament number
402 *
403 * @return integer
404 */
405 public static function get_tl_cf_auto_increament() {
406
407 global $current_user;
408 $index = intval( get_user_meta( $current_user->ID, get_current_blog_id() . '-wpsc-tl-cf-auto-increament', true ) );
409 update_user_meta( $current_user->ID, get_current_blog_id() . '-wpsc-tl-cf-auto-increament', ++$index );
410 return $index;
411 }
412
413 /**
414 * Return css classes string as per size
415 *
416 * @param WPSC_custom_field $cf - custom field.
417 * @param WPSC_TFF $tff - ticket form field.
418 * @return string
419 */
420 public static function get_tff_classes( $cf, $tff ) {
421
422 $classes = 'wpsc-tff ' . $cf->slug . ' wpsc-xs-12 ';
423 switch ( $tff['width'] ) {
424 case '1/3':
425 $classes .= 'wpsc-sm-4 wpsc-md-4 wpsc-lg-4 ';
426 break;
427
428 case 'half':
429 $classes .= 'wpsc-sm-6 wpsc-md-6 wpsc-lg-6 ';
430 break;
431
432 case 'full':
433 $classes .= 'wpsc-sm-12 wpsc-md-12 wpsc-lg-12 ';
434 break;
435 }
436 $classes .= $tff['is-required'] ? 'required ' : '';
437 $visibility = WPSC_TFF::get_visibility( $tff, true );
438 $classes .= $visibility ? 'wpsc-hidden conditional' : 'wpsc-visible';
439
440 return $classes;
441 }
442
443 /**
444 * Sort an array with key
445 *
446 * @param array $array_name - macro array.
447 * @param array $on - title.
448 * @param array $order - SORT_ASC.
449 * @return array
450 */
451 public static function array_sort( $array_name, $on, $order = SORT_ASC ) {
452
453 $new_array = array();
454 $sortable_array = array();
455
456 if ( count( $array_name ) > 0 ) {
457 foreach ( $array_name as $k => $v ) {
458 if ( is_array( $v ) ) {
459 foreach ( $v as $k2 => $v2 ) {
460 if ( $k2 == $on ) {
461 $sortable_array[ $k ] = $v2;
462 }
463 }
464 } else {
465 $sortable_array[ $k ] = $v;
466 }
467 }
468 switch ( $order ) {
469 case SORT_ASC:
470 asort( $sortable_array );
471 break;
472 case SORT_DESC:
473 arsort( $sortable_array );
474 break;
475 }
476 foreach ( $sortable_array as $k => $v ) {
477 $new_array[ $k ] = $array_name[ $k ];
478 }
479 }
480 return $new_array;
481 }
482
483 /**
484 * Convert date from WP timezone date to UTC equivalant date
485 *
486 * @param string $date_str - date to UTC equivalant date.
487 * @return DateTime
488 */
489 public static function get_utc_date_str( $date_str ) {
490
491 $tz = wp_timezone();
492 $date = DateTime::createFromFormat( 'Y-m-d H:i:s', $date_str, $tz );
493 $date->setTimezone( new DateTimeZone( '+0000' ) );
494 return $date->format( 'Y-m-d H:i:s' );
495 }
496
497 /**
498 * Check whether given date is valid or not
499 *
500 * @param string $date - date string.
501 * @param string $format - date format.
502 * @return boolean
503 */
504 public static function is_valid_date( $date, $format = 'Y-m-d' ) {
505 if ( preg_match( '/^\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}(:\d{2})?$/', $date ) ) {
506 return self::is_valid_datetime( $date );
507 }
508 $d = DateTime::createFromFormat( $format, $date );
509 return $d && $d->format( $format ) === $date;
510 }
511
512 /**
513 * Check whether given datetime is valid or not
514 *
515 * @param string $datetime - datetime string.
516 * @return boolean
517 */
518 public static function is_valid_datetime( $datetime ) {
519
520 if ( ! is_string( $datetime ) ) {
521 return false;
522 }
523 if ( preg_match( '/^\d{4}-\d{2}-\d{2}$/', $datetime ) ) {
524 return self::is_valid_date( $datetime );
525 }
526 $datetime = trim( $datetime );
527 $formats = array(
528 'Y-m-d H:i',
529 'Y-m-d H:i:s',
530 );
531
532 foreach ( $formats as $format ) {
533 $dt = DateTime::createFromFormat( $format, $datetime );
534 if (
535 $dt !== false &&
536 $dt->format( $format ) === $datetime
537 ) {
538 return true;
539 }
540 }
541 return false;
542 }
543
544 /**
545 * Check whether given time is valid or not
546 *
547 * @param string $time - time string.
548 * @return boolean
549 */
550 public static function is_valid_time( $time ) {
551 if ( ! is_string( $time ) ) {
552 return false;
553 }
554
555 // Accept HH:MM or HH:MM:SS (24-hour).
556 return (bool) preg_match(
557 '/^(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d)?$/',
558 $time
559 );
560 }
561
562 /**
563 * Normalize date string to UTC date string
564 *
565 * @param string $date - date string.
566 * @param boolean $end_of_day - end of day flag.
567 * @return string|false
568 */
569 public static function normalize_date( $date, $end_of_day = false ) {
570 if ( ! is_string( $date ) ) {
571 return false;
572 }
573
574 $date = trim( $date );
575
576 $formats = array(
577 'Y-m-d H:i:s',
578 'Y-m-d',
579 );
580
581 foreach ( $formats as $format ) {
582 $dt = DateTime::createFromFormat( $format, $date );
583 if ( $dt instanceof DateTime ) {
584 if ( $format === 'Y-m-d' ) {
585 $dt->setTime(
586 $end_of_day ? 23 : 0,
587 $end_of_day ? 59 : 0,
588 $end_of_day ? 59 : 0
589 );
590 }
591 return self::get_utc_date_str( $dt->format( 'Y-m-d H:i:s' ) );
592 }
593 }
594
595 return false;
596 }
597
598 /**
599 * Return SQL BETWEEN string
600 *
601 * @param string $column - column name.
602 * @param string $from - from value.
603 * @param string $to - to value.
604 * @return string
605 */
606 public static function sql_between( $column, $from, $to ) {
607 return sprintf(
608 "%s BETWEEN '%s' AND '%s'",
609 $column,
610 esc_sql( $from ),
611 esc_sql( $to )
612 );
613 }
614
615
616 /**
617 * Get new ticket url
618 *
619 * @return string
620 */
621 public static function get_new_ticket_url() {
622
623 $page_settings = get_option( 'wpsc-gs-page-settings' );
624 $url = '';
625
626 if ( $page_settings['new-ticket-page'] == 'default' && $page_settings['support-page'] ) {
627
628 $url = get_permalink( $page_settings['support-page'] );
629 $url = add_query_arg( array( 'wpsc-section' => 'new-ticket' ), $url );
630
631 } elseif ( $page_settings['new-ticket-page'] == 'custom' && $page_settings['new-ticket-url'] ) {
632
633 $url = $page_settings['new-ticket-url'];
634 }
635
636 return apply_filters( 'wpsc_get_new_ticket_url', $url );
637 }
638
639 /**
640 * Load package for third-party php library without composer
641 *
642 * @param string $dir - directory path of package.
643 * @return void
644 */
645 public static function load_library( $dir ) {
646
647 $composer = json_decode( file_get_contents( "$dir/composer.json" ), 1 ); // phpcs:ignore
648 $namespaces = $composer['autoload']['psr-4'];
649
650 // Foreach namespace specified in the composer, load the given classes.
651 foreach ( $namespaces as $namespace => $classpaths ) {
652 if ( ! is_array( $classpaths ) ) {
653 $classpaths = array( $classpaths );
654 }
655 spl_autoload_register(
656 function ( $classname ) use ( $namespace, $classpaths, $dir ) {
657 // Check if the namespace matches the class we are looking for.
658 if ( preg_match( '#^' . preg_quote( $namespace, '/' ) . '#', $classname ) ) {
659 // Remove the namespace from the file path since it's psr4.
660 $classname = str_replace( $namespace, '', $classname );
661 $filename = preg_replace( '#\\\\#', '/', $classname ) . '.php';
662 foreach ( $classpaths as $classpath ) {
663 $fullpath = $dir . '/' . $classpath . "/$filename";
664 if ( file_exists( $fullpath ) ) {
665 include_once $fullpath;
666 }
667 }
668 }
669 }
670 );
671 }
672 }
673
674 /**
675 * Create a random string
676 *
677 * @param integer $length - random strig lenght.
678 * @return string
679 */
680 public static function get_random_string( $length = 8 ) {
681
682 $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
683 $string = '';
684 for ( $i = 0; $i < $length; $i++ ) {
685 $string .= $characters[ wp_rand( 0, strlen( $characters ) - 1 ) ];
686 }
687 return $string;
688 }
689
690 /**
691 * Perform sum of date intervals
692 *
693 * @param array $arr - date interval array.
694 * @return DateInterval
695 */
696 public static function date_interval_sum( $arr ) {
697
698 $response = $arr[0];
699 $arrau_count = count( $arr );
700 for ( $i = 1; $i < $arrau_count; $i++ ) {
701 $today = new DateTime();
702 $sum_today = clone $today;
703 $sum_today->add( $response );
704 $sum_today->add( $arr[ $i ] );
705 $response = $today->diff( $sum_today );
706 }
707 return $response;
708 }
709
710 /**
711 * Return string representation of date interval
712 *
713 * @param DateInterval $diff - date interval.
714 * @return string
715 */
716 public static function date_interval_to_string( $diff ) {
717
718 $str = 'P';
719
720 if ( $diff->days ) {
721
722 $str .= $diff->format( '%aD' );
723
724 } elseif ( $diff->d ) {
725
726 $str .= $diff->format( '%dD' );
727
728 }
729
730 if ( $diff->h || $diff->i ) {
731
732 $str .= 'T';
733 $str .= $diff->h ? $diff->format( '%hH' ) : '';
734 $str .= $diff->i ? $diff->format( '%iM' ) : '';
735 }
736
737 if ( $str === 'P' ) {
738 $str = 'PT0M';
739 }
740
741 return $str;
742 }
743
744 /**
745 * Return readable time interval so that we can print this on user interface.
746 *
747 * @param DateInterval $diff - time interval.
748 * @return string
749 */
750 public static function date_interval_to_readable( $diff ) {
751
752 $arr = array();
753
754 // days.
755 if ( $diff->days ) {
756
757 $arr[] = sprintf(
758 /* translators: %d: number of days */
759 __( '%dd', 'supportcandy' ),
760 $diff->format( '%a' )
761 );
762
763 } elseif ( $diff->d ) {
764
765 $arr[] = sprintf(
766 /* translators: %d: number of days */
767 __( '%dd', 'supportcandy' ),
768 $diff->format( '%d' )
769 );
770
771 }
772
773 // hours.
774 if ( $diff->h ) {
775 $arr[] = sprintf(
776 /* translators: %d: number of hours */
777 __( '%dh', 'supportcandy' ),
778 $diff->format( '%h' )
779 );
780 }
781
782 // minutes.
783 if ( $diff->i ) {
784 $arr[] = sprintf(
785 /* translators: %d: number of minutes */
786 __( '%dm', 'supportcandy' ),
787 $diff->format( '%i' )
788 );
789 }
790
791 return $arr ? implode( ' ', $arr ) : '0m';
792 }
793
794 /**
795 * Return time interval object from readable string (usually comes from user interface)
796 *
797 * @param string $str - string to date format.
798 * @return DateInterval
799 */
800 public static function readable_to_date_interval( $str ) {
801
802 // remove spaces in between.
803 $str = str_replace( ' ', '', $str );
804
805 // invalid if not given.
806 if ( ! $str ) {
807 return false;
808 }
809
810 // validate format.
811 $flag = preg_match( '/^(\d*d)?(\d*h)?(\d*m)?$/', $str, $matches );
812 if ( ! $flag ) {
813 return false;
814 }
815
816 // build interval string.
817 $str = 'P';
818 $str .= $matches[1] ? strtoupper( $matches[1] ) : '';
819 if ( $matches[2] || $matches[3] ) {
820 $str .= 'T';
821 $str .= $matches[2] ? strtoupper( $matches[2] ) : '';
822 $str .= $matches[3] ? strtoupper( $matches[3] ) : '';
823 }
824
825 // return dateinterval object.
826 return new DateInterval( $str );
827 }
828
829 /**
830 * Return time ago string for highest unit. For example, if difference is 2hr 30min 34sec then return 2 hour ago.
831 *
832 * @param DateInterval $diff - date interval object.
833 * @return string
834 */
835 public static function date_interval_highest_unit_ago( $diff ) {
836
837 // return years if any.
838 if ( $diff->y ) {
839 return sprintf(
840 /* translators: %d: number of years */
841 __( '%d years ago', 'supportcandy' ),
842 intval( $diff->format( '%y' ) )
843 );
844 }
845
846 // return months if any.
847 if ( $diff->m ) {
848 return sprintf(
849 /* translators: %d: number of months */
850 __( '%d months ago', 'supportcandy' ),
851 intval( $diff->format( '%m' ) )
852 );
853 }
854
855 // return days if any.
856 $days = $diff->days ? intval( $diff->format( '%a' ) ) : intval( $diff->format( '%d' ) );
857 if ( $days ) {
858 return sprintf(
859 /* translators: %d: number of days */
860 __( '%d days ago', 'supportcandy' ),
861 $days
862 );
863 }
864
865 // return hours if any.
866 if ( $diff->h ) {
867 return sprintf(
868 /* translators: %d: number of hours */
869 __( '%d hours ago', 'supportcandy' ),
870 intval( $diff->format( '%h' ) )
871 );
872 }
873
874 // return minutes if any.
875 if ( $diff->i ) {
876 return sprintf(
877 /* translators: %d: number of minutes */
878 __( '%d minutes ago', 'supportcandy' ),
879 intval( $diff->format( '%i' ) )
880 );
881 }
882
883 // return seconds if any.
884 if ( $diff->s ) {
885 return sprintf(
886 /* translators: %d: number of seconds */
887 __( '%d seconds ago', 'supportcandy' ),
888 intval( $diff->format( '%s' ) )
889 );
890 }
891
892 return __( 'Just now', 'supportcandy' );
893 }
894
895 /**
896 * Return day name
897 *
898 * @param int $day - week days.
899 * @return string
900 */
901 public static function get_day_name( $day ) {
902
903 $days = array(
904 1 => wpsc__( 'Monday' ),
905 2 => wpsc__( 'Tuesday' ),
906 3 => wpsc__( 'Wednesday' ),
907 4 => wpsc__( 'Thursday' ),
908 5 => wpsc__( 'Friday' ),
909 6 => wpsc__( 'Saturday' ),
910 7 => wpsc__( 'Sunday' ),
911 );
912 return isset( $days[ $day ] ) ? $days[ $day ] : '';
913 }
914
915 /**
916 * Return month name
917 *
918 * @param int $month - month names.
919 * @return string
920 */
921 public static function get_month_name( $month ) {
922
923 $months = array(
924 1 => wpsc__( 'January' ),
925 2 => wpsc__( 'February' ),
926 3 => wpsc__( 'March' ),
927 4 => wpsc__( 'April' ),
928 5 => wpsc__( 'May' ),
929 6 => wpsc__( 'June' ),
930 7 => wpsc__( 'July' ),
931 8 => wpsc__( 'August' ),
932 9 => wpsc__( 'September' ),
933 10 => wpsc__( 'October' ),
934 11 => wpsc__( 'November' ),
935 12 => wpsc__( 'December' ),
936 );
937 return isset( $months[ $month ] ) ? $months[ $month ] : '';
938 }
939
940 /**
941 * Sanitize SQL key to allow only possible lowercase keys with joins.
942 *
943 * @param string $key - key to sanitize.
944 * @return string
945 */
946 public static function sanitize_sql_key( $key ) {
947
948 $sanitized_key = '';
949
950 if ( is_scalar( $key ) ) {
951 $key = strtolower( $key );
952 if ( preg_match( '/^([a-z]{1,2}\.)?[a-z0-9_]+$/', $key ) ) {
953 $sanitized_key = $key;
954 }
955 }
956
957 return $sanitized_key;
958 }
959
960 /**
961 * Sanitize date string e.g. "2022-12-25" and return with datetime format
962 *
963 * @param string $date - date string to be sanitized.
964 * @return string
965 */
966 public static function sanitize_date( $date ) {
967
968 if ( ! $date ) {
969 return $date;
970 }
971
972 if ( ! preg_match( '/\d{4}-\d{2}-\d{2}/', $date ) ) {
973 return '';
974 }
975
976 $format = 'Y-m-d';
977 $d = DateTime::createFromFormat( $format, $date );
978 return $d && $d->format( $format ) == $date ? $date . ' 00:00:00' : '';
979 }
980
981 /**
982 * Sanitize date string e.g. "2022-12-25 12:00:00"
983 *
984 * @param string $date - date string to be sanitized.
985 * @return string
986 */
987 public static function sanitize_datetime( $date ) {
988
989 if ( ! $date ) {
990 return $date;
991 }
992
993 if ( ! preg_match( '/\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}/', $date ) ) {
994 return '';
995 }
996
997 $format = 'Y-m-d H:i';
998 $d = DateTime::createFromFormat( $format, $date );
999 return $d && $d->format( $format ) == $date ? $date . ':00' : '';
1000 }
1001
1002 /**
1003 * Sanitize time string e.g. "12:31"
1004 *
1005 * @param string $time - time string to be sanitized.
1006 * @return string
1007 */
1008 public static function sanitize_time( $time ) {
1009
1010 if ( ! $time ) {
1011 return $time;
1012 }
1013
1014 if ( ! preg_match( '/(\d{2}):(\d{2})/', $time, $matches ) ) {
1015 return '';
1016 }
1017
1018 return intval( $matches[1] ) < 24 && intval( $matches[2] ) < 60 ? $time : '';
1019 }
1020
1021 /**
1022 * Sanitiize email string
1023 *
1024 * @param string $email - email string to be sanitized.
1025 * @return string
1026 */
1027 public static function sanitize_email( $email ) {
1028
1029 return filter_var( $email, FILTER_VALIDATE_EMAIL ) ? $email : '';
1030 }
1031
1032 /**
1033 * Sanitiize url string
1034 *
1035 * @param string $url - email string to be sanitized.
1036 * @return string
1037 */
1038 public static function sanitize_url( $url ) {
1039
1040 return filter_var( $url, FILTER_VALIDATE_URL ) ? $url : '';
1041 }
1042
1043 /**
1044 * Sanitize attachment id came as user input
1045 *
1046 * @param integer $id - attachment id.
1047 * @return integer|boolean
1048 */
1049 public static function sanitize_attachment( $id ) {
1050
1051 if ( ! $id ) {
1052 return false;
1053 }
1054
1055 $attachment = new WPSC_Attachment( $id );
1056 return $attachment->id ? $id : false;
1057 }
1058
1059 /**
1060 * Sanitize option id provided by user
1061 *
1062 * @param integer $id - option id.
1063 * @param WPSC_Custom_Field $cf - custom field object.
1064 * @param array $options - array of option objects.
1065 * @return integer|boolean
1066 */
1067 public static function sanitize_option( $id, $cf, $options ) {
1068
1069 $option = new WPSC_Option( $id );
1070 $options = array_filter(
1071 array_map(
1072 fn( $option ) => $option->id ? $option->id : false,
1073 $options
1074 )
1075 );
1076 return $option->id && in_array( $option->id, $options ) ? $id : false;
1077 }
1078
1079 /**
1080 * Get current langauge iso code. Use for datepicker library.
1081 *
1082 * @return string
1083 */
1084 public static function get_locale_iso() {
1085
1086 $locale = substr( get_locale(), 0, 2 );
1087 if ( $locale == 'el' ) {
1088 $locale = 'gr';
1089 }
1090 return $locale;
1091 }
1092
1093 /**
1094 * Get anonymous customer id.
1095 *
1096 * @return object
1097 */
1098 public static function anonymous_customer() {
1099
1100 global $wpdb;
1101 $id = get_option( 'wpsc-anonymous-user-id' );
1102 if ( ! $id ) {
1103
1104 // add anonymuos customer to customer table.
1105 $success = $wpdb->insert(
1106 $wpdb->prefix . 'psmsc_customers',
1107 array(
1108 'name' => 'Anonymous',
1109 'email' => '[email protected]',
1110 )
1111 );
1112 if ( ! $success ) {
1113 return false;
1114 }
1115
1116 // Store anonymous customer ID in option.
1117 $id = $wpdb->insert_id;
1118 update_option( 'wpsc-anonymous-user-id', $id );
1119 }
1120
1121 $anonymous = new WPSC_Customer( $id );
1122 return $anonymous;
1123 }
1124
1125 /**
1126 * Calculate date range
1127 *
1128 * @param string $date - date string.
1129 * @return array
1130 */
1131 public static function get_dashboard_date_range( $date ) {
1132
1133 $today = new DateTime();
1134 switch ( $date ) {
1135 case 'today':
1136 // Start of today at 00:00:00.
1137 $today_start = clone $today;
1138 $today_start->setTime( 0, 0, 0 );
1139 // End of today with the current time.
1140 $today_end = clone $today;
1141 return array( $today_start->format( 'Y-m-d H:i:s' ), $today_end->format( 'Y-m-d H:i:s' ) );
1142
1143 case 'yesterday':
1144 // Start of yesterday at 00:00:00.
1145 $yesterday_start = clone $today;
1146 $yesterday_start->modify( '-1 day' )->setTime( 0, 0, 0 );
1147 // End of yesterday at 23:59:59.
1148 $yesterday_end = clone $yesterday_start;
1149 $yesterday_end->setTime( 23, 59, 59 );
1150 return array( $yesterday_start->format( 'Y-m-d H:i:s' ), $yesterday_end->format( 'Y-m-d H:i:s' ) );
1151
1152 case 'last-7':
1153 // Calculate last 7 days from today's date.
1154 $last7_days_start = clone $today;
1155 $last7_days_start->modify( '-6 days' )->setTime( 0, 0, 0 );
1156 $last7_days_end = clone $today;
1157 $last7_days_end->setTime( 23, 59, 59 );
1158 return array( $last7_days_start->format( 'Y-m-d H:i:s' ), $last7_days_end->format( 'Y-m-d H:i:s' ) );
1159
1160 case 'this-week':
1161 // Calculate this week's start and end (Sunday to Saturday).
1162 $week_start = clone $today;
1163 $week_start->modify( 'last Sunday' )->setTime( 0, 0, 0 );
1164 $week_end = clone $week_start;
1165 $week_end->modify( 'next Saturday' )->setTime( 23, 59, 59 );
1166 return array( $week_start->format( 'Y-m-d H:i:s' ), $week_end->format( 'Y-m-d H:i:s' ) );
1167
1168 case 'last-week':
1169 // Calculate last week's start and end (Sunday to Saturday).
1170 $last_week_start = clone $today;
1171 $last_week_start->modify( 'last Sunday' )->sub( new DateInterval( 'P7D' ) )->setTime( 0, 0, 0 );
1172 $last_week_end = clone $last_week_start;
1173 $last_week_end->modify( 'next Saturday' )->setTime( 23, 59, 59 );
1174 return array( $last_week_start->format( 'Y-m-d H:i:s' ), $last_week_end->format( 'Y-m-d H:i:s' ) );
1175
1176 case 'last-30-days':
1177 // Calculate last 30 days from today's date.
1178 $last30_days_start = clone $today;
1179 $last30_days_start->modify( '-29 days' )->setTime( 0, 0, 0 );
1180 return array( $last30_days_start->format( 'Y-m-d H:i:s' ), $today->format( 'Y-m-d H:i:s' ) );
1181
1182 case 'this-month':
1183 // Calculate this month's start and end date.
1184 $start_month = clone $today;
1185 $start_month->modify( 'first day of this month' )->setTime( 0, 0, 0 );
1186 $end_month = clone $today;
1187 $end_month->setTime( 23, 59, 59 );
1188 return array( $start_month->format( 'Y-m-d H:i:s' ), $end_month->format( 'Y-m-d H:i:s' ) );
1189
1190 case 'this-quarter':
1191 // Calculate this quarter's start and end date.
1192 $month = (int) $today->format( 'n' );
1193 $start_quarter = clone $today;
1194 $start_quarter->setDate( $today->format( 'Y' ), floor( ( $month - 1 ) / 3 ) * 3 + 1, 1 )->setTime( 0, 0, 0 );
1195 return array( $start_quarter->format( 'Y-m-d H:i:s' ), $today->format( 'Y-m-d H:i:s' ) );
1196
1197 case 'this-year':
1198 // Calculate this year's start date to today.
1199 $start_year = clone $today;
1200 $start_year->setDate( $today->format( 'Y' ), 1, 1 )->setTime( 0, 0, 0 );
1201 return array( $start_year->format( 'Y-m-d H:i:s' ), $today->format( 'Y-m-d H:i:s' ) );
1202
1203 case 'last-month':
1204 // Calculate last month's start and end date.
1205 $last_month_start = clone $today;
1206 $last_month_start->modify( 'first day of last month' )->setTime( 0, 0, 0 );
1207 $last_month_end = clone $today;
1208 $last_month_end->modify( 'last day of last month' )->setTime( 23, 59, 59 );
1209 return array( $last_month_start->format( 'Y-m-d H:i:s' ), $last_month_end->format( 'Y-m-d H:i:s' ) );
1210
1211 case 'last-quarter':
1212 // Last quarter: from the first to the last day of the previous quarter.
1213 $current_month = (int) $today->format( 'n' );
1214
1215 // Calculate the first month of the previous quarter.
1216 $last_quarter_start_month = 3 * ( floor( ( $current_month - 1 ) / 3 ) ) - 2;
1217 if ( $last_quarter_start_month <= 0 ) {
1218 // If the calculated month is 0 or negative, it means we are in Q1, so the last quarter is Q4 of the previous year.
1219 $last_quarter_start_month += 12;
1220 $start_last_quarter = ( clone $today )->setDate( $today->format( 'Y' ) - 1, $last_quarter_start_month, 1 )->setTime( 0, 0, 0 );
1221 } else {
1222 $start_last_quarter = ( clone $today )->setDate( $today->format( 'Y' ), $last_quarter_start_month, 1 )->setTime( 0, 0, 0 );
1223 }
1224
1225 // Calculate the end of the last quarter.
1226 $end_last_quarter = clone $start_last_quarter;
1227 $end_last_quarter->modify( '+2 months' )->modify( 'last day of this month' )->setTime( 23, 59, 59 );
1228
1229 return array( $start_last_quarter->format( 'Y-m-d H:i:s' ), $end_last_quarter->format( 'Y-m-d H:i:s' ) );
1230
1231 case 'last-year':
1232 // Calculate last year's start and end date.
1233 $start_last_year = clone $today;
1234 $start_last_year->setDate( $today->format( 'Y' ) - 1, 1, 1 )->setTime( 0, 0, 0 );
1235 $end_last_year = clone $start_last_year;
1236 $end_last_year->setDate( $today->format( 'Y' ) - 1, 12, 31 )->setTime( 23, 59, 59 );
1237 return array( $start_last_year->format( 'Y-m-d H:i:s' ), $end_last_year->format( 'Y-m-d H:i:s' ) );
1238 }
1239 }
1240
1241 /**
1242 * Generate random colors.
1243 *
1244 * @return string
1245 */
1246 public static function generate_random_color() {
1247 $min_luminance = 0.7; // Adjust this value based on your preference.
1248
1249 do {
1250 $color = wp_rand( 0x000000, 0xFFFFFF );
1251 $red = ( $color >> 16 ) & 0xFF;
1252 $green = ( $color >> 8 ) & 0xFF;
1253 $blue = $color & 0xFF;
1254
1255 // Calculate luminance (brightness).
1256 $luminance = ( 0.299 * $red + 0.587 * $green + 0.114 * $blue ) / 255;
1257
1258 } while ( $luminance < $min_luminance );
1259
1260 return '#' . dechex( $color );
1261 }
1262
1263 /**
1264 * Get ticket url depending on view using ticket id.
1265 *
1266 * @param int $ticket_id - ticket id.
1267 * @param string $view - view - frontend/backend.
1268 * @return string
1269 */
1270 public static function get_ticket_url( $ticket_id, $view ) {
1271
1272 $ticket_id = absint( $ticket_id );
1273 $view = (int) $view;
1274 $url = '';
1275
1276 if ( ! $ticket_id ) {
1277 return $url;
1278 }
1279
1280 $page_settings = get_option( 'wpsc-gs-page-settings', array() );
1281
1282 // Detect ticket type.
1283 $is_archive = false;
1284 $ticket = new WPSC_Ticket( $ticket_id );
1285 if ( ! $ticket->id ) {
1286 $ticket = new WPSC_Archive_Ticket( $ticket_id );
1287 if ( ! $ticket->id ) {
1288 return $url;
1289 }
1290 $is_archive = true;
1291 }
1292
1293 $type = $is_archive
1294 ? 'wpsc-archive-tickets&section=archive-ticket-list'
1295 : 'wpsc-tickets&section=ticket-list';
1296
1297 $admin_url = admin_url( 'admin.php?page=' . $type . '&id=' . $ticket_id );
1298
1299 // Backend view always wins.
1300 if ( $view === 0 || $is_archive ) {
1301 return apply_filters( 'wpsc_get_ticket_url_by_view', $admin_url, $ticket_id, $view );
1302 }
1303
1304 // Frontend view (normal tickets only).
1305 if ( $view === 1 ) {
1306
1307 $ticket_url_page = $page_settings['ticket-url-page'] ?? '';
1308 $support_page = absint( $page_settings['support-page'] ?? 0 );
1309 $open_page = absint( $page_settings['open-ticket-page'] ?? 0 );
1310
1311 if ( $ticket_url_page === 'support-page' && $support_page ) {
1312
1313 $url = add_query_arg(
1314 array(
1315 'wpsc-section' => 'ticket-list',
1316 'ticket-id' => $ticket_id,
1317 ),
1318 get_permalink( $support_page )
1319 );
1320
1321 } elseif ( $ticket_url_page === 'open-ticket-page' && $open_page && $ticket->auth_code ) {
1322
1323 $url = add_query_arg(
1324 array(
1325 'ticket-id' => $ticket_id,
1326 'auth-code' => $ticket->auth_code,
1327 ),
1328 get_permalink( $open_page )
1329 );
1330 }
1331 }
1332
1333 if ( ! $url ) {
1334 $url = $admin_url;
1335 }
1336
1337 return apply_filters( 'wpsc_get_ticket_url_by_view', $url, $ticket_id, $view );
1338 }
1339
1340 /**
1341 * Get unique, non-empty closed statuses from merged settings.
1342 *
1343 * Merges advanced and general settings, filters out empty values,
1344 * ensures uniqueness, and returns an indexed array.
1345 *
1346 * @return array Unique, non-empty closed statuses.
1347 */
1348 public static function get_closed_statuses() {
1349
1350 $tl_ms_advance_settings = get_option( 'wpsc-tl-ms-advanced' );
1351 $general_settings = get_option( 'wpsc-gs-general' );
1352
1353 // Merge, filter non-empty, get unique values, and convert all to string.
1354 $closed_statuses = array_unique(
1355 array_filter(
1356 array_merge(
1357 $tl_ms_advance_settings['closed-ticket-statuses'],
1358 (array) $general_settings['close-ticket-status']
1359 ),
1360 function ( $statuses ) {
1361 return ! empty( $statuses );
1362 }
1363 )
1364 );
1365 return array_map( 'strval', $closed_statuses ); // Ensure indexed array.
1366 }
1367
1368 /**
1369 * Check whether any SupportCandy addon is active.
1370 *
1371 * @return bool
1372 */
1373 public static function is_paid_customer() {
1374
1375 $cache_key = 'wpsc_is_paid_customer';
1376 $cached_status = get_option( $cache_key, null );
1377
1378 if ( null !== $cached_status ) {
1379 return (bool) $cached_status;
1380 }
1381
1382 return self::update_paid_customer_status_option();
1383 }
1384
1385 /**
1386 * Update the paid customer status option based on active addons.
1387 *
1388 * @param string $plugin - Optional plugin path to exclude from check (used during activation/deactivation).
1389 * @return bool Updated paid customer status.
1390 */
1391 public static function update_paid_customer_status_option( $plugin = '' ) {
1392 $cache_key = 'wpsc_is_paid_customer';
1393
1394 $addon_plugins = array(
1395 'wpsc-agentgroup/wpsc-agentgroup.php',
1396 'wpsc-assign-agent-rules/wpsc-assign-agent-rules.php',
1397 'wpsc-automatic-close-ticket/wpsc-automatic-close-ticket.php',
1398 'wpsc-canned-reply/wpsc-canned-reply.php',
1399 'wpsc-edd/wpsc-edd.php',
1400 'wpsc-email-marketing-tools/wpsc-email-marketing-tools-integration.php',
1401 'wpsc-email-piping/wpsc-email-piping.php',
1402 'wpsc-export-ticket/wpsc-export-ticket.php',
1403 'wpsc-gravity-forms/wpsc-gravity-form-integration.php',
1404 'wpsc-lms/wpsc-lms.php',
1405 'wpsc-pressapps-knowledge-base/wpsc-pressapps-knowledge-base.php',
1406 'wpsc-print-ticket/wpsc-print-ticket.php',
1407 'wpsc-private-credentials/wpsc_private_credentials.php',
1408 'wpsc-productivity-suite/wpsc-productivity-suite.php',
1409 'wpsc-reports/wpsc-reports.php',
1410 'wpsc-satisfaction-survey/wpsc-satisfaction-survey.php',
1411 'wpsc-schedule-tickets/wpsc-schedule-tickets.php',
1412 'wpsc-sla/wpsc-sla.php',
1413 'wpsc-slack/wpsc-slack.php',
1414 'wpsc-timer/wpsc-timer.php',
1415 'wpsc-ultimate-faq/wpsc-ultimate-faq.php',
1416 'wpsc-usergroup/wpsc-usergroup.php',
1417 'wpsc-webhooks/wpsc-webhooks.php',
1418 'wpsc-woocommerce/wpsc-woocommerce.php',
1419 'wpsc-workflows/wpsc-workflows.php',
1420 );
1421
1422 $active_plugins = (array) get_option( 'active_plugins', array() );
1423
1424 if ( current_filter() === 'deactivated_plugin' && ! empty( $plugin ) ) {
1425 $active_plugins = array_diff( $active_plugins, array( $plugin ) );
1426 }
1427
1428 if ( is_multisite() ) {
1429 $active_network_plugins = (array) get_site_option( 'active_sitewide_plugins', array() );
1430 $active_plugins = array_merge( $active_plugins, array_keys( $active_network_plugins ) );
1431 }
1432
1433 $is_paid_customer = ! empty( array_intersect( $addon_plugins, $active_plugins ) );
1434 update_option( $cache_key, $is_paid_customer ? 1 : 0 );
1435 return (bool) $is_paid_customer;
1436 }
1437
1438 /**
1439 * Whether SupportCandy front-end scripts/styles should load on the current page,
1440 * based on Settings -> General -> Page settings -> Load scripts.
1441 *
1442 * Translation aware: if the current page is a WPML or Polylang translation of a
1443 * page selected in the 'Custom' list, scripts still load - the settings screen can
1444 * only ever store the ID of the page in the language active while saving.
1445 *
1446 * @return bool
1447 */
1448 public static function is_load_scripts_page() {
1449
1450 $page_settings = get_option( 'wpsc-gs-page-settings' );
1451
1452 $load = true;
1453
1454 if ( isset( $page_settings['load-scripts'] ) && $page_settings['load-scripts'] === 'custom' ) {
1455
1456 $current_id = get_the_id();
1457 $pages = isset( $page_settings['load-script-pages'] ) ? (array) $page_settings['load-script-pages'] : array();
1458
1459 $load = $current_id && self::page_id_in_pages_or_translations( $current_id, $pages );
1460 }
1461
1462 return (bool) apply_filters( 'wpsc_load_frontend_scripts', $load, $page_settings );
1463 }
1464
1465 /**
1466 * Check if given post id matches (directly or via translation) any of the given page ids.
1467 *
1468 * @param int $current_id current post id.
1469 * @param array $page_ids selected page ids from settings.
1470 * @return bool
1471 */
1472 private static function page_id_in_pages_or_translations( $current_id, $page_ids ) {
1473
1474 if ( in_array( $current_id, $page_ids ) ) { // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
1475 return true;
1476 }
1477
1478 foreach ( $page_ids as $page_id ) {
1479
1480 // WPML.
1481 if ( has_filter( 'wpml_object_id' ) ) {
1482 $translated_id = apply_filters( 'wpml_object_id', $page_id, 'page', false );
1483 if ( $translated_id && (int) $translated_id === (int) $current_id ) {
1484 return true;
1485 }
1486 }
1487
1488 // Polylang.
1489 if ( function_exists( 'pll_get_post_translations' ) ) {
1490 $translations = pll_get_post_translations( $page_id );
1491 if ( in_array( $current_id, $translations ) ) { // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
1492 return true;
1493 }
1494 }
1495 }
1496
1497 return false;
1498 }
1499
1500 /**
1501 * Get current active language code, if a multilingual plugin is active.
1502 *
1503 * @return string|null
1504 */
1505 public static function get_current_language_code() {
1506
1507 if ( has_filter( 'wpml_current_language' ) ) {
1508 $lang = apply_filters( 'wpml_current_language', null );
1509 return $lang ? $lang : null;
1510 }
1511
1512 if ( function_exists( 'pll_current_language' ) ) {
1513 $lang = pll_current_language();
1514 return $lang ? $lang : null;
1515 }
1516
1517 return null;
1518 }
1519 }
1520 endif;
1521
1522 WPSC_Functions::init();
1523