Launch offer
Business website $150 USD Custom plugin $200 USD Ready in 5 days
Get a quote
WPForms

Thenameda Partial Form Recovery for WPForms: Complete Developer Guide

A source-code walkthrough of Thenameda Partial Form Recovery for WPForms: architecture, custom table storage, AJAX/sendBeacon tracking, WPForms hooks, recovery links, Settings API, security, and how to build a similar addon.

Thenameda Partial Form Recovery for WPForms is a WordPress addon that captures incomplete WPForms field data when a visitor starts filling a form and leaves before submitting. This guide explains the plugin from a developer’s perspective, based on the actual 1.0.0 source under thenameda-partial-form-recovery-for-wpforms.

Focus keyword: WPForms partial form recovery developer guide
Audience: WordPress / WPForms plugin developers
Plugin version analyzed: 1.0.0 · Requires WordPress 6.2+, PHP 7.4+, WPForms Lite or Pro

1. What the plugin does and its main purpose

The plugin’s purpose is lead recovery for abandoned WPForms. When tracking is enabled globally and on a specific form, the front end watches WPForms inputs and, on abandonment signals (mouse leave, link navigation, tab hide, page hide), posts a sanitized snapshot of field values to WordPress via admin-ajax.php.

Stored records live in a custom table ({prefix}wpfrmaf_entries), appear under a top-level admin menu (Partial Recovery), can be opened with a signed recovery URL that refills fields, and can optionally receive a manual reminder email containing that recovery link.

It also ships a shortcode [wpfrmaf_stats] that prints an abandoned-entry count.

2. How the plugin works internally

Architecture is a classic WordPress singleton bootstrap:

  1. Main file defines constants and waits for plugins_loaded.
  2. If WPForms is active, it waits for wpforms_loaded (or runs immediately if that action already fired).
  3. wpfrmaf_init() loads helpers/DB, runs WPFRMAF_DB::maybe_upgrade(), then boots WPFRMAF::instance().
  4. The singleton wires AJAX, frontend, Settings API, shortcode, and (in admin) menus, list table actions, and builder settings.

Runtime split:

  • Front JS detects dirty forms and posts payloads.
  • AJAX PHP validates nonces, maps fields, inserts/updates rows.
  • DB layer encapsulates $wpdb CRUD.
  • Admin lists/views/deletes and can send reminder mail.
  • Recovery reads ?wpfrmaf_recover=&wpfrmaf_token=, verifies the token, and localizes fill data into JS.

3. Complete plugin folder and file structure

thenameda-partial-form-recovery-for-wpforms/
├── thenameda-partial-form-recovery-for-wpforms.php  # Bootstrap, constants, activation hooks
├── uninstall.php                                    # Drop table + delete options
├── readme.txt
├── index.php                                        # Silence directory listing
├── assets/
│   ├── css/admin.css
│   └── js/
│       ├── front.js                                 # Abandon tracking + recovery fill
│       └── admin.js                                 # Delete confirmations
├── includes/
│   ├── class-wpfrmaf.php                            # Singleton, wpforms_process_complete
│   ├── class-wpfrmaf-helpers.php                    # Settings, parsing, security helpers
│   ├── class-wpfrmaf-db.php                         # Custom table + CRUD
│   ├── class-wpfrmaf-ajax.php                       # track / remove AJAX
│   ├── class-wpfrmaf-frontend.php                   # Enqueue + localize recovery data
│   ├── class-wpfrmaf-settings.php                   # Settings API
│   ├── class-wpfrmaf-shortcode.php                  # [wpfrmaf_stats]
│   ├── class-wpfrmaf-activator.php
│   ├── class-wpfrmaf-deactivator.php
│   └── admin/
│       ├── class-wpfrmaf-admin-menu.php
│       ├── class-wpfrmaf-admin.php                  # Delete + send_mail actions
│       ├── class-wpfrmaf-list-table.php             # WP_List_Table
│       ├── class-wpfrmaf-builder.php                # WPForms builder panel
│       └── views/
│           ├── entries-page.php
│           ├── entry-view.php
│           └── settings-page.php
└── languages/

4. Important PHP, JavaScript, CSS, and other files

Bootstrap file

thenameda-partial-form-recovery-for-wpforms.php defines WPFRMAF_VERSION, WPFRMAF_DIR, WPFRMAF_URL, WPFRMAF_BASENAME, checks WPForms via is_plugin_active(), registers activation/deactivation hooks, and starts the plugin only after WPForms is ready.

