Contact Form 7 remains one of the most widely used WordPress form plugins in the world, but its core weakness has always been simple: by default, it sends the form by email and does not operate as a complete lead recovery system.
For a hobby website, that may be acceptable. For a serious business website, it is not enough.
A single missed inquiry can mean a lost project, a lost client, a lost appointment, or a lost sale. Agencies, service companies, clinics, law firms, real estate websites, B2B companies, and high-ticket lead generation pages need more than a form that says “message sent.” They need proof that the lead was captured, preserved, traceable, recoverable, and visible inside the business workflow.
That is why LiMiT built Lead Black Box for Contact Form 7, a lightweight WordPress plugin that turns Contact Form 7 into a forensic lead capture and recovery system.
The Problem with Standard Contact Form 7 Workflows
Contact Form 7 is fast, flexible, and trusted by millions of WordPress websites. Its simplicity is the reason many developers still use it. But in its default form, Contact Form 7 depends heavily on the email delivery layer.
That creates a serious operational risk.
A form can be submitted successfully on the front end while the actual notification email is delayed, rejected, filtered into spam, blocked by the hosting server, misconfigured, or lost somewhere between WordPress and the mailbox.
For the visitor, the form may look successful. For the business, the lead may never arrive.
This is not a design issue. It is a business continuity issue.
Common causes include:
- Broken WordPress mail configuration.
- Hosting servers that block or rate-limit PHP mail.
- Missing SMTP authentication.
- Incorrect recipient settings inside Contact Form 7.
- DNS issues with SPF, DKIM, or DMARC.
- Spam filters rejecting legitimate leads.
- Webhook or CRM integrations failing silently.
- No internal system for checking whether a lead was actually handled.
Most businesses discover the problem too late. They notice fewer inquiries, assume marketing is underperforming, and only later realize that Contact Form 7 was receiving submissions that never reached the correct inbox.
What Lead Black Box for Contact Form 7 Does
Lead Black Box for Contact Form 7 captures every valid Contact Form 7 submission before the mail delivery layer becomes the single point of failure.
Instead of treating the email as the only record, the plugin creates a local forensic record inside WordPress. Each submission receives a unique ID and a complete lead timeline.
The plugin stores:
- Unique submission ID.
- Form ID and form title.
- Submission date and time.
- Page URL.
- Referrer.
- UTM parameters.
- Click IDs such as gclid, fbclid, msclkid, gbraid, and wbraid.
- Privacy-safe IP hash.
- User agent.
- Submitted fields.
- Uploaded file names.
- Mail status.
- Spam status.
- Lead workflow status.
- Internal notes.
- Forensic event timeline.
This turns a basic form submission into a measurable business record.
A Contact Form 7 Black Box Recorder
The best way to understand the plugin is to think of it as a black box recorder for Contact Form 7.
When a lead comes in, the plugin captures the event before the most fragile parts of the system can lose it. Even if the outgoing email fails, the lead still exists inside WordPress, the mailbox filters the message, the submission can still be recovered or the client says they never received the inquiry, the agency can open the dashboard and show the captured record.
This is especially important for websites where inquiries have real financial value.
Examples:
- A legal website receiving case inquiries.
- A clinic receiving treatment requests.
- A real estate website receiving property leads.
- A web design agency receiving project requests.
- A B2B service provider receiving quote requests.
- An eCommerce store receiving wholesale requests.
- A local business receiving appointment requests.
In these cases, form reliability is not a small technical detail. It is part of the revenue infrastructure.
How the Plugin Works
Lead Black Box hooks into the Contact Form 7 submission process and captures the lead before the final email delivery result becomes the only record.
After capture, the plugin tracks the Contact Form 7 mail result and listens for WordPress mail failures. It then writes events into a simple forensic timeline.
The workflow looks like this:
- A visitor submits a Contact Form 7 form.
- Lead Black Box captures the submission locally.
- The plugin stores the form fields, tracking data, page URL, referrer, and system context.
- Contact Form 7 attempts to send the email notification.
- Lead Black Box records whether the mail event succeeded or failed.
- If mail fails, the plugin can alert the configured recovery email.
- The lead remains available inside WordPress for review, resend, export, or workflow management.
The result is simple: even when mail delivery fails, the business still has the lead.
Main Features
Local Submission Capture
The plugin stores form submissions locally inside custom WordPress database tables. It does not send lead data to LiMiT or any external service.
Mail Failure Detection
Lead Black Box tracks Contact Form 7 mail results and also listens for WordPress mail failure events. If a failure is detected, the lead is marked clearly in the dashboard.
Recovery Email Resend
Every captured lead can be resent manually to the configured recovery email. This is useful when the original notification was missed, deleted, blocked, or filtered.
Forensic Timeline
Each lead includes an event timeline showing capture, mail status, resend actions, spam status, and lead workflow updates.
UTM and Click ID Tracking
The plugin can capture campaign data such as utm_source, utm_medium, utm_campaign, utm_term, utm_content, gclid, fbclid, msclkid, gbraid, and wbraid.
Lead Workflow Status
Admins can mark leads as new, contacted, converted, or spam. This gives agencies and site owners a simple lead management layer without replacing their CRM.
Internal Notes
Every submission can include internal notes for follow-up status, qualification notes, client feedback, or sales context.
CSV and JSON Export
The plugin supports CSV export for recent leads and JSON export for individual submissions, including the forensic timeline.
Privacy-Safe IP Hashing
Instead of storing raw IP addresses, the plugin can store a hashed version for operational context without keeping the raw value.
Retention Settings
Admins can define how long submissions should remain stored. This helps businesses balance lead recovery with data minimization.
Sensitive Field Redaction
The plugin includes a configurable list of field names that should be redacted before storage, such as passwords, card numbers, CVV fields, tokens, secrets, and API keys.
Download the Plugin
You can download the official plugin below.
Download Lead Black Box for Contact Form 7 v1.0.0
File name: lead-black-box-for-contact-form-7-1.0.0.zip
Version: 1.0.0
Requires: WordPress 5.8+, PHP 7.4+, and Contact Form 7 installed and active.
How to Install
- Download the ZIP file.
- Go to your WordPress dashboard.
- Open Plugins > Add New > Upload Plugin.
- Select the ZIP file and click Install Now.
- Activate the plugin.
- Make sure Contact Form 7 is active.
- Go to CF7 Lead Black Box > Settings.
- Set your recovery email and retention period.
- Submit a test form.
- Open CF7 Lead Black Box > Submissions to verify the captured lead.
Plugin Code
The plugin is delivered as a clean WordPress plugin with three files:
- lead-black-box-for-contact-form-7.php
- readme.txt
- uninstall.php
<?php
/**
* Plugin Name: Lead Black Box for Contact Form 7 by LiMiT
* Plugin URI: https://www.limit.agency/
* Description: A Contact Form 7 lead audit, recovery, tracking, resend, export, and delivery monitoring plugin. Built for agencies that cannot afford lost leads.
* Version: 1.0.0
* Author: LiMiT
* Author URI: https://www.limit.agency/
* License: GPLv2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: lead-black-box-for-contact-form-7
* Requires at least: 5.8
* Requires PHP: 7.4
*/
if (!defined('ABSPATH')) {
exit;
}
final class LBB_CF7_Plugin {
const VERSION = '1.0.0';
const SLUG = 'lead-black-box-for-contact-form-7';
const OPTION = 'lbbcf7_options';
const DB_VERSION = '1.0.0';
const DB_OPTION = 'lbbcf7_db_version';
private static $instance = null;
private static $current_submission_id = 0;
private static $current_uuid = '';
private static $suppress_mail_failure = false;
public static function instance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
add_action('plugins_loaded', array($this, 'maybe_upgrade'));
add_action('admin_init', array($this, 'register_settings'));
add_action('admin_menu', array($this, 'admin_menu'));
add_action('admin_notices', array($this, 'dependency_notice'));
add_filter('plugin_action_links_' . plugin_basename(__FILE__), array($this, 'plugin_action_links'));
add_action('wp_enqueue_scripts', array($this, 'enqueue_tracking_script'));
add_action('wpcf7_before_send_mail', array($this, 'capture_before_send'), 9, 3);
add_action('wpcf7_mail_sent', array($this, 'mark_mail_sent'), 10, 1);
add_action('wpcf7_mail_failed', array($this, 'mark_mail_failed'), 10, 1);
add_action('wpcf7_spam', array($this, 'mark_spam'), 10, 1);
add_action('wp_mail_failed', array($this, 'mark_wp_mail_failed'), 10, 1);
add_action('admin_post_lbbcf7_resend', array($this, 'handle_resend'));
add_action('admin_post_lbbcf7_export_json', array($this, 'handle_export_json'));
add_action('admin_post_lbbcf7_export_csv', array($this, 'handle_export_csv'));
add_action('admin_post_lbbcf7_delete', array($this, 'handle_delete'));
add_action('admin_post_lbbcf7_update_status', array($this, 'handle_update_status'));
add_action('admin_post_lbbcf7_run_retention', array($this, 'handle_run_retention'));
add_action('lbbcf7_daily_retention', array($this, 'run_retention'));
}
public static function activate() {
self::create_tables();
$options = wp_parse_args(get_option(self::OPTION, array()), self::default_options());
if (empty($options['hash_salt'])) {
$options['hash_salt'] = wp_generate_password(64, true, true);
}
update_option(self::OPTION, $options, false);
update_option(self::DB_OPTION, self::DB_VERSION, false);
if (!wp_next_scheduled('lbbcf7_daily_retention')) {
wp_schedule_event(time() + HOUR_IN_SECONDS, 'daily', 'lbbcf7_daily_retention');
}
}
public static function deactivate() {
$timestamp = wp_next_scheduled('lbbcf7_daily_retention');
if ($timestamp) {
wp_unschedule_event($timestamp, 'lbbcf7_daily_retention');
}
}
public function maybe_upgrade() {
$db_version = get_option(self::DB_OPTION, '');
if ($db_version !== self::DB_VERSION) {
self::create_tables();
update_option(self::DB_OPTION, self::DB_VERSION, false);
}
}
public static function default_options() {
return array(
'recovery_email' => get_option('admin_email'),
'capture_tracking' => 1,
'capture_ip_hash' => 1,
'capture_user_agent' => 1,
'capture_files' => 1,
'store_spam' => 1,
'alert_on_mail_failure' => 1,
'retention_days' => 365,
'redacted_fields' => 'password,pass,passwd,credit_card,cardnumber,card_number,cc,cvv,cvc,token,secret,api_key,apikey,private_key',
'delete_data_on_uninstall' => 0,
'hash_salt' => '',
);
}
public static function options() {
$options = wp_parse_args(get_option(self::OPTION, array()), self::default_options());
if (empty($options['hash_salt'])) {
$options['hash_salt'] = wp_generate_password(64, true, true);
update_option(self::OPTION, $options, false);
}
return $options;
}
public static function table_submissions() {
global $wpdb;
return $wpdb->prefix . 'lbbcf7_submissions';
}
public static function table_events() {
global $wpdb;
return $wpdb->prefix . 'lbbcf7_events';
}
public static function create_tables() {
global $wpdb;
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
$charset_collate = $wpdb->get_charset_collate();
$submissions = self::table_submissions();
$events = self::table_events();
$sql1 = "CREATE TABLE {$submissions} (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
uuid varchar(64) NOT NULL,
form_id bigint(20) unsigned NOT NULL DEFAULT 0,
form_title varchar(255) NOT NULL DEFAULT '',
container_post_id bigint(20) unsigned NOT NULL DEFAULT 0,
page_url text NULL,
referrer text NULL,
ip_hash varchar(128) NOT NULL DEFAULT '',
user_agent text NULL,
status varchar(32) NOT NULL DEFAULT 'captured',
mail_status varchar(32) NOT NULL DEFAULT 'pending',
spam_status varchar(32) NOT NULL DEFAULT 'unknown',
lead_status varchar(32) NOT NULL DEFAULT 'new',
fields_json longtext NULL,
tracking_json longtext NULL,
files_json longtext NULL,
meta_json longtext NULL,
notes longtext NULL,
created_at datetime NOT NULL,
updated_at datetime NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY uuid (uuid),
KEY form_id (form_id),
KEY status (status),
KEY mail_status (mail_status),
KEY spam_status (spam_status),
KEY lead_status (lead_status),
KEY created_at (created_at)
) {$charset_collate};";
$sql2 = "CREATE TABLE {$events} (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
submission_id bigint(20) unsigned NOT NULL DEFAULT 0,
event_type varchar(64) NOT NULL DEFAULT '',
event_status varchar(32) NOT NULL DEFAULT '',
message text NULL,
data_json longtext NULL,
created_at datetime NOT NULL,
PRIMARY KEY (id),
KEY submission_id (submission_id),
KEY event_type (event_type),
KEY created_at (created_at)
) {$charset_collate};";
dbDelta($sql1);
dbDelta($sql2);
}
public function dependency_notice() {
if (!current_user_can('activate_plugins')) {
return;
}
if (!class_exists('WPCF7')) {
echo '<div class="notice notice-warning"><p><strong>Lead Black Box for Contact Form 7:</strong> Contact Form 7 is not active. The plugin is installed, but lead capture starts only after Contact Form 7 is active.</p></div>';
}
}
public function plugin_action_links($links) {
$settings = '<a href="' . esc_url(admin_url('admin.php?page=lbbcf7-settings')) . '">Settings</a>';
$dashboard = '<a href="' . esc_url(admin_url('admin.php?page=lbbcf7')) . '">Lead Black Box</a>';
array_unshift($links, $settings, $dashboard);
return $links;
}
public function register_settings() {
register_setting(self::OPTION, self::OPTION, array($this, 'sanitize_options'));
}
public function sanitize_options($input) {
$defaults = self::default_options();
$old = self::options();
$input = is_array($input) ? $input : array();
$out = wp_parse_args($input, $old);
$out['recovery_email'] = sanitize_email($out['recovery_email']);
if (empty($out['recovery_email'])) {
$out['recovery_email'] = $defaults['recovery_email'];
}
$out['capture_tracking'] = !empty($out['capture_tracking']) ? 1 : 0;
$out['capture_ip_hash'] = !empty($out['capture_ip_hash']) ? 1 : 0;
$out['capture_user_agent'] = !empty($out['capture_user_agent']) ? 1 : 0;
$out['capture_files'] = !empty($out['capture_files']) ? 1 : 0;
$out['store_spam'] = !empty($out['store_spam']) ? 1 : 0;
$out['alert_on_mail_failure'] = !empty($out['alert_on_mail_failure']) ? 1 : 0;
$out['delete_data_on_uninstall'] = !empty($out['delete_data_on_uninstall']) ? 1 : 0;
$out['retention_days'] = max(1, intval($out['retention_days']));
$out['redacted_fields'] = sanitize_text_field($out['redacted_fields']);
$out['hash_salt'] = !empty($old['hash_salt']) ? $old['hash_salt'] : wp_generate_password(64, true, true);
return $out;
}
public function admin_menu() {
add_menu_page(
'CF7 Lead Black Box',
'CF7 Lead Black Box',
'manage_options',
'lbbcf7',
array($this, 'render_dashboard'),
'dashicons-clipboard',
56
);
add_submenu_page('lbbcf7', 'Submissions', 'Submissions', 'manage_options', 'lbbcf7-submissions', array($this, 'render_submissions'));
add_submenu_page('lbbcf7', 'Settings', 'Settings', 'manage_options', 'lbbcf7-settings', array($this, 'render_settings'));
}
public function enqueue_tracking_script() {
$options = self::options();
if (empty($options['capture_tracking'])) {
return;
}
wp_register_script('lbbcf7-tracking', '', array(), self::VERSION, true);
wp_enqueue_script('lbbcf7-tracking');
$js = "(function(){\n" .
"var keys=['utm_source','utm_medium','utm_campaign','utm_term','utm_content','gclid','gbraid','wbraid','fbclid','msclkid'];\n" .
"function safeSet(k,v){try{if(v){localStorage.setItem('lbbcf7_'+k,v);}}catch(e){}}\n" .
"function safeGet(k){try{return localStorage.getItem('lbbcf7_'+k)||'';}catch(e){return '';}}\n" .
"var p=new URLSearchParams(window.location.search);\n" .
"keys.forEach(function(k){var v=p.get(k); if(v){safeSet(k,v);}});\n" .
"safeSet('landing_page', safeGet('landing_page') || window.location.href);\n" .
"document.addEventListener('DOMContentLoaded',function(){\n" .
"var forms=document.querySelectorAll('.wpcf7 form, form.wpcf7-form');\n" .
"forms.forEach(function(form){\n" .
"var data={current_url:window.location.href, referrer:document.referrer||'', landing_page:safeGet('landing_page')};\n" .
"keys.forEach(function(k){data[k]=safeGet(k);});\n" .
"Object.keys(data).forEach(function(k){if(!data[k])return; var name='lbbcf7_'+k; var input=form.querySelector('input[name="+JSON.stringify('')+"'+name+'"+JSON.stringify('')+"]'); if(!input){input=document.createElement('input'); input.type='hidden'; input.name=name; form.appendChild(input);} input.value=data[k];});\n" .
"});\n" .
"});\n" .
"})();";
$js = str_replace("form.querySelector('input[name=\"+JSON.stringify('')+\"'+name+'\"+JSON.stringify('')+\"]')", "form.querySelector('input[name=\"'+name+'\"]')", $js);
wp_add_inline_script('lbbcf7-tracking', $js);
}
public function capture_before_send($contact_form, &$abort = null, $submission = null) {
if (!class_exists('WPCF7_Submission')) {
return;
}
if (!$submission || !is_object($submission)) {
$submission = WPCF7_Submission::get_instance();
}
if (!$submission) {
return;
}
$options = self::options();
$posted = method_exists($submission, 'get_posted_data') ? (array) $submission->get_posted_data() : array();
$posted = $this->normalize_array($posted);
$tracking = $this->extract_tracking($posted);
$fields = $this->redact_fields($posted, $options['redacted_fields']);
$files = array();
if (!empty($options['capture_files']) && method_exists($submission, 'uploaded_files')) {
$files = $this->normalize_files((array) $submission->uploaded_files());
}
$form_id = is_object($contact_form) && method_exists($contact_form, 'id') ? intval($contact_form->id()) : 0;
$form_title = is_object($contact_form) && method_exists($contact_form, 'title') ? sanitize_text_field($contact_form->title()) : '';
$container_post_id = isset($posted['_wpcf7_container_post']) ? intval($posted['_wpcf7_container_post']) : 0;
$page_url = $this->detect_page_url($posted, $container_post_id);
$referrer = $this->detect_referrer($posted);
$ip_hash = !empty($options['capture_ip_hash']) ? $this->ip_hash() : '';
$user_agent = !empty($options['capture_user_agent']) && isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_textarea_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : '';
$uuid = $this->uuid();
$now = current_time('mysql');
global $wpdb;
$table = self::table_submissions();
$inserted = $wpdb->insert(
$table,
array(
'uuid' => $uuid,
'form_id' => $form_id,
'form_title' => $form_title,
'container_post_id' => $container_post_id,
'page_url' => esc_url_raw($page_url),
'referrer' => esc_url_raw($referrer),
'ip_hash' => $ip_hash,
'user_agent' => $user_agent,
'status' => 'captured',
'mail_status' => 'pending',
'spam_status' => 'unknown',
'lead_status' => 'new',
'fields_json' => wp_json_encode($fields),
'tracking_json' => wp_json_encode($tracking),
'files_json' => wp_json_encode($files),
'meta_json' => wp_json_encode($this->request_meta()),
'created_at' => $now,
'updated_at' => $now,
),
array('%s','%d','%s','%d','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')
);
if ($inserted) {
self::$current_submission_id = intval($wpdb->insert_id);
self::$current_uuid = $uuid;
$this->add_event(self::$current_submission_id, 'capture', 'ok', 'Submission captured before mail delivery.', array('uuid' => $uuid));
}
}
public function mark_mail_sent($contact_form) {
if (!self::$current_submission_id) {
return;
}
$this->update_submission(self::$current_submission_id, array('status' => 'delivered', 'mail_status' => 'sent'));
$this->add_event(self::$current_submission_id, 'mail', 'sent', 'Contact Form 7 reported successful mail delivery.', array());
}
public function mark_mail_failed($contact_form) {
if (!self::$current_submission_id) {
return;
}
$this->update_submission(self::$current_submission_id, array('status' => 'mail_failed', 'mail_status' => 'failed'));
$this->add_event(self::$current_submission_id, 'mail', 'failed', 'Contact Form 7 reported mail failure.', array());
$this->send_failure_alert(self::$current_submission_id, 'Contact Form 7 reported mail failure.');
}
public function mark_spam($contact_form) {
$options = self::options();
if (empty($options['store_spam'])) {
return;
}
if (!self::$current_submission_id && class_exists('WPCF7_Submission')) {
$submission = WPCF7_Submission::get_instance();
$abort = null;
$this->capture_before_send($contact_form, $abort, $submission);
}
if (!self::$current_submission_id) {
return;
}
$this->update_submission(self::$current_submission_id, array('status' => 'spam', 'spam_status' => 'spam'));
$this->add_event(self::$current_submission_id, 'spam', 'detected', 'Contact Form 7 marked the submission as spam.', array());
}
public function mark_wp_mail_failed($wp_error) {
if (self::$suppress_mail_failure) {
return;
}
if (!self::$current_submission_id) {
return;
}
$message = 'WordPress wp_mail_failed fired during this submission.';
if (is_wp_error($wp_error)) {
$message = $wp_error->get_error_message();
}
$this->update_submission(self::$current_submission_id, array('status' => 'wp_mail_failed', 'mail_status' => 'failed'));
$this->add_event(self::$current_submission_id, 'wp_mail_failed', 'failed', $message, array());
$this->send_failure_alert(self::$current_submission_id, $message);
}
private function update_submission($id, $data) {
global $wpdb;
$data['updated_at'] = current_time('mysql');
$formats = array();
foreach ($data as $key => $value) {
$formats[] = is_numeric($value) && $key !== 'notes' ? '%d' : '%s';
}
$wpdb->update(self::table_submissions(), $data, array('id' => intval($id)), $formats, array('%d'));
}
private function add_event($submission_id, $type, $status, $message, $data = array()) {
global $wpdb;
$wpdb->insert(
self::table_events(),
array(
'submission_id' => intval($submission_id),
'event_type' => sanitize_key($type),
'event_status' => sanitize_key($status),
'message' => sanitize_textarea_field($message),
'data_json' => wp_json_encode($data),
'created_at' => current_time('mysql'),
),
array('%d','%s','%s','%s','%s','%s')
);
}
private function send_failure_alert($submission_id, $reason) {
$options = self::options();
if (empty($options['alert_on_mail_failure'])) {
return;
}
$email = sanitize_email($options['recovery_email']);
if (empty($email)) {
return;
}
$submission = $this->get_submission($submission_id);
if (!$submission) {
return;
}
$subject = '[CF7 Lead Black Box] Mail failure captured for ' . $submission->form_title;
$body = $this->format_submission_email($submission, $reason);
self::$suppress_mail_failure = true;
wp_mail($email, $subject, $body);
self::$suppress_mail_failure = false;
}
private function get_submission($id) {
global $wpdb;
return $wpdb->get_row($wpdb->prepare('SELECT * FROM ' . self::table_submissions() . ' WHERE id = %d', intval($id)));
}
private function get_events($submission_id) {
global $wpdb;
return $wpdb->get_results($wpdb->prepare('SELECT * FROM ' . self::table_events() . ' WHERE submission_id = %d ORDER BY created_at ASC, id ASC', intval($submission_id)));
}
private function format_submission_email($submission, $intro = '') {
$fields = json_decode($submission->fields_json, true);
$tracking = json_decode($submission->tracking_json, true);
$files = json_decode($submission->files_json, true);
$lines = array();
$lines[] = 'Lead Black Box for Contact Form 7';
$lines[] = 'Submission ID: ' . $submission->uuid;
$lines[] = 'Form: ' . $submission->form_title . ' (#' . $submission->form_id . ')';
$lines[] = 'Created: ' . $submission->created_at;
$lines[] = 'Status: ' . $submission->status . ' / Mail: ' . $submission->mail_status . ' / Spam: ' . $submission->spam_status;
if ($intro) {
$lines[] = '';
$lines[] = 'Reason: ' . $intro;
}
$lines[] = '';
$lines[] = 'Page URL: ' . $submission->page_url;
$lines[] = 'Referrer: ' . $submission->referrer;
$lines[] = '';
$lines[] = 'Fields:';
if (is_array($fields)) {
foreach ($fields as $key => $value) {
if (strpos((string) $key, '_wpcf7') === 0) {
continue;
}
$lines[] = $key . ': ' . $this->value_to_text($value);
}
}
if (is_array($tracking) && !empty($tracking)) {
$lines[] = '';
$lines[] = 'Tracking:';
foreach ($tracking as $key => $value) {
$lines[] = $key . ': ' . $this->value_to_text($value);
}
}
if (is_array($files) && !empty($files)) {
$lines[] = '';
$lines[] = 'Files:';
foreach ($files as $key => $value) {
$lines[] = $key . ': ' . $this->value_to_text($value);
}
}
$lines[] = '';
$lines[] = 'Open WordPress admin to view the forensic timeline.';
return implode("\n", $lines);
}
private function value_to_text($value) {
if (is_array($value)) {
return implode(', ', array_map(array($this, 'value_to_text'), $value));
}
return sanitize_textarea_field((string) $value);
}
private function normalize_array($array) {
$out = array();
foreach ((array) $array as $key => $value) {
$key = sanitize_key((string) $key);
if (is_array($value)) {
$out[$key] = $this->normalize_array($value);
} else {
$out[$key] = sanitize_textarea_field(wp_unslash((string) $value));
}
}
return $out;
}
private function normalize_files($files) {
$out = array();
foreach ((array) $files as $key => $value) {
$key = sanitize_key((string) $key);
if (is_array($value)) {
$out[$key] = $this->normalize_files($value);
} else {
$out[$key] = basename(sanitize_text_field((string) $value));
}
}
return $out;
}
private function redact_fields($fields, $redacted_fields) {
$redacted = array_filter(array_map('trim', explode(',', strtolower((string) $redacted_fields))));
foreach ($fields as $key => $value) {
$lk = strtolower((string) $key);
foreach ($redacted as $needle) {
if ($needle !== '' && strpos($lk, $needle) !== false) {
$fields[$key] = '[redacted]';
continue 2;
}
}
}
return $fields;
}
private function extract_tracking($posted) {
$tracking = array();
$keys = array('utm_source','utm_medium','utm_campaign','utm_term','utm_content','gclid','gbraid','wbraid','fbclid','msclkid','landing_page','current_url','referrer');
foreach ($keys as $key) {
$field_key = 'lbbcf7_' . $key;
if (isset($posted[$field_key]) && $posted[$field_key] !== '') {
$tracking[$key] = $posted[$field_key];
}
if (isset($posted[$key]) && $posted[$key] !== '') {
$tracking[$key] = $posted[$key];
}
}
return $tracking;
}
private function detect_page_url($posted, $container_post_id) {
if (!empty($posted['lbbcf7_current_url'])) {
return $posted['lbbcf7_current_url'];
}
if ($container_post_id) {
$permalink = get_permalink($container_post_id);
if ($permalink) {
return $permalink;
}
}
if (!empty($_SERVER['HTTP_REFERER'])) {
return esc_url_raw(wp_unslash($_SERVER['HTTP_REFERER']));
}
return home_url('/');
}
private function detect_referrer($posted) {
if (!empty($posted['lbbcf7_referrer'])) {
return $posted['lbbcf7_referrer'];
}
if (!empty($_SERVER['HTTP_REFERER'])) {
return esc_url_raw(wp_unslash($_SERVER['HTTP_REFERER']));
}
return '';
}
private function request_meta() {
$meta = array(
'remote_port' => isset($_SERVER['REMOTE_PORT']) ? intval($_SERVER['REMOTE_PORT']) : 0,
'request_time' => isset($_SERVER['REQUEST_TIME']) ? intval($_SERVER['REQUEST_TIME']) : time(),
'https' => (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 1 : 0,
);
return $meta;
}
private function ip_hash() {
$options = self::options();
$ip = '';
if (!empty($_SERVER['HTTP_CF_CONNECTING_IP'])) {
$ip = sanitize_text_field(wp_unslash($_SERVER['HTTP_CF_CONNECTING_IP']));
} elseif (!empty($_SERVER['REMOTE_ADDR'])) {
$ip = sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR']));
}
if (!$ip) {
return '';
}
return hash_hmac('sha256', $ip, $options['hash_salt']);
}
private function uuid() {
if (function_exists('wp_generate_uuid4')) {
return wp_generate_uuid4();
}
return md5(uniqid('', true) . wp_rand());
}
public function render_dashboard() {
if (!current_user_can('manage_options')) {
return;
}
global $wpdb;
$table = self::table_submissions();
$total = intval($wpdb->get_var("SELECT COUNT(*) FROM {$table}"));
$failed = intval($wpdb->get_var("SELECT COUNT(*) FROM {$table} WHERE mail_status = 'failed'"));
$sent = intval($wpdb->get_var("SELECT COUNT(*) FROM {$table} WHERE mail_status = 'sent'"));
$spam = intval($wpdb->get_var("SELECT COUNT(*) FROM {$table} WHERE spam_status = 'spam'"));
$new = intval($wpdb->get_var("SELECT COUNT(*) FROM {$table} WHERE lead_status = 'new'"));
$latest = $wpdb->get_results("SELECT * FROM {$table} ORDER BY created_at DESC LIMIT 10");
echo '<div class="wrap lbbcf7-wrap">';
echo '<h1>CF7 Lead Black Box</h1>';
echo '<p>A lead audit and recovery layer for Contact Form 7. Built by <a href="https://www.limit.agency/" target="_blank" rel="noopener">LiMiT Agency</a>.</p>';
$this->render_stat_cards(array(
'Total captured' => $total,
'Mail sent' => $sent,
'Mail failed' => $failed,
'Spam captured' => $spam,
'New leads' => $new,
));
echo '<h2>Latest submissions</h2>';
$this->render_submission_table($latest, false);
echo '<p><a class="button button-primary" href="' . esc_url(admin_url('admin.php?page=lbbcf7-submissions')) . '">View all submissions</a> <a class="button" href="' . esc_url(admin_url('admin.php?page=lbbcf7-settings')) . '">Settings</a></p>';
echo '</div>';
}
private function render_stat_cards($stats) {
echo '<style>.lbbcf7-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:14px;margin:18px 0}.lbbcf7-card{background:#fff;border:1px solid #dcdcde;border-radius:8px;padding:16px}.lbbcf7-card strong{display:block;font-size:24px;line-height:1.2}.lbbcf7-table td,.lbbcf7-table th{vertical-align:top}.lbbcf7-badge{display:inline-block;border-radius:999px;padding:3px 8px;background:#f0f0f1;font-size:12px}.lbbcf7-badge.failed{background:#fce8e6;color:#8a1f11}.lbbcf7-badge.sent{background:#e7f7ed;color:#165c2f}.lbbcf7-badge.spam{background:#fff4ce;color:#6d4b00}</style>';
echo '<div class="lbbcf7-cards">';
foreach ($stats as $label => $value) {
echo '<div class="lbbcf7-card"><strong>' . esc_html(number_format_i18n($value)) . '</strong><span>' . esc_html($label) . '</span></div>';
}
echo '</div>';
}
public function render_submissions() {
if (!current_user_can('manage_options')) {
return;
}
$action = isset($_GET['action']) ? sanitize_key($_GET['action']) : '';
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
if ($action === 'view' && $id) {
$this->render_single_submission($id);
return;
}
global $wpdb;
$table = self::table_submissions();
$where = 'WHERE 1=1';
$params = array();
$mail_status = isset($_GET['mail_status']) ? sanitize_key($_GET['mail_status']) : '';
$lead_status = isset($_GET['lead_status']) ? sanitize_key($_GET['lead_status']) : '';
$search = isset($_GET['s']) ? sanitize_text_field(wp_unslash($_GET['s'])) : '';
if ($mail_status) {
$where .= ' AND mail_status = %s';
$params[] = $mail_status;
}
if ($lead_status) {
$where .= ' AND lead_status = %s';
$params[] = $lead_status;
}
if ($search) {
$where .= ' AND (uuid LIKE %s OR form_title LIKE %s OR fields_json LIKE %s OR tracking_json LIKE %s)';
$like = '%' . $wpdb->esc_like($search) . '%';
$params[] = $like;
$params[] = $like;
$params[] = $like;
$params[] = $like;
}
$per_page = 25;
$paged = max(1, isset($_GET['paged']) ? intval($_GET['paged']) : 1);
$offset = ($paged - 1) * $per_page;
$count_sql = "SELECT COUNT(*) FROM {$table} {$where}";
$data_sql = "SELECT * FROM {$table} {$where} ORDER BY created_at DESC LIMIT %d OFFSET %d";
$count_params = $params;
$data_params = array_merge($params, array($per_page, $offset));
$total = !empty($count_params) ? intval($wpdb->get_var($wpdb->prepare($count_sql, $count_params))) : intval($wpdb->get_var($count_sql));
$rows = !empty($data_params) ? $wpdb->get_results($wpdb->prepare($data_sql, $data_params)) : array();
$export_url = wp_nonce_url(admin_url('admin-post.php?action=lbbcf7_export_csv&' . http_build_query(array('mail_status' => $mail_status, 'lead_status' => $lead_status, 's' => $search))), 'lbbcf7_export_csv');
echo '<div class="wrap lbbcf7-wrap"><h1>CF7 Lead Black Box Submissions</h1>';
echo '<form method="get" style="margin:16px 0;display:flex;gap:8px;align-items:center;flex-wrap:wrap">';
echo '<input type="hidden" name="page" value="lbbcf7-submissions">';
echo '<input type="search" name="s" value="' . esc_attr($search) . '" placeholder="Search leads">';
echo '<select name="mail_status"><option value="">All mail statuses</option><option value="sent" ' . selected($mail_status, 'sent', false) . '>Sent</option><option value="failed" ' . selected($mail_status, 'failed', false) . '>Failed</option><option value="pending" ' . selected($mail_status, 'pending', false) . '>Pending</option></select>';
echo '<select name="lead_status"><option value="">All lead statuses</option><option value="new" ' . selected($lead_status, 'new', false) . '>New</option><option value="contacted" ' . selected($lead_status, 'contacted', false) . '>Contacted</option><option value="converted" ' . selected($lead_status, 'converted', false) . '>Converted</option><option value="spam" ' . selected($lead_status, 'spam', false) . '>Spam</option></select>';
submit_button('Filter', 'secondary', '', false);
echo '<a class="button" href="' . esc_url($export_url) . '">Export CSV</a>';
echo '</form>';
$this->render_submission_table($rows, true);
$pages = max(1, ceil($total / $per_page));
if ($pages > 1) {
echo '<div class="tablenav"><div class="tablenav-pages">';
echo paginate_links(array(
'base' => add_query_arg('paged', '%#%'),
'format' => '',
'prev_text' => '«',
'next_text' => '»',
'total' => $pages,
'current' => $paged,
));
echo '</div></div>';
}
echo '</div>';
}
private function render_submission_table($rows, $actions) {
echo '<table class="widefat striped lbbcf7-table"><thead><tr><th>ID</th><th>Date</th><th>Form</th><th>Status</th><th>Lead</th><th>Page</th><th>Preview</th><th>Actions</th></tr></thead><tbody>';
if (empty($rows)) {
echo '<tr><td colspan="8">No submissions found.</td></tr>';
}
foreach ($rows as $row) {
$fields = json_decode($row->fields_json, true);
$preview = '';
if (is_array($fields)) {
foreach ($fields as $key => $value) {
if (strpos((string) $key, '_wpcf7') === 0 || strpos((string) $key, 'lbbcf7_') === 0) {
continue;
}
$preview .= $key . ': ' . $this->value_to_text($value) . ' | ';
if (strlen($preview) > 180) {
break;
}
}
}
$view_url = admin_url('admin.php?page=lbbcf7-submissions&action=view&id=' . intval($row->id));
$mail_class = $row->mail_status === 'failed' ? 'failed' : ($row->mail_status === 'sent' ? 'sent' : '');
$spam_class = $row->spam_status === 'spam' ? 'spam' : '';
echo '<tr>';
echo '<td><code>' . esc_html($row->uuid) . '</code></td>';
echo '<td>' . esc_html($row->created_at) . '</td>';
echo '<td>' . esc_html($row->form_title) . '<br><small>#' . esc_html($row->form_id) . '</small></td>';
echo '<td><span class="lbbcf7-badge ' . esc_attr($mail_class) . '">mail: ' . esc_html($row->mail_status) . '</span><br><span class="lbbcf7-badge ' . esc_attr($spam_class) . '">spam: ' . esc_html($row->spam_status) . '</span></td>';
echo '<td>' . esc_html($row->lead_status) . '</td>';
echo '<td><a href="' . esc_url($row->page_url) . '" target="_blank" rel="noopener">open</a></td>';
echo '<td>' . esc_html(wp_trim_words($preview, 24)) . '</td>';
echo '<td><a class="button button-small" href="' . esc_url($view_url) . '">View</a></td>';
echo '</tr>';
}
echo '</tbody></table>';
}
private function render_single_submission($id) {
$row = $this->get_submission($id);
if (!$row) {
echo '<div class="wrap"><h1>Submission not found</h1></div>';
return;
}
$events = $this->get_events($id);
$fields = json_decode($row->fields_json, true);
$tracking = json_decode($row->tracking_json, true);
$files = json_decode($row->files_json, true);
$meta = json_decode($row->meta_json, true);
$resend_url = wp_nonce_url(admin_url('admin-post.php?action=lbbcf7_resend&id=' . intval($id)), 'lbbcf7_resend_' . intval($id));
$json_url = wp_nonce_url(admin_url('admin-post.php?action=lbbcf7_export_json&id=' . intval($id)), 'lbbcf7_export_json_' . intval($id));
$delete_url = wp_nonce_url(admin_url('admin-post.php?action=lbbcf7_delete&id=' . intval($id)), 'lbbcf7_delete_' . intval($id));
echo '<div class="wrap lbbcf7-wrap">';
echo '<h1>Submission ' . esc_html($row->uuid) . '</h1>';
echo '<p><a class="button" href="' . esc_url(admin_url('admin.php?page=lbbcf7-submissions')) . '">Back to submissions</a> <a class="button button-primary" href="' . esc_url($resend_url) . '">Resend to recovery email</a> <a class="button" href="' . esc_url($json_url) . '">Export JSON</a> <a class="button" onclick="return confirm(\'Delete this submission?\')" href="' . esc_url($delete_url) . '">Delete</a></p>';
echo '<div class="lbbcf7-cards">';
echo '<div class="lbbcf7-card"><strong>' . esc_html($row->mail_status) . '</strong><span>Mail status</span></div>';
echo '<div class="lbbcf7-card"><strong>' . esc_html($row->spam_status) . '</strong><span>Spam status</span></div>';
echo '<div class="lbbcf7-card"><strong>' . esc_html($row->lead_status) . '</strong><span>Lead status</span></div>';
echo '<div class="lbbcf7-card"><strong>' . esc_html($row->created_at) . '</strong><span>Captured</span></div>';
echo '</div>';
echo '<h2>Lead workflow</h2>';
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '">';
wp_nonce_field('lbbcf7_update_status_' . intval($id));
echo '<input type="hidden" name="action" value="lbbcf7_update_status"><input type="hidden" name="id" value="' . intval($id) . '">';
echo '<select name="lead_status"><option value="new" ' . selected($row->lead_status, 'new', false) . '>New</option><option value="contacted" ' . selected($row->lead_status, 'contacted', false) . '>Contacted</option><option value="converted" ' . selected($row->lead_status, 'converted', false) . '>Converted</option><option value="spam" ' . selected($row->lead_status, 'spam', false) . '>Spam</option></select> ';
echo '<br><br><textarea name="notes" rows="4" class="large-text" placeholder="Internal notes">' . esc_textarea($row->notes) . '</textarea><br>';
submit_button('Save lead status', 'secondary', '', false);
echo '</form>';
echo '<h2>Submission data</h2>';
$this->render_key_value_table($fields);
echo '<h2>Tracking</h2>';
$this->render_key_value_table($tracking);
echo '<h2>Files</h2>';
$this->render_key_value_table($files);
echo '<h2>System meta</h2>';
$this->render_key_value_table($meta);
echo '<h2>Forensic timeline</h2><table class="widefat striped"><thead><tr><th>Date</th><th>Type</th><th>Status</th><th>Message</th></tr></thead><tbody>';
foreach ($events as $event) {
echo '<tr><td>' . esc_html($event->created_at) . '</td><td>' . esc_html($event->event_type) . '</td><td>' . esc_html($event->event_status) . '</td><td>' . esc_html($event->message) . '</td></tr>';
}
echo '</tbody></table>';
echo '</div>';
}
private function render_key_value_table($data) {
echo '<table class="widefat striped"><tbody>';
if (!is_array($data) || empty($data)) {
echo '<tr><td>No data.</td></tr>';
} else {
foreach ($data as $key => $value) {
echo '<tr><th style="width:220px">' . esc_html($key) . '</th><td><pre style="white-space:pre-wrap;margin:0">' . esc_html($this->value_to_text($value)) . '</pre></td></tr>';
}
}
echo '</tbody></table>';
}
public function render_settings() {
if (!current_user_can('manage_options')) {
return;
}
$options = self::options();
$retention_url = wp_nonce_url(admin_url('admin-post.php?action=lbbcf7_run_retention'), 'lbbcf7_run_retention');
echo '<div class="wrap"><h1>CF7 Lead Black Box Settings</h1>';
echo '<form method="post" action="options.php">';
settings_fields(self::OPTION);
echo '<table class="form-table" role="presentation">';
echo '<tr><th scope="row">Recovery email</th><td><input type="email" class="regular-text" name="' . esc_attr(self::OPTION) . '[recovery_email]" value="' . esc_attr($options['recovery_email']) . '"><p class="description">Failed or manually resent leads are sent here.</p></td></tr>';
$this->settings_checkbox('capture_tracking', 'Capture UTM and click IDs', $options);
$this->settings_checkbox('capture_ip_hash', 'Capture privacy-safe IP hash', $options);
$this->settings_checkbox('capture_user_agent', 'Capture user agent', $options);
$this->settings_checkbox('capture_files', 'Capture uploaded file names', $options);
$this->settings_checkbox('store_spam', 'Store submissions marked as spam', $options);
$this->settings_checkbox('alert_on_mail_failure', 'Send alert on mail failure', $options);
echo '<tr><th scope="row">Retention days</th><td><input type="number" min="1" class="small-text" name="' . esc_attr(self::OPTION) . '[retention_days]" value="' . esc_attr($options['retention_days']) . '"> <a class="button" href="' . esc_url($retention_url) . '">Run cleanup now</a></td></tr>';
echo '<tr><th scope="row">Redacted field names</th><td><textarea class="large-text" rows="3" name="' . esc_attr(self::OPTION) . '[redacted_fields]">' . esc_textarea($options['redacted_fields']) . '</textarea><p class="description">Comma-separated partial field names. Matching fields are stored as [redacted].</p></td></tr>';
$this->settings_checkbox('delete_data_on_uninstall', 'Delete plugin data on uninstall', $options);
echo '</table>';
submit_button('Save Settings');
echo '</form>';
echo '<hr><p><strong>Built by LiMiT Agency.</strong> This plugin is designed as a practical recovery layer for Contact Form 7 lead generation websites.</p>';
echo '</div>';
}
private function settings_checkbox($key, $label, $options) {
echo '<tr><th scope="row">' . esc_html($label) . '</th><td><label><input type="checkbox" name="' . esc_attr(self::OPTION) . '[' . esc_attr($key) . ']" value="1" ' . checked(!empty($options[$key]), true, false) . '> Enable</label></td></tr>';
}
public function handle_resend() {
if (!current_user_can('manage_options')) {
wp_die('Permission denied.');
}
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
check_admin_referer('lbbcf7_resend_' . $id);
$submission = $this->get_submission($id);
if (!$submission) {
wp_die('Submission not found.');
}
$options = self::options();
$email = sanitize_email($options['recovery_email']);
$subject = '[CF7 Lead Black Box] Resent lead: ' . $submission->form_title;
$body = $this->format_submission_email($submission, 'Manual resend requested from WordPress admin.');
$ok = wp_mail($email, $subject, $body);
$this->add_event($id, 'manual_resend', $ok ? 'sent' : 'failed', $ok ? 'Lead resent to recovery email.' : 'Manual resend failed.', array('email' => $email));
wp_safe_redirect(admin_url('admin.php?page=lbbcf7-submissions&action=view&id=' . $id . '&resent=' . ($ok ? '1' : '0')));
exit;
}
public function handle_export_json() {
if (!current_user_can('manage_options')) {
wp_die('Permission denied.');
}
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
check_admin_referer('lbbcf7_export_json_' . $id);
$submission = $this->get_submission($id);
if (!$submission) {
wp_die('Submission not found.');
}
$events = $this->get_events($id);
$data = array(
'submission' => $submission,
'events' => $events,
);
nocache_headers();
header('Content-Type: application/json; charset=utf-8');
header('Content-Disposition: attachment; filename="cf7-lead-black-box-' . $submission->uuid . '.json"');
echo wp_json_encode($data, JSON_PRETTY_PRINT);
exit;
}
public function handle_export_csv() {
if (!current_user_can('manage_options')) {
wp_die('Permission denied.');
}
check_admin_referer('lbbcf7_export_csv');
global $wpdb;
$table = self::table_submissions();
$rows = $wpdb->get_results("SELECT * FROM {$table} ORDER BY created_at DESC LIMIT 5000");
nocache_headers();
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="cf7-lead-black-box-export.csv"');
$out = fopen('php://output', 'w');
fputcsv($out, array('id','uuid','created_at','form_title','page_url','referrer','status','mail_status','spam_status','lead_status','fields','tracking','notes'));
foreach ($rows as $row) {
fputcsv($out, array($row->id, $row->uuid, $row->created_at, $row->form_title, $row->page_url, $row->referrer, $row->status, $row->mail_status, $row->spam_status, $row->lead_status, $row->fields_json, $row->tracking_json, $row->notes));
}
fclose($out);
exit;
}
public function handle_delete() {
if (!current_user_can('manage_options')) {
wp_die('Permission denied.');
}
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
check_admin_referer('lbbcf7_delete_' . $id);
global $wpdb;
$wpdb->delete(self::table_events(), array('submission_id' => $id), array('%d'));
$wpdb->delete(self::table_submissions(), array('id' => $id), array('%d'));
wp_safe_redirect(admin_url('admin.php?page=lbbcf7-submissions&deleted=1'));
exit;
}
public function handle_update_status() {
if (!current_user_can('manage_options')) {
wp_die('Permission denied.');
}
$id = isset($_POST['id']) ? intval($_POST['id']) : 0;
check_admin_referer('lbbcf7_update_status_' . $id);
$allowed = array('new','contacted','converted','spam');
$status = isset($_POST['lead_status']) ? sanitize_key($_POST['lead_status']) : 'new';
if (!in_array($status, $allowed, true)) {
$status = 'new';
}
$notes = isset($_POST['notes']) ? sanitize_textarea_field(wp_unslash($_POST['notes'])) : '';
$this->update_submission($id, array('lead_status' => $status, 'notes' => $notes));
$this->add_event($id, 'lead_status', 'updated', 'Lead status updated to ' . $status . '.', array());
wp_safe_redirect(admin_url('admin.php?page=lbbcf7-submissions&action=view&id=' . $id . '&updated=1'));
exit;
}
public function handle_run_retention() {
if (!current_user_can('manage_options')) {
wp_die('Permission denied.');
}
check_admin_referer('lbbcf7_run_retention');
$deleted = $this->run_retention();
wp_safe_redirect(admin_url('admin.php?page=lbbcf7-settings&retention_deleted=' . intval($deleted)));
exit;
}
public function run_retention() {
global $wpdb;
$options = self::options();
$days = max(1, intval($options['retention_days']));
$cutoff = date('Y-m-d H:i:s', current_time('timestamp') - ($days * DAY_IN_SECONDS));
$table = self::table_submissions();
$event_table = self::table_events();
$ids = $wpdb->get_col($wpdb->prepare("SELECT id FROM {$table} WHERE created_at < %s", $cutoff));
if (empty($ids)) {
return 0;
}
$ids = array_map('intval', $ids);
$in = implode(',', $ids);
$wpdb->query("DELETE FROM {$event_table} WHERE submission_id IN ({$in})");
$wpdb->query("DELETE FROM {$table} WHERE id IN ({$in})");
return count($ids);
}
}
register_activation_hook(__FILE__, array('LBB_CF7_Plugin', 'activate'));
register_deactivation_hook(__FILE__, array('LBB_CF7_Plugin', 'deactivate'));
LBB_CF7_Plugin::instance();
The main PHP file handles activation, database tables, Contact Form 7 hooks, tracking capture, admin dashboard, submissions list, single lead timeline, resend, export, retention, and settings.
Why This Matters for Agencies
Agencies are often blamed when a client says leads are down.
Sometimes the traffic is fine, the landing page is fine, sometimes the campaign is fine. The real problem is that the form notification system is broken or unreliable.
Without a local lead audit layer, nobody has proof.
Lead Black Box gives agencies a stronger position:
- They can prove when forms were submitted.
- They can prove which landing pages generated leads.
- They can detect mail failures faster.
- They can resend missing inquiries.
- They can show clients that lead capture is protected.
- They can separate traffic problems from technical delivery problems.
This changes the conversation from guessing to evidence.
SEO and Conversion Value
Lead tracking is not only a technical issue. It also affects SEO and conversion analysis.
When a website generates organic traffic but leads are not visible, the business may wrongly assume that SEO is not working. In reality, the website may be producing inquiries that are never reaching the right inbox.
By storing page URL, referrer, UTM data, and click IDs, Lead Black Box helps connect form submissions to real acquisition channels.
This gives site owners a clearer view of:
- Which pages generate leads.
- Which campaigns generate inquiries.
- Which forms receive the highest-quality submissions.
- Which technical failures affect sales follow-up.
- Which leads became contacted or converted.
For SEO agencies, this is a practical advantage. It turns form submissions into evidence that organic visibility, landing pages, and technical infrastructure are working together.
Security and Privacy by Design
A lead recovery plugin must not become a data risk.
Lead Black Box is built with a local-first approach. It does not send submissions to a third-party platform by default. Data remains inside the WordPress installation.
The plugin also includes privacy-conscious controls:
- Raw IP addresses are not stored by default.
- IP hashing can be enabled or disabled.
- Sensitive fields can be redacted by name.
- Retention days can be configured.
- Old records can be cleaned automatically.
- Data deletion on uninstall is optional.
This makes the plugin practical for European websites, agencies, and businesses that need better operational control over form data.
What Makes It Different
Lead Black Box is not just another Contact Form 7 database plugin.
The difference is the operational layer.
Instead of only storing a submitted message, the plugin focuses on lead continuity: capture, status, context, timeline, failure detection, recovery, export, notes, and workflow state.
That is the level of visibility missing from many Contact Form 7 setups.
In practical terms, this means that a business owner or agency can open WordPress and answer the most important questions immediately:
- Did the form receive the lead?
- Did the notification fail?
- Where did the lead come from?
- Can we resend it?
- Was it contacted?
- Was it converted?
That is the difference between a form entry and a lead system.
Ideal Use Cases
Lead Black Box is useful for:
- Web design agencies managing client websites.
- SEO agencies tracking organic lead generation.
- Local businesses using Contact Form 7.
- High-ticket service websites.
- Clinics and appointment-based businesses.
- Legal and consulting websites.
- Real estate inquiry forms.
- B2B lead generation websites.
- WooCommerce wholesale or custom quote forms.
- Any WordPress site where lost inquiries create revenue damage.
Technical Benefits for Developers
For developers, the plugin adds a clean operational safety layer without replacing Contact Form 7 or forcing a heavy form builder migration.
Technical benefits include:
- Uses native WordPress hooks.
- Uses custom database tables for clean separation.
- Works locally without external APIs.
- Captures data before email delivery becomes the failure point.
- Includes manual resend and export tools.
- Includes retention controls.
- Uses standard WordPress admin patterns.
- Does not require a SaaS account.
For agencies, this means fewer emergency investigations and more predictable client support.
Future Improvements
The first version focuses on the critical foundation: capture, audit, resend, export, retention, and status tracking.
Future versions can expand the system with:
- Webhook delivery queue.
- Webhook retries with exponential backoff.
- Slack and Telegram alerts.
- CRM forwarding rules.
- Lead quality scoring.
- Duplicate lead detection.
- Multi-site agency dashboard.
- Per-form routing rules.
- Conversion reports by landing page.
- Monthly client reports.
- White-label agency mode.
- Spam false-positive review.
The direction is clear: Contact Form 7 does not need to become a full CRM. It needs a strong reliability and recovery layer around the lead capture process.
Final Thoughts
Contact Form 7 is still valuable because it is simple, flexible, and widely understood. But serious business websites need more than a basic email notification.
They need a way to prove that leads were captured.
A way to recover missed inquiries.
Understand where submissions came from.
Detect failures before revenue disappears silently.
Lead Black Box for Contact Form 7 gives WordPress sites that missing layer.
It protects the lead before the mail system can lose it.
That is the real upgrade.
Built by LiMiT for serious web agencies.






