Index: advpoll-form.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/advpoll/advpoll-form.js,v
retrieving revision 1.1.2.3
diff -u -p -r1.1.2.3 advpoll-form.js
--- advpoll-form.js	30 Jul 2007 20:36:39 -0000	1.1.2.3
+++ advpoll-form.js	6 Aug 2007 03:11:14 -0000
@@ -4,7 +4,7 @@ if (!Drupal.advpoll) {
   Drupal.advpoll = {};
 }
 
-// Update maxchoices, called when adding and removing choices
+// Update maxchoices, called when adding and removing choices.
 Drupal.advpoll.maxChoices = function(newChoiceN) {
   var selected = $("#edit-settings-maxchoices").val();
   var label = $("#edit-settings-maxchoices").prev();
@@ -27,7 +27,7 @@ Drupal.advpoll.maxChoices = function(new
   $(newMaxChoices).insertAfter(label);
 }
 
-// Click event for Remove link, called on pageload and when Add choice is clicked
+// Click event for Remove link, called on pageload and when Add choice is clicked.
 Drupal.advpoll.removeChoiceClick = function()  {
   $('a.remove-choice').unclick().click(function() {
     var nextRemoveLink = $(this).parent().next().find(".remove-choice");
@@ -45,7 +45,11 @@ Drupal.advpoll.removeChoiceClick = funct
       // 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").attr("checked")) {
+      i++;
+    }
+        
     Drupal.advpoll.maxChoices(i-1);
   });
 }
@@ -65,6 +69,21 @@ Drupal.advpoll.updateStartDate = functio
   }
 }
 
+// Update maxChoices and show/hide "display write-ins" option when user checks/
+// unchecks the write-ins box.
+Drupal.advpoll.updateWriteins = function() {
+  if ($("#edit-settings-writeins").attr("checked")) {
+    Drupal.advpoll.maxChoices($("#edit-settings-maxchoices").children().length);
+    $(".edit-settings-displaywriteins").show();
+    $("#edit-settings-displaywriteins").removeAttr("disabled");
+  }
+  else {
+    Drupal.advpoll.maxChoices($("#edit-settings-maxchoices").children().length - 2);
+    $(".edit-settings-displaywriteins").hide();
+    $("#edit-settings-displaywriteins").attr("disabled", "disabled");
+  }
+}
+
 Drupal.advpoll.nodeFormAutoAttach = function() {
   // Hide "need more choices" checkbox
   $("#morechoices").hide();
@@ -73,6 +92,15 @@ Drupal.advpoll.nodeFormAutoAttach = func
   Drupal.advpoll.updateStartDate();
   $("#edit-settings-usestart").click(Drupal.advpoll.updateStartDate);
   
+  // Remove extra maxChoices entry from write-ins
+  if (!$("#edit-settings-writeins").attr("checked")) {
+    Drupal.advpoll.maxChoices($("#edit-settings-maxchoices").children().length-2);
+  }
+  
+  // Add behavior when write-in box is (un)checked.
+  Drupal.advpoll.updateWriteins();
+  $("#edit-settings-writeins").click(Drupal.advpoll.updateWriteins);
+  
   // Insert Remove links
   $('<a class="remove-choice">' + Drupal.settings.advPoll.remove + '</a>').insertAfter("input.choices");
   Drupal.advpoll.removeChoiceClick();
@@ -84,16 +112,21 @@ Drupal.advpoll.nodeFormAutoAttach = func
     var newChoiceN = $("input.choices").length + 1;
     // If all choices are removed, use a "backup" of the first choice, else clone the first.
     newChoice = ($("input.choices:first").parent().html() ? $("input.choices:first").parent().clone() : newChoice);
-    // Replace choice numbers in label, name and id with the new choice number
+    // Replace choice numbers in label, name and id with the new choice number.
     newChoice.html(newChoice.html().replace(/\d+(?=<)|\d+(?=-)|\d+(?=\])/g, newChoiceN));
     // Clear the value, insert and fade in.
     newChoice.find("input").val("").end().insertBefore("#morechoices").fadeIn();
-    // Update hidden form values
+    // Update hidden form values.
     $("#edit-choices").val(newChoiceN);
     $("#edit-changed").val($("#edit-changed").val() + 1);
     
     Drupal.advpoll.removeChoiceClick();
     
