Gravity Forms Response Management
Utility functions to manage and aggregate form entries for groups of respondents and forms.
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/hughc/gravityforms-response-management/archive/refs/heads/master.zipAggregate, browse and export Gravity Forms entries as a single workshop/survey dataset.
Gravity Forms is oriented towards anonymous collection — contact forms, questionnaires — and exports one form at a time. On a workshop or survey site the model is different: a user moves through a sequence of screens, answers several forms along the way, and those answers together constitute one response. This plugin treats a set of forms as one dataset and a set of respondents as one cohort.
Each output row is one known respondent's answers across all forms in a group; each column is a field from one of those forms. Respondents are identified by WordPress user ID — anonymous submissions are not handled.
Requirements
| Component | Requirement |
|---|---|
| WordPress | 6+ (uses parse_blocks()) |
| Gravity Forms | Required — all entry access goes through GFAPI, tested against 3.0 |
| Groups plugin | Optional — enables Groups-backed respondent cohorts |
No custom database tables. All configuration lives in two WordPress options.
Installation
Drop the folder into wp-content/plugins/ and activate. Nothing to configure on activation.
Usage
The UI is a single tabbed screen at Forms → Response Management. Every tab, AJAX endpoint and export
requires the manage_options capability — this is an admin-only tool.
Setup
Define the two axes of a dataset:
Form groups — the forms that make up one dataset.
- Manual selection: pick forms from a list.
- Pattern: point at a parent page and the plugin discovers every
gravityforms/formblock in its child pages. Blocks are parsed recursively, so forms nested inside wrapper blocks (page builders, the Show Response wrapper) are found, and[gravityforms id=N]shortcodes are picked up as a fallback. Child pages are read inmenu_order, which fixes the linear screen sequence and therefore the order of columns in the export.
Respondent groups — the cohort of users.
- Manual selection: pick users; the ID list is stored as saved.
- Groups plugin: pick a Groups group; membership is resolved fresh on every read, so later joins and leaves are picked up automatically.
Browse Responses
Pick a form group and a respondent group, then navigate respondent by respondent with
< prev | (x/y) | next >.
- Simple mode — choose one form; each step shows that user's complete submission to it.
- Advanced mode — tick individual fields from any form in the group; each step shows just those fields, so you can compare one question across a cohort or skim a shortlist of answers across several forms.
Export Responses
Choose a form group, a respondent scope, and download a CSV.
| Scope | Rows exported |
|---|---|
| A respondent group | Members of that group |
| All users | Every logged-in user who has submitted to the selected form group |
"All users" is a built-in scope, not a group you create — no Setup entry needed, and it works even with
zero respondent groups defined. It resolves respondents from the entry table itself
(DISTINCT created_by, scoped to the group's forms), so it means everyone who actually responded, not
every WordPress user, and it picks up participants who were never assigned to a group. Anonymous
submissions are excluded.
Entry status is not filtered — entries in any state, including trashed, are read.
Utilities
- Clear user responses — delete every entry created by a user, across all forms or a selected subset.
- Clear form responses — delete all entries for one form, in batches of 100.
Both delete permanently; there is no undo.
CSV layout
| Row | Contents |
|---|---|
| 1 | Form title, repeated across that form's columns (User over the first column) |
| 2 | Field label — the column header (user_login value sits under the first column) |
| 3 … | One row per respondent |
Per respondent and per form, the most recent entry wins — the same rule as browse and aggregation, so repeated submissions don't duplicate rows. Checkbox and multi-select values are comma-joined, and field values are rendered through Gravity Forms' export logic, so Likert scales and custom field types resolve to readable text rather than stored IDs. A UTF-8 BOM is written for Excel.
Downloads are named responses-<form-group>-<respondent-scope>-<YYYY-MM-DD>.csv, e.g.
responses-vacypa-all-users-2026-08-28.csv.
Data storage
| Option | Holds |
|---|---|
gfrm_form_groups |
Form groups: name, type, resolved form_ids, parent page ID |
gfrm_respondent_groups |
Respondent groups: name, type, user_ids, Groups group ID |
Serialized arrays keyed by fg_<uniqid> / rg_<uniqid>. The all respondent scope is not stored here —
it is a pseudo group, resolved at export time.
Architecture
Flat, static-only classes, no inheritance:
gravityforms-response-management.php Bootstrap, admin menu, export handler, 4 AJAX endpoints
includes/class-form-groups.php Form group CRUD + pattern discovery
includes/class-respondent-groups.php Respondent group CRUD + Groups sync
includes/class-response-aggregator.php Aggregation, browse, CSV generation
includes/class-utils.php Clear user / form responses
admin/class-admin-page.php 4-tab admin screen
admin/js/admin.js Toggles, field loading, browse navigation
Full design notes, decisions and known trade-offs: docs/implementation.md. Original requirements: docs/spec.md.
Notes and limitations
- Destructive utilities delete entries outright, rather than blanking their field values.
- Browse loads every respondent's entries for the selected scope, so very large cohorts are worth exporting instead of browsing.
- A saved form group's form list is a snapshot of the pattern at save time; re-save to pick up forms added to the page tree afterwards.
- Only respondents with a live WordPress user account appear in an export — entries belonging to a deleted account are skipped, though they stay in the database.