MDL-70153 qtype_essay: Fix max size displayed for attachments
[moodle.git] / question / type / essay / renderer.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * Essay question renderer class.
19  *
20  * @package    qtype
21  * @subpackage essay
22  * @copyright  2009 The Open University
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
27 defined('MOODLE_INTERNAL') || die();
30 /**
31  * Generates the output for essay questions.
32  *
33  * @copyright  2009 The Open University
34  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35  */
36 class qtype_essay_renderer extends qtype_renderer {
37     public function formulation_and_controls(question_attempt $qa,
38             question_display_options $options) {
40         $question = $qa->get_question();
41         $responseoutput = $question->get_format_renderer($this->page);
43         // Answer field.
44         $step = $qa->get_last_step_with_qt_var('answer');
46         if (!$step->has_qt_var('answer') && empty($options->readonly)) {
47             // Question has never been answered, fill it with response template.
48             $step = new question_attempt_step(array('answer' => $question->responsetemplate));
49         }
51         if (empty($options->readonly)) {
52             $answer = $responseoutput->response_area_input('answer', $qa,
53                     $step, $question->responsefieldlines, $options->context);
55         } else {
56             $answer = $responseoutput->response_area_read_only('answer', $qa,
57                     $step, $question->responsefieldlines, $options->context);
58         }
60         $files = '';
61         if ($question->attachments) {
62             if (empty($options->readonly)) {
63                 $files = $this->files_input($qa, $question->attachments, $options);
65             } else {
66                 $files = $this->files_read_only($qa, $options);
67             }
68         }
70         $result = '';
71         $result .= html_writer::tag('div', $question->format_questiontext($qa),
72                 array('class' => 'qtext'));
74         $result .= html_writer::start_tag('div', array('class' => 'ablock'));
75         $result .= html_writer::tag('div', $answer, array('class' => 'answer'));
76         $result .= html_writer::tag('div', $files, array('class' => 'attachments'));
77         $result .= html_writer::end_tag('div');
79         return $result;
80     }
82     /**
83      * Displays any attached files when the question is in read-only mode.
84      * @param question_attempt $qa the question attempt to display.
85      * @param question_display_options $options controls what should and should
86      *      not be displayed. Used to get the context.
87      */
88     public function files_read_only(question_attempt $qa, question_display_options $options) {
89         $files = $qa->get_last_qt_files('attachments', $options->context->id);
90         $output = array();
92         foreach ($files as $file) {
93             $output[] = html_writer::tag('p', html_writer::link($qa->get_response_file_url($file),
94                     $this->output->pix_icon(file_file_icon($file), get_mimetype_description($file),
95                     'moodle', array('class' => 'icon')) . ' ' . s($file->get_filename())));
96         }
97         return implode($output);
98     }
100     /**
101      * Displays the input control for when the student should upload a single file.
102      * @param question_attempt $qa the question attempt to display.
103      * @param int $numallowed the maximum number of attachments allowed. -1 = unlimited.
104      * @param question_display_options $options controls what should and should
105      *      not be displayed. Used to get the context.
106      */
107     public function files_input(question_attempt $qa, $numallowed,
108             question_display_options $options) {
109         global $CFG, $COURSE;
110         require_once($CFG->dirroot . '/lib/form/filemanager.php');
112         $pickeroptions = new stdClass();
113         $pickeroptions->mainfile = null;
114         $pickeroptions->maxfiles = $numallowed;
115         $pickeroptions->itemid = $qa->prepare_response_files_draft_itemid(
116                 'attachments', $options->context->id);
117         $pickeroptions->context = $options->context;
118         $pickeroptions->return_types = FILE_INTERNAL | FILE_CONTROLLED_LINK;
120         $pickeroptions->itemid = $qa->prepare_response_files_draft_itemid(
121                 'attachments', $options->context->id);
122         $pickeroptions->accepted_types = $qa->get_question()->filetypeslist;
124         $fm = new form_filemanager($pickeroptions);
125         $fm->options->maxbytes = get_user_max_upload_file_size(
126                 $this->page->context, $CFG->maxbytes, $COURSE->maxbytes, $qa->get_question()->maxbytes);
127         $filesrenderer = $this->page->get_renderer('core', 'files');
129         $text = '';
130         if (!empty($qa->get_question()->filetypeslist)) {
131             $text = html_writer::tag('p', get_string('acceptedfiletypes', 'qtype_essay'));
132             $filetypesutil = new \core_form\filetypes_util();
133             $filetypes = $qa->get_question()->filetypeslist;
134             $filetypedescriptions = $filetypesutil->describe_file_types($filetypes);
135             $text .= $this->render_from_template('core_form/filetypes-descriptions', $filetypedescriptions);
136         }
137         return $filesrenderer->render($fm). html_writer::empty_tag(
138                 'input', array('type' => 'hidden', 'name' => $qa->get_qt_field_name('attachments'),
139                 'value' => $pickeroptions->itemid)) . $text;
140     }
142     public function manual_comment(question_attempt $qa, question_display_options $options) {
143         if ($options->manualcomment != question_display_options::EDITABLE) {
144             return '';
145         }
147         $question = $qa->get_question();
148         return html_writer::nonempty_tag('div', $question->format_text(
149                 $question->graderinfo, $question->graderinfo, $qa, 'qtype_essay',
150                 'graderinfo', $question->id), array('class' => 'graderinfo'));
151     }
155 /**
156  * A base class to abstract out the differences between different type of
157  * response format.
158  *
159  * @copyright  2011 The Open University
160  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
161  */
162 abstract class qtype_essay_format_renderer_base extends plugin_renderer_base {
163     /**
164      * Render the students respone when the question is in read-only mode.
165      * @param string $name the variable name this input edits.
166      * @param question_attempt $qa the question attempt being display.
167      * @param question_attempt_step $step the current step.
168      * @param int $lines approximate size of input box to display.
169      * @param object $context the context teh output belongs to.
170      * @return string html to display the response.
171      */
172     public abstract function response_area_read_only($name, question_attempt $qa,
173             question_attempt_step $step, $lines, $context);
175     /**
176      * Render the students respone when the question is in read-only mode.
177      * @param string $name the variable name this input edits.
178      * @param question_attempt $qa the question attempt being display.
179      * @param question_attempt_step $step the current step.
180      * @param int $lines approximate size of input box to display.
181      * @param object $context the context teh output belongs to.
182      * @return string html to display the response for editing.
183      */
184     public abstract function response_area_input($name, question_attempt $qa,
185             question_attempt_step $step, $lines, $context);
187     /**
188      * @return string specific class name to add to the input element.
189      */
190     protected abstract function class_name();
193 /**
194  * An essay format renderer for essays where the student should not enter
195  * any inline response.
196  *
197  * @copyright  2013 Binghamton University
198  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
199  */
200 class qtype_essay_format_noinline_renderer extends plugin_renderer_base {
202     protected function class_name() {
203         return 'qtype_essay_noinline';
204     }
206     public function response_area_read_only($name, $qa, $step, $lines, $context) {
207         return '';
208     }
210     public function response_area_input($name, $qa, $step, $lines, $context) {
211         return '';
212     }
216 /**
217  * An essay format renderer for essays where the student should use the HTML
218  * editor without the file picker.
219  *
220  * @copyright  2011 The Open University
221  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
222  */
223 class qtype_essay_format_editor_renderer extends plugin_renderer_base {
224     protected function class_name() {
225         return 'qtype_essay_editor';
226     }
228     public function response_area_read_only($name, $qa, $step, $lines, $context) {
229         return html_writer::tag('div', $this->prepare_response($name, $qa, $step, $context),
230                 ['class' => $this->class_name() . ' qtype_essay_response readonly',
231                         'style' => 'min-height: ' . ($lines * 1.5) . 'em;']);
232         // Height $lines * 1.5 because that is a typical line-height on web pages.
233         // That seems to give results that look OK.
234     }
236     public function response_area_input($name, $qa, $step, $lines, $context) {
237         global $CFG;
238         require_once($CFG->dirroot . '/repository/lib.php');
240         $inputname = $qa->get_qt_field_name($name);
241         $responseformat = $step->get_qt_var($name . 'format');
242         $id = $inputname . '_id';
244         $editor = editors_get_preferred_editor($responseformat);
245         $strformats = format_text_menu();
246         $formats = $editor->get_supported_formats();
247         foreach ($formats as $fid) {
248             $formats[$fid] = $strformats[$fid];
249         }
251         list($draftitemid, $response) = $this->prepare_response_for_editing(
252                 $name, $step, $context);
254         $editor->set_text($response);
255         $editor->use_editor($id, $this->get_editor_options($context),
256                 $this->get_filepicker_options($context, $draftitemid));
258         $output = '';
259         $output .= html_writer::start_tag('div', array('class' =>
260                 $this->class_name() . ' qtype_essay_response'));
262         $output .= html_writer::tag('div', html_writer::tag('textarea', s($response),
263                 array('id' => $id, 'name' => $inputname, 'rows' => $lines, 'cols' => 60)));
265         $output .= html_writer::start_tag('div');
266         if (count($formats) == 1) {
267             reset($formats);
268             $output .= html_writer::empty_tag('input', array('type' => 'hidden',
269                     'name' => $inputname . 'format', 'value' => key($formats)));
271         } else {
272             $output .= html_writer::label(get_string('format'), 'menu' . $inputname . 'format', false);
273             $output .= ' ';
274             $output .= html_writer::select($formats, $inputname . 'format', $responseformat, '');
275         }
276         $output .= html_writer::end_tag('div');
278         $output .= $this->filepicker_html($inputname, $draftitemid);
280         $output .= html_writer::end_tag('div');
281         return $output;
282     }
284     /**
285      * Prepare the response for read-only display.
286      * @param string $name the variable name this input edits.
287      * @param question_attempt $qa the question attempt being display.
288      * @param question_attempt_step $step the current step.
289      * @param object $context the context the attempt belongs to.
290      * @return string the response prepared for display.
291      */
292     protected function prepare_response($name, question_attempt $qa,
293             question_attempt_step $step, $context) {
294         if (!$step->has_qt_var($name)) {
295             return '';
296         }
298         $formatoptions = new stdClass();
299         $formatoptions->para = false;
300         return format_text($step->get_qt_var($name), $step->get_qt_var($name . 'format'),
301                 $formatoptions);
302     }
304     /**
305      * Prepare the response for editing.
306      * @param string $name the variable name this input edits.
307      * @param question_attempt_step $step the current step.
308      * @param object $context the context the attempt belongs to.
309      * @return string the response prepared for display.
310      */
311     protected function prepare_response_for_editing($name,
312             question_attempt_step $step, $context) {
313         return array(0, $step->get_qt_var($name));
314     }
316     /**
317      * @param object $context the context the attempt belongs to.
318      * @return array options for the editor.
319      */
320     protected function get_editor_options($context) {
321         // Disable the text-editor autosave because quiz has it's own auto save function.
322         return array('context' => $context, 'autosave' => false);
323     }
325     /**
326      * @param object $context the context the attempt belongs to.
327      * @param int $draftitemid draft item id.
328      * @return array filepicker options for the editor.
329      */
330     protected function get_filepicker_options($context, $draftitemid) {
331         return array('return_types'  => FILE_INTERNAL | FILE_EXTERNAL);
332     }
334     /**
335      * @param string $inputname input field name.
336      * @param int $draftitemid draft file area itemid.
337      * @return string HTML for the filepicker, if used.
338      */
339     protected function filepicker_html($inputname, $draftitemid) {
340         return '';
341     }
345 /**
346  * An essay format renderer for essays where the student should use the HTML
347  * editor with the file picker.
348  *
349  * @copyright  2011 The Open University
350  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
351  */
352 class qtype_essay_format_editorfilepicker_renderer extends qtype_essay_format_editor_renderer {
353     protected function class_name() {
354         return 'qtype_essay_editorfilepicker';
355     }
357     protected function prepare_response($name, question_attempt $qa,
358             question_attempt_step $step, $context) {
359         if (!$step->has_qt_var($name)) {
360             return '';
361         }
363         $formatoptions = new stdClass();
364         $formatoptions->para = false;
365         $text = $qa->rewrite_response_pluginfile_urls($step->get_qt_var($name),
366                 $context->id, 'answer', $step);
367         return format_text($text, $step->get_qt_var($name . 'format'), $formatoptions);
368     }
370     protected function prepare_response_for_editing($name,
371             question_attempt_step $step, $context) {
372         return $step->prepare_response_files_draft_itemid_with_text(
373                 $name, $context->id, $step->get_qt_var($name));
374     }
376     /**
377      * Get editor options for question response text area.
378      * @param object $context the context the attempt belongs to.
379      * @return array options for the editor.
380      */
381     protected function get_editor_options($context) {
382         return question_utils::get_editor_options($context);
383     }
385     /**
386      * Get the options required to configure the filepicker for one of the editor
387      * toolbar buttons.
388      * @deprecated since 3.5
389      * @param mixed $acceptedtypes array of types of '*'.
390      * @param int $draftitemid the draft area item id.
391      * @param object $context the context.
392      * @return object the required options.
393      */
394     protected function specific_filepicker_options($acceptedtypes, $draftitemid, $context) {
395         debugging('qtype_essay_format_editorfilepicker_renderer::specific_filepicker_options() is deprecated, ' .
396             'use question_utils::specific_filepicker_options() instead.', DEBUG_DEVELOPER);
398         $filepickeroptions = new stdClass();
399         $filepickeroptions->accepted_types = $acceptedtypes;
400         $filepickeroptions->return_types = FILE_INTERNAL | FILE_EXTERNAL;
401         $filepickeroptions->context = $context;
402         $filepickeroptions->env = 'filepicker';
404         $options = initialise_filepicker($filepickeroptions);
405         $options->context = $context;
406         $options->client_id = uniqid();
407         $options->env = 'editor';
408         $options->itemid = $draftitemid;
410         return $options;
411     }
413     /**
414      * @param object $context the context the attempt belongs to.
415      * @param int $draftitemid draft item id.
416      * @return array filepicker options for the editor.
417      */
418     protected function get_filepicker_options($context, $draftitemid) {
419         return question_utils::get_filepicker_options($context, $draftitemid);
420     }
422     protected function filepicker_html($inputname, $draftitemid) {
423         $nonjspickerurl = new moodle_url('/repository/draftfiles_manager.php', array(
424             'action' => 'browse',
425             'env' => 'editor',
426             'itemid' => $draftitemid,
427             'subdirs' => false,
428             'maxfiles' => -1,
429             'sesskey' => sesskey(),
430         ));
432         return html_writer::empty_tag('input', array('type' => 'hidden',
433                 'name' => $inputname . ':itemid', 'value' => $draftitemid)) .
434                 html_writer::tag('noscript', html_writer::tag('div',
435                     html_writer::tag('object', '', array('type' => 'text/html',
436                         'data' => $nonjspickerurl, 'height' => 160, 'width' => 600,
437                         'style' => 'border: 1px solid #000;'))));
438     }
442 /**
443  * An essay format renderer for essays where the student should use a plain
444  * input box, but with a normal, proportional font.
445  *
446  * @copyright  2011 The Open University
447  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
448  */
449 class qtype_essay_format_plain_renderer extends plugin_renderer_base {
450     /**
451      * @return string the HTML for the textarea.
452      */
453     protected function textarea($response, $lines, $attributes) {
454         $attributes['class'] = $this->class_name() . ' qtype_essay_response';
455         $attributes['rows'] = $lines;
456         $attributes['cols'] = 60;
457         return html_writer::tag('textarea', s($response), $attributes);
458     }
460     protected function class_name() {
461         return 'qtype_essay_plain';
462     }
464     public function response_area_read_only($name, $qa, $step, $lines, $context) {
465         return $this->textarea($step->get_qt_var($name), $lines, array('readonly' => 'readonly'));
466     }
468     public function response_area_input($name, $qa, $step, $lines, $context) {
469         $inputname = $qa->get_qt_field_name($name);
470         return $this->textarea($step->get_qt_var($name), $lines, array('name' => $inputname)) .
471                 html_writer::empty_tag('input', array('type' => 'hidden',
472                     'name' => $inputname . 'format', 'value' => FORMAT_PLAIN));
473     }
477 /**
478  * An essay format renderer for essays where the student should use a plain
479  * input box with a monospaced font. You might use this, for example, for a
480  * question where the students should type computer code.
481  *
482  * @copyright  2011 The Open University
483  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
484  */
485 class qtype_essay_format_monospaced_renderer extends qtype_essay_format_plain_renderer {
486     protected function class_name() {
487         return 'qtype_essay_monospaced';
488     }