Only in C:\Documents and Settings\JD\workspace\advpoll: .project
diff -ur C:\Documents and Settings\JD\workspace\advpoll/advpoll-form.js advpoll/advpoll-form.js
--- C:\Documents and Settings\JD\workspace\advpoll/advpoll-form.js	2006-11-29 02:27:06.000000000 +0000
+++ advpoll/advpoll-form.js	2007-06-04 17:16:23.625000000 +0100
@@ -45,7 +45,11 @@
       // Give each label it's correct number
       $(this).html($(this).html().replace(/\d+(?=<)/g, i++));
     });
-    
+    // Add an extra maxChoice if write-ins are enabled
+    if ($("#edit-settings-writeins-allow").attr("checked")) {
+      i++;
+    }
+        
     Drupal.advpoll.maxChoices(i-1);
     
     return false;
@@ -67,6 +71,15 @@
   }
 }
 
+Drupal.advpoll.updateWriteinsAllow = function() {
+  if ($("#edit-settings-writeins-allow").attr("checked")) {
+    Drupal.advpoll.maxChoices($("#edit-settings-maxchoices").children().length);
+  }
+  else {
+    Drupal.advpoll.maxChoices($("#edit-settings-maxchoices").children().length - 2);
+  }
+}
+
 Drupal.advpoll.nodeFormAutoAttach = function() {
   // Hide "need more choices" checkbox
   $("#morechoices").hide();
@@ -75,6 +88,15 @@
   Drupal.advpoll.updateStartDate();
   $("#edit-settings-usestart").click(Drupal.advpoll.updateStartDate);
   
+  // Remove extra maxChoices entry from write-ins
+  if (!$("#edit-settings-writeins-allow").attr("checked")) {
+    Drupal.advpoll.maxChoices($("#edit-settings-maxchoices").children().length-2);
+  }
+  
+  // Update maxChoices when user checks/unchecks write-ins box
+  Drupal.advpoll.updateStartDate();
+  $("#edit-settings-writeins-allow").click(Drupal.advpoll.updateWriteinsAllow);
+  
   // Insert Remove links
   $('<a class="remove-choice" href="#">' + Drupal.settings.advPoll.remove + '</a>').insertAfter("input.choices");
   Drupal.advpoll.removeChoiceClick();
@@ -96,6 +118,11 @@
     
     Drupal.advpoll.removeChoiceClick();
     
+    // Add an extra maxChoice if write-ins are enabled
+    if ($("#edit-settings-writeins-allow").attr("checked")) {
+      newChoiceN++;
+    }
+    
     Drupal.advpoll.maxChoices(newChoiceN);
     
     return false;
diff -ur C:\Documents and Settings\JD\workspace\advpoll/advpoll.install advpoll/advpoll.install
--- C:\Documents and Settings\JD\workspace\advpoll/advpoll.install	2006-11-28 06:36:17.000000000 +0000
+++ advpoll/advpoll.install	2007-06-03 03:35:46.625000000 +0100
@@ -19,7 +19,9 @@
           `maxchoices` int unsigned NOT NULL default '0',
           `algorithm` VARCHAR(100),
           `showvotes` tinyint,
-          `startdate` int unsigned,
+          `startdate` int unsigned,
+          `writeins' tinyint NOT NULL default '0',
+          `displaywriteins' tinyint NOT NULL default '0',
           PRIMARY KEY  (`nid`)
       ) ENGINE=MyISAM /*!40100 DEFAULT CHARACTER SET utf8 */");
 
@@ -32,7 +34,8 @@
       db_query("CREATE TABLE {advpoll_choices} (
           `nid` int(10) NOT NULL,
           `label` text NOT NULL,
-          `vote_offset` int(2) unsigned default NULL,
+          `vote_offset` int(2) unsigned default NULL,
+          `writein` tinyint NOT NULL default '0',
           PRIMARY KEY  (`nid`, `vote_offset`),
           KEY `vote_offset` (`vote_offset`)
         ) ENGINE=MyISAM /*!40100 DEFAULT CHARACTER SET utf8 */");
@@ -49,7 +52,9 @@
           maxchoices integer NOT NULL DEFAULT '0',
           algorithm varchar(100),
           showvotes smallint,
-          startdate integer,
+          startdate integer,
+          writeins smallint NOT NULL DEFAULT '0',
+          displaywriteins smallint NOT NULL DEFAULT '0',
           PRIMARY KEY (nid)
       )");
 
