Skip to content

Instantly share code, notes, and snippets.

@lkwdwrd
Last active September 15, 2021 15:15
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save lkwdwrd/10690612 to your computer and use it in GitHub Desktop.
Save lkwdwrd/10690612 to your computer and use it in GitHub Desktop.
Ajax Comment Fun with WordPress JS Helpers
<?php // Templates for ajax comments ?>
<?php /* Wrap for comments in general should none be present */ ?>
<script type="text/html" id="tmpl-comment-wrap">
<div class="comments">
<h2 class="comments-title">Comments</h2>
<ol class="commentlist"></ol>
</div>
</script>
<?php /* Markup for a single comment when inserted into the DOM */ ?>
<script type="text/html" id="tmpl-comment-single">
<li class="{{data.comment_class}}" id="li-comment-{{data.comment_ID}}">
<div id="comment-{{data.comment_ID}}" class="comment">
<div class="comment-meta comment-author vcard">
{{{data.gravatar}}}
<div class="comment-meta-content">
<cite class="fn">
<# if ( data.comment_author_url ) { #>
<a href="{{data.comment_author_url}}" rel="nofollow" class="url">
<# } #>
{{data.comment_author}}
<# if ( data.comment_author_url ) { #>
</a>
<# } #>
</cite>
<p>
<a href="<?php the_permalink(); ?>#comment-{{data.comment_ID}}">
{{data.date_formatted}} at {{data.time_formatted}}
</a>
</p>
</div> <!-- /comment-meta-content -->
</div> <!-- /comment-meta -->
<div class="comment-content post-content">
<# if ( "1" !== data.comment_approved ) { #>
<p class="comment-awaiting-moderation"><?php _e( 'Awaiting moderation', 'wilson' ); ?></p>
<# } #>
{{{data.content_formatted}}}
</div><!-- /comment-content -->
</div><!-- /comment-## -->
</li>
<!-- #comment-## -->
</script>
//javascript
//wp-content/plugins/lw-comment-fun/js/comments.js
(function( $, window, undefined ){
// Define needed vars in the closure.
var wp = window.wp,
$commentForm = $( "#commentform" ),
$commentWrap = $( ".comments .commentlist" ),
$error = false, $comment = false,
nonce = window.lsCommentFormNonce, //passed from PHP
wrapTemplate = wp.template( 'comment-wrap' ),
singleTemplate = wp.template( 'comment-single' );
// If we didn't get the comment wrap, lets leave ourselves a clue.
if ( 0 === $commentWrap.length ) {
$commentWrap = false;
}
/**
* Handles a successful comment submission via Ajax and displays it.
*
* @param {object} data The comment object that was created
* @return {void}
*/
function commentSuccess( data ){
// Template the comment and unlock submissions.
$comment.before( singleTemplate( data ) );
window.Prism.highlightAll();
$comment.remove();
$comment = false;
// Reset the form.
$commentForm.get(0).reset();
}
/**
* Handles a processing error in comment submission outputting it for the user.
*
* @param {string} data The error message generated.
* @return {void}
*/
function commentError( data ){
// Print the error in the current pending comment li.
$error = $comment;
$error.html( data );
// Unlock comment submissions.
$comment = false;
}
/**
* Submits a comment via ajax when the form submit event fires.
*
* @param {object} event The submit event object.
* @return {void}
*/
function submitComment( event ) {
// Stop the form from submitting normally and bail if we're processing a comment.
event.preventDefault();
if ( $comment !== false ) {
return;
}
// Go get the form data as an object.
var formData = _serialToObject( $commentForm.serializeArray() );
formData.comment = wp.shortcode.replace( 'code', formData.comment, processCodeBlocks );
// Remove the error message if one is currently displaying.
if ( $error ) {
$error.remove();
$error = false;
}
// Create the comment list item.
$comment = $( '<li />' ).text( 'Processing Comment...' );
// If we don't have a comment wrap, make one!
if( ! $commentWrap ) {
$('#respond').before( wrapTemplate() );
$commentWrap = $( ".comments .commentlist" );
}
// Insert the comment list item.
$commentWrap.append( $comment );
// Send the form via ajax
wp.ajax.send( "lw_submit_comment", {
success: commentSuccess,
error: commentError,
data: {
nonce: nonce,
comment: formData
}
});
}
/**
* A simple method to turn a serialized array into an object.
*
* @param {array} serialized The serialized form array.
* @return {object} The array transformed into an object.
*/
function _serialToObject( serialized ){
// Empty object to fill up with form values
var object = {};
// Declare the each callback so it"s not compiled for every iteration.
function _serializeEachCallback() {
// if we have this key already, fill the array
if ( object[ this.name ] !== undefined ) {
if ( ! object[ this.name ].push ) {
object[ this.name ] = [ object[ this.name ] ];
}
object[this.name].push( this.value || "" );
} else {
// We don"t have this object yet, add it to the object.
object[this.name] = this.value || "";
}
}
// Iterate through each item of the serialized array.
$.each( serialized, _serializeEachCallback );
// Return the final object.
return object;
}
/**
* Processes shortcode blocks into HTML codeblocks for highlighting.
*
* @param {object} shortcode The wp.shortcode object matched.
* @return {string} The replacement string for the shortcode.
*/
function processCodeBlocks( shortcode ) {
// If this codeblock has no code, strip it out.
if ( ! shortcode.content ) {
return ' ';
}
// Set up the codeblock with the parsed shortcode attrs.
return wp.html.string({
tag: 'pre',
content: {
tag: 'code',
attrs: {
class: 'language-' + shortcode.get( 'language' )
},
content: window._.escape( shortcode.content.trim() )
}
});
}
// Grab a reference to the comment form and take over submissions.
$commentForm.on( "submit", submitComment );
})( jQuery, this );
<?php
/**
* Initializes the comment fun by adding some hooks and enqueueing JS.
*
* @return void.
*/
function lw_comment_fun_init() {
//Don't do anything if we don't have comments to work with
if ( ! is_singular() || ! comments_open() ) {
return;
}
// Add our comments templates
add_action( 'wp_footer', 'lw_comment_templates' );
// Enqueue your script file
wp_enqueue_script(
"lw-comments",
get_stylesheet_directory_uri() . "/assets/js/comments.js",
array( "wp-util", "shortcode" ),
"1.0.0",
true //makes sure this is enqueued in the footer
);
// Print out a nonce so we can verify this request.
wp_localize_script( "lw-comments", "lsCommentFormNonce", wp_create_nonce( 'ls-comment-fun!' ) );
}
// Run on the wp action so we can check is_singular
add_action( "wp", "lw_comment_fun_init" );
// Make our lives easier an don't thread comments.
add_filter( "pre_option_thread_comments", "__return_zero" );
/**
* Outputs the templates.
*
* @return void.
*/
function lw_comment_templates() {
include_once get_stylesheet_directory() . "/templates/comments-templates.php";
}
/**
* Handle a request to the admin-ajax processor for comments.
*
* @return void. This outputs via wp_send_json.
*/
function lw_ajax_comment() {
// Make sure we've got data.
$nonce = isset( $_POST['nonce'] ) ? $_POST['nonce'] : '';
$comment = isset( $_POST['comment'] ) ? $_POST['comment'] : array();
//Verify the nonce and that we have a comment.
if( wp_verify_nonce( $nonce, 'ls-comment-fun!' ) && ! empty( $comment ) ) {
// Verify and convert the data to the needed format.
$comment_data = lw_process_comment_data( $comment );
if ( ! is_array( $comment_data ) ) {
wp_send_json_error( $comment_data );
} else {
// This just dies, so we'll make it work with our system.
add_action( 'comment_duplicate_trigger', 'lw_send_comment_dup_message' );
// Create a new comment with the sent data.
$comment_id = wp_new_comment( $comment_data );
}
} else {
// If we're here something went wrong.
wp_send_json_error( 'This came from the wrong place' );
}
// Get the comment object ready for JS.
$comment_obj = lw_prepare_comment_for_js( $comment_id );
// Send the comment object back to our Javascript.
wp_send_json_success( $comment_obj );
}
// Comments can be submitted without being logged in, so
// we'll add both wp_ajax_ and wp_ajax_nopriv_ actions.
add_action( 'wp_ajax_lw_submit_comment', 'lw_ajax_comment' );
add_action( 'wp_ajax_nopriv_lw_submit_comment', 'lw_ajax_comment' );
/**
* Helper to fix the error sent on duplicate comments.
*
* @return void. Sends it's error back with wp_send_json
*/
function lw_send_comment_dup_message() {
wp_send_json_error( __('Duplicate comment detected; it looks as though you&#8217;ve already said that!') );
}
/**
* Processes and validates raw comment data into something usable with wp_new_comment.
*
* Much of this is duplicated in wp-comments-post.php
*
* On error we go ahead and use wp_send_json_error to bail.
*
* @param array $comment The raw comment data.
* @return array The validated and converted data.
*/
function lw_process_comment_data( $comment ) {
$comment_post_ID = ( isset($comment['comment_post_ID']) ) ? (int) $comment['comment_post_ID'] : 0;
$comment_author = ( isset($comment['author']) ) ? trim(strip_tags($comment['author'])) : null;
$comment_author_email = ( isset($comment['email']) ) ? trim($comment['email']) : null;
$comment_author_url = ( isset($comment['url']) ) ? trim($comment['url']) : null;
$comment_content = ( isset($comment['comment']) ) ? trim($comment['comment']) : null;
// If the user is logged in
$user = wp_get_current_user();
if ( $user->exists() ) {
if ( empty( $user->display_name ) )
$user->display_name=$user->user_login;
$comment_author = wp_slash( $user->display_name );
$comment_author_email = wp_slash( $user->user_email );
$comment_author_url = wp_slash( $user->user_url );
if ( current_user_can( 'unfiltered_html' ) ) {
if ( ! isset( $comment['_wp_unfiltered_html_comment'] )
|| ! wp_verify_nonce( $comment['_wp_unfiltered_html_comment'], 'unfiltered-html-comment_' . $comment_post_ID )
) {
kses_remove_filters(); // start with a clean slate
kses_init_filters(); // set up the filters
}
}
} else {
if ( get_option('comment_registration') || 'private' == $status )
return __('Sorry, you must be logged in to post a comment.');
}
$comment_type = '';
if ( get_option('require_name_email') && !$user->exists() ) {
if ( 6 > strlen($comment_author_email) || '' == $comment_author )
return __('<strong>ERROR</strong>: please fill the required fields (name, email).');
elseif ( !is_email($comment_author_email))
return __('<strong>ERROR</strong>: please enter a valid email address.');
}
if ( '' == $comment_content )
return __('<strong>ERROR</strong>: please type a comment.');
return compact('comment_post_ID', 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_content', 'comment_type', 'user_ID');
}
/**
* Based on comment ID, prepares a comment object for our JS templates.
*
* We don't have everything we need to display a comment in the raw comment
* object, so we'll add that to it and then send it back.
* @param int $comment_id The comment ID of the desired comment.
* @return object The comment object ready for sending back to JS.
*/
function lw_prepare_comment_for_js( $comment_id ) {
$comment_obj = get_comment( $comment_id );
if ( is_null( $comment_obj ) ) {
wp_send_json_error( 'Something went wrong.' );
} else {
$comment_obj->content_formatted = apply_filters( 'comment_text', $comment_obj->comment_content, $comment_obj, array() );
$comment_obj->gravatar = get_avatar( $comment_obj, 120 );
$comment_obj->date_formatted = get_comment_date( '', $comment_id );
$comment_obj->time_formatted = mysql2date( get_option('time_format'), $comment_obj->comment_date, true );
$comment_obj->comment_class = join( ' ', get_comment_class( '', $comment_id, $comment_obj->post_id ) );
}
return $comment_obj;
}
/**
* Filters the allowed markup in comments so code class and pre tags work.
* @param array $allowed_tags The array of allowed tags for comments.
* @param string $context The context for this run through kses.
* @return array The array of allowed tags updated as needed.
*/
function lw_comment_codeblocks( $allowed_tags, $context ) {
if ( 'pre_comment_content' === $context ) {
$allowed_tags['code'] = array( 'class' => true );
$allowed_tags['pre'] = array();
}
return $allowed_tags;
}
add_filter( 'wp_kses_allowed_html', 'lw_comment_codeblocks', 10, 2 );
/**
* Adds notes to the comment for for shortcode style codeblocks.
* @param array $defaults The defaults of the comment form.
* @return array The defaults of the comment form, updated with our output.
*/
function lw_comment_codeblocks_notes( $defaults ){
$note = '<p class="form-allowed-tags">';
$note .= 'You may also print out highlighted code blocks with <code>[code language=""]{{code goes here}}[/code]</code>. ';
$note .= 'Supported languages are "markup" (HTML), "css", "javascript", and "php"';
$note .= '</p>';
$defaults['comment_notes_after'] .= $note;
return $defaults;
}
add_filter( 'comment_form_defaults', 'lw_comment_codeblocks_notes' );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment