diff --git a/includes/sources/class-cf7-source.php b/includes/sources/class-cf7-source.php index 79dbe8d..de5f079 100644 --- a/includes/sources/class-cf7-source.php +++ b/includes/sources/class-cf7-source.php @@ -35,6 +35,17 @@ final class CF7Source implements Interface_Spam_Source $reason = $this->detect_reason($submission, $fallback_reason); $classification = Classifier::classify($fields, ['reason' => $reason]); + $label = $classification['label']; + $decision = self::decide_drop_action($label); + + if ($decision['action'] === 'skip') { + Settings::increment_drop_counter($decision['counter_mode']); + return; + } + + if (!self::should_enqueue($label, $fields, $submission)) { + return; + } $post_id = FlamingoHelper::find_post_id_for_submission($submission); if ($post_id !== null && Queue::exists_for_flamingo($post_id, false)) { @@ -54,14 +65,19 @@ final class CF7Source implements Interface_Spam_Source 'ip' => $ip, 'user_agent' => $ua, 'is_retro' => false, + 'dry_run_drop' => $decision['action'] === 'enqueue_marked', ]); + if ($decision['action'] === 'enqueue_marked') { + Settings::increment_drop_counter($decision['counter_mode']); + } + Queue::enqueue([ 'flamingo_post_id' => $post_id, 'form_id' => $form_id, 'source_key' => 'cf7', 'payload_json' => wp_json_encode($payload), - 'label' => $classification['label'], + 'label' => $label, 'is_retro' => 0, ]); } @@ -91,4 +107,30 @@ final class CF7Source implements Interface_Spam_Source } return ''; } + + /** + * Decides what to do with a classified submission based on its label and the + * dry-run setting. Pure function — no side effects. + * + * @return array{action:'enqueue'|'enqueue_marked'|'skip',counter_mode:?string} + */ + public static function decide_drop_action(string $label): array + { + if ($label !== 'drop') { + return ['action' => 'enqueue', 'counter_mode' => null]; + } + if (Settings::is_dry_run_drop()) { + return ['action' => 'enqueue_marked', 'counter_mode' => 'dry_run']; + } + return ['action' => 'skip', 'counter_mode' => 'real']; + } + + /** + * Filterable gate — allows external code to block non-drop submissions + * via filter `cf7stg_should_enqueue`. + */ + public static function should_enqueue(string $label, array $fields, $submission): bool + { + return (bool)apply_filters('cf7stg_should_enqueue', true, $label, $fields, $submission); + } } diff --git a/tests/CF7SourceDropTest.php b/tests/CF7SourceDropTest.php new file mode 100644 index 0000000..38ad3d8 --- /dev/null +++ b/tests/CF7SourceDropTest.php @@ -0,0 +1,77 @@ + $label, 'fields' => $fields, 'submission' => $submission]; + return $should; + }, 10, 4); + + $sub = new \stdClass(); + $sub->marker = 'test-submission'; + CF7Source::should_enqueue('client', ['k' => 'v'], $sub); + + self::assertSame('client', $captured['label']); + self::assertSame(['k' => 'v'], $captured['fields']); + self::assertSame($sub, $captured['submission']); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 198ab9d..af84564 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -9,9 +9,19 @@ spl_autoload_register(static function (string $class): void { $relative = substr($class, strlen('Cf7stg\\')); $slug = strtolower(preg_replace('/([a-z0-9])([A-Z])/', '$1-$2', $relative)); $slug = str_replace('_', '-', $slug); - $path = __DIR__ . '/../includes/class-' . $slug . '.php'; - if (is_file($path)) { - require_once $path; + $candidates = [ + __DIR__ . '/../includes/class-' . $slug . '.php', + __DIR__ . '/../includes/sources/class-' . $slug . '.php', + __DIR__ . '/../admin/class-' . $slug . '.php', + __DIR__ . '/../includes/' . $slug . '.php', + __DIR__ . '/../includes/sources/' . $slug . '.php', + __DIR__ . '/../admin/' . $slug . '.php', + ]; + foreach ($candidates as $path) { + if (is_file($path)) { + require_once $path; + return; + } } });