Entries Manager for Contact Form 7 – Documentation
Version 1.0.1
Saves every Contact Form 7 submission to your WordPress database and gives you a full entries manager: search and filters, read/unread and flagged (important) status, private notes, private file attachments, CSV and PDF export, optional Google Sheets sync, a dashboard widget and privacy tools. One-time purchase; nothing to activate and no external account.
1. Requirements and install
- WordPress 6.2+, PHP 7.4+ and Contact Form 7 (free on WordPress.org).
- Upload the zip under Plugins > Add New > Upload Plugin and activate. On WordPress 6.5+ Contact Form 7 is listed as a required plugin.
- If Contact Form 7 is later deactivated, an admin notice appears. Saved entries stay available; new submissions are not captured until Contact Form 7 is active again.
- Multisite: works per site or network-activated. Network activation creates the tables on every site, including sites added later.
- Google Sheets sync needs the PHP OpenSSL extension (enabled on almost every host).
2. How submissions are saved
- Entries are saved after Contact Form 7 has validated the form and run its spam checks, just before the email is sent. Entries are kept even if the email fails. Submissions marked as spam or failing validation are never saved.
- Each field is stored under its Contact Form 7 field name (for example
your-name). Hidden system fields (names starting with an underscore) and captcha tokens are skipped. - Duplicate protection: an identical submission (same form, same values, same files, same visitor) sent again within 60 seconds is ignored. A unique database key enforces this, so double clicks and retries cannot create duplicates. Developers can change the window with the
ajem_dedupe_windowfilter. - To stop saving a form, tick it under Do not save these forms, or add
do_not_store: trueto the form’s Additional Settings tab (a Contact Form 7 setting this plugin respects). - To never store a sensitive field, list its name under Fields to skip.
3. Entries screen
Go to Form Entries > All Entries. The menu shows a badge with the number of unread entries.
- Views: All, Unread, Read, Flagged.
- Filters: form, date from / to, and a search box that searches field values, email and form title.
- Bulk actions: mark as read/unread, flag/unflag as important, export selected to CSV, delete permanently.
- Pagination: choose entries per page under Screen Options (up to 200). Lists stay fast with hundreds of thousands of entries.
- Click an entry to open it. Opening an entry marks it as read.
4. Single entry
- All fields, submission details (form, date, email, user, page, and IP / user agent if enabled).
- Attachments: files uploaded through the form, with secure download links.
- Notes: private notes for your team, with author and date.
- Buttons: Flag as important (or Unflag), Mark as unread, Send to Google Sheets (when configured), Delete, and Download PDF.
5. Exports
- CSV: Export filtered to CSV exports everything matching the current filters; the bulk action exports only the selected entries. Columns: ID, Submitted, Form, Status, Starred, Email, (IP, User agent), Page URL, then one column per field name. The file is streamed in batches of 500, so large exports do not exhaust memory. Values that start with
= + - @and similar characters are prefixed with an apostrophe so spreadsheet apps cannot run them as formulas. The file is UTF-8 with a byte-order mark so Excel opens accents correctly. - PDF: one entry per PDF with its fields and notes. Generated by built-in code, no external service and no extra library. It uses the standard PDF Helvetica font, which covers Western European characters; other scripts (for example Cyrillic, Greek, Chinese) are shown as
?in the PDF. Use CSV for those.
6. File uploads (private storage)
- Files uploaded through Contact Form 7 file fields are copied to a private folder before Contact Form 7 deletes its temporary copy.
- The folder is created outside the web root (next to your WordPress folder) when the server allows it. Otherwise it is
wp-content/uploads/ajem-private, protected with.htaccess,web.configandindex.php. - Files are stored with random names and a
.ajemextension, so they can never be executed by the web server, and are served only through a download link that checks login, capability and a security token. - Nginx: Nginx ignores
.htaccess. If the folder is inside the web root, Tools > Site Health tests whether files can be opened by direct link and, if so, shows the exact rule to add, for example:location ^~ /wp-content/uploads/ajem-private/ { deny all; } - Developers can choose another folder with the
ajem_private_dirfilter.
7. Google Sheets sync
- In Google Cloud Console create a project, enable the Google Sheets API, create a service account and add a JSON key. Download the JSON file.
- Open your spreadsheet and Share it with the service account’s email address as Editor.
- In Form Entries > Settings, upload (or paste) the JSON key and click Save key. The key is validated, stored in your database and never displayed again.
- Tick Append new entries to a Google Sheet, enter the spreadsheet URL or ID and the tab name (default
Sheet1), optionally pick forms, and save. Click Test connection.
- New entries are queued and appended in the background (WP-Cron) in batches of 50, so the visitor never waits for Google. Each row contains: entry ID, date, form, email, then one cell per field as
field-name: value. Values are sent as plain text (Google never evaluates them as formulas). - The entries list shows a Sheets column (Pending, Synced, Failed). Use Retry failed entries or Send to Google Sheets on a single entry. The last error is shown on the settings page.
- The plugin signs the token request itself with OpenSSL (no Google SDK). These are the only outgoing requests the plugin makes, to
oauth2.googleapis.comandsheets.googleapis.com, and only when you configure sync. Developers can change the row with theajem_sheets_rowfilter.
8. Privacy
- IP address and user agent are not stored unless you enable them.
- Tools > Export Personal Data / Erase Personal Data include contact form entries matched by email address. Erasure deletes the entries, their notes and files.
- Set Delete entries after N days to remove old entries automatically (daily, in batches).
- Suggested text is added to Settings > Privacy > Policy Guide.
- No tracking, no analytics, no calls home.
9. Permissions
| Capability | Allows |
|---|---|
ajem_view_entries |
See entries, download attachments and PDFs, read documentation, dashboard widget |
ajem_manage_entries |
Everything above, plus status/flag changes, notes, delete and CSV export |
ajem_manage_settings |
Settings and Google Sheets key |
Administrators get all three on activation. Give them to other roles (for example Editor) with any role editor plugin. Every action checks the capability and a security token (nonce).
10. Dashboard widget
Shows unread and today’s counts and the five latest entries for users who can view entries.
11. Uninstall
Deactivating keeps everything. Deleting the plugin keeps your entries unless you tick Delete all entries, files and settings when the plugin is deleted in Settings; then the tables, private files, settings, Google key and capabilities are removed (on every site of a network).
12. Developer hooks
ajem_store_submission(filter): return false to skip storing a submission. Args:$store, $form_id, $posted_data.ajem_entry_saved(action): after an entry is saved. Args:$entry_id, $form_id.ajem_dedupe_window(filter): duplicate window in seconds (default 60).ajem_sheets_row(filter): cells sent to Google Sheets.ajem_private_dir(filter): private upload folder.
13. Troubleshooting
- No entries appear: check Contact Form 7 is active, the form is not excluded and has no
do_not_store: true, and the submission passed validation and spam checks. - Sheets stays Pending: WP-Cron must run. On sites with
DISABLE_WP_CRON, make sure a real cron job callswp-cron.php. - Sheets Failed with “permission”: share the spreadsheet with the service-account email as Editor and check the tab name.
- Site Health says files are exposed: add the Nginx rule shown there and reload Nginx.
14. Changelog
1.0.1 Google Sheets sync uses an atomic lock and per-row claim (no duplicate rows from overlapping runs), retries 429/5xx with backoff, sends rows one by one if a batch fails and retries failed rows automatically. Retention continues in background batches when more than 10,000 entries are due, with an admin notice. All Contact Form 7 forms appear in filters and settings.
1.0.0 Initial release.
15. Limits
- Google Sheets: up to 50 entries per sync run; further runs are queued automatically. Rate-limit (429) and server (5xx) responses are retried up to 3 times with growing waits. Failed rows are retried automatically after 5, 10, 20, 40 minutes and then hourly, and can be re-queued at any time from Settings.
- Retention: up to 10,000 old entries are deleted per run; if more are due, another batch runs about a minute later until done, and an admin notice shows how many remain.
- Exports run in batches of 500 entries and include all matching entries.
16. Support
Email admin@007aj.com or send a message through WPBay. To help us answer quickly, please include:
- WordPress, PHP, Contact Form 7 and 007AJ Entries Manager versions (Tools → Site Health → Info shows all of them).
- The steps that reproduce the problem, what you expected and what happened.
- Any error text shown, and a short excerpt from
wp-content/debug.log(enableWP_DEBUG_LOG) or your server error log. - For Google Sheets issues: the Sheets status of the entry and the last error shown in Settings.
Never send passwords, API keys, service-account JSON files or other credentials. We will never ask for them.