A Better Voting System for WordPress
by Bandicoot Marketing on | Posted in Tutorials | 23 comments
I published a tutorial quite a while back called Simple Voting for WordPress with PHP and jQuery. It was not without its problems, and recently I needed to improve on it for a client, so I thought it would be a good idea to finally release an updated version.
The original tutorial relied on an outside PHP file to process the votes but now I use the built-in Ajax function in WordPress. That way it’s more secure and efficient. To get everything started, let’s take a look at the jQuery required:
<script type="text/javascript"> /* <![CDATA[ */ (function($) { $("#vote").not(".disabled").click(function() { var el = $(this); var nonce = $("input#voting_nonce").val(); var data = { action: 'add_votes_options', nonce: nonce, postid: '<?php echo $post->ID; ?>', ip: '<?php echo $_SERVER['REMOTE_ADDR']; ?>' }; $.post('<?php echo admin_url('admin-ajax.php'); ?>', data, function(response){ if(response!="-1") { el.html("VOTED").unbind("click"); if(response=="null") { alert("A vote has already been registered to this IP address."); } else { $("#votecounter").html(response); alert("Thanks for your vote."); } } else { alert("There was a problem registering your vote. Please try again later."); } }); return false; }); })(jQuery); /* ]]> */ </script>
If the vote button is clicked, it will send the Ajax request and process it accordingly, depending on a few things. The only thing missing, is the code to register a cookie once the vote has been calculated so that voters can only vote once. Let’s include two cookie functions originally created by Peter-Paul Koch and add a block of code to our original script:
<script type="text/javascript"> /* <![CDATA[ */ (function($) { function setCookie(name,value,days) { if (days) { var date = new Date(); date.setTime(date.getTime()+(days*24*60*60*1000)); var expires = "; expires="+date.toGMTString(); } else var expires = ""; document.cookie = name+"="+value+expires+"; path=/"; } function getCookie(name) { var nameEQ = name + "="; var ca = document.cookie.split(';'); for(var i=0;i < ca.length;i++) { var c = ca[i]; while (c.charAt(0)==' ') c = c.substring(1,c.length); if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); } return null; } $("#vote").not(".disabled").click(function() { var el = $(this); el.html('<span id="loader"></span>'); var nonce = $("input#voting_nonce").val(); var data = { action: 'add_votes_options', nonce: nonce, postid: '<?php echo $post->ID; ?>', ip: '<?php echo $_SERVER['REMOTE_ADDR']; ?>' }; $.post('<?php echo admin_url('admin-ajax.php'); ?>', data, function(response){ if(response!="-1") { el.html("VOTED").unbind("click"); if(response=="null") { alert("A vote has already been registered to this IP address."); } else { $("#votecounter").html(response); alert("Thanks for your vote."); } var cookie = getCookie("better_votes"); if(!cookie) { var newcookie = "<?php echo $post->ID; ?>"; } else { var newcookie = cookie + ",<?php echo $post->ID; ?>"; } setCookie("better_votes", newcookie, 365); } else { alert("There was a problem registering your vote. Please try again later."); } }); return false; }); })(jQuery); /* ]]> */ </script>
Now a cookie will be registered once the vote has been tallied. This is all the jQuery we need to make our voting system work. If jQuery is not already loaded in your WordPress theme, you may also need to add this into your header.php file before the wp_head()
code:
<?php wp_enqueue_script( 'jquery' ) ?>
The following is the PHP code that we need to process the vote on the server side. This will need to be placed in your theme’s functions.php file. If the file doesn’t exist, just create it and make sure this code is placed between the PHP tags:
add_action("wp_ajax_add_votes_options", "add_votes_options"); add_action("wp_ajax_nopriv_add_votes_options", "add_votes_options"); function add_votes_options() { if (!wp_verify_nonce($_POST['nonce'], 'voting_nonce')) return; $postid = $_POST['postid']; $ip = $_POST['ip']; $voter_ips = get_post_meta($postid, "voter_ips", true); if(!empty($voter_ips) && in_array($ip, $voter_ips)) { echo "null"; die(0); } else { $voter_ips[] = $ip; update_post_meta($postid, "voter_ips", $voter_ips); } $current_votes = get_post_meta($postid, "votes", true); $new_votes = intval($current_votes) + 1; update_post_meta($postid, "votes", $new_votes); $return = $new_votes>1 ? $new_votes." votes" : $new_votes." vote"; echo $return; die(0); }
The two action calls at the beginning hook into the Ajax function from the jQuery. We need both since one is for logged-in users and the other is for guests. Next we check for the security nonce, and end the process if it is not set correctly. This is a huge improvement over my old code that had absolutely no security whatsoever. If all is good, we start processing the vote by checking if the voters IP address has already been registered. If so, the process stops and returns a “null” value. Otherwise we registered the vote and IP address and echo out the total votes.
If you take a look at the jQuery above, you can see how the different responses will be processed. A “null” response will alert the voter that their IP address has already been registered so their new vote will not count. A response of “-1” means something went wrong and we alert the voter. If everything goes well, the vote is registered, the voter is thanked and the total number of votes increases on the page.
There you have all the jQuery and PHP required to get it working on the backend, but you still need some PHP/HTML in order for things to work on the front end.
<?php // This will display "0 votes" and increase as votes are added $votes = get_post_meta($post->ID, "votes", true); $votes = !empty($votes) ? $votes : "0"; if($votes == 1) $plural = ""; else $plural = "s"; echo '<div id="votecounter">'.$votes.' vote'.$plural.'</div>'; ?> <?php // This will display the vote button and disable it if a cookie has already // been set. We also add the security nonce here. $hasvoted = $_COOKIE['better_votes']; $hasvoted = explode(",", $hasvoted); if(in_array($post->ID, $hasvoted)) { $vtext = "VOTED"; $class = ' class="disabled"'; } else { $vtext = "VOTE"; $class = ""; } ?> <a href="javascript:void(0)" id="vote"<?php echo $class; ?>><?php echo $vtext; ?></a> <?php if(function_exists('wp_nonce_field')) wp_nonce_field('voting_nonce', 'voting_nonce'); ?>
So there you have it, a more efficient voting system for WordPress that is secure and will hopefully double guard you against fraudulent voting. You can see this working live on the new Bloodshots website. It will only be active between Oct 24th and Oct 28th while people can vote for the films.
23 comments for “A Better Voting System for WordPress”