← All changes
|
includes/rest-api/controller/version2/class-mainwp-rest-monitors-controller.php
+104
-9
6.0.11
→
6.2.1
View file →
| @@ -366,10 +366,11 @@ | ||
| 366 | 366 | $incidents_7d = (int) $this->db->count_site_incidents_stats( $monitor_id, $one_week_ago, $today ); |
| 367 | 367 | $incidents_30d = (int) $this->db->count_site_incidents_stats( $monitor_id, $one_month_ago, $today ); |
| 368 | 368 | |
| 369 | 369 | // Get monitor type & check frequency. |
| 370 | - $type = $this->get_apply_setting( 'type', $monitor->type ?? '', 'useglobal', 'http' ); | |
| 371 | - $interval_min = (int) $this->get_apply_setting( 'interval', (int) ( $monitor->interval ?? 0 ), -1, 60 ); | |
| 370 | + $type = $this->get_apply_setting( 'type', $monitor->type ?? '', 'useglobal', 'http' ); | |
| 371 | + $interval_min = (int) $this->get_apply_setting( 'interval', (int) ( $monitor->interval ?? 0 ), -1, 60 ); | |
| 372 | + $bypass_cache_opt = (int) $this->get_apply_setting( 'bypass_cache', (int) ( $monitor->bypass_cache ?? -1 ), -1, 0 ); | |
| 372 | 373 | |
| 373 | 374 | $record = array( |
| 374 | 375 | 'id' => $monitor_id, |
| 375 | 376 | 'url' => $monitor->url ?? '', |
| @@ -385,8 +386,9 @@ | ||
| 385 | 386 | 'response_time_max_ms' => (int) ( $reports_data['max_time_ms'] ?? 0 ), |
| 386 | 387 | 'last_check_at' => ! empty( $monitor->lasttime_check ) ? gmdate( 'Y-m-d H:i:s', (int) $monitor->lasttime_check ) : '', |
| 387 | 388 | 'last_status_code' => ! empty( $monitor->last_http_code ) ? (int) $monitor->last_http_code : '', |
| 388 | 389 | 'last_status' => $this->uptime_status( $monitor->last_status ?? null ), |
| 390 | + 'bypass_cache' => $bypass_cache_opt, | |
| 389 | 391 | ); |
| 390 | 392 | |
| 391 | 393 | // Filter data by allowed fields. |
| 392 | 394 | $data[] = $this->filter_response_data_by_allowed_fields( $record, 'view' ); |
| @@ -530,10 +532,11 @@ | ||
| 530 | 532 | // Get full heartbeat data. |
| 531 | 533 | $heartbeat_data = $this->db->get_uptime_monitoring_stats( $monitor_id, $one_month_ago, $today ); |
| 532 | 534 | |
| 533 | 535 | // Get monitor type & check frequency. |
| 534 | - $type = $this->get_apply_setting( 'type', $monitor->type ?? '', 'useglobal', 'http' ); | |
| 535 | - $interval_min = (int) $this->get_apply_setting( 'interval', (int) ( $monitor->interval ?? 0 ), -1, 60 ); | |
| 536 | + $type = $this->get_apply_setting( 'type', $monitor->type ?? '', 'useglobal', 'http' ); | |
| 537 | + $interval_min = (int) $this->get_apply_setting( 'interval', (int) ( $monitor->interval ?? 0 ), -1, 60 ); | |
| 538 | + $bypass_cache_opt = (int) $this->get_apply_setting( 'bypass_cache', (int) ( $monitor->bypass_cache ?? -1 ), -1, 0 ); | |
| 536 | 539 | |
| 537 | 540 | $record = array( |
| 538 | 541 | 'id' => $monitor_id, |
| 539 | 542 | 'name' => $monitor->name ?? '', |
| @@ -551,8 +554,9 @@ | ||
| 551 | 554 | 'heartbeats' => $heartbeat_data ?? array(), |
| 552 | 555 | 'last_check_at' => ! empty( $monitor->lasttime_check ) ? gmdate( 'Y-m-d H:i:s', (int) $monitor->lasttime_check ) : '', |
| 553 | 556 | 'last_status_code' => ! empty( $monitor->last_http_code ) ? (int) $monitor->last_http_code : '', |
| 554 | 557 | 'last_status' => $this->uptime_status( $monitor->last_status ?? null ), |
| 558 | + 'bypass_cache' => $bypass_cache_opt, | |
| 555 | 559 | ); |
| 556 | 560 | |
| 557 | 561 | return rest_ensure_response( |
| 558 | 562 | array( |
| @@ -879,8 +883,16 @@ | ||
| 879 | 883 | */ |
| 880 | 884 | public function update_global_monitoring_settings( $request ) { |
| 881 | 885 | // Get request body. |
| 882 | 886 | $body = $request->get_json_params(); |
| 887 | + | |
| 888 | + // A JSON scalar or list body carries none of the settings keys: array_intersect_key() below | |
| 889 | + // fatals on the scalar, and the list would intersect down to nothing and answer as a | |
| 890 | + // successful no-op, so both are treated as no body at all. | |
| 891 | + if ( ! is_array( $body ) || ( array() !== $body && array_keys( $body ) === range( 0, count( $body ) - 1 ) ) ) { | |
| 892 | + $body = array(); | |
| 893 | + } | |
| 894 | + | |
| 883 | 895 | if ( empty( $body ) ) { |
| 884 | 896 | return new WP_Error( |
| 885 | 897 | 'empty_body', |
| 886 | 898 | __( 'Request body is empty.', 'mainwp' ), |
| @@ -896,10 +908,27 @@ | ||
| 896 | 908 | __( 'Invalid content type. Expected application/json.', 'mainwp' ), |
| 897 | 909 | ); |
| 898 | 910 | } |
| 899 | 911 | |
| 912 | + // Reject a key the route does not declare. get_sanitized_settings_params() drops it, so a | |
| 913 | + // misspelled setting would otherwise reach the processor as an empty settings array and | |
| 914 | + // answer as a successful no-op. The individual route reports the same mistake through its | |
| 915 | + // schema, so it gets the same error code here. | |
| 916 | + $unknown_keys = array_diff( array_keys( $body ), array_keys( $this->get_global_monitor_settings_allowed_fields() ) ); | |
| 917 | + if ( ! empty( $unknown_keys ) ) { | |
| 918 | + return new WP_Error( | |
| 919 | + 'rest_additional_properties_forbidden', | |
| 920 | + sprintf( | |
| 921 | + /* translators: %s: comma separated list of request body keys the route does not accept. */ | |
| 922 | + __( 'Unknown settings: %s.', 'mainwp' ), | |
| 923 | + implode( ', ', $unknown_keys ) | |
| 924 | + ), | |
| 925 | + array( 'status' => 400 ) | |
| 926 | + ); | |
| 927 | + } | |
| 928 | + | |
| 900 | 929 | // Process global monitoring settings update. |
| 901 | - $result = $this->process_global_monitoring_settings_update( $body ); | |
| 930 | + $result = $this->process_global_monitoring_settings_update( $this->get_sanitized_settings_params( $request, $body ) ); | |
| 902 | 931 | if ( is_wp_error( $result ) ) { |
| 903 | 932 | return $result; |
| 904 | 933 | } |
| 905 | 934 | |
| @@ -934,12 +963,20 @@ | ||
| 934 | 963 | |
| 935 | 964 | // Get request body. |
| 936 | 965 | $body = $request->get_json_params(); |
| 937 | 966 | |
| 967 | + // A JSON scalar or list body carries none of the settings keys: array_intersect_key() below | |
| 968 | + // fatals on the scalar, and the list would intersect down to nothing and answer as a | |
| 969 | + // successful no-op, so both are treated as no body at all. | |
| 970 | + if ( ! is_array( $body ) || ( array() !== $body && array_keys( $body ) === range( 0, count( $body ) - 1 ) ) ) { | |
| 971 | + $body = array(); | |
| 972 | + } | |
| 973 | + | |
| 938 | 974 | if ( empty( $body ) ) { |
| 939 | 975 | return new WP_Error( |
| 940 | 976 | 'empty_body', |
| 941 | 977 | __( 'Request body is empty.', 'mainwp' ), |
| 978 | + array( 'status' => 400 ) | |
| 942 | 979 | ); |
| 943 | 980 | } |
| 944 | 981 | |
| 945 | 982 | // Check content type. |
| @@ -950,17 +987,22 @@ | ||
| 950 | 987 | __( 'Invalid content type. Expected application/json.', 'mainwp' ), |
| 951 | 988 | ); |
| 952 | 989 | } |
| 953 | 990 | |
| 954 | - // Validate request body against schema. | |
| 991 | + // Validate request body against schema. The schema has to see the raw body: the sanitized | |
| 992 | + // params keep only the keys the route registered, so a misspelled key would be filtered out | |
| 993 | + // before additionalProperties could reject it. | |
| 955 | 994 | $schema = $this->get_monitor_settings_schema(); // Get default schema. |
| 956 | 995 | $valid = rest_validate_value_from_schema( $body, $schema, 'body' ); |
| 957 | 996 | if ( is_wp_error( $valid ) ) { |
| 997 | + // A schema error carries no status of its own and REST falls back to 500 for one that | |
| 998 | + // has none, which is the wrong answer for a body the caller got wrong. | |
| 999 | + $valid->add_data( array( 'status' => 400 ) ); | |
| 958 | 1000 | return $valid; |
| 959 | 1001 | } |
| 960 | 1002 | |
| 961 | 1003 | // Sanitize request body. |
| 962 | - $data = rest_sanitize_value_from_schema( $body, $schema ); | |
| 1004 | + $data = rest_sanitize_value_from_schema( $this->get_sanitized_settings_params( $request, $body ), $schema ); | |
| 963 | 1005 | |
| 964 | 1006 | // Process individual monitor settings update. |
| 965 | 1007 | $result = $this->process_individual_monitor_settings_update( $monitor, $data ); |
| 966 | 1008 | |
| @@ -1013,9 +1055,18 @@ | ||
| 1013 | 1055 | // Check website. |
| 1014 | 1056 | $error_msg = ''; |
| 1015 | 1057 | $result = ''; |
| 1016 | 1058 | try { |
| 1017 | - $result = MainWP_Monitoring_Handler::handle_check_website( $website ); | |
| 1059 | + if ( ! empty( $monitor->issub ) ) { | |
| 1060 | + $result = MainWP_Uptime_Monitoring_Handle::check_website_uptime_monitoring_status( | |
| 1061 | + $monitor, | |
| 1062 | + array( | |
| 1063 | + 'ignore_compatible_save' => 1, // compatible. | |
| 1064 | + ) | |
| 1065 | + ); | |
| 1066 | + } else { | |
| 1067 | + $result = MainWP_Monitoring_Handler::handle_check_website( $website ); | |
| 1068 | + } | |
| 1018 | 1069 | } catch ( \Exception $e ) { |
| 1019 | 1070 | $error_msg = $e->getMessage(); |
| 1020 | 1071 | } |
| 1021 | 1072 | |
| @@ -1044,8 +1095,30 @@ | ||
| 1044 | 1095 | ); |
| 1045 | 1096 | } |
| 1046 | 1097 | |
| 1047 | 1098 | /** |
| 1099 | + * Get the settings the request carries, as the registered callbacks sanitized them. | |
| 1100 | + * | |
| 1101 | + * @param WP_REST_Request $request Full details about the request. | |
| 1102 | + * @param array $body Raw request body. | |
| 1103 | + * | |
| 1104 | + * @return array Sanitized settings, keyed by the keys the body carried. | |
| 1105 | + */ | |
| 1106 | + private function get_sanitized_settings_params( $request, $body ) { | |
| 1107 | + // The registered sanitize_callbacks produce the request params, so the persisted value is | |
| 1108 | + // read from there rather than from the raw body; WordPress currently writes the sanitized | |
| 1109 | + // value back into the JSON body too, but that is internal to sanitize_params(), not a | |
| 1110 | + // contract. get_params() also carries auth, query and route params, so keep only the | |
| 1111 | + // settings keys the route declares and only the ones this body actually sent. It merges in | |
| 1112 | + // the reverse of get_parameter_order(), so a JSON body value beats a query parameter of the | |
| 1113 | + // same name rather than the other way round. | |
| 1114 | + $allowed = $this->get_global_monitor_settings_allowed_fields(); | |
| 1115 | + $params = array_intersect_key( $request->get_params(), $allowed ); | |
| 1116 | + | |
| 1117 | + return array_intersect_key( $params, $body ); | |
| 1118 | + } | |
| 1119 | + | |
| 1120 | + /** | |
| 1048 | 1121 | * Process global monitoring settings update with all business logic. |
| 1049 | 1122 | * |
| 1050 | 1123 | * @param array $settings Settings to update. |
| 1051 | 1124 | * |
| @@ -1109,8 +1182,12 @@ | ||
| 1109 | 1182 | if ( isset( $settings['keyword'] ) ) { |
| 1110 | 1183 | $updated_settings['keyword'] = $settings['keyword']; |
| 1111 | 1184 | } |
| 1112 | 1185 | |
| 1186 | + if ( isset( $settings['bypass_cache'] ) && in_array( (int) $settings['bypass_cache'], array( 1, 0 ), true ) ) { | |
| 1187 | + $updated_settings['bypass_cache'] = $settings['bypass_cache']; | |
| 1188 | + } | |
| 1189 | + | |
| 1113 | 1190 | // Apply filters to allow customization. |
| 1114 | 1191 | $updated_settings = apply_filters( 'mainwp_uptime_monitoring_update_global_settings', $updated_settings ); |
| 1115 | 1192 | |
| 1116 | 1193 | // Save the updated settings. |
| @@ -1189,8 +1266,16 @@ | ||
| 1189 | 1266 | |
| 1190 | 1267 | // Update the monitor. |
| 1191 | 1268 | $result = $this->db->update_wp_monitor( $update_data ); |
| 1192 | 1269 | |
| 1270 | + if ( isset( $settings['bypass_cache'] ) ) { | |
| 1271 | + $bypass_cache = (int) $settings['bypass_cache']; | |
| 1272 | + if ( in_array( $bypass_cache, array( -1, 1, 0 ), true ) ) { | |
| 1273 | + MainWP_DB_Uptime_Monitoring::instance()->update_website_option( $monitor->wpid, 'bypass_cache', $bypass_cache ); | |
| 1274 | + $update_data['bypass_cache'] = $bypass_cache; | |
| 1275 | + } | |
| 1276 | + } | |
| 1277 | + | |
| 1193 | 1278 | if ( false === $result ) { |
| 1194 | 1279 | return new WP_Error( |
| 1195 | 1280 | 'update_failed', |
| 1196 | 1281 | __( 'Failed to update monitor settings.', 'mainwp' ), |
| @@ -1501,9 +1586,9 @@ | ||
| 1501 | 1586 | 'validate_callback' => array( $this, 'settings_validate_method_param' ), |
| 1502 | 1587 | ), |
| 1503 | 1588 | 'expected_status' => array( |
| 1504 | 1589 | 'required' => false, |
| 1505 | - 'type' => 'array', | |
| 1590 | + 'type' => 'string', | |
| 1506 | 1591 | 'description' => __( 'Expected HTTP status codes.', 'mainwp' ), |
| 1507 | 1592 | 'sanitize_callback' => array( $this, 'sanitize_expected_status_text_field' ), |
| 1508 | 1593 | 'validate_callback' => array( $this, 'settings_validate_expected_status_param' ), |
| 1509 | 1594 | ), |
| @@ -2207,8 +2292,13 @@ | ||
| 2207 | 2292 | 'timeout' => array( 'type' => 'integer' ), |
| 2208 | 2293 | 'interval' => array( 'type' => 'string' ), |
| 2209 | 2294 | 'expected_status' => array( 'type' => array( 'string' ) ), |
| 2210 | 2295 | 'keyword' => array( 'type' => 'string' ), |
| 2296 | + 'bypass_cache' => array( | |
| 2297 | + 'type' => 'integer', | |
| 2298 | + 'enum' => array( -1, 0, 1 ), | |
| 2299 | + 'description' => __( 'Cache bypass setting. -1 uses the global setting, 0 disables it, and 1 enables it.', 'mainwp' ), | |
| 2300 | + ), | |
| 2211 | 2301 | ), |
| 2212 | 2302 | ); |
| 2213 | 2303 | } |
| 2214 | 2304 | |
| @@ -2255,8 +2345,13 @@ | ||
| 2255 | 2345 | ), |
| 2256 | 2346 | 'type' => array( |
| 2257 | 2347 | 'type' => 'string', |
| 2258 | 2348 | 'description' => __( 'Monitor type.', 'mainwp' ), |
| 2349 | + 'context' => array( 'view', 'monitor_view' ), | |
| 2350 | + ), | |
| 2351 | + 'bypass_cache' => array( | |
| 2352 | + 'type' => 'integer', | |
| 2353 | + 'description' => __( 'Attempt to bypass page cache.', 'mainwp' ), | |
| 2259 | 2354 | 'context' => array( 'view', 'monitor_view' ), |
| 2260 | 2355 | ), |
| 2261 | 2356 | 'last_status' => array( |
| 2262 | 2357 | 'type' => 'string', |