feat(source): drop label → skip enqueue (or marked enqueue in dry-run); cf7stg_should_enqueue filter
Also extends test bootstrap autoloader to resolve classes in includes/sources/ and admin/ subdirectories.
This commit is contained in:
@@ -35,6 +35,17 @@ final class CF7Source implements Interface_Spam_Source
|
|||||||
|
|
||||||
$reason = $this->detect_reason($submission, $fallback_reason);
|
$reason = $this->detect_reason($submission, $fallback_reason);
|
||||||
$classification = Classifier::classify($fields, ['reason' => $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);
|
$post_id = FlamingoHelper::find_post_id_for_submission($submission);
|
||||||
if ($post_id !== null && Queue::exists_for_flamingo($post_id, false)) {
|
if ($post_id !== null && Queue::exists_for_flamingo($post_id, false)) {
|
||||||
@@ -54,14 +65,19 @@ final class CF7Source implements Interface_Spam_Source
|
|||||||
'ip' => $ip,
|
'ip' => $ip,
|
||||||
'user_agent' => $ua,
|
'user_agent' => $ua,
|
||||||
'is_retro' => false,
|
'is_retro' => false,
|
||||||
|
'dry_run_drop' => $decision['action'] === 'enqueue_marked',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if ($decision['action'] === 'enqueue_marked') {
|
||||||
|
Settings::increment_drop_counter($decision['counter_mode']);
|
||||||
|
}
|
||||||
|
|
||||||
Queue::enqueue([
|
Queue::enqueue([
|
||||||
'flamingo_post_id' => $post_id,
|
'flamingo_post_id' => $post_id,
|
||||||
'form_id' => $form_id,
|
'form_id' => $form_id,
|
||||||
'source_key' => 'cf7',
|
'source_key' => 'cf7',
|
||||||
'payload_json' => wp_json_encode($payload),
|
'payload_json' => wp_json_encode($payload),
|
||||||
'label' => $classification['label'],
|
'label' => $label,
|
||||||
'is_retro' => 0,
|
'is_retro' => 0,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -91,4 +107,30 @@ final class CF7Source implements Interface_Spam_Source
|
|||||||
}
|
}
|
||||||
return '';
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Cf7stg\Tests;
|
||||||
|
|
||||||
|
use Cf7stg\CF7Source;
|
||||||
|
use Cf7stg\Settings;
|
||||||
|
|
||||||
|
final class CF7SourceDropTest extends Cf7stgTestCase
|
||||||
|
{
|
||||||
|
public function test_drop_label_with_dry_run_off_returns_skip_decision(): void
|
||||||
|
{
|
||||||
|
Settings::set_dry_run_drop(false);
|
||||||
|
$decision = CF7Source::decide_drop_action('drop');
|
||||||
|
self::assertSame('skip', $decision['action']);
|
||||||
|
self::assertSame('real', $decision['counter_mode']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_drop_label_with_dry_run_on_returns_enqueue_with_marker(): void
|
||||||
|
{
|
||||||
|
Settings::set_dry_run_drop(true);
|
||||||
|
$decision = CF7Source::decide_drop_action('drop');
|
||||||
|
self::assertSame('enqueue_marked', $decision['action']);
|
||||||
|
self::assertSame('dry_run', $decision['counter_mode']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_non_drop_label_returns_normal_enqueue(): void
|
||||||
|
{
|
||||||
|
$decision = CF7Source::decide_drop_action('unclear');
|
||||||
|
self::assertSame('enqueue', $decision['action']);
|
||||||
|
self::assertNull($decision['counter_mode']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_non_drop_label_client_returns_normal_enqueue(): void
|
||||||
|
{
|
||||||
|
$decision = CF7Source::decide_drop_action('client');
|
||||||
|
self::assertSame('enqueue', $decision['action']);
|
||||||
|
self::assertNull($decision['counter_mode']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_non_drop_label_spam_returns_normal_enqueue(): void
|
||||||
|
{
|
||||||
|
$decision = CF7Source::decide_drop_action('spam');
|
||||||
|
self::assertSame('enqueue', $decision['action']);
|
||||||
|
self::assertNull($decision['counter_mode']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_filter_should_enqueue_can_block_unclear(): void
|
||||||
|
{
|
||||||
|
add_filter('cf7stg_should_enqueue', static function ($should, $label) {
|
||||||
|
return $label === 'unclear' ? false : $should;
|
||||||
|
}, 10, 2);
|
||||||
|
self::assertFalse(CF7Source::should_enqueue('unclear', [], null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_filter_should_enqueue_default_true(): void
|
||||||
|
{
|
||||||
|
self::assertTrue(CF7Source::should_enqueue('client', [], null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_filter_should_enqueue_passes_fields_and_submission(): void
|
||||||
|
{
|
||||||
|
$captured = [];
|
||||||
|
add_filter('cf7stg_should_enqueue', static function ($should, $label, $fields, $submission) use (&$captured) {
|
||||||
|
$captured = ['label' => $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']);
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
-1
@@ -9,9 +9,19 @@ spl_autoload_register(static function (string $class): void {
|
|||||||
$relative = substr($class, strlen('Cf7stg\\'));
|
$relative = substr($class, strlen('Cf7stg\\'));
|
||||||
$slug = strtolower(preg_replace('/([a-z0-9])([A-Z])/', '$1-$2', $relative));
|
$slug = strtolower(preg_replace('/([a-z0-9])([A-Z])/', '$1-$2', $relative));
|
||||||
$slug = str_replace('_', '-', $slug);
|
$slug = str_replace('_', '-', $slug);
|
||||||
$path = __DIR__ . '/../includes/class-' . $slug . '.php';
|
$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)) {
|
if (is_file($path)) {
|
||||||
require_once $path;
|
require_once $path;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user