Core classes

  • WPFRMAF — wires modules; deletes abandoned rows on successful submit when configured.
  • WPFRMAF_DBdbDelta schema, insert/update/query/delete.
  • WPFRMAF_Helpers — option keys, field exclusion list, serializeArray parsing, email extraction, recovery URL/token helpers.
  • WPFRMAF_Ajaxwpfrmaf_track_abandoned and wpfrmaf_remove_abandoned (logged-in + nopriv).
  • WPFRMAF_Frontend — enqueues front.js and localizes AJAX URL, nonces, and fill payload.
  • WPFRMAF_Settings — Settings API registration and sanitization.
  • WPFRMAF_Builder — adds a Partial Recovery section in the WPForms form builder.

JavaScript

assets/js/front.js is the abandonment engine: listens for input/change, WPForms page changes, mouse leave / touch, visibilitychange, pagehide, and outbound link clicks; prefers navigator.sendBeacon when leaving the page; fills recovery fields; clears storage after wpformsAjaxSubmitSuccess.

assets/js/admin.js only confirms single and bulk deletes.

CSS

assets/css/admin.css styles admin list/detail screens. There is no dedicated front-end stylesheet; recovery is script-driven field filling.

5. How the plugin hooks into WordPress and WPForms

WordPress hooks

  • plugins_loadedwpfrmaf_bootstrap()
  • register_activation_hook / register_deactivation_hook
  • wp_enqueue_scripts → front script
  • wp_ajax_* / wp_ajax_nopriv_* → tracking endpoints
  • admin_menu, admin_init, admin_enqueue_scripts
  • admin_notices when WPForms is missing
  • plugin_action_links_{basename}
  • register_setting / Settings API callbacks
  • add_shortcode( 'wpfrmaf_stats' )

WPForms hooks / APIs

  • wpforms_loaded — safe init after WPForms bootstraps
  • wpforms_builder_settings_sections — add builder tab
  • wpforms_form_settings_panel_content — render panel fields via wpforms_panel_field()
  • wpforms_process_complete — delete abandoned rows after successful processing
  • wpforms()->obj( 'form' )->get() — load form JSON for field mapping and recovery fill
  • Front JS events: wpformsReady, wpformsPageChange, wpformsAjaxSubmitSuccess

6. How partial form entries are detected and saved

Detection is client-side. After wpformsReady (or DOM ready if a form already exists), WPFRMAFAbandoned.init() binds:

  • input / change on form.wpforms-form :input → mark dirty and serializeArray()
  • wpformsPageChange → same for multipage forms
  • mouseleave (and touch equivalents) → attempt send
  • outbound link mousedown/click → send before navigation (may intercept same-window navigations)
  • visibilitychange === 'hidden' and pagehide → send with beacon when possible

Before sending, JS requires a “meaningful” payload: at least one non-empty wpforms[fields]… value. Duplicate payloads are skipped via a signature of form_id|JSON(forms).

On the server, track_abandoned() additionally requires:

  • Valid AJAX nonce
  • Global enable_tracking setting on
  • Per-form wpfrmaf_enable setting on
  • Resolvable form_id
  • For new rows: has_meaningful_abandoned_data() (email or non-empty mapped fields)

7. Database / storage mechanism

Storage uses a custom table, not a CPT. Table name: {$wpdb->prefix}wpfrmaf_entries.

Created with dbDelta() on activation and whenever wpfrmaf_db_versionWPFRMAF_DB::DB_VERSION (1.0.0).

Schema (columns)

  • id — primary key
  • form_id — WPForms form ID
  • email — extracted email for reminders
  • form_data — JSON map of field_id → value
  • ip_address, page_url
  • recover_token — random 32-char password-style token
  • emails_sent, fail_count
  • status — default abandoned
  • date_created, date_updated

Indexed on form_id, email, status, date_created.

Inserts sanitize email/IP/URL/token and encode arrays with wp_json_encode(). Queries support search across email, page URL, and IP, with allow-listed ORDER BY columns.

On plugin delete, uninstall.php drops the table and deletes wpfrmaf_settings + wpfrmaf_db_version. Deactivation does not delete data.

8. How AJAX requests are handled

Track abandoned — wpfrmaf_track_abandoned

check_ajax_referer( 'wpfrmaf_abandoned_track', 'wpfrmaf_abandoned_nonce' );

