multichoice.module in Quiz 5.2
Same filename and directory in other branches
Multiple choice question type for the Quiz module.
Allows the creation of multiple choice questions (ex: a, b, c, d or true/false)
File
multichoice.moduleView source
<?php
/**
* @file
* Multiple choice question type for the Quiz module.
*
* Allows the creation of multiple choice questions (ex: a, b, c, d or true/false)
*/
define('MULTICHOICE_NAME', 'Multi-Choice Question');
/**
* Implementation of hook_perm().
*/
function multichoice_perm() {
return array(
'create multichoice',
'edit own multichoice',
'allow any number of answers',
'allow multiple correct answers',
'allow feedback',
'allow user titles',
);
}
/**
* Implementation of hook_access().
*/
function multichoice_access($op, $node) {
global $user;
if ($op == 'create') {
return user_access('create multichoice');
}
if ($op == 'update' || $op == 'delete') {
if (user_access('edit own multichoice') && $user->uid == $node->uid) {
return TRUE;
}
}
}
/**
* Implementation of hook_node_info().
*/
function multichoice_node_info() {
return array(
'multichoice' => array(
'name' => t(MULTICHOICE_NAME),
'module' => 'multichoice',
'description' => t('A question type for the quiz module: allows you to create multiple choice questions (ex: A, B, C, D or true/false)'),
),
);
}
/**
* Implementation of hook_menu().
*/
function multichoice_menu($may_cache) {
$items = array();
if ($may_cache) {
$items[] = array(
'path' => 'node/add/multichoice',
'title' => t(MULTICHOICE_NAME),
'access' => user_access('create multichoice'),
);
$items[] = array(
'path' => 'admin/settings/multichoice',
'title' => t('Multichoice Configuration'),
'description' => t('Configure Multichoice questions fo users.'),
'callback' => 'drupal_get_form',
'callback arguments' => array(
'multichoice_admin_settings_form',
),
'access' => user_access(QUIZ_PERM_ADMIN_CONFIG),
'type' => MENU_NORMAL_ITEM,
);
}
return $items;
}
/**
* Admin settings form.
*/
function multichoice_admin_settings_form() {
$form['multichoice_default_answers'] = array(
'#type' => 'textfield',
'#title' => t('Default Number of Answers'),
'#default_value' => variable_get('multichoice_default_answers', 5),
'#description' => t('The default number of answers to display when creating a question.'),
'#required' => TRUE,
);
return system_settings_form($form);
}
/**
* Implementation of hook_form().
*/
function multichoice_form(&$node) {
// Quiz ID used here to tie creation of a question to a specific quiz.
$quiz_id = arg(3);
if (!empty($quiz_id)) {
$quiz = node_load((int) $quiz_id);
$form['quiz_id'] = array(
'#type' => 'value',
'#value' => $quiz_id,
);
$form['quiz_vid'] = array(
'#type' => 'value',
'#value' => $quiz->vid,
);
}
// Display the multichoice form.
// Allow user to set title?
if (user_access('allow user titles')) {
$form['title'] = array(
'#type' => 'textfield',
'#title' => t('Title'),
'#default_value' => $node->title,
'#required' => FALSE,
'#description' => t('Add a title that will help distinguish this question from other questions. This will not be seen during the quiz.'),
);
}
else {
$form['title'] = array(
'#type' => 'value',
'#value' => $node->title,
);
}
$form['body'] = array(
'#type' => 'textarea',
'#title' => t('Question'),
'#default_value' => $node->body,
'#required' => TRUE,
);
$form['body_filter']['format'] = filter_form($node->format);
if (user_access('allow multiple correct answers')) {
$form['multiple_answers'] = array(
'#type' => 'checkbox',
'#title' => t('Multiple answers'),
'#description' => t('Should the quiz-taker be allowed to select multiple correct answers?'),
'#default_value' => $node->multiple_answers,
);
}
else {
$form['multiple_answers'] = array(
'#type' => 'value',
'#value' => FALSE,
);
}
// Determine number of answer rows to display.
if (!isset($node->rows)) {
$node->rows = max(2, $node->answers ? count($node->answers) : variable_get('multichoice_default_answers', 5));
}
if ($_POST['more']) {
$node->rows += 5;
}
$answers = $node->answers;
// Display answer rows.
$form['answers'] = array(
'#type' => 'fieldset',
'#title' => t('Choices'),
'#tree' => TRUE,
'#theme' => 'multichoice_form',
);
$form['scored_quiz'] = array(
'#type' => 'value',
'#value' => !empty($quiz_id) ? $quiz->pass_rate > 0 : TRUE,
);
for ($i = 0; $i < $node->rows; $i++) {
// This is not a scored quiz, therefore no correct answers
// so creator must assign answers to result options.
if (isset($quiz_id) && $quiz->pass_rate == 0) {
if (empty($result_options)) {
$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'];
}
}
}
$form['answers'][$i]['result_option'] = array(
'#type' => 'select',
'#options' => $result_options,
'#default_value' => $answers[$i]['result_option'],
);
}
else {
if (user_access('allow multiple correct answers')) {
$form['answers'][$i]['correct'] = array(
'#type' => 'checkbox',
'#default_value' => $answers[$i]['is_correct'],
);
}
}
$form['answers'][$i]['answer'] = array(
'#type' => 'textarea',
'#default_value' => $answers[$i]['answer'],
'#cols' => 30,
'#rows' => 2,
);
if (user_access('allow feedback')) {
$form['answers'][$i]['feedback'] = array(
'#type' => 'textarea',
'#default_value' => $answers[$i]['feedback'],
'#cols' => 30,
'#rows' => 2,
);
}
else {
$form['answers'][$i]['feedback'] = array(
'#type' => 'value',
'#value' => '',
);
}
if ($answers[$i]['answer_id']) {
$form['answers'][$i]['delete'] = array(
'#type' => 'checkbox',
'#default_value' => 0,
);
$form['answers'][$i]['answer_id'] = array(
'#type' => 'hidden',
'#value' => $answers[$i]['answer_id'],
);
}
}
if (user_access('allow any number of answers')) {
$form['more'] = array(
'#type' => 'checkbox',
'#title' => t('I need more answers'),
);
}
if (!user_access('allow multiple correct answers')) {
$form['correct'] = array(
'#type' => 'select',
'#title' => t('Correct Answer'),
'#description' => t('Which of the answer choices is correct?'),
'#required' => TRUE,
'#size' => 1,
'#options' => range(1, $node->rows),
'#default_value' => _multichoice_find_correct($node->answers),
);
}
// If coming from quiz view, go back there on submit.
if (!empty($quiz_id)) {
$form['#redirect'] = 'node/' . $quiz_id . '/questions';
}
return $form;
}
/**
* Find the correct answers in an array of answers.
*
* @param $answers
* An indexed array of answer choices.
* @return
* The index of the first correct answer.
*/
function _multichoice_find_correct($answers) {
if (is_array($answers)) {
foreach ($answers as $id => $answer) {
if ($answer['is_correct']) {
return $id;
}
}
}
return 0;
}
/**
* Implementation of hook_validate().
*/
function multichoice_validate(&$node) {
// Hard-code questions to have no teaser and to not be promoted to front page.
$node->teaser = 0;
$node->promote = 0;
if (!$node->nid && empty($_POST)) {
return;
}
// Validate body.
if (!$node->body) {
form_set_error('body', t('Question text is empty'));
}
// Validate answers.
$answers = 0;
$corrects = user_access('allow multiple correct answers') ? 0 : 1;
if ($node->scored_quiz) {
foreach ($node->answers as $key => $answer) {
if (trim($answer['answer']) != '') {
$answers++;
if ($answer['correct'] > 0) {
if ($corrects && !$node->multiple_answers) {
form_set_error('multiple_answers', t('Single choice yet multiple correct answers are present'));
}
$corrects++;
}
}
else {
// Warn that feedback is present without an answer.
if (trim($answer['feedback']) != '') {
form_set_error("answers][{$key}][feedback", t('Feedback is given without an answer.'));
}
// Warn about marking correct without an answer present.
if ($answer['correct'] > 0) {
form_set_error("answers][{$key}][correct", t('Empty answer marked as correct choice.'));
}
}
}
if (!$corrects) {
form_set_error("answers][0]['correct'", t('No correct choice(s).'));
}
if ($node->multiple_answers && $corrects < 2) {
form_set_error('multiple_answers', t('Multiple answers selected, but only %count correct answer(s) present.', array(
'%count' => $corrects,
)));
}
if (!$answers) {
form_set_error("answers][0]['answer'", t('No answers.'));
}
if ($answers < 2) {
form_set_error("answers][1]['answer'", t('Must have at least two answers.'));
}
}
else {
// Unscored quiz.
foreach ($node->answers as $key => $answer) {
if ($answer['answer'] && !$answer['result_option']) {
form_set_error('answers][$key][result_option', t('You must select an association for each answer'));
}
}
}
// Validate number of answers.
if (!user_access('allow any number of answers')) {
// Users must create questions with exactly a certain number of answers.
$required = variable_get('multichoice_default_answers', 5);
if ($answers != $required) {
form_set_error("answers[0]['answer'", t('You must have exactly @num answer choices. There are currently @answers answer choices.', array(
'@num' => $required,
'@answers' => $answers,
)));
}
}
}
/**
* Implementation of hook_submit().
*/
function multichoice_submit(&$node) {
if (!user_access('allow user titles') || empty($node->title)) {
// User is not allowed to set a title or they left it blank, so make one for them.
$node->title = theme('multichoice_generate_title', $node);
}
if (!user_access('allow multiple correct answers')) {
// Convert the select box to the old checkbox.
$node->answers[$node->correct]['correct'] = TRUE;
}
}
/**
* Get the number of correct answers in an array of multichoice answers.
*
* @param $answers
* Array of multichoice answers.
* @return $corrects
* The number of correct answers found in that array. Returns 0 if none found.
*/
function multichoice_get_number_of_corrects($answers) {
$corrects = 0;
if (is_array($answers)) {
foreach ($answers as $answer) {
$corrects += $answer['correct'];
}
}
return $corrects;
}
/**
* Implementation of hook_insert().
*/
function multichoice_insert(&$node) {
$node->number_of_answers = multichoice_get_number_of_corrects($node->answers);
$quiz_vid = $node->quiz_vid;
$question_vid = $node->vid;
// Instead of just storing whether a question has multiple answers,
// we will store the actual number of correct answers.
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} ' . '(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);
}
foreach ($node->answers as $value) {
if (trim($value['answer']) != "") {
db_query("INSERT INTO {quiz_multichoice_answers} " . "(answer_id, nid, vid, answer, feedback, is_correct, result_option) VALUES (%d, %d, %d, '%s', '%s', %d, %d)", db_next_id('{quiz_multichoice_answers}_answer_id'), $node->nid, $question_vid, $value['answer'], $value['feedback'], $value['correct'], $value['result_option']);
}
}
}
/**
* Implementation of hook_update().
*/
function multichoice_update($node) {
$question_vid = $node->vid;
$node->number_of_answers = multichoice_get_number_of_corrects($node->answers);
if ($node->revision) {
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);
}
else {
db_query("UPDATE {quiz_node_question_properties} " . "SET number_of_answers = %d WHERE nid = %d AND vid = %d", $node->number_of_answers, $node->nid, $node->vid);
}
while (list($key, $value) = each($node->answers)) {
if ($value['answer_id']) {
$value['answer'] = trim($value['answer']);
if ($value['delete'] == 1 || !isset($value['answer']) || $value['answer'] == '') {
// Delete this entry.
db_query("DELETE FROM {quiz_multichoice_answers} WHERE answer_id = %d", $value['answer_id']);
}
else {
// Update this entry.
db_query("UPDATE {quiz_multichoice_answers} SET answer = '%s', feedback = '%s', is_correct = %d, result_option = %d WHERE answer_id = %d", $value['answer'], $value['feedback'], $value['correct'], $value['result_option'], $value['answer_id']);
}
}
else {
if (trim($value['answer']) != "") {
// If there is an answer, insert a new row.
db_query("INSERT INTO {quiz_multichoice_answers} (answer_id, nid, vid, answer, feedback, is_correct, result_option) " . "VALUES(%d, %d, %d, '%s', '%s', %d, %d)", db_next_id('{quiz_multichoice_answers}_answer_id'), $node->nid, $question_vid, $value['answer'], $value['feedback'], $value['correct'], $value['result_option']);
}
}
}
// Quiz node vid (revision) was updated.
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);
// 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);
}
}
}
/**
* Implementation of hook_delete().
*/
function multichoice_delete(&$node) {
// Delete all answers for this question.
db_query("DELETE FROM {quiz_multichoice_answers} WHERE nid = %d", $node->nid);
// For all quizzes that have this quiz with status ALWAYS, reduce number of questions by 1.
// TODO: This should also create a new revision of every quiz that used to contain this question.
//db_query("UPDATE {quiz_node_properties} SET number_of_questions = number_of_questions-1 WHERE nid IN " .
// "(SELECT parent_vid FROM {quiz_node_relationship} WHERE question_status = %d AND child_nid = %d)", QUESTION_ALWAYS, $node->nid);
// Delete this question from all quizzes.
db_query("DELETE FROM {quiz_node_question_properties} WHERE nid = %d", $node->nid);
db_query("DELETE FROM {quiz_node_relationship} WHERE child_nid = %d", $node->nid);
}
/**
* Implementation of hook_load().
*/
function multichoice_load($node) {
$additions = new stdClass();
$question_vid = $node->vid;
$additions->properties = db_fetch_array(db_query("SELECT * FROM {quiz_node_question_properties} WHERE nid = %d AND vid = %d", $node->nid, $question_vid));
$answers = array();
$result = db_query("SELECT * FROM {quiz_multichoice_answers} WHERE nid = %d AND vid = %d", $node->nid, $question_vid);
while ($line = db_fetch_array($result)) {
$answers[] = $line;
}
$additions->answers = $answers;
// Just check for multiple answers for now.
$additions->multiple_answers = $additions->properties['number_of_answers'] > 1 ? 1 : 0;
return $additions;
}
/**
* Implementation of hook_view().
*/
function multichoice_view(&$node, $teaser = FALSE, $page = FALSE) {
if (!$teaser && user_access('create multichoice')) {
$mynode = node_prepare($node, $teaser);
$mynode->content['body'] = array(
'#value' => multichoice_render_question($node),
);
return $mynode;
}
else {
if ($teaser) {
$mynode = node_prepare($node, $teaser);
return $mynode;
}
else {
drupal_access_denied();
}
}
}
/**
* Print question to screen.
*
* @param $node
* Question node.
*
* @return
* HTML output.
*/
function multichoice_render_question_form($node) {
// Radio buttons for single selection questions, checkboxes for multiselect.
if ($node->multiple_answers == 0) {
$type = 'radios';
}
else {
$type = 'checkboxes';
}
// Get options.
$options = array();
while (list($key, $answer) = each($node->answers)) {
if (empty($answer['correct']) && !isset($answer['answer']) && empty($answer['feedback'])) {
unset($node->answers[$key]);
}
else {
$options[$answer['answer_id']] = '<span class="multichoice_answer_text">' . check_markup($answer['answer'], $node->format, FALSE) . '</span>';
}
}
$form['start'] = array(
'#type' => 'markup',
'#value' => '<div class="multichoice_form">',
);
$form['question'] = array(
'#type' => 'markup',
'#value' => check_markup($node->body, $node->format, FALSE),
);
// Create form.
$form['tries'] = array(
'#type' => $type,
'#options' => $options,
'#default_value' => -1,
);
$quiz = node_load(arg(1));
if ($quiz->backwards_navigation == 1 && $node->question_number) {
$form['back'] = array(
'#type' => 'submit',
'#value' => t('Back'),
);
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
function multichoice_render_question($node) {
return drupal_get_form('multichoice_render_question_form', $node);
}
/**
* Evaluate whether a question is correct.
*
* @param $question
* Question node.
*
* @return
* Array of results, in the form of:
* array(
* 'answers' => array of correct answer(s)
* 'tried' => array of selected answer(s)
* );
*/
function multichoice_evaluate_question($question, $rid = null) {
$tries = is_array($_POST['tries']) ? $_POST['tries'] : array(
$_POST['tries'],
);
// Unset $_POST, otherwise it tries to use the previous answers on the next page...
unset($_POST['tries']);
$numanswers = db_query("SELECT count(answer_id) FROM {quiz_multichoice_answers} " . "WHERE nid = %d AND vid = %d AND is_correct = 1", $question->nid, $question->vid);
if ($rid && !empty($tries)) {
foreach ($tries as $answer_id) {
multichoice_store_answer($question->nid, $question->vid, $rid, $answer_id, $numanswers);
}
}
// Return whether or not the user's response was correct.
return multichoice_calculate_result($question->nid, $question->vid, $tries);
}
/**
* Store one response to a multichoice question.
*
* @param $nid
* Question node id.
* @param $vid
* Question node revision id.
* @param $rid
* Result id.
* @param $answer_id
* The answer id.
*/
function multichoice_store_answer($nid, $vid, $rid, $answer_id, $numanswers) {
$result = db_query("SELECT result_id FROM {quiz_multichoice_user_answers} WHERE question_nid = %d AND question_vid = %d AND result_id = %d", $nid, $vid, $rid);
if (db_num_rows($result) == $numanswers) {
db_query("UPDATE {quiz_multichoice_user_answers} " . "SET answer_id = %d " . "WHERE question_nid = %d AND question_vid = %d AND result_id = %d", $answer_id, $nid, $vid, $rid);
}
else {
db_query("INSERT INTO {quiz_multichoice_user_answers} " . "(question_nid, question_vid, result_id, answer_id) " . "VALUES (%d, %d, %d, %d)", $nid, $vid, $rid, $answer_id);
}
}
/**
* Check if the user selected all the correct answers for this question.
*
* @param $answers
* Array of answers.
* @param $tried
* Array of answer id's the user selected.
* @return
* TRUE if the user selected exactly all of the correct answers, otherwise FALSE.
*/
function multichoice_calculate_result($nid, $vid, $tried) {
$answers = db_query("SELECT answer_id FROM {quiz_multichoice_answers} " . "WHERE nid = %d AND vid = %d AND is_correct = 1", $nid, $vid);
while ($answer = db_fetch_array($answers)) {
$correct_answers[] = $answer['answer_id'];
}
return array_values($correct_answers) === array_values($tried);
}
// New singing and dancing one.
function multichoice_calculate_results($answers, $tried, $showpoints = FALSE, $showfeedback = FALSE) {
// Create results table.
$rows = array();
$correctanswers = array();
while (list($key, $answer) = each($answers)) {
$cols = array();
$cols[] = $answer['answer'];
if ($showpoints) {
$cols[] = $answer['is_correct'] == 0 ? theme_multichoice_unselected() : theme_multichoice_selected();
}
$selected = array_search($answer['answer_id'], $tried) !== FALSE;
$cols[] = $selected ? theme_multichoice_selected() : theme_multichoice_unselected();
if ($showfeedback) {
$cols[] = $selected ? '<div class="quiz_answer_feedback">' . $answer['feedback'] . '</div>' : '';
}
$rows[] = $cols;
if ($answer['is_correct'] > 0) {
$correctanswers[] = $answer['answer_id'];
}
}
if ($correctanswers === $tried) {
$score = 1;
}
else {
$score = 0;
}
return array(
'score' => $score,
'resultstable' => $rows,
);
}
/**
* Retrieve a full multichoice question with answers and user answers for reporting.
*
* @param $nid
* The question node id.
* @param $vid
* The question node revision id.
* @param $rid
* The result id.
* @return $question
* Question object with all available answers, and the user answers, and question properties.
*/
function multichoice_get_report($nid, $vid, $rid) {
$result = db_query("SELECT *, " . "COALESCE((SELECT 1 FROM {quiz_multichoice_user_answers} ua WHERE question_nid = n.nid AND question_vid = n.vid AND result_id = %d AND ua.answer_id = ma.answer_id),0) as user_answer, " . "(SELECT is_correct FROM {quiz_node_results_answers} WHERE question_nid = n.nid AND question_vid = n.vid AND result_id = %d) as question_correct " . "FROM {node} n " . "LEFT JOIN {node_revisions} USING (nid, vid) " . "LEFT JOIN {quiz_multichoice_answers} ma USING (nid, vid) " . "WHERE n.nid = %d AND n.vid = %d", $rid, $rid, $nid, $vid);
if ($result) {
$question = new stdClass();
while ($next_row = db_fetch_array($result)) {
$row = $next_row;
$question->answers[$row['answer_id']] = $row;
}
$question->title = $row['title'];
$question->body = $row['body'];
$question->teaser = $row['teaser'];
$question->correct = $row['question_correct'];
$question->type = $row['type'];
}
return $question;
}
/**
* List all multiple choice questions.
*
* @return
* Array of questions.
*/
function multichoice_list_questions() {
$result = db_query("SELECT nid, body, format FROM {node} WHERE type= '%s'", 'multichoice');
$questions = array();
while ($node = db_fetch_object($result)) {
$question = new stdClass();
$question->question = check_markup($node->body, $node->format);
$question->nid = $node->nid;
$questions[] = $question;
}
return $questions;
}
/**
*
*
* THEME FUNCTIONS!
*
*
*/
/**
* Theme function for multichoice form.
*
* Lays out answer field elements into a table.
*
* @return string
* HTML output.
*
* @ingroup themeable
*/
function theme_multichoice_form($form) {
// Format table header.
$scored_quiz = isset($form[0]['result_option']) ? FALSE : TRUE;
$deleteable = isset($form[0]['aid']) ? TRUE : FALSE;
if (user_access('allow multiple correct answers')) {
$header = array(
'data' => $scored_quiz ? t('Correct') : t('Result Option'),
);
}
$header[] = array(
'data' => t('Answer'),
);
if (user_access('allow feedback')) {
$header[] = array(
'data' => t('Feedback'),
'style' => 'width:250px;',
);
}
if ($deleteable) {
$header[] = array(
'data' => t('Delete'),
);
}
// Format table rows.
$rows = array();
foreach (element_children($form) as $key) {
$score_col = $scored_quiz ? $form[$key]['correct'] : $form[$key]['result_option'];
$row = array();
if ($score_col) {
$row[] = drupal_render($score_col);
}
$row[] = drupal_render($form[$key]['answer']);
if (user_access('allow feedback')) {
$row[] = drupal_render($form[$key]['feedback']);
}
if ($deleteable) {
drupal_render($form[$key]['delete']);
}
$rows[] = $row;
}
// Theme output and display to screen.
$output = theme('table', $header, $rows);
return $output;
}
/**
* Theme a selection indicator for an answer.
*
* @ingroup themeable
*/
function theme_multichoice_selected() {
return theme_image(drupal_get_path('module', 'quiz') . '/images/selected.gif', t('selected'));
}
/**
* Theme an indicator that an answer is not selected or correct.
*
* @ingroup themeable
*/
function theme_multichoice_unselected() {
return theme_image(drupal_get_path('module', 'quiz') . '/images/unselected.gif', t('unselected'));
}
/**
* Theme function for the multichoice form.
*
* @ingroup themeable
*/
function theme_multichoice_render_question_form($form) {
$output = '';
$output .= drupal_render($form) . '</div>';
return $output;
}
/**
* Theme a multichoice question report for quiz feedback.
*
* @ingroup themeable
*/
function theme_multichoice_report($question, $showpoints, $showfeedback) {
// Build the question answers header (add blank space for IE).
$innerheader = array(
t('Answers'),
);
if ($showpoints) {
$innerheader[] = t('Correct Answer');
}
$innerheader[] = t('User Answer');
if ($showfeedback) {
$innerheader[] = ' ';
}
foreach ($question->answers as $aid => $answer) {
$cols = array();
$cols[] = $answer['answer'];
if ($showpoints) {
$cols[] = $answer['is_correct'] ? theme_multichoice_selected() : theme_multichoice_unselected();
}
$cols[] = $answer['user_answer'] ? theme_multichoice_selected() : theme_multichoice_unselected();
$cols[] = $showfeedback && $answer['user_answer'] ? '<div class="quiz_answer_feedback">' . $answer['feedback'] . '</div>' : '';
$rows[] = $cols;
}
// Add the cell with the question and the answers.
$q_output = '<div class="quiz_summary_question"><span class="quiz_question_bullet">Q:</span> ' . check_markup($question->body) . '</div>';
$q_output .= theme('table', $innerheader, $rows) . '<br />';
return $q_output;
}
/**
* Displays feedback for multichoice
*
* @param $quiz
* The quiz node.
* @param $report
* The report variables.
* @return $output
* HTML output to be written to the screen.
*
* @ingroup themeable
*/
function theme_multichoice_feedback($quiz, $report) {
$output = '<span class="quiz-summary-text">' . $report->body . '</span><br />';
foreach ($report->answers as $answer) {
if ($answer['user_answer']) {
$answers[] = $answer['answer'];
$feedbacks[] = $answer['feedback'];
}
if ($answer['is_correct']) {
$corrects[] = $answer['answer'];
}
}
$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) {
$output .= "You're right!";
}
else {
$output .= "Sorry!<br />";
$output .= "Correct Answer(s): " . implode(',', $corrects);
}
return $output;
}
/**
* Create a decent question title based on taxonomy and question body.
*
* This is themeable so others can create their own titles without too much trouble:
* 1. Pick the first taxonomy term from the first vocabulary.
* 2. Append a numeric sequence.
* 3. Append the first 50 chars of question body, or less, depending on where the word breaks are.
*
* @param $node
* The question node to generate a title for.
* @return
* A title string.
*
* @ingroup themeable
*/
function theme_multichoice_generate_title($node) {
$title = '';
// Get the first taxonomy term if available.
$taxonomy = $node->taxonomy;
while (is_array($taxonomy)) {
$taxonomy = current($taxonomy);
}
if (!empty($taxonomy)) {
$taxonomy = taxonomy_get_term($taxonomy);
$title .= $taxonomy->name . ' - ';
}
// Append a numeric sequence.
$qnum = variable_get('multichoice_qnum', 0);
$title .= $qnum++ . ' - ';
variable_set('multichoice_qnum', $qnum);
// Strip tags from question body and shorten to 50 or less chars.
$shortstring = trim(substr(strip_tags($node->body), 0, 50));
if (FALSE !== ($breakpoint = strrpos($shortstring, ' '))) {
$shortstring = substr($shortstring, 0, $breakpoint);
}
return $title . $shortstring;
}
Functions
Name | Description |
---|---|
multichoice_access | Implementation of hook_access(). |
multichoice_admin_settings_form | Admin settings form. |
multichoice_calculate_result | Check if the user selected all the correct answers for this question. |
multichoice_calculate_results | |
multichoice_delete | Implementation of hook_delete(). |
multichoice_evaluate_question | Evaluate whether a question is correct. |
multichoice_form | Implementation of hook_form(). |
multichoice_get_number_of_corrects | Get the number of correct answers in an array of multichoice answers. |
multichoice_get_report | Retrieve a full multichoice question with answers and user answers for reporting. |
multichoice_insert | Implementation of hook_insert(). |
multichoice_list_questions | List all multiple choice questions. |
multichoice_load | Implementation of hook_load(). |
multichoice_menu | Implementation of hook_menu(). |
multichoice_node_info | Implementation of hook_node_info(). |
multichoice_perm | Implementation of hook_perm(). |
multichoice_render_question | |
multichoice_render_question_form | Print question to screen. |
multichoice_store_answer | Store one response to a multichoice question. |
multichoice_submit | Implementation of hook_submit(). |
multichoice_update | Implementation of hook_update(). |
multichoice_validate | Implementation of hook_validate(). |
multichoice_view | Implementation of hook_view(). |
theme_multichoice_feedback | Displays feedback for multichoice |
theme_multichoice_form | Theme function for multichoice form. |
theme_multichoice_generate_title | Create a decent question title based on taxonomy and question body. |
theme_multichoice_render_question_form | Theme function for the multichoice form. |
theme_multichoice_report | Theme a multichoice question report for quiz feedback. |
theme_multichoice_selected | Theme a selection indicator for an answer. |
theme_multichoice_unselected | Theme an indicator that an answer is not selected or correct. |
_multichoice_find_correct | Find the correct answers in an array of answers. |
Constants
Name | Description |
---|---|
MULTICHOICE_NAME | @file Multiple choice question type for the Quiz module. |