@@ -62,12 +67,34 @@
       db_query("CREATE TABLE {advpoll_choices} (
           nid integer NOT NULL,
           label text NOT NULL,
-          vote_offset smallint DEFAULT NULL,
+          vote_offset smallint DEFAULT NULL,
+          writein smallint NOT NULL DEFAULT '0',
           PRIMARY KEY (nid, vote_offset)
       )");
       db_query("CREATE INDEX {advpoll_choices}_vote_offset_idx ON {advpoll_choices} (vote_offset)");
       break;
   }
+}
+
+/**
+ * Implementation of hook_update_N().
+ */
+function advpoll_update_1() {
+  $ret = array();
+  switch ($GLOBALS['db_type']) {
+    case 'mysql':
+    case 'mysqli':
+      $ret[] = update_sql("ALTER TABLE {advpoll} ADD `writeins` TINYINT NOT NULL DEFAULT '0'");
+      $ret[] = update_sql("ALTER TABLE {advpoll} ADD `displaywriteins` TINYINT NOT NULL DEFAULT '0'");
+      $ret[] = update_sql("ALTER TABLE {advpoll_choices} ADD `writein` TINYINT NOT NULL DEFAULT '0'");
+      break;
+    case 'pgsql':
+      $ret[] = update_sql("ALTER TABLE {advpoll} ADD writeins SMALLINT NOT NULL DEFAULT '0'");
+      $ret[] = update_sql("ALTER TABLE {advpoll} ADD displaywriteins SMALLINT NOT NULL DEFAULT '0'");
+      $ret[] = update_sql("ALTER TABLE {advpoll_choices} ADD writein SMALLINT NOT NULL DEFAULT '0'");
+      break;    
+    }
+    return $ret;
 }
 
 /**
diff -ur C:\Documents and Settings\JD\workspace\advpoll/advpoll.module advpoll/advpoll.module
--- C:\Documents and Settings\JD\workspace\advpoll/advpoll.module	2006-11-29 02:27:06.000000000 +0000
+++ advpoll/advpoll.module	2007-06-06 04:09:32.750000000 +0100
@@ -202,7 +202,7 @@
     '#tree' => TRUE,
     '#weight' => 1,
   );
-  
+
   $form['choice']['morechoices'] = array(
     '#type' => 'checkbox',
     '#title' => t('Need more choices'),
@@ -229,12 +229,38 @@
     '#collapsible' => TRUE,
     '#weight' => 2,
     '#tree' => TRUE,
+  );
+
+  $form['settings']['writeins'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Write-in voting settings'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+
+  $form['settings']['writeins']['allow'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Allow user to cast a write-in vote'),
+    '#default_value' => (isset($node->writeins)? $node->writeins : 0),
+    '#description' => t('Enabling this option will allow an eligible voter with the \'add write-ins\' permission to write-in up to one choice.'),
+  );
+
+  $form['settings']['writeins']['display'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Display write-in votes as choices for future voters'),
+    '#default_value' => (isset($node->displaywriteins)? $node->displaywriteins : 0),
+    '#description' => t('Enabling this option will allow a voter to see and choose from previous voters\' write-in votes.'),
   );
 
   $maxChoiceList = array();
   for ($i = 0; $i <= $choices; $i++) {
     $maxChoiceList[$i] = ($i == 0? 'No limit' : $i);
-  }
+  }
+  
+  // Allow for one more choice because write-ins may be enabled. If Javascript 
+  // is enabled, this will be undone to allow jQuery to adjust the maxChoices 
+  // based on whether the write-ins box is checked.
+  $maxChoiceList[$i] = $i;
 
   $form['settings']['maxchoices'] = array(
     '#type' => 'select',
@@ -363,7 +389,7 @@
 function advpoll_load($node) {
   global $user;
   $poll = db_fetch_object(db_query("SELECT * FROM {advpoll} WHERE nid = %d", $node->nid));
-  $result = db_query("SELECT vote_offset, label FROM {advpoll_choices} WHERE nid = %d ORDER BY vote_offset", $node->nid);
+  $result = db_query("SELECT vote_offset, label, writein FROM {advpoll_choices} WHERE nid = %d ORDER BY vote_offset", $node->nid);
   while ($choice = db_fetch_array($result)) {
     $poll->choice[$choice['vote_offset']] = $choice;
   }
@@ -742,7 +768,7 @@
  * Implementation of hook_perm().
  */
 function advpoll_perm() {
-  return array('create polls', 'delete polls', 'view polls', 'vote on polls', 'cancel own vote', 'administer polls', 'inspect all votes');
+  return array('create polls', 'delete polls', 'view polls', 'vote on polls', 'cancel own vote', 'administer polls', 'inspect all votes', 'add write-ins');
 }
 
 /**
@@ -795,11 +821,12 @@
     $node->settings['active'] = _advpoll_calculate_active($node);
   }
 
-  db_query("UPDATE {advpoll} SET active=%d, runtime=%d, maxchoices=%d, algorithm='%s', uselist=%d, showvotes=%d, startdate=%s WHERE nid = %d",
+  db_query("UPDATE {advpoll} SET active=%d, runtime=%d, maxchoices=%d, algorithm='%s', uselist=%d, showvotes=%d, startdate=%s, writeins=%d, displaywriteins=%d WHERE nid = %d",
     $node->settings['active'], $node->settings['runtime'],
     $node->settings['maxchoices'], $node->settings['algorithm'],
     $node->settings['uselist'], $node->settings['showvotes'],
-    $node->settings['usestart']? _advpoll_create_startdate($node): 'NULL',
+    $node->settings['usestart']? _advpoll_create_startdate($node): 'NULL',
+    $node->settings['writeins']['allow'], $node->settings['writeins']['display'],
     $node->nid);
 
   _advpoll_insert_choices($node);
@@ -843,7 +870,7 @@
   $i = 1;
   foreach ($_POST['choice'] as $choice) {
     if ($choice['label'] != '') {
-      db_query("INSERT INTO {advpoll_choices} (nid, label, vote_offset) VALUES (%d, '%s', %d)", $node->nid, check_plain($choice['label']), $i++);
+      db_query("INSERT INTO {advpoll_choices} (nid, label, vote_offset, writein) VALUES (%d, '%s', %d, 0)", $node->nid, check_plain($choice['label']), $i++);
     }
   }
 }
@@ -871,12 +898,13 @@
   }
 
   db_query('INSERT INTO {advpoll} (nid, mode, uselist, active, runtime, '
-    . 'maxchoices, algorithm, showvotes, startdate)'
-    . ' VALUES (%d, "%s", %d, %d, %d, %d, "%s", %d, %s)',
+    . 'maxchoices, algorithm, showvotes, startdate, writeins, displaywriteins)'
+    . ' VALUES (%d, "%s", %d, %d, %d, %d, "%s", %d, %s, %d, %d)',
     $node->nid, $mode, $node->settings['uselist'], $node->settings['active'],
     $node->settings['runtime'], $node->settings['maxchoices'],
     $node->settings['algorithm'], $node->settings['showvotes'],
-    $node->settings['usestart']? _advpoll_create_startdate($node): 'NULL');
+    $node->settings['usestart']? _advpoll_create_startdate($node): 'NULL',
+    $node->settings['writeins']['allow'], $node->settings['writeins']['display']);
 
   // create the electoral list if desired
 
@@ -954,7 +982,12 @@
       if ($choice['label'] != '') {
         $realchoices++;
       }
-    }
+    }
+    
+    // Add one to counter if the write-ins are enabled for this node
+	if($node->writeins) {
+	  $realchoices++;
+	}
 
     if ($realchoices < 2) {
       form_set_error("choice][$realchoices][label", t('You must fill in at least two choices.'));
@@ -966,7 +999,8 @@
       form_set_error('settings][maxchoices]', t('Maximum choices must be a non-negative integer.'));
     }
 
-    if ($node->settings['maxchoices'] > count($node->choice)) {
+    if ((!$node->writeins && $node->settings['maxchoices'] > count($node->choice)) ||
+        (($node->writeins && $node->settings['maxchoices'] > count($node->choice) + 1))) {
       form_set_error('settings][maxchoices]', t('Maximum choices cannot be larger than the number of choices submitted.'));
     }
   }
@@ -1246,4 +1280,110 @@
     }
   }
   return $_advpoll_modes;
-}
+}
+
+/**
+ * Voting form validation logic specific to writeins. This has been abstracted 
+ * away from includes in the modes directory.
+ */
+function _advpoll_writeins_voting_form_validate($node, $writein_option, $writein_text, &$errors, &$ok, $ajax) {
+  // Do write-in specific checks if write-ins are enabled and user has permission
+  if($node->writeins && user_access('add write-ins')) {
+    // If something is in the write-in textbox
+    if($writein_text) {
+      $writein_choice_lower = strtolower($writein_text);
+      foreach ($node->choice as $i => $val) {
+        // Check that user isn't writing in an existing visible choice. (If user
+        // is writing in an existing choice and either write-ins are all being 
+        // displayed or the existing choice is not a write-in,)
+        if ((strtolower($val['label']) == $writein_choice_lower) && ($node->displaywriteins || !$val['writein'])) {
+          $msg = t('A write-in vote can not be for an existing choice. Select the choice\'s option instead.');
+          if ($ajax) {
+            $errors[] = $msg;
+          }
+          else {
+            form_set_error('writein_choice', $msg);
+          }
+          $ok = false;
+        }
+      }
+    }
+
+    // If the write-in option is selected and there is nothing in the write-in textbox  
+    if($writein_option && !$writein_text) {
+      $msg = t('If the \'write-in\' option is selected, a choice must be written in.');
+      if ($ajax) {
+        $errors[] = $msg;
+      }
+      else {
+        form_set_error('writein_choice', $msg);
+      }
+      $ok = false;      
+    }
+
+    // If the write-in option is not selected but there is something in the write-in textbox 
+    if(!$writein_option && $writein_text) {
+      $msg = t('If a choice is written in, the \'write-in\' option must be selected.');
+      if ($ajax) {
+        $errors[] = $msg;
+      }
+      else {
+        form_set_error('writein_choice', $msg);
+      }
+      $ok = false;
+    }
+  }
+}
+
+/**
+ * Voting form submission logic specific to writeins. This has been abstracted 
+ * away from includes in the modes directory.
+ */
+function _advpoll_writeins_voting_form_submit($node, $form_values, &$vote, $vote_value) {
+  // A write-in vote is being made
+  if($form_values['choice'][$form_values['writein_key']]) {
+    // Check if someone has previously voted for this choice
+    $result = db_query("SELECT vote_offset FROM {advpoll_choices} WHERE nid = %d AND LOWER(label) = LOWER('%s')", $node->nid, $form_values['writein_choice']);
+    // If there's more than one match, redo the query, being more exact
+    if(db_num_rows($result) > 1) {
+      $result = db_query("SELECT vote_offset FROM {advpoll_choices} WHERE nid = %d AND label = '%s'", $node->nid, $form_values['writein_choice']);      
+    }
+    // If there is at least one match, add a vote for the first one returned. It
+	// should be rare to find more than one choice for any one node with a given
+	// label.
+    if(db_num_rows($result)) {
+      $obj = db_fetch_object($result);
+      $existing_vote_offset = $obj->vote_offset;
+      // Set a vote
+      unset($temp);
+      $temp->value = $vote_value;
+      $temp->tag = $existing_vote_offset;
+      $temp->value_type = 'option';
+      $vote[] = $temp;
+    }
+    // This write-in choice has not been previously voted for
+    else {
+      // Get last vote offset for this node
+      $result = db_query("SELECT MAX(vote_offset) as last_vote_offset FROM {advpoll_choices} WHERE nid = %d", $node->nid);
+      $obj = db_fetch_object($result);
+      // Set the last tag value
+      $last_vote_offset = $obj->last_vote_offset;
+      // Default value
+      if(!$last_vote_offset) {
+        // Start at one rather than 0 due to Drupal FormAPI
+        $last_vote_offset = 1;
+      }
+
+      // Insert new choice into node
+      db_query("INSERT INTO {advpoll_choices} (nid, label, vote_offset, writein) VALUES (%d, '%s', %d, 1)", $node->nid, check_plain($form_values['writein_choice']), $last_vote_offset+1);
+      
+      // Add vote
+      unset($temp);
+      $temp->value = $vote_value;
+      $temp->tag = $last_vote_offset+1;
+      $temp->value_type = 'option';
+      $vote[] = $temp;
+    }
+  }
+}
+  
\ No newline at end of file
diff -ur C:\Documents and Settings\JD\workspace\advpoll/modes/binary.inc advpoll/modes/binary.inc
--- C:\Documents and Settings\JD\workspace\advpoll/modes/binary.inc	2006-11-29 02:27:06.000000000 +0000
+++ advpoll/modes/binary.inc	2007-06-06 03:46:47.296875000 +0100
@@ -22,13 +22,23 @@
   if ($node->choice) {
     $list = array();
     foreach ($node->choice as $i => $choice) {
-      // Don't show blank choices
-      if ($choice['label']) {
+      // Don't show blank choices or write-in votes if the setting is disabled
+      if ($choice['label'] && ($node->displaywriteins || !$choice['writein'])) {
          $list[$i] = check_plain($choice['label']);
       }
-    }
+    }
+    // Add write-in checkbox/radio if write-ins are enabled and user has permission
+    if($node->writeins && user_access('add write-ins')) {
+      $list[$i+1] = t('(write-in)');
+      $form['writein_key'] = array(
+        '#type' => 'value',
+        '#value' => $i+1,
+      );
+    }
+    
+
     $form['choice'] = array(
-       '#title' => $page ? '' : check_plain($node->title),
+      '#title' => $page ? '' : check_plain($node->title),
       '#options' => $list,
     );
 
@@ -41,6 +51,15 @@
       // Approval voting
       $form['choice']['#type'] = 'checkboxes';
     }
