Election software/index.php

From Wikimedia Australia
Jump to navigation Jump to search
<?php

include('config.php');

$conn = mysql_connect($dbhost, $dbuser, $dbpass) or die ('Error connecting to mysql');

mysql_select_db($dbname);

session_start();

$output = "";
$access = "standard";
$displayTemplate = true;

// ------------------------------------------------------------------------------------
// Login code
//
// Covers both creating a new account and logging in once one is created.
// ------------------------------------------------------------------------------------

// ------------------------------------------------------------------------------------
// Create account
// ------------------------------------------------------------------------------------

if ($_REQUEST['action'] == "create")
{
	// Clean up data submitted in case of problems (especially SQL injection attacks).
	$username = stripslashes($_REQUEST['name']);
	$useraddress = stripslashes($_REQUEST['address']);
	$useremail = stripslashes($_REQUEST['email']);
	$username = mysql_real_escape_string($username);
	$useraddress = mysql_real_escape_string($useraddress);
	$useremail = mysql_real_escape_string($useremail);

	// Make sure that a username was provided.
	if ($username != "")
	{
		// If email was included, load the details based on username and email.
		if ($useremail != "")
		{
			$sql = "SELECT * FROM Voters WHERE name='".$username."' AND email='".$useremail."'";
		}
		// If not, just rely on the username alone.
		else
		{
			$sql = "SELECT * FROM Voters WHERE name='".$username."'";
		}
	
		$result = mysql_query($sql);
	
		// Check to see if the details already exist. If a row was returned, they do, and thus
		// we can't use the combination, so I'll give an error message below.
		// Otherwise we can create the new account.
		
		if ($result && mysql_num_rows($result) == 0)
		{
			// This is a tad odd, but I wanted a unique password as it makes me feel a tad safer.
			// Thus this auto-generates a password (on the grounds that an auto-generated random
			// password is bound to be safer than the typical user-generated one), and then checks
			// to see if it already exists by querying the database. The odds are very small, so 
			// in almost all instances this will just be a minor speed bump, but it makes me feel
			// safer. If it does exist a new one will be generated, and it will keep checking.
			
			// Note that the password is encrypted using a Unix-style crypt function. Thus getting
			// access to the db won't give you access to a user's password. Of course, getting
			// access to the db is still very bad.
			
			// Thinking about it, I'm surprised I didn't use a do-while, but I never remember to
			// use them. :)
			
			$passwordcopies = 1;
			
			while ($passwordcopies > 0)
			{
				$userpassword = generatePassword(10);
				$encryptedUserPassword = crypt($userpassword, $salt);
				$sql = "SELECT * FROM Voters WHERE password = '$encryptedUserPassword'";
				$result = mysql_query($sql);
				$passwordcopies = mysql_num_rows($result);
			}
			
			// Reference code is a unique identifier for the particular user, employed for a single login.
			// As it is 20 digits, I'm not so concerned about conflicts. The main role here is to
			// cover any cases where someone has logged in, wandered off, and logged in at a different
			// computer - the second login generates a new reference code, and thus the old login 
			// will no longer function. It seemed like a good idea at the time.
			
			$referencecode = generatePassword(20);
			
			// Cool. Time to generate the SQL.
			
			$sql  = "INSERT INTO Voters (name, address, ip, lastaccessed, checked, approved, password, email, code) ";
			$sql .= "VALUES ('".$username."', '".$useraddress."', '12', Now(), false, false, '".$encryptedUserPassword."', '".$useremail."', '".$referencecode."')";
			
			mysql_query($sql);
			$userid = mysql_insert_id();
			
			// Register the session variables.
			// I've commented out the old approach, but left it here for legacy systems.
			//session_register("userid");
			//session_register("referencecode");
			
			$_SESSION['userid'] = $userid;
			$_SESSION['referencecode'] = $referencecode;
			
			// Almost done. Now for the messages. There are two possibilities. If they have entered
			// an email address, we can send them an email containing their random password and 
			// instructions. I do note that sending a secure password over email is a problem, but
			// such is the internet. Alternativly, if they didn't enter an email address, they 
			// get a message but, obviously, don't get an email. I generate that message twice
			// just in case later we want to say something different in each situation.
			
			if ($useremail != "")
			{
				$message = "Thankyou for registering to vote in the ".$year." Wikimedia Australia elections. ".
					"If you would like to change your vote, or if you choose not to ".
					"complete the process in one sitting, please visit ".$url." ".
					"and enter the code: ".$userpassword;
	
				$headers = 	"From: ".$email."\r\n".
    						"Reply-To: ".$email."\r\n".
    						"X-Mailer: PHP/".phpversion();
				mail($useremail, "WMAU Board Election" , $message, $headers);
				
				$output .= "<blockquote><em>Thankyou for registering to vote in the ".$year." Wikimedia Australia elections. ".
					"If you would like to change your vote once it has been submitted, or if you choose not to ".
					"complete the process in one sitting, please revisit this page ".
					"and enter the code: </em>".$userpassword."<em> - a copy of which has been sent to your email address.</em></blockquote>";
			}
			else
			{
				$output .= "<blockquote><em>Thankyou for registering to vote for the ".$year." Wikimedia Australia elections. ".
					"If you would later like to change your vote, or if you choose not to ".
					"complete the process in one sitting, please revisit this page ".
					"and enter the code: </em>".$userpassword."</blockquote>";
			}
			
			// As they have now registered we provide them with the instructions on what to do next.
		
			$output .=  file_get_contents($instructions);
		}
		
		// Error message in an account with that username or username + email has been previously 
		// created.
		else
		{
			$output .= '<p><em>An account with those details has already been created. Please contact <a href="mailto:'.$email.'">'.$email.'</a> for assistance.</em></p>';
		}
	}
}

// ------------------------------------------------------------------------------------
// Login
// ------------------------------------------------------------------------------------

elseif ($_REQUEST['action'] == "login" && $_REQUEST['password'] != "")
{
	// Make sure the submitted data is safe.
	$userpassword = stripslashes($_REQUEST['password']);
	$userpassword = mysql_real_escape_string($userpassword);
	
	// Encrypt the password - I store it in encrypted form, so we need to encrypt it here
	// to make sure it matches what is in the database, as the crypt function is one way. This
	// makes me happy.
	$encryptedUserPassword = crypt($userpassword, $salt);
	
	$sql = "SELECT * FROM Voters WHERE password = '$encryptedUserPassword'";
	
	$result = mysql_query($sql);
	
	// Check to see if there is an account with that password and username.
	
	if ($result && mysql_num_rows($result) == 1)
	{
		// If there is ...
		$row = mysql_fetch_array($result, MYSQL_ASSOC);
		$userid = $row['id'];
			
		// ... generate the session key ...
		$referencecode = generatePassword(20);
			
		// ... register the session variables ...
	
		$_SESSION['userid'] = $userid;
		$_SESSION['referencecode'] = $referencecode;
		
		//session_register("userid");			// Legacy code
		//session_register("referencecode");	// Legacy code
		
		// ... and update the database with the new code and the current date/time, before ...
		$sql  = "UPDATE Voters SET lastaccessed=Now(), code='".$referencecode."' WHERE id=".$userid;
		mysql_query($sql);
		
		// ... providing the instructions.
		$output .=  file_get_contents($instructions);
	}
}


// ------------------------------------------------------------------------------------
// Voting and Admin Functions
//
// Methods that are used if, and only if, the user has logged in.
// ------------------------------------------------------------------------------------

// First, check to see if the user has logged in. If not we'll need to give them the introduction 
// text instead. Note that this uses the newer isset($_SESSION[]) rather than the older
// session_is_registered(), although I've left the older form commented out for legacy code.

//if (!session_is_registered("userid"))
if (!isset($_SESSION['userid'])) 
{
//	$output .=  file_get_contents($introform);
	$output .=  str_replace('ditmar.php', $defaultpage, file_get_contents($introform));
}

