MDL-20636 Essay questions can now handle files in the HTML editor. #216
[moodle.git] / question / type / essay / renderer.php
CommitLineData
6d03299d
TH
1<?php
2
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/>.
17
6d03299d
TH
18/**
19 * Essay question renderer class.
20 *
7764183a
TH
21 * @package qtype
22 * @subpackage essay
23 * @copyright 2009 The Open University
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6d03299d
TH
25 */
26
27
a17b297d
TH
28defined('MOODLE_INTERNAL') || die();
29
30
6d03299d
TH
31/**
32 * Generates the output for essay questions.
33 *
7764183a
TH
34 * @copyright 2009 The Open University
35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6d03299d
TH
36 */
37class qtype_essay_renderer extends qtype_renderer {
38 public function formulation_and_controls(question_attempt $qa,
39 question_display_options $options) {
40
41 $question = $qa->get_question();
b36d2d06 42 $responseoutput = $question->get_format_renderer($this->page);
6d03299d
TH
43
44 // Answer field.
48d9c17d 45 $step = $qa->get_last_step_with_qt_var('answer');
6d03299d 46 if (empty($options->readonly)) {
48d9c17d
TH
47 $answer = $responseoutput->response_area_input('answer', $qa,
48 $step, $question->responsefieldlines, $options->context);
6d03299d
TH
49
50 } else {
48d9c17d
TH
51 $answer = $responseoutput->response_area_read_only('answer', $qa,
52 $step, $question->responsefieldlines, $options->context);
b36d2d06
TH
53 }
54
55 $files = '';
d42dbe87
TH
56 if ($question->attachments) {
57 if (empty($options->readonly)) {
58 $files = $this->files_input($qa, $question->attachments, $options);
b36d2d06 59
d42dbe87
TH
60 } else {
61 $files = $this->files_read_only($qa, $options);
62 }
6d03299d
TH
63 }
64
65 $result = '';
66 $result .= html_writer::tag('div', $question->format_questiontext($qa),
67 array('class' => 'qtext'));
68
b36d2d06 69 $result .= html_writer::start_tag('div', array('class' => 'ablock'));
6d03299d 70 $result .= html_writer::tag('div', $answer, array('class' => 'answer'));
caeeff07 71 $result .= html_writer::tag('div', $files, array('class' => 'attachments'));
6d03299d
TH
72 $result .= html_writer::end_tag('div');
73
74 return $result;
75 }
b36d2d06
TH
76
77 /**
78 * Displays any attached files when the question is in read-only mode.
d42dbe87
TH
79 * @param question_attempt $qa the question attempt to display.
80 * @param question_display_options $options controls what should and should
81 * not be displayed. Used to get the context.
b36d2d06 82 */
caeeff07 83 public function files_read_only(question_attempt $qa, question_display_options $options) {
8026d4aa 84 $files = $qa->get_last_qt_files('attachments', $options->context->id);
caeeff07
TH
85 $output = array();
86
87 foreach ($files as $file) {
88 $mimetype = $file->get_mimetype();
89 $output[] = html_writer::tag('p', html_writer::link($qa->get_response_file_url($file),
90 $this->output->pix_icon(file_mimetype_icon($mimetype), $mimetype,
91 'moodle', array('class' => 'icon')) . ' ' . s($file->get_filename())));
92 }
93 return implode($output);
b36d2d06
TH
94 }
95
96 /**
97 * Displays the input control for when the student should upload a single file.
d42dbe87
TH
98 * @param question_attempt $qa the question attempt to display.
99 * @param int $numallowed the maximum number of attachments allowed. -1 = unlimited.
100 * @param question_display_options $options controls what should and should
101 * not be displayed. Used to get the context.
b36d2d06 102 */
d42dbe87 103 public function files_input(question_attempt $qa, $numallowed, question_display_options $options) {
8026d4aa
TH
104 global $CFG;
105 require_once($CFG->dirroot . '/lib/form/filemanager.php');
106
107 $pickeroptions = new stdClass();
108 $pickeroptions->mainfile = null;
d42dbe87 109 $pickeroptions->maxfiles = $numallowed;
8026d4aa
TH
110 $pickeroptions->itemid = $qa->prepare_response_files_draft_itemid(
111 'attachments', $options->context->id);
112 $pickeroptions->context = $options->context;
113
48d9c17d
TH
114 $pickeroptions->itemid = $qa->prepare_response_files_draft_itemid(
115 'attachments', $options->context->id);
116
217f9a61
TH
117 return form_filemanager_render($pickeroptions) . html_writer::empty_tag(
118 'input', array('type' => 'hidden', 'name' => $qa->get_qt_field_name('attachments'),
119 'value' => $pickeroptions->itemid));
b36d2d06
TH
120 }
121
783af252
TH
122 public function manual_comment(question_attempt $qa, question_display_options $options) {
123 if ($options->manualcomment != question_display_options::EDITABLE) {
124 return '';
125 }
126
127 $question = $qa->get_question();
128 return html_writer::nonempty_tag('div', $question->format_text(
129 $question->graderinfo, $question->graderinfo, $qa, 'qtype_essay',
130 'graderinfo', $question->id), array('class' => 'graderinfo'));
131 }
b36d2d06
TH
132}
133
134
135/**
136 * A base class to abstract out the differences between different type of
137 * response format.
138 *
139 * @copyright 2011 The Open University
140 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
141 */
142abstract class qtype_essay_format_renderer_base extends plugin_renderer_base {
143 /**
144 * Render the students respone when the question is in read-only mode.
48d9c17d
TH
145 * @param string $name the variable name this input edits.
146 * @param question_attempt $qa the question attempt being display.
147 * @param question_attempt_step $step the current step.
b36d2d06 148 * @param int $lines approximate size of input box to display.
48d9c17d
TH
149 * @param object $context the context teh output belongs to.
150 * @return string html to display the response.
b36d2d06 151 */
48d9c17d
TH
152 public abstract function response_area_read_only($name, question_attempt $qa,
153 question_attempt_step $step, $lines, $context);
b36d2d06
TH
154
155 /**
156 * Render the students respone when the question is in read-only mode.
48d9c17d
TH
157 * @param string $name the variable name this input edits.
158 * @param question_attempt $qa the question attempt being display.
159 * @param question_attempt_step $step the current step.
b36d2d06 160 * @param int $lines approximate size of input box to display.
48d9c17d
TH
161 * @param object $context the context teh output belongs to.
162 * @return string html to display the response for editing.
163 */
164 public abstract function response_area_input($name, question_attempt $qa,
165 question_attempt_step $step, $lines, $context);
166
167 /**
168 * @return string specific class name to add to the input element.
b36d2d06 169 */
48d9c17d 170 protected abstract function class_name();
b36d2d06
TH
171}
172
173
174/**
175 * An essay format renderer for essays where the student should use the HTML
176 * editor without the file picker.
177 *
178 * @copyright 2011 The Open University
179 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
180 */
181class qtype_essay_format_editor_renderer extends plugin_renderer_base {
48d9c17d
TH
182 protected function class_name() {
183 return 'qtype_essay_editor';
b36d2d06
TH
184 }
185
48d9c17d
TH
186 public function response_area_read_only($name, $qa, $step, $lines, $context) {
187 return html_writer::tag('div', $this->prepare_response($name, $qa, $step, $context),
188 array('class' => $this->class_name() . ' qtype_essay_response'));
189 }
190
191 public function response_area_input($name, $qa, $step, $lines, $context) {
a54ecbbb
TH
192 global $CFG, $PAGE;
193 require_once($CFG->dirroot.'/repository/lib.php');
194
48d9c17d
TH
195 $inputname = $qa->get_qt_field_name($name);
196 $responseformat = $step->get_qt_var($name . 'format');
a54ecbbb
TH
197 $id = $inputname . '_id';
198
199 $editor = editors_get_preferred_editor($responseformat);
200 $strformats = format_text_menu();
201 $formats = $editor->get_supported_formats();
202 foreach ($formats as $fid) {
203 $formats[$fid] = $strformats[$fid];
204 }
205
48d9c17d
TH
206 list($draftitemid, $reponse) = $this->prepare_response_for_editing(
207 $name, $step, $context);
208
a54ecbbb 209 $editor->use_editor($id, $this->get_editor_options($context),
48d9c17d 210 $this->get_filepicker_options($context, $draftitemid));
a54ecbbb
TH
211
212 $output = '';
48d9c17d
TH
213 $output .= html_writer::start_tag('div', array('class' =>
214 $this->class_name() . ' qtype_essay_response'));
a54ecbbb 215
48d9c17d 216 $output .= html_writer::tag('div', html_writer::tag('textarea', s($reponse),
a54ecbbb
TH
217 array('id' => $id, 'name' => $inputname, 'rows' => $lines)));
218
219 $output .= html_writer::start_tag('div');
220 if (count($formats == 1)) {
221 reset($formats);
222 $output .= html_writer::empty_tag('input', array('type' => 'hidden',
223 'name' => $inputname . 'format', 'value' => key($formats)));
224
225 } else {
226 $output .= html_writer::select($formats, $inputname . 'format', $responseformat, '');
227 }
228 $output .= html_writer::end_tag('div');
229
48d9c17d 230 $output .= $this->filepicker_html($inputname, $draftitemid);
a54ecbbb
TH
231
232 $output .= html_writer::end_tag('div');
233 return $output;
234 }
235
236 /**
48d9c17d
TH
237 * Prepare the response for read-only display.
238 * @param string $name the variable name this input edits.
239 * @param question_attempt $qa the question attempt being display.
240 * @param question_attempt_step $step the current step.
241 * @param object $context the context the attempt belongs to.
242 * @return string the response prepared for display.
a54ecbbb 243 */
48d9c17d
TH
244 protected function prepare_response($name, question_attempt $qa,
245 question_attempt_step $step, $context) {
246 if (!$step->has_qt_var($name)) {
247 return '';
248 }
249
250 $formatoptions = new stdClass();
251 $formatoptions->para = false;
252 return format_text($step->get_qt_var($name), $step->get_qt_var($name . 'format'),
253 $formatoptions);
a54ecbbb
TH
254 }
255
256 /**
48d9c17d
TH
257 * Prepare the response for editing.
258 * @param string $name the variable name this input edits.
259 * @param question_attempt_step $step the current step.
260 * @param object $context the context the attempt belongs to.
261 * @return string the response prepared for display.
a54ecbbb 262 */
48d9c17d
TH
263 protected function prepare_response_for_editing($name,
264 question_attempt_step $step, $context) {
265 return array(0, $step->get_qt_var($name));
a54ecbbb
TH
266 }
267
268 /**
48d9c17d 269 * @param object $context the context the attempt belongs to.
a54ecbbb
TH
270 * @return array options for the editor.
271 */
272 protected function get_editor_options($context) {
273 return array('context' => $context);
274 }
275
276 /**
48d9c17d
TH
277 * @param object $context the context the attempt belongs to.
278 * @param int $draftitemid draft item id.
279 * @return array filepicker options for the editor.
280 */
281 protected function get_filepicker_options($context, $draftitemid) {
282 return array();
283 }
284
285 /**
286 * @param string $inputname input field name.
287 * @param int $draftitemid draft file area itemid.
288 * @return string HTML for the filepicker, if used.
a54ecbbb 289 */
48d9c17d 290 protected function filepicker_html($inputname, $draftitemid) {
a54ecbbb 291 return '';
b36d2d06
TH
292 }
293}
294
295
296/**
297 * An essay format renderer for essays where the student should use the HTML
298 * editor with the file picker.
299 *
300 * @copyright 2011 The Open University
301 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
302 */
a54ecbbb 303class qtype_essay_format_editorfilepicker_renderer extends qtype_essay_format_editor_renderer {
48d9c17d
TH
304 protected function class_name() {
305 return 'qtype_essay_editorfilepicker';
306 }
307
308 protected function prepare_response($name, question_attempt $qa,
309 question_attempt_step $step, $context) {
310 if (!$step->has_qt_var($name)) {
311 return '';
312 }
a54ecbbb 313
48d9c17d
TH
314 $formatoptions = new stdClass();
315 $formatoptions->para = false;
316 $text = $qa->rewrite_response_pluginfile_urls($step->get_qt_var($name),
317 $context->id, 'answer', $step);
318 return format_text($text, $step->get_qt_var($name . 'format'), $formatoptions);
a54ecbbb
TH
319 }
320
48d9c17d
TH
321 protected function prepare_response_for_editing($name,
322 question_attempt_step $step, $context) {
323 return $step->prepare_response_files_draft_itemid_with_text(
324 $name, $context->id, $step->get_qt_var($name));
b36d2d06
TH
325 }
326
48d9c17d
TH
327 protected function get_editor_options($context) {
328 return array(
329 'subdirs' => 0,
330 'maxbytes' => 0,
331 'maxfiles' => -1,
332 'context' => $context,
333 'noclean' => 0,
334 'trusttext'=>0
335 );
336 }
337
338 /**
339 * Get the options required to configure the filepicker for one of the editor
340 * toolbar buttons.
341 * @param mixed $acceptedtypes array of types of '*'.
342 * @param int $draftitemid the draft area item id.
343 * @param object $context the context.
344 * @return object the required options.
345 */
346 protected function specific_filepicker_options($acceptedtypes, $draftitemid, $context) {
347 $filepickeroptions = new stdClass();
348 $filepickeroptions->accepted_types = $acceptedtypes;
349 $filepickeroptions->return_types = FILE_INTERNAL | FILE_EXTERNAL;
350 $filepickeroptions->context = $context;
351 $filepickeroptions->env = 'filepicker';
352
353 $options = initialise_filepicker($filepickeroptions);
354 $options->context = $context;
355 $options->client_id = uniqid();
356 $options->env = 'editor';
357 $options->itemid = $draftitemid;
358
359 return $options;
360 }
361
362 protected function get_filepicker_options($context, $draftitemid) {
363 global $CFG;
364
365 return array(
366 'image' => $this->specific_filepicker_options(array('image'),
367 $draftitemid, $context),
368 'media' => $this->specific_filepicker_options(array('video', 'audio'),
369 $draftitemid, $context),
370 'link' => $this->specific_filepicker_options('*',
371 $draftitemid, $context),
372 );
373 }
374
375 protected function filepicker_html($inputname, $draftitemid) {
376 $nonjspickerurl = new moodle_url('/repository/draftfiles_manager.php', array(
377 'action' => 'browse',
378 'env' => 'editor',
379 'itemid' => $draftitemid,
380 'subdirs' => false,
381 'maxfiles' => -1,
382 'sesskey' => sesskey(),
383 ));
384
385 return html_writer::empty_tag('input', array('type' => 'hidden',
386 'name' => $inputname . ':itemid', 'value' => $draftitemid)) .
387 html_writer::tag('noscript', html_writer::tag('div',
388 html_writer::tag('object', '', array('type' => 'text/html',
389 'data' => $nonjspickerurl, 'height' => 160, 'width' => 600,
390 'style' => 'border: 1px solid #000;'))));
b36d2d06
TH
391 }
392}
393
394
395/**
396 * An essay format renderer for essays where the student should use a plain
397 * input box, but with a normal, proportional font.
398 *
399 * @copyright 2011 The Open University
400 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
401 */
402class qtype_essay_format_plain_renderer extends plugin_renderer_base {
403 /**
404 * @return string the HTML for the textarea.
405 */
406 protected function textarea($response, $lines, $attributes) {
407 $attributes['class'] = $this->class_name() . ' qtype_essay_response';
408 $attributes['rows'] = $lines;
409 return html_writer::tag('textarea', s($response), $attributes);
410 }
411
412 protected function class_name() {
413 return 'qtype_essay_plain';
414 }
415
48d9c17d
TH
416 public function response_area_read_only($name, $qa, $step, $lines, $context) {
417 return $this->textarea($step->get_qt_var($name), $lines, array('readonly' => 'readonly'));
b36d2d06
TH
418 }
419
48d9c17d
TH
420 public function response_area_input($name, $qa, $step, $lines, $context) {
421 $inputname = $qa->get_qt_field_name($name);
422 return $this->textarea($step->get_qt_var($name), $lines, array('name' => $inputname)) .
a54ecbbb
TH
423 html_writer::empty_tag('input', array('type' => 'hidden',
424 'name' => $inputname . 'format', 'value' => FORMAT_PLAIN));
b36d2d06
TH
425 }
426}
427
428
429/**
430 * An essay format renderer for essays where the student should use a plain
431 * input box with a monospaced font. You might use this, for example, for a
432 * question where the students should type computer code.
433 *
434 * @copyright 2011 The Open University
435 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
436 */
437class qtype_essay_format_monospaced_renderer extends qtype_essay_format_plain_renderer {
438 protected function class_name() {
439 return 'qtype_essay_monospaced';
440 }
6d03299d 441}