+    // Add an extra maxChoice if write-ins are enabled.
+    if ($("#edit-settings-writeins").attr("checked")) {
+      newChoiceN++;
+    }
+    
     Drupal.advpoll.maxChoices(newChoiceN);
     
     return false;
Index: advpoll-vote.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/advpoll/advpoll-vote.js,v
retrieving revision 1.1.2.7
diff -u -p -r1.1.2.7 advpoll-vote.js
--- advpoll-vote.js	1 Dec 2006 15:40:49 -0000	1.1.2.7
+++ advpoll-vote.js	6 Aug 2007 03:11:14 -0000
@@ -52,8 +52,25 @@ Drupal.advpoll.nodeVoteAutoAttach = func
   Drupal.advpoll.attachVoteAjax();
 }
 
+Drupal.advpoll.handleWriteins = function() {
+  // Toggle display of the write-in text box for radios/checkboxes.
+  $("#vote_choices input").click(function() {
+    var isLast = $(this).val() == $("#vote_choices input:last").val();
+    var type = $(this).attr("type"); 
+    // The logic here is tricky but intentional.
+    if (isLast || type == "radio") {
+      $("#writein_choice")[(isLast && (type == "radio" || $(this).attr("checked"))) ? "show" : "hide"]();
+    }
+  });
+  // Toggle display of the write-in text box for select boxes.
+  $("#vote_choices select:last").click(function() {
+    $("#writein_choice")[$(this).val() > 0 ? "show" : "hide"]();
+  });
+};
+
 if (Drupal.jsEnabled) {
   $(document).ready(function(){  
     Drupal.advpoll.nodeVoteAutoAttach();
+    Drupal.advpoll.handleWriteins();
   });
 };
Index: advpoll.css
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/advpoll/advpoll.css,v
retrieving revision 1.2.2.3
diff -u -p -r1.2.2.3 advpoll.css
--- advpoll.css	30 Jul 2007 20:36:39 -0000	1.2.2.3
+++ advpoll.css	6 Aug 2007 03:11:14 -0000
@@ -10,3 +10,7 @@ a.remove-choice {
   font-size: 0.85em;
   cursor:pointer;
 }
+
+html.js #writein_choice {
+  display: none;
+}
Index: advpoll.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/advpoll/advpoll.install,v
retrieving revision 1.5.2.9
diff -u -p -r1.5.2.9 advpoll.install
--- advpoll.install	10 May 2007 23:38:02 -0000	1.5.2.9
+++ advpoll.install	6 Aug 2007 03:11:14 -0000
@@ -20,6 +20,8 @@ function advpoll_install() {
           `algorithm` VARCHAR(100),
           `showvotes` tinyint,
           `startdate` int unsigned,
+          `writeins` tinyint NOT NULL default '0',
+          `displaywriteins` tinyint NOT NULL default '0',
           PRIMARY KEY  (`nid`)
       ) /*!40100 DEFAULT CHARACTER SET utf8 */");
 
@@ -33,6 +35,7 @@ function advpoll_install() {
           `nid` int(10) NOT NULL,
           `label` text NOT NULL,
           `vote_offset` int(2) unsigned default NULL,
+          `writein` tinyint NOT NULL default '0',
           PRIMARY KEY  (`nid`, `vote_offset`),
           KEY `vote_offset` (`vote_offset`)
         ) /*!40100 DEFAULT CHARACTER SET utf8 */");
@@ -50,6 +53,8 @@ function advpoll_install() {
           algorithm varchar(100),
           showvotes smallint,
           startdate integer,
+          writeins smallint NOT NULL DEFAULT '0',
+          displaywriteins smallint NOT NULL DEFAULT '0',
           PRIMARY KEY (nid)
       )");
 
@@ -63,6 +68,7 @@ function advpoll_install() {
           nid integer NOT NULL,
           label text NOT 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)");
@@ -121,3 +127,24 @@ function advpoll_update_1() {
   }
   return $ret;  
 }