Flow:

  1. Sanitize forms array (serializeArray shape).
  2. Parse into form_id + field map via regex on names like wpforms[fields][12], …[], …[first].
  3. Load per-form settings; abort if tracking disabled.
  4. Map tracked fields (skip excluded types such as payments, captcha, file upload, layout, etc.).
  5. Extract email from configured email field or auto-detect.
  6. If an existing entry ID/token is provided and valid for that form, merge non-empty new values and update().
  7. Otherwise insert() with a new recover_token from wp_generate_password( 32, false ).
  8. Return JSON { entry_id: N }. Front JS stores that ID in memory + sessionStorage key wpfrmaf_entry_{formId}.

Remove abandoned — wpfrmaf_remove_abandoned

Called after successful AJAX submit. Verifies wpfrmaf_remove_abandoned nonce, then deletes by entry ID (+ optional token) or all abandoned rows for a form ID.

Both actions register wp_ajax_ and wp_ajax_nopriv_ so anonymous visitors can be tracked.

9. How the admin settings work

Global settings (Settings API)

Option name: wpfrmaf_settings. Group: wpfrmaf_settings_group. Screen under Partial Recovery → Settings.

Fields:

  • enable_tracking (default on)
  • delete_on_submit (default on)
  • enable_reminders (default off)
  • reminder_subject / reminder_message (supports {recovery_link})
  • max_reminders (0–10, default 2)

Sanitization coerces checkboxes to 0/1, runs sanitize_text_field / sanitize_textarea_field, and clamps max_reminders.

Per-form settings (WPForms builder)

Stored inside the WPForms form settings JSON:

  • wpfrmaf_enable — toggle
  • wpfrmaf_email_field — email field ID or empty for auto-detect

Helpers also define wpfrmaf_track_fields (allow-list or null = all fields). The builder UI in 1.0.0 exposes enable + email field only; with no track-fields value saved, mapping tracks all non-excluded field types.

Entries UI

WP_List_Table lists abandoned rows with search, sort, bulk delete (admin referer), and row actions View/Delete. Detail view shows fields, recovery link, and optional “Send reminder email” when reminders are enabled and an email exists.

10. How the frontend functionality works

WPFRMAF_Frontend::enqueue_scripts() skips admin and skips when global tracking is off. It localizes wpfrmaf_abandoned with:

  • ajaxurl, home_url
  • track + remove nonces
  • optional wpfrmaf_recover, token, and wpfrmaf_fill_fields array

Recovery fill supports textarea, radio, select (including multi via comma-split), checkbox, name (first/middle/last), email (including [primary]), address (address1 from comma-split), and a default input/textarea/select path. Fill runs immediately, after short timeouts, and again on wpformsReady so late-rendered fields still populate.

11. Data flow: interaction → storage → recovery

  1. Admin enables global tracking and enables Partial Recovery on a form in the builder.
  2. Visitor loads a page with that WPForms form; front.js initializes.
  3. Visitor types; JS marks dirty and keeps a serialized snapshot.
  4. Abandonment event fires → POST to admin-ajax.php?action=wpfrmaf_track_abandoned (XHR or sendBeacon).
  5. PHP validates, maps fields, inserts/updates wpfrmaf_entries, returns entry_id.
  6. JS stores entry ID in sessionStorage so later updates merge into the same row.
  7. Admin views the entry and may send a reminder (wp_mail) with a URL containing wpfrmaf_recover + wpfrmaf_token.
  8. Visitor opens the link; frontend verifies token, localizes fill data, JS refills the form.
  9. On successful submit, JS calls wpfrmaf_remove_abandoned; PHP also may delete abandoned rows for that form on wpforms_process_complete when delete_on_submit is enabled.

12. Important WordPress and WPForms hooks/functions used

WordPress: check_ajax_referer, wp_send_json_success, wp_create_nonce, wp_verify_nonce, check_admin_referer, current_user_can, wp_mail, wp_safe_redirect, dbDelta, $wpdb->prepare/insert/update/query, register_setting, hash_equals, wp_generate_password, add_query_arg, sessionStorage (client).

WPForms: wpforms_loaded, wpforms_process_complete, builder section/panel hooks, wpforms_panel_field, form object API, front events listed above.

Filter provided for capability: wpfrmaf_admin_capability (default manage_options).

13. Security measures

  • Nonces: AJAX track/remove use check_ajax_referer; admin delete/send mail use action-specific nonces; bulk delete uses list-table referer.
  • Capabilities: Admin pages and actions require manage_options (filterable).
  • Sanitization: sanitize_text_field, sanitize_email, sanitize_key, sanitize_textarea_field, absint, esc_url_raw, map_deep, wp_unslash.
  • Escaping: Admin views and shortcode use esc_html, esc_attr, esc_url, esc_textarea.
  • Recovery tokens: Compared with hash_equals; fill data only localized when token matches.
  • Field exclusions: Payment, captcha, file upload, signature, HTML, pagebreak, layout, repeater, and similar types are never mapped for storage/fill.
  • Direct file access: ABSPATH guards and directory index.php silencers; uninstall gated by WP_UNINSTALL_PLUGIN.

Note: track/remove AJAX are intentionally available to logged-out users. Protection relies on nonces, per-form enablement, meaningful-data checks, and token verification when updating a recovered entry—not on capability checks.

14. Error handling and validation

  • Missing WPForms → admin error notice; plugin does not boot feature classes.
  • AJAX failures often return soft success with entry_id: 0 (disabled tracking, missing form, empty payload) to avoid noisy front-end errors.
  • Invalid recovery token → entry treated as new / fill data cleared.
  • Form ID mismatch between stored entry and posted form resets the entry context.
  • Reminder send fails closed if reminders disabled, email invalid, or emails_sent >= max_reminders; increments fail_count when wp_mail returns false.
  • Admin unauthorized access → wp_die; bad nonce → wp_die with security message.
  • JS wraps sessionStorage in try/catch to ignore private-mode failures.

15. Complete step-by-step workflow

  1. Activate → create table, seed default settings option if missing, flush rewrites.
  2. Configure global options under Partial Recovery → Settings.
  3. Enable on a form: WPForms builder → Settings → Partial Recovery.
  4. Publish form on a page; front script loads.
  5. Partial fill → abandon event → AJAX/beacon → DB row.
  6. Admin reviews under Partial Recovery → Entries.
  7. Optional reminder from entry detail; visitor returns via recovery URL.
  8. Complete submit → remove AJAX + optional wpforms_process_complete cleanup.
  9. Delete plugin → uninstall drops table and options.

16. How a developer could build a similar plugin from scratch

  1. Create a bootstrap file with dependency checks for WPForms.
  2. On activation, create a custom table with dbDelta and store a schema version option.
  3. Add WPForms builder settings via wpforms_builder_settings_sections + panel content.
  4. Enqueue a front script that serializes form.wpforms-form and posts on leave/hide events; use sendBeacon for unload reliability.
  5. Register wp_ajax_nopriv_ handlers with nonces; sanitize and map only allow-listed fields.
  6. Persist JSON field maps with a random recover token; expose recovery via query args + hash_equals.
  7. Build an admin WP_List_Table over $wpdb queries.
  8. Hook wpforms_process_complete (and AJAX success) to remove recovered/abandoned rows.
  9. Use Settings API for global toggles; keep destructive cleanup in uninstall.php only.

17. Common issues and troubleshooting

  • No rows saved: Confirm WPForms is active, global tracking is on, and the form’s Partial Recovery toggle is enabled. Empty forms never insert.
  • Beacon vs XHR: On tab close, beacon may be used; entry ID response may not update sessionStorage until a later XHR update.
  • Recovery not filling: Token must match; excluded field types are skipped; wait for wpformsReady retries.
  • Reminder not sending: Enable reminders in settings, ensure a valid email was captured, respect max_reminders, and verify site mail transport (local WAMP often cannot send).
  • Rows remain after submit: Check delete_on_submit; Lite may not fire entry creation the same way, but this plugin still hooks wpforms_process_complete and front AJAX remove.
  • Cron expectation: Deactivator clears a hook named wpfrmaf_send_reminder_emails, but 1.0.0 does not schedule automated reminder cron jobs—reminders are sent manually from the entry screen.

18. Best practices used in the plugin

  • Singleton + modular classes with clear responsibilities
  • Dependency-aware bootstrap (plugins_loadedwpforms_loaded)
  • Custom table for high-volume partial entries instead of posts
  • Schema version option + maybe_upgrade()
  • Settings API for nonces/capabilities/sanitization
  • Prepared statements and typed $wpdb->insert/update formats
  • Token comparison via hash_equals
  • Sensitive field-type exclusion list
  • Unload-safe tracking with sendBeacon
  • Admin assets loaded only on plugin screens
  • Data preserved on deactivate; removed only on uninstall
  • i18n-ready strings and text domain

Conclusion

Thenameda Partial Form Recovery for WPForms is a practical reference for building WPForms addons that combine front-end abandonment detection, secure AJAX, a custom $wpdb table, builder settings, Settings API options, and recovery UX. Study front.js for the browser workflow and WPFRMAF_Ajax + WPFRMAF_DB for the server contract—those three files define most of the product behavior.

Plugin listing: Thenameda Partial Form Recovery for WPForms on WordPress.org

Leave a reply

Your email address will not be published. Required fields are marked *