// If they have logged in, we can start doing real work.
else
{
	// First, we need to grab their info, as we will need their details.
	$sql = "SELECT * FROM Voters WHERE id='".$_SESSION['userid']."'";
	
	$result = mysql_query($sql);
	
	// Hopefully someone was returned. If not something has gone horribly wrong.
	
	if ($result && mysql_num_rows($result) == 1)
	{
		$row = mysql_fetch_array($result, MYSQL_ASSOC);
		
		$referencecode = $row['code'];
		$access = $row['access'];
		
		// Quickly check to see if we need to generate the form. We do if they aren't creating an account,
		// logging in or haven't specified what the task is.

		if (!isset($_REQUEST['action']) || $_REQUEST['action'] == "login" || $_REQUEST['action'] == "create")
		{
			if ($access != "admin") // admins can't vote
			{
				$output .= generateVotingForm($year, $_SESSION['userid']);
			}
		}
		
		// Ok. If they don't need the form, what if they need to submit a vote? That's what this is 
		// for.

		if ($_REQUEST['action'] == "savevotes")
		{
			$errors = "";
		
			if ($referencecode == $_SESSION['referencecode'])
			{
				// Load nominees and votes.
				
				$categories = loadNominees($year);
				$votes = loadVotes($_SESSION['userid']);
			
				// Cool. Time to cycle through the nominees and record the votes.
			
				foreach ($categories as $categoryID => $categoryDetails)
				{
					$numberOfNominees = count($categoryDetails['nominees']);
				
					$voteCount = "";
					$tooBig = false;
					$nonInt = false;
					
					// Loop through the nominees so we can check to see if a value has been added.
					
					foreach ($categoryDetails['nominees'] as $nomineeDetails)
					{
						if (!isset($_REQUEST[$nomineeDetails['id']]) || $_REQUEST[$nomineeDetails['id']] == "") { $_REQUEST[$nomineeDetails['id']] = 0; }
						
						// Make sure that what they submitted was a number. If so we can save it.
					
						if (is_numeric($_REQUEST[$nomineeDetails['id']]))
						{
							// Record the vote to check for errors later.
							$voteCount[$_REQUEST[$nomineeDetails['id']]]++;
						
							// Check for a couple of quick errors better handled here.
							if ($_REQUEST[$nomineeDetails['id']] > $numberOfNominees) { $tooBig = true; } 						// Check to see if the numbers end at the right value
							else if ($_REQUEST[$nomineeDetails['id']] != intval($_REQUEST[$nomineeDetails['id']])) { $nonInt = true; } 	// Check to see if the numbers is an int or not
											
							// Record the vote.
							if ($votes[$nomineeDetails['id']] != "")
							{
								$sql = "UPDATE Votes SET vote = ".$_REQUEST[$nomineeDetails['id']]." WHERE voterid = ".$_SESSION['userid']." AND nomineeid = ".$nomineeDetails['id'];
								mysql_query($sql);
							}
							else
							{
								$sql = "INSERT INTO Votes (voterid, nomineeid, vote) VALUES (".$_SESSION['userid'].", ".$nomineeDetails['id'].", ".$_REQUEST[$nomineeDetails['id']].")";
								mysql_query($sql);
							}
						}
					}
					
					// Now that we've updated the votes, record the time the votes were entered.
			
					$sql  = "UPDATE Voters SET votesubmitted=Now() WHERE id=".$_SESSION['userid'];
					mysql_query($sql);
				
					$double = false;
					$endFound = false;
					$falseEnd = false;
				
					// This checks for come common errors. Note that I saved the values first - that was deliberate, as
					// I don't want to loose someone's votes even if they aren't correct. This way
					// they don't have to start again.
				
					for ($i = 1; $i <= $numberOfNominees; $i++)
					{
						if ($voteCount[$i] > 1) { $double = true; }			// Checks for two nominees with the same vote: eg, two in one category getting 3
						if ($voteCount[$i] == 0) { $endFound = true; }		
						else if ($endFound) { $falseEnd = true; }			// Checks to see if a number was skipped. Eg. Entering 1, 3, & 4, but not entering a 2.
					}
				
					// Work out the formal error messages.
					if ($tooBig || $nonInt || $double || $falseEnd)
					{
						$errors .= "<p>".$categoryDetails['name']."</p>\n<ul>\n";
				
						if ($tooBig) { $errors .= "<li>One of your values was too large</li>\n"; }
						if ($nonInt) { $errors .= "<li>Only whole numbers are able to be used</li>\n"; }
						if ($double) { $errors .= "<li>Two of the nominees have been given the same value</li>\n"; }
						if ($falseEnd) { $errors .= "<li>The values need to be sequential, starting from 1</li>\n"; }
				
						$errors .= "</ul>\n";
					}
				}
			
				if ($errors) { $errors = "<p>Some errors were detected in your submission. While your vote has been recorded, you may wish to make some changes.</p>\n\n".$errors; }
			}
		
			if ($_REQUEST['submit'] == "Save and Continue")
			{
				$output .= "<blockquote><p><em>Your votes have been saved.</em></p>$errors</blockquote>";
				$output .= generateVotingForm($year, $_SESSION['userid']);
			}
			else if ($errors)
			{
				$output .= "<blockquote><p>Thankyou for your votes. They have been recorded.</p>";
				$output .= $errors;
				$output .= "<p>If you have the code that was provided to when you you first logged in, you might wish to <a href=\"".$url."?action=logout\">quit</a> and return to make repairs later. ";
				$output .= "Otherwise, you can <a href=\"".$url."\">return to the voting form</a> and make some changes now.</p></blockquote>";
			}
			else
			{
				$output .= "<blockquote><p>Thankyou for your votes. They have been recorded.</p>";
				$output .= "<p>If you have retained the code provided earlier, you will be able to revisit this page should you wish to make changes to your votes. ";
				$output .= "</p></blockquote>";
				session_destroy();
			}
		}
		
// ------------------------------------------------------------------------------------
// Admin Functions
//
// Methods specifically for an admin.
// ------------------------------------------------------------------------------------
		
		if ($access == "admin")
		{
			// Load the admin menu
			$menu =  file_get_contents($adminmenu);
			
			// This handles updating voters as they are checked and (hopefully) approved.
			
			if ($_REQUEST['action'] == "updatevoters")
			{
				// Grab the voters.
				
				$sql = "SELECT * FROM Voters";
				
				$result = mysql_query($sql);
						
				$recordsUpdated = 0;
	
				// Loop through them, checking to see if a value was for that one, and update
				// the record accordingly.
				if ($result && mysql_num_rows($result) > 0)
				{
					while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
					{
						$checked = 0;
						if ($_REQUEST[$row['id']."_checked"] == "on") { $checked = 1; }
						
						$approved = 0;
						if ($_REQUEST[$row['id']."_approved"] == "on") { $approved = 1; }
						
						if ($checked != $row['checked'] || $approved != $row['approved'])
						{
							$sql  = "UPDATE Voters SET checked=".$checked.", approved=".$approved." WHERE id=".$row['id'];
							mysql_query($sql);
							$recordsUpdated++;
						}
					}
				}
				
				$output .= "<blockquote><em>".$recordsUpdated." records updated.</em></blockquote>\n";
				
				$_REQUEST['action'] = "voters";
			}
			
			// List all the voters so that we can update their status as they are checked and confirmed.

			if ($_REQUEST['action'] == "voters")
			{
				$output .= '<form action="'.$defaultpage.'" method="post">';
				$output .= '<input type="hidden" name="action" value="updatevoters" />';
		
				$output .= "<table width=\"100%\" cellpadding=\"2\" cellspacing=\"0\" border=\"0\">\n";
				$output .= "<tr><th>Name</th><th>Address</th><th>Email</th><th>Access</th><th>Accessed</th><th>Voted</th><th>Checked</th><th>Approved</th></tr>\n";
				
				$sql = "SELECT * FROM Voters";
				
				$result = mysql_query($sql);
	
				if ($result && mysql_num_rows($result) > 0)
				{
					while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
					{
						$output .= "\t<tr>\n";
						$output .= "\t\t<td>".$row['name']."</td>\n";
						$output .= "\t\t<td>".$row['address']."</td>\n";
						$output .= "\t\t<td><a href=\"mailto:".$row['email']."\">".$row['email']."</a></td>\n";
						$output .= "\t\t<td>".$row['access']."</td>\n";
						$output .= "\t\t<td>".$row['lastaccessed']."</td>\n";
						$output .= "\t\t<td><a href=\"?voterid=".$row['id']."&action=display\">".$row['votesubmitted']."</a></td>\n";
						$output .= "\t\t<td><input type=\"checkbox\" name=\"".$row['id']."_checked\"";
							if ($row['checked']) { $output .= " checked"; }
							$output .= " /></td>\n";
						$output .= "\t\t<td><input type=\"checkbox\" name=\"".$row['id']."_approved\"";
							if ($row['approved']) { $output .= " checked"; }
							$output .= " /></td>\n";
						$output .= "\t</tr>\n";
					}
				}
				
				$output .= "</table>";
				$output .= '<p><input type="submit" value="Update" /></p>';
				$output .= '</form>';
			}
			
			// Display the votes for a given voter.

			if ($_REQUEST['action'] == "display" && $_REQUEST['voterid'] != "")
			{
				
				$sql = "SELECT * FROM Voters WHERE id=".$_REQUEST['voterid'];
				
				$result = mysql_query($sql);
	
				if ($result && mysql_num_rows($result) == 1)
				{
					$row = mysql_fetch_array($result, MYSQL_ASSOC);
					
					$output .= "<p><strong>Name:</strong> ".$row['name']."<br />\n";
					$output .= "<p><strong>Address:</strong> ".$row['address']."<br />\n";
					$output .= "<p><strong>Email:</strong> <a href=\"mailto:".$row['email']."\">".$row['email']."</a><br />\n";
					$output .= "<p><strong>Checked:</strong> ";
						if ($row['checked']) { $output .= "Yes<br />\n"; } else { $output .= "No<br />\n"; }
					$output .= "<p><strong>Approved:</strong> ";
						if ($row['approved']) { $output .= "Yes<br />\n"; } else { $output .= "No<br />\n"; }
					$output .= "<p><strong>Submitted:</strong> ".$row['votesubmitted']."</p>\n";
						
					$output .= generateVotingForm($year, $row['id'], false, false);
				}
			}
			
			// List the categories in order to generate results
			
			if ($_REQUEST['action'] == "results")
			{
				$output .= "<h1>Result Generation</h1>";
				
				$output .= "<table cellpadding=\"3\" cellspacing=\"0\" border=\"0\">\n";
				$output .= "<tr bgcolor=\"#cccccc\"><td colspan=\"3\">Category</td></tr>\n";
				
				$sql = "SELECT * FROM Categories";

				$result = mysql_query($sql);
				
				if ($result && mysql_num_rows($result) > 0)
				{
					while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
					{
						$output .= "<tr><td>".$row['name']."</td>";
						$output .= "<td><a href=\"?action=export&category=".$row['id']."\">[Export]</a></td>";
						$output .= "<td><a href=\"?action=generate&category=".$row['id']."\">[Count]</a></td>";
					}
				}
			}
			
			if ($_REQUEST['action'] == "export")
			{
				$displayTemplate = false;
				$output = "";
				$category = stripslashes($_REQUEST['category']);
				include ("export.inc");
			}
			
			if ($_REQUEST['action'] == "generatefinalresult")
			{
				$category = stripslashes($_REQUEST['category']);
				$positions = stripslashes($_REQUEST['positions']);
				include ("tally.inc");
			}
			
			// In some cases, more than one candidate will need to be elected for a category.
			// For example, five people may be competing for three seats.
			// Accordingly, we need some code prior to the tally to enter how many positions
			// are needed. Later this number will be stored in the db, but for now I don't
			// want to modify the db.
			if ($_REQUEST['action'] == "generate")
			{
				$category = stripslashes($_REQUEST['category']);
				$year = stripslashes($_REQUEST['year']);
				
				// Grab the category details. Make sure that the category exists.
				$sql = "SELECT * FROM Categories WHERE id=".$category;
				
				$result = mysql_query($sql);
				
				if ($result && mysql_num_rows($result) == 1)
				{
					$category = mysql_fetch_array($result, MYSQL_ASSOC);
					
					$output = "<h2>Nominees for ".$category['name']."</h2>\n";
				
					$sql = "SELECT * FROM Nominees WHERE category=".$category['id'];

					$result = mysql_query($sql);
				
					if ($result && mysql_num_rows($result) > 0)
					{					
						// Nominees found. Give the help.
						$output .= "<p>The following are the nominees for this category. If, ";
						$output .= "for whatever reason, one of these nominees must be precluded, ";
						$output .= "select the box next to their name. This will remove them ";
						$output .= "from the election, and any votes will be redistributed.</p>";
				
						$output .= '<form action="'.$defaultpage.'" method="post">';
						$output .= '<input type="hidden" name="action" value="generatefinalresult" />';
						$output .= '<input type="hidden" name="category" value="'.$category['id'].'" />';
					
						$output .= "<table><tr><th>Nominee</th><th>Exclude</th></tr>\n";
					
						while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
						{
							$output .= "<tr><td>".$row['description']."</td>";
							$output .= "<td><input type=\"checkbox\" name=\"".$row['id']."_checked\"/></td></tr>\n";
						}
					
						$output .= "</table>\n";
						
						$output .= "<p>Most categries are expected to have only one winner. ";
						$output .= "If this category requires more than one winner, increase ";
						$output .= "the value in the \"Positions available\" box.</p>";
						
						$output .= "<p>Positions available: <input type=\"text\" name=\"positions\" value=\"1\" /></p>\n";
				
						$output .= '<p><input type="submit" value="Count votes" /></p>';
						$output .= '</form>';
					}
					else
					{
						$output .= "<p>No candidates found.</p>\n";
					}
				}
				else
				{
					$output .= "<p>No category found.</p>\n";
				}
				
				//include ("tally.inc");
			}
				
		}
		
		// Logout.

		if ($_REQUEST['action'] == "logoff")
		{
			$output .= "<p>Thankyou for using the Ditmarator for your WMAU voting needs.</p>";

			unset($_SESSION['userid']);
			unset($_SESSION['referencecode']);
		}
	}
}
			
