Skip to main content

Simple Comment Reply Email Notification

This is a simple code snippet to send a notification when his/her comment is replied by someone. Does not need to have a plugin for this. Here is the code.

<?php
/**
 * RR - Email Parent on Comment Reply (v1.4 AOL-safe)
 * PHP 8.1+, WP 6.5+. Works with FluentSMTP.
 */

$rrcpr_cfg = [
    'allowed_post_types'    => [], // e.g. ['post','toolkit']
    'skip_if_same_author'   => true,
    'include_reply_content' => true,
    'include_parent_link'   => true,
    'include_parent_excerpt'=> true,

    'from_email'            => '[email protected]',
    'from_name'             => 'RuhaniRabin.com',

    'subject_tpl'           => '[{site}] New reply to your comment on "{post}" by {replier}',
    'body_intro'            => 'Hi {parent_name}, you received a reply to your comment on β€œ{post}”.',
    'add_reply_to'          => true,

    'enable_global_from_filters' => true,
];

if ( ! function_exists( 'rrcpr_send_parent_reply_email' ) ) :

function rrcpr_send_parent_reply_email( $comment_id ) {
    global $rrcpr_cfg;

    if ( get_comment_meta( $comment_id, '_rrcpr_parent_notified', true ) ) return;

    $comment = get_comment( $comment_id );
    if ( ! $comment || empty( $comment->comment_parent ) ) return;

    $post = get_post( (int) $comment->comment_post_ID );
    if ( ! $post ) return;

    $post_type = get_post_type( $post );
    if ( ! $post_type || ! post_type_supports( $post_type, 'comments' ) ) return;

    if ( ! empty( $rrcpr_cfg['allowed_post_types'] ) && ! in_array( $post_type, $rrcpr_cfg['allowed_post_types'], true ) ) return;

    $parent = get_comment( (int) $comment->comment_parent );
    if ( ! $parent ) return;

    $to = trim( (string) $parent->comment_author_email );
    if ( ! $to || ! is_email( $to ) ) return;

    if ( ! empty( $rrcpr_cfg['skip_if_same_author'] ) ) {
        $reply_email = trim( (string) $comment->comment_author_email );
        if ( $reply_email && strcasecmp( $reply_email, $to ) === 0 ) return;
    }

    $site_name   = wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES );
    $post_title  = get_the_title( $post ) ?: '(no title)';
    $replier     = $comment->comment_author ?: 'Someone';
    $parent_name = $parent->comment_author ?: 'there';

    $reply_link  = get_comment_link( $comment );
    $parent_link = get_comment_link( $parent );

    $subject = trim( wp_strip_all_tags( strtr( (string) $rrcpr_cfg['subject_tpl'], [
        '{site}'    => $site_name,
        '{post}'    => $post_title,
        '{replier}' => $replier,
    ] ) ) );
    if ( $subject === '' ) $subject = sprintf( '[%s] New reply to your comment', $site_name ?: 'Website' );

    $intro = strtr( (string) $rrcpr_cfg['body_intro'], [
        '{parent_name}' => $parent_name,
        '{post}'        => $post_title,
    ] );

    // Very safe HTML only.
    $allowed = [
        'a'      => [ 'href' => [], 'title' => [], 'rel' => [], 'target' => [] ],
        'p'      => [],
        'br'     => [],
        'strong' => [],
        'em'     => [],
        'code'   => [],
        'blockquote' => [],
    ];

    $reply_html = '';
    if ( ! empty( $rrcpr_cfg['include_reply_content'] ) && ! empty( $comment->comment_content ) ) {
        $clean = wp_kses( make_clickable( $comment->comment_content ), $allowed );
        $reply_html = '<p><strong>New reply:</strong></p><blockquote>' . $clean . '</blockquote>';
    }

    $parent_excerpt_html = '';
    if ( ! empty( $rrcpr_cfg['include_parent_excerpt'] ) && ! empty( $parent->comment_content ) ) {
        $excerpt = wp_html_excerpt( wp_strip_all_tags( $parent->comment_content ), 240, '…' );
        $parent_excerpt_html = '<p><strong>Your comment excerpt:</strong></p><blockquote>' . esc_html( $excerpt ) . '</blockquote>';
    }

    // Links duplicated: as anchor and as visible full URL text.
    $reply_anchor = '<a href="' . esc_url( $reply_link ) . '">View the reply</a>';
    $reply_full   = '<p>' . esc_html( $reply_link ) . '</p>';

    $parent_anchor = '';
    $parent_full   = '';
    if ( ! empty( $rrcpr_cfg['include_parent_link'] ) ) {
        $parent_anchor = '<a href="' . esc_url( $parent_link ) . '">View your original comment</a>';
        $parent_full   = '<p>' . esc_html( $parent_link ) . '</p>';
    }

    $message  = '<div style="font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;line-height:1.6;font-size:15px;color:#222">';
    $message .= '<p>' . esc_html( $intro ) . '</p>';
    $message .= $reply_html . $parent_excerpt_html;
    $message .= '<p>' . $reply_anchor . '</p>' . $reply_full;
    if ( $parent_anchor ) {
        $message .= '<p>' . $parent_anchor . '</p>' . $parent_full;
    }
    $message .= '<p style="color:#666;font-size:12px;margin-top:16px">You’re receiving this because someone replied to your comment on ' . esc_html( $site_name ) . '.</p>';
    $message .= '</div>';

    $headers = [ 'Content-Type: text/html; charset=UTF-8' ];

    $from_email = sanitize_email( $rrcpr_cfg['from_email'] );
    $from_name  = sanitize_text_field( $rrcpr_cfg['from_name'] ?: $site_name );
    if ( $from_email ) $headers[] = 'From: ' . $from_name . ' <' . $from_email . '>';

    if ( ! empty( $rrcpr_cfg['add_reply_to'] ) && is_email( $comment->comment_author_email ) ) {
        $headers[] = 'Reply-To: ' . sanitize_text_field( $replier ) . ' <' . sanitize_email( $comment->comment_author_email ) . '>';
    }

    $ct = function() { return 'text/html; charset=UTF-8'; };
    add_filter( 'wp_mail_content_type', $ct );

    $sent = wp_mail(
        apply_filters( 'rrcpr_email_to', $to, $comment, $parent, $post ),
        apply_filters( 'rrcpr_email_subject', $subject, $comment, $parent, $post ),
        apply_filters( 'rrcpr_email_message', $message, $comment, $parent, $post ),
        apply_filters( 'rrcpr_email_headers', $headers, $comment, $parent, $post )
    );

    remove_filter( 'wp_mail_content_type', $ct );

    if ( $sent ) add_comment_meta( $comment_id, '_rrcpr_parent_notified', time(), true );
}

add_action( 'comment_post', function( $comment_id, $approved ) {
    if ( (int) $approved === 1 ) rrcpr_send_parent_reply_email( $comment_id );
}, 10, 2 );

add_action( 'transition_comment_status', function( $new_status, $old_status, $comment ) {
    if ( 'approved' === $new_status && 'approved' !== $old_status && $comment && ! empty( $comment->comment_ID ) ) {
        rrcpr_send_parent_reply_email( (int) $comment->comment_ID );
    }
}, 10, 3 );

// Global From fallback.
if ( ! function_exists( 'rrcpr_mail_from' ) && ! empty( $rrcpr_cfg['enable_global_from_filters'] ) ) {
    function rrcpr_mail_from( $from ) { global $rrcpr_cfg; $c = sanitize_email( $rrcpr_cfg['from_email'] ); return $c ?: $from; }
    function rrcpr_mail_from_name( $name ) { global $rrcpr_cfg; $s = wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ); $c = sanitize_text_field( $rrcpr_cfg['from_name'] ?: $s ); return $c ?: $name; }
    add_filter( 'wp_mail_from', 'rrcpr_mail_from' );
    add_filter( 'wp_mail_from_name', 'rrcpr_mail_from_name' );
}

endif;