Blog and Comment Hooks

1 views Markdown

The seven hooks over blog comments: moderation, editing, deletion, votes and the list.

Overview

The path of a blog comment lives here: it is written, filtered, listed, moderated and if need be deleted.

Two distinctions to hold from the start. On a guest comment the author id is zero. And the moderation gate hands you the request while the event hands you the result: because pinning is a toggle, the two are not the same.

Reference

Stopping a comment moderation

gateclient.blog_comment_moderate
ClientBlog the request, not the result

Runs before a comment is moderated.

Parameters 5
$cidintThe comment id.
$actionstringThe operation requested: approve, spam or pin. ? Pinning is a toggle request: whether it ends as pinning or unpinning depends on the current state. This value states the request, not the result.
$actor_idintThe id of the administrator doing it.
$author_idintThe author of the comment. It is zero on a guest comment: test before doing anything tied to a member.
$owner_idintThe article the comment belongs to.
Return 1
string|nullA non-empty text blocks the operation and is shown as the error. An empty return lets it carry on.
Listener
Hook::add('gate:client.blog_comment_moderate', 10,
    function ($cid, $action, $actor_id, $author_id, $owner_id) {
        // 'pin' is a TOGGLE request: you cannot tell the result from here.
        if ($action === 'spam' && Acme::protectedAuthor($author_id))
            return 'Comments from this author cannot be marked as spam.';

        return null;
    });

Following a comment moderation

actionclient.blog_comment_moderated
ClientBlog the result, not the request

Runs after the moderation is applied. Unlike the gate, the value here is the operation that actually happened.

Parameters 5
$cidintThe comment id.
$actionstringThe operation that happened: approved, spam, pinned or unpinned. Against the three values in the gate there are four here: the toggle request has split into two separate results.
$actor_idintThe administrator who did it.
$author_idintThe author of the comment. It is zero on a guest comment: test before doing anything tied to a member.
$owner_idintThe article the comment belongs to.
Return 1
voidThe return is ignored.
Listener
Hook::add('action:client.blog_comment_moderated', 10,
    function ($cid, $action, $actor_id, $author_id, $owner_id) {
        // FOUR values here: the toggle has split in two.
        if ($action === 'approved' && $author_id) Acme::thankAuthor($author_id);
    });

Following a comment being edited

actionclient.blog_comment_edited
ClientBlog the body is encoded

Runs after a comment is edited by an administrator.

Parameters 5
$cidintThe comment id.
$actor_idintThe administrator who edited it.
$author_idintThe author of the comment. It is zero on a guest comment: test before doing anything tied to a member.
$oldstringThe body before the edit. Both arrive encoded: showing them directly turns markup into visible text.
$newstringThe body after the edit, as saved.
Return 1
voidThe return is ignored.
Listener
Hook::add('action:client.blog_comment_edited', 10,
    function ($cid, $actor_id, $author_id, $old, $new) {
        // Both bodies arrive ENCODED.
        Acme::auditEdit($cid, $actor_id, html_entity_decode($old), html_entity_decode($new));
    });

Following a comment being deleted

actionclient.blog_comment_deleted
ClientBlog the replies go too

Runs after a comment is deleted.

Parameters 5
$cidintThe id of the deleted comment; the record is gone.
$actor_idintThe administrator who deleted it.
$author_idintThe author of the comment. It is zero on a guest comment: test before doing anything tied to a member.
$owner_idintThe article the comment belongs to.
$parent_idintThe comment replied to. ? A zero means a root comment went, and its replies went with it — with no separate call for them. Clear the whole branch in your own store.
Return 1
voidThe return is ignored.
Listener
Hook::add('action:client.blog_comment_deleted', 10,
    function ($cid, $actor_id, $author_id, $owner_id, $parent_id) {
        // On a root comment the replies went too, with no separate call.
        if ($parent_id === 0) Acme::dropBranch($cid);
        else Acme::dropComment($cid);
    });

Following a comment vote

actionclient.blog_comment_voted
ClientBlog a toggle

Runs when a comment is marked helpful or the mark is taken back.

Parameters 4
$cidintThe comment voted on.
$viewer_idintThe member voting. This flow is members only, so the value is always filled.
$activeboolThe direction: true for a vote, false for taking it back.
$countintThe current total after the vote.
Return 1
voidThe return is ignored.
Listener
Hook::add('action:client.blog_comment_voted', 10,
    function ($cid, $viewer_id, $active, $count) {
        if ($active && $count >= 10) Acme::promoteComment($cid);
    });

Changing a comment body before it is saved

filterclient.blog_comment_message
ClientBlog raw text

Runs before a comment is saved. The place to put a filter of your own.

Parameters 2
$messagestringby linkThe comment body, raw and not yet encoded. What you write over it is saved.
$ctxarrayby linkContext: the article, the comment replied to, the member id and whether it is a guest. The member id is zero for a guest.
Return 1
voidThe return is ignored; you write over the data.
Listener
Hook::add('filter:client.blog_comment_message', 10, function (&$message, &$ctx) {
    // The body is RAW: encoding happens afterwards.
    $message = Acme::stripLinks($message);
});

Changing the comment list

filterclient.blog_comment_list
ClientBlog an admin sees more

Runs before the comments reach the screen. It runs separately for reply lists too.

Parameters 2
$itemsarrayby linkThe comments to show, with their formatted fields.
$ctxarrayby linkContext: whether this is a reply list, which article, and whether an administrator is looking. ? When an administrator looks, comments awaiting approval are in the list too. A listener ignoring that counts or exports unpublished content.
Return 1
voidThe return is ignored; you write over the data.
Listener
Hook::add('filter:client.blog_comment_list', 10, function (&$items, &$ctx) {
    // When an administrator looks, pending comments are in the list too.
    if ($ctx['is_admin'] ?? false) return;

    $items = Acme::hideFlagged($items);
});

Pitfalls

The gate states the request, the event the result

In the moderation gate pinning is a toggle request: you cannot tell from there whether it ends as pinning or unpinning. In the event the value arrives already split in two. A rule assuming the result at the gate misjudges an unpinning.

Deleting a root comment takes its replies

A zero parent id in the delete event means a root comment went and every reply beneath it went too. But no separate call arrives for those replies: you must clear the whole branch on your side, or orphan records remain.

Was this helpful?

Thanks for your feedback!

Still Need Help?

Our support team is here around the clock for anything you can't find above.