MDL-20636 Previewing a truefalse question in deferred feedback mode now works.
[moodle.git] / question / type / rendererbase.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
19 /**
20  * Defines the renderer base classes for question types.
21  *
22  * @package moodlecore
23  * @subpackage questiontypes
24  * @copyright 2009 The Open University
25  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26  */
29 /**
30  * Renderer base classes for question types.
31  *
32  * @copyright 2009 The Open University
33  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
34  */
35 abstract class qtype_renderer extends plugin_renderer_base {
36     /**
37      * Generate the display of the formulation part of the question. This is the
38      * area that contains the quetsion text, and the controls for students to
39      * input their answers. Some question types also embed bits of feedback, for
40      * example ticks and crosses, in this area.
41      *
42      * @param question_attempt $qa the question attempt to display.
43      * @param question_display_options $options controls what should and should not be displayed.
44      * @return string HTML fragment.
45      */
46     public function formulation_and_controls(question_attempt $qa,
47             question_display_options $options) {
48         return $qa->get_question()->format_questiontext($qa);
49     }
51     /**
52      * Output hidden form fields to clear any wrong parts of the student's response.
53      *
54      * This method will only be called if the question is in read-only mode.
55      * @param question_attempt $qa the question attempt to display.
56      * @return string HTML fragment.
57      */
58     public function clear_wrong(question_attempt $qa) {
59         $response = $qa->get_last_qt_data();
60         if (!$response) {
61             return '';
62         }
63         $cleanresponse = $qa->get_question()->clear_wrong_from_response($response);
64         $output = '';
65         foreach ($cleanresponse as $name => $value) {
66             $attr = array(
67                 'type' => 'hidden',
68                 'name' => $qa->get_qt_field_name($name),
69                 'value' => s($value),
70             );
71             $output .= html_writer::empty_tag('input', $attr);
72         }
73         return $output;
74     }
76     /**
77      * Generate the display of the outcome part of the question. This is the
78      * area that contains the various forms of feedback. This function generates
79      * the content of this area belonging to the question type.
80      *
81      * Subclasses will normally want to override the more specific methods
82      * {specific_feedback()}, {general_feedback()} and {correct_response()}
83      * that this method calls.
84      *
85      * @param question_attempt $qa the question attempt to display.
86      * @param question_display_options $options controls what should and should not be displayed.
87      * @return string HTML fragment.
88      */
89     public function feedback(question_attempt $qa, question_display_options $options) {
90         $output = '';
91         $hint = null;
93         if ($options->feedback) {
94             $output .= html_writer::nonempty_tag('div', $this->specific_feedback($qa),
95                     array('class' => 'specificfeedback'));
96             $hint = $qa->get_applicable_hint();
97         }
99         if ($options->numpartscorrect) {
100             $output .= html_writer::nonempty_tag('div', $this->num_parts_correct($qa),
101                     array('class' => 'numpartscorrect'));
102         }
104         if ($hint) {
105             $output .= $this->hint($qa->get_question(), $hint);
106         }
108         if ($options->generalfeedback) {
109             $output .= html_writer::nonempty_tag('div', $this->general_feedback($qa),
110                     array('class' => 'generalfeedback'));
111         }
113         if ($options->rightanswer) {
114             $output .= html_writer::nonempty_tag('div', $this->correct_response($qa),
115                     array('class' => 'rightanswer'));
116         }
118         return $output;
119     }
121     /**
122      * Gereate the specific feedback. This is feedback that varies accordin to
123      * the reponse the student gave.
124      * @param question_attempt $qa the question attempt to display.
125      * @return string HTML fragment.
126      */
127     protected function specific_feedback(question_attempt $qa) {
128         return '';
129     }
131     /**
132      * Gereate a brief statement of how many sub-parts of this question the
133      * student got right.
134      * @param question_attempt $qa the question attempt to display.
135      * @return string HTML fragment.
136      */
137     protected function num_parts_correct(question_attempt $qa) {
138         $a = new stdClass;
139         list($a->num, $a->outof) = $qa->get_question()->get_num_parts_right(
140                 $qa->get_last_qt_data());
141         if (is_null($a->outof)) {
142             return '';
143         } else {
144             return get_string('yougotnright', 'question', $a);
145         }
146     }
148     /**
149      * Gereate the specific feedback. This is feedback that varies accordin to
150      * the reponse the student gave.
151      * @param question_attempt $qa the question attempt to display.
152      * @return string HTML fragment.
153      */
154     protected function hint(question_definition $question, question_hint $hint) {
155         return html_writer::nonempty_tag('div', $question->format_text($hint->hint),
156                 array('class' => 'hint'));
157     }
159     /**
160      * Gereate the general feedback. This is feedback is shown ot all students.
161      *
162      * @param question_attempt $qa the question attempt to display.
163      * @return string HTML fragment.
164      */
165     protected function general_feedback(question_attempt $qa) {
166         return $qa->get_question()->format_generalfeedback($qa);
167     }
169     /**
170      * Gereate an automatic description of the correct response to this question.
171      * Not all question types can do this. If it is not possible, this method
172      * should just return an empty string.
173      *
174      * @param question_attempt $qa the question attempt to display.
175      * @return string HTML fragment.
176      */
177     protected function correct_response(question_attempt $qa) {
178         return '';
179     }
181     /**
182      * Return any HTML that needs to be included in the page's <head> when this
183      * question is used.
184      * @param $qa the question attempt that will be displayed on the page.
185      * @return string HTML fragment.
186      */
187     public function head_code(question_attempt $qa) {
188         // TODO I think we can get rid of this, but what about Opaque?
189         $qa->get_question()->qtype->find_standard_scripts();
190     }
192     protected function feedback_class($fraction) {
193         return question_state::graded_state_for_fraction($fraction)
194                 ->get_feedback_class();
195     }
197     /**
198      * Return an appropriate icon (green tick, red cross, etc.) for a grade.
199      * @param float $fraction grade on a scale 0..1.
200      * @param boolean $selected whether to show a big or small icon. (Deprecated)
201      * @return string html fragment.
202      */
203     function feedback_image($fraction, $selected = true) {
204         $state = question_state::graded_state_for_fraction($fraction);
206         if ($state == question_state::$gradedright) {
207             $icon = 'tick_green';
208         } else if ($state == question_state::$gradedpartial) {
209             $icon = 'tick_amber';
210         } else {
211             $icon = 'cross_red';
212         }
213         if ($selected) {
214             $icon .= '_big';
215         } else {
216             $icon .= '_small';
217         }
219         $attributes = array(
220             'src' => $this->output->pix_url('i/' . $icon),
221             'alt' => get_string($state->get_feedback_class(), 'question'),
222             'class' => 'questioncorrectnessicon',
223         );
225         return html_writer::empty_tag('img', $attributes);
226     }
229 /**
230  * Renderer base classes for question types.
231  *
232  * @copyright 2010 The Open University
233  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
234  */
235 abstract class qtype_with_combined_feedback_renderer extends qtype_renderer {
236     protected function combined_feedback(question_attempt $qa) {
237         $question = $qa->get_question();
239         $state = $qa->get_state();
241         if (!$state->is_finished()) {
242             $response = $qa->get_last_qt_data();
243             if (!$qa->get_question()->is_gradable_response($response)) {
244                 return '';
245             }
246             list($notused, $state) = $qa->get_question()->grade_response($response);
247         }
249         $feedback = '';
250         if ($state->is_correct()) {
251             $feedback = $question->correctfeedback;
252         } else if ($state->is_partially_correct()) {
253             $feedback = $question->partiallycorrectfeedback;
254         } else if ($state->is_incorrect()) {
255             $feedback = $question->incorrectfeedback;
256         }
258         if ($feedback) {
259             $feedback = $question->format_text($feedback);
260         }
262         return $feedback;
263     }