diff -rupN advpoll/advpoll.css advpoll_new/advpoll.css
--- advpoll/advpoll.css	2009-04-12 02:02:47.000000000 +1000
+++ advpoll.css	2010-10-26 12:47:37.489936999 +1100
@@ -111,3 +111,33 @@ html.js input.writein-choice {
 .advpoll-choice-action-link {
   margin-left: 10pt;
 }
+
+/* Multirate poll css */
+.poll.multirate .rating-criteria div.form-item {
+  float: left;
+}
+
+.poll.multirate .form-item label {
+  float: left;
+}
+
+.advpoll-multirate-table {
+  width: auto;
+}
+
+.advpoll-multirate-table .low-header {
+  text-align: left;
+}
+
+.advpoll-multirate-table .high-header {
+  text-align: right;
+}
+
+.advpoll-multirate-table thead th {
+  padding-right: 0;
+}
+
+.advpoll-multirate-table td.criteria-title {
+  font-weight: bold;
+}
+
diff -rupN advpoll/advpoll-display-multirate-form.tpl.php advpoll_new/advpoll-display-multirate-form.tpl.php
--- advpoll/advpoll-display-multirate-form.tpl.php	1970-01-01 10:00:00.000000000 +1000
+++ advpoll-display-multirate-form.tpl.php	2010-10-26 12:46:05.779937002 +1100
@@ -0,0 +1,30 @@
+<?php
+// $Id$
+
+/**
+ * @file advpoll-display-multirate-form.tpl.php
+ * Default theme implementation to show voting form for multirate polls.
+ *
+ * $form_id
+ * $form_submit
+ * $choice_list - choices in the poll.
+ */
+?>
+<div class="poll multirate">
+  <div class="advpoll-available-choices">
+    <div class="choice-header">
+      <?php print t('Choices'); ?>
+    </div>
+    <div class="vote-choices">
+      <?php print $choice_list; ?>
+    </div>
+  </div>
+  <?php print $form_submit; ?>
+  <br/>
+  <?php if ($message): ?>
+    <p class="message">
+      <?php print $message; ?>
+    </p>
+  <?php endif; ?>
+</div>
+
diff -rupN advpoll/advpoll.install advpoll_new/advpoll.install
--- advpoll/advpoll.install	2009-03-22 05:32:00.000000000 +1100
+++ advpoll.install	2010-10-26 12:46:05.779937002 +1100
@@ -102,6 +102,38 @@ function advpoll_schema() {
         'not null' => TRUE,
         'default' => '',
       ),
+      'max_points' => array(
+        'description' => t('The maximum number of points a user can give each criteria.'),
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => '2',
+      ),
+      'display_overall_rating' => array(
+        'description' => t('Whether or not to display an overall rating along with the individual criteria ratings.'),
+        'type' => 'int',
+        'unsigned' => TRUE,
+      ),
+      'display_avg_results' => array(
+        'description' => t('Whether to display average points or total points.'),
+        'type' => 'int',
+        'unsigned' => TRUE,
+      ),
+      'low_rating_label' => array(
+        'description' => t('The label of the lowest rating.'),
+        'type' => 'varchar',
+        'length' => 32,
+      ),
+      'high_rating_label' => array(
+        'description' => t('The label of the highest rating.'),
+        'type' => 'varchar',
+        'length' => 32,
+      ),
+      'overall_rating_label' => array(
+        'description' => t('The label of the overall rating.'),
+        'type' => 'varchar',
+        'length' => 32,
+      ),
     ),
     'primary key' => array('nid'),
   );
