From dcedbb0e257cc48e1076f79b8ad8ce270d5eb44f Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Thu, 26 May 2011 16:42:54 +0100 Subject: [PATCH] MDL-27413 qtype_multianswer now works for all embedded types, I think. --- question/type/multianswer/question.php | 2 +- question/type/multianswer/questiontype.php | 2 + question/type/multianswer/renderer.php | 385 +++++------------- .../type/multianswer/simpletest/helper.php | 2 +- .../multianswer/simpletest/testquestion.php | 4 +- question/type/multianswer/styles.css | 17 + 6 files changed, 115 insertions(+), 297 deletions(-) diff --git a/question/type/multianswer/question.php b/question/type/multianswer/question.php index 1f8a572e5a667..d8f8c898ff71b 100644 --- a/question/type/multianswer/question.php +++ b/question/type/multianswer/question.php @@ -118,7 +118,7 @@ public function get_expected_data() { $substep = $this->get_substep(null, $i); foreach ($subq->get_expected_data() as $name => $type) { if ($subq->qtype->name() == 'multichoice' && - $subq->layout = qtype_multichoice_base::LAYOUT_DROPDOWN) { + $subq->layout == qtype_multichoice_base::LAYOUT_DROPDOWN) { // Hack or MC inline does not work. $expected[$substep->add_prefix($name)] = PARAM_RAW; } else { diff --git a/question/type/multianswer/questiontype.php b/question/type/multianswer/questiontype.php index 0994d622af8a9..dbd0af6ea9144 100644 --- a/question/type/multianswer/questiontype.php +++ b/question/type/multianswer/questiontype.php @@ -26,6 +26,8 @@ defined('MOODLE_INTERNAL') || die(); +require_once($CFG->dirroot . '/question/type/multichoice/question.php'); + /** * The multi-answer question type class. diff --git a/question/type/multianswer/renderer.php b/question/type/multianswer/renderer.php index 09c79cd333bc8..2d96dcf60042d 100644 --- a/question/type/multianswer/renderer.php +++ b/question/type/multianswer/renderer.php @@ -227,7 +227,7 @@ public function subquestion(question_attempt $qa, question_display_options $opti /** * Render an embedded multiple-choice question that is displayed as a select menu. * - * @copyright 2010 Pierre Pichet + * @copyright 2011 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class qtype_multianswer_multichoice_inline_renderer @@ -285,346 +285,145 @@ public function subquestion(question_attempt $qa, question_display_options $opti return $output; } - - public function formulation_and_controls(question_attempt $qa, - question_display_options $options) { - $questiontot = $qa->get_question(); - $subquestion = $questiontot->subquestions[$qa->subquestionindex]; - $answers = $subquestion->answers; - $correctanswers = $subquestion->get_correct_response(); - foreach ($correctanswers as $key => $value) { - $correct = $value; - } - $order = $subquestion->get_order($qa); - $response = $this->get_response($qa); - $currentanswer = $response; - $answername = $subquestion->fieldid.'answer'; - $inputname = $qa->get_qt_field_name($answername); - $inputattributes = array( - 'type' => $this->get_input_type(), - 'name' => $inputname, - ); - - if ($options->readonly) { - $inputattributes['disabled'] = 'disabled'; - $readonly = 'disabled ="disabled"'; - } - $choices = array(); - $popup = ''; - $feedback = ''; - $answer = ''; - $classes = 'control'; - $feedbackimage = ''; - $fraction = 0; - $chosen = 0; - - foreach ($order as $value => $ansid) { - $mcanswer = $subquestion->answers[$ansid]; - $choices[$value] = strip_tags($mcanswer->answer); - $selected = ''; - $isselected = false; - if ( $response != '') { - $isselected = $this->is_choice_selected($response, $value); - } - if ($isselected) { - $chosen = $value; - $answer = $mcanswer; - $fraction = $mcanswer->fraction; - $selected = ' selected="selected"'; - } - } - if ($options->feedback) { - if ($answer) { - $classes .= ' ' . question_get_feedback_class($fraction); - $feedbackimage = question_get_feedback_image($answer->fraction); - if ($answer->feedback) { - $feedback .= $subquestion->format_text($answer->feedback); - } - } else { - $classes .= ' ' . question_get_feedback_class(0); - $feedbackimage = question_get_feedback_image(0); - } - } - // determine popup - // answer feedback (specific)i.e if options->feedback already set - // subquestion status correctness or Finished validator if correctness - // Correct response - // marks - $strfeedbackwrapped = 'Response Status'; - if ($options->feedback) { - $feedback = get_string('feedback', 'quiz').":".$feedback."
"; - - if ($options->correctness) { - if (!$answer) { - $state = $qa->get_state(); - $state = question_state::$invalid; - $strfeedbackwrapped .= ":".$state->default_string().""; - $feedback = "".get_string('singleanswer', 'quiz') ."
"; - } else { - $state = $qa->get_state(); - $state = question_state::graded_state_for_fraction($fraction); - $strfeedbackwrapped .= ":".$state->default_string(); - } - } - - if ($options->correctresponse) { - $feedback .= $this->correct_response($qa)."
"; - } - if ($options->marks) { - $subgrade= $fraction * $subquestion->defaultmark; - $feedback .= $questiontot->mark_summary($options, $subquestion->defaultmark , $subgrade); - } - - $feedback .= ''; - } - - if ($options->feedback) { - // need to replace ' and " as they could break the popup string - // as the text comes from database, slashes have been removed - // addslashes will not work as it keeps the " - // HTML ' for ' does not work - $feedback = str_replace("'", "\'", $feedback); - $feedback = str_replace('"', "\'", $feedback); - $strfeedbackwrapped = str_replace("'", "\'", $strfeedbackwrapped); - $strfeedbackwrapped = str_replace('"', "\'", $strfeedbackwrapped); - - $popup = " onmouseover=\"return overlib('$feedback', STICKY, MOUSEOFF, CAPTION, '$strfeedbackwrapped', FGCOLOR, '#FFFFFF');\" ". - " onmouseout=\"return nd();\" "; - } - $result = ''; - - $result .= ""; - $result .= html_writer::start_tag('span', array('class' => $classes), ''); - - $result .= choose_from_menu($choices, $inputname, $chosen, - ' ', '', '', true, $options->readonly) . $feedbackimage; - $result .= html_writer::end_tag('span'); - $result .= html_writer::end_tag('span'); - - return $result; - } - - protected function format_choices($question) { - $choices = array(); - foreach ($question->get_choice_order() as $key => $choiceid) { - $choices[$key] = strip_tags($question->format_text($question->choices[$choiceid])); - } - return $choices; - } } /** - * As multianswer have specific display requirements for multichoice display - * a new class was defined although largely following the multichoice one + * Render an embedded multiple-choice question vertically, like for a normal + * multiple-choice question. * * @copyright 2010 Pierre Pichet * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -abstract class qtype_multianswer_multichoice_renderer_base extends qtype_renderer { - abstract protected function get_input_type(); +class qtype_multianswer_multichoice_vertical_renderer extends qtype_multianswer_subq_renderer_base { - abstract protected function get_input_name(question_attempt $qa, $value); - - abstract protected function get_input_value($value); - - abstract protected function get_input_id(question_attempt $qa, $value); - - abstract protected function is_choice_selected($response, $value); - - abstract protected function is_right(question_answer $ans); - - abstract protected function get_response(question_attempt $qa); - - public function specific_feedback(question_attempt $qa) { - return ''; - } + public function subquestion(question_attempt $qa, question_display_options $options, + $index, question_graded_automatically $subq) { - public function formulation_and_controls(question_attempt $qa, - question_display_options $options) { + $fieldprefix = 'sub' . $index . '_'; + $fieldname = $fieldprefix . 'answer'; + $response = $qa->get_last_qt_var($fieldname); - $questiontot = $qa->get_question(); - $subquestion = $questiontot->subquestions[$qa->subquestionindex]; - $order = $subquestion->get_order($qa); - $response = $this->get_response($qa); $inputattributes = array( - 'type' => $this->get_input_type(), + 'type' => 'radio', + 'name' => $qa->get_qt_field_name($fieldname), ); - if ($options->readonly) { $inputattributes['disabled'] = 'disabled'; } - $radiobuttons = array(); - $feedbackimg = array(); - $feedback = array(); - $classes = array(); - $totfraction = 0; - $nullresponse = true; - foreach ($order as $value => $ansid) { - $ans = $subquestion->answers[$ansid]; - $inputattributes['name'] = $this->get_input_name($qa, $value); - $inputattributes['value'] = $this->get_input_value($value); - $inputattributes['id'] = $this->get_input_id($qa, $value); - if ($subquestion->single) { - $isselected = $this->is_choice_selected($response, $value); - } else { - $isselected = $this->is_choice_selected($response, $value); - } + + $result = $this->all_choices_wrapper_start(); + $fraction = null; + foreach ($subq->get_order($qa) as $value => $ansid) { + $ans = $subq->answers[$ansid]; + + $inputattributes['value'] = $value; + $inputattributes['id'] = $inputattributes['name'] . $value; + + $isselected = $subq->is_choice_selected($response, $value); if ($isselected) { $inputattributes['checked'] = 'checked'; - $totfraction += $ans->fraction; - $nullresponse = false; + $fraction = $ans->fraction; } else { unset($inputattributes['checked']); } - $radiobuttons[] = html_writer::empty_tag('input', $inputattributes) . - html_writer::tag('label', $subquestion->format_text($ans->answer), array('for' => $inputattributes['id'])); - if (($options->feedback || $options->correctresponse) && $response !== -1) { - $feedbackimg[] = question_get_feedback_image($this->is_right($ans), $isselected && $options->feedback); - } else { - $feedbackimg[] = ''; - } - if (($options->feedback || $options->correctresponse) && $isselected) { - $feedback[] = $subquestion->format_text($ans->feedback); - } else { - $feedback[] = ''; - } $class = 'r' . ($value % 2); - if ($options->correctresponse && $ans->fraction > 0) { - $class .= ' ' . question_get_feedback_class($ans->fraction); - } - $classes[] = $class; - } - - $result = ''; - - $answername = 'answer'; - if ($subquestion->layout == 1) { - $result .= html_writer::start_tag('div', array('class' => 'ablock')); - - $result .= html_writer::start_tag('table', array('class' => $answername)); - foreach ($radiobuttons as $key => $radio) { - $result .= html_writer::start_tag('tr', array('class' => $answername)); - $result .= html_writer::start_tag('td', array('class' => $answername)); - $result .= html_writer::tag('span', $radio . $feedbackimg[$key] . $feedback[$key], array('class' => $classes[$key])) . "\n"; - $result .= html_writer::end_tag('td'); - $result .= html_writer::end_tag('tr'); + if ($options->correctness && $isselected) { + $feedbackimg = $this->feedback_image($ans->fraction); + $class .= ' ' . $this->feedback_class($ans->fraction); + } else { + $feedbackimg = ''; } - $result .= html_writer::end_tag('table'); // answer - $result .= html_writer::end_tag('div'); // ablock - } - if ($subquestion->layout == 2) { - $result .= html_writer::start_tag('div', array('class' => 'ablock')); - $result .= html_writer::start_tag('table', array('class' => $answername)); - $result .= html_writer::start_tag('tr', array('class' => $answername)); - foreach ($radiobuttons as $key => $radio) { - $result .= html_writer::start_tag('td', array('class' => $answername)); - $result .= html_writer::tag('span', $radio . $feedbackimg[$key] . $feedback[$key] - , array('class' => $classes[$key])) . "\n"; - $result .= html_writer::end_tag('td'); + $result .= $this->choice_wrapper_start($class); + $result .= html_writer::empty_tag('input', $inputattributes); + $result .= html_writer::tag('label', $subq->format_text($ans->answer, + $ans->answerformat, $qa, 'question', 'answer', $ansid), + array('for' => $inputattributes['id'])); + $result .= $feedbackimg; + + if ($options->feedback && $isselected && trim($ans->feedback)) { + $result .= html_writer::tag('div', + $subq->format_text($ans->feedback, $ans->feedbackformat, + $qa, 'question', 'answerfeedback', $ansid), + array('class' => 'specificfeedback')); } - $result .= html_writer::end_tag('tr'); - $result .= html_writer::end_tag('table'); // answer - $result .= html_writer::end_tag('div'); // ablock + $result .= $this->choice_wrapper_end(); } - if ($options->feedback) { - $result .= html_writer::start_tag('div', array('class' => 'outcome')); - - if ($options->correctness) { - if ( $nullresponse) { - $state = $qa->get_state(); - $state = question_state::$invalid; - $result1 = $state->default_string(); - $result .= html_writer::nonempty_tag('div', $result1, - array('class' => 'validationerror')); - $result1 = ($subquestion->single) ? get_string('singleanswer', 'quiz') : get_string('multipleanswers', 'quiz'); - $result .= html_writer::nonempty_tag('div', $result1, - array('class' => 'validationerror')); - } else { - $state = $qa->get_state(); - $state = question_state::graded_state_for_fraction($totfraction); - $result1 = $state->default_string(); - $result .= html_writer::nonempty_tag('div', $result1, - array('class' => 'outcome')); - } - } - - if ($options->correctresponse) { - $result1 = $this->correct_response($qa); - $result .= html_writer::nonempty_tag('div', $result1, array('class' => 'outcome')); - } - if ($options->marks) { - $subgrade= $totfraction * $subquestion->defaultmark; - $result .= $questiontot->mark_summary($options, $subquestion->defaultmark , $subgrade); - } + $result .= $this->all_choices_wrapper_end(); - if ($qa->get_state() == question_state::$invalid) { - $result .= html_writer::nonempty_tag('div', array('class' => 'validationerror'), - $subquestion->get_validation_error($qa->get_last_qt_data())); - } - $result .= html_writer::end_tag('div'); + if ($options->feedback && $options->marks >= question_display_options::MARK_AND_MAX && + $subq->maxmark > 0) { + $a = new stdClass(); + $a->mark = format_float($fraction * $subq->maxmark, $options->markdp); + $a->max = format_float($subq->maxmark, $options->markdp); + $result .= html_writer::tag('div', get_string('markoutofmax', 'question', $a), + array('class' => 'outcome')); } - return $result; - } - - -} - -class qtype_multianswer_multichoice_single_renderer extends qtype_multianswer_multichoice_renderer_base { - protected function get_input_type() { - return 'radio'; + return $result; } - protected function is_choice_selected($response, $value) { - return $response == $value; + /** + * @param string $class class attribute value. + * @return string HTML to go before each choice. + */ + protected function choice_wrapper_start($class) { + return html_writer::start_tag('div', array('class' => $class)); } - protected function is_right(question_answer $ans) { - return $ans->fraction > 0.9999999; + /** + * @return string HTML to go after each choice. + */ + protected function choice_wrapper_end() { + return html_writer::end_tag('div'); } - protected function get_input_name(question_attempt $qa, $value) { - $questiontot = $qa->get_question(); - $subquestion = $questiontot->subquestions[$qa->subquestionindex]; - $answername = $subquestion->fieldid.'answer'; - return $qa->get_qt_field_name($answername); + /** + * @return string HTML to go before all the choices. + */ + protected function all_choices_wrapper_start() { + return html_writer::start_tag('div', array('class' => 'answer')); } - protected function get_input_value($value) { - return $value; + /** + * @return string HTML to go after all the choices. + */ + protected function all_choices_wrapper_end() { + return html_writer::end_tag('div'); } +} - protected function get_input_id(question_attempt $qa, $value) { - $questiontot = $qa->get_question(); - $subquestion = $questiontot->subquestions[$qa->subquestionindex]; - $answername = $subquestion->fieldid.'answer'; - return $qa->get_qt_field_name($answername); - } - protected function get_response(question_attempt $qa) { - $questiontot = $qa->get_question(); - $subquestion = $questiontot->subquestions[$qa->subquestionindex]; - return $qa->get_last_qt_var($subquestion->fieldid.'answer', -1); +/** + * Render an embedded multiple-choice question vertically, like for a normal + * multiple-choice question. + * + * @copyright 2010 Pierre Pichet + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class qtype_multianswer_multichoice_horizontal_renderer + extends qtype_multianswer_multichoice_vertical_renderer { + protected function choice_wrapper_start($class) { + return html_writer::start_tag('td', array('class' => $class)); } - public function correct_response(question_attempt $qa) { - $questiontot = $qa->get_question(); - $subquestion = $questiontot->subquestions[$qa->subquestionindex]; + protected function choice_wrapper_end() { + return html_writer::end_tag('td'); + } - foreach ($subquestion->answers as $ans) { - if ($ans->fraction > 0.9999999) { - return get_string('correctansweris', 'qtype_multichoice', - $subquestion->format_text($ans->answer)); - } - } + protected function all_choices_wrapper_start() { + return html_writer::start_tag('table', array('class' => 'answer')) . + html_writer::start_tag('tbody') . html_writer::start_tag('tr'); + } - return ''; + protected function all_choices_wrapper_end() { + return html_writer::end_tag('tr') . html_writer::end_tag('tbody') . + html_writer::end_tag('table'); } } diff --git a/question/type/multianswer/simpletest/helper.php b/question/type/multianswer/simpletest/helper.php index 4541a61b87d64..bd828749b5910 100644 --- a/question/type/multianswer/simpletest/helper.php +++ b/question/type/multianswer/simpletest/helper.php @@ -87,7 +87,7 @@ public function make_multianswer_question_twosubq() { $mc->shuffleanswers = 1; $mc->answernumbering = 'none'; - $mc->layout = 1; + $mc->layout = qtype_multichoice_base::LAYOUT_DROPDOWN; $mc->answers = array( 13 => new question_answer(13, 'Bow-wow', 0, diff --git a/question/type/multianswer/simpletest/testquestion.php b/question/type/multianswer/simpletest/testquestion.php index a22971b0de29b..0550b7769225e 100644 --- a/question/type/multianswer/simpletest/testquestion.php +++ b/question/type/multianswer/simpletest/testquestion.php @@ -37,8 +37,8 @@ class qtype_multianswer_question_test extends UnitTestCase { public function test_get_expected_data() { $question = test_question_maker::make_question('multianswer'); - $this->assertEqual(array('sub1_answer' => PARAM_RAW_TRIMMED, 'sub2_answer' => PARAM_RAW), - $question->get_expected_data()); + $this->assertEqual(array('sub1_answer' => PARAM_RAW_TRIMMED, + 'sub2_answer' => PARAM_RAW), $question->get_expected_data()); } public function test_is_complete_response() { diff --git a/question/type/multianswer/styles.css b/question/type/multianswer/styles.css index 0bc1b7ed8d1d1..fbeb0fe006541 100644 --- a/question/type/multianswer/styles.css +++ b/question/type/multianswer/styles.css @@ -5,3 +5,20 @@ margin-top: 1em; box-shadow: 0.5em 0.5em 1em #000000; } +.que.multianswer .answer .specificfeedback { + display: inline; + padding: 0 0.7em; + background: #FFF3BF; +} +.que.multianswer .answer .specificfeedback * { + display: inline; + background: #FFF3BF; +} +.que.multianswer .answer div.r0, +.que.multianswer .answer div.r1 { + padding: 0.3em; +} +.que.multianswer table.answer { + margin-bottom: 0; + width: 100%; +}