├── README.md ├── composer.json ├── css └── simple-likes-public.css ├── js └── simple-likes-public.js └── post-like.php /README.md: -------------------------------------------------------------------------------- 1 | WordPress-Post-Like-System 2 | ========================== 3 | 4 |

This Repository is Not Actively Maintained

5 | 6 | A simple and efficient post like system for WordPress. View the demo. 7 | 8 |

Four Steps to Glory

9 |
    10 |
  1. Add the CSS to your theme's main stylesheet.
  2. 11 |
  3. Add the javascript file to your theme's js folder (if it exists). If there is no js folder at your theme's root level, create one and add simple-likes-public.js to it.
  4. 12 |
  5. Add the contents of post-like.php to your theme's functions.php file.
  6. 13 |
  7. Output the button by doing the following: 14 | 19 |
  8. 20 |
21 | 22 |

Upgrades

23 | I've cleaned up a lot here, fixing a few errors, and making it easier to revise this system to suit your theme. Here are the highlights: 24 | 34 | 35 |

Removed

36 | I've removed some functions and changed the way this system works a bit. None are breaking changes. These were for efficiency. 37 | 41 | 42 |

Issues

43 | This post like system is pretty simple, and does not have many moving parts. Some folks have encountered issues with AJAX-loaded content, and others have had some trouble with plugin conflicts. 44 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jonmasterson/wordpress-post-like-system", 3 | "minimum-stability": "dev", 4 | "description": "A simple and efficient post like system for WordPress", 5 | "keywords": ["wordpress", "plugin", "like", "post"], 6 | "homepage": "http://jonmasterson.com/post-like-demo/", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Jon Masterson", 11 | "homepage": "http://jonmasterson.com" 12 | } 13 | ], 14 | "type": "wordpress-plugin", 15 | "require": { 16 | "php": ">=5.5", 17 | "composer/installers": "~1.0.12" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /css/simple-likes-public.css: -------------------------------------------------------------------------------- 1 | /* Wrapper */ 2 | .sl-wrapper a { 3 | border-bottom: 0 !important; 4 | text-decoration: none !important; 5 | } 6 | .sl-button { 7 | padding: 0.375em 0.625em; 8 | font-size: 1em; 9 | line-height: 1; 10 | font-weight: normal; 11 | } 12 | 13 | /* Colors */ 14 | a.liked { 15 | color: #da1b1b; 16 | } 17 | a.liked:hover, 18 | a.liked:active, 19 | a.liked:focus { 20 | color: #666666; 21 | } 22 | a.liked span.sl-count, 23 | .sl-count { 24 | color: #666666; 25 | } 26 | 27 | /* Icon */ 28 | .sl-icon { 29 | margin-right: 0.3125em; 30 | font-family: 'Arial Unicode MS', Arial, sans-serif; 31 | vertical-align: middle; 32 | } 33 | .sl-icon svg { 34 | fill: currentColor; 35 | width: 1em; 36 | height: 1em; 37 | } 38 | .sl-icon svg:after { 39 | content: ""; 40 | position: absolute; 41 | top: 0; 42 | right: 0; 43 | bottom: 0; 44 | left:0; 45 | } 46 | 47 | /* Count */ 48 | .sl-count { 49 | font-size: 0.625em; 50 | font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; 51 | text-transform: uppercase; 52 | vertical-align: middle; 53 | } 54 | 55 | /* Loader */ 56 | .loader, 57 | .loader:before, 58 | .loader:after { 59 | background: rgba(0, 0, 0, 0.2); 60 | -webkit-animation: load1 1s infinite ease-in-out; 61 | animation: load1 1s infinite ease-in-out; 62 | width: .2em; 63 | height: .6em; 64 | } 65 | .loader:before, 66 | .loader:after { 67 | position: absolute; 68 | top: 0; 69 | content: ''; 70 | } 71 | .loader:before { 72 | left: -.375em; 73 | -webkit-animation-delay: -0.32s; 74 | animation-delay: -0.32s; 75 | } 76 | .loader { 77 | text-indent: -9999em; 78 | display: inline-block; 79 | position: relative; 80 | vertical-align: middle; 81 | font-size: 1em; 82 | -webkit-transform: translateZ(0); 83 | -ms-transform: translateZ(0); 84 | transform: translateZ(0); 85 | -webkit-animation-delay: -0.16s; 86 | animation-delay: -0.16s; 87 | } 88 | .loader:after { 89 | left: .375em; 90 | } 91 | @-webkit-keyframes load1 { 92 | 0%, 93 | 80%, 94 | 100% { 95 | box-shadow: 0 0 rgba(0, 0, 0, 0.2); 96 | height: .6em; 97 | } 98 | 40% { 99 | box-shadow: 0 -.3em rgba(0, 0, 0, 0.2); 100 | height: 1em; 101 | } 102 | } 103 | @keyframes load1 { 104 | 0%, 105 | 80%, 106 | 100% { 107 | box-shadow: 0 0 rgba(0, 0, 0, 0.2); 108 | height: .6em; 109 | } 110 | 40% { 111 | box-shadow: 0 -.3em rgba(0, 0, 0, 0.2); 112 | height: 1em; 113 | } 114 | } -------------------------------------------------------------------------------- /js/simple-likes-public.js: -------------------------------------------------------------------------------- 1 | (function( $ ) { 2 | 'use strict'; 3 | $(document).on('click', '.sl-button', function() { 4 | var button = $(this); 5 | var post_id = button.attr('data-post-id'); 6 | var security = button.attr('data-nonce'); 7 | var iscomment = button.attr('data-iscomment'); 8 | var allbuttons; 9 | if ( iscomment === '1' ) { /* Comments can have same id */ 10 | allbuttons = $('.sl-comment-button-'+post_id); 11 | } else { 12 | allbuttons = $('.sl-button-'+post_id); 13 | } 14 | var loader = allbuttons.next('#sl-loader'); 15 | if (post_id !== '') { 16 | $.ajax({ 17 | type: 'POST', 18 | url: simpleLikes.ajaxurl, 19 | data : { 20 | action : 'process_simple_like', 21 | post_id : post_id, 22 | nonce : security, 23 | is_comment : iscomment, 24 | }, 25 | beforeSend:function(){ 26 | loader.html(' 
Loading...
'); 27 | }, 28 | success: function(response){ 29 | var icon = response.icon; 30 | var count = response.count; 31 | allbuttons.html(icon+count); 32 | if(response.status === 'unliked') { 33 | var like_text = simpleLikes.like; 34 | allbuttons.prop('title', like_text); 35 | allbuttons.removeClass('liked'); 36 | } else { 37 | var unlike_text = simpleLikes.unlike; 38 | allbuttons.prop('title', unlike_text); 39 | allbuttons.addClass('liked'); 40 | } 41 | loader.empty(); 42 | } 43 | }); 44 | 45 | } 46 | return false; 47 | }); 48 | })( jQuery ); 49 | -------------------------------------------------------------------------------- /post-like.php: -------------------------------------------------------------------------------- 1 | . 21 | */ 22 | 23 | /** 24 | * Register the stylesheets for the public-facing side of the site. 25 | * @since 0.5 26 | */ 27 | add_action( 'wp_enqueue_scripts', 'sl_enqueue_scripts' ); 28 | function sl_enqueue_scripts() { 29 | wp_enqueue_script( 'simple-likes-public-js', get_template_directory_uri() . '/js/simple-likes-public.js', array( 'jquery' ), '0.5', false ); 30 | wp_localize_script( 'simple-likes-public-js', 'simpleLikes', array( 31 | 'ajaxurl' => admin_url( 'admin-ajax.php' ), 32 | 'like' => __( 'Like', 'YourThemeTextDomain' ), 33 | 'unlike' => __( 'Unlike', 'YourThemeTextDomain' ) 34 | ) ); 35 | } 36 | 37 | /** 38 | * Processes like/unlike 39 | * @since 0.5 40 | */ 41 | add_action( 'wp_ajax_nopriv_process_simple_like', 'process_simple_like' ); 42 | add_action( 'wp_ajax_process_simple_like', 'process_simple_like' ); 43 | function process_simple_like() { 44 | // Security 45 | $nonce = isset( $_REQUEST['nonce'] ) ? sanitize_text_field( $_REQUEST['nonce'] ) : 0; 46 | if ( !wp_verify_nonce( $nonce, 'simple-likes-nonce' ) ) { 47 | exit( __( 'Not permitted', 'YourThemeTextDomain' ) ); 48 | } 49 | // Test if javascript is disabled 50 | $disabled = ( isset( $_REQUEST['disabled'] ) && $_REQUEST['disabled'] == true ) ? true : false; 51 | // Test if this is a comment 52 | $is_comment = ( isset( $_REQUEST['is_comment'] ) && $_REQUEST['is_comment'] == 1 ) ? 1 : 0; 53 | // Base variables 54 | $post_id = ( isset( $_REQUEST['post_id'] ) && is_numeric( $_REQUEST['post_id'] ) ) ? $_REQUEST['post_id'] : ''; 55 | $result = array(); 56 | $post_users = NULL; 57 | $like_count = 0; 58 | // Get plugin options 59 | if ( $post_id != '' ) { 60 | $count = ( $is_comment == 1 ) ? get_comment_meta( $post_id, "_comment_like_count", true ) : get_post_meta( $post_id, "_post_like_count", true ); // like count 61 | $count = ( isset( $count ) && is_numeric( $count ) ) ? $count : 0; 62 | if ( !already_liked( $post_id, $is_comment ) ) { // Like the post 63 | if ( is_user_logged_in() ) { // user is logged in 64 | $user_id = get_current_user_id(); 65 | $post_users = post_user_likes( $user_id, $post_id, $is_comment ); 66 | if ( $is_comment == 1 ) { 67 | // Update User & Comment 68 | $user_like_count = get_user_option( "_comment_like_count", $user_id ); 69 | $user_like_count = ( isset( $user_like_count ) && is_numeric( $user_like_count ) ) ? $user_like_count : 0; 70 | update_user_option( $user_id, "_comment_like_count", ++$user_like_count ); 71 | if ( $post_users ) { 72 | update_comment_meta( $post_id, "_user_comment_liked", $post_users ); 73 | } 74 | } else { 75 | // Update User & Post 76 | $user_like_count = get_user_option( "_user_like_count", $user_id ); 77 | $user_like_count = ( isset( $user_like_count ) && is_numeric( $user_like_count ) ) ? $user_like_count : 0; 78 | update_user_option( $user_id, "_user_like_count", ++$user_like_count ); 79 | if ( $post_users ) { 80 | update_post_meta( $post_id, "_user_liked", $post_users ); 81 | } 82 | } 83 | } else { // user is anonymous 84 | $user_ip = sl_get_ip(); 85 | $post_users = post_ip_likes( $user_ip, $post_id, $is_comment ); 86 | // Update Post 87 | if ( $post_users ) { 88 | if ( $is_comment == 1 ) { 89 | update_comment_meta( $post_id, "_user_comment_IP", $post_users ); 90 | } else { 91 | update_post_meta( $post_id, "_user_IP", $post_users ); 92 | } 93 | } 94 | } 95 | $like_count = ++$count; 96 | $response['status'] = "liked"; 97 | $response['icon'] = get_liked_icon(); 98 | } else { // Unlike the post 99 | if ( is_user_logged_in() ) { // user is logged in 100 | $user_id = get_current_user_id(); 101 | $post_users = post_user_likes( $user_id, $post_id, $is_comment ); 102 | // Update User 103 | if ( $is_comment == 1 ) { 104 | $user_like_count = get_user_option( "_comment_like_count", $user_id ); 105 | $user_like_count = ( isset( $user_like_count ) && is_numeric( $user_like_count ) ) ? $user_like_count : 0; 106 | if ( $user_like_count > 0 ) { 107 | update_user_option( $user_id, "_comment_like_count", --$user_like_count ); 108 | } 109 | } else { 110 | $user_like_count = get_user_option( "_user_like_count", $user_id ); 111 | $user_like_count = ( isset( $user_like_count ) && is_numeric( $user_like_count ) ) ? $user_like_count : 0; 112 | if ( $user_like_count > 0 ) { 113 | update_user_option( $user_id, '_user_like_count', --$user_like_count ); 114 | } 115 | } 116 | // Update Post 117 | if ( $post_users ) { 118 | $uid_key = array_search( $user_id, $post_users ); 119 | unset( $post_users[$uid_key] ); 120 | if ( $is_comment == 1 ) { 121 | update_comment_meta( $post_id, "_user_comment_liked", $post_users ); 122 | } else { 123 | update_post_meta( $post_id, "_user_liked", $post_users ); 124 | } 125 | } 126 | } else { // user is anonymous 127 | $user_ip = sl_get_ip(); 128 | $post_users = post_ip_likes( $user_ip, $post_id, $is_comment ); 129 | // Update Post 130 | if ( $post_users ) { 131 | $uip_key = array_search( $user_ip, $post_users ); 132 | unset( $post_users[$uip_key] ); 133 | if ( $is_comment == 1 ) { 134 | update_comment_meta( $post_id, "_user_comment_IP", $post_users ); 135 | } else { 136 | update_post_meta( $post_id, "_user_IP", $post_users ); 137 | } 138 | } 139 | } 140 | $like_count = ( $count > 0 ) ? --$count : 0; // Prevent negative number 141 | $response['status'] = "unliked"; 142 | $response['icon'] = get_unliked_icon(); 143 | } 144 | if ( $is_comment == 1 ) { 145 | update_comment_meta( $post_id, "_comment_like_count", $like_count ); 146 | update_comment_meta( $post_id, "_comment_like_modified", date( 'Y-m-d H:i:s' ) ); 147 | } else { 148 | update_post_meta( $post_id, "_post_like_count", $like_count ); 149 | update_post_meta( $post_id, "_post_like_modified", date( 'Y-m-d H:i:s' ) ); 150 | } 151 | $response['count'] = get_like_count( $like_count ); 152 | $response['testing'] = $is_comment; 153 | if ( $disabled == true ) { 154 | if ( $is_comment == 1 ) { 155 | wp_redirect( get_permalink( get_the_ID() ) ); 156 | exit(); 157 | } else { 158 | wp_redirect( get_permalink( $post_id ) ); 159 | exit(); 160 | } 161 | } else { 162 | wp_send_json( $response ); 163 | } 164 | } 165 | } 166 | 167 | /** 168 | * Utility to test if the post is already liked 169 | * @since 0.5 170 | */ 171 | function already_liked( $post_id, $is_comment ) { 172 | $post_users = NULL; 173 | $user_id = NULL; 174 | if ( is_user_logged_in() ) { // user is logged in 175 | $user_id = get_current_user_id(); 176 | $post_meta_users = ( $is_comment == 1 ) ? get_comment_meta( $post_id, "_user_comment_liked" ) : get_post_meta( $post_id, "_user_liked" ); 177 | if ( count( $post_meta_users ) != 0 ) { 178 | $post_users = $post_meta_users[0]; 179 | } 180 | } else { // user is anonymous 181 | $user_id = sl_get_ip(); 182 | $post_meta_users = ( $is_comment == 1 ) ? get_comment_meta( $post_id, "_user_comment_IP" ) : get_post_meta( $post_id, "_user_IP" ); 183 | if ( count( $post_meta_users ) != 0 ) { // meta exists, set up values 184 | $post_users = $post_meta_users[0]; 185 | } 186 | } 187 | if ( is_array( $post_users ) && in_array( $user_id, $post_users ) ) { 188 | return true; 189 | } else { 190 | return false; 191 | } 192 | } // already_liked() 193 | 194 | /** 195 | * Output the like button 196 | * @since 0.5 197 | */ 198 | function get_simple_likes_button( $post_id, $is_comment = NULL ) { 199 | $is_comment = ( NULL == $is_comment ) ? 0 : 1; 200 | $output = ''; 201 | $nonce = wp_create_nonce( 'simple-likes-nonce' ); // Security 202 | if ( $is_comment == 1 ) { 203 | $post_id_class = esc_attr( ' sl-comment-button-' . $post_id ); 204 | $comment_class = esc_attr( ' sl-comment' ); 205 | $like_count = get_comment_meta( $post_id, "_comment_like_count", true ); 206 | $like_count = ( isset( $like_count ) && is_numeric( $like_count ) ) ? $like_count : 0; 207 | } else { 208 | $post_id_class = esc_attr( ' sl-button-' . $post_id ); 209 | $comment_class = esc_attr( '' ); 210 | $like_count = get_post_meta( $post_id, "_post_like_count", true ); 211 | $like_count = ( isset( $like_count ) && is_numeric( $like_count ) ) ? $like_count : 0; 212 | } 213 | $count = get_like_count( $like_count ); 214 | $icon_empty = get_unliked_icon(); 215 | $icon_full = get_liked_icon(); 216 | // Loader 217 | $loader = ''; 218 | // Liked/Unliked Variables 219 | if ( already_liked( $post_id, $is_comment ) ) { 220 | $class = esc_attr( ' liked' ); 221 | $title = __( 'Unlike', 'YourThemeTextDomain' ); 222 | $icon = $icon_full; 223 | } else { 224 | $class = ''; 225 | $title = __( 'Like', 'YourThemeTextDomain' ); 226 | $icon = $icon_empty; 227 | } 228 | $output = '' . $icon . $count . '' . $loader . ''; 229 | return $output; 230 | } // get_simple_likes_button() 231 | 232 | /** 233 | * Processes shortcode to manually add the button to posts 234 | * @since 0.5 235 | */ 236 | add_shortcode( 'jmliker', 'sl_shortcode' ); 237 | function sl_shortcode() { 238 | return get_simple_likes_button( get_the_ID(), 0 ); 239 | } // shortcode() 240 | 241 | /** 242 | * Utility retrieves post meta user likes (user id array), 243 | * then adds new user id to retrieved array 244 | * @since 0.5 245 | */ 246 | function post_user_likes( $user_id, $post_id, $is_comment ) { 247 | $post_users = ''; 248 | $post_meta_users = ( $is_comment == 1 ) ? get_comment_meta( $post_id, "_user_comment_liked" ) : get_post_meta( $post_id, "_user_liked" ); 249 | if ( count( $post_meta_users ) != 0 ) { 250 | $post_users = $post_meta_users[0]; 251 | } 252 | if ( !is_array( $post_users ) ) { 253 | $post_users = array(); 254 | } 255 | if ( !in_array( $user_id, $post_users ) ) { 256 | $post_users['user-' . $user_id] = $user_id; 257 | } 258 | return $post_users; 259 | } // post_user_likes() 260 | 261 | /** 262 | * Utility retrieves post meta ip likes (ip array), 263 | * then adds new ip to retrieved array 264 | * @since 0.5 265 | */ 266 | function post_ip_likes( $user_ip, $post_id, $is_comment ) { 267 | $post_users = ''; 268 | $post_meta_users = ( $is_comment == 1 ) ? get_comment_meta( $post_id, "_user_comment_IP" ) : get_post_meta( $post_id, "_user_IP" ); 269 | // Retrieve post information 270 | if ( count( $post_meta_users ) != 0 ) { 271 | $post_users = $post_meta_users[0]; 272 | } 273 | if ( !is_array( $post_users ) ) { 274 | $post_users = array(); 275 | } 276 | if ( !in_array( $user_ip, $post_users ) ) { 277 | $post_users['ip-' . $user_ip] = $user_ip; 278 | } 279 | return $post_users; 280 | } // post_ip_likes() 281 | 282 | /** 283 | * Utility to retrieve IP address 284 | * @since 0.5 285 | */ 286 | function sl_get_ip() { 287 | if ( isset( $_SERVER['HTTP_CLIENT_IP'] ) && ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) { 288 | $ip = $_SERVER['HTTP_CLIENT_IP']; 289 | } elseif ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) && ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) { 290 | $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; 291 | } else { 292 | $ip = ( isset( $_SERVER['REMOTE_ADDR'] ) ) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0'; 293 | } 294 | $ip = filter_var( $ip, FILTER_VALIDATE_IP ); 295 | $ip = ( $ip === false ) ? '0.0.0.0' : $ip; 296 | return $ip; 297 | } // sl_get_ip() 298 | 299 | /** 300 | * Utility returns the button icon for "like" action 301 | * @since 0.5 302 | */ 303 | function get_liked_icon() { 304 | /* If already using Font Awesome with your theme, replace svg with: */ 305 | $icon = ''; 306 | return $icon; 307 | } // get_liked_icon() 308 | 309 | /** 310 | * Utility returns the button icon for "unlike" action 311 | * @since 0.5 312 | */ 313 | function get_unliked_icon() { 314 | /* If already using Font Awesome with your theme, replace svg with: */ 315 | $icon = ''; 316 | return $icon; 317 | } // get_unliked_icon() 318 | 319 | /** 320 | * Utility function to format the button count, 321 | * appending "K" if one thousand or greater, 322 | * "M" if one million or greater, 323 | * and "B" if one billion or greater (unlikely). 324 | * $precision = how many decimal points to display (1.25K) 325 | * @since 0.5 326 | */ 327 | function sl_format_count( $number ) { 328 | $precision = 2; 329 | if ( $number >= 1000 && $number < 1000000 ) { 330 | $formatted = number_format( $number/1000, $precision ).'K'; 331 | } else if ( $number >= 1000000 && $number < 1000000000 ) { 332 | $formatted = number_format( $number/1000000, $precision ).'M'; 333 | } else if ( $number >= 1000000000 ) { 334 | $formatted = number_format( $number/1000000000, $precision ).'B'; 335 | } else { 336 | $formatted = $number; // Number is less than 1000 337 | } 338 | $formatted = str_replace( '.00', '', $formatted ); 339 | return $formatted; 340 | } // sl_format_count() 341 | 342 | /** 343 | * Utility retrieves count plus count options, 344 | * returns appropriate format based on options 345 | * @since 0.5 346 | */ 347 | function get_like_count( $like_count ) { 348 | $like_text = __( 'Like', 'YourThemeTextDomain' ); 349 | if ( is_numeric( $like_count ) && $like_count > 0 ) { 350 | $number = sl_format_count( $like_count ); 351 | } else { 352 | $number = $like_text; 353 | } 354 | $count = '' . $number . ''; 355 | return $count; 356 | } // get_like_count() 357 | 358 | // User Profile List 359 | add_action( 'show_user_profile', 'show_user_likes' ); 360 | add_action( 'edit_user_profile', 'show_user_likes' ); 361 | function show_user_likes( $user ) { ?> 362 | 363 | 364 | 365 | 396 | 397 |
366 | true ) ); 368 | $args = array( 369 | 'numberposts' => -1, 370 | 'post_type' => $types, 371 | 'meta_query' => array ( 372 | array ( 373 | 'key' => '_user_liked', 374 | 'value' => $user->ID, 375 | 'compare' => 'LIKE' 376 | ) 377 | ) ); 378 | $sep = ''; 379 | $like_query = new WP_Query( $args ); 380 | if ( $like_query->have_posts() ) : ?> 381 |

382 | have_posts() ) : $like_query->the_post(); 383 | echo $sep; ?> 384 | 388 |

389 | 390 |

391 | 395 |
398 |