// ------------------------------------------------------------------------------------
// Generate Password
//
// A simple bit of code, using a fairly ugly method, that generates a random combination
// of upper case letters, lower case letters and numbers of a secified length.
// ------------------------------------------------------------------------------------

function generatePassword($size = 10)
{
	$newpassword = "";
	
	for ($i = 0; $i < $size; $i++)
	{
		$type = rand(0,2);
		
		if ($type == 0) { $newpassword .= rand(1,9); }
		
		if ($type == 1) { $newpassword .= chr(rand(65,90)); }
		
		if ($type == 2) { $newpassword .= chr(rand(97,122)); }
	}
	
	return $newpassword;
}

// ------------------------------------------------------------------------------------
// Load Nominees
//
// Loads the nominees and categories for a given year.
// ------------------------------------------------------------------------------------

function loadNominees($year)
{
	// This joins the two tables - nominees and categories - based on category ID, but limits them 
	// to the current year. Note that the categories have an order number, which they are sorted on.
	
	$query  = "SELECT Nominees.id, Nominees.description, Nominees.category, Categories.name FROM Categories, Nominees WHERE Categories.id = Nominees.category AND year = ".$year." ORDER BY Categories.order, Nominees.id";
	$result = mysql_query($query);

	while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
	{
		// If some categories and nominees were found for the given year, this drops them all into
		// a big set of multi-dimensional arrays (objects would be trendier and more robust, but 
		// this will do) and returns them ready for whatever the calling method had in mind.
		
		$categories[$row['category']]['name'] = $row['name'];
	
		$numNominees = count($categories[$row['category']]['nominees']);
	
		$categories[$row['category']]['nominees'][$numNominees]['id'] = $row['id'];
		$categories[$row['category']]['nominees'][$numNominees]['description'] = $row['description'];  
	}
	
	return $categories;
}

