PluginProbe
MainWP Dashboard: Self-hosted WordPress Management for Agencies / 6.2.1
MainWP Dashboard: Self-hosted WordPress Management for Agencies v6.2.1
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
mainwp / class / class-mainwp-monitoring-handler.php

class-mainwp-monitoring-handler.php in MainWP Dashboard: Self-hosted WordPress Management for Agencies 6.2.1, at class/class-mainwp-monitoring-handler.php

444 lines 16.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * MainWP Monotoring Sites Handler.
4 *
5 * @package MainWP/Dashboard
6 */
7
8 namespace MainWP\Dashboard;
9
10 // Exit if accessed directly.
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit;
13 }
14
15 /**
16 * Class MainWP_Monitoring_Handler
17 *
18 * @package MainWP\Dashboard
19 *
20 * This class handles the $_POST of Settings Options.
21 */
22 class MainWP_Monitoring_Handler { // phpcs:ignore Generic.Classes.OpeningBraceSameLine.ContentAfterBrace -- NOSONAR.
23
24
25 /** Cache for uptime status to avoid multiple queries during sites table rendering.
26 *
27 * @var array|null
28 */
29 private static $uptime_cache = null;
30
31 /**
32 * Manage the settings post.
33 *
34 * @uses MainWP_Utility::update_option()
35 *
36 * @return bool True on success, false on failure.
37 *
38 * @uses \MainWP\Dashboard\MainWP_System_Utility::is_admin()
39 * @uses \MainWP\Dashboard\MainWP_Utility::update_option()
40 */
41 public static function handle_settings_post() { // phpcs:ignore -- NOSONAR - complex.
42 // Check if form was submitted and nonce is present.
43 if ( ! isset( $_POST['submit'] ) || ! isset( $_POST['wp_nonce'] ) ) {
44 return false;
45 }
46
47 // Verify nonce and admin permissions.
48 $nonce = sanitize_key( $_POST['wp_nonce'] );
49 $is_valid_nonce = wp_verify_nonce( $nonce, 'MonitoringSettings' );
50
51 if ( ! $is_valid_nonce || ! MainWP_System_Utility::is_admin() ) {
52 return false;
53 }
54
55 // Save global uptime monitoring settings.
56 MainWP_Uptime_Monitoring_Edit::instance()->handle_save_settings();
57
58 // Save Site Health Monitoring setting.
59 $disable_health = isset( $_POST['mainwp_disable_sitesHealthMonitoring'] ) ? 0 : 1;
60 MainWP_Utility::update_option( 'mainwp_disableSitesHealthMonitoring', $disable_health );
61
62 // Save Site Health Threshold with validation.
63 $threshold = isset( $_POST['mainwp_site_healthThreshold'] ) ? intval( $_POST['mainwp_site_healthThreshold'] ) : 80;
64
65 // Validate threshold value - only allow 80 or 100.
66 $allowed_thresholds = array( 80, 100 );
67 if ( ! in_array( $threshold, $allowed_thresholds, true ) ) {
68 $threshold = 80; // Default to 80 if invalid value.
69 }
70
71 MainWP_Utility::update_option( 'mainwp_sitehealthThreshold', $threshold );
72
73 /**
74 * Action: mainwp_after_save_monitoring_settings
75 *
76 * Fires after monitoring settings are saved.
77 *
78 * @since 4.1
79 */
80 do_action( 'mainwp_after_save_monitoring_settings', $_POST );
81
82 return true;
83 }
84
85
86 /**
87 * Check a website HTTP header status.
88 *
89 * @param object $website Object containing the website info.
90 * @param bool $chk_http_site Check site http response.
91 *
92 * @return mixed Check result.
93 *
94 * @uses \MainWP\Dashboard\MainWP_Connect::check_website_status()
95 * @uses \MainWP\Dashboard\MainWP_Connect::check_ignored_http_code()
96 * @uses \MainWP\Dashboard\MainWP_DB::update_website_values()
97 */
98 public static function handle_check_website( $website, $chk_http_site = false ) {
99
100 $result_comp = MainWP_Connect::check_website_status( $website, $chk_http_site );
101
102 if ( ! is_array( $result_comp ) ) {
103 return false;
104 }
105
106 $new_code = ( is_array( $result_comp ) && isset( $result_comp['httpCode'] ) ) ? (int) $result_comp['httpCode'] : 0;
107
108 if ( isset( $result_comp['new_uptime_status'] ) ) {
109 $new_status = (int) $result_comp['new_uptime_status'];
110 $new_check_result = 0; // pending.
111 if ( MainWP_Uptime_Monitoring_Connect::UP === $new_status ) {
112 $new_check_result = 1; // 1 - online, -1 offline.
113 } elseif ( MainWP_Uptime_Monitoring_Connect::DOWN === $new_status ) {
114 $new_check_result = -1; // 1 - online, -1 offline.
115 }
116 } else {
117 $new_status = MainWP_Connect::check_ignored_http_code( $new_code, $website ); // legacy check http code.
118 $new_check_result = $new_status ? 1 : -1; // 1 - online, -1 offline.
119 }
120
121 $time = isset( $result_comp['check_offline_time'] ) ? $result_comp['check_offline_time'] : time();
122 $noticed = static::check_http_status_notification_threshold( $website, $new_check_result );
123 // Save last status.
124 MainWP_DB::instance()->update_website_values(
125 $website->id,
126 array(
127 'offline_check_result' => $new_check_result,
128 'offline_checks_last' => $time,
129 'http_response_code' => $new_code,
130 'http_code_noticed' => $noticed,
131 )
132 );
133
134 MainWP_Logger::instance()->log_uptime_check( 'Check website status :: [website=' . (string) $website->url . '] :: [offline_check_result=' . intval( $new_check_result ) . '] :: [http_response_code=' . esc_html( $new_code ) . '] :: [http_code_noticed=' . esc_html( $noticed ) . ']' );
135
136 return $result_comp; // return results for ajax check requests.
137 }
138
139
140 /**
141 * Get a new HTTP status notice.
142 *
143 * @param object $website Object containing the website info.
144 * @param int $check_result The new HTTP code value.
145 *
146 * @return int $noticed Noticed value.
147 */
148 public static function check_http_status_notification_threshold( $website, $check_result ) {
149 $threshold = HOUR_IN_SECONDS;
150 $noticed = 1; // default is noticed.
151 if ( property_exists( $website, 'offline_checks_last' ) ) {
152 $last_noticed = MainWP_DB::instance()->get_website_option( $website, 'http_status_notice_check_time', 0 );
153 if ( -1 === $check_result ) {
154 $noticed = 0;
155 if ( $last_noticed > time() - $threshold ) {
156 $noticed = 1;
157 }
158 }
159 }
160 return $noticed;
161 }
162
163
164 /**
165 * Get a new HTTP status notice.
166 *
167 * @compatible.
168 *
169 * @return int $noticed_value New HTTP status.
170 */
171 public static function get_http_noticed_status_value() {
172 return 1;
173 }
174
175 /**
176 * Get new site health value.
177 *
178 * @param object $website Object containing the website info.
179 * @param object $new_health New site health value.
180 *
181 * @return int $noticed_value new site health status.
182 */
183 public static function get_health_noticed_status_value( $website, $new_health ) {
184
185 // for sure if property does not existed.
186 if ( ! property_exists( $website, 'health_value' ) || ! property_exists( $website, 'health_site_noticed' ) ) {
187 return null;
188 }
189
190 $old_value = $website->health_value;
191 $noticed_value = $website->health_site_noticed;
192
193 if ( ( ( 80 <= $old_value && 80 > $new_health ) || ( 80 > $old_value && 80 <= $new_health ) ) && 1 === (int) $noticed_value ) {
194 $noticed_value = 0;
195 }
196 return $noticed_value;
197 }
198
199 /**
200 * Determin if site is up or down based on HTTP code.
201 *
202 * @param int $http_code HTTP code.
203 */
204 public static function get_site_checking_status( $http_code ) {
205 $code200 = 200 === (int) $http_code ? 1 : 0;
206 return empty( $http_code ) ? 0 : $code200;
207 }
208
209 /**
210 * Basic site uptime monitoring.
211 *
212 * @param array $websites Array containing the websites.
213 * @param string $admin_email Notification email.
214 * @param string $email_settings Email settings.
215 * @param bool $plain_text Determines if the plain text format should be used.
216 * @param bool $to_admin Send to admin or not.
217 *
218 * @uses \MainWP\Dashboard\MainWP_DB::update_website_values()
219 * @uses \MainWP\Dashboard\MainWP_Notification::send_websites_uptime_monitoring()
220 * @uses \MainWP\Dashboard\MainWP_Notification_Template::get_template_html()
221 */
222 public static function notice_sites_uptime_monitoring( $websites, $admin_email, $email_settings, $plain_text, $to_admin = false ) {
223
224 $heading = $email_settings['heading'];
225 $subject = $email_settings['subject'];
226
227 if ( $to_admin && ! empty( $admin_email ) ) {
228 $mail_content = MainWP_Notification_Template::instance()->get_template_html(
229 'emails/mainwp-uptime-monitoring-email.php',
230 array(
231 'sites' => $websites,
232 'heading' => $heading,
233 )
234 );
235 if ( ! empty( $mail_content ) ) {
236 MainWP_Logger::instance()->log_uptime_notice( 'Uptime notification is being sent for admin.' );
237 MainWP_Notification::send_websites_uptime_monitoring( $admin_email, $subject, $mail_content, $plain_text, $websites );
238 usleep( 100000 );
239 }
240 do_action( 'mainwp_after_notice_sites_uptime_monitoring_admin', $websites );
241 return;
242 }
243
244 // Send individual notifications by iterating through each site.
245 foreach ( $websites as $site ) {
246 $email = '';
247 $addition_emails = $site->monitoring_notification_emails;
248 if ( ! empty( $addition_emails ) ) {
249 $email .= ',' . $addition_emails; // send to addition emails too.
250 }
251
252 $mail_content = MainWP_Notification_Template::instance()->get_template_html(
253 'emails/mainwp-uptime-monitoring-email.php',
254 array(
255 'current_email_site' => $site, // support tokens process.
256 'heading' => $heading,
257 )
258 );
259
260 if ( ! empty( $email_settings['recipients'] ) ) {
261 $email .= ',' . $email_settings['recipients']; // send to recipients.
262 }
263
264 $email = trim( $email, ',' );
265
266 if ( ! empty( $mail_content ) ) {
267 MainWP_Logger::instance()->log_uptime_notice( 'Uptime notification is being sent for individual site.' );
268 MainWP_Notification::send_websites_uptime_monitoring( $email, $subject, $mail_content, $plain_text, $site );
269 usleep( 100000 );
270 }
271 do_action( 'mainwp_after_notice_sites_uptime_monitoring_individual', $site );
272 }
273 }
274
275 /**
276 * Site health monitoring.
277 *
278 * @param string $email_settings Email settings.
279 * @param array $websites Array containing the websites.
280 * @param string $email Notification email.
281 * @param bool $plain_text Determines if the plain text format should be used.
282 * @param bool $general Determines if it's a general notification.
283 * @param bool $to_admin Send to admin or not.
284 *
285 * @uses \MainWP\Dashboard\MainWP_DB_Common::get_user_notification_email()
286 * @uses \MainWP\Dashboard\MainWP_DB::update_website_sync_values()
287 * @uses \MainWP\Dashboard\MainWP_Notification::send_websites_health_status_notification()
288 * @uses \MainWP\Dashboard\MainWP_Notification_Template::get_template_html()
289 */
290 public static function notice_site_health_threshold( $email_settings, $websites, $email, $plain_text, $general = true, $to_admin = false ) { // phpcs:ignore -- NOSONAR - complex.
291
292 $to_email = $email;
293
294 $admin_email = MainWP_Notification_Settings::get_general_email();
295
296 $heading = $email_settings['heading'];
297
298 if ( $to_admin && ! empty( $admin_email ) ) {
299 $mail_content = MainWP_Notification_Template::instance()->get_template_html(
300 'emails/mainwp-site-health-monitoring-email.php',
301 array(
302 'sites' => $websites,
303 'heading' => $heading,
304 )
305 );
306 if ( ! empty( $mail_content ) ) {
307 $subject = $email_settings['subject'];
308 MainWP_Notification::send_websites_health_status_notification( $admin_email, $subject, $mail_content, $plain_text );
309 usleep( 100000 );
310 }
311 return;
312 }
313
314 $to_email = $general ? $admin_email : $to_email;
315
316 foreach ( $websites as $site ) {
317 $email_site_settings = $email_settings;
318 MainWP_Notification_Settings::prepare_general_email_settings_for_site( $email_site_settings, $site );
319 $heading = $email_site_settings['heading'];
320 $subject = $email_site_settings['subject'];
321
322 if ( ! $general ) {
323 $addition_emails = $site->monitoring_notification_emails;
324 if ( ! empty( $addition_emails ) ) {
325 $to_email .= ',' . $addition_emails; // send to addition emails too.
326 }
327 }
328
329 $mail_content = MainWP_Notification_Template::instance()->get_template_html(
330 'emails/mainwp-site-health-monitoring-email.php',
331 array(
332 'current_email_site' => $site, // support tokens process.
333 'heading' => $heading,
334 )
335 );
336
337 if ( ! empty( $email_settings['recipients'] ) ) {
338 $to_email .= ',' . $email_settings['recipients']; // send to recipients.
339 }
340
341 $to_email = trim( $to_email, ',' );
342
343 if ( ! empty( $to_email ) && ! empty( $mail_content ) ) {
344 MainWP_Notification::send_websites_health_status_notification( $to_email, $subject, $mail_content, $plain_text );
345 // update noticed value.
346 MainWP_DB::instance()->update_website_sync_values(
347 $site->id,
348 array(
349 'health_site_noticed' => 1, // as noticed.
350 )
351 );
352 usleep( 100000 );
353 }
354 }
355 }
356
357 /**
358 * Preload uptime data for all websites.
359 * Instead of querying monitor_stat_hourly per site (19 queries), do one batch query.
360 *
361 * @param array $websites Array of websites to preload data for.
362 *
363 * @return void
364 */
365 public static function preload_uptime_data( $websites ) { //phpcs:ignore -- NOSONAR -- complex func.
366
367 if ( null !== self::$uptime_cache ) {
368 return;
369 }
370
371 self::$uptime_cache = array();
372
373 // Collect monitor_ids from websites.
374 $monitor_ids = array();
375
376 if ( MainWP_DB::is_result( $websites ) ) {
377 MainWP_DB::data_seek( $websites, 0 );
378 while ( $websites && ( $site = MainWP_DB::fetch_array( $websites ) ) ) {
379 $mid = isset( $site['monitor_id'] ) ? (int) $site['monitor_id'] : 0;
380 if ( $mid > 0 ) {
381 $monitor_ids[ $mid ] = true;
382 }
383 }
384 MainWP_DB::data_seek( $websites, 0 );
385 }
386
387 $int_monitor_ids = array_map( 'intval', array_keys( $monitor_ids ) );
388
389 if ( empty( $int_monitor_ids ) ) {
390 return;
391 }
392
393 $last24_time = MainWP_Uptime_Monitoring_Handle::get_hourly_key_by_timestamp( time() - DAY_IN_SECONDS );
394
395 // Single batch query for all monitors.
396 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name from $wpdb->prefix.
397 $results = MainWP_DB_Uptime_Monitoring::instance()->get_batch_stat_hourly( $int_monitor_ids, $last24_time );
398
399 if ( is_array( $results ) ) {
400 // Group by monitor_id and compute percentage.
401 $grouped = array();
402 foreach ( $results as $r ) {
403 $mid = (int) $r['monitor_id'];
404 if ( ! isset( $grouped[ $mid ] ) ) {
405 $grouped[ $mid ] = array();
406 }
407 $grouped[ $mid ][] = $r;
408 }
409
410 foreach ( $grouped as $mid => $uptime_status ) {
411 ob_start();
412 MainWP_Monitoring_Sites_List_Table::instance()->render_last24_uptime_status( $uptime_status, $last24_time );
413 $html = ob_get_clean();
414 self::$uptime_cache[ $mid ] = $html;
415 }
416 }
417 }
418
419 /**
420 * Method intercept_column()
421 *
422 * Intercept column display in the sites table.
423 *
424 * @param string $content Column content.
425 * @param string $column_name Column name.
426 * @param array $website Website data.
427 *
428 * @return string $content Modified column content.
429 */
430 public static function intercept_column( $content, $column_name, $website ) {
431 // Uptime: use preloaded batch data instead of per-row query.
432 if ( 'uptime' === $column_name ) {
433 $mid = isset( $website['monitor_id'] ) ? (int) $website['monitor_id'] : 0;
434 if ( $mid > 0 && isset( self::$uptime_cache[ $mid ] ) ) {
435 return self::$uptime_cache[ $mid ];
436 }
437 if ( $mid <= 0 ) {
438 return '<span class="ui small text">N/A</span>';
439 }
440 }
441 return $content;
442 }
443 }
444