PluginProbe
MainWP Dashboard: Self-hosted WordPress Management for Agencies / trunk
MainWP Dashboard: Self-hosted WordPress Management for Agencies vtrunk
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 5.2.2 5.3 All 153 releases
← All changes | class/class-mainwp-system-handler.php +333 -12 5.3trunk 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 /**
11 16 * Class MainWP_System_Handler
12 17 *
13 18 * @package MainWP\Dashboard
@@ -27,9 +32,9 @@
27 32
28 33 /**
29 34 * Private variable to hold the upgrade version info.
30 35 *
31 - * @var null Version info.
36 + * @var \stdClass|null Version info.
32 37 */
33 38 private $upgradeVersionInfo = null;
34 39
35 40 /**
@@ -66,12 +71,12 @@
66 71 *
67 72 * @see \MainWP_Extensions::hook_get_sites
68 73 */
69 74 add_filter( 'mainwp-getsites', array( MainWP_Extensions_Handler::get_class_name(), 'hook_get_sites' ), 10, 5 ); // @deprecated Use 'mainwp_getsites' instead.
70 - add_filter( 'mainwp-getdbsites', array( MainWP_Extensions_Handler::get_class_name(), 'hook_get_db_sites' ), 10, 5 ); // @deprecated Use 'mainwp_getdbsites' instead.
75 + add_filter( 'mainwp-getdbsites', array( MainWP_Extensions_Handler::get_class_name(), 'hook_get_db_sites' ), 10, 6 ); // @deprecated Use 'mainwp_getdbsites' instead.
71 76
72 77 add_filter( 'mainwp_getsites', array( MainWP_Extensions_Handler::get_class_name(), 'hook_get_sites' ), 10, 5 );
73 - add_filter( 'mainwp_getdbsites', array( MainWP_Extensions_Handler::get_class_name(), 'hook_get_db_sites' ), 10, 5 );
78 + add_filter( 'mainwp_getdbsites', array( MainWP_Extensions_Handler::get_class_name(), 'hook_get_db_sites' ), 10, 6 );
74 79 add_filter( 'mainwp_get_db_websites', array( MainWP_Extensions_Handler::get_class_name(), 'hook_get_db_websites' ), 10, 5 );
75 80
76 81 /**
77 82 * This hook allows you to get a information about groups via the 'mainwp-getgroups' filter.
@@ -220,8 +225,10 @@
220 225 if ( ! MainWP_System_Utility::is_admin() ) {
221 226 return;
222 227 }
223 228
229 + $this->handle_quick_theme_change();
230 +
224 231 global $pagenow;
225 232
226 233 if ( 'plugins.php' === $pagenow && isset( $_GET['do'] ) && 'checkUpgrade' === $_GET['do'] && ( ( time() - $this->upgradeVersionInfo->updated ) > 30 ) ) { // phpcs:ignore WordPress.Security.NonceVerification,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
227 234 $this->check_upgrade();
@@ -230,10 +237,8 @@
230 237 exit;
231 238 }
232 239 }
233 240
234 -
235 -
236 241 /**
237 242 * Method hook_get_sql_websites_for_current_user()
238 243 *
239 244 * Get sql websites for current user.
@@ -249,8 +254,16 @@
249 254 unset( $input_value );
250 255 if ( ! MainWP_Extensions_Handler::hook_verify( $pluginFile, $key ) ) {
251 256 return false;
252 257 }
258 +
259 + // Prevent conflicts in wp_options query conditions.
260 + // If use_join_wp_options is not enabled,
261 + // force the use of a compatible subquery approach to ensure the query remains correct.
262 + if ( is_array( $params ) && empty( $params['use_join_wp_options'] ) ) {
263 + $params['use_compatible_subquery'] = 1;
264 + }
265 +
253 266 return MainWP_DB::instance()->get_sql_wp_for_current_user( $params );
254 267 }
255 268 /**
256 269 * Method handle_manage_sites_screen_settings()
@@ -362,18 +375,52 @@
362 375 }
363 376 }
364 377
365 378 /**
379 + * Method handle_insights_events_screen_settings()
380 + *
381 + * Handle manage insights events screen settings
382 + */
383 + public function handle_insights_events_screen_settings() { // phpcs:ignore -- NOSONAR - complex.
384 + if ( isset( $_POST['wp_nonce'] ) && wp_verify_nonce( sanitize_key( $_POST['wp_nonce'] ), 'ManageEventsScrOptions' ) ) {
385 + $show_cols = array();
386 + foreach ( array_map( 'sanitize_text_field', wp_unslash( $_POST ) ) as $key => $val ) {
387 + if ( false !== strpos( $key, 'mainwp_show_column_' ) ) {
388 + $col = str_replace( 'mainwp_show_column_', '', $key );
389 + $show_cols[ $col ] = 1;
390 + }
391 + }
392 + if ( isset( $_POST['show_columns_name'] ) ) {
393 + foreach ( array_map( 'sanitize_text_field', wp_unslash( $_POST['show_columns_name'] ) ) as $col ) {
394 + if ( ! isset( $show_cols[ $col ] ) ) {
395 + $show_cols[ $col ] = 0; // uncheck, hide columns.
396 + }
397 + }
398 + }
399 +
400 + $user = wp_get_current_user();
401 + if ( $user ) {
402 + update_user_option( $user->ID, 'mainwp_settings_show_insights_events_columns', $show_cols, true );
403 + update_option( 'mainwp_default_manage_insights_events_per_page', ( isset( $_POST['mainwp_default_sites_per_page'] ) ? intval( $_POST['mainwp_default_sites_per_page'] ) : 25 ) );
404 + }
405 + wp_safe_redirect( admin_url( 'admin.php?page=InsightsManage&message=savedscreenopts' ) );
406 + exit();
407 + }
408 + }
409 +
410 + /**
366 411 * Method handle_clients_screen_settings()
367 412 *
368 413 * Handle manage clients screen settings
369 414 */
370 - public function handle_updates_screen_settings() {
415 + public function handle_updates_screen_settings() { //phpcs:ignore -- NOSONAR - complex.
371 416 if ( isset( $_POST['wp_nonce'] ) && wp_verify_nonce( sanitize_key( $_POST['wp_nonce'] ), 'UpdatesScrOptions' ) ) {
372 417 $val = ( ! isset( $_POST['mainwp_pluginAutomaticDailyUpdate'] ) ? 0 : intval( $_POST['mainwp_pluginAutomaticDailyUpdate'] ) );
373 418 MainWP_Utility::update_option( 'mainwp_pluginAutomaticDailyUpdate', $val );
374 419 $val = ( ! isset( $_POST['mainwp_themeAutomaticDailyUpdate'] ) ? 0 : intval( $_POST['mainwp_themeAutomaticDailyUpdate'] ) );
375 420 MainWP_Utility::update_option( 'mainwp_themeAutomaticDailyUpdate', $val );
421 + $val = ( ! isset( $_POST['mainwp_transAutomaticDailyUpdate'] ) ? 0 : intval( $_POST['mainwp_transAutomaticDailyUpdate'] ) );
422 + MainWP_Utility::update_option( 'mainwp_transAutomaticDailyUpdate', $val );
376 423 $val = ( ! isset( $_POST['mainwp_automaticDailyUpdate'] ) ? 0 : intval( $_POST['mainwp_automaticDailyUpdate'] ) );
377 424 MainWP_Utility::update_option( 'mainwp_automaticDailyUpdate', $val );
378 425 $val = ( isset( $_POST['mainwp_delay_autoupdate'] ) ? intval( $_POST['mainwp_delay_autoupdate'] ) : 1 );
379 426 MainWP_Utility::update_option( 'mainwp_delay_autoupdate', $val );
@@ -383,9 +430,43 @@
383 430 MainWP_Utility::update_option( 'mainwp_disable_update_confirmations', $val );
384 431 }
385 432 }
386 433
434 + /**
435 + * Method handle_quick_theme_change()
436 + *
437 + * Handle quick theme switching via URL parameter.
438 + */
439 + public function handle_quick_theme_change() {
440 + if ( ! isset( $_GET['mainwp_quick_theme_change'] ) || ! isset( $_GET['_wpnonce'] ) ) {
441 + return;
442 + }
387 443
444 + if ( ! wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), 'mainwp_quick_theme_change' ) ) {
445 + wp_die( esc_html__( 'Security check failed', 'mainwp' ) );
446 + }
447 +
448 + $user = wp_get_current_user();
449 +
450 + if ( ! $user || ! $user->ID || $user->ID <= 0 ) {
451 + wp_die( esc_html__( 'Authentication required', 'mainwp' ) );
452 + }
453 +
454 + $theme = sanitize_text_field( wp_unslash( $_GET['mainwp_quick_theme_change'] ) );
455 +
456 + $allowed_themes = array( 'default', 'default-dark' );
457 + if ( ! in_array( $theme, $allowed_themes, true ) ) {
458 + wp_die( esc_html__( 'Invalid theme selection', 'mainwp' ) );
459 + }
460 +
461 + update_user_option( $user->ID, 'mainwp_selected_theme', $theme );
462 + set_transient( 'mainwp_settings_saved', 1, 30 );
463 +
464 + $redirect_url = remove_query_arg( array( 'mainwp_quick_theme_change', '_wpnonce' ) );
465 + wp_safe_redirect( $redirect_url );
466 + exit;
467 + }
468 +
388 469 /**
389 470 * Method handle_mainwp_tools_settings()
390 471 *
391 472 * Handle mainwp tools settings.
@@ -397,10 +478,14 @@
397 478
398 479 $user = wp_get_current_user();
399 480
400 481 $update_selected_mainwp_themes = false;
482 + $should_redirect = false; // Redirect status.
401 483
402 484 if ( isset( $_GET['page'] ) && 'MainWPTools' === $_GET['page'] && isset( $_POST['wp_nonce'] ) && wp_verify_nonce( sanitize_key( $_POST['wp_nonce'] ), 'MainWPTools' ) ) {
485 +
486 + $old_settings = MainWP_Settings::get_all_settings_values();
487 +
403 488 if ( isset( $_POST['mainwp_restore_info_messages'] ) && ! empty( $_POST['mainwp_restore_info_messages'] ) ) {
404 489 delete_user_option( $user->ID, 'mainwp_notice_saved_status' );
405 490 }
406 491 $enabled_tours = ! isset( $_POST['mainwp-guided-tours-option'] ) ? 0 : 1;
@@ -405,15 +490,39 @@
405 490 }
406 491 $enabled_tours = ! isset( $_POST['mainwp-guided-tours-option'] ) ? 0 : 1;
407 492 MainWP_Utility::update_option( 'mainwp_enable_guided_tours', $enabled_tours );
408 493
494 + $enabled1 = ! isset( $_POST['mainwp-guided-chatbase-option'] ) ? 0 : 1;
495 + MainWP_Utility::update_option( 'mainwp_enable_guided_chatbase', $enabled1 );
496 +
497 + $enabled2 = ! isset( $_POST['mainwp-guided-video-option'] ) ? 0 : 1;
498 + MainWP_Utility::update_option( 'mainwp_enable_guided_video', $enabled2 );
499 +
409 500 if ( isset( $_POST['mainwp_settings_custom_theme'] ) ) {
410 501 $update_selected_mainwp_themes = true;
411 502 }
503 +
504 + $new_settings = MainWP_Settings::get_all_settings_values();
505 +
506 + /**
507 + * Action: mainwp_after_save_settings
508 + *
509 + * Fires after save settings.
510 + *
511 + * @since 6.0
512 + *
513 + * @param array $new_settings The new settings.
514 + * @param array $old_settings The old settings.
515 + */
516 + do_action( 'mainwp_after_save_settings', $new_settings, $old_settings );
517 + $should_redirect = true;
518 + set_transient( 'mainwp_settings_saved', 1, 30 );
412 519 }
413 520
414 521 if ( isset( $_POST['wp_scr_options_nonce'] ) && wp_verify_nonce( sanitize_key( $_POST['wp_scr_options_nonce'] ), 'MainWPSelectThemes' ) ) {
415 522 $update_selected_mainwp_themes = true;
523 + $should_redirect = true;
524 + set_transient( 'mainwp_settings_saved', 1, 30 );
416 525 }
417 526
418 527 if ( $update_selected_mainwp_themes && isset( $_POST['mainwp_settings_custom_theme'] ) ) {
419 528 $custom_theme = sanitize_text_field( wp_unslash( $_POST['mainwp_settings_custom_theme'] ) );
@@ -482,8 +591,9 @@
482 591
483 592 if ( isset( $_POST['reset_overview_settings'] ) && ! empty( $_POST['reset_overview_settings'] ) && isset( $_POST['reset_overview_which_settings'] ) && 'overview_settings' === $_POST['reset_overview_which_settings'] ) {
484 593 update_user_option( $user->ID, 'mainwp_widgets_sorted_toplevel_page_mainwp_tab', false, true );
485 594 update_user_option( $user->ID, 'mainwp_widgets_sorted_mainwp_page_managesites', false, true );
595 + MainWP_Cache_Warm_Helper::invalidate_manage_pages( array( 'mainwp_tab' ) );
486 596 }
487 597 }
488 598
489 599 $update_clients_screen_options = false;
@@ -516,8 +626,15 @@
516 626 if ( isset( $_POST['reset_client_overview_settings'] ) && ! empty( $_POST['reset_client_overview_settings'] ) ) {
517 627 update_user_option( $user->ID, 'mainwp_widgets_sorted_mainwp_page_manageclients', false, true );
518 628 }
519 629 }
630 +
631 + // Redirect after save settings and redirect to the same page.
632 + if ( $should_redirect ) {
633 + $page = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : 'MainWPTools';
634 + wp_safe_redirect( admin_url( 'admin.php?page=' . $page ) );
635 + exit();
636 + }
520 637 }
521 638
522 639 /**
523 640 * Method handle_settings_post()
@@ -538,8 +655,9 @@
538 655 $this->handle_manage_sites_screen_settings();
539 656 $this->handle_monitoring_sites_screen_settings();
540 657 $this->handle_clients_screen_settings();
541 658 $this->handle_updates_screen_settings();
659 + $this->handle_insights_events_screen_settings();
542 660 }
543 661
544 662 if ( isset( $_POST['select_mainwp_options_siteview'] ) ) {
545 663 $this->include_pluggable();
@@ -564,9 +682,12 @@
564 682 }
565 683
566 684 if ( isset( $_POST['submit'] ) && isset( $_POST['wp_nonce'] ) ) {
567 685 $this->include_pluggable();
568 - if ( wp_verify_nonce( sanitize_key( $_POST['wp_nonce'] ), 'Settings' ) ) {
686 + $nonce = sanitize_key( $_POST['wp_nonce'] );
687 + // Check for both 'Settings' or 'MonitoringSettings' nonces.
688 + $is_valid_nonce = wp_verify_nonce( $nonce, 'Settings' ) || wp_verify_nonce( $nonce, 'MonitoringSettings' );
689 + if ( $is_valid_nonce ) {
569 690 $updated = MainWP_Settings::handle_settings_post();
570 691 $updated |= MainWP_Backup_Handler::handle_settings_post();
571 692 $updated |= MainWP_Monitoring_Handler::handle_settings_post();
572 693 $msg = '';
@@ -572,9 +693,15 @@
572 693 $msg = '';
573 694 if ( $updated ) {
574 695 $msg = '&message=saved';
575 696 }
576 - wp_safe_redirect( admin_url( 'admin.php?page=Settings' . $msg ) );
697 +
698 + // Redirect to the correct page based on the nonce.
699 + $url_page = 'admin.php?page=Settings' . $msg;
700 + if ( wp_verify_nonce( $nonce, 'MonitoringSettings' ) ) {
701 + $url_page = 'admin.php?page=MonitoringSettings' . $msg;
702 + }
703 + wp_safe_redirect( admin_url( $url_page ) );
577 704 exit();
578 705 }
579 706 }
580 707
@@ -598,8 +725,22 @@
598 725 exit;
599 726 }
600 727 }
601 728 }
729 +
730 + // Redirect current page after reset and save overview settings.
731 + if ( isset( $_GET['page'] ) && isset( $_POST['reset_overview_settings'] ) && ( isset( $_POST['reset_overview_which_settings'] ) && 'overview_settings' === $_POST['reset_overview_which_settings'] ) ) {
732 + $page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : '';
733 + $url = 'admin.php?page=' . $page;
734 +
735 + $dashboard_id = isset( $_GET['dashboard'] ) ? absint( $_GET['dashboard'] ) : 0;
736 + if ( ! empty( $dashboard_id ) ) {
737 + $url .= '&dashboard=' . $dashboard_id;
738 + }
739 +
740 + wp_safe_redirect( admin_url( $url ) );
741 + exit;
742 + }
602 743 }
603 744
604 745 /**
605 746 * Method include_pluggable()
@@ -882,21 +1023,170 @@
882 1023 *
883 1024 * @uses \MainWP\Dashboard\MainWP_API_Handler::check_exts_upgrade()
884 1025 * @uses \MainWP\Dashboard\MainWP_Utility::update_option()
885 1026 */
886 - private function check_upgrade() {
887 - $result = MainWP_API_Handler::check_exts_upgrade();
1027 + private function check_upgrade() { // phpcs:ignore -- NOSONAR - validates and preserves per-extension update state.
1028 + $expected_extensions = $this->get_expected_extension_updates();
1029 + $has_full_scope = $this->has_full_extension_update_scope( $expected_extensions );
1030 +
888 1031 if ( null === $this->upgradeVersionInfo ) {
889 1032 $this->upgradeVersionInfo = new \stdClass();
890 1033 }
1034 +
891 1035 $this->upgradeVersionInfo->updated = time();
892 - if ( ! empty( $result ) ) {
893 - $this->upgradeVersionInfo->result = $result;
1036 +
1037 + if ( empty( $expected_extensions ) ) {
1038 + if ( $has_full_scope ) {
1039 + $this->clear_extension_update_failure();
1040 + }
1041 + MainWP_Utility::update_option( 'mainwp_upgradeVersionInfo', $this->upgradeVersionInfo );
1042 + return $has_full_scope;
894 1043 }
1044 +
1045 + $result = MainWP_API_Handler::check_exts_upgrade();
1046 + $previous_result = property_exists( $this->upgradeVersionInfo, 'result' ) && is_array( $this->upgradeVersionInfo->result ) ? $this->upgradeVersionInfo->result : array();
1047 + $fresh_result = array();
1048 + $failed = ! is_array( $result );
1049 +
1050 + foreach ( $expected_extensions as $api_slug => $installed_version ) {
1051 + $fresh = is_array( $result ) && isset( $result[ $api_slug ] ) ? $result[ $api_slug ] : null;
1052 + if ( $this->is_usable_extension_update_result( $fresh, $api_slug, $installed_version ) ) {
1053 + $fresh_result[ $api_slug ] = $fresh;
1054 + } else {
1055 + $failed = true;
1056 + }
1057 + }
1058 +
1059 + if ( ! $failed ) {
1060 + $this->upgradeVersionInfo->result = $has_full_scope ? $fresh_result : array_merge( $previous_result, $fresh_result );
1061 + }
1062 +
1063 + if ( $has_full_scope ) {
1064 + if ( $failed ) {
1065 + $this->record_extension_update_failure();
1066 + } else {
1067 + $this->clear_extension_update_failure();
1068 + }
1069 + }
1070 +
895 1071 MainWP_Utility::update_option( 'mainwp_upgradeVersionInfo', $this->upgradeVersionInfo );
1072 + return ! $failed;
896 1073 }
897 1074
898 1075 /**
1076 + * Get Add-ons expected in an update-check response.
1077 + *
1078 + * @return array API slugs keyed to installed versions.
1079 + */
1080 + private function get_expected_extension_updates() {
1081 + $expected = array();
1082 +
1083 + foreach ( MainWP_Extensions_Handler::get_extensions() as $extension ) {
1084 + if ( ! isset( $extension['activated_key'] ) || 'Activated' !== $extension['activated_key'] || empty( $extension['api'] ) ) {
1085 + continue;
1086 + }
1087 +
1088 + $expected[ $extension['api'] ] = isset( $extension['version'] ) ? (string) $extension['version'] : '';
1089 + }
1090 +
1091 + return $expected;
1092 + }
1093 +
1094 + /**
1095 + * Check whether visible Add-ons cover the shared update cache.
1096 + *
1097 + * @param array $expected Visible Add-ons expected in the response.
1098 + *
1099 + * @return bool Whether this request covers every activated Add-on.
1100 + */
1101 + private function has_full_extension_update_scope( $expected ) {
1102 + $all_expected = array();
1103 +
1104 + foreach ( get_option( 'mainwp_extensions', array() ) as $extension ) {
1105 + if ( isset( $extension['activated_key'], $extension['api'] ) && 'Activated' === $extension['activated_key'] && '' !== $extension['api'] ) {
1106 + $all_expected[] = $extension['api'];
1107 + }
1108 + }
1109 +
1110 + $visible = array_keys( $expected );
1111 + sort( $all_expected );
1112 + sort( $visible );
1113 +
1114 + return $all_expected === $visible;
1115 + }
1116 +
1117 + /**
1118 + * Check whether an Add-on update result is safe to persist.
1119 + *
1120 + * @param mixed $result Update result.
1121 + * @param string $api_slug Expected API slug.
1122 + * @param string $installed_version Installed Add-on version.
1123 + *
1124 + * @return bool Whether the result is usable.
1125 + */
1126 + private function is_usable_extension_update_result( $result, $api_slug, $installed_version ) {
1127 + if ( ! is_object( $result ) || ! isset( $result->slug ) || $api_slug !== $result->slug || ! property_exists( $result, 'new_version' ) || ! is_string( $result->new_version ) ) {
1128 + return false;
1129 + }
1130 +
1131 + $new_version = trim( $result->new_version );
1132 + if ( '' === $new_version || ( property_exists( $result, 'error' ) && ! empty( $result->error ) ) ) {
1133 + return false;
1134 + }
1135 +
1136 + if ( version_compare( $new_version, $installed_version, '>' ) ) {
1137 + return property_exists( $result, 'package' ) && is_string( $result->package ) && '' !== trim( $result->package );
1138 + }
1139 +
1140 + return true;
1141 + }
1142 +
1143 + /**
1144 + * Record a failed Add-on update-check attempt.
1145 + */
1146 + private function record_extension_update_failure() {
1147 + $failure_count = property_exists( $this->upgradeVersionInfo, 'extension_update_failure_count' ) ? (int) $this->upgradeVersionInfo->extension_update_failure_count : 0;
1148 +
1149 + if ( $failure_count < 1 || ! property_exists( $this->upgradeVersionInfo, 'extension_update_failure_episode' ) || empty( $this->upgradeVersionInfo->extension_update_failure_episode ) ) {
1150 + $this->upgradeVersionInfo->extension_update_failure_episode = wp_generate_uuid4();
1151 + }
1152 +
1153 + $this->upgradeVersionInfo->extension_update_failure_count = $failure_count + 1;
1154 + }
1155 +
1156 + /**
1157 + * Clear Add-on update-check failure state after a complete response.
1158 + */
1159 + private function clear_extension_update_failure() {
1160 + unset( $this->upgradeVersionInfo->extension_update_failure_count, $this->upgradeVersionInfo->extension_update_failure_episode );
1161 + }
1162 +
1163 + /**
1164 + * Get the active repeated-failure notice ID.
1165 + *
1166 + * @return string Notice ID, or an empty string before the second consecutive failure.
1167 + */
1168 + public function get_extension_update_failure_notice_id() {
1169 + if ( ! $this->has_full_extension_update_scope( $this->get_expected_extension_updates() ) || ! is_object( $this->upgradeVersionInfo ) || ! property_exists( $this->upgradeVersionInfo, 'extension_update_failure_count' ) || 2 > (int) $this->upgradeVersionInfo->extension_update_failure_count || ! property_exists( $this->upgradeVersionInfo, 'extension_update_failure_episode' ) ) {
1170 + return '';
1171 + }
1172 +
1173 + $episode = sanitize_key( $this->upgradeVersionInfo->extension_update_failure_episode );
1174 + return '' !== $episode ? 'mainwp-extension-update-check-failure-' . $episode : '';
1175 + }
1176 +
1177 + /**
1178 + * Retry the Add-on update check and refresh WordPress update data.
1179 + *
1180 + * @return bool Whether every expected Add-on returned a usable result.
1181 + */
1182 + public function retry_extension_update_check() {
1183 + $success = $this->check_upgrade();
1184 + delete_site_transient( 'update_plugins' );
1185 + return $success;
1186 + }
1187 +
1188 + /**
899 1189 * Method pre_check_update_custom()
900 1190 *
901 1191 * Pre-check for extension updates.
902 1192 *
@@ -911,8 +1201,14 @@
911 1201 if ( ! isset( $transient->checked ) ) {
912 1202 return $transient;
913 1203 }
914 1204
1205 + if ( defined( 'MAINWP_PLUGIN_UPGRADING' ) && true === MAINWP_PLUGIN_UPGRADING ) {
1206 + return $transient;
1207 + }
1208 +
1209 + MainWP_Logger::instance()->log_events( 'extension-updates-check', 'pre_set_site_transient_update_plugins' );
1210 +
915 1211 if ( ( null === $this->upgradeVersionInfo ) || ! property_exists( $this->upgradeVersionInfo, 'updated' ) || ( ( time() - $this->upgradeVersionInfo->updated ) > 60 ) ) {
916 1212 $this->check_upgrade();
917 1213 }
918 1214
@@ -931,8 +1227,33 @@
931 1227 }
932 1228
933 1229 return $transient;
934 1230 }
1231 +
1232 + /**
1233 + * Method hook_refresh_trusted_extension_package_url
1234 + *
1235 + * @param mixed $reply The reply from the upgrader.
1236 + * @param mixed $package The package URL.
1237 + * @param mixed $upgrader The upgrader instance.
1238 + * @param mixed $hook_extra Extra information about the upgrade, including the plugin being updated.
1239 + * @return void
1240 + */
1241 + public function hook_refresh_trusted_extension_package_url( $reply, $package, $upgrader, $hook_extra ) {
1242 + if ( isset( $hook_extra['plugin'] ) ) { // && isset( $_POST['mainwpsignature'] ) && isset( $_POST['function'] ) && 'upgradeplugintheme' === $_POST['function'] ) { // phpcs:ignore WordPress.Security.NonceVerification,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- NOSONAR - hook for plugin update action, it is safe to use $_POST here.
1243 + $ext_api_slug = dirname( $hook_extra['plugin'] );
1244 + if ( ! empty( $ext_api_slug ) ) {
1245 + $rslt_info = MainWP_API_Handler::get_upgrade_information( $ext_api_slug );
1246 + if ( ! empty( $rslt_info ) && is_object( $rslt_info ) && property_exists( $rslt_info, 'package' ) && ! empty( $rslt_info->package ) ) {
1247 + // Replace package URL.
1248 + $upgrader->skin->options['package'] = $rslt_info->package;
1249 + unset( $package );
1250 + }
1251 + }
1252 + }
1253 + return $reply;
1254 + }
1255 +
935 1256
936 1257 /**
937 1258 * Method upload_file()
938 1259 *