Index: multichoice.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/quiz/multichoice.module,v
retrieving revision 1.49
diff -u -r1.49 multichoice.module
--- multichoice.module	21 Sep 2007 00:10:07 -0000	1.49
+++ multichoice.module	22 Sep 2007 02:17:00 -0000
@@ -169,9 +169,9 @@
 
     // this is not a scored quiz, therefore no correct answers
     // so user must assign answers to result options
-    if(isset($quiz_id) && $quiz->pass_rate == 0) { 
+    if (isset($quiz_id) && $quiz->pass_rate == 0) { 
       if (empty($result_options)) {               
-        $result_options = array (0 => 'None');
+        $result_options = array(0 => 'None');
         if (is_array($quiz->resultoptions)) {
           foreach ($quiz->resultoptions as $r_option) {
             $result_options[$r_option['option_id']] = $r_option['option_name'];
@@ -387,7 +387,7 @@
   db_query("INSERT INTO {quiz_node_question_properties} (nid, vid, number_of_answers) VALUES(%d, %d, %d)", $node->nid, $question_vid, $node->number_of_answers);
   // we came from editing a quiz, so we should add this question to the quiz directly
   if ($node->quiz_id > 0) { 
-    db_query('INSERT INTO {quiz_node_relationship} ' .
+    db_query('INSERT INTO {quiz_node_relationship} '.
               '(parent_nid, parent_vid, child_nid, child_vid, question_status) VALUES (%d, %d, %d, %d, %d)', 
               $node->quiz_id, $quiz_vid, $node->nid, $node->vid, QUESTION_ALWAYS);
   }
@@ -441,22 +441,22 @@
   if ($node->revision) {
   	// Gather all quiz node vids from quiz_node_relationship table that contain
   	// the question being updated and create a new revision of the quizzes.
-  	$sql = "SELECT DISTINCT parent_vid FROM {quiz_node_relationship} WHERE child_vid = %d AND status != %d";
-  	$result = db_query($sql, $node->old_vid, QUESTION_NEVER);
-  	while ($quiz = db_fetch_object($result)) {
-  		// Create new revision of the quiz.
-  		// This will also create new quiz-question relation entries in the quiz_node_relationship table.
-  		$sql = "SELECT * FROM {node} WHERE vid = %d";
-  		$quiz_old = db_fetch_object(db_query($sql, $quiz->parent_vid));
-  		drupal_execute('node_form', array('revision' => '1'), $quiz_old);
+    $sql = "SELECT DISTINCT parent_vid FROM {quiz_node_relationship} WHERE child_vid = %d AND status != %d";
+    $result = db_query($sql, $node->old_vid, QUESTION_NEVER);
+    while ($quiz = db_fetch_object($result)) {
+      // Create new revision of the quiz.
+      // This will also create new quiz-question relation entries in the quiz_node_relationship table.
+      $sql = "SELECT * FROM {node} WHERE vid = %d";
+      $quiz_old = db_fetch_object(db_query($sql, $quiz->parent_vid));
+      drupal_execute('node_form', array('revision' => '1'), $quiz_old);
   		
-			// Update question vid in quiz_node_relationship table for each row that
-			// contains the question vid prior to the increment (new revision).
-  		$sql = "SELECT vid FROM {node} WHERE nid = %d";
-  		$quiz_new = db_fetch_object(db_query($sql, $quiz_old->nid));
-			$sql ="UPDATE {quiz_node_relationship} SET child_vid = %d WHERE parent_vid = %d AND child_vid = %d";
-			db_query($sql, $node->vid, $quiz_new->vid, $node->old_vid);
-  	}
+      // Update question vid in quiz_node_relationship table for each row that
+      // contains the question vid prior to the increment (new revision).
+      $sql = "SELECT vid FROM {node} WHERE nid = %d";
+      $quiz_new = db_fetch_object(db_query($sql, $quiz_old->nid));
+      $sql ="UPDATE {quiz_node_relationship} SET child_vid = %d WHERE parent_vid = %d AND child_vid = %d";
+      db_query($sql, $node->vid, $quiz_new->vid, $node->old_vid);
+    }
   }
 }
 
@@ -677,7 +677,7 @@
                      "LEFT JOIN quiz_multichoice_answers ma USING (nid, vid)" .
                      "WHERE nid = %d AND vid = %d", $rid, $rid, $nid, $vid);
   if ($result) {
-    $question = new StdClass();
+    $question = new stdClass();
     while ($next_row = db_fetch_array($result)) {
       $row = $next_row;
       $question->answers[$row['answer_id']] = $row;
@@ -805,7 +805,7 @@
     $innerheader[] = '&nbsp;';
   }
   
-  foreach($question->answers as $aid => $answer) {
+  foreach ($question->answers as $aid => $answer) {
     $cols = array();
     
     $cols[] = $answer['answer'];
@@ -824,7 +824,7 @@
 }
 
 function theme_multichoice_feedback($quiz, $report) {
-  $output = '<span class="quiz-summary-text">' . $report->body . '</span><br />';
+  $output = '<span class="quiz-summary-text">'. $report->body .'</span><br />';
   foreach ($report->answers as $answer) {
     if ($answer['user_answer']) {
       $answers[] = $answer['answer'];
@@ -835,9 +835,9 @@
     }
   }
   
-  $output .= '<span class="quiz-summary-header">Your Answer(s):</span> <span class="quiz-summary-text">' . implode(', ', $answers) . '</span>';
-  if($answer['feedback']) {
-    $output .= '<br /><span class="quiz-summary-text">' . implode(',', $feedbacks) . '</span>';
+  $output .= '<span class="quiz-summary-header">Your Answer(s):</span> <span class="quiz-summary-text">'. implode(', ', $answers) .'</span>';
+  if ($answer['feedback']) {
+    $output .= '<br /><span class="quiz-summary-text">'. implode(',', $feedbacks) .'</span>';
   }
   $output .= '<br />';
   if ($report->correct) {
@@ -845,7 +845,7 @@
   }
   else {
     $output .= "Sorry!<br>";
-    $output .= "Correct Answer(s): ".implode(',',$corrects);
+    $output .= "Correct Answer(s): ". implode(',', $corrects);
   }
   return $output;
 }
@@ -885,5 +885,5 @@
     $shortstring = substr($shortstring, 0, $breakpoint);
   }
   
-  return $title.$shortstring;
+  return $title . $shortstring;
 }
Index: quiz.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/quiz/quiz.module,v
retrieving revision 1.106
diff -u -r1.106 quiz.module
--- quiz.module	21 Sep 2007 00:10:07 -0000	1.106
+++ quiz.module	22 Sep 2007 02:17:02 -0000
@@ -30,10 +30,24 @@
 define('QUIZ_FEEDBACK_NEVER',           2);
 
 /**
+ * Implementation of hook_init().
+  */
+function quiz_init() {
+   // ensure we are not serving a cached page
+   if (function_exists('drupal_set_content')) {
+     // we don't do this in hook_menu to ensure the files are already included when
+     // views_menu is executed
+     if (module_exists('views')) {
+       include_once('./'. drupal_get_path('module', 'quiz') .'/quiz_views.inc');
+     }
+   }
+}
+
+/**
  * get an array of feedback options
  * @return array of feedback options
  */
-function _quiz_get_feedback_options(){
+function _quiz_get_feedback_options() {
   return array(QUIZ_FEEDBACK_END => t('At the end of the @quiz', array('@quiz' => QUIZ_NAME)), 
                QUIZ_FEEDBACK_QUESTION => t('After each question'), 
                QUIZ_FEEDBACK_NEVER => t('Do not show'),
@@ -325,14 +339,14 @@
       '#maxlength' => 40,
       '#size' => 40,
     );
-    $form['resultoptions'][$i]['option_start'] = array (
+    $form['resultoptions'][$i]['option_start'] = array(
       '#type' => 'textfield',
       '#title' => t('Percentage Start Range'),
       '#description' => 'Show this result for scored quizzes in this range. Leave blank for personality quizzes.',
       '#default_value' => $option['option_start'],
       '#size' => 5,
     );
-    $form['resultoptions'][$i]['option_end'] = array (
+    $form['resultoptions'][$i]['option_end'] = array(
       '#type' => 'textfield',
       '#title' => t('Percentage End Range'),
       '#description' => 'Show this result for scored quizzes in this range. Leave blank for personality quizzes.',
@@ -450,7 +464,7 @@
       if ($node->pass_rate && (isset($option['option_start']) || isset($option['option_end']))) {
       
         // check for a number 0-100
-        foreach(array('option_start' => 'start', 'option_end' => 'end') as $bound => $bound_text) {
+        foreach (array('option_start' => 'start', 'option_end' => 'end') as $bound => $bound_text) {
           if (!is_numeric($option[$bound])) {
             form_set_error($bound, t('The range %start value must be a number between 0% and 100%.', array('%start' => $bound_text))); 
           }    
@@ -470,7 +484,7 @@
         // check that range doesn't collide with any other range
         $option_range = range($option['option_start'], $option['option_end']);
         if ($intersect = array_intersect($taken_values, $option_range)) {
-          form_set_error('option_start', t('The ranges must not overlap each other. (%intersect)', array('%intersect' => implode(',',$intersect))));
+          form_set_error('option_start', t('The ranges must not overlap each other. (%intersect)', array('%intersect' => implode(',', $intersect))));
         } 
         else {
           $taken_values = array_merge($taken_values, $option_range);
@@ -525,7 +539,7 @@
  */
 function _quiz_insert_resultoptions($node) {
   foreach ($node->resultoptions as $option) {
-    if($option['option_name']) {
+    if ($option['option_name']) {
       $option['nid'] = $node->nid;
       $option['vid'] = $node->vid;
       _quiz_insert_result_option($option);
@@ -539,17 +553,17 @@
  */
 function _quiz_insert_result_option($option) {
   if (empty($option['option_id'])) {
-    $option['option_id'] = db_next_id('{quiz_node_result_options}' . '_option_id');
+    $option['option_id'] = db_next_id('{quiz_node_result_options}'.'_option_id');
     $sql = "INSERT INTO {quiz_node_result_options} (nid, vid, option_id, option_name, option_summary, option_start, option_end)".
            " VALUES(%d, %d, %d, '%s', '%s', %d, %d)";
-    $values = array ($option['nid'],
-                     $option['vid'],
-                     $option['option_id'],                       
-                     $option['option_name'],
-                     $option['option_summary'],
-                     $option['option_start'],
-                     $option['option_end'],
-                     );
+    $values = array($option['nid'],
+                    $option['vid'],
+                    $option['option_id'],                       
+                    $option['option_name'],
+                    $option['option_summary'],
+                    $option['option_start'],
+                    $option['option_end'],
+                    );
     db_query($sql, $values);
   }
 }
@@ -610,13 +624,14 @@
       // update an existing result option
       $sql = "UPDATE {quiz_node_result_options} SET option_name='%s', option_summary='%s', option_start = %d, " .
              " option_end = %d WHERE nid=%d AND vid=%d AND option_id=%d";
-      $values = array ($option['option_name'],
-                       $option['option_summary'],
-                       $option['option_start'],
-                       $option['option_end'],
-                       $node->nid,
-                       $node->vid,
-                       $option['option_id'],);
+      $values = array($option['option_name'],
+                      $option['option_summary'],
+                      $option['option_start'],
+                      $option['option_end'],
+                      $node->nid,
+                      $node->vid,
+                      $option['option_id'],
+                      );
     
       db_query($sql, $values);
     }
@@ -731,7 +746,7 @@
 
     // format the availability info
     $output .= '<p>'. format_date($node->quiz_open) .' &mdash; '. format_date($node->quiz_close) .'</p>';
-    $output .= '<p><strong>' . t('Days @quiz live for: ', array('@quiz' => QUIZ_NAME))  . '</strong> ' . floor(($node->quiz_close - $node->quiz_open) / 60 / 60 / 24) . '</p>';
+    $output .= '<p><strong>'. t('Days @quiz live for: ', array('@quiz' => QUIZ_NAME))  .'</strong> '. floor(($node->quiz_close - $node->quiz_open) / 60 / 60 / 24) .'</p>';
     $remaining = floor(($node->quiz_close - time()) / 60 / 60 / 24);
     $remaining = ($remaining < 0)?'Expired':$remaining;
     $output .= '<p><strong>Days remaining:</strong> '. $remaining .'</p>';
@@ -855,7 +870,7 @@
      if ($quiz = node_load(arg(1))) {
         // if anonymous user and no unique hash, refresh with a unique string to prevent caching
         if (!$user->uid && arg(4) == NULL) {
-          drupal_goto('node/' . $quiz->nid . '/quiz/start/' . md5(mt_rand() . time()));
+          drupal_goto('node/'. $quiz->nid .'/quiz/start/'. md5(mt_rand() . time()));
         }
 
        if (!isset($_SESSION['quiz_'. $quiz->nid]['quiz_questions'])) {
@@ -901,7 +916,7 @@
           }
           // if anonymous user, refresh url with unique hash to prevent caching
           if (!$user->uid) {
-            drupal_goto('node/' . $quiz->nid . '/quiz/start/' . md5(mt_rand() . time()));
+            drupal_goto('node/'. $quiz->nid .'/quiz/start/'. md5(mt_rand() . time()));
           }
         }
       }
@@ -993,7 +1008,7 @@
 function quiz_get_answers($answers, $tried) {
   $selected = array();
   if (is_array($answers)) {
-    foreach($answers as $key=>$answer) {
+    foreach ($answers as $key => $answer) {
       if (($key = array_search($answer['aid'], $tried)) !== FALSE) {
         $selected[] = $answer;
         //Correct answer was selected, so lets take that out the tried list
@@ -1038,7 +1053,7 @@
  * @return
  *   Filtered summary text or null if we are not displaying any summary
  */
-function _quiz_get_summary_text($quiz, $score){
+function _quiz_get_summary_text($quiz, $score) {
 
   if ($score['result_option']) {
     // unscored quiz, return the proper result option
@@ -1057,7 +1072,7 @@
     if ($admin) {
       $summary = t('The user passed this quiz.');      
     }
-    else if(trim($quiz->summary_pass) != ''){
+    else if (trim($quiz->summary_pass) != '') {
       $summary = (!empty($score_result)) ? $score_result : check_markup($quiz->summary_pass, $quiz->format);
     }
   } 
@@ -1066,11 +1081,11 @@
     // If we are coming from the admin view page 
     // only show a summary if we are using pass / fail.
     if ($admin) {
-      if ($quiz->pass_rate > 0){
+      if ($quiz->pass_rate > 0) {
         $summary = t('The user failed this quiz.');
       }
     }
-    else if(trim($quiz->summary_pass) != ''){
+    else if (trim($quiz->summary_pass) != '') {
       $summary = (!empty($score_result)) ? $score_result : check_markup($quiz->summary_default, $quiz->format);
     }
   }
@@ -1085,7 +1100,7 @@
  * @return summary text for the user's score
  */
 function _quiz_pick_result_option($qnid, $qvid, $score) {
-  return db_result(db_query('SELECT option_summary FROM {quiz_node_result_options} WHERE nid = %d AND vid = %d AND %d BETWEEN option_start AND option_end',$qnid, $qvid, $score));
+  return db_result(db_query('SELECT option_summary FROM {quiz_node_result_options} WHERE nid = %d AND vid = %d AND %d BETWEEN option_start AND option_end', $qnid, $qvid, $score));
 }
 
 /**
@@ -1244,8 +1259,8 @@
       $result = db_query_range("SELECT n.nid, n.vid " .
                                "FROM {node} n " .
                                "INNER JOIN {term_node} tn USING (nid)".
-                               "WHERE n.status = 1 AND tn.tid IN ($term_ids) " .
-                               "AND n.type IN ('" . implode("','", _quiz_get_question_types()) . "') " .
+                               "WHERE n.status = 1 AND tn.tid IN ($term_ids) ".
+                               "AND n.type IN ('". implode("','", _quiz_get_question_types()) ."') ".
                                "ORDER BY RAND()", 0, $num_random);
     }
     else {
@@ -1606,7 +1621,7 @@
   $questions = array();
   
   if (count($types)) {
-    $where_sql = "AND question.type IN ('" . implode("','", $types) . "')";
+    $where_sql = "AND question.type IN ('". implode("','", $types) ."')";
   }
 
   $result = db_query('
@@ -1618,8 +1633,8 @@
       (SELECT DISTINCT qnr.child_vid
       FROM {quiz_node_relationship} qnr
       WHERE qnr.parent_vid = %d
-      AND qnr.question_status != ' . QUESTION_NEVER . '))
-    ' . $where_sql, $quiz_vid
+      AND qnr.question_status != '. QUESTION_NEVER .'))
+    '. $where_sql, $quiz_vid
   );
 
   // Create questions array.
@@ -1797,7 +1812,7 @@
     $child_vid = $questions_in_db[$nid]->vid;
     $sql =
       "UPDATE {quiz_node_relationship} SET question_status = %d "
-      . "WHERE parent_vid = (SELECT src.vid FROM {node} AS src WHERE src.nid = %d) AND parent_nid = %d AND child_vid = %d";
+      ."WHERE parent_vid = (SELECT src.vid FROM {node} AS src WHERE src.nid = %d) AND parent_nid = %d AND child_vid = %d";
     $result = db_query($sql, $status, $quiz->nid, $quiz->nid, $child_vid);
     
     if (!$result) {
@@ -2006,10 +2021,12 @@
     if ($node->quiz_open > time()) {
       $status = 'future';      
     // the quiz is currently closed but will open in the future
-    } elseif (($node->quiz_open < time()) && ($node->quiz_close > time())) {
+    }
+    elseif (($node->quiz_open < time()) && ($node->quiz_close > time())) {
       $status = 'open';      
     // the quiz was open and is now closed
-    } else {
+    }
+    else {
       $status = 'closed';
     }
   }
@@ -2375,7 +2392,7 @@
   foreach ($questions as $question) {
     $cols = array();
     // Ask each question to render a themed report of how the user did
-    $cols[] = array('data' => theme($question->type . '_report', $question, $showpoints, $showfeedback), 'class' => 'quiz_summary_qrow');
+    $cols[] = array('data' => theme($question->type .'_report', $question, $showpoints, $showfeedback), 'class' => 'quiz_summary_qrow');
 
     // Get the score result for each question only if it's a scored quiz
     if ($showpoints) {
@@ -2401,7 +2418,7 @@
  */
 function theme_quiz_question_feedback($quiz, $report) {
   $output = '<div class="quiz-summary-question">';
-  $output .= theme($report->type . '_feedback', $quiz, $report);
+  $output .= theme($report->type .'_feedback', $quiz, $report);
   $output .= '</div><br class="clear" />';
   return $output;
 }
Index: quiz.install
===================================================================
RCS file: /cvs/drupal/contributions/modules/quiz/quiz.install,v
retrieving revision 1.12
diff -u -r1.12 quiz.install
--- quiz.install	12 Sep 2007 18:09:04 -0000	1.12
+++ quiz.install	22 Sep 2007 02:17:00 -0000
@@ -1,4 +1,5 @@
 <?php
+//$Id$
 
 /**
  * Implementation of hook_install()
@@ -28,7 +29,7 @@
         quiz_close INTEGER UNSIGNED DEFAULT 0,
         takes TINYINT UNSIGNED NOT NULL,
         time_limit INTEGER UNSIGNED DEFAULT 0 NOT NULL,
-        quiz_always TINYINT NOT NULL default 0,
+        quiz_always TINYINT NOT NULL DEFAULT 0,
         tid INTEGER UNSIGNED NOT NULL DEFAULT 0,
         PRIMARY KEY(property_id),
         KEY vid (vid, nid)
@@ -136,18 +137,105 @@
   variable_del('quiz_default_pass_rate');
 }
 
+ /*
+  *  This update will make schema up-to-date from version 1.1
+  *
+  *  The following databases have changed names:
+  *    From              	To
+  *    quiz 					quiz_node_properties
+  *    quiz_questions 		quiz_node_relationship
+  *	  quiz_question 		quiz_node_question_properties
+  *    quiz_result 			quiz_node_results
+  *    quiz_question_results quiz_node_results_answers
+  *	  
+  *
+  *  TODO: SCORE NEEDS TO BE UPDATED WITH A CALCULATED SCORE
+  *  A new function needs to be added to this script which will calculate the
+  *  score for each result.
+  *
+  *  'answers' in quiz_node_results_answers need to be checked against the 
+  *  answer in the multichoice module. Then this result needs to be added to the 
+  *  score column in quiz_node_properties.
+  *
+  *  I have not deleted the following columns as they are needed to 
+  *  calculate the score column.
+  *
+  *  
+  */
 function quiz_update_1() {
   $ret = array();
   
   switch ($GLOBALS['db_type']) {
     case 'mysql':
     case 'mysqli':
-      // add a result option to the answer table
-      $ret[] = update_sql("ALTER TABLE {quiz_question_answer} ADD result_option INTEGER UNSIGNED NULL");
+     
+       // rename quiz to quiz_node_properties
+       $ret[] = update_sql("ALTER TABLE {quiz} RENAME TO {quiz_node_properties}");
+       // update quiz_node_properties to new schema
+       $ret[] = update_sql("ALTER TABLE {quiz_node_properties} DROP number_of_questions;");
+       $ret[] = update_sql("ALTER TABLE {quiz_node_properties} 
+         ADD property_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
+         ADD vid INTEGER UNSIGNED NOT NULL AFTER property_id,
+         ADD number_of_random_questions TINYINT UNSIGNED DEFAULT 0 NOT NULL AFTER nid,
+         ADD tid INTEGER UNSIGNED NOT NULL DEFAULT 0,
+         DROP PRIMARY KEY,
+         ADD PRIMARY KEY (property_id),
+         ADD KEY vid (nid, vid);
+       ");
+       
+       // rename quiz_questions to quiz_node_relationship
+       $ret[] = update_sql("ALTER TABLE {quiz_questions} RENAME TO {quiz_node_relationship}");
+       // update quiz_node_relationship to new schema
+       $ret[] = update_sql("ALTER TABLE {quiz_node_relationship}
+         DROP PRIMARY KEY,
+         CHANGE quiz_nid parent_nid INTEGER UNSIGNED NOT NULL,
+         ADD parent_vid INTEGER UNSIGNED NOT NULL AFTER parent_nid,
+         CHANGE question_nid child_nid INTEGER UNSIGNED NOT NULL,
+         ADD child_vid INTEGER UNSIGNED NOT NULL AFTER child_nid,
+         ADD PRIMARY KEY (parent_nid, parent_vid, child_nid, child_vid);
+       ");
+       
+       // rename quiz_question to quiz_node_question_properties
+       $ret[] = update_sql("ALTER TABLE {quiz_question} RENAME TO {quiz_node_question_properties}");
+       // update quiz_node_question_properties to new schema
+       $ret[] = update_sql("ALTER TABLE {quiz_node_question_properties}
+         ADD vid INTEGER UNSIGNED NOT NULL AFTER nid,
+         ADD number_of_answers TINYINT UNSIGNED DEFAULT 1 NOT NULL,
+         DROP PRIMARY KEY;
+       ");
+       
+       
+       
+       // rename quiz_result to quiz_node_result
+       $ret[] = update_sql("ALTER TABLE {quiz_result} RENAME TO {quiz_node_results}");
+       // update quiz_node_result to new schema
+       $ret[] = update_sql("ALTER TABLE {quiz_node_results}
+         DROP PRIMARY KEY,
+         DROP score,
+         CHANGE rid result_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
+         CHANGE quiz_nid nid INTEGER UNSIGNED NOT NULL,
+         ADD vid INTEGER UNSIGNED NOT NULL AFTER nid,
+         ADD score TINYINT NOT NULL DEFAULT 0,
+         ADD PRIMARY KEY (result_id);
+       ");
+       
+       // rename quiz_question_results to quiz_node_results_answers
+       $ret[] = update_sql("ALTER TABLE {quiz_question_results} RENAME TO {quiz_node_results_answers}");
+       // update quiz_node_results_answers to new schema
+       $ret[] = update_sql("ALTER TABLE {quiz_node_results_answers}
+         DROP PRIMARY KEY,
+         CHANGE result_rid result_id INT UNSIGNED NOT NULL,
+         ADD question_vid INT UNSIGNED NOT NULL AFTER question_nid,
+         ADD is_correct TINYINT UNSIGNED NOT NULL DEFAULT '0',
+         ADD points_awarded TINYINT NOT NULL DEFAULT '0',
+         ADD answer_timestamp INT UNSIGNED NOT NULL,
+         ADD PRIMARY KEY(result_id, question_nid, question_vid);
+       ");
 
       // Create the result options table
-      $ret[] = update_sql("CREATE TABLE {quiz_result_options} (
+      $ret[] = update_sql("CREATE TABLE {quiz_node_result_options} (
         nid INTEGER UNSIGNED NOT NULL,
+        vid INTEGER UNSIGNED NOT NULL,
         option_id INTEGER UNSIGNED NOT NULL,
         option_name VARCHAR(255) character set utf8 NOT NULL,
         option_summary LONGTEXT character set utf8 NOT NULL,
Index: multichoice.install
===================================================================
RCS file: /cvs/drupal/contributions/modules/quiz/multichoice.install,v
retrieving revision 1.1
diff -u -r1.1 multichoice.install
--- multichoice.install	1 Sep 2007 01:08:14 -0000	1.1
+++ multichoice.install	22 Sep 2007 02:16:59 -0000
@@ -1,4 +1,5 @@
 <?php 
+// $Id$
         
 /**     
  * Implementation of hook_install()
@@ -55,3 +56,44 @@
   // delete from nodes and node_revisions
   db_query('DELETE FROM node, node_revisions, quiz_node_question_properties USING node LEFT JOIN node_revisions USING (nid) LEFT JOIN {quiz_node_question_properties} USING (nid) WHERE type IN ("multichoice")');
 }
+
+/**
+ * Update from quiz module 1.1
+ */
+function multichoice_update_1() {
+  $ret = array();
+   
+  switch ($GLOBALS['db_type']) {
+    case 'mysql':
+    case 'mysqli':
+      $ret[] = "Because of the vast updates to the database schema/n"
+               ."this upgrade will update your questions to the new schema/n"
+               ."but will not convert the score of previous entries";
+      // Create the quiz node user answers multichoice table
+      $ret[] = update_sql("CREATE TABLE {quiz_multichoice_user_answers} (
+        question_nid INTEGER UNSIGNED NOT NULL,
+        question_vid INTEGER UNSIGNED NOT NULL,
+        result_id INTEGER UNSIGNED NOT NULL,
+        answer_id INTEGER UNSIGNED NOT NULL,
+        PRIMARY KEY(result_id, question_nid, question_vid, answer_id)
+      ) /*!40100 DEFAULT CHARACTER SET utf8 */;");
+      
+      // add a result option to the answer table
+      $ret[] = update_sql("ALTER TABLE {quiz_question_answer} DROP INDEX question_nid;");
+      $ret[] = update_sql("ALTER TABLE {quiz_question_answer} RENAME TO {quiz_multichoice_answers};");
+      $ret[] = update_sql("ALTER TABLE {quiz_multichoice_answers}
+        DROP PRIMARY KEY,
+        CHANGE aid answer_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
+        CHANGE question_nid nid INTEGER UNSIGNED NOT NULL,
+        ADD vid INTEGER UNSIGNED NOT NULL AFTER nid,
+        CHANGE answer answer varchar(255) NOT NULL,
+        CHANGE feedback feedback LONGTEXT,
+        ADD result_option INTEGER UNSIGNED DEFAULT 0 AFTER feedback,
+        CHANGE score is_correct TINYINT UNSIGNED DEFAULT 0,
+        ADD PRIMARY KEY(answer_id);
+      ");
+      break;
+  }
+  return $ret;
+}
+       
\ No newline at end of file
Index: quiz_views.inc
===================================================================
RCS file: quiz_views.inc
diff -N quiz_views.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ quiz_views.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,506 @@
+<?php
+// $Id: views_quiz.inc,v 1.1 2007/08/13 00:47:38 westwesterson Exp $
+
+/**
+ * This include file implements views functionality on behalf of quiz.module
+ */
+
+function quiz_views_tables() {
+    $tables['quiz_user'] = array(
+      'name' => 'users',
+      'provider' => 'internal', // won't show up in external list.
+      // attach quiz user to results
+      'join' => array(
+        'type' => 'inner',
+        'left' => array(
+           'table' => 'quiz_node_results',
+           'field' => 'uid',
+         ),
+         'right' => array(
+           'field' => 'uid',
+         ),
+      ),
+      'fields' => array(
+        'name' => array(
+          'name' => t('Quiz Result: Quiz Taker'),
+          'sortable' => TRUE,
+          //'option' => 'string',
+          'handler' => array(
+            'quiz_views_handler_field_username_text' => t('normal text'),
+            'quiz_views_handler_field_username_link' => t('themed userlink'),
+          ),
+          'uid' => 'uid',
+          'addlfields' => array('uid'),
+          'help' => t('Displays the user who created this quiz result.'),
+        ),
+      ),
+      /**
+       * While this would be really great functionality, I can't for the life of me
+       * figure out how to fix sorting and filtering by username
+       */
+      /*
+      'sort' => array(
+        'name' => array(
+          'name' => t('Quiz Result: Quiz Taker'),
+          'help' => t('Sort by the user who created the quiz result.'),
+       //   'field' => 'name',
+        ),
+      ),
+      'filter' => array(
+        'uid' => array(
+          'name' => t('Quiz Result: Quiz Taker'),
+          'operator' => 'views_handler_operator_or',
+          'list' => 'views_handler_filter_username',
+          'value-type' => 'array',
+          'help' => t('This allows you to filter quiz results by a particular user. You might not find this useful if you have a lot of users.'),
+        ),
+      ),*/
+    );
+  $tables['quiz_node_results'] = array(
+    'name' => 'quiz_node_results',
+    'provider' => 'internal', // won't show up in external list.
+    // attach quiz results to node
+    'join' => array(
+      'left' => array(
+        'table' => 'node',
+        'field' => 'nid',
+       ),
+      'right' => array(
+        'field' => 'nid',
+      ),
+    ),
+    'fields' => array(
+      'result_id' => array(
+        'name' => t('Quiz Result: Result ID'),
+        'sortable' => TRUE,
+        'help' => t('Display the Result ID of the Quiz Result. Optionally you can show as a link to results, the result id with or without a link, or admin options, view and delete'),
+        'handler' => 'quiz_views_handler_field_result_id',
+        'option' => array(
+          '#type' => 'select',
+          '#options' => array(
+            'link' => 'As link',
+            'nolink' => 'Without link',
+            'onlylink' => 'As view link',
+            'actions' => 'As admin actions',
+            'adminview' => 'As admin view link',
+            'admindelete' => 'As admin delete link',
+          ),
+        ),
+      ),
+      'time_start' => array(
+        'name' => t('Quiz Result: Start Time'),
+        'sortable' => TRUE,
+        'handler' => views_handler_field_dates(),
+        'help' => t('Display the start time of a quiz result.'),
+      ),
+      'time_end' => array(
+        'name' => t('Quiz Result: End Time'),
+        'sortable' => TRUE,
+        'handler' => views_handler_field_dates(),
+        'help' => t('Display the end time of a quiz result.'),
+      ),
+      'score' => array(
+        'name' => t('Quiz Result: Score'),
+        'sortable' => TRUE,
+        'handler' => 'quiz_views_handler_field_score',
+        'addlfields' => array('result_id'),
+        'option' => array(
+          '#type' => 'select',
+          '#options' => array(
+            'link' => 'As number with link',
+            'nolink' => 'Without link'
+          ),
+        ),
+        'help' => t('Display the final score of a quiz result.'),
+      ),
+      'avg_score' => array(
+        'name' => t('Quiz Result: AVG Score'),
+        'sortable' => TRUE,
+        'help' => t('A user\'s average score across all quizzes they have taken'),
+        'notafield' => TRUE,
+        'handler' => 'quiz_views_handler_field_avg_score',
+        'query_handler' => 'quiz_views_handler_query_avg_score',
+        //'validate' => 'quiz_views_handler_avg_score_validate',
+      ),
+    ),
+    'sorts' => array(
+      'result_id' => array(
+        'name' => t('Quiz Result: Result ID'),
+        'help' => t('This allows you to sort by the Result ID.'),
+      ),
+      'score' => array(
+        'name' => t('Quiz Result: Score'),
+        'help' => t('This allows you to sort by the percentage needed to pass the quiz.'),
+      ),
+      'time_start' => array(
+        'name' => t('Quiz Result: Start Time'),
+        'handler' => 'views_handler_sort_date',
+        'option' => views_handler_sort_date_options(),
+        'help' => t('This allows you to sort by the start time of the quiz result.'),
+      ),
+      'time_end' => array(
+        'name' => t('Quiz Result: End Time'),
+        'handler' => 'views_handler_sort_date',
+        'option' => views_handler_sort_date_options(),
+        'help' => t('This allows you to sort by the end time of the quiz result.'),
+      ),
+      'avg_score' => array(
+        'name' => t('Quiz Result: AVG Score'),
+        'help' => t('Sort by overall score (the average across all completed quizzes by a user)'),
+        'handler' => 'quiz_views_handler_sort_avg_score',
+      ),
+    ),
+    'filters' => array(
+      'score' => array(
+        'name' => t('Quiz Result: Score'),
+        'operator' => 'views_handler_operator_gtlt',
+        'option' => 'integer',
+        'help' => t('This allows you to filter by the quiz result score.'),
+      ),
+      'time_start' => array(
+        'name' => t('Quiz Result: Start Time'),
+        'operator' => 'views_handler_operator_gtlt',
+        'value' => views_handler_filter_date_value_form(),
+        'handler' => 'views_handler_filter_timestamp',
+        'option' => 'string',
+        'help' => t('This allows you to sort by the start time of the quiz result.'),
+      ),
+      'time_end' => array(
+        'name' => t('Quiz Result: End Time'),
+        'operator' => 'views_handler_operator_gtlt',
+        'value' => views_handler_filter_date_value_form(),
+        'handler' => 'views_handler_filter_timestamp',
+        'option' => 'string',
+        'help' => t('This allows you to sort by the end time of the quiz result.'),
+      ),
+    ),
+  );
+  $tables['quiz_node_properties'] = array(
+    'name' => 'quiz_node_properties',
+    'provider' => 'internal', // won't show up in external list.
+    // attach quiz table to node
+    'join' => array(
+      'left' => array(
+        'table' => 'node',
+        'field' => 'nid',
+      ),
+      'right' => array(
+        'field' => 'nid',
+      ),
+    ),
+    'fields' => array(
+      'pass_rate' => array(
+        'name' => t('Quiz: Passing Percentage Rate'),
+        'sortable' => TRUE,
+        'handler' => 'quiz_views_handler_percentage',
+        'help' => t('Display the percentage needed to pass the quiz.'),
+      ),
+      'takes' => array(
+        'name' => t('Quiz: Number of Takes Allowed'),
+        'sortable' => TRUE,
+        'handler' => 'views_handler_field_takes',
+        'help' => t('Display the number of takes allowed on a quiz.'),
+      ),
+      /*
+       * The database has replaced this field with the number of random questions
+       * 
+       * TODO add to database total number of questions per quiz, 
+       * otherwise it takes too many queries if there are a lot of quizzes on a page 
+       * and is a performance issue.
+       */
+      /*'number_of_questions' => array(
+        'name' => t('Quiz: Number of Questions'),
+        'sortable' => TRUE,
+        'handler' => 'views_handler_field_int',
+        'help' => t('Display the number of quiz questions.'),
+      ),*/
+      'quiz_open' => array(
+        'name' => t('Quiz: Start Time'),
+        'sortable' => TRUE,
+        'handler' => views_handler_field_dates(),
+        'help' => t('Display the start date of a quiz.'),
+      ),
+      'quiz_closed' => array(
+        'name' => t('Quiz: End Time'),
+        'sortable' => TRUE,
+        'handler' => views_handler_field_dates(),
+        'help' => t('Display the end date of a quiz.'),
+      ),
+      'quiz_always' => array(
+        'name' => t('Quiz: Always Available'),
+        'sortable' => TRUE,
+        'handler' => views_handler_yes_no,
+        'help' => t('Display the end date of a quiz.'),
+      ),      
+    ),
+    'sorts' => array(
+      'pass_rate' => array(
+        'name' => t('Quiz: Passing Rate'),
+        'help' => t('This allows you to sort by the percentage needed to pass the quiz.'),
+      ),
+      'takes' => array(
+        'name' => t('Quiz: Number of Takes Allowed'),
+        'help' => t('This allows you to sort by the number of takes allowed of the quiz.'),
+      ),
+      // changed to number of random questions
+      /*'number_of_questions' => array(
+        'name' => t('Quiz: Number of Questions'),
+        'help' => t('This allows you to sort by the number of questions in the quiz.'),
+      ),*/
+      'quiz_open' => array(
+        'name' => t('Quiz: Start Time'),
+        'handler' => 'views_handler_sort_date',
+        'option' => views_handler_sort_date_options(),
+        'help' => t('This allows you to sort by the Start Date of the quiz.'),
+      ),
+      'quiz_closed' => array(
+        'name' => t('Quiz: End Time'),
+        'handler' => 'views_handler_sort_date',
+        'option' => views_handler_sort_date_options(),
+        'help' => t('This allows you to sort by the end date of the quiz.'),
+      ),
+    ),
+    'filters' => array(
+      'pass_rate' => array(
+        'name' => t('Quiz: Passing Rate'),
+        'operator' => 'views_handler_operator_gtlt',
+        'option' => 'integer',
+        'help' => t('This allows you to filter by the percentage needed to pass the quiz.'),
+      ),
+      // changed to number of random questions
+      /*'number_of_questions' => array(
+        'name' => t('Quiz: Number of Questions'),
+        'operator' => 'views_handler_operator_gtlt',
+        'option' => 'integer',
+        'help' => t('This allows you to filter by the number of questions in the quiz.'),
+      ),*/
+      'takes' => array(
+        'name' => t('Quiz: Number of Takes Allowed'),
+        'operator' => 'views_handler_operator_gtlt',
+        'option' => 'integer',
+        'help' => t('This allows you to filter by the number of takes allowed of the quiz. Enter 0 for unlimited'),
+      ),
+      'quiz_open' => array(
+        'name' => t('Quiz: Start Time'),
+        'operator' => 'views_handler_operator_gtlt',
+        'value' => views_handler_filter_date_value_form(),
+        'handler' => 'views_handler_filter_timestamp',
+        'option' => 'string',
+        'help' => t('This allows you to sort by the Start Date of the quiz.'),
+      ),
+      'quiz_closed' => array(
+        'name' => t('Quiz: End Time'),
+        'operator' => 'views_handler_operator_yesno()',
+        'value' => views_handler_filter_date_value_form(),
+        'handler' => 'views_handler_filter_timestamp',
+        'option' => 'string',
+        'help' => t('This allows you to sort by the end date of the quiz.'),
+      ),
+      'quiz_always' => array(
+        'name' => t('Quiz: Always Available'),
+        'operator' => 'views_handler_operator_yesno',
+        'value' => integer,
+        'handler' => 'views_handler_yes_no',
+        'option' => 'string',
+        'help' => t('This allows you to sort by the end date of the quiz.'),
+      ),
+    ),
+  );
+  return $tables;
+}
+
+/**
+ * Callback for quiz_views_tables(): user name as plaintext.
+ */
+function quiz_views_handler_field_username_text($fieldinfo, $fielddata, $value, $data) {
+  return check_plain($value);
+}
+
+/**
+ * Callback for quiz_views_tables(): user name as a link to the usernode.
+ */
+function quiz_views_handler_field_username_link($fieldinfo, $fielddata, $value, $data) {
+  if ($value) {
+    $obj->name = $value;
+    $uidfield = $fielddata['tablename'] ."_". $fieldinfo['uid'];
+    $obj->uid = $data->$uidfield;
+    return theme('username', $obj);
+  }
+}
+
+/**
+ * Generic handler given a 1 & 0 will output as a yes for 1 and no for 0.
+ */
+function views_handler_yes_no($fieldinfo, $fielddata, $value, $data) {
+  if ($value == '0') {
+    $value = "No";
+  }
+  else $value = "Yes";
+  return $value;
+}
+
+/**
+ * Generic handler given a string will add a percentage sign to the end
+ */
+function quiz_views_handler_percentage($fieldinfo, $fielddata, $value, $data) {
+  return $value .'%';
+}
+/**
+ * Handler for result_id field allowing it to optionally show 
+ * as the result_id with a link, the result_id with no link or
+ * just a link to the result.
+ *
+ * Ideally, there should be some way to theme the word Results
+ */
+function quiz_views_handler_field_result_id($fieldinfo, $fielddata, $value, $data) {
+  switch ($fielddata['options']) {
+    case 'nolink':
+      return check_plain($value);
+    case 'onlylink':
+      return l(t('Results'), "user/quiz/$value/userresults");
+    case 'link':
+      return l($value, "user/quiz/$value/userresults");
+    case 'adminview':
+      return l(t('view'), "admin/quiz/$value/view");
+    case 'actions':
+      return l(t('view'), "admin/quiz/$value/view") .' | '. l(t('delete'), "admin/quiz/$value/delete");
+    case 'admindelete':
+      return l(t('delete'), "admin/quiz/$value/delete");
+  }
+}
+
+/*
+ * Handler turning a score into a link to the quiz result
+ * TODO this should link to the userresults page, but result_id wont follow
+ * so for now, there is no link option.
+ */
+function quiz_views_handler_field_score($fieldinfo, $fielddata, $value, $data) {
+  $value .= '%';
+//  if ($fielddata['options'] == 'nolink') {
+    return check_plain($value);
+//  }
+//  return l($value, "user/quiz/$data->result_id/userresults");
+}
+
+/**
+ * Generic handler outputs a number except for 0 which outputs unlimited.
+ */
+function views_handler_field_takes($fieldinfo, $fielddata, $value, $data) {
+  if ($value == 0) $value = 'Unlimited';
+  return $value;
+}
+/*
+ * Argument Implementation not yet ready for prime-time (need to do serious coding)
+ */
+function quiz_views_arguments() {
+  $arguments = array(
+     // number of questions no longer exists in the database.
+     /*'number_of_questions' => array(
+       'name' => t('Quiz: Number of Questions'),
+       'handler' => 'views_handler_arg_noq',
+       'help' => t('The Number of Questions Argument allows you to filter to nodes by the number of questions in the quiz.'),
+     ),*/
+  );
+}
+
+/*function views_handler_arg_noq() {
+  switch($op) {
+    case 'summary':
+    case 'sort':
+    case 'filter':
+    case 'link':
+    case 'title':
+  }
+}*/
+
+function quiz_views_default_views() {
+  $view = new stdClass();
+  $view->name = 'quiz_results';
+  $view->description = t('Quiz Results');
+  $view->access = array (
+  0 => '-1',
+);
+  $view->page = TRUE;
+  $view->page_title = t('Quiz Results');
+  $view->page_empty = t('There are no quiz results.');
+  $view->page_empty_format = '1';
+  $view->page_type = 'table';
+  $view->url = 'admin/quiz';
+  $view->use_pager = TRUE;
+  $view->nodes_per_page = '25';
+  $view->menu = TRUE;
+  $view->menu_title = t('Quiz Results');
+  $view->field = array (
+    array (
+      'tablename' => 'quiz_node_results',
+      'field' => 'result_id',
+      'label' => 'Action',
+      'options' => 'actions',
+    ),
+    array (
+      'tablename' => 'node',
+      'field' => 'title',
+      'label' => t('Quiz Title'),
+      'handler' => 'views_handler_field_nodelink',
+      'sortable' => '1',
+      'options' => 'link',
+    ),
+    array (
+      'tablename' => 'quiz_user',
+      'field' => 'name',
+      'label' => t('User Name'),
+      'handler' => 'quiz_views_handler_field_username_link',
+      'sortable' => '1',
+    ),
+    array (
+      'tablename' => 'quiz_node_results',
+      'field' => 'result_id',
+      'label' => t('Result ID'),
+      'sortable' => '1',
+      'options' => 'nolink',
+    ),
+    array (
+      'tablename' => 'quiz_node_results',
+      'field' => 'time_end',
+      'label' => t('Finished'),
+      'sortable' => '1',
+      'handler' => 'views_handler_field_date_small',
+    ),
+    array (
+      'tablename' => 'quiz_node_results',
+      'field' => 'score',
+      'label' => t('Score'),
+      'sortable' => '1',
+      'options' => 'link',
+    ),
+  );
+  $view->filter = array (
+    array (
+      'tablename' => 'node',
+      'field' => 'type',
+      'operator' => 'OR',
+      'options' => '',
+      'value' => array (
+  0 => 'quiz',
+),
+    ),
+  );
+  $view->requires = array(quiz_node_results, node, quiz_user);
+  $views[$view->name] = $view;
+  return $views;
+}
+
+function quiz_views_handler_query_avg_score($field, $fieldinfo, &$query) {
+  $query->add_field("AVG(score)", null, "avg_score");
+  $query->add_groupby("quiz_node_results.uid");
+  $query->add_where("score IS NOT NULL");
+  $query->add_where("time_end > 0");
+}
+
+function quiz_views_handler_field_avg_score($fieldinfo, $fielddata, $value, $data) {
+  return number_format($data->avg_score, 2) .'%';
+}
+
+function quiz_views_handler_sort_avg_score($op, &$query, $sortinfo, $sortdata) {
+  $query->add_orderby(NULL, "AVG(score)", $sortdata['sortorder'], 'avg_score');
+}
