Common subdirectories: cvs20061101/images and dev/images
diff -up cvs20061101/multichoice.module dev/multichoice.module
--- cvs20061101/multichoice.module	2006-11-14 03:06:51.000000000 +0100
+++ dev/multichoice.module	2006-11-15 01:40:24.000000000 +0100
@@ -36,7 +36,10 @@ function multichoice_access($op, $node) 
  * Implementation of hook_node_info().
  */
 function multichoice_node_info() {
-  return array('multichoice' => array('name' => t('multichoice'), 'base' => 'multichoice'));
+/// prefix quiz parts (multichoice, matching, fillblank, etc.) with 'quiz -'
+/// so we remember that those node type are not independant, but  part of a quiz.
+/// in another hand, they are grouped, what ease content addition in the jungle..
+  return array('multichoice' => array('name' => t('quiz - Multiple choice'), 'base' => 'multichoice'));
 }
 
 /**
@@ -74,10 +77,11 @@ function multichoice_form(&$node) {
   );
   $form['body_filter']['format'] = filter_form($node->format);
 
-  $form['multiple_answers'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Multiple answers'),
-    '#default_value' => $node->multiple_answers,
+  $form['properties'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Settings'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
   );
 
   // Determine number of answer rows to display
@@ -87,6 +91,49 @@ function multichoice_form(&$node) {
   if ($_POST['edit']['more']) {
     $node->rows += 5;
   }
+
+  $form['properties']['multiple_answers'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Multiple answers'),
+    '#default_value' => (isset($node->multiple_answers) ? $node->multiple_answers : 0),
+  )
+
+  // prevent from automatic click without thinking about the answer:
+/// this may happen when you take the same quiz too often but do not
+/// understand the feedback (then just click the position known to be the right
+/// one, but don't know why)... sure one can simply memorise the full answer,
+/// but it may be harder, especialy when the answers look similar...
+  $form['properties']['shuffled_answers'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('shuffle answers order'),
+    '#default_value' => (isset($node->shuffled_answers) ? $node->shuffled_answers : 0),
+    '#description' => t('Check that box if the answers should be displayed in a random order.'),
+  );
+
+  // ovewrite global setting: use of inline drop-down list when answers are short (often one word)
+/// question: choose the right tense for this sentense
+///           when he !l! in, we were still playing
+/// answers: 1) came
+///          2) comes
+///          3) will come
+/// a selection list is shown here at the location marked  !l! ;)
+  $form['properties']['printable_answers'] = array(
+    '#type' => 'select',
+    '#title' => t('display'),
+    '#default_value' => (isset($node->printable_answers) ? $node->printable_answers : check_plain(variable_get('quiz_default_answer_displaying', 1))),
+    '#options' => array(0 => t('web-only/compact'), 1 => t('printable/full'), ),
+    '#description' => t('Do you want all the answers visible at the same time?'),
+  );
+
+  // ovewrite global setting: use of automatic/basic scoring or advanced/personalized one
+  $form['properties']['extend_scoring'] = array(
+    '#type' => 'select',
+    '#title' => t('scoring'),
+    '#default_value' => (isset($node->extend_scoring) ? $node->extend_scoring : check_plain(variable_get('quiz_default_answer_scoring', 0))),
+    '#options' => array(0 => t('automatic {0}||{1}'), 1 => t('personalized {-3, -2, -1, 0}||{1, 2, 3, 4, 5}'), ),
+    '#description' => t('Please fill the answers and choose the correct ones, then Preview if you change the score handlling.'),
+  );
+
   $answers = $node->answers;
 
   // Display answer rows
@@ -97,11 +144,27 @@ function multichoice_form(&$node) {
     '#theme' => 'multichoice_form'
   );
 
+  $points_list = array();
+  for ($i = -3; $i < 6; $i++) {
+    $points_list[$i] = $i;
+  }
   for ($i = 0; $i < $node->rows; $i++) {
-    $form['answers'][$i]['correct'] = array(
-      '#type' => 'checkbox',
-      '#default_value' => $answers[$i]['points'],
-    );
+    if($node->extend_scoring) {
+/// each answer can have different point(s), not only 0 || 1.
+/// read <http://drupal.org/node/15934> clairem's example on CMS (http://drupal.org/comment/reply/15934/26603)
+      $form['answers'][$i]['correct'] = array(
+        '#type' => 'select',
+        '#default_value' => $answers[$i]['points'],
+        '#options' => $points_list,
+        '#description' => t('>0 if correct.'), // explain/remember how it works.
+      );
+    }
+    else {
+      $form['answers'][$i]['correct'] = array(
+        '#type' => 'checkbox',
+        '#default_value' => $answers[$i]['points'],
+      );
+    }
     $form['answers'][$i]['answer'] = array(
       '#type' => 'textarea',
       '#default_value' => $answers[$i]['answer'],
@@ -127,6 +190,7 @@ function multichoice_form(&$node) {
   $form['more'] = array(
     '#type' => 'checkbox',
     '#title' => t('I need more answers'),
+    '#description' => t("If the amount of boxes above isn't enough, check this box and click the Preview button below to add some more.") .t(' Let it checked after previewing..'),
   );
 
   return $form;
@@ -141,7 +205,9 @@ function multichoice_validate(&$node) {
   $node->teaser = 0;
   $node->promote = 0;
 
-  if (!$node->nid && empty($_POST)) return;
+  if (!$node->nid && empty($_POST)) {
+    return;
+  }
 
   // Validate body
   if (!$node->body) {
@@ -153,8 +219,7 @@ function multichoice_validate(&$node) {
   $corrects = 0;
 
   while(list($key, $answer) = each($node->answers)) {
-
-    if ($answer['correct']) {
+    if ($answer['correct'] > 0) {
       if ($corrects && !$node->multiple_answers) {
         form_set_error('multiple_answers', t('Single choice yet multiple correct answers are present'));
       }
@@ -180,7 +245,7 @@ function multichoice_validate(&$node) {
  * Implementation of hook_insert().
  */
 function multichoice_insert(&$node) {
-  db_query("INSERT INTO {quiz_question} (nid, properties) VALUES(%d, '%s')", $node->nid, serialize(array('multiple_answers' => $node->multiple_answers)));
+  db_query("INSERT INTO {quiz_question} (nid, properties) VALUES(%d, '%s')", $node->nid, serialize(array('multiple_answers' => $node->multiple_answers, 'shuffled_answers' => $node->shuffled_answers, 'printable_answers' => $node->printable_answers, 'extend_scoring' => $node->extend_scoring, )));
 
   while(list($key, $value) = each($node->answers)) {
     if (trim($value['answer']) != "")
@@ -193,11 +258,10 @@ function multichoice_insert(&$node) {
  * Implementation of hook_update().
  */
 function multichoice_update($node) {
-  db_query("UPDATE {quiz_question} SET properties = '%s' WHERE nid = %d", serialize(array('multiple_answers' => $node->multiple_answers)), $node->nid);
+  db_query("UPDATE {quiz_question} SET properties = '%s' WHERE nid = %d", serialize(array('multiple_answers' => $node->multiple_answers, 'shuffled_answers' => $node->shuffled_answers, 'printable_answers' => $node->printable_answers, 'extend_scoring' => $node->extend_scoring, )), $node->nid);
 
   while(list($key, $value) = each($node->answers)) {
     if ($value['aid']) {
-      $value['answer'] = trim($value['answer']);
       if (!empty($value['delete']) || trim($value['answer']) == '') {
         //Delete this entry
         db_query("DELETE FROM {quiz_question_answer} WHERE aid = %d", $value['aid']);
@@ -239,6 +303,9 @@ function multichoice_load($node) {
 
   $additions->properties = unserialize($additions->properties);
   $additions->multiple_answers = $additions->properties['multiple_answers'];
+  $additions->shuffled_answers = $additions->properties['shuffled_answers'];
+  $additions->printable_answers = $additions->properties['printable_answers'];
+  $additions->extend_scoring = $additions->properties['extend_scoring'];
 
   return $additions;
 }
@@ -283,37 +350,92 @@ function multichoice_help($section) {
  *   HTML output
  */
 function multichoice_render_question($node) {
-  // Radio buttons for single selection questions, checkboxes for multiselect
-  if ($node->multiple_answers == 0) {
-    $type = 'radios';
+  $question = check_markup($node->body, $node->format, FALSE);
+  $ok = eregi("![a-z]!", $question, $tag);
+  $prefix = '';
+  /// full interface : should always be the default.
+  if ($node->printable_answers != 0) {
+
+    // Radio buttons for single selection questions, checkboxes for multiselect
+    if (!$node->multiple_answers) {
+      $type = 'radios';
+    }
+    else {
+      $type = 'checkboxes';
+    }
+
+    if ($ok) {
+      $prefix .= str_replace($tag[0], '', $question); // remove useless tag
+    }
+    else {
+      $prefix .= $question;
+    }
+    $prefix .= '<div class="multichoice_answer_text">';
+    $suffix = '</div>'; // class="multichoice_answer_text"
+
+  }
+  /// compact interface : here having a default answer ("can't answer") is
+  /// usefull :) but this interface can be used only if choices are one-line...
+  else {
+
+    // simple select for single selection questions, select multiple for multiselect
+    $type = 'select';
+
+    if ($ok) {
+      $prefix .= substr($question, 0, strpos($question, $tag[0], 0)); // head..
+    }
+    else {
+      $prefix .= $question;
+    }
+    $prefix .= '<span class="multichoice_answer_text">';
+    $suffix = '</span>'; // class="multichoice_answer_text"
+    if ($ok) {
+      $suffix .= substr($question, strpos($question, $tag[0], 0) + 3); // tail..
+    }
+
+  }
+
+  if ($node->multiple_answers) {
+    //$desc = t('Please choose the correct answers from 1 to %maxCount', array('%maxCount' => $node->multiple_answers)); // precious hint
+    $desc = t('Please choose from 1 to %maxCount correct answer(s)', array('%maxCount' => count($node->answers)));
   }
   else {
-    $type = 'checkboxes';
+    $desc = t('Please choose the right aswer');
   }
 
+//// do it here ?
+  if ($node->shuffled_answers) {
+    shuffle($node->answers);
+  }
   // Get options
   $options = array();
-
+  if ($node->shuffled_answers) {
+    shuffle($node->answers);
+  }
   while(list($key, $answer) = each($node->answers)) {
     if (empty($answer['correct']) && empty($answer['answer']) && empty($answer['feedback'])) {
       unset($node->answers[$key]);
     }
     else {
-      $options[$key] = check_markup($answer['answer'], $node->filter, FALSE);
-      $options[$key] = '<div class="multichoice_answer_text">'. check_markup($answer['answer'], $node->filter, FALSE) .'</div>';
+      $options[$key] = (($node->printable_answers) ? check_markup($answer['answer'], $node->filter, FALSE) : check_plain($answer['answer']));
     }
   }
-
-  $form['start'] = array('#type' => 'markup', '#value' => '<div class="multichoice_form">');
-  $form['question'] = array('#type' => 'markup', '#value' => check_markup($node->body, $node->format, FALSE));
+//// or do it here ?
+//  if ($node->shuffled_answers) {
+//    shuffle($options);
+//  }
 
   // Create form
   $form['tries'] = array(
     '#type' => $type,
+    '#prefix' => $prefix,
     '#options' => array_merge(
       array( -1 => t(check_plain(variable_get('quiz_question_unanswer_text', 'skip this question'))), ),
       $options),
     '#default_value' => -1,
+    '#multiple' => $node->multiple_answers,
+    '#suffix' => $suffix,
+    '#description' => $desc,
   );
   $form['submit'] = array(
     '#type' => 'submit',
@@ -332,8 +454,9 @@ function multichoice_render_question($no
  * @return
  *   Array of results, in the form of:
  *   array(
- *     'answers' => array of correct answer(s)
+ *     'answers' => array of correct answer(s) & feedback(s) & point(s)
  *     'tried' => array of selected answer(s)
+ *     'properties' => array of question propertie(s)
  *   );
  */
 function multichoice_evaluate_question($nid) {
@@ -341,18 +464,21 @@ function multichoice_evaluate_question($
   $results = array();
 
   if (isset($_POST['edit']['tries'])) {
+    $results['answers'] = $question->answers;
     if (is_array($_POST['edit']['tries'])) {
       // Multi-answer question
       while(list($key, $try) = each($_POST['edit']['tries'])) {
-        $results['answers'] = $question->answers;
         $results['tried'][] = $question->answers[$try]['aid'];
       }
     }
     else {
       // Single-answer question
-      $results['answers'] = $question->answers;
       $results['tried'][] = $question->answers[$_POST['edit']['tries']]['aid'];
     }
+/// like answers, some properties may change when a node is edited.
+/// we need to keep and restore the exact context of the quiz.
+/// and last but not least, some of those properties are needed when computing the score...
+    $results['properties'] = $question->properties;
   }
   //Unset $_POST, otherwise it tries to use the previous answers on the next page...
   unset($_POST['edit']['tries']);
@@ -362,9 +488,22 @@ function multichoice_evaluate_question($
 }
 
 //Old claculate result function
-function multichoice_calculate_result($answers, $tried) {
+function multichoice_calculate_result($answers, $tried, $properties) {  
+  $results = multichoice_calculate_results($answers, $tried, $properties);
+  return $results['succes']; 
+/// we should use only the new now :)
+/// so, it's better to change these lines in quiz.module
+///    $s = module_invoke($r['type'], 'calculate_result', $r['answer']['answers'], $r['answer']['tried']);
+///    $num_correct += $s;
+/// by the following ones:
+///    $s = module_invoke($r['type'], 'calculate_results', $r['answer']['answers'], $r['answer']['tried'], $r['answer']['properties']);
+///    $num_correct += $s['succes'];
+
+/// The following (old code slightly modified) works... 
+/// exept when there are many True answers for single-answer question type.
+/*
   while(list($key, $answer) = each($answers)) {
-    if ($answer['points'] == 1) {
+    if ($answer['points'] > 0) {
       if (($key = array_search($answer['aid'], $tried)) !== FALSE) {
         //Correct answer was selected, so lets take that out the tried list
         unset($tried[$key]);
@@ -383,20 +522,24 @@ function multichoice_calculate_result($a
 
   //Finally, we can consider this correct if its passed the above tests!
   return 1;
+*/
+
 }
 
 //New singing and dancing one
-function multichoice_calculate_results($answers, $tried, $showPoints = FALSE, $showFeedback = FALSE) {
-  //Create results table
+/// call changed ! there's an additional $properties parameter...
+function multichoice_calculate_results($answers, $tried, $properties, $showPoints = FALSE, $showFeedback = FALSE) {
+  $winPoints = 0;
+  $maxPoints = 0;
+
+  //Create results table while computing the score
   $rows = array();
   $correctAnswers = array();
-
   while(list($key, $answer) = each($answers)) {
     $cols = array();
-
     $cols[] = $answer['answer'];
     if ($showPoints) {
-      $cols[] = (($answer['points'] == 0) ? theme_multichoice_unselected() : theme_multichoice_selected());
+      $cols[] = (($answer['points'] <= 0) ? theme_multichoice_unselected() : theme_multichoice_selected());
     }
     $isSelected = (array_search($answer['aid'], $tried) !== FALSE);
     $cols[] = ($isSelected ? theme_multichoice_selected() : theme_multichoice_unselected());
@@ -405,20 +548,31 @@ function multichoice_calculate_results($
     }
 
     $rows[] = $cols;
-  	
-  	if ($answer['points'] > 0) {
-  	  $correctAnswers[] = $answer['aid'];
-  	}
+
+  //Scoring.. begin
+    if($answer['points'] > 0) {
+      $correctAnswers[] = $answer['aid'];
+      if ($properties['multiple_answers']) {
+        $maxPoints += $answer['points'];
+      }
+      else {
+        $maxPoints = max($maxPoints, $answer['points']);
+      }
+    }
+    $winPoints += $answer['points'];
+
   }
 
-  if ($correctAnswers === $tried) {
-    $score = 1;
+  //Scoring.. end
+  if ($properties['multiple_answers']) {
+    //$succes = ($maxPoints == $winPoints) ? 1 : 0 ;
+    $succes = ($correctAnswers === $tried) ? 1 : 0;
   }
   else {
-    $score = 0;
+    $succes = (int)($winPoints > 0);
   }
 
-  return array('score' => $score, 'resultstable' => $rows);
+  return array('succes' => $succes, 'resultstable' => $rows, 'maxPoints' => $maxPoints, 'winPoints' => $winPoints, ); //future way
 }
 
 /**
@@ -455,10 +609,11 @@ function theme_multichoice_form($form) {
 
   // Format table header
   $header = array(
-    array('data' => t('Correct')),
-    array('data' => t('Answer'), 'style' => 'width:250px;'),
-    array('data' => t('Feedback'), 'style' => 'width:250px;'),
-    array('data' => t('Delete')),
+    array('data' => (($node->extend_scoring) ? t('Score') : t('Correct')), ),
+    array('data' => t('Answer'), 'style' => 'width:45%;', ),
+/// sorry 205px breaks my layout
+    array('data' => t('Feedback'), 'style' => 'width:45%;', ),
+    array('data' => t('Delete'), ),
   );
 
   // Format table rows
@@ -498,7 +653,8 @@ function theme_multichoice_unselected(){
  * Theme function for the multichoice form
  */
 function theme_multichoice_render_question($form){
-  $output = '';
+  $output = '<div class="multichoice_form">'; // was $form['start']
   $output .= form_render($form);
+  $output .= '</div>'; // class="multichoice_form"
   return $output;
 }
diff -up cvs20061101/quiz.module dev/quiz.module
--- cvs20061101/quiz.module	2006-11-13 03:28:38.000000000 +0100
+++ dev/quiz.module	2006-11-15 01:31:24.000000000 +0100
@@ -10,18 +10,13 @@ include(drupal_get_path('module', 'quiz'
  * 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().
  */
@@ -48,7 +43,7 @@ function quiz_access($op, $node) {
       return TRUE;
     }
   }
-  
+
   if (user_access('administer quizzes')) {
     return TRUE;
   }
@@ -119,14 +114,14 @@ function quiz_menu($may_cache) {
         'callback' => 'quiz_user_results',
         'access' => user_access('user results'),
         'type' => MENU_CALLBACK);
-        
+
       $items[] = array(
         'path' => 'admin/quiz/' . arg(2) . "/view",
         'title' => t('view %quiz', array('%quiz' => $quiz_name)),
         'callback' => 'quiz_admin_results',
         'access' => user_access('administer quizzes'),
         'type' => MENU_CALLBACK);
-                       
+
       $items[] = array(
         'path' => 'admin/quiz/' . arg(2) . "/delete",
         'title' => t('delete %quiz', array('%quiz' => $quiz_name)),
@@ -145,7 +140,7 @@ function quiz_menu($may_cache) {
  */
 function quiz_form(&$node) {
   $quiz_name = check_plain(variable_get('quiz_name', 'quiz'));
-  
+
   $form['title'] = array(
     '#type' => 'textfield',
     '#title' => t('Title'),
@@ -170,7 +165,7 @@ function quiz_form(&$node) {
     '#description' => t('The number of questions to include in this %quiz from the question bank', array('%quiz' => $quiz_name)),
     '#required' => TRUE,
   );
-  
+
   $form['shuffle'] = array(
     '#type' => 'checkbox',
     '#title' => t('Shuffle questions'),
@@ -279,7 +274,7 @@ function quiz_form(&$node) {
 
 /**
  * Takes a time element and prepares to send it to form_date()
- * 
+ *
  * @param $time
  *   The time to be turned into an array. This can be:
  *   - a timestamp when from the database
@@ -298,7 +293,7 @@ function _quiz_form_prepare_date($time =
   $time_array = array();
   if(is_array($time)){
     $time_array = $time;
-  } 
+  }
   // otherwise build the array from the timestamp
   elseif(is_numeric($time)){
     $time_array = array(
@@ -320,7 +315,7 @@ function _quiz_form_prepare_date($time =
  *   Returns the number of quiz questions.
  */
 function quiz_get_number_of_questions($nid) {
-  $result = db_fetch_object(db_query('SELECT number_of_questions FROM {quiz} WHERE nid = %d', $nid));  
+  $result = db_fetch_object(db_query('SELECT number_of_questions FROM {quiz} WHERE nid = %d', $nid));
   $numberOfQuestions = $result->number_of_questions;
   return $numberOfQuestions;
 }
@@ -334,7 +329,7 @@ function quiz_get_number_of_questions($n
  */
 function quiz_get_pass_rate($nid) {
     $passrate = db_fetch_object(db_query('SELECT pass_rate FROM {quiz} WHERE nid = %d', $nid));
-    return $passrate->pass_rate;   
+    return $passrate->pass_rate;
 }
 
 /**
@@ -343,11 +338,11 @@ function quiz_get_pass_rate($nid) {
 function quiz_validate(&$node) {
   $quiz_name = check_plain(variable_get('quiz_name', 'quiz'));
   if (!$node->nid && empty($_POST)) return;
-  
+
   if (empty($node->body)) {
     form_set_error('body', t('Description is required.'));
   }
-  
+
   // validate the number of questions against the actual questions assigned to this quiz
   if ($node->number_of_questions < 1) {
     form_set_error('number_of_questions', t('Number of questions is required and must be a positive number.'));
@@ -368,7 +363,7 @@ function quiz_validate(&$node) {
     // format the valid range
     if($anum_always != $anum_total){
       $range = theme('placeholder', t('between %low and %high', array('%low' => $anum_always, '%high' => $anum_total)));
-    } 
+    }
     else {
       $range = theme('placeholder', $anum_total);
     }
@@ -387,7 +382,7 @@ function quiz_validate(&$node) {
   }
   if (!is_numeric($node->pass_rate)) {
     form_set_error('pass_rate', t('The pass rate value must be a number between 0% and 100%.'));
-  } 
+  }
   if ($node->pass_rate > 100) {
     form_set_error('pass_rate', t('The pass rate value must not be more than 100%.'));
   }
@@ -442,7 +437,7 @@ function quiz_load($node) {
 function quiz_view(&$node, $teaser = FALSE, $page = FALSE) {
   if (!$teaser && user_access('create quizzes')) {
     $node->body = theme('quiz_view', &$node, $teaser, $page);
-  } 
+  }
   elseif(!$teaser && !user_access('create quizzes')) {
     $node->body .= theme('quiz_availability', $node);
   }
@@ -450,18 +445,16 @@ function quiz_view(&$node, $teaser = FAL
 
 /**
  * Themes a message about the quiz's availability for quiz takers
- * 
- * 
  */
 function theme_quiz_availability($node){
   $output = '<div class="quiz_availability"><p>';
   if(!$node->quiz_always){
     if($node->quiz_open > time()){
-      $output .= t('This quiz will not be available until %time.', array('%time' => format_date($node->quiz_open)));      
+      $output .= t('This quiz will not be available until %time.', array('%time' => format_date($node->quiz_open)));
     }
     elseif($node->quiz_close < time()){
-      $output .= t('This quiz closes %time.', array('%time' => format_date($node->quiz_close)));      
-    } 
+      $output .= t('This quiz closes %time.', array('%time' => format_date($node->quiz_close)));
+    }
     else {
       $output .= t('This quiz is no longer available.');
     }
@@ -476,7 +469,7 @@ function theme_quiz_availability($node){
 function theme_quiz_view(&$node, $teaser = FALSE, $page = FALSE){
   $output = '';
   $quiz_name = check_plain(variable_get('quiz_name', 'quiz'));
-  
+
   // Ouput quiz options
   $output .= '<h3>'. t('%quiz Options', array('%quiz' => $quiz_name)) .'</h3>';
   $header = array(
@@ -518,7 +511,7 @@ function theme_quiz_view(&$node, $teaser
     $elapsed = floor((time() - $node->quiz_open) / 60 / 60 / 24);
     $elapsed = ($elapsed < 0)?(-$elapsed)." days to go":$elapsed;
     $output .= "<p><strong>Days since start:</strong> " . $elapsed . "</p>";
-  } 
+  }
   else {
     $output .= '<p>'. t('This Quiz is always available.') .'</p>'."\n";
   }
@@ -557,7 +550,7 @@ function theme_quiz_view(&$node, $teaser
   $output .= '<h3>'. t('%quiz Questions', array('%quiz' => $quiz_name)) .'</h3>';
   $questions = _quiz_get_questions();
   $output .= theme('quiz_question_table', $questions);
-  
+
   return $output;
 }
 
@@ -567,7 +560,7 @@ function theme_quiz_view(&$node, $teaser
  *
  * @return
  *  HTML output for page
- */ 
+ */
 function quiz_get_user_results() {
   global $user;
   $results = array();
@@ -595,7 +588,7 @@ function quiz_get_user_results() {
     $results[$line['rid']] = $line;
   }
   return theme('quiz_get_user_results', $results);
-   
+
 }
 
 /**
@@ -624,7 +617,6 @@ function quiz_take_quiz() {
           $_SESSION['quiz_'.$quiz->nid]['quiz_questions'] = $questions;
           $_SESSION['quiz_'.$quiz->nid]['rid'] = $rid;
           $_SESSION['quiz_'.$quiz->nid]['question_number'] = 0;
-
         }
         else {
           return '';
@@ -639,7 +631,7 @@ function quiz_take_quiz() {
         else {
           $former_question = node_load(array('nid' => array_shift($_SESSION['quiz_'.$quiz->nid]['quiz_questions'])));
           $result = module_invoke($former_question->type, 'evaluate_question', $former_question->nid);
-          db_query("REPLACE {quiz_question_results} VALUES(%d, %d, '%s')", $_SESSION['quiz_'.$quiz->nid]['rid'], $former_question->nid, serialize($result)); 
+          db_query("REPLACE {quiz_question_results} VALUES(%d, %d, '%s')", $_SESSION['quiz_'.$quiz->nid]['rid'], $former_question->nid, serialize($result));
         }
       }
 
@@ -670,7 +662,6 @@ function quiz_take_quiz() {
 
         // return
         return $output;
-
       }
     }
   }
@@ -678,18 +669,17 @@ function quiz_take_quiz() {
   drupal_not_found();
 }
 
-
-/***
+/**
  * Get the summary message for a completed quiz
- * 
+ *
  * Summary is determined by whether we are using the
- * pass / fail options, how the student did, and 
+ * pass / fail options, how the student did, and
  * whether this is being called from admin/quiz/[quizid]/view.
- * 
+ *
  * TODO: Need better feedback for when a student is viewing
  * their quiz results from the results list (and possibily
  * when revisiting a quiz they can't take again)
- * 
+ *
  * @param $quiz
  *   The quiz node object
  * @param $score
@@ -703,15 +693,15 @@ function _quiz_get_summary_text($quiz, $
   if (trim($quiz->summary_pass) != '' && $quiz->pass_rate > 0 && $score['percentage_score'] >= $quiz->pass_rate) {
     // If we are coming from the admin view page
     if (arg(3) == 'view') {
-      $summary = t('The student passed this quiz.');      
+      $summary = t('The student passed this quiz.');
     }
     else {
-      $summary = check_markup($quiz->summary_pass, $quiz->format);      
+      $summary = check_markup($quiz->summary_pass, $quiz->format);
     }
-  } 
+  }
   // If the student did not pass or we are not using pass / fail
   else {
-    // If we are coming from the admin view page 
+    // If we are coming from the admin view page
     // only show a summary if we are using pass / fail.
     if (arg(3) == 'view') {
       if ($node->pass_rate > 0){
@@ -754,7 +744,7 @@ function quiz_start_actions($uid, $nid) 
   // get the results
   global $user;
   $results = _quiz_get_results($quiz->nid, $user->uid);
-    
+
   // Check to see if the user alredy passed this quiz
   // but only perform this check if it is a registered user
   if($user->uid){
@@ -825,11 +815,11 @@ function quiz_calculate_score($rid) {
   while($r = db_fetch_array($result)) {
     $question_count++;
     $r['answer'] = unserialize($r['answer']);
-    $s = module_invoke($r['type'], 'calculate_result', $r['answer']['answers'], $r['answer']['tried']);
+    $s = module_invoke($r['type'], 'calculate_result', $r['answer']['answers'], $r['answer']['tried'], $r['answer']['properties']);
     $num_correct += $s;
     $r['score'] = $s; // I think this is legacy
   }
-  
+
   // calculate the percentage score
   if($question_count > 0){
     $percentage_score = round(($num_correct*100)/$question_count);
@@ -838,11 +828,11 @@ function quiz_calculate_score($rid) {
   // build the score array
   $score = array(
     'question_count' => $question_count,
-    'num_correct' => $num_correct, 
+    'num_correct' => $num_correct,
     'percentage_score' => $percentage_score,
   );
 
-  // return the array 
+  // return the array
   return $score;
 }
 
@@ -1056,7 +1046,6 @@ function _quiz_get_questions() {
   return $questions;
 }
 
-
 /**
  * Handles "add question" tab
  *
@@ -1068,14 +1057,14 @@ function _quiz_get_questions() {
  */
 function quiz_questions() {
   $quiz_name = check_plain(variable_get('quiz_name', 'quiz'));
-  
+
   if ($_POST) {
     if ($_POST['op'] == 'Filter question list') {
       //BE CAREFUL OF THE $_POST['edit'] ARRAY AS THIS VALUE IS LIABLE TO CHANGE WHEN VOCABS ARE ADDED AND DELETED...IF YOU HAVE MORE THAN ONE VOCAB, MAY GOD HELP YOU!!
       $_SESSION['quiz_filter'] = $_POST['edit'][4];
     }
   }
-    
+
   if (isset($_SESSION['quiz_filter'])) {
     $terms = $_SESSION['quiz_filter'];
   }
@@ -1087,7 +1076,7 @@ function quiz_questions() {
 
   // Set page title
   drupal_set_title(check_plain($quiz->title));
-  
+
   // show the number of questions that this quiz currently has
   $form['numberofquestions'] = array(
     '#prefix' => '<div class="quiz_questions_number">',
@@ -1095,7 +1084,6 @@ function quiz_questions() {
     '#suffix' => '</div><br />'."\n"
   );
 
-
   // Display filtering options
   if (_quiz_taxonomy_select() != array()) {
     $form['taxonomy_filter'] = array(
@@ -1121,7 +1109,6 @@ function quiz_questions() {
       $the_question->status = $assigned_question->question_status;
     }
   }
-  
 
   // Display filtered question list
   $form['filtered_question_list'] = array(
@@ -1180,12 +1167,11 @@ function quiz_questions() {
   return drupal_get_form('quiz_questions', $form);
 }
 
-
 /**
  * Submit function for quiz_questions
- * 
+ *
  * Updates from the "add questions" tab
- * 
+ *
  * @param $form_id
  *   A string containing the form id
  * @param $values
@@ -1233,7 +1219,7 @@ function quiz_questions_submit($form_id,
 
 /**
  * Gets the number questions of a given type for a quiz
- * 
+ *
  * @param $nid
  *   node id of the quiz
  * @param $type
@@ -1245,7 +1231,6 @@ function quiz_get_num_questions($nid, $t
   return db_num_rows(db_query('SELECT question_nid FROM {quiz_questions} WHERE quiz_nid = %d AND question_status = %d', $nid, $type));
 }
 
-
 /**
  * Filters question list by given terms.
  *
@@ -1269,7 +1254,7 @@ function quiz_filter_question_list($term
   }
 
   $result = db_query($sql);
-  
+
   // Create questions array
   $questions = array();
   while ($node = db_fetch_object($result)) {
@@ -1346,10 +1331,8 @@ function quiz_update_questions($question
   }
 
   return $return;
-
 }
 
-
 /**
  * Implementation of hook_settings()
  */
@@ -1368,7 +1351,7 @@ function quiz_settings(){
     '#type' => 'textfield',
     '#title' => t('Assessment name'),
     '#default_value' => $quiz_name,
-    '#description' => t('How do you want to refer to quizzes accross the site (for example: quiz, test, assessment).  This will affect display text but will not affect menu paths.'), 
+    '#description' => t('How do you want to refer to quizzes accross the site (for example: quiz, test, assessment).  This will affect display text but will not affect menu paths.'),
     '#required' => TRUE,
   );
   // option to globally set quiz availability days
@@ -1376,21 +1359,21 @@ function quiz_settings(){
     '#type' => 'textfield',
     '#title' => t('Default number of days before a quiz is closed'),
     '#default_value' => variable_get('quiz_default_close', 30),
-    '#description' => t('Supply a number of days to calculate the default close date for new quizzes.'), 
-  );  
+    '#description' => t('Supply a number of days to calculate the default close date for new quizzes.'),
+  );
   // option to globally turn off pass / fail form elements
   $form['global']['quiz_use_passfail'] = array(
     '#type' => 'checkbox',
     '#title' => t('Display pass / fail options in the quiz form'),
     '#default_value' => variable_get('quiz_use_passfail', 1),
-    '#description' => t('Check this to display the pass / fail options in the quiz form. You can still choose to ignore pass / fail options on a quiz by quiz basis if this is checked, but if you want to prohibit other quiz module creators from using these options, unchecked this option.'), 
+    '#description' => t('Check this to display the pass / fail options in the quiz form. You can still choose to ignore pass / fail options on a quiz by quiz basis if this is checked, but if you want to prohibit other quiz module creators from using these options, unchecked this option.'),
   );
   // option to globally set pass / fail boundary
   $form['global']['quiz_default_pass_rate'] = array (
     '#type' => 'textfield',
     '#title' => t('Default percentage needed to pass a quiz'),
     '#default_value' => variable_get('quiz_default_pass_rate', 75),
-    '#description' => t('Supply a number between 1 and 100 to set as the default percentage correct needed to pass a quiz. Set to 0 if you want to ignore pass / fail summary information by default.'), 
+    '#description' => t('Supply a number between 1 and 100 to set as the default percentage correct needed to pass a quiz. Set to 0 if you want to ignore pass / fail summary information by default.'),
   );
 
   $form['question'] = array(
@@ -1404,7 +1387,7 @@ function quiz_settings(){
     '#type' => 'textfield',
     '#title' => t('No Answer text'),
     '#default_value' => check_plain(variable_get('quiz_question_unanswer_text', 'skip this question')),
-    '#description' => t('What text should be provided as answer in order to skip a question?'), 
+    '#description' => t('What text should be provided as answer in order to skip a question?'),
     '#required' => TRUE,
   );
   // option to set the default scoring type
@@ -1431,10 +1414,10 @@ function quiz_settings(){
 function quiz_settings_form_validate($form_id, $form_values){
   if (!is_numeric($form_values['quiz_default_close']) || $form_values['quiz_default_close'] <= 0) {
     form_set_error('quiz_default_close', t('The default number of days before a quiz is closed must be a number greater than 0.'));
-  } 
+  }
   if (!is_numeric($form_values['quiz_default_pass_rate'])) {
     form_set_error('quiz_default_pass_rate', t('The pass rate value must be a number between 0% and 100%.'));
-  }  
+  }
   if ($form_values['quiz_default_pass_rate'] > 100) {
     form_set_error('quiz_default_pass_rate', t('The pass rate value must not be more than 100%.'));
   }
@@ -1445,15 +1428,14 @@ function quiz_settings_form_validate($fo
 
 /**
  * Quiz Admin
- * 
+ *
  */
 function quiz_admin() {
   $results = _quiz_get_results();
   return theme('quiz_admin', $results);
 }
 
-
-/*
+/**
  * Get a full results list
  */
 function _quiz_get_results($nid = '', $uid = 0) {
@@ -1491,7 +1473,7 @@ function _quiz_get_results($nid = '', $u
   return $results;
 }
 
-/*
+/**
  * Quiz Results User
  */
 function quiz_user_results() {
@@ -1508,7 +1490,7 @@ function quiz_user_results() {
   }
 }
 
-/*
+/**
  * Quiz Results Admin
  */
 function quiz_admin_results() {
@@ -1525,8 +1507,7 @@ function quiz_admin_results() {
   }
 }
 
-
-/*
+/**
  * Delete Result
  */
 function quiz_admin_result_delete() {
@@ -1549,7 +1530,6 @@ function quiz_admin_result_delete_submit
   return "admin/quiz";
 }
 
-
 function _quiz_get_answers($rid) {
   $results = array();
   $dbresult = db_query("SELECT
@@ -1573,14 +1553,13 @@ function _quiz_get_answers($rid) {
   return $results;
 }
 
-
 /////////////////////////////////////////////////
 /// Theme functions
 /////////////////////////////////////////////////
 
 /**
  * Theme the results table
- * 
+ *
  * @param $results
  *   As returned by _quiz_get_results()
  */
@@ -1598,7 +1577,7 @@ function theme_quiz_admin($results) {
       ($result['time_end'] > 0) ? format_date($result['time_end'], 'small') : t('In Progress'),
     );
   }
-  
+
   $header = array(
     t('Action'),
     t('Quiz Title'),
@@ -1606,7 +1585,7 @@ function theme_quiz_admin($results) {
     t('Result<br/>ID'),
     t('Time Started'),
     t('Finished?'));
-  
+
   if (isset($rows)) {
     $output .= theme('table', $header, $rows);
   }
@@ -1619,11 +1598,11 @@ function theme_quiz_admin($results) {
 
 /**
  * Theme the user results page
- * 
+ *
  * @param $results
  *   An array of quiz information
  * @return
- *   Themed html
+ *   Themed HTML
  */
 function theme_quiz_get_user_results($results){
     $output = '';
@@ -1638,7 +1617,7 @@ function theme_quiz_get_user_results($re
       ($result['time_end'] > 0) ? format_date($result['time_end'], 'small') : t('In Progress'),
     );
   }
-  
+
   $header = array(
     t('Action'),
     t('Quiz Title'),
@@ -1646,7 +1625,7 @@ function theme_quiz_get_user_results($re
     t('Result<br/>ID'),
     t('Time Started'),
     t('Finished?'));
-  
+
   if (isset($rows)) {
     $output .= theme('table', $header, $rows);
   }
@@ -1654,7 +1633,7 @@ function theme_quiz_get_user_results($re
     // TODO: Should this actually say "No quizzes found"?
     $output .= t('No questions found.');
   }
-  return $output; 
+  return $output;
 }
 
 /**
@@ -1684,7 +1663,6 @@ function theme_quiz_filtered_questions($
   return $output;
 }
 
-
 /**
  * Theme a table containing array of questions and options
  *
@@ -1713,17 +1691,15 @@ function theme_quiz_question_table($ques
   return $output;
 }
 
-
 /**
  * Pass the correct mark to the theme so that theme authors can use an image
- * 
+ *
  * TODO: A default image might be better here.
  */
 function theme_quiz_score_correct(){
   return theme('image', drupal_get_path('module', 'quiz').'/images/correct.gif', t('correct'));
 }
 
-
 /**
  * Pass the incorrect mark to the theme so that theme authors can use an image
  *
@@ -1733,65 +1709,61 @@ function theme_quiz_score_incorrect(){
   return theme('image', drupal_get_path('module', 'quiz').'/images/incorrect.gif', t('incorrect'));
 }
 
-
 /**
  * Theme a progress indicator for use during a quiz
- * 
+ *
  * @param $question_number
  *   The position of the current question in the sessions' array
  * @param $num_of_question
  *   The number of questions for this quiz as returned by quiz_get_number_of_questions()
  * @return
- *   Themed html
+ *   Themed HTML
  */
 function theme_quiz_progress($question_number, $num_of_question){
-  
+
   // Determine the percentage finished (not used but left for other implementations)
   //$progress = ($question_number*100)/$num_of_question;
-  
+
   // Get the current question # by adding one
   $current_question = $question_number + 1;
-  
-  // return html
+
+  // return HTML
   $output = '';
   $output .= '<div id="quiz_progress">';
   $output .= t('Question %x of %y', array('%x' => $current_question, '%y' => $num_of_question));
   $output .= '</div><br />'."\n";
   return $output;
-
 }
 
-
 /**
  * Theme a question page
- * 
+ *
  * @param $quiz
  *   The quiz node object
  * @param $question_node
  *   The question node
  * @return
- *   Themed html
+ *   Themed HTML
  */
 function theme_quiz_take_question($quiz, $question_node){
 
-  //Calculation for quiz progress bar  
+  //Calculation for quiz progress bar
   $number_of_questions = quiz_get_number_of_questions($quiz->nid);
   $question_number = $number_of_questions - count($_SESSION['quiz_'.$quiz->nid]['quiz_questions']);
 
   // Set the title here in case themers want to do something different
-  drupal_set_title(check_plain($quiz->title)); 
+  drupal_set_title(check_plain($quiz->title));
 
   // Return the elements of the page
   $output = '';
-  $output .= theme('quiz_progress', $question_number, $number_of_questions);         
+  $output .= theme('quiz_progress', $question_number, $number_of_questions);
   $output .= module_invoke($question_node->type, 'render_question', $question_node);
   return $output;
 }
 
-
 /**
  * Theme the summary page after the quiz has been completed
- * 
+ *
  * @param $quiz
  *   The quiz node object
  * @param $questions
@@ -1801,7 +1773,7 @@ function theme_quiz_take_question($quiz,
  * @param $summary
  *   Filtered text of the summary
  * @return
- *   Themed html
+ *   Themed HTML
  */
 function theme_quiz_take_summary($quiz, $questions, $score, $summary){
 
@@ -1813,18 +1785,16 @@ function theme_quiz_take_summary($quiz, 
   $output .= '<div id="quiz_score_possible">'. t('You got %num_correct of %question_count correct.', array('%num_correct' => $score['num_correct'], '%question_count' => $score['question_count'])) .'</div>'."\n";
   $output .= '<div id="quiz_score_percent">'. t('Your score: %score%', array('%score' => $score['percentage_score'])) .'</div><br />'."\n";
   $output .= '<div id="quiz_summary">'. $summary .'</div><br />'."\n";
-  
+
   // Get the feedback for all questions
   $output .= theme('quiz_feedback', $questions, FALSE, TRUE);
 
   return $output;
-
 }
 
-
 /**
  * Theme the summary page for admins
- * 
+ *
  * @param $quiz
  *   The quiz node object
  * @param $questions
@@ -1834,7 +1804,7 @@ function theme_quiz_take_summary($quiz, 
  * @param $summary
  *   Filtered text of the summary
  * @return
- *   Themed html
+ *   Themed HTML
  */
 function theme_quiz_admin_summary($quiz, $questions, $score, $summary){
 
@@ -1846,17 +1816,16 @@ function theme_quiz_admin_summary($quiz,
   $output .= '<div id="quiz_score_possible">'. t('This person got %num_correct of %question_count correct.', array('%num_correct' => $score['num_correct'], '%question_count' => $score['question_count'])) .'</div>'."\n";
   $output .= '<div id="quiz_score_percent">'. t('Total score: %score%', array('%score' => $score['percentage_score'])) .'</div><br />'."\n";
   $output .= '<div id="quiz_summary">'. $summary .'</div><br />'."\n";
-  
+
   // Get the feedback for all questions
   $output .= theme('quiz_feedback', $questions, TRUE, TRUE);
 
   return $output;
 }
 
-
 /**
  * Theme the summary page for user results
- * 
+ *
  * @param $quiz
  *   The quiz node object
  * @param $questions
@@ -1866,7 +1835,7 @@ function theme_quiz_admin_summary($quiz,
  * @param $summary
  *   Filtered text of the summary
  * @return
- *   Themed html
+ *   Themed HTML
  */
 function theme_quiz_user_summary($quiz, $questions, $score, $summary){
 
@@ -1878,18 +1847,16 @@ function theme_quiz_user_summary($quiz, 
   $output .= '<div id="quiz_score_possible">'. t('You got %num_correct of %question_count correct.', array('%num_correct' => $score['num_correct'], '%question_count' => $score['question_count'])) .'</div>'."\n";
   $output .= '<div id="quiz_score_percent">'. t('Your score was: %score%', array('%score' => $score['percentage_score'])) .'</div><br />'."\n";
   $output .= '<div id="quiz_summary">'. $summary .'</div><br />'."\n";
-  
+
   // Get the feedback for all questions
   $output .= theme('quiz_feedback', $questions, FALSE, TRUE);
 
   return $output;
-
 }
 
-
 /**
  * Theme the question feedback
- * 
+ *
  * @param $questions
  *   Array of quiz objects as returned by _quiz_get_answers
  * @param showPoints
@@ -1897,7 +1864,7 @@ function theme_quiz_user_summary($quiz, 
  * @param $showFeedback
  *   binary flag for whether to show question feedback
  * @return
- *   Themed html
+ *   Themed HTML
  */
 function theme_quiz_feedback($questions, $showPoints = TRUE, $showFeedback = FALSE){
   $rows = array();
@@ -1907,11 +1874,11 @@ function theme_quiz_feedback($questions,
 
     // reset the cols array
     $cols = array();
-    
+
     // Get the answer table for this question
     $question['qanswer'] = unserialize($question['qanswer']);
-    $result = module_invoke($question['type'], 'calculate_results', $question['qanswer']['answers'], $question['qanswer']['tried'], $showPoints, $showFeedback);
-    
+    $result = module_invoke($question['type'], 'calculate_results', $question['qanswer']['answers'], $question['qanswer']['tried'], $question['qanswer']['properties'], $showPoints, $showFeedback);
+
     // Build the question answers header (add blank space for IE)
     $innerHeader = array(t('Answers'));
     if($showPoints){
@@ -1928,11 +1895,11 @@ function theme_quiz_feedback($questions,
     $cols[] = array('data' => $q_output, 'class'=> 'quiz_summary_qcell');
 
     // Get the score result for each question.
-    if($result['score'] == 1) {
+    if($result['succes'] > 0) {
       $cols[] = array('data' => theme('quiz_score_correct'), 'class' => 'quiz_summary_qcell');
     }
     else {
-      $cols[] = array('data' => theme('quiz_score_incorrect'), 'class' => 'quiz_summary_qcell');      
+      $cols[] = array('data' => theme('quiz_score_incorrect'), 'class' => 'quiz_summary_qcell');
     }
 
     // pack all of this into this row
@@ -1941,7 +1908,6 @@ function theme_quiz_feedback($questions,
   return theme('table', $header, $rows);
 }
 
-
 /**
  * Allow the option to theme the questions form
  */
