Skip to content

Commit

Permalink
MDL-27413 qtype_multianswer start work on rendering.
Browse files Browse the repository at this point in the history
Embedded shortanswer questions now work.

Note that overlib is no longer required by the JS.
  • Loading branch information
timhunt committed May 25, 2011
1 parent df55047 commit 42a5b05
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 457 deletions.
12 changes: 12 additions & 0 deletions question/type/multianswer/question.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ class qtype_multianswer_question extends question_graded_automatically {
/** @var array of question_graded_automatically. */
public $subquestions = array();

/**
* @var array place number => insex in the $subquestions array. Places are
* numbered from 1.
*/
public $places;

/**
* @var array of strings, one longer than $places, which is achieved by
* indexing from 0. The bits of question text that go between the subquestions.
*/
public $textfragments;

/**
* Get a question_attempt_step_subquestion_adapter
* @param question_attempt_step $step the step to adapt.
Expand Down
318 changes: 11 additions & 307 deletions question/type/multianswer/questiontype.php
Original file line number Diff line number Diff line change
Expand Up @@ -185,320 +185,24 @@ public function delete_question($questionid, $contextid) {

protected function initialise_question_instance($question, $questiondata) {
parent::initialise_question_instance($question, $questiondata);

$bits = preg_split('/\{#(\d+)\}/', $question->questiontext,
null, PREG_SPLIT_DELIM_CAPTURE);
$question->textfragments[0] = array_shift($bits);
$i = 1;
while (!empty($bits)) {
$question->places[$i] = array_shift($bits);
$question->textfragments[$i] = array_shift($bits);
$i += 1;
}

foreach ($questiondata->options->questions as $key => $subqdata) {
$subqdata->contextid = $questiondata->contextid;
$question->subquestions[$key] = question_bank::make_question($subqdata);
$question->subquestions[$key]->maxmark = $subqdata->defaultmark;
}
}

public function get_html_head_contributions(&$question, &$state) {
global $PAGE;
parent::get_html_head_contributions($question, $state);
$PAGE->requires->js('/lib/overlib/overlib.js', true);
$PAGE->requires->js('/lib/overlib/overlib_cssstyle.js', true);
}

public function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
global $CFG, $USER, $OUTPUT, $PAGE;

$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=\"".$OUTPUT->pix_url('icon', 'qtype_'.$question->qtype)."\" ".
"class=\"icon\" alt=\"".get_string('clozeaid', 'qtype_multichoice')."\" /> ";
}

echo '<div class="ablock clearfix">';

$qtextremaining = format_text($question->questiontext,
$question->questiontextformat, $formatoptions, $cmoptions->course);

$strfeedback = get_string('feedback', 'question');

// The regex will recognize text snippets of type {#X}
// where the X can be any text not containg } or white-space characters.
while (preg_match('~\{#([^[: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;

$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, $state)) {
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', 'question', s($correctanswer));
$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])) {
$chosenanswer = clone($answers[$response]);
}
break;
default:
break;
}

// Set up a default chosenanswer so that all non-empty wrong
// answers are highlighted red
if (empty($chosenanswer) && $response != '') {
$chosenanswer = new stdClass();
$chosenanswer->fraction = 0.0;
}

if (!empty($chosenanswer->feedback)) {
$feedback = s(str_replace(array("\\", "'"), array("\\\\", "\\'"), $feedback.$chosenanswer->feedback));
if ($options->readonly && $options->correct_responses) {
$strfeedbackwrapped = get_string('correctanswerandfeedback', 'qtype_multianswer');
} else {
$strfeedbackwrapped = get_string('feedback', 'question');
}
$popup = " onmouseover=\"return overlib('$feedback', STICKY, MOUSEOFF, CAPTION, '$strfeedbackwrapped', FGCOLOR, '#FFFFFF');\" ".
" onmouseout=\"return nd();\" ";
}

/// Determine style
if ($options->feedback && $response != '') {
$style = 'class = "'.question_get_feedback_class($chosenanswer->fraction).'"';
$feedbackimg = question_get_feedback_image($chosenanswer->fraction);
} else {
$style = '';
$feedbackimg = '';
}
}
if ($feedback != '' && $popup == '') {
$strfeedbackwrapped = get_string('correctanswer', 'qtype_multianswer');
$feedback = s(str_replace(array("\\", "'"), array("\\\\", "\\'"), $feedback));
$popup = " onmouseover=\"return overlib('$feedback', STICKY, MOUSEOFF, CAPTION, '$strfeedbackwrapped', FGCOLOR, '#FFFFFF');\" ".
" onmouseout=\"return nd();\" ";
}

// Print the input control
switch ($wrapped->qtype) {
case 'shortanswer':
case 'numerical':
$size = 1;
foreach ($answers as $answer) {
if (strlen(trim($answer->answer)) > $size) {
$size = strlen(trim($answer->answer));
}
}
if (strlen(trim($response))> $size) {
$size = strlen(trim($response))+1;
}
$size = $size + rand(0, $size*0.15);
$size > 60 ? $size = 60 : $size = $size;
$styleinfo = "size=\"$size\"";

echo "<input $style $readonly $popup name=\"$inputname\"";
echo " type=\"text\" value=\"".s($response)."\" ".$styleinfo." /> ";
if (!empty($feedback) && !empty($USER->screenreader)) {
echo "<img src=\"" . $OUTPUT->pix_url('i/feedback') . "\" alt=\"$feedback\" />";
}
echo $feedbackimg;
break;
case 'multichoice':
if ($wrapped->options->layout == 0) {
$outputoptions = '<option></option>'; // Default empty option
foreach ($answers as $mcanswer) {
$selected = '';
if ($response == $mcanswer->id) {
$selected = ' selected="selected"';
}
$outputoptions .= "<option value=\"$mcanswer->id\"$selected>" .
s($mcanswer->answer) . '</option>';
}
// In the next line, $readonly is invalid HTML, but it works in
// all browsers. $disabled would be valid, but then the JS for
// displaying the feedback does not work. Of course, we should
// not be relying on JS (for accessibility reasons), but that is
// a bigger problem.
//
// The span is used for safari, which does not allow styling of
// selects.
echo "<span $style><select $popup $readonly $style name=\"$inputname\">";
echo $outputoptions;
echo '</select></span>';
if (!empty($feedback) && !empty($USER->screenreader)) {
echo "<img src=\"" . $OUTPUT->pix_url('i/feedback') . "\" alt=\"$feedback\" />";
}
echo $feedbackimg;
} else if ($wrapped->options->layout == 1 || $wrapped->options->layout == 2) {
$ordernumber = 0;
$anss = array();
foreach ($answers as $mcanswer) {
$ordernumber++;
$checked = '';
$chosen = false;
$type = 'type="radio"';
$name = "name=\"{$inputname}\"";
if ($response == $mcanswer->id) {
$checked = 'checked="checked"';
$chosen = true;
}
$a = new stdClass();
$a->id = $question->name_prefix . $mcanswer->id;
$a->class = '';
$a->feedbackimg = '';

// Print the control
$a->control = "<input $readonly id=\"$a->id\" $name $checked $type value=\"$mcanswer->id\" />";
if ($options->correct_responses && $mcanswer->fraction > 0) {
$a->class = question_get_feedback_class(1);
}
if (($options->feedback && $chosen) || $options->correct_responses) {
if ($type == ' type="checkbox" ') {
$a->feedbackimg = question_get_feedback_image($mcanswer->fraction > 0 ? 1 : 0, $chosen && $options->feedback);
} else {
$a->feedbackimg = question_get_feedback_image($mcanswer->fraction, $chosen && $options->feedback);
}
}

// Print the answer text: no automatic numbering
$a->text = format_text($mcanswer->answer, $mcanswer->answerformat, $formatoptions, $cmoptions->course);

// Print feedback if feedback is on
if (($options->feedback || $options->correct_responses) && ($checked)) { //|| $options->readonly
$a->feedback = format_text($mcanswer->feedback, $mcanswer->feedbackformat, $formatoptions, $cmoptions->course);
} else {
$a->feedback = '';
}

$anss[] = clone($a);
}
if ($wrapped->options->layout == 1) {
?><table class="answer"><?php
$row = 1;
foreach ($anss as $answer) {
?><tr class="<?php echo 'r'.$row = $row ? 0 : 1; ?>">
<td class="c0 control">
<?php echo $answer->control; ?>
</td>
<td class="c1 text <?php echo $answer->class ?>">
<label for="<?php echo $answer->id ?>">
<?php echo $answer->text; ?>
<?php echo $answer->feedbackimg; ?>
</label>
</td>
<td class="c0 feedback">
<?php echo $answer->feedback; ?>
</td>
</tr><?php
}
?></table><?php
} else if ($wrapped->options->layout == 2) {
?><table class="answer">
<tr class="<?php echo 'r'.$row = $row ? 0 : 1; ?>">
<?php $row = 1;
foreach ($anss as $answer) { ?>
<td class="c0 control">
<?php echo $answer->control; ?>
</td>
<td class="c1 text <?php echo $answer->class ?>">
<label for="<?php echo $answer->id ?>">
<?php echo $answer->text; ?>
<?php echo $answer->feedbackimg; ?>
</label>
</td>
<td class="c0 feedback">
<?php echo $answer->feedback; ?>
</td><?php
}
?>
</tr>
</table><?php
}

} else {
echo "no valid layout";
}

break;
default:
$a = new stdClass();
$a->type = $wrapped->qtype;
$a->sub = $positionkey;
print_error('unknownquestiontypeofsubquestion', 'qtype_multianswer', '', $a);
break;
}
echo "</label>"; // MDL-7497
} else {
if (! isset($question->options->questions[$positionkey])) {
echo $regs[0]."</label>";
} else {
echo '</label><div class="error" >'.get_string('questionnotfound', 'qtype_multianswer', $positionkey).'</div>';
}
}
}

// Print the final piece of question text:
echo $qtextremaining;
$this->print_question_submit_buttons($question, $state, $cmoptions, $options);
echo '</div>';
}

public function get_random_guess_score($questiondata) {
$fractionsum = 0;
$fractionmax = 0;
Expand Down
Loading

0 comments on commit 42a5b05

Please sign in to comment.