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_window filter.
  • To stop saving a form, tick it under Do not save these forms, or add do_not_store: true to 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.config and index.php.
  • Files are stored with random names and a .ajem extension, 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_dir filter.

7. Google Sheets sync

  1. 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.
  2. Open your spreadsheet and Share it with the service account’s email address as Editor.
  3. 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.
  4. 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.com and sheets.googleapis.com, and only when you configure sync. Developers can change the row with the ajem_sheets_row filter.

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 calls wp-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 (enable WP_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.