prefix . 'cf7stg_queue'; $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE {$table} ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, flamingo_post_id BIGINT UNSIGNED NULL, form_id BIGINT UNSIGNED NULL, source_key VARCHAR(40) NOT NULL DEFAULT 'cf7', payload_json LONGTEXT NOT NULL, label VARCHAR(10) NOT NULL DEFAULT 'unclear', is_retro TINYINT(1) NOT NULL DEFAULT 0, status VARCHAR(20) NOT NULL DEFAULT 'pending', attempts SMALLINT UNSIGNED NOT NULL DEFAULT 0, last_error TEXT NULL, next_try_at DATETIME NULL, created_at DATETIME NOT NULL, sent_at DATETIME NULL, PRIMARY KEY (id), KEY idx_status_next (status, next_try_at), UNIQUE KEY uq_flamingo (flamingo_post_id, is_retro) ) {$charset_collate};"; require_once ABSPATH . 'wp-admin/includes/upgrade.php'; dbDelta($sql); update_option(self::DB_VERSION_OPTION, self::DB_VERSION); } private static function schedule_events(): void { // Register the custom 60-second schedule here — Dispatcher::register_hooks() // runs on plugins_loaded, which is AFTER activation. Without this filter // being in place during activate(), wp_schedule_event with an unknown // recurrence ('cf7stg_every_minute') silently fails and the event never // fires. add_filter('cron_schedules', ['\\Cf7stg\\Dispatcher', 'register_schedule']); if (!wp_next_scheduled('cf7stg_dispatch')) { wp_schedule_event(time() + 60, 'cf7stg_every_minute', 'cf7stg_dispatch'); } if (!wp_next_scheduled('cf7stg_cleanup')) { wp_schedule_event(time() + 3600, 'daily', 'cf7stg_cleanup'); } } }