PluginProbe
MainWP Dashboard: Self-hosted WordPress Management for Agencies / 6.2.2
MainWP Dashboard: Self-hosted WordPress Management for Agencies v6.2.2
6.2.2 6.2.1 6.2 6.1.8 6.1.7 6.1.6 6.1.5 6.1.4 6.1.3 6.1.2 6.1.1 6.1 6.0.12 6.0.11 4.6.0.1 5.0 5.0.1 5.0.2 5.0.3 5.0.3.1 5.0.3.2 5.1 5.1.1 5.2 5.2.1 All 155 releases
← All changes | class/class-mainwp-utility.php +1050 -28 5.1.1 → 6.2.2 View file →
@@ -6,8 +6,13 @@
6 6 */
7 7
8 8 namespace MainWP\Dashboard;
9 9
10 +// Exit if accessed directly.
11 +if ( ! defined( 'ABSPATH' ) ) {
12 + exit;
13 +}
14 +
10 15 // phpcs:disable WordPress.DB.RestrictedFunctions, WordPress.WP.AlternativeFunctions, WordPress.PHP.NoSilencedErrors, Generic.Metrics.CyclomaticComplexity -- Using cURL functions.
11 16
12 17 /**
13 18 * Class MainWP_Utility
@@ -16,8 +21,26 @@
16 21 */
17 22 class MainWP_Utility { // phpcs:ignore Generic.Classes.OpeningBraceSameLine.ContentAfterBrace -- NOSONAR.
18 23
19 24 /**
25 + * Prefix for short-term notice options.
26 + */
27 + const SHORT_TERM_NOTICE_OPTION_PREFIX = '_mainwp_temp_notice_';
28 +
29 +
30 + /**
31 + * Prefix for per-user short-term notice options.
32 + */
33 + const USER_SHORT_TERM_NOTICE_OPTION_PREFIX = '_mainwp_user_temp_notice_';
34 +
35 +
36 + /**
37 + * New short term notice TTL.
38 + */
39 + const SHORT_TERM_NOTICE_TTL = MONTH_IN_SECONDS;
40 +
41 +
42 + /**
20 43 * Yoast SEO is enabled return true else return null.
21 44 *
22 45 * @static
23 46 * @var boolean $enabled_wp_seo If Yoast SEO is enabled return true else return null.
@@ -110,18 +133,17 @@
110 133 return substr( $haystack, - $length ) === $needle;
111 134 }
112 135
113 136 /**
114 - * Method get_nice_url()
137 + * Get a nice URL.
115 138 *
116 - * Grab url.
139 + * @param string $pUrl Website URL.
140 + * @param bool $showHttp Whether to show HTTP.
141 + * @param bool $removeTrailingSlash Whether to removev the trailing slash.
117 142 *
118 - * @param string $pUrl Website URL.
119 - * @param bool $showHttp Show HTTP.
120 - *
121 - * @return string $url.
143 + * @return string The formatted URL.
122 144 */
123 - public static function get_nice_url( $pUrl, $showHttp = false ) {
145 + public static function get_nice_url( $pUrl, $showHttp = false, $removeTrailingSlash = false ) {
124 146 $url = $pUrl;
125 147
126 148 if ( static::starts_with( $url, 'http://' ) ) {
127 149 if ( ! $showHttp ) {
@@ -126,14 +148,14 @@
126 148 if ( static::starts_with( $url, 'http://' ) ) {
127 149 if ( ! $showHttp ) {
128 150 $url = substr( $url, 7 );
129 151 }
130 - } elseif ( static::starts_with( $pUrl, 'https://' ) ) {
152 + } elseif ( static::starts_with( $url, 'https://' ) ) {
131 153 if ( ! $showHttp ) {
132 154 $url = substr( $url, 8 );
133 155 }
134 156 } elseif ( $showHttp ) {
135 - $url = 'http://' . $url;
157 + $url = 'http://' . $url; // phpcs:ignore -- NOSONAR -compatible.
136 158 }
137 159
138 160 if ( static::ends_with( $url, '/' ) ) {
139 161 if ( ! $showHttp ) {
@@ -139,11 +161,15 @@
139 161 if ( ! $showHttp ) {
140 162 $url = substr( $url, 0, strlen( $url ) - 1 );
141 163 }
142 164 } else {
143 - $url = $url . '/';
165 + $url .= '/';
144 166 }
145 167
168 + if ( $removeTrailingSlash ) {
169 + $url = rtrim( $url, '/' );
170 + }
171 +
146 172 return $url;
147 173 }
148 174
149 175 /**
@@ -246,8 +272,40 @@
246 272 return $output;
247 273 }
248 274
249 275 /**
276 + * Method get_sub_array_with_limit()
277 + *
278 + * Get sub array.
279 + *
280 + * @param mixed $arr Array to traverse.
281 + * @param mixed $start start index of array.
282 + * @param mixed $count count values.
283 + *
284 + * void array $output Sub array.
285 + */
286 + public static function get_sub_array_with_limit( $arr, $start, $count ) {
287 + $output = array();
288 + if ( is_array( $arr ) && ! empty( $arr ) ) {
289 + if ( $start > count( $arr ) ) {
290 + return array();
291 + }
292 + $i = 0;
293 + foreach ( $arr as $value ) {
294 + if ( $i >= $start && $i < $start + $count ) {
295 + $output[] = $value;
296 + }
297 + ++$i;
298 + if ( $i > $start + $count ) {
299 + break;
300 + }
301 + }
302 + }
303 + return $output;
304 + }
305 +
306 +
307 + /**
250 308 * Method trim_slashes()
251 309 *
252 310 * Trim stashes from element.
253 311 *
@@ -356,8 +414,87 @@
356 414 return date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $timestamp );
357 415 }
358 416
359 417 /**
418 + * Method format_timezone()
419 + *
420 + * Format the given timestamp.
421 + *
422 + * @param mixed $timestamp Timestamp to format.
423 + * @param mixed $with_tz_info Return date time with timezone infor.
424 + * @param mixed $use_tzformat Input tz format, to support display tz child site format.
425 + *
426 + * @return string Formatted timestamp.
427 + */
428 + public static function format_timezone( $timestamp, $with_tz_info = false, $use_tzformat = false ) { // phpcs:ignore -- NOSONAR - complex.
429 + $tzinfo = '';
430 + if ( false !== $use_tzformat ) {
431 + if ( is_array( $use_tzformat ) && ( isset( $use_tzformat['timezone_string'] ) || isset( $use_tzformat['gmt_offset'] ) || isset( $use_tzformat['date_format'] ) || isset( $use_tzformat['time_format'] ) ) ) {
432 + $wp_timezone = ! empty( $use_tzformat['timezone_string'] ) ? $use_tzformat['timezone_string'] : '';
433 +
434 + $format = '';
435 + if ( ! empty( $use_tzformat['date_format'] ) ) {
436 + $format .= $use_tzformat['date_format'] . ' ';
437 + }
438 + if ( ! empty( $use_tzformat['time_format'] ) ) {
439 + $format .= $use_tzformat['time_format'] . ' ';
440 + }
441 + $format = rtrim( $format );
442 +
443 + if ( empty( $wp_timezone ) ) {
444 + $gmt = ! empty( $use_tzformat['gmt_offset'] ) ? $use_tzformat['gmt_offset'] : 0;
445 + return date_i18n( $format, $timestamp, $gmt );
446 + }
447 +
448 + $datetime = new \DateTime( '@' . $timestamp );
449 + $datetime->setTimezone( new \DateTimeZone( $wp_timezone ) );
450 +
451 + return $datetime->format( $format );
452 + }
453 + return '';
454 + }
455 +
456 + $wp_timezone = static::clean_wp_timezone_string( get_option( 'timezone_string' ) );
457 +
458 + if ( ! $wp_timezone ) {
459 + if ( $with_tz_info ) {
460 + $tzinfo = ' ( UTC ' . get_option( 'gmt_offset' ) . ' )';
461 + }
462 + return static::format_timestamp( static::get_timestamp( $timestamp ) ) . $tzinfo;
463 + }
464 +
465 + if ( $with_tz_info ) {
466 + $tzinfo = ' ( ' . $wp_timezone . ' )';
467 + }
468 +
469 + $datetime = new \DateTime( '@' . $timestamp );
470 + $datetime->setTimezone( new \DateTimeZone( $wp_timezone ) );
471 +
472 + $format = get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) . $tzinfo;
473 + return $datetime->format( $format );
474 + }
475 +
476 + /**
477 + * Method clean_wp_timezone_string().
478 + *
479 + * @param mixed $raw Raw tz string to clean.
480 + * @return string Clean tz string.
481 + */
482 + public static function clean_wp_timezone_string( $raw ) {
483 + // If it's already a valid timezone, just return it.
484 + if ( in_array( $raw, timezone_identifiers_list(), true ) ) {
485 + return $raw;
486 + }
487 + // Try to find a valid timezone inside the messy string.
488 + foreach ( timezone_identifiers_list() as $tz ) {
489 + if ( strpos( $raw, $tz ) !== false ) {
490 + return $tz;
491 + }
492 + }
493 + return '';
494 + }
495 +
496 + /**
360 497 * Method format_timestamp()
361 498 *
362 499 * Format the given timestamp.
363 500 *
@@ -369,8 +506,79 @@
369 506 return date_i18n( get_option( 'date_format' ), $timestamp );
370 507 }
371 508
372 509 /**
510 + * Method format_time()
511 + *
512 + * Format the given timestamp.
513 + *
514 + * @param mixed $timestamp Timestamp to format.
515 + *
516 + * @return string Formatted timestamp.
517 + */
518 + public static function format_time( $timestamp ) {
519 + return date_i18n( get_option( 'time_format' ), $timestamp );
520 + }
521 +
522 + /**
523 + * Get last sync information.
524 + *
525 + * Retrieves synchronization timestamp for global view or specific site.
526 + * For global view, checks all sites and returns the most recent sync.
527 + * For specific site, returns that site's last sync timestamp.
528 + *
529 + * @param int|null $site_id Optional. Site ID to get sync info for. Null for global view.
530 + * @return array {
531 + * Last sync information.
532 + *
533 + * @type int $timestamp Unix timestamp of last sync (0 if never synced).
534 + * @type string $formatted Formatted date/time string using WordPress date/time format.
535 + * @type string $status Sync status (only for global view): 'all_synced', 'not_synced', or false.
536 + * @type string $message Human-readable sync message.
537 + * }
538 + */
539 + public static function get_last_sync_info( $site_id = null ) {
540 + $timestamp = 0;
541 + $status = false;
542 +
543 + if ( null === $site_id ) {
544 + $result = MainWP_DB_Common::instance()->get_last_sync_status();
545 + $status = $result['sync_status'];
546 + $timestamp = $result['last_sync'];
547 +
548 + if ( 'all_synced' === $status ) {
549 + $timestamp = get_option( 'mainwp_last_synced_all_sites', $timestamp );
550 + }
551 + } else {
552 + $site_id = absint( $site_id );
553 + if ( $site_id > 0 ) {
554 + $website = MainWP_DB::instance()->get_website_by_id( $site_id );
555 + if ( $website && ! empty( $website->dtsSync ) ) {
556 + $timestamp = $website->dtsSync;
557 + }
558 + }
559 + }
560 +
561 + $formatted = '';
562 + $message = '';
563 +
564 + if ( $timestamp ) {
565 + $formatted = static::format_timestamp( static::get_timestamp( $timestamp ) );
566 + /* translators: %s: formatted date/time */
567 + $message = sprintf( esc_html__( 'Last synchronization completed on: %s', 'mainwp' ), $formatted );
568 + } else {
569 + $message = esc_html__( 'Not yet synchronized', 'mainwp' );
570 + }
571 +
572 + return array(
573 + 'timestamp' => $timestamp,
574 + 'formatted' => $formatted,
575 + 'status' => $status,
576 + 'message' => $message,
577 + );
578 + }
579 +
580 + /**
373 581 * Format duration time to show.
374 582 *
375 583 * @param float $time timestamp.
376 584 * @return mixed result.
@@ -391,10 +599,131 @@
391 599 }
392 600 return '<bdi>' . esc_html( $formatted_dura ) . '</bdi>';
393 601 }
394 602
603 + /**
604 + * Get UTC timestamp by date string.
605 + *
606 + * @param string $dt_str date.
607 + * @param int $add_days Add days.
608 + *
609 + * @return mixed Local timestamp.
610 + */
611 + public static function get_utc_timestamp_by_date( $dt_str, $add_days = 0 ) {
395 612
613 + $tz = wp_timezone(); // site timezone.
614 + $day = new \DateTimeImmutable( $dt_str, $tz );
615 +
616 + if ( is_numeric( $add_days ) && $add_days > 0 ) {
617 + $day = $day->modify( '+' . $add_days . ' day' );
618 + }
619 +
620 + return $day->setTimezone( new \DateTimeZone( 'UTC' ) )->getTimestamp();
621 + }
622 +
623 +
396 624 /**
625 + * Converts a UTC timestamp (integer or float) to a local date string
626 + * using the site's timezone (handles DST automatically).
627 + *
628 + * Supports:
629 + * - Integer seconds timestamps (e.g. 1696930123)
630 + * - Float seconds with microseconds (e.g. 1696930123.123456)
631 + *
632 + * Uses WordPress `wp_timezone()` to determine the local timezone.
633 + *
634 + * @since 6.0.0
635 + *
636 + * @param int|float|string $utc_time UTC timestamp in seconds. Can be integer (seconds)
637 + * or float (seconds with microseconds).
638 + * @param string $format_str PHP date format string. Default 'Y-m-d'.
639 + * @param int $add_days Optional. Number of days to add (can be negative). Default 0.
640 + *
641 + * @return string Formatted local date string, or empty string on invalid input.
642 + *
643 + * @example
644 + * // From plain timestamp:
645 + * echo MyClass::get_local_date_by_utc_timestamp(1696930123, 'Y-m-d H:i:s');
646 + * // → "2023-10-10 15:35:23" (depending on site timezone)
647 + *
648 + * @example
649 + * // From microtime (float seconds):
650 + * echo MyClass::get_local_date_by_utc_timestamp(1696930123.123456, 'Y-m-d H:i:s.v');
651 + * // → "2023-10-10 15:35:23.123" (microseconds preserved)
652 + */
653 + public static function get_local_date_by_utc_timestamp( $utc_time, $format_str = 'Y-m-d', $add_days = 0 ) {
654 + if ( ! is_numeric( $utc_time ) ) {
655 + return '';
656 + }
657 +
658 + $tz = wp_timezone();
659 + $utc_zone = new \DateTimeZone( 'UTC' );
660 +
661 + // Detect float (has fractional seconds).
662 + if ( is_float( $utc_time ) || strpos( (string) $utc_time, '.' ) !== false ) {
663 + $dt_utc = \DateTimeImmutable::createFromFormat( 'U.u', sprintf( '%.6F', $utc_time ), $utc_zone );
664 + } else {
665 + $dt_utc = ( new \DateTimeImmutable( '@' . $utc_time ) )->setTimezone( $utc_zone );
666 + }
667 +
668 + if ( $add_days ) {
669 + $dt_utc = $dt_utc->modify( sprintf( '%+d day', $add_days ) );
670 + }
671 +
672 + return $dt_utc->setTimezone( $tz )->format( $format_str );
673 + }
674 +
675 +
676 + /**
677 + * Compute day/offset values and UTC datetime string for a local input time.
678 + *
679 + * - Automatically uses WordPress site timezone if none provided.
680 + * - Converts the local datetime to UTC for MySQL compatibility.
681 + * - Returns microsecond constants for daily grouping and offset adjustments.
682 + *
683 + * @param string $from_date_local 'Y-m-d H:i:s' in local timezone.
684 + * @param string|null $local_timezone Optional. PHP timezone ID (e.g. 'Asia/Ho_Chi_Minh').
685 + * @return array {
686 + * @type int $day_micros Microseconds in one day (86400000000).
687 + * @type int $offset_micro Timezone offset in microseconds (e.g. +25200000000).
688 + * @type string $from_date_utc UTC datetime string ('Y-m-d H:i:s') for MySQL.
689 + * @type string $local_timezone The timezone actually used.
690 + * }
691 + */
692 + public static function get_time_context( $from_date_local, $local_timezone = null ) {
693 + if ( empty( $local_timezone ) ) {
694 + if ( function_exists( 'wp_timezone' ) ) {
695 + $tz_obj = wp_timezone();
696 + $local_timezone = $tz_obj->getName();
697 + } else {
698 + $local_timezone = get_option( 'timezone_string' ) ? get_option( 'timezone_string' ) : 'UTC';
699 + }
700 + }
701 +
702 + $MICRO = 1000000;
703 + $SECONDS_PER_DAY = 86400;
704 + $day_micros = $SECONDS_PER_DAY * $MICRO;
705 +
706 + $tz = new \DateTimeZone( $local_timezone );
707 +
708 + // 👇 End of local day instead of start.
709 + $dt = new \DateTimeImmutable( $from_date_local . ' 23:59:59', $tz );
710 +
711 + $offset_seconds = $tz->getOffset( $dt );
712 + $offset_micro = $offset_seconds * $MICRO;
713 +
714 + $from_date_utc = $dt->setTimezone( new \DateTimeZone( 'UTC' ) )->format( 'Y-m-d H:i:s' );
715 +
716 + return array(
717 + 'day_micros' => $day_micros,
718 + 'offset_micro' => $offset_micro,
719 + 'from_date_utc' => $from_date_utc,
720 + 'local_timezone' => $local_timezone,
721 + );
722 + }
723 +
724 +
725 + /**
397 726 * Method human_filesize()
398 727 *
399 728 * Convert to human readable file size format,
400 729 * (B|kB|MB|GB|TB|PB|EB|ZB|YB).
@@ -442,13 +771,21 @@
442 771 $outputSite = new \stdClass();
443 772 if ( ! empty( $website ) ) {
444 773 if ( is_object( $website ) ) {
445 774 foreach ( $keys as $key ) {
446 - $outputSite->{$key} = $website->$key;
775 + if ( property_exists( $website, $key ) ) {
776 + $outputSite->{$key} = $website->$key;
777 + } else {
778 + $outputSite->{$key} = '';
779 + }
447 780 }
448 781 } elseif ( is_array( $website ) ) {
449 782 foreach ( $keys as $key ) {
450 - $outputSite->{$key} = $website[ $key ];
783 + if ( isset( $website[ $key ] ) ) {
784 + $outputSite->{$key} = $website[ $key ];
785 + } else {
786 + $outputSite->{$key} = '';
787 + }
451 788 }
452 789 }
453 790 }
454 791 } else {
@@ -455,13 +792,21 @@
455 792 $outputSite = array();
456 793 if ( ! empty( $website ) ) {
457 794 if ( is_object( $website ) ) {
458 795 foreach ( $keys as $key ) {
459 - $outputSite[ $key ] = $website->$key;
796 + if ( property_exists( $website, $key ) ) {
797 + $outputSite[ $key ] = $website->$key;
798 + } else {
799 + $outputSite[ $key ] = '';
800 + }
460 801 }
461 802 } elseif ( is_array( $website ) ) {
462 803 foreach ( $keys as $key ) {
463 - $outputSite[ $key ] = $website[ $key ];
804 + if ( isset( $website[ $key ] ) ) {
805 + $outputSite[ $key ] = $website[ $key ];
806 + } else {
807 + $outputSite[ $key ] = '';
808 + }
464 809 }
465 810 }
466 811 }
467 812 }
@@ -538,8 +883,22 @@
538 883 return false;
539 884 }
540 885
541 886 /**
887 + * Method delete_user_option()
888 + *
889 + * @param string $option_name Option name.
890 + *
891 + * @return void.
892 + */
893 + public static function delete_user_option( $option_name ) {
894 + $user = wp_get_current_user();
895 + if ( $user ) {
896 + delete_user_option( $user->ID, $option_name );
897 + }
898 + }
899 +
900 + /**
542 901 * Method remove_preslash_spaces()
543 902 *
544 903 * Remove spaces before slashes.
545 904 *
@@ -865,8 +1224,38 @@
865 1224 return true;
866 1225 }
867 1226
868 1227 /**
1228 + * Un-hide a MainWP message for the current user.
1229 + *
1230 + * Removes the notice ID from the saved notice status so the message
1231 + * can be displayed again.
1232 + *
1233 + * @param mixed $notice_id Notice ID.
1234 + *
1235 + * @return bool True if the notice was un-hidden, otherwise false.
1236 + */
1237 + public static function unhide_mainwp_message( $notice_id ) {
1238 + $status = get_user_option( 'mainwp_notice_saved_status' );
1239 +
1240 + if ( ! is_array( $status ) || ! isset( $status[ $notice_id ] ) ) {
1241 + return false;
1242 + }
1243 +
1244 + unset( $status[ $notice_id ] );
1245 +
1246 + if ( empty( $status ) ) {
1247 + return delete_user_option( get_current_user_id(), 'mainwp_notice_saved_status' );
1248 + }
1249 +
1250 + return update_user_option(
1251 + get_current_user_id(),
1252 + 'mainwp_notice_saved_status',
1253 + $status
1254 + );
1255 + }
1256 +
1257 + /**
869 1258 * Method get_hide_notice_status()
870 1259 *
871 1260 * Check whenther or not to show the MainWP Message.
872 1261 *
@@ -911,8 +1300,241 @@
911 1300 return $content;
912 1301 }
913 1302
914 1303 /**
1304 + * Method set_short_term_notice()
1305 + *
1306 + * Set new temporary notice.
1307 + *
1308 + * @param string $monitor Monitor.
1309 + * @param string $noti_key Notice key.
1310 + * @param int $checked_at Notice checked at.
1311 + *
1312 + * @return void.
1313 + */
1314 + public static function set_short_term_notice( $monitor, $noti_key, $checked_at ) {
1315 + if ( empty( $monitor ) || empty( $noti_key ) ) {
1316 + return;
1317 + }
1318 + $noti_handle_name = static::SHORT_TERM_NOTICE_OPTION_PREFIX . $monitor . '_' . $noti_key;
1319 + static::update_option( $noti_handle_name, $checked_at );
1320 + }
1321 +
1322 +
1323 + /**
1324 + * Method is_short_term_notice()
1325 + *
1326 + * Check whether the short-term notice is still active.
1327 + *
1328 + * @param string $monitor Monitor name.
1329 + * @param string $noti_key Notice key.
1330 + *
1331 + * @return boolean true|false.
1332 + */
1333 + public static function is_short_term_notice( $monitor, $noti_key ) {
1334 +
1335 + if ( empty( $monitor ) || empty( $noti_key ) ) {
1336 + return false;
1337 + }
1338 +
1339 + $noti_handle_name = static::SHORT_TERM_NOTICE_OPTION_PREFIX . $monitor . '_' . $noti_key;
1340 +
1341 + $notice_time = get_option( $noti_handle_name );
1342 +
1343 + if ( false === $notice_time ) {
1344 + return false;
1345 + }
1346 +
1347 + if ( ! is_numeric( $notice_time ) ) {
1348 + delete_option( $noti_handle_name );
1349 + }
1350 +
1351 + if ( time() - (int) $notice_time < self::SHORT_TERM_NOTICE_TTL ) {
1352 + if ( static::is_short_term_dismissed_notice( $monitor, $noti_key ) ) {
1353 + return false;
1354 + }
1355 + return true;
1356 + } else {
1357 + delete_option( $noti_handle_name );
1358 + }
1359 + return false;
1360 + }
1361 +
1362 +
1363 + /**
1364 + * Method is_short_term_dismissed_notice()
1365 + *
1366 + * Check whether the short-term notice is user dismissed.
1367 + *
1368 + * @param string $monitor Monitor.
1369 + * @param string $noti_key Notice key.
1370 + *
1371 + * @return boolean true|false.
1372 + */
1373 + public static function is_short_term_dismissed_notice( $monitor, $noti_key ) {
1374 + if ( empty( $monitor ) || empty( $noti_key ) ) {
1375 + return false;
1376 + }
1377 + $user_noti_handle = static::USER_SHORT_TERM_NOTICE_OPTION_PREFIX . $monitor . '_' . $noti_key;
1378 + $user = wp_get_current_user();
1379 + if ( $user && ! empty( $user->ID ) ) {
1380 + return 'dismissed' === get_option( $user_noti_handle . '_user_' . $user->ID );
1381 + }
1382 + return false;
1383 + }
1384 +
1385 +
1386 + /**
1387 + * Purge short-term notices.
1388 + *
1389 + * @param string $monitor Optional monitor name. When provided, only
1390 + * notices for the specified monitor are purged.
1391 + * @param bool $expired_only Whether to purge only expired notices.
1392 + *
1393 + * @return void
1394 + */
1395 + public static function purge_short_term_notices( $monitor = '', $expired_only = true ) { // phpcs:ignore -- NOSONAR - complex.
1396 +
1397 + $opt_prefix = self::SHORT_TERM_NOTICE_OPTION_PREFIX;
1398 +
1399 + $prefix = '' !== $monitor ? $monitor . '_' : '';
1400 +
1401 + $timeout = time() - self::SHORT_TERM_NOTICE_TTL;
1402 +
1403 + $db = MainWP_DB::instance()->get_wpdb_instance();
1404 +
1405 + $sql = $db->prepare(
1406 + "
1407 + SELECT option_name
1408 + FROM {$db->options}
1409 + WHERE option_name LIKE %s
1410 + ",
1411 + $db->esc_like( $opt_prefix . (string) $prefix ) . '%',
1412 + );
1413 +
1414 + if ( $expired_only ) {
1415 + $sql .= $db->prepare(
1416 + ' AND CAST(option_value AS UNSIGNED) <= %d ',
1417 + $timeout
1418 + );
1419 + }
1420 +
1421 + $options = $db->get_col( $sql );
1422 +
1423 + foreach ( $options as $option_name ) {
1424 + delete_option( $option_name );
1425 + }
1426 + }
1427 +
1428 +
1429 + /**
1430 + * Purge short-term notices for a specific issue.
1431 + *
1432 + * @param string $monitor Monitor name.
1433 + * @param string $issue_code Issue code.
1434 + *
1435 + * @return bool True if the operation completed, false if the input is invalid.
1436 + */
1437 + public static function delete_short_term_notices_for_issue( $monitor, $issue_code ) {
1438 +
1439 + if ( empty( $monitor ) || empty( $issue_code ) ) {
1440 + return false;
1441 + }
1442 +
1443 + $opt_prefix = self::SHORT_TERM_NOTICE_OPTION_PREFIX;
1444 + $prefix = $monitor . '_' . $issue_code;
1445 +
1446 + $db = MainWP_DB::instance()->get_wpdb_instance();
1447 +
1448 + $sql = $db->prepare(
1449 + "
1450 + SELECT option_name
1451 + FROM {$db->options}
1452 + WHERE option_name LIKE %s
1453 + ",
1454 + $db->esc_like( $opt_prefix . $prefix ) . '%'
1455 + );
1456 +
1457 + $options = $db->get_col( $sql );
1458 +
1459 + foreach ( $options as $option_name ) {
1460 + delete_option( $option_name ); // Delete options and cache.
1461 + }
1462 +
1463 + return true;
1464 + }
1465 +
1466 + /**
1467 + * Method dismiss_short_term_notice()
1468 + *
1469 + * Hide short term notice.
1470 + *
1471 + * @param string $monitor_noti_key Notice key.
1472 + *
1473 + * @return void.
1474 + */
1475 + public static function dismiss_short_term_notice( $monitor_noti_key ) {
1476 + $noti_handle_name = static::USER_SHORT_TERM_NOTICE_OPTION_PREFIX . $monitor_noti_key;
1477 + $user = wp_get_current_user();
1478 + if ( $user && ! empty( $user->ID ) ) {
1479 + static::update_option( $noti_handle_name . '_user_' . $user->ID, 'dismissed' );
1480 + }
1481 + }
1482 +
1483 + /**
1484 + * Delete user short-term notice options for the specified monitor.
1485 + *
1486 + * Optionally preserves the dismissed state for the specified issue codes.
1487 + *
1488 + * @param string $monitor Monitor name.
1489 + * @param array $preserved_issues Issue codes whose dismissed state should be preserved.
1490 + *
1491 + * @return void
1492 + */
1493 + public static function delete_short_term_notice_options( $monitor, $preserved_issues = array() ) {
1494 +
1495 + if ( empty( $monitor ) ) {
1496 + return;
1497 + }
1498 +
1499 + $db = MainWP_DB::instance()->get_wpdb_instance();
1500 +
1501 + $sql = "
1502 + SELECT option_name
1503 + FROM {$db->options}
1504 + WHERE option_name LIKE %s
1505 + ";
1506 +
1507 + $args = array(
1508 + $db->esc_like(
1509 + static::USER_SHORT_TERM_NOTICE_OPTION_PREFIX . $monitor . '_'
1510 + ) . '%',
1511 + );
1512 +
1513 + // Preserve the user's dismissed state for the specified issues.
1514 + if ( ! empty( $preserved_issues ) && is_array( $preserved_issues ) ) {
1515 + foreach ( $preserved_issues as $issue_code ) {
1516 + $sql .= ' AND option_name NOT LIKE %s';
1517 + $args[] = $db->esc_like(
1518 + static::USER_SHORT_TERM_NOTICE_OPTION_PREFIX . $monitor . '_' . $issue_code . '_user_'
1519 + ) . '%';
1520 + }
1521 + }
1522 +
1523 + $option_names = $db->get_col(
1524 + $db->prepare( $sql, ...$args )
1525 + );
1526 +
1527 + if ( empty( $option_names ) ) {
1528 + return;
1529 + }
1530 +
1531 + foreach ( $option_names as $option_name ) {
1532 + delete_option( $option_name );
1533 + }
1534 + }
1535 +
1536 + /**
915 1537 * Method update_flash_message()
916 1538 *
917 1539 * Check whenther or not to show the MainWP Message.
918 1540 *
@@ -949,9 +1571,13 @@
949 1571 $sorter = array();
950 1572 $ret = array();
951 1573 reset( $arr );
952 1574 foreach ( $arr as $ii => $val ) {
953 - $sorter[ $ii ] = $val[ $key ];
1575 + if ( isset( $val[ $key ] ) ) {
1576 + $sorter[ $ii ] = $val[ $key ];
1577 + } elseif ( SORT_NUMERIC === $sort_flag ) {
1578 + $sorter[ $ii ] = count( $sorter );
1579 + }
954 1580 }
955 1581 asort( $sorter, $sort_flag );
956 1582 foreach ( $sorter as $ii => $val ) {
957 1583 $ret[ $ii ] = $arr[ $ii ];
@@ -1066,19 +1692,30 @@
1066 1692 * @return array Health status value.
1067 1693 */
1068 1694 public static function get_site_health( $issue_counts ) {
1069 1695
1070 - if ( empty( $issue_counts ) ) {
1071 - $issue_counts = array(
1696 + // Coerce non-array/empty/partial input to a full set of counts so a scalar
1697 + // (e.g. a JSON-decoded string) or a missing key never fatals or warns.
1698 + $issue_counts = array_merge(
1699 + array(
1072 1700 'good' => 0,
1073 1701 'recommended' => 0,
1074 1702 'critical' => 0,
1075 - );
1076 - }
1703 + ),
1704 + is_array( $issue_counts ) ? $issue_counts : array()
1705 + );
1077 1706
1078 - $totalTests = intval( $issue_counts['good'] ) + intval( $issue_counts['recommended'] ) + intval( $issue_counts['critical'] ) * 1.5;
1079 - $failedTests = intval( $issue_counts['recommended'] ) * 0.5 + $issue_counts['critical'] * 1.5;
1707 + // Normalize each counter to a non-negative integer before arithmetic so a
1708 + // non-numeric or negative stored value cannot fatal or skew the score.
1709 + // is_numeric() first: intval() maps a non-empty array to 1, which would
1710 + // count a malformed nested value as a real issue.
1711 + $good = is_numeric( $issue_counts['good'] ) ? max( 0, intval( $issue_counts['good'] ) ) : 0;
1712 + $recommended = is_numeric( $issue_counts['recommended'] ) ? max( 0, intval( $issue_counts['recommended'] ) ) : 0;
1713 + $critical = is_numeric( $issue_counts['critical'] ) ? max( 0, intval( $issue_counts['critical'] ) ) : 0;
1080 1714
1715 + $totalTests = $good + $recommended + $critical * 1.5;
1716 + $failedTests = $recommended * 0.5 + $critical * 1.5;
1717 +
1081 1718 if ( empty( $totalTests ) ) {
1082 1719 $val = 100;
1083 1720 } else {
1084 1721 $val = 100 - ceil( ( $failedTests / $totalTests ) * 100 );
@@ -1093,9 +1730,9 @@
1093 1730 }
1094 1731
1095 1732 return array(
1096 1733 'val' => $val,
1097 - 'critical' => $issue_counts['critical'],
1734 + 'critical' => $critical,
1098 1735 );
1099 1736 }
1100 1737
1101 1738
@@ -1198,9 +1835,9 @@
1198 1835 if ( validate_file( $filename ) ) {
1199 1836 return false;
1200 1837 }
1201 1838
1202 - $allowed_files = array( 'jpg', 'jpeg', 'jpe', 'gif', 'png', 'bmp', 'tif', 'tiff', 'ico' );
1839 + $allowed_files = array( 'jpg', 'jpeg', 'jpe', 'gif', 'png', 'bmp', 'tif', 'tiff', 'ico', 'webp', 'heic' );
1203 1840 $file_ext = array_values( array_slice( explode( '.', $filename ), -1 ) )[0];
1204 1841 $file_ext = strtolower( $file_ext );
1205 1842 if ( ! in_array( $file_ext, $allowed_files ) ) {
1206 1843 return false;
@@ -1226,12 +1863,8 @@
1226 1863 if ( MainWP_System_Utility::can_edit_website( $website ) ) {
1227 1864 $error = '';
1228 1865 try {
1229 1866 $information = MainWP_Connect::fetch_url_authed( $website, 'check_abandoned', array( 'which' => $which ) );
1230 - if ( is_array( $information ) && isset( $information['sync'] ) && ! empty( $information['sync'] ) ) {
1231 - MainWP_Sync::sync_information_array( $website, $information['sync'] );
1232 - unset( $information['sync'] );
1233 - }
1234 1867 } catch ( MainWP_Exception $e ) {
1235 1868 $error = $e->getMessage();
1236 1869 }
1237 1870
@@ -1278,9 +1911,9 @@
1278 1911 */
1279 1912 public static function get_siteview_mode() {
1280 1913 $viewmode = get_user_option( 'mainwp_sitesviewmode' );
1281 1914 if ( 'grid' !== $viewmode && 'table' !== $viewmode ) {
1282 - $viewmode = 'grid';
1915 + $viewmode = 'table';
1283 1916 }
1284 1917 return $viewmode;
1285 1918 }
1286 1919
@@ -1617,6 +2250,395 @@
1617 2250 }
1618 2251 }
1619 2252
1620 2253 return $values;
2254 + }
2255 +
2256 + /**
2257 + * Method valid_file_check().
2258 + *
2259 + * @param string $path file path.
2260 + * @param bool $readable readable.
2261 + *
2262 + * @return bool is valid.
2263 + */
2264 + public static function valid_file_check( $path, $readable = true ) {
2265 + $valid = is_string( $path ) && ! stristr( $path, '..' );
2266 + if ( $valid && $readable ) {
2267 + $valid = is_readable( $path );
2268 + }
2269 + return $valid;
2270 + }
2271 +
2272 +
2273 + /**
2274 + * Handle sanitize POST data.
2275 + *
2276 + * @param array $data input data.
2277 + *
2278 + * @return array
2279 + */
2280 + public function sanitize_data( $data ) {
2281 + if ( ! is_array( $data ) ) {
2282 + return array();
2283 + }
2284 +
2285 + // Sanitize all record values.
2286 + return array_map(
2287 + function ( $value ) {
2288 + if ( ! is_array( $value ) ) {
2289 + return wp_strip_all_tags( $value );
2290 + }
2291 +
2292 + return $value;
2293 + },
2294 + $data
2295 + );
2296 + }
2297 +
2298 + /**
2299 + * String ends by.
2300 + *
2301 + * @param mixed $str str.
2302 + * @param mixed $ends ends.
2303 + * @return bool value value.
2304 + */
2305 + public static function string_ends_by( $str, $ends ) {
2306 + if ( function_exists( '\str_ends_with' ) ) {
2307 + return \str_ends_with( $str, $ends );
2308 + } else {
2309 + $ends_len = strlen( $ends );
2310 + if ( $ends_len > strlen( $str ) ) {
2311 + return false;
2312 + }
2313 + return substr( $str, -$ends_len ) === $ends;
2314 + }
2315 + }
2316 + /**
2317 + * Returns number in shorter format.
2318 + *
2319 + * @param int $number str.
2320 + * @return string $number Shorer number.
2321 + */
2322 + public static function short_number_format( $number ) {
2323 + if ( $number > 999 && $number < 1000000 ) {
2324 + // Anything between 1000 and 1000000.
2325 + $number = number_format( $number / 1000, 1 ) . 'K';
2326 + } elseif ( $number >= 1000000000 ) {
2327 + // 1000000 or higher.
2328 + $number = number_format( $number / 1000000, 2 ) . 'M';
2329 + }
2330 + return $number;
2331 + }
2332 +
2333 + /**
2334 + * Returns date in time ago format
2335 + *
2336 + * @param mixed $ptime Date stamp.
2337 + * @return string $string Time elapsed string.
2338 + */
2339 + public static function time_elapsed_string( $ptime ) {
2340 + $etime = time() - $ptime;
2341 +
2342 + if ( $etime < 1 ) {
2343 + return '0 seconds';
2344 + }
2345 +
2346 + $a = array(
2347 + 365 * 24 * 60 * 60 => 'year',
2348 + 30 * 24 * 60 * 60 => 'month',
2349 + 24 * 60 * 60 => 'day',
2350 + 60 * 60 => 'hour',
2351 + 60 => 'minute',
2352 + 1 => 'second',
2353 + );
2354 + $a_plural = array(
2355 + 'year' => 'years',
2356 + 'month' => 'months',
2357 + 'day' => 'days',
2358 + 'hour' => 'hours',
2359 + 'minute' => 'minutes',
2360 + 'second' => 'seconds',
2361 + );
2362 +
2363 + foreach ( $a as $secs => $str ) {
2364 + $d = $etime / $secs;
2365 + if ( $d >= 1 ) {
2366 + $r = round( $d );
2367 + return $r . ' ' . ( $r > 1 ? $a_plural[ $str ] : $str ) . ' ago';
2368 + }
2369 + }
2370 + }
2371 +
2372 + /**
2373 + * Returns language as flag.
2374 + *
2375 + * @param string $language Language code.
2376 + * @return void
2377 + */
2378 + public static function get_language_code_as_flag( $language ) {
2379 + // For flag extraction, remove trailing _formal or _informal if present.
2380 + $flag_language = preg_replace( '/_(formal|informal)$/', '', $language );
2381 + // Get the last 2 characters of the flag language code.
2382 + $last_two_chars = ! empty( $flag_language ) ? substr( $flag_language, -2 ) : '';
2383 + // Convert to lowercase.
2384 + $lowercase_last_two_chars = strtolower( $last_two_chars );
2385 + $lowercase_flag_language = strtolower( $flag_language );
2386 +
2387 + // Get display name using the original language string.
2388 + $display_language = function_exists( 'locale_get_display_name' ) ? locale_get_display_name( $language ) : $language;
2389 +
2390 + // Adjust special country codes.
2391 + if ( 'et' === $lowercase_last_two_chars ) {
2392 + $lowercase_last_two_chars = 'ee';
2393 + }
2394 + if ( 'sq' === $lowercase_last_two_chars ) {
2395 + $lowercase_last_two_chars = 'al';
2396 + }
2397 + if ( 'ab' === $lowercase_last_two_chars ) {
2398 + $lowercase_last_two_chars = 'dz';
2399 + }
2400 +
2401 + $stacked_flags = array(
2402 + 'ca' => array(
2403 + 'primary' => 'es',
2404 + 'secondary' => 'ad',
2405 + ),
2406 + );
2407 + // Only stack flags for explicit Catalan locales to avoid affecting Canada.
2408 + $stacked_flag_locales = array(
2409 + 'ca' => array(
2410 + 'ca',
2411 + ),
2412 + );
2413 +
2414 + if ( isset( $stacked_flags[ $lowercase_last_two_chars ] ) ) {
2415 + $should_stack = true;
2416 + if ( isset( $stacked_flag_locales[ $lowercase_last_two_chars ] ) ) {
2417 + $should_stack = in_array( $lowercase_flag_language, $stacked_flag_locales[ $lowercase_last_two_chars ], true );
2418 + }
2419 +
2420 + if ( $should_stack ) {
2421 + $primary_flag = $stacked_flags[ $lowercase_last_two_chars ]['primary'];
2422 + $secondary_flag = $stacked_flags[ $lowercase_last_two_chars ]['secondary'];
2423 +
2424 + echo '<span data-tooltip="' . esc_html__( 'Site Language: ', 'mainwp' ) . esc_attr( $display_language ) . '" data-position="left center" data-inverted="">';
2425 + echo '<span class="mainwp-flag-stack">';
2426 + echo '<i class="small ' . esc_attr( $primary_flag ) . ' flag mainwp-flag-stack__flag mainwp-flag-stack__flag--primary"></i>';
2427 + echo '<i class="small ' . esc_attr( $secondary_flag ) . ' flag mainwp-flag-stack__flag mainwp-flag-stack__flag--secondary"></i>';
2428 + echo '</span>';
2429 + echo '</span>';
2430 + return;
2431 + }
2432 + }
2433 +
2434 + echo '<span data-tooltip="' . esc_html__( 'Site Language: ', 'mainwp' ) . esc_attr( $display_language ) . '" data-position="left center" data-inverted=""><i class="small ' . esc_attr( $lowercase_last_two_chars ) . ' flag"></i></span>';
2435 + }
2436 +
2437 + /**
2438 + * Returns icon for the site indexability status.
2439 + *
2440 + * @param int $status Status, 1 or 0.
2441 + * @return void.
2442 + */
2443 + public static function get_site_index_option_icon( $status ) {
2444 + $icon = '';
2445 + $tooltip = '';
2446 + if ( isset( $status ) && '' !== $status ) {
2447 + if ( 1 === intval( $status ) ) {
2448 + $icon = 'green dot circle outline';
2449 + $tooltip = 'Search engines can index this site.';
2450 + } elseif ( 0 === intval( $status ) ) {
2451 + $icon = 'red ban';
2452 + $tooltip = 'This site is blocking search engines.';
2453 + }
2454 + } else {
2455 + $icon = 'grey circle';
2456 + $tooltip = 'Indexing status unknown. Resync the site or check manually in WordPress Settings > Reading.';
2457 + }
2458 + echo '<span data-tooltip="' . $tooltip . '" data-position="left center" data-inverted=""><i class="' . $icon . ' icon"></i></span>'; //phpcs:ignore -- ok.
2459 + }
2460 +
2461 + /**
2462 + * Returns the appropriate Fomantic UI color class based on number of updates
2463 + *
2464 + * @param int $update_count Number of available updates.
2465 + *
2466 + * @return string CSS class for the element
2467 + */
2468 + public static function mainwp_get_update_count_class( $update_count ) {
2469 + // Convert to integer using intval().
2470 + $update_count = intval( $update_count );
2471 +
2472 + // Ensure count is not negative.
2473 + if ( 0 > $update_count ) {
2474 + $update_count = 0;
2475 + }
2476 +
2477 + if ( 0 === $update_count ) {
2478 + return 'grey';
2479 + } elseif ( $update_count >= 1 && $update_count <= 3 ) {
2480 + return 'yellow';
2481 + } elseif ( $update_count >= 4 && $update_count <= 5 ) {
2482 + return 'orange';
2483 + } else {
2484 + return 'red';
2485 + }
2486 + }
2487 +
2488 + /**
2489 + * Display site name and URL with optional WP Admin link.
2490 + *
2491 + * @param object|int $site Site object or Site ID.
2492 + * @param bool $wp_admin Whether to show WP Admin link.
2493 + * @param bool $print_content Whether to print or return the content.
2494 + * @return string HTML markup for site display.
2495 + */
2496 + public static function mainwp_display_site( $site = '', $wp_admin = true, $print_content = false ) {
2497 + if ( empty( $site ) ) {
2498 + return '';
2499 + }
2500 +
2501 + if ( static::ctype_digit( $site ) ) {
2502 + $website = MainWP_DB::instance()->get_website_by_id( $site );
2503 + } elseif ( is_object( $site ) && isset( $site->id ) ) {
2504 + $website = $site;
2505 + } else {
2506 + return '';
2507 + }
2508 +
2509 + if ( ! $website ) {
2510 + return '';
2511 + }
2512 +
2513 + $site_name = esc_html( stripslashes( $website->name ) );
2514 + $site_url = esc_url( $website->url );
2515 + $nice_url = esc_html( static::get_nice_url( $website->url ) );
2516 +
2517 + $html = '<div class="mainwp-site-display">';
2518 +
2519 + // WP Admin link (if enabled and user has permission).
2520 + if ( $wp_admin && \mainwp_current_user_can( 'dashboard', 'access_wpadmin_on_child_sites' ) ) {
2521 + $admin_url = MainWP_Site_Open::get_open_site_url( $website->id, '', false );
2522 + $html .= '<a href="' . esc_url( $admin_url ) . '" class="open_newwindow_wpadmin" target="_blank" data-tooltip="' . esc_attr__( 'Go to WP Admin', 'mainwp' ) . '" data-position="top left" data-inverted=""><i class="sign in icon"></i></a> ';
2523 + } elseif ( $wp_admin ) {
2524 + $html .= '<i class="sign in icon"></i> ';
2525 + }
2526 +
2527 + // Site name with dashboard link.
2528 + $html .= '<a href="' . esc_url( admin_url( 'admin.php?page=managesites&dashboard=' . intval( $website->id ) ) ) . '">' . $site_name . '</a>';
2529 +
2530 + // Site URL.
2531 + $html .= '<div><span class="ui small text">';
2532 + $html .= '<a href="' . $site_url . '" class="mainwp-may-hide-referrer open_site_url ui grey text" target="_blank">' . $nice_url . '</a>';
2533 + $html .= '</span></div>';
2534 +
2535 + $html .= '</div>';
2536 +
2537 + if ( $print_content ) {
2538 + echo $html; // phpcs:ignore -- ok.
2539 + return '';
2540 + }
2541 +
2542 + return $html;
2543 + }
2544 +
2545 + /**
2546 + * Generate a sortable BIGINT for WordPress versions.
2547 + * Newer versions produce larger numbers.
2548 + * Ordering matches WP core version_compare().
2549 + *
2550 + * @param string $version Version value.
2551 + */
2552 + public function wp_versions_order_num( $version ) {
2553 +
2554 + $v = strtolower( trim( $version ) );
2555 +
2556 + // 1) Normalize WordPress aliases.
2557 + $v = str_replace(
2558 + array( '-alpha', '-beta', '-rc' ),
2559 + array( '-a', '-b', '-rc' ),
2560 + $v
2561 + );
2562 +
2563 + // nightly / dev / trunk → dev.
2564 + if ( preg_match( '/-(nightly|dev|trunk)/', $v ) ) {
2565 + $v = preg_replace( '/-.+$/', '-dev', $v );
2566 + }
2567 +
2568 + // unknown tags → dev.
2569 + if ( preg_match( '/-[a-z]+/', $v ) && ! preg_match( '/-(a|b|rc|dev)/', $v ) ) {
2570 + $v = preg_replace( '/-.+$/', '-dev', $v );
2571 + }
2572 +
2573 + // 2) Extract numeric base version.
2574 + $base = explode( '-', $v, 2 )[0];
2575 + $parts = array_map( 'intval', explode( '.', $base ) );
2576 +
2577 + if ( count( $parts ) < 4 ) {
2578 + $parts = array_pad( $parts, 4, 0 );
2579 + }
2580 +
2581 + $versionNum =
2582 + ( $parts[0] << 24 ) |
2583 + ( $parts[1] << 16 ) |
2584 + ( $parts[2] << 8 ) |
2585 + $parts[3];
2586 +
2587 + // 3) Release channel rank (lower = newer).
2588 + $rank = 1; // stable.
2589 + if ( strpos( $v, '-rc' ) !== false ) {
2590 + $rank = 2;
2591 + } elseif ( strpos( $v, '-b' ) !== false ) {
2592 + $rank = 3;
2593 + } elseif ( strpos( $v, '-a' ) !== false ) {
2594 + $rank = 4;
2595 + } elseif ( strpos( $v, '-dev' ) !== false ) {
2596 + $rank = 5;
2597 + }
2598 +
2599 + // 4) Prerelease / build number.
2600 + $build = 0;
2601 + if ( preg_match( '/rc(\d+)/', $v, $m ) ) {
2602 + $build = (int) $m[1];
2603 + } elseif ( preg_match( '/b(\d+)/', $v, $m ) ) { // NOSONAR - same above ok.
2604 + $build = (int) $m[1];
2605 + } elseif ( preg_match( '/a(\d+)/', $v, $m ) ) { // NOSONAR - same above ok.
2606 + $build = (int) $m[1];
2607 + }
2608 +
2609 + // 5) Compose sortable BIGINT.
2610 + return ( $versionNum << 32 )
2611 + | ( $rank << 29 )
2612 + | min( $build, ( 1 << 29 ) - 1 );
2613 + }
2614 +
2615 + /**
2616 + * Method handle gen_rand_id().
2617 + *
2618 + * @param string $str Input string.
2619 + * @param bool $more_entropy More entropy False if a robust prefix is required, default false.
2620 + * @return string Random ID 8 characters.
2621 + */
2622 + public static function gen_rand_id( $str = '', $more_entropy = false ) {
2623 + return hash( 'crc32b', uniqid( $str, $more_entropy ? true : false ) );
2624 + }
2625 +
2626 + /**
2627 + * Decode and validate JSON array.
2628 + *
2629 + * @param mixed $data Data to decode.
2630 + */
2631 + public static function get_decoded_array( $data ) {
2632 + $decoded = ! empty( $data ) ? json_decode( $data, true ) : array();
2633 + return is_array( $decoded ) ? $decoded : array();
2634 + }
2635 +
2636 + /**
2637 + * Method get_use_cron()
2638 + *
2639 + * @return bool Whether use cron.
2640 + */
2641 + public static function get_use_cron() {
2642 + return ( get_option( 'mainwp_wp_cron' ) === false ) || ( (int) get_option( 'mainwp_wp_cron' ) === 1 ) ? 1 : 0;
1621 2643 }
1622 2644 }