@@ -403,3 +435,46 @@ function advpoll_update_8() {
 function advpoll_update_6000() {
   return array();
 }
+
+/**
+ * Add support for multirate polls.
+ */
+function advpoll_update_6101() {
+  $ret = array();
+
+  db_add_field($ret, 'advpoll', 'max_points', array(
+    'description' => t('The maximum number of points a user can give each criteria.'),
+    'type' => 'int',
+    'unsigned' => TRUE,
+    'not null' => TRUE,
+    'default' => '2',
+  ));
+  db_add_field($ret, 'advpoll', 'display_overall_rating', array(
+    'description' => t('Whether or not to display an overall rating along with the individual criteria ratings.'),
+    'type' => 'int',
+    'unsigned' => TRUE,
+  ));
+  db_add_field($ret, 'advpoll', 'display_avg_results', array(
+    'description' => t('Whether to display average points or total points.'),
+    'type' => 'int',
+    'unsigned' => TRUE,
+  ));
+  db_add_field($ret, 'advpoll', 'low_rating_label', array(
+    'description' => t('The label of the lowest rating.'),
+    'type' => 'varchar',
+    'length' => 32,
+  ));
+  db_add_field($ret, 'advpoll', 'high_rating_label', array(
+    'description' => t('The label of the highest rating.'),
+    'type' => 'varchar',
+    'length' => 32,
+  ));
+  db_add_field($ret, 'advpoll', 'overall_rating_label', array(
+    'description' => t('The label of the overall rating.'),
+    'type' => 'varchar',
+    'length' => 32,
+  ));
+
+  return $ret;
+}
+
diff -rupN advpoll/advpoll.module advpoll_new/advpoll.module
--- advpoll/advpoll.module	2010-09-09 12:35:24.000000000 +1000
+++ advpoll.module	2010-10-26 12:46:05.779937002 +1100
@@ -15,6 +15,12 @@ define('ADVPOLL_SHOW_WRITEINS', 0);
 define('ADVPOLL_INITIAL_CHOICES', 5);
 define('ADVPOLL_USE_QUESTION', 0);
 define('ADVPOLL_CHOICE_MAX_LENGTH', 2048);
+define('ADVPOLL_MAX_POINTS', 2);
+define('ADVPOLL_LOW_RATING_LABEL', 'Worst');
+define('ADVPOLL_HIGH_RATING_LABEL', 'Best');
+define('ADVPOLL_OVERALL_RATING_LABEL', 'Overall');
+define('ADVPOLL_DISPLAY_OVERALL_RATING', 1);
+define('ADVPOLL_DISPLAY_AVG_RESULTS', 1);
 // Options: always, aftervote, or afterclose.
 define('ADVPOLL_VIEW_RESULTS', 'aftervote');
 
@@ -442,14 +448,67 @@ function advpoll_form(&$node, $form_stat
   for ($i = 0; $i <= $choices; $i++) {
     $max_choice_list[$i] = ($i == 0 ? t('No limit') : $i);
   }
+  $max_points_list = array();
+  for ($i = 2; $i <= 20; $i++) {
+    $max_points_list[$i] = $i;
+  }
 
+  // Hide this option if it is a multirate poll as it is not used.
   $form['settings']['max_choices'] = array(
-    '#type' => 'select',
+    '#type' => ($node->type != 'advpoll_multirate') ? 'select' : 'hidden',
     '#title' => t('Maximum choices'),
     '#default_value' => isset($node->max_choices) ? $node->max_choices : variable_get('advpoll_max_choices_'. $type->type, ADVPOLL_MAX_CHOICES),
     '#options' => $max_choice_list,
     '#description' => t('Limits the total number of choices voters may select.')
   );
+  // Hide this option if it is not a multirate poll as it is not used otherwise
+  $form['settings']['max_points'] = array(
+    '#type' => ($node->type == 'advpoll_multirate') ? 'select' : 'hidden',
+    '#title' => t('Maximum points'),
+    '#default_value' => isset($node->max_points) ? $node->max_points : variable_get('advpoll_max_points_' . $type->type, ADVPOLL_MAX_POINTS),
+    '#options' => $max_points_list,
+    '#description' => t('The maximum number of points a user can give each criteria.')
+  );
+
+  if ($node->type == 'advpoll_multirate') {
+    $form['settings']['display_avg_results'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Display average results'),
+      '#description' => t("If checked, the results displayed with be the averages of all the ratings, otherwise the results will be totals of all the ratings."),
+      '#default_value' => isset($node->display_avg_results) ? $node->display_avg_results : ADVPOLL_DISPLAY_AVG_RESULTS,
+    );
+
+    $form['settings']['display_overall_rating'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Display overall rating'),
+      '#description' => t("If checked, an extra 'Overall' rating will be shown with the poll results, which is the average of all the criteria.  Users don't give a rating for this, it is automatically calculated from their ratings of the other criteria."),
+      '#default_value' => isset($node->display_overall_rating) ? $node->display_overall_rating : ADVPOLL_DISPLAY_OVERALL_RATING,
+    );
+
+    $form['settings']['low_rating_label'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Low rating label'),
+      '#description' => t('The label you want for the lowest possible rating for a given criteria. Defaults to %default.', array('%default' => ADVPOLL_LOW_RATING_LABEL)),
+      '#maxlength' => 32,
+      '#default_value' => isset($node->low_rating_label) ? $node->low_rating_label : ADVPOLL_LOW_RATING_LABEL,
+    );
+
+    $form['settings']['high_rating_label'] = array(
+      '#type' => 'textfield',
+      '#title' => t('High rating label'),
+      '#description' => t('The label you want for the highest possible rating for a given criteria. Defaults to %default.', array('%default' => ADVPOLL_HIGH_RATING_LABEL)),
+      '#maxlength' => 32,
+      '#default_value' => isset($node->high_rating_label) ? $node->high_rating_label : ADVPOLL_HIGH_RATING_LABEL,
+    );
+
+    $form['settings']['overall_rating_label'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Overall rating label'),
+      '#description' => t('The label you want for the overall rating. Defaults to %default.', array('%default' => ADVPOLL_OVERALL_RATING_LABEL)),
+      '#maxlength' => 32,
+      '#default_value' => isset($node->overall_rating_label) ? $node->overall_rating_label : ADVPOLL_OVERALL_RATING_LABEL,
+    );
+  }
 
   $voting_algorithms = advpoll_algorithms($mode);
   if (count($voting_algorithms) > 1) {
@@ -515,14 +574,14 @@ function advpoll_form(&$node, $form_stat
       '#value' => '<div id="edit-settings-admin-note" class="description">'. t('The settings below are only available for users with the <em>administer polls</em> permission.') .'</div>',
     );
     $form['settings']['writeins'] = array(
-      '#type' => 'checkbox',
+      '#type' => ($node->type != 'advpoll_multirate') ? 'checkbox' : 'hidden',
       '#title' => t('Allow users to cast a write-in vote'),
       '#default_value' => $default_writeins,
       '#description' => t('Allow voters with the "add write-ins" permission to write-in up to one choice each.'),
       '#attributes' => array('class' => 'settings-writeins'),
     );
     $form['settings']['show_writeins'] = array(
-      '#type' => 'checkbox',
+      '#type' => ($node->type != 'advpoll_multirate') ? 'checkbox' : 'hidden',
       '#title' => t('Display write-in votes as choices for future voters'),
       '#default_value' => $default_show_writeins,
       '#description' => t('Allow voters to see and choose from previously submitted write-in votes.'),
@@ -757,7 +816,7 @@ function advpoll_validate($node, &$form)
  */
 function advpoll_insert($node) {
   $mode = _advpoll_get_mode($node->type);
-  db_query("INSERT INTO {advpoll} (nid, mode, use_list, active, max_choices, algorithm, show_votes, start_date, end_date, writeins, show_writeins, question) VALUES (%d, '%s', %d, %d, %d, '%s', %d, '%s', '%s', %d, %d, '%s')", $node->nid, $mode, $node->settings['use_list'], !$node->settings['close'], $node->settings['max_choices'], $node->settings['algorithm'], $node->settings['show_votes'], $node->settings['start_date'] ? strtotime($node->settings['start_date']) : 0, $node->settings['end_date'] ? strtotime($node->settings['end_date']) : 0, $node->settings['writeins'], $node->settings['show_writeins'], isset($node->question) ? $node->question : '');
+  db_query("INSERT INTO {advpoll} (nid, mode, use_list, active, max_choices, algorithm, show_votes, start_date, end_date, writeins, show_writeins, question, max_points, display_overall_rating, display_avg_results, low_rating_label, high_rating_label, overall_rating_label) VALUES (%d, '%s', %d, %d, %d, '%s', %d, '%s', '%s', %d, %d, '%s', %d, %d, %d, '%s', '%s', '%s')", $node->nid, $mode, $node->settings['use_list'], !$node->settings['close'], $node->settings['max_choices'], $node->settings['algorithm'], $node->settings['show_votes'], $node->settings['start_date'] ? strtotime($node->settings['start_date']) : 0, $node->settings['end_date'] ? strtotime($node->settings['end_date']) : 0, $node->settings['writeins'], $node->settings['show_writeins'], isset($node->question) ? $node->question : '', $node->settings['max_points'], $node->settings['display_overall_rating'], $node->settings['display_avg_results'], $node->settings['low_rating_label'], $node->settings['high_rating_label'], $node->settings['overall_rating_label']);
 
   // Insert the choices.
   _advpoll_insert_choices($node);
@@ -770,8 +829,8 @@ function advpoll_insert($node) {
  */
 function advpoll_update($node) {
 
-  db_query("UPDATE {advpoll} SET active = %d, max_choices = %d, algorithm = '%s', use_list = %d, show_votes = %d, start_date = '%s', end_date = '%s', writeins = %d, show_writeins = %d, question = '%s' WHERE nid = %d", !$node->settings['close'], $node->settings['max_choices'], $node->settings['algorithm'], $node->settings['use_list'], $node->settings['show_votes'], $node->settings['start_date'] ? strtotime($node->settings['start_date']) : 0, $node->settings['end_date'] ? strtotime($node->settings['end_date']) : 0, $node->settings['writeins'], $node->settings['show_writeins'], isset($node->question) ? $node->question : '', $node->nid);
-
+  db_query("UPDATE {advpoll} SET active = %d, max_choices = %d, algorithm = '%s', use_list = %d, show_votes = %d, start_date = '%s', end_date = '%s', writeins = %d, show_writeins = %d, question = '%s', max_points = %d, display_overall_rating = %d, display_avg_results = %d, low_rating_label = '%s', high_rating_label = '%s', overall_rating_label = '%s' WHERE nid = %d", !$node->settings['close'], $node->settings['max_choices'], $node->settings['algorithm'], $node->settings['use_list'], $node->settings['show_votes'], $node->settings['start_date'] ? strtotime($node->settings['start_date']) : 0, $node->settings['end_date'] ? strtotime($node->settings['end_date']) : 0, $node->settings['writeins'], $node->settings['show_writeins'], isset($node->question) ? $node->question : '', $node->settings['max_points'], $node->settings['display_overall_rating'], $node->settings['display_avg_results'], $node->settings['low_rating_label'], $node->settings['high_rating_label'], $node->settings['overall_rating_label'], $node->nid);
+  
   _advpoll_insert_choices($node);
   votingapi_recalculate_results('advpoll', $node->nid);
 }
@@ -1660,6 +1719,13 @@ function advpoll_theme() {
       'arguments' => array(
         'form' => NULL,
       )
+    ),
+    'advpoll_voting_multirate_form' => array(
+      'template' => 'advpoll-display-multirate-form',
+      'file' => 'modes/multirate.inc',
+      'arguments' => array(
+        'form' => NULL,
+      )
     ),
   );
 }
diff -rupN advpoll/advpoll.pages.inc advpoll_new/advpoll.pages.inc
--- advpoll/advpoll.pages.inc	2010-06-22 21:33:06.000000000 +1000
+++ advpoll.pages.inc	2010-10-26 12:46:05.789937005 +1100
@@ -156,14 +156,18 @@ function advpoll_votes_page($node) {
           // Need two dimensional results (if equal rankings are allowed).
           $rows[$key]['votes'][$vote->value][] = _advpoll_choice_markup($node->choice[$vote->tag]['label'], $node->format, FALSE);
         }
+        else if ($node->type == 'advpoll_multirate') {
+          // Need the actual result values for multirate.
+          $rows[$key]['votes'][_advpoll_choice_markup($node->choice[$vote->tag]['label'], $node->format, FALSE)] = $vote->value;
+        }
         else {
           // Just need one dimensional results.
           $rows[$key]['votes'][] = _advpoll_choice_markup($node->choice[$vote->tag]['label'], $node->format, FALSE);
         }
       }
     }
-    
-    $separators = array('advpoll_ranking' => ' > ', 'advpoll_binary' => ', ');
+
+    $separators = array('advpoll_ranking' => ' > ', 'advpoll_binary' => ', ', 'advpoll_multirate' => "<br />");
 
     // Create strings out of each vote.
     $results = array();
@@ -178,6 +182,12 @@ function advpoll_votes_page($node) {
           $rankings[$vote] = implode(' = ', $choices);
         }
       }
+      else if ($node->type == 'advpoll_multirate') {
+        // Include support for displaying the rating given for each criteria.
+        foreach ($ranking as $tag => $rating) {
+          $rankings[] = $tag . ': ' . $rating;
+        }
+      }
       else {
         // Just copy the previous array.
         $rankings = $ranking;
diff -rupN advpoll/modes/multirate.inc advpoll_new/modes/multirate.inc
--- advpoll/modes/multirate.inc	1970-01-01 10:00:00.000000000 +1000
+++ modes/multirate.inc	2010-10-26 12:46:05.789937005 +1100
@@ -0,0 +1,288 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Handle multirate votes.
+ * Multirate votes are made up of multiple critera, each of which can be
+ * individually voted against.
+ */
+
+function advpoll_info_multirate() {
+  return array(
+    'name' => 'multirate',
+    'name_label' => t('Multirate'),
+    'description' => t('Give a rating for a number of different criteria.'),
+  );
+}
+
+function advpoll_algorithms_multirate() {
+  return array('plurality' => t('Plurality'));
+}
+
+/**
+ * Implementation of the view_voting hook for the poll module.
+ * 
+ * This creates a list of choices to allow the user to vote on choices.
+ */
+function advpoll_voting_multirate_form(&$form_state, $node, $teaser, $page, $status) {
+  static $multirate_form_count = 0; 
+  $form = array(
+    '#id' => 'advpoll-voting-multirate-form-' . $multirate_form_count++,
+    '#attributes' => array('class' => 'advpoll-vote'),
+  );
+  $form['ajax'] = array(
+    '#type' => 'hidden',
+    '#attributes' => array('class' => 'ajax'),
+  );
+  $form['#node'] = $node;
+
+  if ($node->choice) {
+    $list = array();
+    // If previewing check the format against the current users permissions.
+    $check = $node->build_mode == NODE_BUILD_PREVIEW;
+    foreach ($node->choice as $i => $choice) {
+      // Don't show blank choices or write-in votes if the setting is disabled.
+      if ($choice['label'] && ($node->show_writeins || !$choice['writein'])) {
+        $list[$i] = _advpoll_choice_markup($choice['label'], $node->format, $check) . ($choice['writein'] ? ' '. t('(write-in)') : '');
+      }
+    }
+
+    $form['choices'] = array('#tree' => TRUE);
+
+    $options = array();
+    for ($i = 1; $i <= $node->max_points; $i++) {
+      $options[$i] = $i;
+    }
+    foreach ($list as $i => $choice) {
+      $form['choices'][$i]['label'] = array(
+        '#type' => 'item',
+        '#value' => t($choice),
+      );
+      $form['choices'][$i]['choice'] = array(
+        '#type' => 'radios',
+        '#default_value' => -1,
+        '#options' => $options,
+        '#attributes' => array('class' => 'rating-criteria'),
+        '#prefix' => '<div class="vote-choices">',
+        '#suffix' => '</div> <div class="clear-block"></div>',
+      );
+    }
+  }
+
+  $form['nid'] = array(
+    '#type' => 'hidden',
+    '#value' => $node->nid,
+    '#attributes' => array('class' => 'edit-nid'),
+  );
+
+  // Hide vote button if user can't vote and instead display appropriate message.
+  if ($node->build_mode != NODE_BUILD_PREVIEW && advpoll_eligible($node) && $status == 'open') {
+    static $multirate_vote_count = 0;
+    $form['vote'] = array(
+      '#type' => 'submit',
+      '#value' => t('Vote'),
+      '#id' => 'edit-vote-multirate-'. $multirate_vote_count++,
+      /* TODO: re-enable this.
+      '#ahah' => array(
+        'path' => 'advpoll/js_vote',
+        'wrapper' => 'vote-choices',
+        'method' => 'replace',
+        'effect' => 'none',
+      ),
+      */
+    );
+  }
+  elseif ($node->build_mode == NODE_BUILD_PREVIEW) {
+    // Display nothing.
+  }
+  elseif ($status == 'pending') {
+    $form['message']['#value'] = t('This poll opens @time.', array('@time' => format_date($node->start_date)));
+  }
+  else {
+    global $user;
+    $login_message = t('<a href="@login">Login</a> to vote in this poll.', array('@login' => url('user/login', array('query' => drupal_get_destination()))));
+    $form['message']['#value'] = isset($user->uid) ? t('You are not eligible to vote in this poll.') : $login_message;
+  }
+
+  $form['#action'] = url('node/'. $node->nid);
+
+  return $form;
+}
+
+function advpoll_view_results_multirate($node, $teaser, $page) {
+  $results = votingapi_select_results(array('content_type' => 'advpoll', 'content_id' => $node->nid));
+  $votes = array();
+  $output = '';
+
+  foreach ($results as $result) {
+    $vote_value = $result['tag'];
+    if ($vote_value == '_advpoll') {
+      if ($result['function'] == 'total_votes') {
+        $total_votes = $result['value'];
+        $total_possible_points = $total_votes * $node->max_points;
+      }
+    }
+    else if (isset($node->choice[$vote_value])) {
+      if ($result['function'] == 'sum') {
+        $votes[$vote_value]['sum'] = $result['value'];
+      }
+      else if ($result['function'] == 'average') {
+        $votes[$vote_value]['average'] = $result['value'];
+      }
+    }
+  }
+
+  if ($node->choice && $total_votes > 0) {
+    // Sort results by votes, descending.
+    arsort($votes);
+    $overall = 0;
+
+    // Display results for each possible choice.
+    foreach ($votes as $i => $vote) {
+      $total_points += $vote['sum'];
+      $choice = $node->choice[$i];
+      // Display as average or total result depending on poll option.
+      if ($node->display_avg_results) {
+        $percentage = round(100 * $vote['average'] / $node->max_points);
+        $output .= theme('advpoll_bar', _advpoll_choice_markup($choice['label'], $node->format, FALSE), $percentage, round($vote['average'], 1) . '/' . format_plural($node->max_points, '1 point', '@count points'), $choice);
+      }
+      else {
+        $percentage = round(100 * $vote['sum'] / $total_possible_points);
+        $output .= theme('advpoll_bar', _advpoll_choice_markup($choice['label'], $node->format, FALSE), $percentage, $vote['sum'] . '/' . format_plural($total_possible_points, '1 point', '@count points'), $choice);
+      }
+    }
+
+    // If the overall rating option has been selected add the overall result.
+    if ($node->display_overall_rating) {
+      $overall = round($total_points / count($votes), 1);
+      $average = round($total_points / count($votes) / $total_votes, 1);
+      $choice = t($node->overall_rating_label ? $node->overall_rating_label : ADVPOLL_OVERALL_RATING_LABEL);
+      // Display as average or total result depending on poll option
+      if ($node->display_avg_results) {
+        $percentage = round(100 * $average / $node->max_points);
+        $output .= theme('advpoll_bar', $choice, $percentage, $average . '/' . format_plural($node->max_points, '1 point', '@count points'), FALSE);
+      }
+      else {
+        $percentage = round(100 * $overall / $total_possible_points);
+        $output .= theme('advpoll_bar', $choice, $percentage, $overall . '/' . format_plural($total_possible_points, '1 point', '@count points'), FALSE);
+      }
+    }
+  }
+
+  return array('results' => $output, 'votes' => $total_votes);
+}
+
+function advpoll_calculate_results_multirate(&$cache, $node) {
+  $result = db_query("SELECT uid, vote_source FROM {votingapi_vote} WHERE content_type = 'advpoll' AND content_id = %d AND value_type = 'points' GROUP BY uid, vote_source, timestamp", $node->nid);
+  $total_votes = 0;
+  while ($vote = db_fetch_object($result)) {
+    $total_votes++;
+  }
+  votingapi_add_results(array(array('content_type' => 'advpoll', 'content_id' => $node->nid, 'value_type' => 'option', 'tag' => '_advpoll', 'function' => 'total_votes', 'value' => $total_votes)));
+}
+
+/**
+ * Registers the vote as a key for this node using votingapi_set_vote().
+ */
+function advpoll_voting_multirate_form_submit($form, &$form_state) {
+  $votes = array();
+  $node = $form['#node'];
+
+  foreach ($form_state['values']['choices'] as $choice => $selected) {
+    $vote = array();
+    // Ignore write-in choice.
+    if (!$node->writeins || $choice != $form_state['values']['writein_key']) {
+      $vote['value'] = $selected['choice'];
+      $vote['tag'] = $choice;
+      $vote['value_type'] = 'points';
+      $vote['content_id'] = $node->nid;
+      $vote['content_type'] = 'advpoll';
+      $votes[] = $vote;
+    }
+  }
+
+  // Need to pass a blank array as the second parameter so that existing votes aren't deleted......
+  $results = votingapi_set_votes($votes, array());
+  _advpoll_vote_response($node, $form_state);
+}
+
+/**
+ * Check if the submitted key exists, just to make sure the form is not bypassed.
+ *
+ * @returns boolean true if the form is valid
+ */
+function advpoll_voting_multirate_form_validate($form, &$form_state) {
+  $node = node_load($form_state['values']['nid']);
+  $ajax = $form_state['values']['ajax'];
+  
+  // Check if user is eligible to vote
+  if (!advpoll_eligible($node)) {
+    _advpoll_form_set_error('choice[', t('You are not allowed to vote in this poll.'), $ajax);
+  }
+  
+  // Check if poll is active
+  if (!_advpoll_is_active($node)) {
+    _advpoll_form_set_error('choice[', t('This poll is closed.'), $ajax);
+  }
+
+  // Check if user has already voted
+  list($voted, $cancel_vote) = _advpoll_user_voted($node->nid);
+  if ($voted) {
+    _advpoll_form_set_error('choice[', t('You have already voted in this poll.'), $ajax);
+    // Redirect to the current poll node to view the poll result instead of the voting form. This is only
+    // initiated for non-Ajax voting.
+    drupal_goto('node/'. $node->nid);
+  }
+
+  // Approval voting.
+  $all_rated = TRUE;
+  foreach ($form_state['values']['choices'] as $i => $choice) {
+    if ($choice['choice'] == -1) {
+      $all_rated = FALSE;
+    }
+  }
+
+  // Some criteria unranked.
+  if (!$all_rated) {
+    _advpoll_form_set_error('choices', t('At rating must be selected for each criteria.'), $ajax);
+  }
+}
+
+/**
+ * Process variables for advpoll-display-multirate-form.tpl.php.
+ *
+ * The variables array contains the following arguments:
+ * - $form
+ *
+ * @see advpoll-display-multirate-form.tpl.php
+ */
+function advpoll_preprocess_advpoll_voting_multirate_form(&$variables) {
+  $form = &$variables['form'];
+  $variables['message'] = drupal_render($form['message']);
+
+  $variables['form_id'] = $form['#id'];
+
+  // Set up the voting table as the choice list.
+  $node = $form['#node'];
+
+  $header = array(
+    array('data' => t(''), 'class' => 'label-header'),
+    array('data' => t($node->low_rating_label ? $node->low_rating_label : ADVPOLL_LOW_RATING_LABEL), 'class' => 'low-header'),
+    array('data' => t($node->high_rating_label ? $node->high_rating_label : ADVPOLL_HIGH_RATING_LABEL), 'class' => 'high-header'),
+  );
+
+  $rows = array();
+  foreach (element_children($form['choices']) as $key) {
+    $rows[] = array(
+      array('data' => drupal_render($form['choices'][$key]['label']), 'class' => 'criteria-title'),
+      array('data' => drupal_render($form['choices'][$key]['choice']), 'class' => 'criteria-options', 'colspan' => '2'),
+    );
+  }
+
+  $variables['choice_list'] = theme('table', $header, $rows, array('class' => 'advpoll-multirate-table'));
+
+  // All remaining form elements.
+  $variables['form_submit'] = drupal_render($form);
+}
+
