ajaxfy_comment

プラグインに頼らずテーマ内で完結するAJAXコメントフォーム。

前回に引き続き、コメントの周りのカスタマイズ。

一昔前にQuick Commentsというのがあって、いまはAjax CommentsやWP Ajaxify Commentsというのもあって、でも環境依存の問題か上手く動作しなかったり、もっさりしていたりで、いくつもある他のプラグインの動作確認をやるのもしんどいしで、どうにも…あ゛ーって感じになったので、自作してみたら意外とすんなりまとまった、と。

※2015/2/25 …. undefinedを区別するようにjsを修正しました。このコードはAJAXを使用しているので、htmlタグが誤っていたり、複雑な構造になっている場合、あるいは他のjs系プラグインを使用している場合、干渉しあって期待通りの動作をしないこともあります。十分に注意してお使いください。

 

CODE: Ajaxify WordPress comments

<?php
// コメントフォームのAJAX化
function ajaxify_comments_script(){
if ( is_singular() && comments_open() )://固定や投稿等シングルページで動作、場所によっては要変更
?>
<script id="comment-script" type="text/javascript">
jQuery(function( $ ){

    var commentbox_name = '#comments', //コメントテンプレート名、カスタマイズ時は要変更
        commentform_name = '#commentform', //コメントフォーム名、カスタマイズ時は要変更
        commentbox = $( commentbox_name ),
        commentform = $( commentform_name, commentbox );
    ajaxCommentsTrigger();

    function ajaxComments( this_form ){
        var statusdiv = $('<div class="comment-ajax"></div>'); // AJAXの結果を吹き出す窓、デザインはCSSで指定してね
        this_form.append( statusdiv ).submit( function (e) {
            var formdata = this_form.serialize(), // フォームをシリアライズ
                formurl = this_form.attr('action'); // フォームのactionに設定されたurl
            e.preventDefault();
            statusdiv.html('<div class="ajax-processing">コメントを送信中…</div>'); // 送信されると.comment-ajax内に表示
            $.ajax({ 
                type: 'post',
                url: formurl,
                data: formdata
            }).done(function(data,status){
                if( status == "success" ){
                    var matches = data.match(/<body+s+id="error-page">([sS]*?)</body>/);
                    if ( matches ) {
                        var output = $('<div class="ajax-error">' + matches[1] + '</div>');
                        output.find('*:empty').remove();
                        output.find('*:first-child').siblings().remove();
                        statusdiv.html( output );
                        return false;
                    }
                    var newlist = $( data ).find( commentbox_name );
                    if ( newlist.length ){ 
                        statusdiv.html('<div class="ajax-success">コメントありがとうございました。</div>');
                        reply = this_form.parents( 'li.depth-1' ).attr( 'id' );
                        if ( typeof reply !== "undefined" ) {
                            window.location.hash = reply;
                            statusdiv.find( '.ajax-success' ).stop().delay( 500 ).slideUp( 200,function(){
                                commentbox.replaceWith( newlist );
                                commentbox = $( commentbox_name );
                                ajaxCommentsTrigger();
                                $('html, body').animate({
                                    scrollTop: $( '#' + reply ).offset().top
                                }, 500);
                            });
                        }
                    } else {
                        statusdiv.html('<div class="ajax-error">エラーが発生しました。ウェブマスターにご連絡ください。</div>');
                        return false;
                    }
                } else {
                    statusdiv.html('<div class="ajax-error">エラーが発生しました。時間をおいて再度送信して下さい。</div>');
                    return false;
                }
            }).fail(function(data){
                statusdiv.html('<div class="ajax-error">送信が中断されました。<strong>記入もれや間違いがないか</strong>再度ご確認ください。</div>');
            });
        });
    }

    function ajaxCommentsTrigger(){
        var replybox = $( '<div id="reply-box"></div>' ),
            commentform = $( commentform_name, commentbox );
        commentform.clone( true ).appendTo( replybox );
        replybox.find( '[id]' ).each(function(){
            $(this).prop('id', 'reply-' + $(this).attr( 'id' ) );
        });
        ajaxComments( commentform );
        ajaxComments( replybox.children( 'form' ) );
        $( 'a.comment-reply-link', commentbox ).click(function(e){ 
            e.preventDefault();
            var replylink = $(this), list = replylink.parents( 'li.depth-1' );
            if ( list.hasClass( 'replying' ) ){
                list.removeClass( 'replying' );
                replylink.html( '返信する' );
                replybox.slideUp( 500 );
            } else {
                list.addClass( 'replying' );
                list.siblings('.replying').removeClass( 'replying' ).find( 'a.comment-reply-link').html( '返信する' );
                replylink.html( '返信をキャンセル' );
                var parentid = list.attr('id').slice(8); // ex:#comment-999
                replybox.find( '#reply-comment_parent' ).val( parentid );
                replybox.hide().appendTo( list ).slideDown( 500 );
            }
        });
    }
});
</script>
<?php
endif;
}
add_action('wp_footer', 'ajaxify_comments_script');
?>

