AJAX комментарии в WordPress

Итак, комментарии на AJAX. Перед написанием этого поста я потратил добрую половину дня на их разработку — хорошо, что я уже был с ними знаком. В самый первый раз, когда я ещё начинал изучать jQuery, на создание асинхронных комментариев с нуля у меня ушли первые две недели января.

Кстати, я долго думал, имеет ли смысл поставить комментарии со стороннего сервиса, например disqus — но в итоге решил что свои комментарии на AJAX будут покруче.

Требования к AJAX-комментам

Допустим, мы только что решили поставить асинхронные комментарии у себя на сайте. Какую функциональность, на наш взгляд, они должны иметь?

  • самое главное — возможность работы со стандартными древовидными комментариями comment-reply.js,
  • вывод ошибок, в том числе вордпрессовских, особо с этим заморачиваться не буду, сделаю через alert() пока что,
  • проверка комментария на наличие запрещенных HTML-тегов. Читайте о том, как изменить список разрешенных тегов,
  • модерация комментария при необходимости,
  • запоминание в кукисах значений полей «Имя» и «Email»,
  • исключить возможность двойного нажатия на кнопку «Отправить»,
  • не используем никакие плагины WordPress;

Шаг 1. Подготовка. Структура HTML.

Будем отталкиваться от стандартных тем WordPress, это Twenty Ten и Twenty Eleven. Разумеется, то, что представлено в следующем куске кода — всего лишь HTML-шаблон, который должен сориентировать вас по атрибутам class и id, а также по расположению элементов, а вообще все эти штуки делаются через php!

<!-- можно <ul> или <ol> -->
<ol class="commentlist">
    <!-- класс depth-1 показывает уровень вложенности -->
    <li class="comment depth-1" id="li-comment-4">
        <div id="comment-4">
            <!-- тут содержимое комментария -->
            <div class="reply">
                <a class="comment-reply-link">Ответить</a>
            </div>
        </div>
        <!-- дальше идут ответы к предыдущему комментарию  -->
        <ul class="children">
            <!-- depth-2 - значит вложенность второго уровня -->
            <li class="comment depth-2" id="li-comment-5">
                <div id="comment-5">
                    <!-- тут содержимое комментария -->
                    <div class="reply">
                        <a class="comment-reply-link">Ответить</a>
                    </div>
                </div>
            </li>
        </ul>
    </li>
</ol>
<div id="respond">
    <!-- тут нужно обратить внимание также на атрибуты name -->
    <a id="cancel-comment-reply-link" style="display:none;">Отменить ответ</a>
    <form id="commentform">
        <input name="author" id="author" type="text" />
        <input name="email" id="email" type="text" /><!-- или type="email", как хотите -->
        <input name="url" id="url" type="text" />
        <textarea name="comment" id="comment"></textarea>
        <input name="submit" type="submit" id="submit" value="Отправить" />
        <!-- если у вас нет hidden-полей попробуйте вставить их через php-функцию comment_id_fields() -->
        <input type="hidden" name="comment_post_ID" />
        <input type="hidden" name="comment_parent" />
    </form>
</div>

Так что, если у вас что-то добавляется не туда, куда надо, сверяйтесь по этому листингу.

Примерно так это будет выглядеть:

список комментариев с указанием CSS-классов элементов

В код моего блога советую не залазить, у меня там структура немного другая, она может вас только запутать ещё больше. Если что-то не получается или есть вопрос — просто задайте его мне прямо в комментариях к этому посту.

Шаг 2. CSS

Если у вас со структурой всё окей, то стили по сути и не понадобятся, за исключением оформления ошибок, возникающих в результате валидации полей. То есть, если кто-то ввёл неверный адрес email, поле должно стать красного цвета (например).

input.error, textarea.error{
    background: #fe0000; /* или background:red или любой другой цвет, который больше вписывается в дизайн вашего блога */
}

Добавляем этот код в основной файл стилей вашей темы, обычно это — style.css.

Шаг 3. Подключение скриптов

Во-первых, давайте в папке с темой создадим какой-нибудь файл JavaScript, в который мы потом добавим весь наш код. У меня это будет файл comments.js.

Теперь наша задача — правильно подключить библиотеку jQuery и файл comments.js. Для этого воспользуемся функцией wp_enqueue_script().

Следующий код вставляем в файл functions.php вашей текущей темы:

function true_include_my_comment_script() {
    wp_enqueue_script( 'jquery' );
    wp_enqueue_script( 'commentjs', get_stylesheet_directory_uri() . '/comments.js', array('jquery'), null );
}
 
add_action( 'wp_enqueue_scripts', 'true_include_my_comment_script' );

Зайдите в исходный код страницы (в Windows — Ctrl + U) и посмотрите, появился ли там comments.js, если да — переходим к следующему шагу, если нет — открываем файлы header.php и footer.php и убеждаемся, что там присутствуют функции wp_head() и wp_footer() соответственно.

Шаг 4. Скрипты jQuery

Открываем наш файл comments.js и вписываем туда:

jQuery.extend(jQuery.fn, {
    /*
     * функция проверки, что длина поля не меньше 3х символов 
     */
    checka: function () {
        if (jQuery(this).val().length < 3) {jQuery(this).addClass('error');return false} else {jQuery(this).removeClass('error');return true}
    },
    /*
     * функция проверки правильности введенного email
     */
    checke: function () {
        var emailReg = /^([w-.]+@([w-]+.)+[w-]{2,4})?$/;
        var emailaddressVal = jQuery(this).val();
        if (!emailReg.test(emailaddressVal) || emailaddressVal == "") {
            jQuery(this).addClass('error');return false
        } else {
            jQuery(this).removeClass('error');return true
        }
    },
});
 
jQuery(function($){
    $('#commentform').submit(function(){
        // может такое случиться, что пользователь залогинен - нужно это проверить, иначе валидация не пройдет
        if($("#author").length) var author = $("#author").checka();
        if($("#email").length) var email = $("#email").checke();
        var comment = $("#comment").checka();
        // небольшое условие для того, чтобы исключить двойные нажатия на кнопку отправки
        // в это условие также входит валидация полей
        if (!$('#submit').hasClass('loadingform') && !$("#author").hasClass('error') && !$("#email").hasClass('error') && !$("#comment").hasClass('error')){
            $.ajax({
                type : 'POST',
                url : 'http://' + location.host + '/wp-admin/admin-ajax.php',
                data: $(this).serialize() + '&action=ajaxcomments',
                beforeSend: function(xhr){
                    // действие при отправке формы, сразу после нажатия на кнопку #submit 
                    $('#submit').addClass('loadingform').val('Загрузка');
                },
                error: function (request, status, error) {
                    if(status==500){
                        alert('Ошибка при добавлении комментария');
                    } else if(status=='timeout'){
                        alert('Ошибка: Сервер не отвечает, попробуй ещё.');
                    } else {
                        // ворпдрессовские ошибочки, не уверен, что это самый оптимальный вариант
                        // если знаете способ получше - поделитесь
                        var errormsg = request.responseText;
                        var string1 = errormsg.split("<p>");
                        var string2 = string1[1].split("</p>");
                        alert(string2[0]);
                    }
                },
                success: function (newComment) {
                    // Если уже есть какие-то комментарии
                    if($('.commentlist').length > 0){
                        // Если текущий комментарий является ответом
                        if($('#respond').parent().hasClass('comment')){
                            // Если уже есть какие-то ответы
                            if($('#respond').parent().children('.children').length){   
                                $('#respond').parent().children('.children').append(newComment);
                            } else {
                                // Если нет, то добавляем  <ul class="children">
                                newComment = '<ul class="children">'+newComment+'</ul>';
                                $('#respond').parent().append(newComment);
                            }
                            // закрываем форму ответа
                            $("#cancel-comment-reply-link").trigger("click");
                        } else {
                            // обычный коммент
                            $('.commentlist').append(newComment);
                        }
                    }else{
                        // если комментов пока ещё нет, тогда
                        newComment = '<ul class="commentlist">'+newComment+'</ol>';
                        $('#respond').before($(newComment));
                    }
                    // очищаем поле textarea
                    $('#comment').val('');
                },
                complete: function(){
                    // действие, после того, как комментарий был добавлен
                    $('#submit').removeClass('loadingform').val('Отправить');
                }
            });
        }
        return false;
    });
});

Последний шаг. PHP-обработчик

Читайте подробнее о том, как обрабатываются асинхронные запросы в WordPress. Код обработчика:

