This week I had a client request a modification to an existing WordPress page template which already had two post anchor identifiers to retrieve get_the_author_meta and get_the_date. It was a custom template that displays a CPT by category to a page.
You can see the example below of the get requests:
So I found this repository on Github by Jon Masters and decided to implement it, but modifying the custom post type template instead of the standard blog page template.
Jon gives walkthrough instructions, but since I modified it for a custom post type template I will do my own walkthrough, in case you are looking to modify a CPT template as well.
1. Run Query Monitor to find the template file you are after
If you install the Query Monitor plugin, you can click on ‘templates’ to see which WordPress template file is being called in. It’s not perfect though. In this case, it showed me a template hierarchy of 5 template files, none of which contained the section I wished to modify. If this happens, then you can just dig around through the template files and if the naming convention is obvious you shouldn’t have too much trouble. In this case I was editing the header-insight.php
2. open your functions file and add the post-like.php file code
Ok, this will make your functions file enormously long, but since Jon has shared the code I am going with what he as built for us already. Of course you can refactor that code into a post-like.php template file and call it into your functions file, but let’s keep it simple and add the post-like.php into functions.php like so:
<?php | |
/* | |
Name: WordPress Post Like System | |
Description: A simple and efficient post like system for WordPress. | |
Version: 0.5.2 | |
Author: Jon Masterson | |
Author URI: http://jonmasterson.com/ | |
License: | |
Copyright (C) 2015 Jon Masterson | |
This program is free software: you can redistribute it and/or modify | |
it under the terms of the GNU General Public License as published by | |
the Free Software Foundation, either version 3 of the License, or | |
(at your option) any later version. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License | |
along with this program. If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
/** | |
* Register the stylesheets for the public-facing side of the site. | |
* @since 0.5 | |
*/ | |
add_action( 'wp_enqueue_scripts', 'sl_enqueue_scripts' ); | |
function sl_enqueue_scripts() { | |
wp_enqueue_script( 'simple-likes-public-js', get_template_directory_uri() . '/js/simple-likes-public.js', array( 'jquery' ), '0.5', false ); | |
wp_localize_script( 'simple-likes-public-js', 'simpleLikes', array( | |
'ajaxurl' => admin_url( 'admin-ajax.php' ), | |
'like' => __( 'Like', 'YourThemeTextDomain' ), | |
'unlike' => __( 'Unlike', 'YourThemeTextDomain' ) | |
) ); | |
} | |
/** | |
* Processes like/unlike | |
* @since 0.5 | |
*/ | |
add_action( 'wp_ajax_nopriv_process_simple_like', 'process_simple_like' ); | |
add_action( 'wp_ajax_process_simple_like', 'process_simple_like' ); | |
function process_simple_like() { | |
// Security | |
$nonce = isset( $_REQUEST['nonce'] ) ? sanitize_text_field( $_REQUEST['nonce'] ) : 0; | |
if ( !wp_verify_nonce( $nonce, 'simple-likes-nonce' ) ) { | |
exit( __( 'Not permitted', 'YourThemeTextDomain' ) ); | |
} | |
// Test if javascript is disabled | |
$disabled = ( isset( $_REQUEST['disabled'] ) && $_REQUEST['disabled'] == true ) ? true : false; | |
// Test if this is a comment | |
$is_comment = ( isset( $_REQUEST['is_comment'] ) && $_REQUEST['is_comment'] == 1 ) ? 1 : 0; | |
// Base variables | |
$post_id = ( isset( $_REQUEST['post_id'] ) && is_numeric( $_REQUEST['post_id'] ) ) ? $_REQUEST['post_id'] : ''; | |
$result = array(); | |
$post_users = NULL; | |
$like_count = 0; | |
// Get plugin options | |
if ( $post_id != '' ) { | |
$count = ( $is_comment == 1 ) ? get_comment_meta( $post_id, "_comment_like_count", true ) : get_post_meta( $post_id, "_post_like_count", true ); // like count | |
$count = ( isset( $count ) && is_numeric( $count ) ) ? $count : 0; | |
if ( !already_liked( $post_id, $is_comment ) ) { // Like the post | |
if ( is_user_logged_in() ) { // user is logged in | |
$user_id = get_current_user_id(); | |
$post_users = post_user_likes( $user_id, $post_id, $is_comment ); | |
if ( $is_comment == 1 ) { | |
// Update User & Comment | |
$user_like_count = get_user_option( "_comment_like_count", $user_id ); | |
$user_like_count = ( isset( $user_like_count ) && is_numeric( $user_like_count ) ) ? $user_like_count : 0; | |
update_user_option( $user_id, "_comment_like_count", ++$user_like_count ); | |
if ( $post_users ) { | |
update_comment_meta( $post_id, "_user_comment_liked", $post_users ); | |
} | |
} else { | |
// Update User & Post | |
$user_like_count = get_user_option( "_user_like_count", $user_id ); | |
$user_like_count = ( isset( $user_like_count ) && is_numeric( $user_like_count ) ) ? $user_like_count : 0; | |
update_user_option( $user_id, "_user_like_count", ++$user_like_count ); | |
if ( $post_users ) { | |
update_post_meta( $post_id, "_user_liked", $post_users ); | |
} | |
} | |
} else { // user is anonymous | |
$user_ip = sl_get_ip(); | |
$post_users = post_ip_likes( $user_ip, $post_id, $is_comment ); | |
// Update Post | |
if ( $post_users ) { | |
if ( $is_comment == 1 ) { | |
update_comment_meta( $post_id, "_user_comment_IP", $post_users ); | |
} else { | |
update_post_meta( $post_id, "_user_IP", $post_users ); | |
} | |
} | |
} | |
$like_count = ++$count; | |
$response['status'] = "liked"; | |
$response['icon'] = get_liked_icon(); | |
} else { // Unlike the post | |
if ( is_user_logged_in() ) { // user is logged in | |
$user_id = get_current_user_id(); | |
$post_users = post_user_likes( $user_id, $post_id, $is_comment ); | |
// Update User | |
if ( $is_comment == 1 ) { | |
$user_like_count = get_user_option( "_comment_like_count", $user_id ); | |
$user_like_count = ( isset( $user_like_count ) && is_numeric( $user_like_count ) ) ? $user_like_count : 0; | |
if ( $user_like_count > 0 ) { | |
update_user_option( $user_id, "_comment_like_count", --$user_like_count ); | |
} | |
} else { | |
$user_like_count = get_user_option( "_user_like_count", $user_id ); | |
$user_like_count = ( isset( $user_like_count ) && is_numeric( $user_like_count ) ) ? $user_like_count : 0; | |
if ( $user_like_count > 0 ) { | |
update_user_option( $user_id, '_user_like_count', --$user_like_count ); | |
} | |
} | |
// Update Post | |
if ( $post_users ) { | |
$uid_key = array_search( $user_id, $post_users ); | |
unset( $post_users[$uid_key] ); | |
if ( $is_comment == 1 ) { | |
update_comment_meta( $post_id, "_user_comment_liked", $post_users ); | |
} else { | |
update_post_meta( $post_id, "_user_liked", $post_users ); | |
} | |
} | |
} else { // user is anonymous | |
$user_ip = sl_get_ip(); | |
$post_users = post_ip_likes( $user_ip, $post_id, $is_comment ); | |
// Update Post | |
if ( $post_users ) { | |
$uip_key = array_search( $user_ip, $post_users ); | |
unset( $post_users[$uip_key] ); | |
if ( $is_comment == 1 ) { | |
update_comment_meta( $post_id, "_user_comment_IP", $post_users ); | |
} else { | |
update_post_meta( $post_id, "_user_IP", $post_users ); | |
} | |
} | |
} | |
$like_count = ( $count > 0 ) ? --$count : 0; // Prevent negative number | |
$response['status'] = "unliked"; | |
$response['icon'] = get_unliked_icon(); | |
} | |
if ( $is_comment == 1 ) { | |
update_comment_meta( $post_id, "_comment_like_count", $like_count ); | |
update_comment_meta( $post_id, "_comment_like_modified", date( 'Y-m-d H:i:s' ) ); | |
} else { | |
update_post_meta( $post_id, "_post_like_count", $like_count ); | |
update_post_meta( $post_id, "_post_like_modified", date( 'Y-m-d H:i:s' ) ); | |
} | |
$response['count'] = get_like_count( $like_count ); | |
$response['testing'] = $is_comment; | |
if ( $disabled == true ) { | |
if ( $is_comment == 1 ) { | |
wp_redirect( get_permalink( get_the_ID() ) ); | |
exit(); | |
} else { | |
wp_redirect( get_permalink( $post_id ) ); | |
exit(); | |
} | |
} else { | |
wp_send_json( $response ); | |
} | |
} | |
} | |
/** | |
* Utility to test if the post is already liked | |
* @since 0.5 | |
*/ | |
function already_liked( $post_id, $is_comment ) { | |
$post_users = NULL; | |
$user_id = NULL; | |
if ( is_user_logged_in() ) { // user is logged in | |
$user_id = get_current_user_id(); | |
$post_meta_users = ( $is_comment == 1 ) ? get_comment_meta( $post_id, "_user_comment_liked" ) : get_post_meta( $post_id, "_user_liked" ); | |
if ( count( $post_meta_users ) != 0 ) { | |
$post_users = $post_meta_users[0]; | |
} | |
} else { // user is anonymous | |
$user_id = sl_get_ip(); | |
$post_meta_users = ( $is_comment == 1 ) ? get_comment_meta( $post_id, "_user_comment_IP" ) : get_post_meta( $post_id, "_user_IP" ); | |
if ( count( $post_meta_users ) != 0 ) { // meta exists, set up values | |
$post_users = $post_meta_users[0]; | |
} | |
} | |
if ( is_array( $post_users ) && in_array( $user_id, $post_users ) ) { | |
return true; | |
} else { | |
return false; | |
} | |
} // already_liked() | |
/** | |
* Output the like button | |
* @since 0.5 | |
*/ | |
function get_simple_likes_button( $post_id, $is_comment = NULL ) { | |
$is_comment = ( NULL == $is_comment ) ? 0 : 1; | |
$output = ''; | |
$nonce = wp_create_nonce( 'simple-likes-nonce' ); // Security | |
if ( $is_comment == 1 ) { | |
$post_id_class = esc_attr( ' sl-comment-button-' . $post_id ); | |
$comment_class = esc_attr( ' sl-comment' ); | |
$like_count = get_comment_meta( $post_id, "_comment_like_count", true ); | |
$like_count = ( isset( $like_count ) && is_numeric( $like_count ) ) ? $like_count : 0; | |
} else { | |
$post_id_class = esc_attr( ' sl-button-' . $post_id ); | |
$comment_class = esc_attr( '' ); | |
$like_count = get_post_meta( $post_id, "_post_like_count", true ); | |
$like_count = ( isset( $like_count ) && is_numeric( $like_count ) ) ? $like_count : 0; | |
} | |
$count = get_like_count( $like_count ); | |
$icon_empty = get_unliked_icon(); | |
$icon_full = get_liked_icon(); | |
// Loader | |
$loader = '<span id="sl-loader"></span>'; | |
// Liked/Unliked Variables | |
if ( already_liked( $post_id, $is_comment ) ) { | |
$class = esc_attr( ' liked' ); | |
$title = __( 'Unlike', 'YourThemeTextDomain' ); | |
$icon = $icon_full; | |
} else { | |
$class = ''; | |
$title = __( 'Like', 'YourThemeTextDomain' ); | |
$icon = $icon_empty; | |
} | |
$output = '<span class="sl-wrapper"><a href="' . admin_url( 'admin-ajax.php?action=process_simple_like' . '&post_id=' . $post_id . '&nonce=' . $nonce . '&is_comment=' . $is_comment . '&disabled=true' ) . '" class="sl-button' . $post_id_class . $class . $comment_class . '" data-nonce="' . $nonce . '" data-post-id="' . $post_id . '" data-iscomment="' . $is_comment . '" title="' . $title . '">' . $icon . $count . '</a>' . $loader . '</span>'; | |
return $output; | |
} // get_simple_likes_button() | |
/** | |
* Processes shortcode to manually add the button to posts | |
* @since 0.5 | |
*/ | |
add_shortcode( 'jmliker', 'sl_shortcode' ); | |
function sl_shortcode() { | |
return get_simple_likes_button( get_the_ID(), 0 ); | |
} // shortcode() | |
/** | |
* Utility retrieves post meta user likes (user id array), | |
* then adds new user id to retrieved array | |
* @since 0.5 | |
*/ | |
function post_user_likes( $user_id, $post_id, $is_comment ) { | |
$post_users = ''; | |
$post_meta_users = ( $is_comment == 1 ) ? get_comment_meta( $post_id, "_user_comment_liked" ) : get_post_meta( $post_id, "_user_liked" ); | |
if ( count( $post_meta_users ) != 0 ) { | |
$post_users = $post_meta_users[0]; | |
} | |
if ( !is_array( $post_users ) ) { | |
$post_users = array(); | |
} | |
if ( !in_array( $user_id, $post_users ) ) { | |
$post_users['user-' . $user_id] = $user_id; | |
} | |
return $post_users; | |
} // post_user_likes() | |
/** | |
* Utility retrieves post meta ip likes (ip array), | |
* then adds new ip to retrieved array | |
* @since 0.5 | |
*/ | |
function post_ip_likes( $user_ip, $post_id, $is_comment ) { | |
$post_users = ''; | |
$post_meta_users = ( $is_comment == 1 ) ? get_comment_meta( $post_id, "_user_comment_IP" ) : get_post_meta( $post_id, "_user_IP" ); | |
// Retrieve post information | |
if ( count( $post_meta_users ) != 0 ) { | |
$post_users = $post_meta_users[0]; | |
} | |
if ( !is_array( $post_users ) ) { | |
$post_users = array(); | |
} | |
if ( !in_array( $user_ip, $post_users ) ) { | |
$post_users['ip-' . $user_ip] = $user_ip; | |
} | |
return $post_users; | |
} // post_ip_likes() | |
/** | |
* Utility to retrieve IP address | |
* @since 0.5 | |
*/ | |
function sl_get_ip() { | |
if ( isset( $_SERVER['HTTP_CLIENT_IP'] ) && ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) { | |
$ip = $_SERVER['HTTP_CLIENT_IP']; | |
} elseif ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) && ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) { | |
$ip = $_SERVER['HTTP_X_FORWARDED_FOR']; | |
} else { | |
$ip = ( isset( $_SERVER['REMOTE_ADDR'] ) ) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0'; | |
} | |
$ip = filter_var( $ip, FILTER_VALIDATE_IP ); | |
$ip = ( $ip === false ) ? '0.0.0.0' : $ip; | |
return $ip; | |
} // sl_get_ip() | |
/** | |
* Utility returns the button icon for "like" action | |
* @since 0.5 | |
*/ | |
function get_liked_icon() { | |
/* If already using Font Awesome with your theme, replace svg with: <i class="fa fa-heart"></i> */ | |
$icon = '<span class="sl-icon"><svg role="img" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0" y="0" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve"><path id="heart-full" d="M124 20.4C111.5-7 73.7-4.8 64 19 54.3-4.9 16.5-7 4 20.4c-14.7 32.3 19.4 63 60 107.1C104.6 83.4 138.7 52.7 124 20.4z"/>♥</svg></span>'; | |
return $icon; | |
} // get_liked_icon() | |
/** | |
* Utility returns the button icon for "unlike" action | |
* @since 0.5 | |
*/ | |
function get_unliked_icon() { | |
/* If already using Font Awesome with your theme, replace svg with: <i class="fa fa-heart-o"></i> */ | |
$icon = '<span class="sl-icon"><svg role="img" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0" y="0" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve"><path id="heart" d="M64 127.5C17.1 79.9 3.9 62.3 1 44.4c-3.5-22 12.2-43.9 36.7-43.9 10.5 0 20 4.2 26.4 11.2 6.3-7 15.9-11.2 26.4-11.2 24.3 0 40.2 21.8 36.7 43.9C124.2 62 111.9 78.9 64 127.5zM37.6 13.4c-9.9 0-18.2 5.2-22.3 13.8C5 49.5 28.4 72 64 109.2c35.7-37.3 59-59.8 48.6-82 -4.1-8.7-12.4-13.8-22.3-13.8 -15.9 0-22.7 13-26.4 19.2C60.6 26.8 54.4 13.4 37.6 13.4z"/>♥</svg></span>'; | |
return $icon; | |
} // get_unliked_icon() | |
/** | |
* Utility function to format the button count, | |
* appending "K" if one thousand or greater, | |
* "M" if one million or greater, | |
* and "B" if one billion or greater (unlikely). | |
* $precision = how many decimal points to display (1.25K) | |
* @since 0.5 | |
*/ | |
function sl_format_count( $number ) { | |
$precision = 2; | |
if ( $number >= 1000 && $number < 1000000 ) { | |
$formatted = number_format( $number/1000, $precision ).'K'; | |
} else if ( $number >= 1000000 && $number < 1000000000 ) { | |
$formatted = number_format( $number/1000000, $precision ).'M'; | |
} else if ( $number >= 1000000000 ) { | |
$formatted = number_format( $number/1000000000, $precision ).'B'; | |
} else { | |
$formatted = $number; // Number is less than 1000 | |
} | |
$formatted = str_replace( '.00', '', $formatted ); | |
return $formatted; | |
} // sl_format_count() | |
/** | |
* Utility retrieves count plus count options, | |
* returns appropriate format based on options | |
* @since 0.5 | |
*/ | |
function get_like_count( $like_count ) { | |
$like_text = __( 'Like', 'YourThemeTextDomain' ); | |
if ( is_numeric( $like_count ) && $like_count > 0 ) { | |
$number = sl_format_count( $like_count ); | |
} else { | |
$number = $like_text; | |
} | |
$count = '<span class="sl-count">' . $number . '</span>'; | |
return $count; | |
} // get_like_count() | |
// User Profile List | |
add_action( 'show_user_profile', 'show_user_likes' ); | |
add_action( 'edit_user_profile', 'show_user_likes' ); | |
function show_user_likes( $user ) { ?> | |
<table class="form-table"> | |
<tr> | |
<th><label for="user_likes"><?php _e( 'You Like:', 'YourThemeTextDomain' ); ?></label></th> | |
<td> | |
<?php | |
$types = get_post_types( array( 'public' => true ) ); | |
$args = array( | |
'numberposts' => -1, | |
'post_type' => $types, | |
'meta_query' => array ( | |
array ( | |
'key' => '_user_liked', | |
'value' => $user->ID, | |
'compare' => 'LIKE' | |
) | |
) ); | |
$sep = ''; | |
$like_query = new WP_Query( $args ); | |
if ( $like_query->have_posts() ) : ?> | |
<p> | |
<?php while ( $like_query->have_posts() ) : $like_query->the_post(); | |
echo $sep; ?><a href="<?php the_permalink(); ?>" title="<?php the_title_attribute(); ?>"><?php the_title(); ?></a> | |
<?php | |
$sep = ' · '; | |
endwhile; | |
?> | |
</p> | |
<?php else : ?> | |
<p><?php _e( 'You do not like anything yet.', 'YourThemeTextDomain' ); ?></p> | |
<?php | |
endif; | |
wp_reset_postdata(); | |
?> | |
</td> | |
</tr> | |
</table> | |
<?php } // show_user_likes() |
3. Change theme name
In the pile of code we just added to functions.php go hit control F, and replace “YourThemeTextDomain” with your theme name. This isn’t necessary but I think it makes your code clearer to read.
4. Add the javascript file to your library
Add this javascript to your theme/lib/js/simple-likes-public.js path and save. Here’s the code to save you time:
(function( $ ) { | |
'use strict'; | |
$(document).on('click', '.sl-button', function() { | |
var button = $(this); | |
var post_id = button.attr('data-post-id'); | |
var security = button.attr('data-nonce'); | |
var iscomment = button.attr('data-iscomment'); | |
var allbuttons; | |
if ( iscomment === '1' ) { /* Comments can have same id */ | |
allbuttons = $('.sl-comment-button-'+post_id); | |
} else { | |
allbuttons = $('.sl-button-'+post_id); | |
} | |
var loader = allbuttons.next('#sl-loader'); | |
if (post_id !== '') { | |
$.ajax({ | |
type: 'POST', | |
url: simpleLikes.ajaxurl, | |
data : { | |
action : 'process_simple_like', | |
post_id : post_id, | |
nonce : security, | |
is_comment : iscomment, | |
}, | |
beforeSend:function(){ | |
loader.html(' <div class="loader">Loading...</div>'); | |
}, | |
success: function(response){ | |
var icon = response.icon; | |
var count = response.count; | |
allbuttons.html(icon+count); | |
if(response.status === 'unliked') { | |
var like_text = simpleLikes.like; | |
allbuttons.prop('title', like_text); | |
allbuttons.removeClass('liked'); | |
} else { | |
var unlike_text = simpleLikes.unlike; | |
allbuttons.prop('title', unlike_text); | |
allbuttons.addClass('liked'); | |
} | |
loader.empty(); | |
} | |
}); | |
} | |
return false; | |
}); | |
})( jQuery ); |
5. replace the svg with font awesome
I found the SVG file came out massive, and of course its easy to modify it with CSS, but it’s also easy to add font awesome icons in, and Jon has already commented out the instructions on how to do this. In case you would like to see it anyway:
6. add the likes function within the loop of your template
Now we are going to go into our template file, in my case it’s header-insights.php and find the section where the GET parameters that are already calling in the author and the date, and we are going to echo in our function here:
7. Check your page template and make sure your like button is working
Now we’re going to go to our post template file, refresh the page and see if our like button is working. If you click and un-click the button, the heart will turn on and off which is correct. The way the function is setup, it only allows one user to like a post by calling your IP address into the wp_postmeta table in SQL, so for example you cannot publish a post and click like a hundred times to look really cool, LOL. You can see the finished product below:
Jane James is a WordPress web developer based in Melbourne, Australia but also services clients from Sydney, Brisbane, Newcastle, Perth, Adelaide, Darwin and Hobart. Have a project in mind? Contact me here.