このサンプルコードではfunctions.phpに記載することを前提にwp_footerにフックしてますが、jQueryだけで完結してるんでjs部分をテンプレートに直書きしてもOKです。動作はここのフォームでコメントなしで送信ボタンを押せば、エラーが返ってきますのでどうぞ。ちなみに前記事「Really Simple CAPTCHAをコメント欄に応用する。」も併用できます。

 

どうやってヴァリデートするか、が肝なのかも。

ajaxでpostするところまではどのプラグインでも動作は大体同じ。コメントのヴァリデーション方法については作者毎に個性があるが、comment_postにフックしてajax時のみ処理を分岐させて云々…というのが常道のようだった。それで良いのだけども、結局、コメント投稿に失敗したときに返ってくるエラーページを読み込んでPOSTの可否を判断することにした。htmlソースで判断することの是非はあるかもしれないが、フック処理が不要になるしwordpressのエラーメッセージを流用できるので、関数をシンプルに保てて、保安上もメリットになると思えたからだ。

もうひとつの大きな設計判断としては、コメント後にユーザーにだけ見えるモデレート待ち のコメントを表示する方法をどうするかだけども、これはコメントが通過後に返ってくる新しいポストから#commentsタグを抽出して丸ごと差し替えること にした。キャプチャを使用している場合にはその更新が必要だったり、コメントテンプレートのデザインやソートがテーマ毎にまちまちだったりするので、コメントテンンプレートをまるごと差し替えるのが現実的だろうというわけです。

おまけ:ここで適用してるcss

ajaxは思いの外時間がかかることもあるので、css 3のアニメーションなどを活用してユーザーを迷わせないよう設計することが大事ですね。