+  }
+
+  // Add write-in text field if write-ins are enabled and user has permission
+  if($node->writeins && user_access('add write-ins')) {
+    $form['writein_choice'] = array (
+      '#type' => 'textfield',
+      '#title' => t('Write-in vote'),
+      '#size' => 25,
+    );
   }
 
   $form['nid'] = array(
@@ -117,28 +136,38 @@
  */
 function advpoll_voting_binary_form_submit($form_id, $form_values) {
   $vote = array();
-  $node = node_load($form_values['nid']);
-  if ($node->maxchoices == 1) {
-    // Plurality voting
-    $temp->value = 1;
-    $temp->tag = $form_values['choice'];
-    $temp->value_type = 'option';
-    $vote[] = $temp;
-  }
-  else {
-    // Approval voting
-    foreach ($form_values['choice'] as $choice => $selected) {
+  $node = node_load($form_values['nid']);
+
+  // Do submission specific to writeins
+  _advpoll_writeins_voting_form_submit($node, $form_values, $vote, 1);
+
+  // Plurality voting
+  if ($node->maxchoices == 1) {
+    // Ignore write-in choice that has already taken care of
+    if(!$form_values['choice'][$form_values['writein_key']]) {
       unset($temp);
-      $temp->value = $choice;
-      if ($selected) {
-        $temp->value_type = 'option';
-        $temp->tag = $choice;
-        $temp->value = 1;
-        $vote[] = $temp;
+      $temp->value = 1;
+      $temp->tag = $form_values['choice'];
+      $temp->value_type = 'option';
+      $vote[] = $temp;
+    }
+  }
+  // Approval voting
+  else {
+    foreach ($form_values['choice'] as $choice => $selected) {
+      // Ignore write-in choice that has already taken care of
+      if($choice != $form_values['writein_key']) {
+        unset($temp);
+        $temp->value = $choice;
+        if ($selected) {
+          $temp->value_type = 'option';
+          $temp->tag = $choice;
+          $temp->value = 1;
+          $vote[] = $temp;
+        }
       }
     }
   }
-
   votingapi_set_vote('advpoll', $form_values['nid'], $vote);
   _advpoll_vote_response($node, $_POST['ajax']);
 }
@@ -151,10 +180,22 @@
 function advpoll_voting_binary_form_validate($form_id, $form_values) {
   $ok = true;
   $node = node_load($form_values['nid']);
-  $ajax = $_POST['ajax'];
-  if ($node->maxchoices == 1) {
-    // plurality voting
-    if (!($ok = array_key_exists($form_values['choice'], $node->choice))) {
+  $ajax = $_POST['ajax'];
+
+  // Whether the write-in option is selected. This is calculated differently for
+  // radio buttons and checkboxes.
+  $writein_option = false;
+  $writein_text = $form_values['writein_key'] ? $form_values['writein_choice'] : '';
+
+  // Plurality voting (radio buttons)
+  if ($node->maxchoices == 1) {
+    // If write-ins are enabled and user has permission and it is the write-in option
+    if($node->writeins && user_access('add write-ins') && $form_values['choice'][$form_values['writein_key']]) {
+      // Set the flag to true for additional checks
+      $writein_option = true;
+    }
+    // Nothing is selected
+    else {
       $msg = t('At least one choice must be selected.');
       if ($ajax) {
         $errors[] = $msg;
@@ -162,21 +203,28 @@
       else {
         form_set_error('choice[', $msg);
       }
-      $ok = false;
+      $ok = false;
     }
-  }
-  else {
-    // approval voting
-
-    $numchoices = 0;
+  }
+  // Approval voting (checkboxes)
+  else {
+    $numchoices = 0;
+
+    // Count how many options are selected
+    foreach ($node->choice as $i => $val) {
+      if ($form_values['choice'][$i]) {
+        $numchoices++;
+      }
+    }
+
+    // If write-ins are enabled and user has permission and the write-in box is checked
+    if($node->writeins && user_access('add write-ins') && $form_values['choice'][$form_values['writein_key']]) {
+      // Add one to number of choices for check on min/max boxes checked
+      $numchoices++;
+      // Set the flag to true for additional checks
+      $writein_option = true;
+    }
 
-    foreach ($node->choice as $i => $val) {
-      // see if the box is checked
-      if ($form_values['choice'][$i]) {
-        $numchoices++;
-      }
-    }
-  
     // too many choices ranked
     if ($node->maxchoices != 0 && $numchoices > $node->maxchoices) {
       $msg = t('%num choices were selected but only %max are allowed.',
@@ -202,7 +250,11 @@
       }
       $ok = false;
     }
-  }
+  }
+
+  // Do validation specific to writeins
+  _advpoll_writeins_voting_form_validate($node, $writein_option, $writein_text, $errors, $ok, $ajax);
+
   // If the form was posted with AJAX and has errors, print the error message.
   if ($ajax && !$ok) {
     print drupal_to_js(array('response' => '<div class="messages error">'. implode('<br />', $errors) .'</div>', 'error' => TRUE));
diff -ur C:\Documents and Settings\JD\workspace\advpoll/modes/ranking.inc advpoll/modes/ranking.inc
--- C:\Documents and Settings\JD\workspace\advpoll/modes/ranking.inc	2006-12-25 19:09:35.000000000 +0000
+++ advpoll/modes/ranking.inc	2007-06-06 04:12:24.250000000 +0100
@@ -24,7 +24,12 @@
   if ($node->choice) {
     $list = array();
 
-    $numChoices = count($node->choice);
+    $numChoices = count($node->choice);
+
+    // Add one to number of choices if write-ins enabled and user has permission
+    if($node->writeins && user_access('add write-ins')) {
+      $numChoices++;
+    }
 
     // Generate the list of possible rankings
     $choices[0] = '--';
@@ -50,15 +55,38 @@
     );
 
     foreach ($node->choice as $key => $choice) {
-      // Don't show blank choices
-      if ($choice['label']) {
+      // Don't show blank choices or write-in votes if the setting is disabled
+      if ($choice['label'] && ($node->displaywriteins || !$choice['writein'])) {
         $form['choice'][$key] = array(
           '#type' => 'select',
           '#title' => check_plain($choice['label']),
           '#options' => $choices,
         );
       }
-    }
+    }
+
+    // Add write-in select box if write-ins are enabled and user has permission
+    if($node->writeins && user_access('add write-ins')) {
+      $form['choice'][$key+1] = array (
+        '#type' => 'select',
+        '#title' => t('(write-in)'),
+        '#options' => $choices,
+      );
+      // Key index of the write-in option
+      $form['writein_key'] = array(
+        '#type' => 'value',
+        '#value' => $key+1,
+      );
+    }
+  }
+
+  // Add write-in text field if write-ins are enabled and user has permission
+  if($node->writeins && user_access('add write-ins')) {
+    $form['writein_choice'] = array (
+      '#type' => 'textfield',
+      '#title' => t('Write-in vote'),
+      '#size' => 25,
+    );
   }
 
   $form['nid'] = array(
@@ -553,20 +581,27 @@
  * This takes care of registering the vote in runoff nodes.
  */
 function advpoll_voting_ranking_form_submit($form_id, $form_values) {
-  $vote = array();
-  foreach ($form_values['choice'] as $choice => $rank) {
-    unset($temp);
-    $temp->value = $rank;
-    // A zero value indicates they didn't rank that choice
-    if ($temp->value != 0) {
-      $temp->value_type = 'option';
-      $temp->tag = $choice;
-      $vote[] = $temp;
+  $vote = array();
+  $node = node_load($form_values['nid']);
+
+  // Do submission specific to writeins
+  _advpoll_writeins_voting_form_submit($node, $form_values, $vote, $form_values['choice'][$form_values['writein_key']]);
+
+  foreach ($form_values['choice'] as $choice => $rank) {
+    // Ignore write-in choice that has already taken care of
+    if($choice != $form_values['writein_key']) {
+      unset($temp);
+      $temp->value = $rank;
+      // A zero value indicates they didn't rank that choice
+      if ($temp->value != 0) {
+        $temp->value_type = 'option';
+        $temp->tag = $choice;
+        $vote[] = $temp;
+      }
     }
-  }
+  }
 
   votingapi_set_vote('advpoll', $form_values['nid'], $vote);
-  $node = node_load($form_values['nid']);
   _advpoll_vote_response($node, $_POST['ajax']);
 }
 
@@ -578,26 +613,40 @@
  *
  * @returns boolean false on invalid forms, true otherwise.
  */
-function advpoll_voting_ranking_form_validate($form_id, $form_values) {
+function advpoll_voting_ranking_form_validate($form_id, $form_values) {
+  $ok = true;
   $node = node_load($form_values['nid']);
-  $ajax = $_POST['ajax'];
+  $ajax = $_POST['ajax'];
+
+  // Whether the write-in option is selected. This is calculated differently for
+  // radio buttons and checkboxes.
+  $writein_option = false;
+  $writein_text = $form_values['writein_key'] ? $form_values['writein_choice'] : '';
 
-  $ok = TRUE;
   // array used to check which values are set
   $setvalues = array();
-  
-  $numchoices = 0;
+
+  $numchoices = 0;
+
+  // If write-ins are enabled and user has permission and the write-in box is checked
+  if($node->writeins && user_access('add write-ins') && $form_values['choice'][$form_values['writein_key']]) {
+    // Increment the choices counter by one
+    $numchoices++;
+    // Set the flag to true for additional checks
+    $writein_option = true;
+  }
+
   foreach ($node->choice as $key => $choice) {
-    
     // count the number of choices that are ranked
     if ($form_values['choice'][$key]) {
       $numchoices++;
-    }
+    }
+
     $intvalue = intval($form_values['choice'][$key]);
     // mark this value as seen
     $setvalues[$intvalue]++;
     // check range
-    if ($intvalue > count($node->choice) || $intvalue < 0) {
+    if ($intvalue > ($writein_option ? count($node->choice) + 1 : count($node->choice)) || $intvalue < 0) {
       // TODO: clean up this error message
       $msg = "Illegal rank for choice $key: $intvalue (min: 1, max: "
         . count($node->choice) . ')';
@@ -609,8 +658,28 @@
       }
       $ok = FALSE;
     }
-  }
-  
+  }
+
+  // If write-ins are enabled and user has permission and the write-in box is checked
+  if($writein_option) {
+    $intvalue = intval($form_values['choice'][$form_values['writein_key']]);
+    // mark this value as seen
+    $setvalues[$intvalue]++;
+    // check range
+    if ($intvalue > ($writein_option ? count($node->choice) + 1 : count($node->choice)) || $intvalue < 0) {
+      // TODO: clean up this error message
+      $msg = "Illegal rank for the write-in choice: $intvalue (min: 1, max: "
+        . count($node->choice) . ')';
+      if ($ajax) {
+        $errors[] = $msg;
+      }
+      else {
+        form_set_error('choice][', $msg);
+      }
+      $ok = FALSE;
+    }
+  }
+
   // too many choices ranked
   if ($node->maxchoices != 0 && $numchoices > $node->maxchoices) {
     $msg = t('%num choices were selected but only %max are allowed.',
@@ -650,7 +719,11 @@
     
       $ok = false;
     }
-  }
+  }
+
+  // Do validation specific to writeins
+  _advpoll_writeins_voting_form_validate($node, $writein_option, $writein_text, $errors, $ok, $ajax);
+
   // If the form was posted with AJAX and has errors, print the error message.
   if ($ajax && !$ok) {
     print drupal_to_js(array('response' => '<div class="messages error">'. implode('<br />', $errors) .'</div>', 'error' => TRUE));
