Install
This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.
Install straight from GitHub with WP-CLI:
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.