// ------------------------------------------------------------------------------------
// Load the votes that have been submitted
//
// Loads all the votes from a given user. Doesn't care about year, but that will be
// fixed when the connection with nominees is made.
// ------------------------------------------------------------------------------------

function loadVotes($userid)
{
	$sql = "SELECT * FROM Votes WHERE voterid = ".$userid;
	
	$result = mysql_query($sql);
	
	if ($result && mysql_num_rows($result) > 0)
	{
		while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
		{
			$votes[$row['nomineeid']] = $row['vote'];
		}
	}
	
	return $votes;
}

// ------------------------------------------------------------------------------------
// Generate the Voting Form
//
// Generates the voting form by querying the database.
//
//	$year:  	the year to collect the votes for.
//  $userid:   	the user who is about to vote.
//  $editable: 	whether or not this form can be edited. The default is true.
//  $random: 	whether or place nominees for each category in random order. The default is false.
// ------------------------------------------------------------------------------------

function generateVotingForm($year, $userid, $editable = true, $random = false)
{
	// These first two are interesting. The first loads the categories, which I guess
	// is less interesting than I originally thought. But the second loads the current
	// user's votes. This is cool, as we can now populate the form with what they have
	// already entered, so they don't need to start from scratch each time.
	
	$categories = loadNominees($year);
	$votes = loadVotes($userid);
	
	$form = "";
	
	// It is possible to generate a display-only form. If it isn't editable, don't add
	// the form details. Otherwise we'll need then.
	
	if ($editable)
	{
		$form .= '<form action="'.$defaultpage.'" method="post">';
		$form .= '<input type="hidden" name="action" value="savevotes" />';
		$display = "enabled";
	}
	else
	{
		$display = "disabled";
	}
	
	// Cool. Now we loop through all the categories we've loaded ...
	
	foreach($categories as $categoryID => $content)
	{
		$form .= '<div class="votingcategory"><div class="votingcategorytitle">'.$content['name'].'</div>';
		$form .= "\n";
		
		$count = 0;
		$noawardText = "";
		
		// Then loop through all the nominees ...
			
		foreach ($content['nominees'] as $nomineeDetails)
		{
			if ($votes[$nomineeDetails['id']] == 0) { $votes[$nomineeDetails['id']] = ""; }
			
			// And generate the code. 
			
			$nomineeText = '<div class="votingnominee"><input type="text" name="'.$nomineeDetails['id'].'" size="1" value="'.$votes[$nomineeDetails['id']].'" '.$display.' /> '.$nomineeDetails['description'].'</div>';
			$nomineeText .= "\n";
			
			// No Award is a special case, so this manages it. The aim is for "No award"
			// always to come last, even if the listing is random, so we need to identify
			// which nominee it is and treat it separately.
				
			if ($nomineeDetails['description'] == "No Award")
			{
				$noawardText = $nomineeText;
			}
			else
			{
				$nomineeCode[$count++] = $nomineeText;
			}
		}
		
		// If it is to be random, this handles the random order for the nominees.
		
		if ($random)
		{
			for ($i = 0; $i < $count; $i++)
			{
				$chosen = rand(0,count($nomineeCode)-1);
				$form .= $nomineeCode[$chosen];
				for ($j = $chosen; $j < count($nomineeCode) - 1; $j++)
				{
					$nomineeCode[$j] = $nomineeCode[$j+1]; 
				}
				unset($nomineeCode[count($nomineeCode)-1]);
			}
		}
		
		// Otherwise we just add them in order.
		else
		{
			for ($i = 0; $i < $count; $i++)
			{
				$form .= $nomineeCode[$i];
			}
		}
			
		// No Award is added to the end whatever happens.
		$form .= $noawardText;
			
		$form .= "</div>\n";
	}
	
	// Finish off the form stuff.
	
	if ($editable)
	{
		$form .= '<p><input type="submit" name="submit" value="Save and Continue" />';
		$form .= ' <input type="submit" name="submit" value="Save and Quit" /></p>';
		$form .= '</form>';
	}
	
	return $form;
}

if($displayTemplate) { include($template); }

mysql_close($conn);

?>