Help with AJAX front end comment moderation

I am implementing a front end AJAX comment moderation system for a site I manage but I am having a hard time getting it to work. Right now clicking on the link that approves the comment runs the proper php script but it reloads the entire page. So it works but I’d like to approve the comment using ajax without reloading the entire page, but my lack of javascript knowledge is preventing me from getting this to work. Here is the code (located in a plugin):

add_filter( 'comment_text', 'p3_comment_moderation_buttons' );
function p3_comment_moderation_buttons ( ) {
    if ( is_author() || current_user_can( 'moderate_comment' ) ) {
        // Adds moderation buttons under every comment
        $comment_id = get_comment_ID();
        $text = get_comment_text();
        $nonce = wp_create_nonce( 'p3_comment_moderation' );

        $p3_approve_link = admin_url('admin-ajax.php?action=p3_comment_approve&comment_id='. $comment_id.'&nonce='.$nonce);

        $p3_edit_links = '<div class="p3-edit-links"><a class="p3-comment-moderation" href="' . $p3_approve_link . '" data-comment_id="' . $comment_id . '" data-nonce="' . $nonce . '">Approve</a></div>';
        return $text . $p3_edit_links;
    }
    else{
        return get_comment_text();
    }
}


add_action("wp_ajax_p3_comment_approve", "p3_comment_approve");
function p3_comment_approve() {
    if ( ! wp_verify_nonce( $_REQUEST['nonce'], 'p3_comment_moderation' ) ) {
        exit("Go away!"); //If nonce check fails stop everything
    }
    $comment_id = $_REQUEST["comment_id"];
    $success = wp_set_comment_status( $comment_id, 'approve' );
    $success = update_comment_meta( $comment_id, 'p3_comment_status', '' );

    if ( $success = true ) {
        $result['type'] = 'success';
        $result['comment_id'] = $_REQUEST["comment_id"];
    }
    else {
        $result['type'] = 'error';
        $result['comment_id'] = $_REQUEST["comment_id"];
        echo "Something didn't work";
    }

    if(!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
        $result = json_encode($result);
        echo $result;
    }
    else {
        header("Location: ".$_SERVER["HTTP_REFERER"]);
    }

    die(); // this is required to return a proper result
}

add_action( 'init', 'p3_comment_meta_script_enqueuer' );
function p3_comment_meta_script_enqueuer() {
    wp_register_script( "p3_comment_meta", plugins_url().'/p3wp-comments/js/p3_comment_meta.js', array('jquery') );
    wp_localize_script( "p3_comment_meta", 'p3cmetaAjax', array( 'ajaxurl' => admin_url( 'admin-ajax.php' )));

    wp_enqueue_script( 'jquery' );
    wp_enqueue_script( 'p3_comment_meta' );
}

And the corresponding javascript:

Read More
jQuery(document).ready( function() {

    jQuery(".p3-comment-moderation").click( function() {
        comment_id = jQuery(this).attr("data-comment_id")
        nonce = jQuery(this).attr("data-nonce")

        jQuery.ajax({
            type : "post",
            dataType : "json",
            url : p3cmetaAjax.ajaxurl,
            data : {action: "p3_comment_meta", comment_id : comment_id, nonce: nonce},
            success: function(response) {
                if(response.type == "success") {
                    alert("Success!")
                }
                else {
                    alert("Something went wrong.")
                }
            }
        })

    })

})

Can anyone spot the problem?

BTW I have already modified the comment template to display unapproved comments so they can be approved on the front end. I also plan to expand this to to work with custom comment meta, and expand the javascript so it does something more useful than displaying an annoying alert

Thanks

Related posts

1 comment

  1. Seems to me that the only problem is that you put the full approve url in the link, in this ways when you click the link you trigger the ajax and open the page url in the link.

    To be more clear your link is something like:

    <a class="p3-comment-moderation" href="admin-ajax.php?action=p3_comment_approve&comment_id=123&nonce=xxxxx" data-comment_id="123" data-nonce="xxxxx">Approve</a>
    

    thanks to the javascript, when you click on the link you launch an ajax request but, there is nothing that prevent the default link behaviour to be triggered, and so the admin-ajax.php is required to the browser, and thanks to the header statement in your code, then the page comes back again to current page.

    So, actually, this is what happen:

    1. the ajax is triggered
    2. admin-ajax.php is open by the url in the a tag
    3. the page is sended again to previous page because of the header("Location: ".$_SERVER["HTTP_REFERER"]) statement in your code

    I know that putting the full url in the link is the right way to make it works even if the user has javascript disabled, but when javascript is enabled you must prevent the default link behaviour, this can be done via the preventDefault() js function.

    So, in your javascript replace

    jQuery(".p3-comment-moderation").click( function() {
    

    with

    jQuery(".p3-comment-moderation").click( function(e) {
        e.preventDefault();
    

    After that everything should work as expected.

    If, like I guess, this is the only problem, this question is a pure javascript one, so off-topic here. However, to make my answer more WordPress relevant I want add some tips (nothing that make the script work or not, just best practice and improvements).

    Tip 1: Escape attributes and links

    When in WordPress you output something in the html output as tag property, best practice is escape it with esc_attr, in general use all esc_* function when applyable

    $p3_edit_links = '<a class="p3-comment-moderation" href="' . $p3_approve_link . '" data-comment_id="' . $comment_id . '" data-nonce="' . $nonce . '">Approve</a>';
    

    is better wrote:

    $p3_edit_links_f = '<a class="p3-comment-moderation" href="%s" data-comment_id="%d" data-nonce="%s">';
    $p3_edit_links = sprintf($p3_edit_links_f, esc_url($p3_approve_link), esc_attr($comment_id), esc_attr($nonce) );
    

    I know that you getting urls and attributes from WP core functions, however once almost all output from core can be filtered is good to apply data validation (although data validation functions can filtered too…)

    Tip 2: use core constant to check ajax requests

    In your code there’s

    if( ! empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
    

    to check if the current request is from ajax. In WordPress when you properly use admin-ajax.php (like you do) you can check ajax request looking for DOING_AJAX constant, so the previous conditional statement becomes:

    if ( defined('DOING_AJAX') && DOING_AJAX ) {
    

    Tip 3: use core function to output json and exit script

    Your code

    $result = json_encode($result);
    echo $result;
    ...
    die();
    

    can be wrote in one line, using the core wp_send_json function

    wp_send_json( $result );
    

    Tip 4: avoid direct superglobals access

    when you have to handle superglobals ($_REQUEST, $_GET, $_POST…) to avoid notices you should always check that the key you are looking for is setted, so your

    if ( ! wp_verify_nonce( $_REQUEST['nonce'], 'p3_comment_moderation' ) ) {
    

    is better wrote:

    if ( ! isset($_REQUEST['nonce']) || ! wp_verify_nonce( $_REQUEST['nonce'], 'p3_comment_moderation' ) ) {
    

    However, is even better to use filter_var instead of accessing superglobals directly

    $nonce = filter_var( INPUT_GET, 'nonce', FILTER_VALIDATE_STRING);
    if ( empty($nonce) ) $nonce = filter_var( INPUT_POST, 'nonce', FILTER_VALIDATE_STRING);
    if ( ! wp_verify_nonce( $nonce, 'p3_comment_moderation' ) ) {
    

Comments are closed.