From 42e311c05a1591de74fc9d93c48347e8bdb74cce Mon Sep 17 00:00:00 2001 From: Vladimir Bryzgalov Date: Tue, 21 Apr 2026 16:30:07 +0500 Subject: [PATCH] =?UTF-8?q?feat(source):=20drop=20label=20=E2=86=92=20skip?= =?UTF-8?q?=20enqueue=20(or=20marked=20enqueue=20in=20dry-run);=20cf7stg?= =?UTF-8?q?=5Fshould=5Fenqueue=20filter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also extends test bootstrap autoloader to resolve classes in includes/sources/ and admin/ subdirectories. --- includes/sources/class-cf7-source.php | 44 ++++++++++++++- tests/CF7SourceDropTest.php | 77 +++++++++++++++++++++++++++ tests/bootstrap.php | 16 ++++-- 3 files changed, 133 insertions(+), 4 deletions(-) create mode 100644 tests/CF7SourceDropTest.php 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; + } } });