| @@ -51,8 +51,11 @@ | ||
| 51 | 51 | */ |
| 52 | 52 | public function __construct() { |
| 53 | 53 | Hook_Registry::add_action( 'tptn_cron_hook', array( $this, 'run_cron' ) ); |
| 54 | 54 | Hook_Registry::add_action( 'tptn_aggregation_cron_hook', array( $this, 'run_aggregation' ) ); |
| 55 | + Hook_Registry::add_action( 'tptn_count_updated', array( Dashboard_Widgets::class, 'clear_network_dashboard_cache' ) ); | |
| 56 | + Hook_Registry::add_action( 'tptn_delete_counts', array( Dashboard_Widgets::class, 'clear_network_dashboard_cache' ) ); | |
| 57 | + Hook_Registry::add_action( 'tptn_set_count', array( Dashboard_Widgets::class, 'clear_network_dashboard_cache' ) ); | |
| 55 | 58 | Hook_Registry::add_action( 'admin_init', array( $this, 'check_aggregation_cron' ) ); |
| 56 | 59 | Hook_Registry::add_action( 'admin_notices', array( $this, 'aggregation_cron_missing_notice' ) ); |
| 57 | 60 | Hook_Registry::add_action( 'cron_reschedule_event_error', array( $this, 'log_reschedule_error' ), 10, 2 ); |
| 58 | 61 | Hook_Registry::add_action( 'cron_unschedule_event_error', array( $this, 'log_unschedule_error' ), 10, 2 ); |
| @@ -65,8 +68,13 @@ | ||
| 65 | 68 | */ |
| 66 | 69 | public function run_cron() { |
| 67 | 70 | global $wpdb; |
| 68 | 71 | |
| 72 | + $table_statuses = Database::get_table_installation_status( true ); | |
| 73 | + if ( in_array( false, $table_statuses, true ) ) { | |
| 74 | + return; | |
| 75 | + } | |
| 76 | + | |
| 69 | 77 | $delete_from = TOP_TEN_STORE_DATA; |
| 70 | 78 | |
| 71 | 79 | /** |
| 72 | 80 | * Override maintenance day range. |
| @@ -112,8 +120,10 @@ | ||
| 112 | 120 | do { |
| 113 | 121 | // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared |
| 114 | 122 | $deleted_log = $wpdb->query( $wpdb->prepare( "DELETE FROM {$log_table} WHERE visited_at < %s LIMIT 1000", $from_date_log ) ); |
| 115 | 123 | } while ( $deleted_log > 0 && microtime( true ) < $deadline_log ); |
| 124 | + | |
| 125 | + Dashboard_Widgets::clear_network_dashboard_cache(); | |
| 116 | 126 | } |
| 117 | 127 | |
| 118 | 128 | /** |
| 119 | 129 | * Function to enable run or actions. |
| @@ -191,23 +201,31 @@ | ||
| 191 | 201 | public function check_aggregation_cron() { |
| 192 | 202 | /** This filter is documented in includes/admin/class-cron.php */ |
| 193 | 203 | $interval = (string) apply_filters( 'tptn_aggregation_cron_interval', 'two_minutes' ); |
| 194 | 204 | |
| 195 | - $timestamp = wp_next_scheduled( 'tptn_aggregation_cron_hook' ); | |
| 196 | - | |
| 197 | - if ( ! $timestamp ) { | |
| 205 | + if ( ! wp_next_scheduled( 'tptn_aggregation_cron_hook' ) ) { | |
| 198 | 206 | self::enable_aggregation_run(); |
| 199 | 207 | $this->aggregation_cron_was_missing = true; |
| 200 | 208 | return; |
| 201 | 209 | } |
| 202 | 210 | |
| 203 | - $crons = _get_cron_array(); | |
| 204 | - $args_key = md5( serialize( array() ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize | |
| 205 | - $current = isset( $crons[ $timestamp ]['tptn_aggregation_cron_hook'][ $args_key ]['schedule'] ) | |
| 206 | - ? $crons[ $timestamp ]['tptn_aggregation_cron_hook'][ $args_key ]['schedule'] | |
| 207 | - : ''; | |
| 211 | + // Look across all scheduled occurrences of the hook, not just the earliest one: | |
| 212 | + // a legitimate one-off catch-up event (schedule = false) can be scheduled alongside | |
| 213 | + // the recurring event and must not be mistaken for a wrong/missing recurring schedule. | |
| 214 | + $args_key = md5( serialize( array() ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize | |
| 215 | + $crons = _get_cron_array(); | |
| 216 | + $has_correct_recurrence = false; | |
| 208 | 217 | |
| 209 | - if ( $current !== $interval ) { | |
| 218 | + foreach ( $crons as $events ) { | |
| 219 | + if ( isset( $events['tptn_aggregation_cron_hook'][ $args_key ]['schedule'] ) | |
| 220 | + && $interval === $events['tptn_aggregation_cron_hook'][ $args_key ]['schedule'] | |
| 221 | + ) { | |
| 222 | + $has_correct_recurrence = true; | |
| 223 | + break; | |
| 224 | + } | |
| 225 | + } | |
| 226 | + | |
| 227 | + if ( ! $has_correct_recurrence ) { | |
| 210 | 228 | wp_clear_scheduled_hook( 'tptn_aggregation_cron_hook' ); |
| 211 | 229 | self::enable_aggregation_run(); |
| 212 | 230 | $this->aggregation_cron_interval_changed = true; |
| 213 | 231 | } |
| @@ -317,8 +335,12 @@ | ||
| 317 | 335 | |
| 318 | 336 | /** |
| 319 | 337 | * Retrieve the last recorded cron scheduling error for a hook, if any. |
| 320 | 338 | * |
| 339 | + * Core reports a reschedule failure when update_option( 'cron' ) is a no-op — | |
| 340 | + * e.g. a concurrent cron run already saved the identical schedule. If the hook | |
| 341 | + * has a valid next occurrence the recorded error is stale: clear and ignore it. | |
| 342 | + * | |
| 321 | 343 | * @since 4.4.0 |
| 322 | 344 | * |
| 323 | 345 | * @param string $hook Hook name. |
| 324 | 346 | * @return array{code: string, message: string, time: int}|false Error data, or false if none recorded. |
| @@ -325,9 +347,18 @@ | ||
| 325 | 347 | */ |
| 326 | 348 | public static function get_reschedule_error( $hook ) { |
| 327 | 349 | $error = get_transient( self::RESCHEDULE_ERROR_TRANSIENT_PREFIX . $hook ); |
| 328 | 350 | |
| 329 | - return is_array( $error ) ? $error : false; | |
| 351 | + if ( ! is_array( $error ) ) { | |
| 352 | + return false; | |
| 353 | + } | |
| 354 | + | |
| 355 | + if ( wp_next_scheduled( $hook ) ) { | |
| 356 | + delete_transient( self::RESCHEDULE_ERROR_TRANSIENT_PREFIX . $hook ); | |
| 357 | + return false; | |
| 358 | + } | |
| 359 | + | |
| 360 | + return $error; | |
| 330 | 361 | } |
| 331 | 362 | |
| 332 | 363 | /** |
| 333 | 364 | * Clear the recorded cron scheduling errors for all tracked hooks. |