Last active
September 15, 2021 15:15
-
-
Save lkwdwrd/10690612 to your computer and use it in GitHub Desktop.
Ajax Comment Fun with WordPress JS Helpers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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 ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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’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