poll.php

  1: <?php
  2: /****************************************************************************
  3:  * DRBPoll
  4:  * http://www.dbscripts.net/poll/
  5:  *
  6:  * Copyright � 2007-2009 Don B
  7:  ****************************************************************************/
  8: 
  9: $PREVENT_DUPLICATE_VOTES = TRUE;
 10: $SHOW_COUNTS = TRUE;
 11: 
 12: require_once(dirname(__FILE__) . '/config.php');
 13: 
 14: function show_vote_control($poll_id) {
 15: 	global $POLL_URL;
 16: 	global $VOTE_STRING;
 17: 	global $VOTE_PARAM_NAME;
 18: 	global $VOTE_LIST_DEFAULT_LABEL;
 19: 	global $VALID_POLLS;
 20: 	global $POLL_ID_PARAM_NAME;
 21: 	global $SUBMIT_BUTTON_STRING;
 22: 	global $VIEW_RESULTS_STRING;
 23: 
 24: 	// Validate parameters
 25: 	if(!is_valid_poll_id($poll_id)) {
 26: 		die('ERROR: An invalid poll ID was submitted.');
 27: 	}
 28: 
 29: 	$poll = $VALID_POLLS[$poll_id];
 30: 
 31: 	// Output vote control
 32: 	echo("<div class=\"voteContainer\">\r\n");
 33: 	echo("\t<form class=\"vote\" method=\"post\" action=\"" . $POLL_URL . "vote.php\">\r\n");
 34: 	echo("\t<fieldset>\r\n");
 35: 	if(!empty($poll->legend) && sizeof($poll->legend) > 0) {
 36: 		echo("\t<legend>" . htmlspecialchars($poll->legend) . "</legend>\r\n");
 37: 	}
 38: 	echo("\t<p class=\"question\">\r\n");
 39: 	echo("\t\t" . htmlspecialchars($poll->question) . "\r\n");
 40: 	echo("\t</p>\r\n\t<p>\r\n");
 41: 
 42: 	global $CONTROL_RADIOBUTTONS;
 43: 	global $CONTROL_COMBOBOX;
 44: 	if($poll->control_type === $CONTROL_RADIOBUTTONS) {
 45: 
 46: 		// Iterate through poll values
 47: 		foreach ($poll->values as $value_id => $description) {
 48: 			echo("\t\t<input type=\"radio\" name=\"" . htmlspecialchars($VOTE_PARAM_NAME) .
 49: 				"\" value=\"" . htmlspecialchars($value_id) . "\" /> ");
 50: 			if( isset($poll->urls[$value_id]) && !empty($poll->urls[$value_id]) ) {
 51: 				echo("<a href=\""  . htmlspecialchars($poll->urls[$value_id])
 52: 				. "\" target=\"_blank\" rel=\"nofollow\">"
 53: 				. htmlspecialchars($description) . "</a>");
 54: 			} else {
 55: 				echo(htmlspecialchars($description));
 56: 			}
 57: 			echo("<br />\r\n");
 58: 		}
 59: 
 60: 		echo("\t</p>\r\n\t<p>\r\n");
 61: 
 62: 	} else if($poll->control_type === $CONTROL_COMBOBOX) {
 63: 
 64: 		echo("\t\t" . htmlspecialchars($VOTE_STRING) . "\r\n");
 65: 
 66: 		echo("\t\t<select name=\"" . htmlspecialchars($VOTE_PARAM_NAME) . "\">\r\n");
 67: 		echo("\t\t\t<option value=\"\">" . htmlspecialchars($VOTE_LIST_DEFAULT_LABEL) . "</option>\r\n");
 68: 
 69: 		// Iterate through poll values
 70: 		foreach ($poll->values as $value_id => $description) {
 71: 			echo("\t\t\t<option value=\"" . htmlspecialchars($value_id) . "\">");
 72: 			echo(htmlspecialchars($description));
 73: 			echo("</option>\r\n");
 74: 		}
 75: 		echo("\t\t</select>\r\n");
 76: 
 77: 	} else {
 78: 
 79: 		die('ERROR: Invalid control type.');
 80: 
 81: 	}
 82: 	echo("\t\t<input type=\"hidden\" name=\"" . htmlspecialchars($POLL_ID_PARAM_NAME) . "\" value=\"" . htmlspecialchars($poll_id) . "\" />\r\n");
 83: 	echo("\t\t<input type=\"submit\" value=\"" . htmlspecialchars($SUBMIT_BUTTON_STRING) . "\" class=\"submit\" />\r\n");
 84: 	echo("\t</p>\r\n\t<p class=\"currentResults\">\r\n");
 85: 
 86: 	// Show results link
 87: 	echo("\t\t<a href=\"" . $POLL_URL . "results.php?" . htmlspecialchars($POLL_ID_PARAM_NAME) . "=" . htmlspecialchars($poll_id) . "\">" . htmlspecialchars($VIEW_RESULTS_STRING) . "</a>\r\n");
 88: 
 89: 	echo("\t</p>\r\n");
 90: 	echo("\t</fieldset>\r\n");
 91: 	echo("\t</form>\r\n");
 92: 	echo("</div>\r\n");
 93: 
 94: }
 95: 
 96: function show_poll_results($poll_id) {
 97: 
 98: 	// Validate parameters
 99: 	if(!is_valid_poll_id($poll_id)) {
100: 		die('ERROR: An invalid poll ID was submitted.');
101: 	}
102: 
103: 	// Get poll object
104: 	global $VALID_POLLS;
105: 	$poll = $VALID_POLLS[$poll_id];
106: 
107: 	// Get vote summary
108: 	$summarylist = vote_summary_list($poll_id);
109: 	if($summarylist !== FALSE) {
110: 		$totalVotes = $summarylist[0];
111: 		$largest_count = find_largest_vote_value_count($summarylist);
112: 	} else {
113: 		$totalVotes = 0;
114: 		$largest_count = 0;
115: 	}
116: 
117: 	// Show question
118: 	echo("\t<p class=\"question\">\r\n");
119: 	echo("\t\t" . htmlspecialchars($poll->question) . "\r\n");
120: 	echo("\t</p>\r\n");
121: 
122: 	// Start table for results
123: 	echo("<table class=\"pollTable\">\r\n");
124: 
125: 	// Iterate through poll values
126: 	$barNumber = 1;
127: 	global $MAX_POLL_BAR_WIDTH;
128: 	global $SHOW_COUNTS;
129: 	foreach($poll->values as $value_id => $description) {
130: 
131: 		// Find vote count for this value
132: 		$summary_row = find_vote_value_summary($value_id, $summarylist);
133: 
134: 		if($summary_row === FALSE) {
135: 			$count = 0;
136: 		} else {
137: 			$count = $summary_row[1];
138: 		}
139: 
140: 		$percentage = (($totalVotes > 0)?($count / $totalVotes):0);
141: 		$bar_percentage = (($largest_count > 0)?($count / $largest_count):0);
142: 		echo("\t<tr>");
143: 		echo("<td class=\"pollDescriptionCell\">");
144: 		if( isset($poll->urls[$value_id]) && !empty($poll->urls[$value_id]) ) {
145: 			echo("<a href=\""  . htmlspecialchars($poll->urls[$value_id])
146: 			. "\" target=\"_blank\" rel=\"nofollow\">"
147: 			. htmlspecialchars($description) . "</a>");
148: 		} else {
149: 			echo(htmlspecialchars($description));
150: 		}
151: 		echo("</td>");
152: 		echo("<td class=\"pollBarCell\">");
153: 		if($count > 0) {
154: 			echo("<div class=\"pollBar\" id=\"pollBar" . ($barNumber++) . "\" style=\"width:" . round($MAX_POLL_BAR_WIDTH * $bar_percentage, 0) . "px;\"></div>");
155: 		}
156: 		echo("</td>");
157: 		echo("<td class=\"pollCountCell\">" . (($SHOW_COUNTS === TRUE)?$count . " (":"") . (($count > 0)?round($percentage * 100, 2):0) . "%" . (($SHOW_COUNTS === TRUE)?")":"") . "</td>");
158: 		echo("</tr>\r\n");
159: 
160: 	}
161: 
162: 	echo("</table>");
163: 
164: 	if($SHOW_COUNTS === TRUE) {
165: 		global $NUMBER_OF_VOTES_STRING;
166: 		$numberOfVotesString = sprintf($NUMBER_OF_VOTES_STRING, $totalVotes);
167: 		echo("<p>" . htmlspecialchars($numberOfVotesString) . "</p>");
168: 	}
169: 
170: }
171: 
172: function the_current_poll_results() {
173: 	global $requested_poll_id;
174: 	show_poll_results($requested_poll_id);
175: }
176: 
177: function the_return_to_url() {
178: 	global $requested_poll_id;
179: 	global $VALID_POLLS;
180: 	$poll = $VALID_POLLS[$requested_poll_id];
181: 
182: 	if(!empty($poll->returnToURL)) {
183: 		echo $poll->returnToURL;
184: 	} else {
185: 		die("ERROR: Return to URL not defined for this poll.");
186: 	}
187: 
188: }
189: 
190: function smarter_is_int($val) {
191:     return (is_numeric($val)?intval($val)==$val:FALSE);
192: }
193: 
194: function is_valid_poll_id($poll_id) {
195: 	global $VALID_POLLS;
196: 	return (!empty($poll_id) && preg_match('/^[a-zA-Z0-9]+$/D', $poll_id) === 1 && array_key_exists($poll_id, $VALID_POLLS));
197: }
198: 
199: function is_valid_vote($poll, $vote_value_id) {
200: 	return (!empty($vote_value_id) && preg_match('/^[a-zA-Z0-9]+$/D', $vote_value_id) === 1 && array_key_exists($vote_value_id, $poll->values));
201: }
202: 
203: function get_vote_count($summarylist) {
204: 
205: 	// Get vote count from summary
206:  	if($summarylist === FALSE || count($summarylist) < 1) {
207:  		return 0;
208:  	} else {
209:  		return $summarylist[0];
210:  	}
211: 
212: }
213: 
214: function add_new_vote($poll_id, $vote_value_id) {
215: 	global $vote_error_message;
216: 
217: 	// Make sure vote wasn't left blank
218: 	if(empty($vote_value_id)) {
219: 		global $NO_VOTE_SELECTED_ERROR_MSG;
220: 		$vote_error_message = $NO_VOTE_SELECTED_ERROR_MSG;
221: 		return FALSE;
222: 	}
223: 
224: 	// Validate poll ID
225: 	if(!is_valid_poll_id($poll_id)) {
226: 		die('ERROR: An invalid poll ID was submitted.');
227: 	}
228: 
229: 	// Get poll object
230: 	global $VALID_POLLS;
231: 	$poll = $VALID_POLLS[$poll_id];
232: 
233: 	// Validate vote value ID
234: 	if(!is_valid_vote($poll, $vote_value_id) ) {
235: 		die('ERROR: An invalid vote was submitted.');
236: 	}
237: 
238: 	// Check for duplicate vote attempt
239: 	$ipaddress = $_SERVER['REMOTE_ADDR'];
240: 	global $PREVENT_DUPLICATE_VOTES;
241: 	if($PREVENT_DUPLICATE_VOTES && has_voted($poll_id, $ipaddress)) {
242: 		global $DUPLICATE_VOTE_ERROR_MSG;
243: 		$vote_error_message = $DUPLICATE_VOTE_ERROR_MSG;
244: 		return FALSE;
245: 	}
246: 
247: 	// Add vote
248: 	vote_history_add($poll_id, $ipaddress, $vote_value_id);
249: 	vote_summary_add($poll_id, $vote_value_id);
250: 
251: 	return TRUE;
252: }
253: 
254: function vote_summary_file_path($id) {
255:  	return dirname(__FILE__) . '/data/summary_' . $id . '.dat';
256: }
257: 
258: function vote_history_file_path($id) {
259: 	return dirname(__FILE__) . '/data/history_' . $id . '.dat';
260: }
261: 
262: function vote_summary_list($poll_id) {
263: 
264: 	// Load existing
265: 	$summarylist = @file(vote_summary_file_path($poll_id));
266: 	if($summarylist !== FALSE) {
267: 		$summarylist = array_map("trim", $summarylist);
268: 	}
269: 	return $summarylist;
270: 
271: }
272: 
273: function find_vote_value_summary($vote_value_id, $summarylist) {
274: 
275: 	if($summarylist === FALSE) return FALSE;
276: 	for($i = 1; $i < sizeof($summarylist); $i++) {
277: 
278: 		$summary_row = explode_history($summarylist[$i]);
279: 		if($summary_row[0] === ("" . $vote_value_id)) {
280: 			return $summary_row;
281: 		}
282: 
283: 	}
284: 	return FALSE;
285: 
286: }
287: 
288: function find_largest_vote_value_count($summarylist) {
289: 
290: 	$largest_count = 0;
291: 	for($i = 1; $i < sizeof($summarylist); $i++) {
292: 
293: 		$summary_row = explode_history($summarylist[$i]);
294: 		if($summary_row[1] > $largest_count) {
295: 			$largest_count = $summary_row[1];
296: 		}
297: 
298: 	}
299: 	return $largest_count;
300: 
301: }
302: 
303: function vote_summary_add($poll_id, $vote_value_id) {
304: 
305: 	// Get existing poll summary
306: 	$summarylist = vote_summary_list($poll_id);
307: 	$vote_summary_file_path = vote_summary_file_path($poll_id);
308: 
309: 	// Create summary file if it doesn't exist
310: 	if(!file_exists($vote_summary_file_path)) {
311: 		if(@touch($vote_summary_file_path) === FALSE) {
312: 			die("Unable to create summary file");
313: 		}
314: 	}
315: 
316: 	// Open summary file
317:  	$summary_fp = @fopen($vote_summary_file_path, "r+");
318:  	if($summary_fp === FALSE) {
319:  		die("Unable to open summary file for writing");
320:  	}
321:  	if(@flock($summary_fp, LOCK_EX) === FALSE) {
322:  		@fclose($summary_fp);
323:  		die("Unable to lock summary file");
324:  	}
325:  	if(@ftruncate($summary_fp, 0) === FALSE) {
326:  		@fclose($summary_fp);
327:  		die("Unable to truncate summary file");
328:  	}
329: 
330:  	// Update total vote count
331:  	if($summarylist === FALSE || count($summarylist) < 1) {
332:  		$count = 1;
333:  	} else {
334:  		$count = $summarylist[0] + 1;
335:  	}
336:  	fputs($summary_fp, $count . "\n");
337: 
338: 	// Add vote to value total
339: 	if($summarylist === FALSE) {
340: 
341: 		// First vote
342: 		$out = $vote_value_id . "|1";
343: 		fputs($summary_fp, $out . "\n");
344: 
345: 	} else {
346: 
347:  		// Iterate through existing vote values
348:  		$vote_counted = FALSE;
349:  		for($i = 1; $i < sizeof($summarylist); $i++) {
350: 
351:  			$summary_row = explode_history($summarylist[$i]);
352:  			if($summary_row[0] === $vote_value_id) {
353: 
354:  				// Increase vote count for this value
355:  				$summary_row[1] += 1;
356:  				$vote_counted = TRUE;
357: 
358:  			}
359: 
360:  			// Write out new vote count for this id
361:  			$out = implode("|", $summary_row);
362:  			fputs($summary_fp, $out . "\n");
363: 
364:  		}
365: 
366:  		if($vote_counted === FALSE) {
367: 
368:  			// This is the first vote for this value
369:  			$out = $vote_value_id . "|1";
370:  			fputs($summary_fp, $out . "\n");
371: 
372:  		}
373: 
374: 
375:  	}
376:  	fclose($summary_fp);
377: 
378: }
379: 
380: function vote_history_add($poll_id, $ipaddress, $vote_value_id) {
381: 
382: 	// Open/create history file
383:  	$history_fp = @fopen(vote_history_file_path($poll_id), "a");
384:  	if($history_fp === FALSE) {
385:  		die("Unable to open history file for writing");
386:  	}
387: 
388:  	// Add IP address and vote to history
389:  	@flock($history_fp, LOCK_EX);
390:  	fputs($history_fp, $ipaddress . "|" . $vote_value_id . "\n");
391:  	fclose($history_fp);
392: 
393: }
394: 
395: function explode_history($line) {
396: 	return array_map("trim", explode("|", $line));
397: }
398: 
399: function vote_history_list($poll_id) {
400: 
401: 	// Load existing vote history
402: 	$summarylist = @file(vote_history_file_path($poll_id));
403: 	if($summarylist !== FALSE) {
404: 		$summarylist = array_map("explode_history", $summarylist);
405: 	}
406: 
407: 	return $summarylist;
408: 
409: }
410: 
411: function has_voted($poll_id, $ipaddress) {
412: 
413: 	// Find vote history
414: 	$vote_history_list = vote_history_list($poll_id);
415: 	if($vote_history_list !== FALSE) {
416: 		return( find_vote_history(trim($ipaddress), $vote_history_list) !== FALSE );
417: 	} else {
418: 		return FALSE;
419: 	}
420: 
421: }
422: 
423: function find_vote_history($ipaddress, $list) {
424: 
425: 	// Search vote history for this IP address
426: 	if(!empty($list)) {
427: 		for($i = 0; $i < count($list); $i++) {
428: 			if($list[$i][0] == $ipaddress) return $list[$i];
429: 		}
430: 	}
431: 	return FALSE;
432: 
433: }
434: 
435: /* DO NOT REMOVE OR HIDE THE CREDIT BELOW, PER LICENSE! */
436: function the_credits() {
437: 	echo("<div class=\"credit\">Powered by DRBPoll &middot; <a href=\"http://www.dbscripts.net/hosting/\">PHP Hosting</a></div>\n");
438: }
439: /* END CREDIT */
440: 
441: ?>