Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

WordPress

how can I save votes with user ID in wordpress

I want to add this simple voting system (http://code.tutsplus.com/articles/how-to-create-a-simple-post-rating-system-with-wordpress-and-jquery--wp-24474) to my wordpres theme. But it's saving votes by user IPs . I would like to save with user id . and only logged users will vote the posts.

This is the post like function

function post_like()
{
// Check for nonce security
$nonce = $_POST['nonce'];

if ( ! wp_verify_nonce( $nonce, 'ajax-nonce' ) )
    die ( 'Busted!');

if(isset($_POST['post_like']))
{
    // Retrieve user IP address
    $ip = $_SERVER['REMOTE_ADDR'];
    $post_id = $_POST['post_id'];

    // Get voters'IPs for the current post
    $meta_IP = get_post_meta($post_id, "voted_IP");
    $voted_IP = $meta_IP[0];

    if(!is_array($voted_IP))
        $voted_IP = array();

    // Get votes count for the current post
    $meta_count = get_post_meta($post_id, "votes_count", true);

    // Use has already voted ?
    if(!hasAlreadyVoted($post_id))
    {
        $voted_IP[$ip] = time();

        // Save IP and increase votes count
        update_post_meta($post_id, "voted_IP", $voted_IP);
        update_post_meta($post_id, "votes_count", ++$meta_count);

        // Display count (ie jQuery return value)
        echo $meta_count;
    }
    else
        echo "already";
}
exit;
}

and this is the check already voted function

function hasAlreadyVoted($post_id)
{
global $timebeforerevote;

// Retrieve post votes IPs
$meta_IP = get_post_meta($post_id, "voted_IP");
$voted_IP = $meta_IP[0];

if(!is_array($voted_IP))
    $voted_IP = array();

// Retrieve current user IP
$ip = $_SERVER['REMOTE_ADDR'];

// If user has already voted
if(in_array($ip, array_keys($voted_IP)))
{
    $time = $voted_IP[$ip];
    $now = time();

    // Compare between current time and vote time
    if(round(($now - $time) / 60) > $timebeforerevote)
        return false;

    return true;
}

return false;
}

thank you very much for answers

1 Answer

Sue Dough
Sue Dough
35,800 Points

Hi,

Getting the current user id is pretty easy in WordPress as they have some built in functions that help you do this.

http://codex.wordpress.org/Function_Reference/get_current_user_id

<?php

$user_id = get_current_user_id();

You can user this native WordPress function to check if a user is logged in. HOWEVER this is not secure enough. Keep reading

<?php

if ( is_user_logged_in() ) {

 // your code 

}

AJAX can be easily manipulated and changed. This causes security holes for websites. You will also need to REMOVE this 1 line of code below. The reason why is because WordPress makes you use 2 add_action if you want the ajax function to be public and private. One of the add_action has nopriv in the name. That means not private which means anyone regardless of if their logged in can send a post request.

add_action('wp_ajax_nopriv_post-like', 'post_like');

=============================================================================================================================

Now this is beyond your question however since you seem new to WordPress so I want to try and help out. That tutorial is from a long time ago and the code is not so good. I would strongly suggest rolling this into a plugin and not your theme. Then check if the function exists so you can easily turn it on/off. Try to avoid putting functions in your theme and use small modular plugins instead.

Few thoughts on the code and how it could be better:

  1. Functions shouldn't do that much and should be split up into smaller functions. Functions that do 1 thing are easier to maintain. They can also be tested easier. It also allows you to reuse things more. I see both functions doing a lot of things and some of the same things.

  2. It does not follow any coding standards from WordPress. Take a look here: https://make.wordpress.org/core/handbook/best-practices/coding-standards/php/

bad - spacing, indentation, and using double quotes, bad casing

<?php

$meta_IP = get_post_meta($post_id, "voted_IP");
$voted_IP = $meta_IP[0];

good - spacing, indentation, and using single quotes, no upper case

<?php

$meta_ip  = get_post_meta( $post_id, 'voted_ip' );
$voted_ip = $meta_ip[0];

3.

The following line of code fails alot:

<?php

$ip = $_SERVER['REMOTE_ADDR']; 

I would use this instead:

<?php

/**
* Get Real IP from Visitor
*
* @return  str  $ip  visitors IP address
*
* @stackoverflow http://stackoverflow.com/questions/13646690/how-to-get-real-ip-from-visitor
*/

function get_user_ip() {
  $client  = @$_SERVER['HTTP_CLIENT_IP'];
  $forward = @$_SERVER['HTTP_X_FORWARDED_FOR'];
  $remote  = $_SERVER['REMOTE_ADDR'];

  if ( filter_var( $client, FILTER_VALIDATE_IP ) ) {
    $ip = $client;
  } elseif ( filter_var( $forward, FILTER_VALIDATE_IP ) ) {
    $ip = $forward;
  } else {
    $ip = $remote;
  }

  return $ip;
}

and call it like this

$user_ip = get_user_ip();
  1. The ajax request like enocded for you and this is not good practice and hard to read
data: "action=post-like&nonce="+ajax_var.nonce+"&post_like=&post_id="+post_id,

Something like this would be more easier to read. You want to be getting the post ID and user ID in the PHP to avoid ajax manipulation and improve your security.

var data = {
  nonce: ajax_var.nonce,
  action:'post-like',
};

then just pass it like

data: data
  1. The data structure. I am not sure how important it is to you. But I would do it differently. I would create a seperate table that uses hook for when a plugin is installed so it only runs once. http://codex.wordpress.org/Function_Reference/register_activation_hook . This will collect ip, user id, post id, and timestamps.I like logging every event so its easier to set up rate limiting and creates smaller database tables. Your post meta table will be a lot bigger than your custom table causing slower writes to the database. Here is an example function with a schema to create a table. It also allows you to track the features success over time since your collecting timestamps you can easily make graphs and study the data later. The way I look at it now is your losing data you may want later on the way it currently is.
<?php

/**
* Create Tables For Post Likes
*/

function create_table_post_likes() {
  global $wpdb;

  $table_name      = $wpdb->prefix . 'post_likes';
  $charset_collate = $wpdb->get_charset_collate();

  $sql = "CREATE TABLE IF NOT EXISTS $table_name (
    id mediumint(9) NOT NULL AUTO_INCREMENT,
    user_id mediumint(9) NULL,
    post_id mediumint(9) NULL,
    time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    ip_address varchar(15) NOT NULL,
  ) $charset_collate;";

  require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
  dbDelta( $sql );
}


/**
* Insert Data for post likes.   ( Side note - make sure to validate the variables before hand )
*
* @param  int   $user_id       The unique user id
* @param  int   $post_id       The unique post id
* @param  str   $ip_address    Unique IP address
* @param  bool  true | false   true if successful insert
*/

function insert_post_like( $user_id, $post_id, $ip_address ) {
  global $wpdb;

  $table_name = $wpdb->prefix . 'post_likes';

  if ( $wpdb->insert( $table_name, array(
    'user_id'    => $user_id,
    'post_id'    => $post_id,
    'ip_address' => $ip_address,
    'time'       => current_time( 'mysql' ),
  ) ) === FALSE ) {
    return false;
  } else {
    return true;
  }

}

Hope that helps. Happy coding. If its beyond what you need right now do not worry. Use the code as is from the tutorials but be cautious that it could be a lot better.