<?php
// если вы вставляете код не в новый файл, то <?php нужно удалить
function true_add_ajax_comment(){
    global $wpdb;
    $comment_post_ID = isset($_POST['comment_post_ID']) ? (int) $_POST['comment_post_ID'] : 0;
 
    $post = get_post($comment_post_ID);
 
    if ( empty($post->comment_status) ) {
        do_action('comment_id_not_found', $comment_post_ID);
        exit;
    }
 
    $status = get_post_status($post);
 
    $status_obj = get_post_status_object($status);
 
    /*
     * различные проверки комментария
     */
    if ( !comments_open($comment_post_ID) ) {
        do_action('comment_closed', $comment_post_ID);
        wp_die( __('Sorry, comments are closed for this item.') );
    } elseif ( 'trash' == $status ) {
        do_action('comment_on_trash', $comment_post_ID);
        exit;
    } elseif ( !$status_obj->public && !$status_obj->private ) {
        do_action('comment_on_draft', $comment_post_ID);
        exit;
    } elseif ( post_password_required($comment_post_ID) ) {
        do_action('comment_on_password_protected', $comment_post_ID);
        exit;
    } else {
        do_action('pre_comment_on_post', $comment_post_ID);
    }
 
    $comment_author       = ( isset($_POST['author']) )  ? trim(strip_tags($_POST['author'])) : null;
    $comment_author_email = ( isset($_POST['email']) )   ? trim($_POST['email']) : null;
    $comment_author_url   = ( isset($_POST['url']) )     ? trim($_POST['url']) : null;
    $comment_content      = ( isset($_POST['comment']) ) ? trim($_POST['comment']) : null;
 
    /* 
     * проверяем, залогинен ли пользователь
     */
    $user = wp_get_current_user();
    if ( $user->exists() ) {
        if ( empty( $user->display_name ) )
            $user->display_name=$user->user_login;
        $comment_author       = $wpdb->escape($user->display_name);
        $comment_author_email = $wpdb->escape($user->user_email);
        $comment_author_url   = $wpdb->escape($user->user_url);
        $user_ID = get_current_user_id();
        if ( current_user_can('unfiltered_html') ) {
            if ( wp_create_nonce('unfiltered-html-comment_' . $comment_post_ID) != $_POST['_wp_unfiltered_html_comment'] ) {
                kses_remove_filters(); // start with a clean slate
                kses_init_filters(); // set up the filters
            }
        }
    } else {
        if ( get_option('comment_registration') || 'private' == $status )
            wp_die( 'Вы должны зарегистрироваться или войти, чтобы оставлять комментарии.' );
    }
 
    $comment_type = '';
 
    /* 
     * проверяем, заполнил ли пользователь все необходимые поля
     */
    if ( get_option('require_name_email') && !$user->exists() ) {
        if ( 6 > strlen($comment_author_email) || '' == $comment_author )
            wp_die( 'Ошибка: заполните необходимые поля (Имя, Email).' );
        elseif ( !is_email($comment_author_email))
            wp_die( 'Ошибка: введенный вами email некорректный.' );
    }
 
    if ( '' == trim($comment_content) ||  '<p><br></p>' == $comment_content )
        wp_die( 'Вы забыли про комментарий.' );
 
    /* 
     * добавляем новый коммент и сразу же обращаемся к нему
     */
    $comment_parent = isset($_POST['comment_parent']) ? absint($_POST['comment_parent']) : 0;
    $commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_content', 'comment_type', 'comment_parent', 'user_ID');
    $comment_id = wp_new_comment( $commentdata );
    $comment = get_comment($comment_id);
 
    /*
     * выставляем кукисы
     */
    do_action('set_comment_cookies', $comment, $user);
 
    /*
     * вложенность комментариев
     */
    $comment_depth = 1;
    while($comment_parent){
        $comment_depth++;
        $parent_comment = get_comment($comment_parent);
        $comment_parent = $parent_comment->comment_parent;
    }
 
    $GLOBALS['comment'] = $comment;
    $GLOBALS['comment_depth'] = $comment_depth;
    /*
     * ниже идет шаблон нового комментария, вы можете настроить его для себя,
     * а можете воспользоваться функцией(которая скорее всего уже есть в теме) для его вывода
     */
    ?>
    <li <?php comment_class(); ?> id="li-comment-<?php comment_ID(); ?>">
        <div id="comment-<?php comment_ID(); ?>">
            <div class="comment-author vcard">
                <?php echo get_avatar( $comment, 40 ); ?>
                <cite class="fn"><?php echo get_comment_author_link(); ?></cite>
            </div>
            <?php if ( $comment->comment_approved == '0' ) : ?>
                <em class="comment-awaiting-moderation">Комментарий отправлен на проверку</em>
                <br />
            <?php endif; ?>
            <div class="comment-meta commentmetadata"><a href="<?php echo esc_url( get_comment_link( $comment->comment_ID ) ); ?>">
                <?php printf('%1$s в %2$s', get_comment_date(),  get_comment_time() ); ?></a><?php edit_comment_link('ред.', ' ' );  ?>
            </div>
            <div class="comment-body"><?php comment_text(); ?></div>
        </div>
    </li>
    <?php
    die();
}
 
add_action('wp_ajax_ajaxcomments', 'true_add_ajax_comment'); // wp_ajax_{значение параметра action}
add_action('wp_ajax_nopriv_ajaxcomments', 'true_add_ajax_comment'); // wp_ajax_nopriv_{значение параметра action}

Комменты можете протестировать прямо на моем блоге, там совсем незначительные изменения в коде, также я протестировал весь код из статьи ничего не меняя, как есть, на теме Twenty Ten.

Со временем пост будет обновляться по мере совершенствования кода в нем, после появления каких-либо доработок.

Также есть доработки, которые я не буду добавлять, дабы не усложнять пост, но вам стоит попробовать сделать их самим:

  • При добавлении нового комментария неплохо также обновлять цифру с количеством комментариев, которую обычно можно найти рядом с датой публикацией поста и в заголовке непосредственно перед самими комментами,
  • Вы можете оформить ошибки в виде красивых всплывающих окон или в виде подсказок,
  • Очень здорово, если при нажатии на кнопку отправки, на ней будет появляться анимация-прелоадер, как у меня на блоге.

Источник: misha.blog

Миша Рудрастых

Путешествует по миру и рассказывает всем о WordPress лично, у себя в блогах и на курсах в Санкт-Петербурге. Умеет просто объяснять сложные вещи, делает это красиво. Организовывает неплохие WordCamp's, но совсем не умеет слушать чужие доклады.

Добавить комментарий

%d такие блоггеры, как: