Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/question/type/multianswer/renderer.php
Go to the documentation of this file.
00001 <?php
00002 // This file is part of Moodle - http://moodle.org/
00003 //
00004 // Moodle is free software: you can redistribute it and/or modify
00005 // it under the terms of the GNU General Public License as published by
00006 // the Free Software Foundation, either version 3 of the License, or
00007 // (at your option) any later version.
00008 //
00009 // Moodle is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 // GNU General Public License for more details.
00013 //
00014 // You should have received a copy of the GNU General Public License
00015 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
00016 
00028 require_once($CFG->dirroot . '/question/type/shortanswer/renderer.php');
00029 
00030 
00041 class qtype_multianswer_renderer extends qtype_renderer {
00042 
00043     public function formulation_and_controls(question_attempt $qa,
00044             question_display_options $options) {
00045         $question = $qa->get_question();
00046 
00047         $output = '';
00048         foreach ($question->textfragments as $i => $fragment) {
00049             if ($i > 0) {
00050                 $index = $question->places[$i];
00051                 $output .= $this->subquestion($qa, $options, $index,
00052                         $question->subquestions[$index]);
00053             }
00054             $output .= $question->format_text($fragment, $question->questiontextformat,
00055                     $qa, 'question', 'questiontext', $question->id);
00056         }
00057 
00058         $this->page->requires->js_init_call('M.qtype_multianswer.init',
00059                 array('#q' . $qa->get_slot()), false, array(
00060                     'name'     => 'qtype_multianswer',
00061                     'fullpath' => '/question/type/multianswer/module.js',
00062                     'requires' => array('base', 'node', 'event', 'overlay'),
00063                 ));
00064 
00065         return $output;
00066     }
00067 
00068     public function subquestion(question_attempt $qa,
00069             question_display_options $options, $index, question_graded_automatically $subq) {
00070 
00071         $subtype = $subq->qtype->name();
00072         if ($subtype == 'numerical' || $subtype == 'shortanswer') {
00073             $subrenderer = 'textfield';
00074         } else if ($subtype == 'multichoice') {
00075             if ($subq->layout == qtype_multichoice_base::LAYOUT_DROPDOWN) {
00076                 $subrenderer = 'multichoice_inline';
00077             } else if ($subq->layout == qtype_multichoice_base::LAYOUT_HORIZONTAL) {
00078                 $subrenderer = 'multichoice_horizontal';
00079             } else {
00080                 $subrenderer = 'multichoice_vertical';
00081             }
00082         } else {
00083             throw new coding_exception('Unexpected subquestion type.', $subq);
00084         }
00085         $renderer = $this->page->get_renderer('qtype_multianswer', $subrenderer);
00086         return $renderer->subquestion($qa, $options, $index, $subq);
00087     }
00088 
00089     public function correct_response(question_attempt $qa) {
00090         return '';
00091     }
00092 }
00093 
00094 
00102 abstract class qtype_multianswer_subq_renderer_base extends qtype_renderer {
00103 
00104     abstract public function subquestion(question_attempt $qa,
00105             question_display_options $options, $index,
00106             question_graded_automatically $subq);
00107 
00118     protected function feedback_popup(question_graded_automatically $subq,
00119             $fraction, $feedbacktext, $rightanswer, question_display_options $options) {
00120 
00121         if (!$options->feedback) {
00122             return '';
00123         }
00124 
00125         $feedback = array();
00126         if ($options->correctness) {
00127             if (is_null($fraction)) {
00128                 $state = question_state::$gaveup;
00129             } else {
00130                 $state = question_state::graded_state_for_fraction($fraction);
00131             }
00132             $feedback[] = $state->default_string(true);
00133         }
00134 
00135         if ($feedbacktext) { // Note $options->feedback is already checked above.
00136             $feedback[] = $feedbacktext;
00137         }
00138 
00139         if ($options->rightanswer) {
00140             $feedback[] = get_string('correctansweris', 'qtype_shortanswer', $rightanswer);
00141         }
00142 
00143         $subfraction = '';
00144         if ($options->marks >= question_display_options::MARK_AND_MAX && $subq->maxmark > 0) {
00145             $a = new stdClass();
00146             $a->mark = format_float($fraction * $subq->maxmark, $options->markdp);
00147             $a->max =  format_float($subq->maxmark, $options->markdp);
00148             $feedback[] = get_string('markoutofmax', 'question', $a);
00149         }
00150 
00151         if (!$feedback) {
00152             return '';
00153         }
00154 
00155         return html_writer::tag('span', implode('<br />', $feedback),
00156                 array('class' => 'feedbackspan accesshide'));
00157     }
00158 }
00159 
00160 
00168 class qtype_multianswer_textfield_renderer extends qtype_multianswer_subq_renderer_base {
00169 
00170     public function subquestion(question_attempt $qa, question_display_options $options,
00171             $index, question_graded_automatically $subq) {
00172 
00173         $fieldprefix = 'sub' . $index . '_';
00174         $fieldname = $fieldprefix . 'answer';
00175 
00176         $response = $qa->get_last_qt_var($fieldname);
00177         if ($subq->qtype->name() == 'shortanswer') {
00178             $matchinganswer = $subq->get_matching_answer(array('answer' => $response));
00179         } else if ($subq->qtype->name() == 'numerical') {
00180             $matchinganswer = $subq->get_matching_answer($response, 1);
00181         } else {
00182             $matchinganswer = $subq->get_matching_answer($response);
00183         }
00184 
00185         if (!$matchinganswer) {
00186             $matchinganswer = new question_answer(0, '', null, '', FORMAT_HTML);
00187         }
00188 
00189         // Work out a good input field size.
00190         $size = max(1, strlen(trim($response)) + 1);
00191         foreach ($subq->answers as $ans) {
00192             $size = max($size, strlen(trim($ans->answer)));
00193         }
00194         $size = min(60, round($size + rand(0, $size*0.15)));
00195         // The rand bit is to make guessing harder
00196 
00197         $inputattributes = array(
00198             'type' => 'text',
00199             'name' => $qa->get_qt_field_name($fieldname),
00200             'value' => $response,
00201             'id' => $qa->get_qt_field_name($fieldname),
00202             'size' => $size,
00203         );
00204         if ($options->readonly) {
00205             $inputattributes['readonly'] = 'readonly';
00206         }
00207 
00208         $feedbackimg = '';
00209         if ($options->correctness) {
00210             $inputattributes['class'] = $this->feedback_class($matchinganswer->fraction);
00211             $feedbackimg = $this->feedback_image($matchinganswer->fraction);
00212         }
00213 
00214         if ($subq->qtype->name() == 'shortanswer') {
00215             $correctanswer = $subq->get_matching_answer($subq->get_correct_response());
00216         } else {
00217             $correctanswer = $subq->get_correct_answer();
00218         }
00219 
00220         $feedbackpopup = $this->feedback_popup($subq, $matchinganswer->fraction,
00221                 $subq->format_text($matchinganswer->feedback, $matchinganswer->feedbackformat,
00222                         $qa, 'question', 'answerfeedback', $matchinganswer->id),
00223                 s($correctanswer->answer), $options);
00224 
00225         $output = '';
00226         $output .= html_writer::start_tag('label', array('class' => 'subq'));
00227         $output .= html_writer::empty_tag('input', $inputattributes);
00228         $output .= $feedbackimg;
00229         $output .= $feedbackpopup;
00230         $output .= html_writer::end_tag('label');
00231 
00232         return $output;
00233     }
00234 }
00235 
00236 
00243 class qtype_multianswer_multichoice_inline_renderer
00244         extends qtype_multianswer_subq_renderer_base {
00245 
00246     public function subquestion(question_attempt $qa, question_display_options $options,
00247             $index, question_graded_automatically $subq) {
00248 
00249         $fieldprefix = 'sub' . $index . '_';
00250         $fieldname = $fieldprefix . 'answer';
00251 
00252         $response = $qa->get_last_qt_var($fieldname);
00253         $choices = array();
00254         $matchinganswer = new question_answer(0, '', null, '', FORMAT_HTML);
00255         $rightanswer = null;
00256         foreach ($subq->get_order($qa) as $value => $ansid) {
00257             $ans = $subq->answers[$ansid];
00258             $choices[$value] = $subq->format_text($ans->answer, $ans->answerformat,
00259                     $qa, 'question', 'answer', $ansid);
00260             if ($subq->is_choice_selected($response, $value)) {
00261                 $matchinganswer = $ans;
00262             }
00263         }
00264 
00265         $inputattributes = array(
00266             'id' => $qa->get_qt_field_name($fieldname),
00267         );
00268         if ($options->readonly) {
00269             $inputattributes['disabled'] = 'disabled';
00270         }
00271 
00272         $feedbackimg = '';
00273         if ($options->correctness) {
00274             $inputattributes['class'] = $this->feedback_class($matchinganswer->fraction);
00275             $feedbackimg = $this->feedback_image($matchinganswer->fraction);
00276         }
00277 
00278         $select = html_writer::select($choices, $qa->get_qt_field_name($fieldname),
00279                 $response, array('' => ''), $inputattributes);
00280 
00281         $order = $subq->get_order($qa);
00282         $rightanswer = $subq->answers[$order[reset($subq->get_correct_response())]];
00283         $feedbackpopup = $this->feedback_popup($subq, $matchinganswer->fraction,
00284                 $subq->format_text($matchinganswer->feedback, $matchinganswer->feedbackformat,
00285                         $qa, 'question', 'answerfeedback', $matchinganswer->id),
00286                 $subq->format_text($rightanswer->answer, $rightanswer->answerformat,
00287                         $qa, 'question', 'answer', $rightanswer->id), $options);
00288 
00289         $output = '';
00290         $output .= html_writer::start_tag('label', array('class' => 'subq'));
00291         $output .= $select;
00292         $output .= $feedbackimg;
00293         $output .= $feedbackpopup;
00294         $output .= html_writer::end_tag('label');
00295 
00296         return $output;
00297     }
00298 }
00299 
00300 
00308 class qtype_multianswer_multichoice_vertical_renderer extends qtype_multianswer_subq_renderer_base {
00309 
00310     public function subquestion(question_attempt $qa, question_display_options $options,
00311             $index, question_graded_automatically $subq) {
00312 
00313         $fieldprefix = 'sub' . $index . '_';
00314         $fieldname = $fieldprefix . 'answer';
00315         $response = $qa->get_last_qt_var($fieldname);
00316 
00317         $inputattributes = array(
00318             'type' => 'radio',
00319             'name' => $qa->get_qt_field_name($fieldname),
00320         );
00321         if ($options->readonly) {
00322             $inputattributes['disabled'] = 'disabled';
00323         }
00324 
00325         $result = $this->all_choices_wrapper_start();
00326         $fraction = null;
00327         foreach ($subq->get_order($qa) as $value => $ansid) {
00328             $ans = $subq->answers[$ansid];
00329 
00330             $inputattributes['value'] = $value;
00331             $inputattributes['id'] = $inputattributes['name'] . $value;
00332 
00333             $isselected = $subq->is_choice_selected($response, $value);
00334             if ($isselected) {
00335                 $inputattributes['checked'] = 'checked';
00336                 $fraction = $ans->fraction;
00337             } else {
00338                 unset($inputattributes['checked']);
00339             }
00340 
00341             $class = 'r' . ($value % 2);
00342             if ($options->correctness && $isselected) {
00343                 $feedbackimg = $this->feedback_image($ans->fraction);
00344                 $class .= ' ' . $this->feedback_class($ans->fraction);
00345             } else {
00346                 $feedbackimg = '';
00347             }
00348 
00349             $result .= $this->choice_wrapper_start($class);
00350             $result .= html_writer::empty_tag('input', $inputattributes);
00351             $result .= html_writer::tag('label', $subq->format_text($ans->answer,
00352                     $ans->answerformat, $qa, 'question', 'answer', $ansid),
00353                     array('for' => $inputattributes['id']));
00354             $result .= $feedbackimg;
00355 
00356             if ($options->feedback && $isselected && trim($ans->feedback)) {
00357                 $result .= html_writer::tag('div',
00358                         $subq->format_text($ans->feedback, $ans->feedbackformat,
00359                                 $qa, 'question', 'answerfeedback', $ansid),
00360                         array('class' => 'specificfeedback'));
00361             }
00362 
00363             $result .= $this->choice_wrapper_end();
00364         }
00365 
00366         $result .= $this->all_choices_wrapper_end();
00367 
00368         if ($options->feedback && $options->marks >= question_display_options::MARK_AND_MAX &&
00369                 $subq->maxmark > 0) {
00370             $a = new stdClass();
00371             $a->mark = format_float($fraction * $subq->maxmark, $options->markdp);
00372             $a->max =  format_float($subq->maxmark, $options->markdp);
00373 
00374             $result .= html_writer::tag('div', get_string('markoutofmax', 'question', $a),
00375                     array('class' => 'outcome'));
00376         }
00377 
00378         return $result;
00379     }
00380 
00385     protected function choice_wrapper_start($class) {
00386         return html_writer::start_tag('div', array('class' => $class));
00387     }
00388 
00392     protected function choice_wrapper_end() {
00393         return html_writer::end_tag('div');
00394     }
00395 
00399     protected function all_choices_wrapper_start() {
00400         return html_writer::start_tag('div', array('class' => 'answer'));
00401     }
00402 
00406     protected function all_choices_wrapper_end() {
00407         return html_writer::end_tag('div');
00408     }
00409 }
00410 
00411 
00419 class qtype_multianswer_multichoice_horizontal_renderer
00420         extends qtype_multianswer_multichoice_vertical_renderer {
00421 
00422     protected function choice_wrapper_start($class) {
00423         return html_writer::start_tag('td', array('class' => $class));
00424     }
00425 
00426     protected function choice_wrapper_end() {
00427         return html_writer::end_tag('td');
00428     }
00429 
00430     protected function all_choices_wrapper_start() {
00431         return html_writer::start_tag('table', array('class' => 'answer')) .
00432                 html_writer::start_tag('tbody') . html_writer::start_tag('tr');
00433     }
00434 
00435     protected function all_choices_wrapper_end() {
00436         return html_writer::end_tag('tr') . html_writer::end_tag('tbody') .
00437                 html_writer::end_tag('table');
00438     }
00439 }
 All Data Structures Namespaces Files Functions Variables Enumerations