Shared flat-file comments for Grav 2 with moderation, replies, rate limiting, CAPTCHA and an Admin API panel.
From the root of a Grav 2 installation:
bin/gpm install void-commentsThe plugin requires the Grav api, form and email plugins. GPM will offer
to install declared dependencies when the plugin is installed from the Grav
repository.
Install the development dependencies and run the isolated checks with:
composer install
composer test- Grav 2;
- Grav api, form and email plugins;
- the cap CAPTCHA provider supplied by the Grav Form installation.
Configure the plugin in the site's own Grav configuration, not in this
repository. At minimum configure enabled, templates and moderator_subject.
The notification body can be customized with moderator_body; if it is
omitted, the plugin uses its default message.
Use VOID_COMMENTS_MODERATOR_TO for the moderator address when moderator_to is
not set locally.
The notification body is plain text and supports these placeholders:
{route}, {author}, {email}, {id}, {body}, {moderation_url} and
{public_url}.
Runtime data is stored under user-data://void-comments. It contains private moderation data and must remain outside version control and public releases.
Users with the Grav api.super permission can open the Commenti panel from the
Grav administration sidebar. The panel supports searching comments, filtering
by page, pagination, editing, moderation and deletion. Approved comments also
include a link to their public page.
For that link to jump directly to a comment, the consuming theme should render
each comment with an id such as comment-{{ comment.id }}.
The theme can use these Twig variables:
- void_approved_comments;
- void_comment_reply_to;
- void_comments_enabled.
The replies enhancement expects a .comments container, an input named data[parent_id], and the data-comment-reply, data-comment-reply-context, data-comment-reply-author and data-comment-reply-cancel attributes.
Submissions are validated against the current route and an allow-list of page templates. New comments are written to pending/, while the directory controls moderation state. Email addresses and rate-limit material are runtime data and must be protected according to the site's retention policy.
The default comment limit is three submissions per hour for each IP address and
each normalized email address. Both counters must allow a request, so changing
only the IP or only the email does not bypass the limit. The rate-limit file is
stored under the site's user-data://void-comments/ directory and contains only
SHA-256-derived keys and timestamps. Files written by version 0.1.x, which used
an IP+email pair key, remain effective for that exact pair during the migration.
Moderation operations use a stable .storage.lock file. Rate limits use a
separate lock beside the JSON counters. Do not remove these locks while PHP
workers are running. Writes use unique temporary files, check complete writes
and flush before replacing JSON. Use local storage with working flock and
atomic rename semantics; distributed filesystems need separate validation.
The approved file is the commit point for approval. Retrying an interrupted approval removes stale pending data without replacing subsequent approved edits. Readers suppress the stale pending copy; retention removes it without changing the approved record, and includes the cleanup in pruning previews. Deleting an approved comment also removes any pending copy left by an interrupted approval.
The void-comments-retention scheduler job runs daily at 03:20 in Grav's
scheduler timezone. Configure the host to invoke Grav's scheduler regularly;
registering the job alone does not install an operating-system cron task.
It uses the existing policy: technical metadata expires after 7 days and
pending comments after 90 days. Approved comments have no automatic expiry.
CommentStore::prune($now, true) previews removals without changing records.
The same storage lock serializes retention and moderation.
composer test includes worker-process concurrency, short-write, failed-write,
approval-recovery and retention-preview checks. No site records are required.