+
+/**
+ * Add columns for write-in support.
+ */
+function advpoll_update_2() {
+  $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':
+      db_add_column($ret, 'advpoll', 'writeins', 'smallint', array('default' => 0, 'not null' => TRUE));
+      db_add_column($ret, 'advpoll', 'displaywriteins', 'smallint', array('default' => 0, 'not null' => TRUE));
+      db_add_column($ret, 'advpoll_choices', 'writein', 'smallint', array('default' => 0, 'not null' => TRUE));
+      break;    
+    }
+    return $ret;
+}
Index: advpoll.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/advpoll/advpoll.module,v
retrieving revision 1.21.2.49
diff -u -p -r1.21.2.49 advpoll.module
--- advpoll.module	5 Aug 2007 22:11:18 -0000	1.21.2.49
+++ advpoll.module	6 Aug 2007 03:11:14 -0000
@@ -232,15 +232,36 @@ function advpoll_form($node, $form_value
     '#tree' => TRUE,
   );
 
+  $form['settings']['writeins'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Allow user to cast a write-in vote'),
+    '#default_value' => isset($node->writeins)? $node->writeins : 0,
+    '#description' => t("Allow voters with the 'add write-ins' permission to write-in up to one choice each."),
+  );
+
+  $form['settings']['displaywriteins'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Display write-in votes as choices for future voters'),
+    '#default_value' => isset($node->displaywriteins)? $node->displaywriteins : 0,
+    '#description' => t("Allow voters to see and choose from previous voters' write-in votes."),
+    '#prefix' => '<div class="edit-settings-displaywriteins">',
+    '#suffix' => '</div>',
+  );
+
   $max_choice_list = array();
   for ($i = 0; $i <= $choices; $i++) {
     $max_choice_list[$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.
+  $max_choice_list[$i] = $i;
 
   $form['settings']['maxchoices'] = array(
     '#type' => 'select',
     '#title' => t('Maximum choices'),
-    '#default_value' => ($node->maxchoices? $node->maxchoices : 0),
+    '#default_value' => isset($node->maxchoices)? $node->maxchoices : 0,
     '#options' => $max_choice_list,
     '#DANGEROUS_SKIP_CHECK' => true, // Allow jQuery to add new options
     '#description' => t('Limits the total number of choices voters may select.')
@@ -252,22 +273,19 @@ function advpoll_form($node, $form_value
     $voting_algorithms[$alg] = ucfirst($alg);
   }
 
-  $defaultalg = ($form_values['settings']['algorithm']?
-    $form_values['settings']['algorithm'] : $node->algorithm);
-      
   if (count($voting_algorithms) > 1) {
     $form['settings']['algorithm'] = array(
       '#type' => 'select',
       '#title' => t('Algorithm'),
       '#options' => $voting_algorithms,
-      '#default_value' => $defaultalg,
-      '#description' => 'Voting algorithm to use to calculate the winner.',
+      '#default_value' => $node->algorithm,
+      '#description' => t('Voting algorithm to use to calculate the winner.'),
     );
   }
   else {
     $form['settings']['algorithm'] = array(
       '#type' => 'hidden',
-      '#value' => $defaultalg,
+      '#value' => $node->algorithm,
     );
   }
 
@@ -276,7 +294,7 @@ function advpoll_form($node, $form_value
     '#type' => 'radios',
     '#title' => t('Status'),
     '#options' => $active,
-    '#default_value' => (isset($node->active)? $node->active : 1),
+    '#default_value' => isset($node->active)? $node->active : 1,
     '#description' => t('When a poll is closed users may no longer vote on it.'),
   );
 
@@ -286,12 +304,10 @@ function advpoll_form($node, $form_value
     '#type' => 'checkbox',
     '#title' => t('Use start date'),
     '#description' => t('Specify a date that the poll opens.'),
-    '#default_value' => (isset($node->startdate)? TRUE :
-      ($form_values['startdate']? $form_values['startdate'] : FALSE)),
+    '#default_value' => $node->startdate,
   );
 
-  $date = $form_values['startdate']? $form_values['startdate'] :
-    $node->startdate? $node->startdate : time();
+  $date = isset($node->startdate)? $node->startdate : time();
 
   $form['settings']['startdate'] = array(
     '#prefix' => '<div class="edit-settings-startdate">',
@@ -310,7 +326,7 @@ function advpoll_form($node, $form_value
   $form['settings']['runtime'] = array(
     '#type' => 'select',
     '#title' => t('Duration'),
-    '#default_value' => ($node->runtime? $node->runtime : 0),
+    '#default_value' => isset($node->runtime)? $node->runtime : 0,
     '#options' => $_duration,
     '#description' => t('After this period, the poll will be closed automatically. This is relative to the start date if it is specified, otherwise the date the poll was created.')
   );
@@ -359,7 +375,7 @@ function advpoll_help($section) {
 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;
   }
@@ -788,7 +804,7 @@ function advpoll_node_info() {
  * 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');
 }
 
 /**
@@ -841,11 +857,12 @@ function advpoll_update($node) {
     $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['writeins'], $node->settings['displaywriteins'],
     $node->nid);
 
   _advpoll_insert_choices($node->nid);
@@ -889,7 +906,7 @@ function _advpoll_insert_choices($nid) {
   $i = 1;
   foreach ($_POST['choice'] as $choice) {
     if ($choice['label'] != '') {
-      db_query("INSERT INTO {advpoll_choices} (nid, label, vote_offset) VALUES (%d, '%s', %d)", $nid, $choice['label'], $i++);
+      db_query("INSERT INTO {advpoll_choices} (nid, label, vote_offset, writein) VALUES (%d, '%s', %d, 0)", $nid, $choice['label'], $i++);
     }
   }
 }
@@ -916,11 +933,12 @@ function advpoll_insert($node) {
     $node->settings['active'] = _advpoll_calculate_active($node);
   }
 
-  db_query("INSERT INTO {advpoll} (nid, mode, uselist, active, runtime, maxchoices, algorithm, showvotes, startdate) VALUES (%d, '%s', %d, %d, %d, %d, '%s', %d, %s)",
+  db_query("INSERT INTO {advpoll} (nid, mode, uselist, active, runtime, 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'], $node->settings['displaywriteins']);
 
   // Insert the choices
   _advpoll_insert_choices($node->nid);
@@ -987,17 +1005,23 @@ function advpoll_validate(&$node) {
         $realchoices++;
       }
     }
+    
+    // Add one to choices counter if 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.'));
     }
 
-    /* Validate maxchoices since it has #DANGEROUS_SKIP_CHECK set to true */
+    // Validate maxchoices since it has #DANGEROUS_SKIP_CHECK set to true.
     if ($node->settings['maxchoices'] < 0) {
       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.'));
     }
   }
@@ -1015,7 +1039,7 @@ function advpoll_submit(&$node) {
  */
 function advpoll_view($node, $teaser = FALSE, $page = FALSE) {
   $mode = _advpoll_get_mode($node);
-
+  drupal_add_css(drupal_get_path('module', 'advpoll') .'/advpoll.css', 'module');
   // Previewing a node, so don't show results.
   if ($node->in_preview) {
     // Show the voting form but don't let them vote
@@ -1061,7 +1085,7 @@ function theme_advpoll_results($title, $
   $output = '<div class="poll">';
   if ($results) {
     $output .= $results;
-    $output .= '<div class="total">'. t('Total votes: %votes', array('%votes' => $votes)) .'</div>';
+    $output .= '<div class="total">'. t('Total voters: %votes', array('%votes' => $votes)) .'</div>';
   }
   else {
     $output .= '<p>'. t('No votes have been recorded for this poll.') .'</p>';
@@ -1103,6 +1127,26 @@ function _advpoll_vote_response($node, $
   $msg = t('Your vote was registered.');
   // Ajax response
   if ($form_values['ajax']) {
+    // Unset the array of choices so duplicates aren't shown.
+    unset($node->choice);
+    // Get all choices from database. This is necessary to get information about
+    // newly submitted write-in choices.
+    $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)) {
+      $node->choice[$choice['vote_offset']] = $choice;
+    }
+    // Update the number of choices.
+    $node->choices = count($poll->choice);
+    // Get updated total number of votes from database.
+    $result = db_query("SELECT value FROM {votingapi_cache} where content_type='advpoll' AND content_id=%d AND tag='_advpoll' AND function='total_votes'", $node->nid);
+    if (db_num_rows($result) > 0) {
+      $cache = db_fetch_object($result);
+      $node->votes = $cache->value;
+    }
+    else {
+      $node->votes = 0;
+    }
+
     list($node->voted, $node->cancel_vote) = _advpoll_user_voted($node->nid);
     $ajax_output .= advpoll_view_results($node, NULL, NULL);
     // Remove linebreaks as they will break jQuery's insert-HTML methods
@@ -1237,3 +1281,87 @@ function _advpoll_form_set_error($name =
     return form_set_error('choice[', $message);
   }
 }
+
+/**
+ * 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, $ajax) {
+  // Do write-in specific checks if write-ins are enabled and user has permission.
+  if ($node->writeins && user_access('add write-ins')) {
+    // Something is in the write-in textbox.
+    if ($writein_text) {
+      $writein_choice_lower = strtolower($writein_text);
+      foreach ($node->choice as $i => $value) {
+        // Check that user isn't writing in an existing visible choice. (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'])) {
+          _advpoll_form_set_error('writein_choice', t("A write-in vote can not be for an existing choice. Select the choice's option instead."), $ajax);
+        }
+      }
+    }
+
+    // The write-in option is selected and there is nothing in the write-in textbox.
+    if ($writein_option && !$writein_text) {
+      _advpoll_form_set_error('writein_choice', t("If the 'write-in' option is selected, a choice must be written in."), $ajax);
+    }
+
+    // The write-in option is not selected, but there is something in the write-in textbox.
+    if (!$writein_option && $writein_text) {
+      _advpoll_form_set_error('writein_choice', t("If a choice is written in, the 'write-in' option must be selected."), $ajax);
+    }
+  }
+}
+
+/**
+ * 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['writein_choice']) {
+    // 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;
+    }
+  }
+}
Index: modes/binary.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/advpoll/modes/binary.inc,v
retrieving revision 1.7.2.22
diff -u -p -r1.7.2.22 binary.inc
--- modes/binary.inc	5 Aug 2007 22:11:18 -0000	1.7.2.22
+++ modes/binary.inc	6 Aug 2007 03:11:15 -0000
@@ -33,13 +33,24 @@ function advpoll_voting_binary_form(&$no
     // If previewing check the format against the current users permissions.
     $check = $node->in_preview;
     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] = _advpoll_choice_markup($choice['label'], $node->format, $check);
       }
     }
+    // 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(
       '#options' => $list,
+      '#prefix' => '<div id="vote_choices">',
+      '#suffix' => '</div>',
     );
 
     if ($node->in_preview) {
@@ -59,6 +70,17 @@ function advpoll_voting_binary_form(&$no
     }
   }
 
+  // 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 (
+      '#prefix' => '<div id="writein_choice">',
+      '#suffix' => '</div>',
+      '#type' => 'textfield',
+      '#title' => t('Write-in vote'),
+      '#size' => 25,
+    );
+  }
+
   $form['nid'] = array(
     '#type' => 'hidden',
     '#value' => $node->nid,
@@ -150,23 +172,33 @@ function advpoll_calculate_results_binar
 function advpoll_voting_binary_form_submit($form_id, $form_values) {
   $vote = array();
   $node = node_load($form_values['nid']);
+
+  // Do submission specific to writeins.
+  _advpoll_writeins_voting_form_submit($node, $form_values, $vote, 1);
+
   if ($node->maxchoices == 1) {
     // Plurality voting
-    $temp->value = 1;
-    $temp->tag = $form_values['choice'];
-    $temp->value_type = 'option';
-    $vote[] = $temp;
+    // Ignore write-in choice that has already been taken care of.
+    if (!$form_values['choice'][$form_values['writein_key']]) {
+      $temp->value = 1;
+      $temp->tag = $form_values['choice'];
+      $temp->value_type = 'option';
+      $vote[] = $temp;
+    }
   }
   else {
     // Approval voting
     foreach ($form_values['choice'] as $choice => $selected) {
-      unset($temp);
-      $temp->value = $choice;
-      if ($selected) {
-        $temp->value_type = 'option';
-        $temp->tag = $choice;
-        $temp->value = 1;
-        $vote[] = $temp;
+      // Ignore write-in choice that has already been 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;
+        }
       }
     }
   }
@@ -189,6 +221,11 @@ function advpoll_voting_binary_form_vali
     _advpoll_form_set_error('choice[', t('You are not allowed to vote in this poll.'), $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'] : '';
+
   // Check if user has already voted
   list($voted, $cancel_vote) = _advpoll_user_voted($node->nid);
   if ($voted) {
@@ -197,7 +234,14 @@ function advpoll_voting_binary_form_vali
 
   if ($node->maxchoices == 1) {
     // Plurality voting
-    if (!array_key_exists($form_values['choice'], $node->choice)) {
+    // Write-ins are enabled, user has permission, and it's 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;
+    }
+    // The choice is invalid (not between 0 and the write-in key).
+    elseif (!($form_values['choice'] > 0 ) && ($form_values['choice'] < $form_values['writein_key'])) {
+      // Nothing is selected.
       _advpoll_form_set_error('choice[', t('At least one choice must be selected.'), $ajax);
     }
   }
@@ -210,6 +254,14 @@ function advpoll_voting_binary_form_vali
         $numchoices++;
       }
     }
+
+    // Write-ins are enabled, 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;
+    }
   
     // Too many choices ranked
     if ($node->maxchoices != 0 && $numchoices > $node->maxchoices) {
Index: modes/ranking.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/advpoll/modes/ranking.inc,v
retrieving revision 1.8.2.26
diff -u -p -r1.8.2.26 ranking.inc
--- modes/ranking.inc	5 Aug 2007 22:11:18 -0000	1.8.2.26
+++ modes/ranking.inc	6 Aug 2007 03:11:15 -0000
@@ -34,6 +34,11 @@ function advpoll_voting_ranking_form(&$n
 
     $num_choices = 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')) {
+      $num_choices++;
+    }
+
     // Generate the list of possible rankings
     $choices[0] = '--';
     for ($i = 1; $i <= $num_choices; $i++) {
@@ -56,11 +61,13 @@ function advpoll_voting_ranking_form(&$n
       '#tree' => TRUE,
       // XXX: Workaround for FormAPI bug in PHP 4, see http://drupal.org/node/86657
       '#type' => 'checkboxes',
+      '#prefix' => '<div id="vote_choices">',
+      '#suffix' => '</div>',
     );
 
     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' => _advpoll_choice_markup($choice['label'], $node->format),
@@ -68,6 +75,31 @@ function advpoll_voting_ranking_form(&$n
         );
       }
     }
+
+    // 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 (
+      '#prefix' => '<div id="writein_choice">',
+      '#suffix' => '</div>',
+      '#type' => 'textfield',
+      '#title' => t('Write-in vote'),
+      '#size' => 25,
+    );
   }
 
   $form['nid'] = array(
@@ -592,19 +624,26 @@ function _advpoll_calculate_instantrunof
  */
 function advpoll_voting_ranking_form_submit($form_id, $form_values) {
   $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) {
-    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;
+    // Ignore write-in choice that has already been 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, $form_values);
 }
 
@@ -625,6 +664,10 @@ function advpoll_voting_ranking_form_val
     _advpoll_form_set_error('choice[', t('You are not allowed to vote in this poll.'), $ajax);
   }
 
+  // Whether the write-in option is selected.
+  $writein_option = false;
+  $writein_text = $form_values['writein_key'] ? $form_values['writein_choice'] : '';
+
   // Check if user has already voted
   list($voted, $cancel_vote) = _advpoll_user_voted($node->nid);
   if ($voted) {
@@ -635,9 +678,18 @@ function advpoll_voting_ranking_form_val
   $setvalues = array();
   
   $numchoices = 0;
+
+  // Write-ins are enabled, 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
+    // Count the number of choices that are ranked.
     if ($form_values['choice'][$key]) {
       $numchoices++;
     }
@@ -645,13 +697,34 @@ function advpoll_voting_ranking_form_val
     // 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
-      $message = "Illegal rank for choice $key: $intvalue (min: 1, max: ". count($node->choice) .')';
+      $message = "Illegal rank for choice $key: $intvalue (min: 1, max: "
+        . ($writein_option ? count($node->choice) + 1 : count($node->choice)) .')';
       _advpoll_form_set_error('choice]['. $key, $message, $ajax);
     }
   }
-  
+
+  // Write-ins are enabled, 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) {
     $message = t('%num choices were selected but only %max are allowed.', array('%num' => $numchoices, '%max' => $node->maxchoices));
@@ -664,13 +737,17 @@ function advpoll_voting_ranking_form_val
     _advpoll_form_set_error('choice', t('At least one choice must be selected.'), $ajax);
   }
 
-  // Check that multiple choices are not set to the same value
+  // Check that multiple choices are not set to the same value.
   foreach ($setvalues as $val => $count) {
     if ($val != 0 && $count > 1) {
       $message = t('Multiple choices given the rank of %value.', array('%value' => $val));
       _advpoll_form_set_error('choice', $message, $ajax);
     }
   }
+
+  // Do validation specific to writeins.
+  _advpoll_writeins_voting_form_validate($node, $writein_option, $writein_text, $ajax);
+
 }
 
 /**
