prefix . 'wpwand_gen_jobs'; $changed = false; foreach ($schedules as $i => $schedule) { if (!is_array($schedule) || array_key_exists('posts_written', $schedule)) { continue; // Already counted, and the live counters are the truth now. } $id = (string) ($schedule['id'] ?? ''); if ($id === '') { continue; } // Every post still tagged with this schedule. Not the capped query the modal uses — // that one stops at twenty because twenty is all a modal can show. $written = get_posts([ 'post_type' => 'post', 'post_status' => 'any', 'fields' => 'ids', 'posts_per_page' => -1, 'no_found_rows' => false, 'meta_key' => '_wpwand_schedule_id', // phpcs:ignore WordPress.DB.SlowDBQuery 'meta_value' => $id, // phpcs:ignore WordPress.DB.SlowDBQuery ]); // The schedule id lives inside the job's settings JSON, the same way // AutomationController reads it. $needle = '%' . $wpdb->esc_like('"schedule_id":"' . $id . '"') . '%'; $failed = (int) $wpdb->get_var($wpdb->prepare( // phpcs:ignore WordPress.DB "SELECT COUNT(*) FROM {$jobs} WHERE status = 'failed' AND settings LIKE %s", $needle )); $schedules[$i]['posts_written'] = count($written); $schedules[$i]['posts_failed'] = $failed; $changed = true; } if ($changed) { update_option(self::OPTION, $schedules, false); } } }