View source
<?php
define('QUIZ_QUESTION_NUM_LATEST', 10);
function quiz_question_help($path, $args) {
if ($path == 'admin/help#quiz_quesion') {
return t('Support for Quiz question types.');
}
}
function quiz_question_autoload_info() {
return array(
'QuizQuestion' => array(
'file' => 'quiz_question.core.inc',
),
'AbstractQuizQuestion' => array(
'file' => 'quiz_question.core.inc',
),
'QuizQuestionResponse' => array(
'file' => 'quiz_question.core.inc',
),
'AbstractQuizQuestionResponse' => array(
'file' => 'quiz_question.core.inc',
),
);
}
function quiz_question_menu() {
$items = array();
$items['quiz_question/%/%/revision_actions'] = array(
'title' => 'Revision actions',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'quiz_question_revision_actions',
1,
2,
),
'access arguments' => array(
'manual quiz revisioning',
),
'file' => 'quiz_question.pages.inc',
'type' => MENU_NORMAL_ITEM,
);
$items['admin/quiz/settings/questions_settings'] = array(
'title' => 'Question configuration',
'description' => 'Configure the question types.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'quiz_question_config',
),
'access arguments' => array(
'administer quiz configuration',
),
'type' => MENU_NORMAL_ITEM,
);
return $items;
}
function quiz_question_theme() {
return array(
'quiz_question_creation_form' => array(
'arguments' => array(
'form' => NULL,
),
'file' => 'quiz_question.theme.inc',
),
'quiz_question_navigation_form' => array(
'arguments' => array(
'form' => NULL,
),
'file' => 'quiz_question.theme.inc',
),
);
}
function quiz_question_node_info() {
$types = _quiz_question_get_implementations();
$info = array();
$defaults = array(
'module' => 'quiz_question',
'has_body' => TRUE,
'has_title' => TRUE,
'body_label' => t('Question'),
);
foreach ($types as $type => $definition) {
$node_info = array(
'name' => $definition['name'],
'description' => $definition['description'],
);
$info[$type] = $node_info + $defaults;
}
return $info;
}
function quiz_question_access($op, $node, $account) {
switch ($op) {
case 'view':
return user_access('view quiz question outside of a quiz');
case 'create':
return user_access('create quiz', $account);
case 'update':
if (user_access('edit any quiz', $account) || user_access('edit own quiz', $account) && $account->uid == $node->uid) {
return TRUE;
}
case 'delete':
if (user_access('delete any quiz', $account) || user_access('delete own quiz', $account) && $account->uid == $node->uid) {
return TRUE;
}
}
}
function quiz_question_access_to_score($vid, $rid) {
global $user;
$sql = 'SELECT *
FROM {quiz_node_results_answers}
WHERE result_id = %d AND question_vid = %d';
if (!db_fetch_object(db_query($sql, $rid, $vid))) {
return FALSE;
}
if (user_access('score any quiz')) {
return TRUE;
}
if (user_access('score own quiz')) {
$sql = 'SELECT r.uid
FROM {node_revisions} r
WHERE r.nid = (
SELECT nid
FROM {quiz_node_results}
WHERE result_id = %d
)';
return db_result(db_query($sql, $rid)) == $user->uid;
}
}
function quiz_question_form(&$node, $form_state) {
$question = _quiz_question_get_instance($node);
$form = $question
->getNodeForm($form_state);
return $form;
}
function quiz_question_validate($node, &$form) {
if (empty($node->body)) {
form_set_error('body', t('Question text is empty.'));
}
_quiz_question_get_instance($node)
->validateNode($form);
}
function quiz_question_answering_form($form_state, $node, $include_nid_in_id = FALSE) {
$question = _quiz_question_get_instance($node);
$form = $question
->getAnsweringForm($form_state, isset($node->rid) ? $node->rid : NULL);
$quiz = quiz_type_access_load(arg(1));
$form['#attributes']['class'] = 'answering-form';
$is_last = _quiz_is_last_question();
$form['navigation']['#theme'] = 'quiz_question_navigation_form';
if (!empty($quiz->backwards_navigation) && !empty($node->question_number)) {
$form['navigation']['back'] = array(
'#type' => 'submit',
'#value' => t('Back'),
'#attributes' => array(
'class' => 'q-back-button',
),
);
if ($is_last) {
drupal_set_message(t('This is the last question. If you would like to go back and check some of your answers, click "Back", otherwise click the "Finish" button.'), 'status', FALSE);
}
}
$form['navigation']['submit'] = array(
'#type' => 'submit',
'#value' => $is_last ? t('Finish') : t('Next'),
);
if ($node->allow_skipping) {
$form['navigation']['op'] = array(
'#type' => 'submit',
'#value' => $is_last ? t('Leave blank and finish') : t('Leave blank'),
'#attributes' => array(
'class' => 'q-skip-button',
),
);
if ($quiz->allow_jumping) {
$form['jump_to_question'] = array(
'#type' => 'hidden',
'#default_value' => 0,
);
}
}
return $form;
}
function _quiz_question_teaser_content($node) {
$content['question'] = array(
'#type' => 'markup',
'#value' => check_markup($node->body, $node->format, FALSE),
);
$type = node_get_types('type', $node);
$content['question_type'] = array(
'#type' => 'markup',
'#value' => '<div class="question_type_name">' . $type->name . '</div>',
'#weight' => -1,
);
return $content;
}
function quiz_question_evaluate_question($question, $result_id, $answer = NULL) {
if (empty($answer)) {
$answer = $_POST['tries'];
}
unset($_POST['tries']);
$response = _quiz_question_response_get_instance($result_id, $question, $answer);
if ($result_id && isset($answer)) {
$response
->delete();
$response
->saveResult();
}
return $response
->toBareObject();
}
function quiz_question_skip_question($question, $result_id) {
unset($_POST['tries']);
_quiz_question_response_get_instance($result_id, $question)
->delete();
$response = new stdClass();
$response->nid = $question->nid;
$response->vid = $question->vid;
$response->rid = $result_id;
$response->is_skipped = TRUE;
return $response;
}
function quiz_question_list_questions($count = 0, $offset = 0) {
$sql = "SELECT n.nid, n.vid, r.body, r.format\n FROM {node} AS n\n INNER JOIN {node_revisions} AS r USING(vid)\n WHERE n.type IN (%s) ORDER BY n.type, n.changed";
$types = array();
foreach (array_keys(_quiz_question_get_implementations()) as $key) {
$types[] = "'" . $key . "'";
}
$type = implode(',', $types);
if ($count == 0) {
$result = db_query(db_rewrite_sql($sql), $type);
}
else {
$result = db_query_range(db_rewrite_sql($sql), $type, $offset, $count);
}
$questions = array();
while ($question = db_fetch_object($result)) {
$question->question = check_markup($question->body, $question->format, FALSE);
$questions[] = $question;
}
return $questions;
}
function quiz_question_get_report($nid, $vid, $rid) {
$response_instance = _quiz_question_response_get_instance($rid, NULL, NULL, $nid, $vid);
if (!$response_instance) {
drupal_set_message(t('Unable to load question with nid %nid and vid %vid', array(
'%nid' => $nid,
'%vid' => $vid,
)), 'error');
return FALSE;
}
$result = $response_instance
->getReport();
$response_instance->question->answers[$result['answer_id']] = $result;
$response_instance->question->correct = $result['is_correct'];
return $response_instance->question;
}
function quiz_question_has_been_answered($node) {
$question_instance = _quiz_question_get_instance($node, true);
return $question_instance
->hasBeenAnswered();
}
function quiz_question_quiz_question_score($quiz, $question_nid, $question_vid = NULL, $rid = NULL) {
if (!isset($quiz) && !isset($rid)) {
return quiz_question_get_max_score($question_nid, $question_vid);
}
$dummy_node = new stdClass();
$dummy_node->nid = $question_nid;
$dummy_node->vid = $question_vid;
$question = _quiz_question_get_instance($dummy_node, TRUE);
if (!$question) {
return FALSE;
}
$score = new stdClass();
$score->possible = $question
->getMaximumScore();
$score->question_nid = $question->node->nid;
$score->question_vid = $question->node->vid;
if (isset($rid)) {
$response = _quiz_question_response_get_instance($rid, $question->node);
$score->attained = $score->possible > 0 ? $response
->getScore() : 0;
$score->possible = $response
->getMaxScore();
$score->is_evaluated = $response
->isEvaluated();
}
return $score;
}
function quiz_question_delete_result($rid, $nid, $vid) {
$response = _quiz_question_response_get_instance($rid, NULL, NULL, $nid, $vid);
if ($response) {
$response
->delete();
}
else {
drupal_set_message(t('Unable to delete result. A constructor could not be found for the question-type'), 'error');
}
}
function quiz_question_config($context) {
$q_types = _quiz_question_get_implementations();
$form = array();
foreach ($q_types as $type => $values) {
$function = $type . '_config';
if ($admin_form = $function()) {
$form[$type] = $admin_form;
$form[$type]['#type'] = 'fieldset';
$form[$type]['#title'] = $values['name'];
$form[$type]['#collapsible'] = TRUE;
$form[$type]['#collapsed'] = TRUE;
if (isset($admin_form['#validate'])) {
$form['#validate'][] = $admin_form['#validate'];
}
}
}
return system_settings_form($form);
}
function quiz_question_nodeapi(&$node, $op) {
if ($op == 'delete revision') {
$q_types = _quiz_question_get_implementations();
foreach ($q_types as $q_type => $info) {
if ($node->type == $q_type) {
_quiz_delete_question($node, TRUE);
}
}
}
elseif ($op == 'presave') {
$q_types = _quiz_question_get_implementations();
foreach ($q_types as $q_type => $info) {
if ($node->type == $q_type) {
if (drupal_strlen($node->title) == 0 || !user_access('edit question titles')) {
if (drupal_strlen(strip_tags(check_markup($node->body, $node->format, FALSE))) > variable_get('quiz_autotitle_length', 50)) {
$node->title = drupal_substr(strip_tags(check_markup($node->body, $node->format, FALSE)), 0, variable_get('quiz_autotitle_length', 50) - 3) . '...';
}
else {
$node->title = strip_tags(check_markup($node->body, $node->format, FALSE));
}
}
}
}
}
elseif ($node->type == 'quiz') {
switch ($op) {
case 'insert':
case 'update':
quiz_question_refresh_latest_quizzes($node->nid);
break;
case 'delete':
quiz_question_remove_latest_quizzes($node->nid);
break;
}
}
}
function quiz_question_insert(stdClass $node) {
_quiz_question_get_instance($node)
->save(TRUE);
if (isset($node->quiz_nid) && $node->quiz_nid > 0) {
quiz_question_refresh_latest_quizzes($node->quiz_nid);
}
}
function quiz_question_view($node, $teaser = FALSE, $page = FALSE) {
if ($node->build_mode == NODE_BUILD_SEARCH_INDEX && !variable_get('quiz_index_questions', 1)) {
$node->body = '';
$node->content = array();
$node->title = '';
$node->taxonomy = array();
return $node;
}
$content = '';
if ($teaser) {
$content = _quiz_question_teaser_content($node);
}
elseif (_quiz_is_taking_context()) {
$form_markup = drupal_get_form('quiz_question_answering_form', $node);
}
else {
$question = _quiz_question_get_instance($node, TRUE);
$content = $question
->getNodeView();
}
if (!empty($content)) {
if (isset($node->content)) {
$node->content += $content;
}
else {
$node->content = $content;
}
}
if (!empty($form_markup)) {
$node->content['body']['#value'] = $form_markup;
}
return $node;
}
function quiz_question_update($node) {
_quiz_question_get_instance($node)
->save();
}
function quiz_question_delete(&$node) {
_quiz_delete_question($node, FALSE);
}
function _quiz_delete_question(&$node, $only_this_version) {
_quiz_question_get_instance($node, TRUE)
->delete($only_this_version);
$base_sql = "UPDATE {quiz_node_relationship} SET question_status = " . QUESTION_NEVER;
$select_sql = "SELECT parent_vid FROM {quiz_node_relationship}";
if ($only_this_version) {
$filter_sql = ' WHERE child_nid = %d AND child_vid = %d';
}
else {
$filter_sql = ' WHERE child_nid = %d';
}
$res = db_query($select_sql . $filter_sql, $node->nid, $node->vid);
db_query($base_sql . $filter_sql, $node->nid, $node->vid);
$quizzes_to_update = array();
while ($quizzes_to_update[] = db_result($res)) {
}
quiz_update_max_score_properties($quizzes_to_update);
}
function quiz_question_load($node) {
return _quiz_question_get_instance($node, TRUE)
->getNodeProperties();
}
function _quiz_question_get_instance(&$node, $use_cached = FALSE) {
static $question_instances = array();
$using_dummy_node = FALSE;
if (is_object($node)) {
$vid = isset($node->vid) ? $node->vid : 0;
if ($use_cached && isset($question_instances[$vid])) {
return $question_instances[$vid];
}
if (!isset($node->type)) {
$sql = 'SELECT n.type, r.nid, r.vid, r.title, r.body, p.max_score, r.format
FROM {node_revisions} r
JOIN {node} n
ON r.nid = n.nid
JOIN {quiz_question_properties} p
ON r.vid = p.vid
WHERE r.vid = %d';
$res = db_query($sql, $node->vid);
$node = db_fetch_object($res);
$using_dummy_node = TRUE;
}
$name = $node->type;
}
elseif (is_array($node)) {
$name = $node['type'];
$vid = $node['vid'];
if ($use_cached && isset($question_instances[$vid])) {
return $question_instances[$vid];
}
}
$info = _quiz_question_get_implementations();
$constructor = $info[$name]['question provider'];
if (empty($constructor)) {
return FALSE;
}
$to_return = new $constructor($node);
if (!$to_return instanceof QuizQuestion) {
drupal_set_message(t('The question-type %name isn\'t a QuizQuestion. It needs to extend the QuizQuestion class.', array(
'%name' => $name,
)), 'error', FALSE);
}
if ($using_dummy_node) {
$props = $to_return
->getNodeProperties();
foreach ($props as $key => $value) {
$to_return->node->{$key} = $value;
}
}
$question_instances[$vid] = $to_return;
return $to_return;
}
function _quiz_question_response_get_instance($rid, $question, $answer = NULL, $nid = NULL, $vid = NULL) {
static $quiz_responses = array();
if (is_object($question) && isset($quiz_responses[$rid][$question->vid])) {
$quiz_responses[$rid][$question->vid]
->refreshQuestionNode($question);
if ($quiz_responses[$rid][$question->vid]->is_skipped !== FALSE) {
return $quiz_responses[$rid][$question->vid];
}
}
elseif (isset($quiz_responses[$rid][$vid])) {
if ($quiz_responses[$rid][$vid]->is_skipped !== FALSE) {
return $quiz_responses[$rid][$vid];
}
}
if (!isset($quiz_responses[$rid])) {
$quiz_responses[$rid] = array();
}
if (!isset($question)) {
$dummy_node = new stdClass();
$dummy_node->nid = $nid;
$dummy_node->vid = $vid;
$question = _quiz_question_get_instance($dummy_node, TRUE)->node;
}
if (!$question) {
return FALSE;
}
$info = _quiz_question_get_implementations();
$constructor = $info[$question->type]['response provider'];
$to_return = new $constructor($rid, $question, $answer);
if (!$to_return instanceof QuizQuestionResponse) {
drupal_set_message(t('The question-response isn\'t a QuizQuestionResponse. It needs to extend the QuizQuestionResponse interface, or extend the abstractQuizQuestionResponse class.'), 'error', FALSE);
}
$quiz_responses[$rid][$question->vid] = $to_return;
return $to_return;
}
function _quiz_question_get_implementations($name = NULL, $reset = FALSE) {
static $info = array();
if (empty($info) || $reset) {
$qtypes = module_invoke_all('quiz_question_info');
foreach ($qtypes as $type => $definition) {
if (!empty($definition['question provider'])) {
$info[$type] = $definition;
}
}
}
return $info;
}
function quiz_question_refresh_latest_quizzes($nid) {
global $user;
$sql = 'DELETE FROM {quiz_question_latest_quizzes}
WHERE uid = %d AND quiz_nid = %d';
db_query($sql, $user->uid, $nid);
$sql = 'INSERT INTO {quiz_question_latest_quizzes}
(uid, quiz_nid)
VALUES (%d, %d)';
db_query($sql, $user->uid, $nid);
$sql = 'SELECT COUNT(*)
FROM {quiz_question_latest_quizzes}
WHERE uid = %d';
$count = db_result(db_query($sql, $user->uid));
$n_to_delete = (int) $count - QUIZ_QUESTION_NUM_LATEST;
if ($n_to_delete > 0) {
$sql = 'DELETE FROM {quiz_question_latest_quizzes}
WHERE uid = %d
ORDER BY id LIMIT %d';
db_query($sql, $user->uid, $n_to_delete);
}
}
function quiz_question_remove_latest_quizzes($nid) {
$sql = 'DELETE FROM {quiz_question_latest_quizzes}
WHERE quiz_nid = %d';
db_query($sql, $nid);
}
function quiz_question_get_max_score($nid, $vid) {
$sql = 'SELECT max_score
FROM {quiz_question_properties}
WHERE nid = %d AND vid = %d';
return db_result(db_query($sql, $nid, $vid));
}
function quiz_question_content_extra_fields($type_name) {
$extra = array();
$question_types = array_keys(_quiz_question_get_implementations());
if (in_array($type_name, $question_types)) {
$extra['add_directly'] = array(
'label' => t('Add directly'),
'description' => t('Fieldset for adding a question directly into quizzes'),
'weight' => -3,
);
}
return $extra;
}
function quiz_question_content_extra_fields_alter(&$extras, $type_name) {
$question_types = array_keys(_quiz_question_get_implementations());
if (in_array($type_name, $question_types)) {
$extras['body_field']['weight'] = -15;
}
}
function quiz_question_report_form($question, $showpoints, $showfeedback, $allow_scoring = FALSE) {
$answer = $question->answers[0];
$response_instance = _quiz_question_response_get_instance($answer['result_id'], $question, $answer);
if (!isset($response_instance->question->score_weight)) {
$sql = 'SELECT qnr.max_score
FROM {quiz_node_relationship} qnr
WHERE qnr.child_vid = %d
AND qnr.parent_vid = (
SELECT vid
FROM {quiz_node_results}
WHERE result_id = %d
)';
$qnr_max_score = db_result(db_query($sql, $question->vid, $answer['result_id']));
if ($qnr_max_score === FALSE) {
$qnr_max_score = db_result(db_query('SELECT qt.max_score
FROM {quiz_node_results} qnr
JOIN {quiz_node_results_answers} qnra ON (qnr.result_id = qnra.result_id)
JOIN {quiz_terms} qt ON (qt.vid = qnr.vid AND qt.tid = qnra.tid)
WHERE qnr.result_id = %d AND qnra.question_nid = %d AND qnra.question_vid = %d', $answer['result_id'], $question->nid, $question->vid));
}
if ($qnr_max_score == 0) {
$weight = 0;
}
else {
$weight = $qnr_max_score / $response_instance->question->max_score;
}
$response_instance->question->score_weight = $weight;
}
return $response_instance
->getReportForm($showpoints, $showfeedback, $allow_scoring);
}