Index: multichoice.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/quiz/multichoice.module,v
retrieving revision 1.29
diff -u -r1.29 multichoice.module
--- multichoice.module	9 May 2006 20:37:44 -0000	1.29
+++ multichoice.module	11 Sep 2006 22:12:25 -0000
@@ -45,8 +45,9 @@
 function multichoice_menu($may_cache) {
   $items = array();
   if ($may_cache) {
-    $items[] = array('path' => 'node/add/multichoice', 'title' => t('multichoice'),
-      'access' => user_access('create multichoice'));
+    $items[] = array('path' => 'node/add/multichoice',
+                     'title' => t('multichoice'),
+                     'access' => user_access('create multichoice'));
   }
   return $items;
 }
@@ -94,21 +95,33 @@
     '#tree' => TRUE,
     '#theme' => 'multichoice_form'
   );
+  
   for ($i = 0; $i < $node->rows; $i++) {
     $form['answers'][$i]['correct'] = array(
       '#type' => 'checkbox',
-      '#default_value' => $answers[$i]['correct'],
+      '#default_value' => $answers[$i]['points'],
     );
     $form['answers'][$i]['answer'] = array(
       '#type' => 'textarea',
       '#default_value' => $answers[$i]['answer'],
-      '#cols' => 20,
+      '#cols' => 30, '#rows' => 2,
     );
     $form['answers'][$i]['feedback'] = array(
       '#type' => 'textarea',
       '#default_value' => $answers[$i]['feedback'],
-      '#cols' => 20,
+      '#cols' => 30, '#rows' => 2,
     );
+    if ($answers[$i]['aid']) {
+      $form['answers'][$i]['delete'] = array(
+        '#type' => 'checkbox',
+        '#default_value' => 0,
+      );
+
+      $form['answers'][$i]['aid'] = array(
+        '#type' => 'hidden',
+        '#value' => $answers[$i]['aid'],
+      );
+    }
   }
 
   $form['more'] = array(
@@ -133,7 +146,8 @@
   $header = array(
     array('data' => t('Correct')),
     array('data' => t('Answer'), 'style' => 'width:250px;'),
-    array('data' => t('Feedback'), 'style' => 'width:250px;')
+    array('data' => t('Feedback'), 'style' => 'width:250px;'),
+    array('data' => t('Delete')),
   );
 
   // Format table rows
@@ -143,6 +157,7 @@
       form_render($form[$key]['correct']),
       form_render($form[$key]['answer']),
       form_render($form[$key]['feedback']),
+      form_render($form[$key]['delete']),
     );
   }
 
@@ -171,7 +186,9 @@
   // Validate answers
   $answers = array();
   $corrects = 0;
-  foreach ($node->answers as $key => $answer) {
+
+  while(list($key, $answer) = each($node->answers)) {
+
     if ($answer['correct']) {
       if ($corrects && !$node->multiple_answers) {
         form_set_error('multiple_answers', t('Single choice yet multiple correct answers are present'));
@@ -198,30 +215,63 @@
  * Implementation of hook_insert().
  */
 function multichoice_insert($node) {
-  db_query("INSERT INTO {multichoice} (nid, multiple_answers, answers) VALUES (%d, %d, '%s')", $node->nid, $node->multiple_answers, serialize($node->answers));
+  db_query("INSERT INTO {quiz_question} (nid, properties) VALUES(%d, '%s')", $node->nid, serialize(array('multiple_answers' => $node->multiple_answers)));
+  
+  while(list($key, $value) = each($node->answers)) {
+    if (trim($value['answer']) != "")
+      db_query("INSERT INTO {quiz_question_answer} (question_nid, answer, feedback, points) VALUES(%d, '%s', '%s', %d)", $node->nid, $value['answer'], $value['feedback'], $value['correct']);
+  }
 }
 
 /**
  * Implementation of hook_update().
  */
 function multichoice_update($node) {
-  db_query("UPDATE {multichoice} SET multiple_answers = %d, answers = '%s' WHERE nid = %d", $node->multiple_answers, serialize($node->answers), $node->nid);
+  db_query("UPDATE {quiz_question} SET properties = '%s' WHERE nid = %d", serialize(array('multiple_answers' => $node->multiple_answers)), $node->nid);
+  
+  while(list($key, $value) = each($node->answers)) {
+    if ($value['aid']) {
+      $value['answer'] = trim($value['answer']);
+      if ($value['delete'] == 1 || empty($value['answer'])) {
+        //Delete this entry
+        db_query("DELETE FROM {quiz_question_answer} WHERE aid = %d", $value['aid']);
+      } else {
+        //Update this entry
+        db_query("UPDATE {quiz_question_answer} SET answer = '%s', feedback = '%s', points = %s WHERE aid = %d", $value['answer'], $value['feedback'], $value['correct'], $value['aid']);
+      }
+    } else if (trim($value['answer']) != "") {
+      //If there is an answer, insert a new row
+        db_query("INSERT INTO {quiz_question_answer} (question_nid, answer, feedback, points) VALUES(%d, '%s', '%s', %d)", $node->nid, $value['answer'], $value['feedback'], $value['correct']);
+    }
+  }
 }
 
 /**
  * Implementation of hook_delete().
  */
 function multichoice_delete($node) {
-  db_query("DELETE FROM {multichoice} WHERE nid = %d", $node->nid);
-  db_query("DELETE FROM {quiz_question} WHERE question_nid = %d", $node->nid);
+  db_query("DELETE FROM {quiz_question_answer} WHERE question_nid = %d", $node->nid);
+  db_query("DELETE FROM {quiz_question} WHERE nid = %d", $node->nid);
 }
 
 /**
  * Implementation of hook_load().
  */
 function multichoice_load($node) {
-  $additions = db_fetch_object(db_query('SELECT * FROM {multichoice} WHERE nid = %d', $node->nid));
-  $additions->answers = unserialize($additions->answers);
+  $additions = db_fetch_object(db_query("SELECT * FROM {quiz_question} WHERE nid = %d", $node->nid));
+  
+  $answers = array();
+  $result = db_query("SELECT * FROM {quiz_question_answer} WHERE question_nid = %d", $node->nid);
+  while($line = db_fetch_array($result)) {
+    $answers[] = $line;
+  }
+  
+  
+  $additions->answers = $answers;
+  
+  $additions->properties = unserialize($additions->properties);
+  $additions->multiple_answers = $additions->properties['multiple_answers'];
+  
   return $additions;
 }
 
@@ -266,24 +316,28 @@
  *   HTML output
  */
 function multichoice_render_question($node) {
-
   // Radio buttons for single selection questions, checkboxes for multiselect
   if ($node->multiple_answers == 0) {
     $type = 'radios';
-  } else {
+  }
+  else {
     $type = 'checkboxes';
   }
 
   // Get options
   $options = array();
-  foreach ($node->answers as $key => $answer) {
+
+  while(list($key, $answer) = each($node->answers)) {
     if (empty($answer['correct']) && empty($answer['answer']) && empty($answer['feedback'])) {
       unset($node->answers[$key]);
-    } else {
+    }
+    else {
       $options[$key] = $answer['answer'];
     }
   }
-
+  
+  $form['question'] = array('#type' => 'markup', '#value' => $node->body);
+  
   // Create form
   $form['tries'] = array(
     '#type' => $type,
@@ -307,52 +361,58 @@
  * @return
  *   Array of results, in the form of:
  *   array(
- *     'input' => array of answer(s) chosen by user
- *     'feedback' => array of feedback for selected answer(s)
- *     'score' => 0 or 1, depending on whether question was answered properly
+ *     'answers' => array of correct answer(s)
+ *     'tried' => array of selected answer(s)
  *   );
  */
 function multichoice_evaluate_question($nid) {
   $question = node_load($nid);
   $results = array();
-  $points = 0;
 
   if (isset($_POST['edit']['tries'])) {
     if (is_array($_POST['edit']['tries'])) {
 
       // Multi-answer question
-      foreach ($_POST['edit']['tries'] as $key => $try) {
-        if ($try == 1) {
-
-          $results['input'][] = $key;
-
-          $results['feedback'][] = $question->answers[$key]['feedback'];
-        }
-        $points += ($try == $question->answers[$key]['correct']);
+      while(list($key, $try) = each($_POST['edit']['tries'])) {
+        $results['answers'] = $question->answers;
+        $results['tried'][] = $question->answers[$try]['aid'];
       }
-      $results['score'] = ($points == count($question->answers));
     }
     else {
 
       // Single-answer question
-      $count = 0;
-      $input = $_POST['edit']['tries'];
-
-      $results['input'][] = $input;
-      $results['feedback'][] = $question->answers[$input]['feedback'];
-      foreach ($question->answers as $key => $value) {
-        if ($value[0] == 1) {
-          $points = $input == $key ? 1 : 0;
-        }
-        $count++;
-      }
-      $results['score'] = $points;
+      $results['answers'] = $question->answers;
+      $results['tried'][] = $question->answers[$_POST['edit']['tries']]['aid'];
     }
   }
 
   return $results;
 }
 
+function multichoice_calculate_result($answers, $tried) {
+  while(list($key, $answer) = each($answers)) {
+    if ($answer['points'] == 1) {
+      if (($key = array_search($answer['aid'], $tried)) !== FALSE) {
+        //Correct answer was selected, so lets take that out the tried list
+        unset($tried[$key]);
+      }
+      else {
+        //Correct answer was not in the "tried" list, so score 0
+        return 0;
+      }
+    }
+  }
+  //Finally - have we got any answers left?
+  //If so - they weren't knocked out as one of the correct ones so logically they must be incorrect!
+  if (count($tried) > 0) return 0;
+
+  
+  //Finally, we can consider this correct if its passed the above tests!
+  return 1;
+}
+
+
+
 /**
  * List all multiple choice questions
  *
Index: quiz.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/quiz/quiz.module,v
retrieving revision 1.65
diff -u -r1.65 quiz.module
--- quiz.module	9 May 2006 20:37:44 -0000	1.65
+++ quiz.module	11 Sep 2006 22:12:25 -0000
@@ -1,5 +1,5 @@
 <?php
-// $Id: quiz.module,v 1.65 2006/05/09 20:37:44 webchick Exp $
+// $Id$
 
 /**
  * @file
@@ -8,6 +8,18 @@
  * This module allows the creation of interactive quizzes for site visitors
  */
 
+/*
+ * Define question statuses...
+ */
+define("QUESTION_RANDOM", 0);
+define("QUESTION_ALWAYS", 1);
+define("QUESTION_NEVER", 2);
+
+
+
+
+
+
 /**
  * Implementation of hook_perm().
  */
@@ -50,8 +62,10 @@
   $items = array();
 
   if ($may_cache) {
-    $items[] = array('path' => 'node/add/quiz', 'title' => t('quiz'),
-    'access' => user_access('create quizzes'));
+    $items[] = array(
+      'path' => 'node/add/quiz',
+      'title' => t('quiz'),
+      'access' => user_access('create quizzes'));
   }
   else {
     if (arg(0) == 'node' && is_numeric(arg(1))) {
@@ -59,18 +73,46 @@
       if ($node->type == 'quiz') {
 
         // Menu item for creating adding questions to quiz
-        $items[] = array('path' => 'node/'. arg(1) .'/questions', 'title' => t('add questions'),
+        $items[] = array(
+          'path' => 'node/'. arg(1) .'/questions',
+          'title' => t('add questions'),
           'callback' => 'quiz_questions',
           'access' => user_access('create quizzes'),
-          'type' => MENU_LOCAL_TASK);
+          'type' => MENU_LOCAL_TASK,);
 
         // Menu item for quiz taking interface
-        $items[] = array('path' => 'node/'. arg(1) .'/quiz/start', 'title' => t('take quiz'),
+        $items[] = array(
+          'path' => 'node/'. arg(1) .'/quiz/start',
+          'title' => t('take quiz'),
           'callback' => 'quiz_take_quiz',
           'access' => user_access('access quizzes'),
-          'type' => MENU_LOCAL_TASK);
+          'type' => MENU_LOCAL_TASK,);
       }
     }
+    else {
+    
+
+      $items[] = array(
+        'path' => 'admin/quiz/' . arg(2) . "/view",
+        'title' => t('view quiz'),
+        'callback' => 'quiz_admin_results',
+        'access' => user_access('administer quizzes'),
+        'type' => MENU_CALLBACK);
+                       
+      $items[] = array(
+        'path' => 'admin/quiz/' . arg(2) . "/delete",
+        'title' => t('delete quiz'),
+        'callback' => 'quiz_admin_result_delete',
+        'access' => user_access('administer quizzes'),
+        'type' => MENU_CALLBACK);
+
+      $items[] = array(
+        'path' => 'admin/quiz',
+        'title' => t('quizzes'),
+        'callback' => 'quiz_admin',
+        'access' => user_access('administer quizzes'),
+        'type' => MENU_NORMAL_ITEM);
+    }
   }
 
   return $items;
@@ -135,18 +177,20 @@
     ),
     '#description' => t('Indicates at what point feedback for each question will be given to the student'),
   );
-
+  
+  list($d['year'], $d['month'], $d['day']) = explode('-', substr($node->open, 0, 10));
   $form['open'] = array(
     '#type' => 'date',
     '#title' => t('Open date'),
-    '#default_value' => $node->open,
+    '#default_value' => $d,
     '#description' => t('The date students should be able to start taking this quiz'),
   );
 
+  list($d['year'], $d['month'], $d['day']) = explode('-', substr($node->close, 0, 10));
   $form['close'] = array(
     '#type' => 'date',
     '#title' => t('Close date'),
-    '#default_value' => $node->close,
+    '#default_value' => $d,
     '#description' => t('The date the quiz will close and prevent students from taking the quiZ'),
   );
 
@@ -177,20 +221,30 @@
   if ($node->number_of_questions < 1) {
     form_set_error('number_of_questions', t('Number of questions is required and must be a positive number.'));
   }
+  if (mktime(0,0,0, $node->open['month'], $node->open['day'], $node->open['year']) > mktime(0,0,0, $node->close['month'], $node->close['day'], $node->close['year'])) {
+    form_set_error('close', t('Close date before open date'));
+    form_set_error('open', t('Open date after close date'));
+  }
 }
 
 /**
  * Implementation of hook_insert().
  */
 function quiz_insert($node) {
-  db_query("INSERT INTO {quiz} (nid, number_of_questions, shuffle, backwards_navigation, feedback_time, open, close, takes) VALUES(%d, %d, %d, %d, %d, %d, %d, %d)", $node->nid, $node->number_of_questions, $node->shuffle, $node->backwards_navigation, $node->feedback_time, 0, 0, $node->takes);
+  $open = $node->open['year'].'-'.$node->open['month'].'-'.$node->open['day'];
+  $close = $node->close['year'].'-'.$node->close['month'].'-'.$node->close['day'];
+    
+  db_query("INSERT INTO {quiz} (nid, number_of_questions, shuffle, backwards_navigation, feedback_time, open, close, takes) VALUES(%d, %d, %d, %d, %d, '%s', '%s', %d)", $node->nid, $node->number_of_questions, $node->shuffle, $node->backwards_navigation, $node->feedback_time, $open, $close, $node->takes);
 }
 
 /**
  * Implementation of hook_update().
  */
 function quiz_update($node) {
-  db_query("UPDATE {quiz} SET number_of_questions = %d, shuffle = %d, backwards_navigation = %d, feedback_time = %d, open = %d, close = %d, takes = %d WHERE nid = %d", $node->number_of_questions, $node->shuffle, $node->backwards_navigation, $node->feedback_time, 0, 0, $node->takes, $node->nid);
+  $open = $node->open['year'].'-'.$node->open['month'].'-'.$node->open['day'];
+  $close = $node->close['year'].'-'.$node->close['month'].'-'.$node->close['day'];
+    
+  db_query("UPDATE {quiz} SET number_of_questions = %d, shuffle = %d, backwards_navigation = %d, feedback_time = %d, open = '%s', close = '%s', takes = %d WHERE nid = %d", $node->number_of_questions, $node->shuffle, $node->backwards_navigation, $node->feedback_time, $open, $close, $node->takes, $node->nid);
 }
 
 /**
@@ -198,7 +252,7 @@
  */
 function quiz_delete($node) {
   db_query("DELETE FROM {quiz} WHERE nid = %d", $node->nid);
-  db_query("DELETE FROM {quiz_question} WHERE quiz_nid = %d", $node->nid);
+  db_query("DELETE FROM {quiz_questions} WHERE quiz_nid = %d", $node->nid);
 }
 
 /**
@@ -206,7 +260,7 @@
  */
 function quiz_load($node) {
   $additions = db_fetch_object(db_query('SELECT * FROM {quiz} WHERE nid = %d', $node->nid));
-  $results = db_query('SELECT * FROM {quiz_question} WHERE quiz_nid = %d', $node->nid);
+  $results = db_query('SELECT * FROM {quiz_questions} WHERE quiz_nid = %d', $node->nid);
   while ($question = db_fetch_object($results)) {
     $additions->question_status[$question->question_nid] = $question->question_status;
   }
@@ -229,7 +283,10 @@
       t('Number of takes'));
     $shuffle = $node->shuffle == 1 ? t('Yes') : t('No');
     $backwards = $node->backwards_navigation == 1 ? t('Yes') : t('No');
-    $feedback_options = array(t('At the end of each quiz'), t('After each question'), t('Do not show'));
+    $feedback_options = array(
+      t('At the end of each quiz'),
+      t('After each question'),
+      t('Do not show'));
     $feedback = $feedback_options[$node->feedback_time];
     $takes = $node->takes == 0 ? t('Unlimited') : "$node->takes minutes";
     $rows = array();
@@ -240,12 +297,28 @@
       $feedback,
       $takes);
     $node->body .= theme('table', $header, $rows);
+    
+    
+    // Format Quiz Dates
+    $node->body .= '<h3>'. t('Quiz start/end') .'</h3>';
+    $node->body .= "<p>" . date("l, jS F Y", strtotime($node->open)) . " &mdash " . date("l, jS F Y", strtotime($node->close)) ."</p>";
+    $node->body .= "<p><strong>Days quiz live for:</strong> " . floor((strtotime($node->close) - strtotime($node->open)) / 60 / 60 / 24) . "</p>";
+    
+    $remaining = floor((strtotime($node->close) - time()) / 60 / 60 / 24);
+    $remaining = ($remaining < 0)?"Expired":$remaining;
+    $node->body .= "<p><strong>Days remaining:</strong> " . $remaining . "</p>";
+    
+    $elapsed = floor((time() - strtotime($node->open)) / 60 / 60 / 24);
+    $elapsed = ($elapsed < 0)?(-$elapsed)." days to go":$elapsed;
+    $node->body .= "<p><strong>Days since start:</strong> " . $elapsed . "</p>";
+    
 
     // Format taxonomy selection (if applicable)
     if (function_exists(taxonomy_node_get_terms)) {
       $node->body .= '<h3>'. t('Taxonomy selection') .'</h3>';
       $terms = array();
-      foreach (taxonomy_node_get_terms($node->nid) as $term) {
+      
+      foreach(taxonomy_node_get_terms($node->nid) as $term) {
         $terms[] = $term->name;
       }
       if (!empty($terms)) {
@@ -289,24 +362,17 @@
           $_SESSION['quiz_questions'] = $questions;
           $_SESSION['rid'] = $rid;
 
-        } else {
+        }
+        else {
           drupal_set_message(t('There was a problem starting the quiz. Please try again later.', 'error'));
         }
       }
 
       // Check for answer submission
-      $submit_message = t('Submit');
-      if ($_POST['quiz_op'] == t('Submit')) {
-
-        // Update results table with this question and remove it from the list of remaining questions
+      if ($_POST['op'] == t('Submit')) {
         $former_question = node_load(array('nid' => array_shift($_SESSION['quiz_questions'])));
         $result = module_invoke($former_question->type, 'evaluate_question', $former_question->nid);
-
-        // Get current results, add new results, and save back to database
-        $former_result = unserialize(db_result(db_query('SELECT results FROM {quiz_results} WHERE rid = %d', $_SESSION['rid'])));
-        $former_result[$former_question->nid] = $result;
-        db_query("UPDATE {quiz_results} SET results = '%s'", serialize($former_result));
-
+        db_query("REPLACE {quiz_question_results} VALUES(%d, %d, '%s')", $_SESSION['rid'], $former_question->nid, serialize($result));
       }
 
       // Check if at the end of quiz
@@ -315,21 +381,17 @@
         // Load the next question
         $question_node = node_load(array('nid' => $_SESSION['quiz_questions'][0]));
 
-/** TODO: Move this to multichoice?
-        // Don't show Submit button on preview
-        if ($_POST['op'] != t('Preview')) {
-          $question .= form_submit($submit_message, 'quiz_op');
-        }
-
-        // Display question form
-        $node->body = form($question);
-        */
         $node->body = module_invoke($question_node->type, 'render_question', $question_node);
 
       }
       else {
 
-        // At the end of quiz, so display results and remove session variables
+        // At the end of quiz...
+        
+        //First - update the result to show we have finished.
+        db_query("UPDATE {quiz_result} SET time_end = NOW() WHERE rid = %d", $_SESSION['rid']);
+        
+        //display results and remove session variables
         $node->body = "Your score: " .quiz_calculate_score($_SESSION['rid']);
         unset($_SESSION['quiz_questions']);
         unset($_SESSION['rid']);
@@ -356,7 +418,7 @@
 function quiz_start_actions($uid, $nid) {
   // Validate number of takes
   if ($quiz->takes != 0) {
-    $result = db_result(db_query('SELECT COUNT(rid) AS count FROM {quiz_results} WHERE uid = %d && quiz_nid = %d', $uid, $nid));
+    $result = db_result(db_query('SELECT COUNT(rid) AS count FROM {quiz_result} WHERE uid = %d AND quiz_nid = %d', $uid, $nid));
     $quiz = node_load($nid);
     if ($result >= $quiz->takes) {
       drupal_set_message(t('Sorry, you have already taken this quiz %d times.', $result->count), 'error');
@@ -365,11 +427,12 @@
   }
 
   // Insert quiz_results record
-  $rid = db_next_id('{quiz_results}_rid');
-  $result = db_query('INSERT INTO {quiz_results} (rid, uid, quiz_nid, time_start) VALUES (%d, %d, %d, %d)', $rid, $uid, $nid, time());
+  $rid = db_next_id('quiz_results_rid');
+  $result = db_query("INSERT INTO {quiz_result} (rid, quiz_nid, uid, time_start) VALUES (%d, %d, %d, NOW())", $rid, $nid, $uid);
   if ($result) {
     return $rid;
-  } else {
+  }
+  else {
     return FALSE;
   }
 }
@@ -384,10 +447,22 @@
  */
 function quiz_calculate_score($rid) {
   $score = 0;
-  $result = unserialize(db_result(db_query('SELECT results FROM {quiz_results} WHERE rid = %d', $rid)));
-  foreach ($result as $question) {
-    $score += $question['score'];
+  $result = db_query("SELECT
+                       qqr.answer answer,
+                       qqr.question_nid qnid,
+                       n.type type
+                     FROM {quiz_question_results} qqr, {node} n
+                     WHERE qqr.result_rid = %d AND n.nid = qqr.question_nid", $rid);
+  
+  
+  while($r = db_fetch_array($result)) {
+    $r['answer'] = unserialize($r['answer']);
+    $s = module_invoke($r['type'], 'calculate_result', $r['answer']['answers'], $r['answer']['tried']);
+    
+    $score += $s;
+    $r['score'] = $s;
   }
+
   return $score;
 }
 
@@ -404,14 +479,14 @@
   $quiz = node_load($nid);
 
   // Get required questions first
-  $result = db_query('SELECT question_nid FROM {quiz_question} WHERE quiz_nid = %d AND question_status = %d', $nid, 1);
+  $result = db_query('SELECT question_nid FROM {quiz_questions} WHERE quiz_nid = %d AND question_status = %d', $nid, QUESTION_ALWAYS);
   while ($question_node = db_fetch_object($result)) {
     $questions[] = $question_node->question_nid;
   }
 
   // Get random questions for the remainder
   $quiz->number_of_questions -= count($questions);
-  $result = db_query_range('SELECT question_nid FROM {quiz_question} WHERE quiz_nid = %d AND question_status = %d ORDER BY RAND()', $nid, 0, 0, $quiz->number_of_questions);
+  $result = db_query_range('SELECT question_nid FROM {quiz_questions} WHERE quiz_nid = %d AND question_status = %d ORDER BY RAND()', $nid, QUESTION_RANDOM, 0, $quiz->number_of_questions);
   while ($question_node = db_fetch_object($result)) {
     $questions[] = $question_node->question_nid;
   }
@@ -586,7 +661,7 @@
     // Retrieve list of questions
     $result = db_query("
     SELECT n.nid, n.type, nr.body, nr.format, q.question_status
-    FROM {node} n, {node_revisions} nr, {quiz_question} q
+    FROM {node} n, {node_revisions} nr, {quiz_questions} q
     WHERE n.nid = q.question_nid
     AND n.nid = nr.nid
     AND q.quiz_nid = %d", $quiz->nid);
@@ -612,7 +687,7 @@
 function quiz_print_question_table($questions) {
   $output = '';
   $status_descriptions = array(t('Random'), t('Always'), t('Never'));
-  foreach ($questions as $question) {
+  while(list($key, $question) = each($questions)) {
     $rows[] = array(
       $status_descriptions[$question->status],
       $question->question,
@@ -622,7 +697,8 @@
 
   if (isset($rows)) {
     $output .= theme('table', $header, $rows);
-  } else {
+  }
+  else {
     $output .= 'No questions found.';
   }
   return $output;
@@ -639,6 +715,27 @@
  *  HTML output to create page
  */
 function quiz_questions() {
+  if ($_POST) {
+    if ($_POST['op'] == 'Filter question list') {
+      $_SESSION['quiz_filter'] = $_POST['edit'][1];
+    }
+    else if ($_POST['op'] =='Submit questions') {
+      if (quiz_update_questions($_POST['edit']['question_status'])) {
+        drupal_set_message("Question sucessfully updated");
+      }
+      else {
+        drupal_set_message("There was a problem updating the questions...", 'error');
+      }
+    }
+  }
+  
+  if (isset($_SESSION['quiz_filter'])) {
+    $terms = $_SESSION['quiz_filter'];
+  }
+  else {
+    $terms = array();
+  }
+  
   $quiz = node_load(arg(1));
 
   // Set page title
@@ -650,7 +747,9 @@
       '#type' => 'fieldset',
       '#title' => t('Select one or more terms to filter the question list'),
     );
-    $form['taxonomy_filter']['taxonomy'] = _quiz_taxonomy_select($_POST['edit']['taxonomy']);
+    $form['taxonomy_filter']['taxonomy']['#tree'] = TRUE;
+
+    $form['taxonomy_filter']['taxonomy'] = _quiz_taxonomy_select($terms);
     $form['taxonomy_filter']['submit'] = array(
       '#type' => 'submit',
       '#value' => t('Filter question list'),
@@ -658,18 +757,16 @@
   }
 
   // Grab current question status, if any
-  $questions = quiz_filter_question_list($_POST['edit']['taxonomy']);
+  $questions = quiz_filter_question_list($terms);
 
-  $result = db_query('SELECT question_nid, question_status FROM {quiz_question} WHERE quiz_nid = %d', $quiz->nid);
+  $result = db_query('SELECT question_nid, question_status FROM {quiz_questions} WHERE quiz_nid = %d', $quiz->nid);
   while ($assigned_question = db_fetch_object($result)) {
     if (array_key_exists($assigned_question->question_nid, $questions)) {
       $the_question =& $questions[$assigned_question->question_nid];
       $the_question->status = $assigned_question->question_status;
     }
   }
-    print_r($questions);
-
-  // here I have questions 1, 2, and 4
+  
 
   // Display filtered question list
   $form['filtered_question_list'] = array(
@@ -680,12 +777,10 @@
 
   $form['filtered_question_list']['question_status']['#tree'] = TRUE;
 
-  foreach ($questions as $question) {
-  print_r($questions);
-  echo $question->nid . " ";
+  while(list($key, $question) = each($questions)) {
     $form['filtered_question_list']['question_status'][$question->nid] = array(
       '#type' => 'radios',
-      '#options' => array(0 => '', 1 => '', 2 => ''),
+      '#options' => array(QUESTION_RANDOM => '', QUESTION_ALWAYS => '', QUESTION_NEVER => ''),
       '#default_value' => $question->status,
     );
     $form['filtered_question_list']['question'][$question->nid] = array(
@@ -736,7 +831,8 @@
 function theme_filtered_questions($form) {
   $header = array(t('Random'), t('Always'), t('Never'), t('Question'), t('Type'));
   $rows = array();
-  foreach ($form['question_status'] as $nid => $values) {
+
+  while(list($nid, $values) = each($form['question_status'])) {
     if (is_numeric($nid)) {
       $rows[] = array(
         form_render($form['question_status'][$nid][0]),
@@ -749,7 +845,8 @@
   }
   if (isset($rows)) {
     $output .= theme('table', $header, $rows);
-  } else {
+  }
+  else {
     $output .= t('No questions found.');
   }
   return $output;
@@ -769,7 +866,7 @@
 
   // Determine how many more questions are required
   $qnum = $quiz->number_of_questions;
-  $anum = db_num_rows(db_query('SELECT question_nid FROM {quiz_question} WHERE quiz_nid = %d', $quiz->nid));
+  $anum = db_num_rows(db_query('SELECT question_nid FROM {quiz_questions} WHERE quiz_nid = %d', $quiz->nid));
   if ($anum < $qnum) {
     $diff = $qnum - $anum;
     drupal_set_message(t('Note that this quiz is set to show %qnum questions, while there are currently only %anum questions assigned. Please assign at least %diff more questions.', array('%qnum' => $qnum, '%anum' => $anum, '%diff' => $diff)), 'status');
@@ -790,17 +887,16 @@
   $sql = '
     SELECT DISTINCT n.nid, n.type, r.body, r.format
     FROM {node} n, {node_revisions} r ';
-  if ($terms != NULL) {
+  if (count($terms) > 0) {
     $sql .= ', {term_node} t ';
   }
-  $sql .= "WHERE n.nid = r.nid
-    AND n.type IN ('". implode("','", _quiz_get_question_types()) ."') ";
-  if ($terms != NULL) {
-    $sql .= 'AND t.tid IN ('. implode(',', $terms) .')
-      AND t.nid = n.nid';
+  $sql .= "WHERE n.nid = r.nid AND n.type IN ('". implode("','", _quiz_get_question_types()) ."') ";
+  if (count($terms) > 0) {
+    $sql .= 'AND t.tid IN ('. implode(',', $terms) .') AND t.nid = n.nid';
   }
+  
   $result = db_query($sql);
-
+  
   // Create questions array
   $questions = array();
   while ($node = db_fetch_object($result)) {
@@ -841,28 +937,29 @@
 
   if (!empty($questions)) {
     // Get currently assigned questions
-    $result = db_query('SELECT quiz_nid, question_nid, question_status FROM {quiz_question} WHERE quiz_nid = %d', $quiz->nid);
+    $result = db_query('SELECT quiz_nid, question_nid, question_status FROM {quiz_questions} WHERE quiz_nid = %d', $quiz->nid);
     $assigned_questions = array();
     while ($assigned_question = db_fetch_object($result)) {
       $assigned_questions[$assigned_question->question_nid] = $assigned_question->question_status;
     }
 
     // Perform update if question is already assigned, or insert if it's a new question
-    foreach ($questions as $key => $value) {
-      if ($value == 2) {
-        $result = db_query('DELETE FROM {quiz_question} WHERE quiz_nid = %d AND question_nid = %d', $quiz->nid, $key);
+    while(list($key, $value) = each($questions)) {
+      if ($value == QUESTION_NEVER) {
+        $result = db_query('DELETE FROM {quiz_questions} WHERE quiz_nid = %d AND question_nid = %d', $quiz->nid, $key);
         if (!$result) {
           $return = false;
         }
       }
       else {
         if (array_key_exists($key, $assigned_questions)) {
-          $result = db_query('UPDATE {quiz_question} SET question_status = %d WHERE question_nid = %d', $value, $key);
+          $result = db_query('UPDATE {quiz_questions} SET question_status = %d WHERE quiz_nid = %d AND question_nid = %d', $value, $quiz->nid, $key);
           if (!$result) {
             $return = false;
           }
-        } else {
-          $result = db_query('INSERT INTO {quiz_question} (quiz_nid, question_nid, question_status) VALUES (%d, %d, %d)', $quiz->nid, $key, $value);
+        }
+        else {
+          $result = db_query('INSERT INTO {quiz_questions} (quiz_nid, question_nid, question_status) VALUES (%d, %d, %d)', $quiz->nid, $key, $value);
           if (!$result) {
             $return = false;
           }
@@ -870,7 +967,8 @@
       }
     }
 
-  } else {
+  }
+  else {
     $return = false;
   }
 
@@ -878,4 +976,165 @@
 
 }
 
+
+/*
+ * Quiz Admin
+ */
+function quiz_admin() {
+  $results = _quiz_get_results();
+  
+  $form['quizresults'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Quiz Results'));
+  $form['quizresults']['table'] = array(
+    '#type' => 'markup',
+    '#value' => quiz_admin_get_results_table($results));
+  
+  return system_settings_form('quiz_admin_settings', $form);
+}
+
+
+/*
+ * Results Table
+ */
+function quiz_admin_get_results_table($results) {
+  $output = '';
+  $rows = array();
+
+  while(list($key, $result) = each($results)) {
+    $rows[] = array(
+      l("view", "admin/quiz/".$result['rid']."/view") . " | " . l("delete", "admin/quiz/".$result['rid']."/delete"),
+      $result['title'],
+      $result['name'],
+      $result['rid'],
+      $result['time_start'],
+      $result['finished'],);
+  }
+  
+  $header = array(
+    t('Action'),
+    t('Quiz Title'),
+    t('Username'),
+    t('Result<br/>ID'),
+    t('Time Started'),
+    t('Finished?'));
+  
+  if (isset($rows)) {
+    $output .= theme('table', $header, $rows);
+  }
+  else {
+    $output .= 'No questions found.';
+  }
+  return $output;
+}
+
+/*
+ * Get a full results list
+ */
+function _quiz_get_results() {
+  $results = array();
+  $dbresult = db_query("SELECT
+                          n.nid nid,
+                          n.title title,
+                          u.name name,
+                          qr.rid rid,
+                          qr.time_start time_start,
+                          if (qr.time_end, qr.time_end, 'In Progress&hellip;') finished
+                        FROM {node} n, {quiz} q, {quiz_result} qr, {users} u
+                        WHERE
+                          n.type = 'quiz'
+                            AND
+                          n.nid = q.nid
+                            AND
+                          qr.quiz_nid = q.nid
+                            AND
+                          u.uid = qr.uid
+                        ORDER BY qr.rid ASC");
+  //Create results array
+  while($line = db_fetch_array($dbresult)) {
+    $results[$line['rid']] = $line;
+  }
+  return $results;
+}
+
+
+
+/*
+ * Quiz Results Admin
+ */
+function quiz_admin_results() {
+  $questions = _quiz_get_answers(arg(2));
+  
+  $output = '';
+  $rows = array();
+
+  while(list($key, $question) = each($questions)) {
+    $question['qanswer'] = unserialize($question['qanswer']);
+    $score = module_invoke($question['type'], 'calculate_result', $question['qanswer']['answers'], $question['qanswer']['tried']);
+    
+    #dprint_r($question);
+    $rows[] = array(
+      $question['question'],
+      $score,
+      );
+  }
+  
+  $header = array(t('Question'), t('Right or Wrong?'),);
+  
+  if (isset($rows)) {
+    $output .= theme('table', $header, $rows);
+  }
+  else {
+    $output .= 'No answers found in this result!.';
+  }
+  return $output;
+}
+
+/*
+ * Delete Result
+ */
+function quiz_admin_result_delete() {
+  $form['del_rid'] = array('#type' => 'hidden', '#value' => arg(2));
+  return confirm_form('quiz_admin_result_delete',
+                      $form,
+                      t('Are you sure you want to delete this quiz result?'),
+                      'admin/quiz',
+                      t('This action cannot be undone.'),
+                      t('Delete'),
+                      t('Cancel'));
+}
+function quiz_admin_result_delete_submit($form_id, $form_values) {
+
+  db_query("DELETE FROM {quiz_result} WHERE rid = %d", $form_values['del_rid']);
+  db_query("DELETE FROM {quiz_question_results} WHERE result_rid = %d", $form_values['del_rid']);
+  drupal_set_message(t('Deleted result.'));
+
+  return "admin/quiz";
+}
+
+
+function _quiz_get_answers($rid) {
+  $results = array();
+  $dbresult = db_query("SELECT
+                          qqr.question_nid qnid,
+                          qqr.answer qanswer,
+                          nr.body question,
+                          n.type type
+                        FROM quiz_question_results qqr, quiz_question qq, node n, node_revisions nr
+                        WHERE
+                          qqr.result_rid = %d
+                            AND
+                          qqr.question_nid = qq.nid
+                            AND
+                          n.nid = qq.nid
+                            AND
+                          nr.nid = n.nid", $rid);
+  //Create results array
+  while($line = db_fetch_array($dbresult)) {
+    $results[$line['qnid']] = $line;
+  }
+  return $results;
+}
+
+
 // edit: view: taken, feedback, insert into quiz_results, view quiz_results
\ No newline at end of file
Index: quiz.install
===================================================================
RCS file: quiz.install
diff -N quiz.install
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ quiz.install	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,81 @@
+<?php
+
+function quiz_install() {
+  switch ($GLOBALS['db_type']) {
+    case 'mysql':
+    case 'mysqli':
+      /*
+       *      INSTALL TABLES FOR MySQL and MySQLi
+       */
+       
+       
+      /* Create the quiz table */
+      db_query("CREATE TABLE quiz (
+                  nid INTEGER UNSIGNED NOT NULL,
+                  number_of_questions TINYINT UNSIGNED NOT NULL,
+                  shuffle TINYINT UNSIGNED NOT NULL,
+                  backwards_navigation TINYINT UNSIGNED NOT NULL,
+                  feedback_time TINYINT UNSIGNED NOT NULL,
+                  open DATETIME NOT NULL,
+                  close DATETIME NOT NULL,
+                  takes TINYINT UNSIGNED NOT NULL,
+                  time_limit INTEGER UNSIGNED DEFAULT 0 NOT NULL,
+                  PRIMARY KEY(nid)
+                ) TYPE=MyISAM
+                /*!40100 DEFAULT CHARACTER SET utf8 */;");
+   
+      /* Create the questions table */
+      db_query("CREATE TABLE quiz_questions (
+                  quiz_nid INTEGER UNSIGNED NOT NULL,
+                  question_nid INTEGER UNSIGNED NOT NULL,
+                  question_status TINYINT UNSIGNED NOT NULL,
+                  PRIMARY KEY (quiz_nid, question_nid)
+                ) TYPE=MyISAM
+                /*!40100 DEFAULT CHARACTER SET utf8 */;");
+      
+      /* Create the question table */
+      db_query("CREATE TABLE quiz_question (
+                  nid INTEGER UNSIGNED NOT NULL,
+                  properties VARCHAR(255) NOT NULL,
+                  PRIMARY KEY (nid)
+                ) TYPE=MyISAM
+                /*!40100 DEFAULT CHARACTER SET utf8 */;");
+      
+      /* Create the question answer table */
+      db_query("CREATE TABLE quiz_question_answer (
+                  aid INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
+                  question_nid INTEGER UNSIGNED NOT NULL,
+                  answer TEXT NOT NULL,
+                  feedback TEXT NULL,
+                  points TINYINT NOT NULL,
+                  PRIMARY KEY (aid),
+                  INDEX (question_nid)
+                ) TYPE=MyISAM
+                /*!40100 DEFAULT CHARACTER SET utf8 */;");
+      
+      /* Create the results table */
+      db_query("CREATE TABLE quiz_result (
+                  rid INTEGER UNSIGNED NOT NULL,
+                  quiz_nid INTEGER UNSIGNED NOT NULL,
+                  uid INTEGER UNSIGNED NOT NULL,
+                  time_start DATETIME NOT NULL,
+                  time_end DATETIME NOT NULL,
+                  released DATETIME NOT NULL,
+                  score TINYINT NOT NULL,
+                  PRIMARY KEY (rid),
+                  INDEX (quiz_nid)
+                ) TYPE=MyISAM
+                /*!40100 DEFAULT CHARACTER SET utf8 */;");
+      
+      /* Create the question results table */
+      db_query("CREATE TABLE quiz_question_results (
+                  result_rid INTEGER UNSIGNED NOT NULL,
+                  question_nid INTEGER UNSIGNED NOT NULL,
+                  answer TEXT NOT NULL,
+                  PRIMARY KEY (result_rid, question_nid)
+                ) TYPE=MyISAM
+                /*!40100 DEFAULT CHARACTER SET utf8 */;");
+      
+      break;
+  }
+}