.comment-ajax { margin:5px 0; }
.comment-ajax > div { padding:5px 10px; font-size:16px; }
.ajax-error { background:#356; color:#fff; }
.ajax-success { background:#2bb; color:#fff; } 
.ajax-processing { position:relative; background:#eee; } 
.ajax-processing:before { width:100%; display:block; content:' '; margin:0; padding:0; background:#a9e3e4; position:absolute; height:3px; left:0; bottom:0; -webkit-animation:fullexpand 1.5s ease-out; z-index:1; animation:fullexpand 1.5s ease-out; }
@-webkit-keyframes fullexpand { 0%  { width:0;} 100%{ width:100%;} }
@keyframes fullexpand { 0% { width:0;} 100%{ width:100%;} }

Really Simple CAPTCHAをコメント欄に応用する

WordPressのスパムコメント、スパムメールは結構困り者で、キャプチャ(画像認証)を導入するケースも多いと思われる。ところで、Contact Form 7には公式モジュールとしてReally Simple CAPTCHAがあり、名前のとおり軽量でデザイン性も高くありながら、大抵のブログにとって必要充分な機能をもたらしてくれるという優れもの。一方でコメントスパム防止に導入できるキャプチャプラグインも幾つかあるわけだが、Contact Form 7を使っているなら、デザインの整合性からいってこれが流用できるとプラグイン構成の簡素化もできてうれしいのでやってみた、という記事です。

 

CODE:using Really Simple CAPTCHA for wordpress comments

以下、コメントフォームへの挿入とヴァリデーションを有効化するコードです。functions.phpに記入、my_comment_captcha以下のフォームの記述は、テーマに合わせて変更します。Contact Form 7での設置の仕方は、公式でどうぞ。

<?php
// コメント欄にアンチスパム・キャプチャを追加
// contact-form7用プラグイン Really Simple CAPTCHA をインストール済の場合だけ有効
if ( !is_user_logged_in() && class_exists('ReallySimpleCaptcha') ) :

function my_comment_captcha() {
    $captcha = new ReallySimpleCaptcha();
    // 以下キャプチャのサイズや色をカスタマイズ
    // $captcha->img_size = array( '72', '24' ); //画像サイズ
    // $captcha->fg = array( '0', '0', '0' ); // 文字色
    // $captcha->bg = array( '255', '255', '255' ); // 画像背景色
    $captcha_word = $captcha->generate_random_word();
    $captcha_prefix = mt_rand();
    $captcha_src = $captcha->generate_image($captcha_prefix, $captcha_word); // Generate CAPTCHA image
?>
<p class="comment-form-captcha">
    <label for="captcha_code">キャプチャ *</label>
    <img src="<?php echo site_url(). '/wp-content/plugins/really-simple-captcha/tmp/' . $captcha_src; ?>" alt="captcha" width="<?php echo $captcha->img_size[0]; ?>" height="<?php echo $captcha->img_size[1]; ?>" />
    <input id="comment_captcha_code" name="comment_captcha_code" size="<?php echo $captcha->char_length; ?>" type="text" />
    <input id="comment_captcha_prefix" name="comment_captcha_prefix" type="hidden" value="<?php echo $captcha_prefix; ?>" />
</p>
<?php
}
add_action( 'comment_form_after_fields' , 'my_comment_captcha' );//#url.inputと#comment.textareaの間に挿入

function my_check_comment_captcha( $approved, $comment_data ) {
    $captcha = new ReallySimpleCaptcha();
    $captcha_prefix = $_POST['comment_captcha_prefix']; // ユーザー入力によるキャプチャレスポンス
    $captcha_code = $_POST['comment_captcha_code']; // ヴァリデーション
    $captcha_correct = $captcha->check( $captcha_prefix, $captcha_code ); // ヴァリデーションチェックが通ると[true]を返す
    if ( !$captcha_correct ) wp_die('キャプチャコードが間違っています。再入力してください。', 200); //エラーコード200を設定すると、他のコメントヴァリデートと同じ挙動になる
    $captcha->remove( $captcha_prefix );
    $captcha->cleanup();
    return $approved;
}
add_filter( 'pre_comment_approved', 'my_check_comment_captcha', 99, 2 );

endif; // if ( class_exists('ReallySimpleCaptcha') ):
?>

 

関連リンクとダウンロード

sc 2015-03-28 14.45.32

実はこれ結構古いネタで、アンチョコは2010年の「Using Really Simple CAPTCHA Plugin for Comments」というやつです。いつの間にかこの記事の筆者はプラグイン化したみたいで、それは試してません。ちょっと弄れば短くなるからfunctions.phpでもええんちゃうかなー、どうせコメント周りの関数いくつかあるしなー、ってことで今にいたります。ヴァリデーション前後の動作も改変してるのでほとんど別ものですが。コンタクトフォームは数あれど、やっぱContact Form 7が好きだなー、という私のような人にどうぞ。

本家サイト:Really Simple CAPTCHA / contact form 7

276_k

コメント編集ページに親コメントを設定できるメタボックスを設置する。

コメントの返信先が間違えている、コメントツリーがバラけて読みにくい、なんてことがある。どうでも良いことかもしれないが、私は気になる。要はコメント編集ページに親コメントID(コメントメタ内のcomment_parent)の入力欄を設置すれば良いのですが、それだけでは直感的に分かりにくいので、セレクトボックス化してアシストします。

sc 2014-12-05 23.30.06

 

 CODE: Create a dropdown list of comment_parent in a comment edit page.

作っては見たものの、やはりコメントスレッドが奇麗に並んでないことが気になるごく一部の人を除いて、必要のない機能ですね。レビューなどを活用されているCMSだと使えるかもしれません。
functions.phpに以下のコードを貼付けます。

function comment_parent_metabox(){
    add_meta_box( 'comment_parent', '親コメント', 'comment_parent_select', 'comment', 'normal', 'high' );
}
add_action( 'add_meta_boxes_comment', 'comment_parent_metabox' );
    
function comment_parent_select( $comment ){
    wp_nonce_field( 'comment_parent_update', 'comment_parent_update', false );
    $parent = $comment->comment_parent;
    $self_id = $comment->comment_ID;
    echo '<select id="comment_parent_select" name="comment_parent">'."n";
    $siblings = get_comments( 'post_id='.$comment->comment_post_ID );
    $selected ='';
    if($siblings): 
        if ( empty ( $parent ) ) : $selected = 'selected="selected"'; endif;
        echo '<option value="0" '.$selected.'>親コメントなし</option>'."n";
        foreach( $siblings as $sib ) :
            if ( $parent == $sib->comment_ID ) $selected = 'selected="selected"'; else $selected ='';
            if ( $sib->comment_ID != $self_id ) echo '<option id="comment-id-'.$sib->comment_ID.'" class="comment-parent" value="'.$sib->comment_ID.'" '.$selected.'>'.$sib->comment_author.' (id:'.$sib->comment_ID.' / '.$sib->comment_date.')【'.mb_substr( strip_tags($sib->comment_content), 0, 20). '...】</option>'."n";
        endforeach; 
    endif;
    echo '</select>'."n";
}
 
function comment_parent_edit( $comment_id ){
    if( !isset( $_POST['comment_parent_update'] ) || !wp_verify_nonce( $_POST['comment_parent_update'], 'comment_parent_update' ) ) return;
    if( isset( $_POST['comment_parent'] ) )
        update_comment_meta( $comment_id, 'comment_parent', esc_attr( $_POST['comment_parent'] ) );
}
add_action( 'edit_comment', 'comment_parent_edit' );