📄 questiontype.php
字号:
<?php // $Id: questiontype.php,v 1.41.2.17 2009/02/13 06:11:59 tjhunt Exp $////////////////////// MULTIANSWER /// (Embedded - cloze)///////////////////////// The multianswer question type is special in that it/// depends on a few other question types, i.e./// 'multichoice', 'shortanswer' and 'numerical'./// These question types have got a few special features that/// makes them useable by the 'multianswer' question type////// QUESTION TYPE CLASS ///////////////////** * @package questionbank * @subpackage questiontypes */class embedded_cloze_qtype extends default_questiontype { function name() { return 'multianswer'; } function get_question_options(&$question) { global $QTYPES; // Get relevant data indexed by positionkey from the multianswers table if (!$sequence = get_field('question_multianswer', 'sequence', 'question', $question->id)) { notify(get_string('noquestions','qtype_multianswer',$question->name)); $question->options->questions['1']= ''; return true ; } $wrappedquestions = get_records_list('question', 'id', $sequence, 'id ASC'); // We want an array with question ids as index and the positions as values $sequence = array_flip(explode(',', $sequence)); array_walk($sequence, create_function('&$val', '$val++;')); //If a question is lost, the corresponding index is null // so this null convention is used to test $question->options->questions // before using the values. // first all possible questions from sequence are nulled // then filled with the data if available in $wrappedquestions $nbvaliquestion = 0 ; foreach($sequence as $seq){ $question->options->questions[$seq]= ''; } if (isset($wrappedquestions) && is_array($wrappedquestions)){ foreach ($wrappedquestions as $wrapped) { if (!$QTYPES[$wrapped->qtype]->get_question_options($wrapped)) { notify("Unable to get options for questiontype {$wrapped->qtype} (id={$wrapped->id})"); }else { // for wrapped questions the maxgrade is always equal to the defaultgrade, // there is no entry in the question_instances table for them $wrapped->maxgrade = $wrapped->defaultgrade; $nbvaliquestion++ ; $question->options->questions[$sequence[$wrapped->id]] = clone($wrapped); // ??? Why do we need a clone here? } } } if ($nbvaliquestion == 0 ) { notify(get_string('noquestions','qtype_multianswer',$question->name)); } return true; } function save_question_options($question) { global $QTYPES; $result = new stdClass; // This function needs to be able to handle the case where the existing set of wrapped // questions does not match the new set of wrapped questions so that some need to be // created, some modified and some deleted // Unfortunately the code currently simply overwrites existing ones in sequence. This // will make re-marking after a re-ordering of wrapped questions impossible and // will also create difficulties if questiontype specific tables reference the id. // First we get all the existing wrapped questions if (!$oldwrappedids = get_field('question_multianswer', 'sequence', 'question', $question->id)) { $oldwrappedquestions = array(); } else { $oldwrappedquestions = get_records_list('question', 'id', $oldwrappedids, 'id ASC'); } $sequence = array(); foreach($question->options->questions as $wrapped) { if (!empty($wrapped)){ // if we still have some old wrapped question ids, reuse the next of them if (is_array($oldwrappedquestions) && $oldwrappedquestion = array_shift($oldwrappedquestions)) { $wrapped->id = $oldwrappedquestion->id; if($oldwrappedquestion->qtype != $wrapped->qtype ) { switch ($oldwrappedquestion->qtype) { case 'multichoice': delete_records('question_multichoice', 'question' , $oldwrappedquestion->id ); break; case 'shortanswer': delete_records('question_shortanswer', 'question' , $oldwrappedquestion->id ); break; case 'numerical': delete_records('question_numerical', 'question' , $oldwrappedquestion->id ); break; default: print_error('qtypenotrecognized', 'qtype_multianswer','',$oldwrappedquestion->qtype); $wrapped->id = 0 ; } } }else { $wrapped->id = 0 ; } } $wrapped->name = $question->name; $wrapped->parent = $question->id; $previousid = $wrapped->id ; $wrapped->category = $question->category . ',1'; // save_question strips this extra bit off again. $wrapped = $QTYPES[$wrapped->qtype]->save_question($wrapped, $wrapped, $question->course); $sequence[] = $wrapped->id; if ($previousid != 0 && $previousid != $wrapped->id ) { // for some reasons a new question has been created // so delete the old one delete_question($previousid) ; } } // Delete redundant wrapped questions if(is_array($oldwrappedquestions) && count($oldwrappedquestions)){ foreach ($oldwrappedquestions as $oldwrappedquestion) { delete_question($oldwrappedquestion->id) ; } } if (!empty($sequence)) { $multianswer = new stdClass; $multianswer->question = $question->id; $multianswer->sequence = implode(',', $sequence); if ($oldid = get_field('question_multianswer', 'id', 'question', $question->id)) { $multianswer->id = $oldid; if (!update_record("question_multianswer", $multianswer)) { $result->error = "Could not update cloze question options! " . "(id=$multianswer->id)"; return $result; } } else { if (!insert_record("question_multianswer", $multianswer)) { $result->error = "Could not insert cloze question options!"; return $result; } } } } function save_question($authorizedquestion, $form, $course) { $question = qtype_multianswer_extract_question($form->questiontext); if (isset($authorizedquestion->id)) { $question->id = $authorizedquestion->id; } $question->category = $authorizedquestion->category; $form->course = $course; // To pass the course object to // save_question_options, where it is // needed to call type specific // save_question methods. $form->defaultgrade = $question->defaultgrade; $form->questiontext = $question->questiontext; $form->questiontextformat = 0; $form->options = clone($question->options); unset($question->options); return parent::save_question($question, $form, $course); } function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) { $state->responses = array(); foreach ($question->options->questions as $key => $wrapped) { $state->responses[$key] = ''; } return true; } function restore_session_and_responses(&$question, &$state) { $responses = explode(',', $state->responses['']); $state->responses = array(); foreach ($responses as $response) { $tmp = explode("-", $response); // restore encoded characters $state->responses[$tmp[0]] = str_replace(array(",", "-"), array(",", "-"), $tmp[1]); } return true; } function save_session_and_responses(&$question, &$state) { $responses = $state->responses; // encode - (hyphen) and , (comma) to - because they are used as // delimiters array_walk($responses, create_function('&$val, $key', '$val = str_replace(array(",", "-"), array(",", "-"), $val); $val = "$key-$val";')); $responses = implode(',', $responses); // Set the legacy answer field if (!set_field('question_states', 'answer', $responses, 'id', $state->id)) { return false; } return true; } /** * Deletes question from the question-type specific tables * * @return boolean Success/Failure * @param object $question The question being deleted */ function delete_question($questionid) { delete_records("question_multianswer", "question", $questionid); return true; } function get_correct_responses(&$question, &$state) { global $QTYPES; $responses = array(); foreach($question->options->questions as $key => $wrapped) { if ( !empty($wrapped)){ if ($correct = $QTYPES[$wrapped->qtype]->get_correct_responses($wrapped, $state)) { $responses[$key] = $correct['']; } else { // if there is no correct answer to this subquestion then there // can not be a correct answer to the whole question either, so // we have to return null. return null; } } } return $responses; } function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) { global $QTYPES, $CFG, $USER; $readonly = empty($options->readonly) ? '' : 'readonly="readonly"'; $disabled = empty($options->readonly) ? '' : 'disabled="disabled"'; $formatoptions = new stdClass; $formatoptions->noclean = true; $formatoptions->para = false; $nameprefix = $question->name_prefix; // adding an icon with alt to warn user this is a fill in the gap question // MDL-7497 if (!empty($USER->screenreader)) { echo "<img src=\"$CFG->wwwroot/question/type/$question->qtype/icon.gif\" ". "class=\"icon\" alt=\"".get_string('clozeaid','qtype_multichoice')."\" /> "; } echo '<div class="ablock clearfix">'; // For this question type, we better print the image on top: if ($image = get_question_image($question)) { echo('<img class="qimage" src="' . $image . '" alt="" /><br />'); } $qtextremaining = format_text($question->questiontext, $question->questiontextformat, $formatoptions, $cmoptions->course); $strfeedback = get_string('feedback', 'quiz'); // The regex will recognize text snippets of type {#X} // where the X can be any text not containg } or white-space characters. while (ereg('\{#([^[:space:]}]*)}', $qtextremaining, $regs)) { $qtextsplits = explode($regs[0], $qtextremaining, 2); echo $qtextsplits[0]; echo "<label>"; // MDL-7497 $qtextremaining = $qtextsplits[1]; $positionkey = $regs[1]; if (isset($question->options->questions[$positionkey]) && $question->options->questions[$positionkey] != ''){ $wrapped = &$question->options->questions[$positionkey]; $answers = &$wrapped->options->answers; // $correctanswers = $QTYPES[$wrapped->qtype]->get_correct_responses($wrapped, $state); $inputname = $nameprefix.$positionkey; if (isset($state->responses[$positionkey])) { $response = $state->responses[$positionkey]; } else { $response = null; } // Determine feedback popup if any $popup = ''; $style = ''; $feedbackimg = ''; $feedback = '' ; $correctanswer = ''; $strfeedbackwrapped = $strfeedback; $testedstate = clone($state); if ($correctanswers = $QTYPES[$wrapped->qtype]->get_correct_responses($wrapped, $testedstate)) { if ($options->readonly && $options->correct_responses) { $delimiter = ''; if ($correctanswers) { foreach ($correctanswers as $ca) { switch($wrapped->qtype){ case 'numerical': case 'shortanswer': $correctanswer .= $delimiter.$ca; break ; case 'multichoice': if (isset($answers[$ca])){ $correctanswer .= $delimiter.$answers[$ca]->answer; } break ; } $delimiter = ', '; } } } if ($correctanswer) { $feedback = '<div class="correctness">'; $feedback .= get_string('correctansweris', 'quiz', s($correctanswer, true)); $feedback .= '</div>'; } } if ($options->feedback) { $chosenanswer = null; switch ($wrapped->qtype) { case 'numerical': case 'shortanswer': $testedstate = clone($state); $testedstate->responses[''] = $response; foreach ($answers as $answer) { if($QTYPES[$wrapped->qtype] ->test_response($wrapped, $testedstate, $answer)) { $chosenanswer = clone($answer); break; } } break; case 'multichoice': if (isset($answers[$response])) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -