Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]
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/>. 16 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 */ 25 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 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) { 39 40 $question = $qa->get_question(); 41 $responseoutput = $question->get_format_renderer($this->page); 42 43 // Answer field. 44 $step = $qa->get_last_step_with_qt_var('answer'); 45 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 } 50 51 if (empty($options->readonly)) { 52 $answer = $responseoutput->response_area_input('answer', $qa, 53 $step, $question->responsefieldlines, $options->context); 54 55 } else { 56 $answer = $responseoutput->response_area_read_only('answer', $qa, 57 $step, $question->responsefieldlines, $options->context); 58 } 59 60 $files = ''; 61 if ($question->attachments) { 62 if (empty($options->readonly)) { 63 $files = $this->files_input($qa, $question->attachments, $options); 64 65 } else { 66 $files = $this->files_read_only($qa, $options); 67 } 68 } 69 70 $result = ''; 71 $result .= html_writer::tag('div', $question->format_questiontext($qa), 72 array('class' => 'qtext')); 73 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'); 78 79 return $result; 80 } 81 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 $filelist = []; 91 92 foreach ($files as $file) { 93 $out = 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 $filelist[] = html_writer::tag('li', $out, ['class' => 'mb-2']); 97 } 98 99 $labelbyid = $qa->get_qt_field_name('attachments') . '_label'; 100 101 $output = html_writer::tag('h4', get_string('answerfiles', 'qtype_essay'), ['id' => $labelbyid, 'class' => 'sr-only']); 102 $output .= html_writer::tag('ul', implode($filelist), [ 103 'aria-labelledby' => $labelbyid, 104 'class' => 'list-unstyled m-0', 105 ]); 106 return $output; 107 } 108 109 /** 110 * Displays the input control for when the student should upload a single file. 111 * @param question_attempt $qa the question attempt to display. 112 * @param int $numallowed the maximum number of attachments allowed. -1 = unlimited. 113 * @param question_display_options $options controls what should and should 114 * not be displayed. Used to get the context. 115 */ 116 public function files_input(question_attempt $qa, $numallowed, 117 question_display_options $options) { 118 global $CFG, $COURSE; 119 require_once($CFG->dirroot . '/lib/form/filemanager.php'); 120 121 $pickeroptions = new stdClass(); 122 $pickeroptions->mainfile = null; 123 $pickeroptions->maxfiles = $numallowed; 124 $pickeroptions->itemid = $qa->prepare_response_files_draft_itemid( 125 'attachments', $options->context->id); 126 $pickeroptions->context = $options->context; 127 $pickeroptions->return_types = FILE_INTERNAL | FILE_CONTROLLED_LINK; 128 129 $pickeroptions->itemid = $qa->prepare_response_files_draft_itemid( 130 'attachments', $options->context->id); 131 $pickeroptions->accepted_types = $qa->get_question()->filetypeslist; 132 133 $fm = new form_filemanager($pickeroptions); 134 $fm->options->maxbytes = get_user_max_upload_file_size( 135 $this->page->context, 136 $CFG->maxbytes, 137 $COURSE->maxbytes, 138 $qa->get_question()->maxbytes 139 ); 140 $filesrenderer = $this->page->get_renderer('core', 'files'); 141 142 $text = ''; 143 if (!empty($qa->get_question()->filetypeslist)) { 144 $text = html_writer::tag('p', get_string('acceptedfiletypes', 'qtype_essay')); 145 $filetypesutil = new \core_form\filetypes_util(); 146 $filetypes = $qa->get_question()->filetypeslist; 147 $filetypedescriptions = $filetypesutil->describe_file_types($filetypes); 148 $text .= $this->render_from_template('core_form/filetypes-descriptions', $filetypedescriptions); 149 } 150 151 $output = html_writer::start_tag('fieldset'); 152 $output .= html_writer::tag('legend', get_string('answerfiles', 'qtype_essay'), ['class' => 'sr-only']); 153 $output .= $filesrenderer->render($fm); 154 $output .= html_writer::empty_tag('input', [ 155 'type' => 'hidden', 156 'name' => $qa->get_qt_field_name('attachments'), 157 'value' => $pickeroptions->itemid, 158 ]); 159 $output .= $text; 160 $output .= html_writer::end_tag('fieldset'); 161 162 return $output; 163 } 164 165 public function manual_comment(question_attempt $qa, question_display_options $options) { 166 if ($options->manualcomment != question_display_options::EDITABLE) { 167 return ''; 168 } 169 170 $question = $qa->get_question(); 171 return html_writer::nonempty_tag('div', $question->format_text( 172 $question->graderinfo, $question->graderinfoformat, $qa, 'qtype_essay', 173 'graderinfo', $question->id), array('class' => 'graderinfo')); 174 } 175 } 176 177 178 /** 179 * A base class to abstract out the differences between different type of 180 * response format. 181 * 182 * @copyright 2011 The Open University 183 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 184 */ 185 abstract class qtype_essay_format_renderer_base extends plugin_renderer_base { 186 /** 187 * Render the students respone when the question is in read-only mode. 188 * @param string $name the variable name this input edits. 189 * @param question_attempt $qa the question attempt being display. 190 * @param question_attempt_step $step the current step. 191 * @param int $lines approximate size of input box to display. 192 * @param object $context the context teh output belongs to. 193 * @return string html to display the response. 194 */ 195 public abstract function response_area_read_only($name, question_attempt $qa, 196 question_attempt_step $step, $lines, $context); 197 198 /** 199 * Render the students respone when the question is in read-only mode. 200 * @param string $name the variable name this input edits. 201 * @param question_attempt $qa the question attempt being display. 202 * @param question_attempt_step $step the current step. 203 * @param int $lines approximate size of input box to display. 204 * @param object $context the context teh output belongs to. 205 * @return string html to display the response for editing. 206 */ 207 public abstract function response_area_input($name, question_attempt $qa, 208 question_attempt_step $step, $lines, $context); 209 210 /** 211 * @return string specific class name to add to the input element. 212 */ 213 protected abstract function class_name(); 214 } 215 216 /** 217 * An essay format renderer for essays where the student should not enter 218 * any inline response. 219 * 220 * @copyright 2013 Binghamton University 221 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 222 */ 223 class qtype_essay_format_noinline_renderer extends plugin_renderer_base { 224 225 protected function class_name() { 226 return 'qtype_essay_noinline'; 227 } 228 229 public function response_area_read_only($name, $qa, $step, $lines, $context) { 230 return ''; 231 } 232 233 public function response_area_input($name, $qa, $step, $lines, $context) { 234 return ''; 235 } 236 237 } 238 239 /** 240 * An essay format renderer for essays where the student should use the HTML 241 * editor without the file picker. 242 * 243 * @copyright 2011 The Open University 244 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 245 */ 246 class qtype_essay_format_editor_renderer extends plugin_renderer_base { 247 protected function class_name() { 248 return 'qtype_essay_editor'; 249 } 250 251 public function response_area_read_only($name, $qa, $step, $lines, $context) { 252 $labelbyid = $qa->get_qt_field_name($name) . '_label'; 253 254 $output = html_writer::tag('h4', get_string('answertext', 'qtype_essay'), ['id' => $labelbyid, 'class' => 'sr-only']); 255 $output .= html_writer::tag('div', $this->prepare_response($name, $qa, $step, $context), [ 256 'role' => 'textbox', 257 'aria-readonly' => 'true', 258 'aria-labelledby' => $labelbyid, 259 'class' => $this->class_name() . ' qtype_essay_response readonly', 260 'style' => 'min-height: ' . ($lines * 1.5) . 'em;', 261 ]); 262 // Height $lines * 1.5 because that is a typical line-height on web pages. 263 // That seems to give results that look OK. 264 265 return $output; 266 } 267 268 public function response_area_input($name, $qa, $step, $lines, $context) { 269 global $CFG; 270 require_once($CFG->dirroot . '/repository/lib.php'); 271 272 $inputname = $qa->get_qt_field_name($name); 273 $responseformat = $step->get_qt_var($name . 'format'); 274 $id = $inputname . '_id'; 275 276 $editor = editors_get_preferred_editor($responseformat); 277 $strformats = format_text_menu(); 278 $formats = $editor->get_supported_formats(); 279 foreach ($formats as $fid) { 280 $formats[$fid] = $strformats[$fid]; 281 } 282 283 list($draftitemid, $response) = $this->prepare_response_for_editing( 284 $name, $step, $context); 285 286 $editor->set_text($response); 287 $editor->use_editor($id, $this->get_editor_options($context), 288 $this->get_filepicker_options($context, $draftitemid)); 289 290 $output = html_writer::tag('label', get_string('answertext', 'qtype_essay'), [ 291 'class' => 'sr-only', 292 'for' => $id, 293 ]); 294 $output .= html_writer::start_tag('div', array('class' => 295 $this->class_name() . ' qtype_essay_response')); 296 297 $output .= html_writer::tag('div', html_writer::tag('textarea', s($response), 298 array('id' => $id, 'name' => $inputname, 'rows' => $lines, 'cols' => 60, 'class' => 'form-control'))); 299 300 $output .= html_writer::start_tag('div'); 301 if (count($formats) == 1) { 302 reset($formats); 303 $output .= html_writer::empty_tag('input', array('type' => 'hidden', 304 'name' => $inputname . 'format', 'value' => key($formats))); 305 306 } else { 307 $output .= html_writer::label(get_string('format'), 'menu' . $inputname . 'format', false); 308 $output .= ' '; 309 $output .= html_writer::select($formats, $inputname . 'format', $responseformat, ''); 310 } 311 $output .= html_writer::end_tag('div'); 312 313 $output .= $this->filepicker_html($inputname, $draftitemid); 314 315 $output .= html_writer::end_tag('div'); 316 return $output; 317 } 318 319 /** 320 * Prepare the response for read-only display. 321 * @param string $name the variable name this input edits. 322 * @param question_attempt $qa the question attempt being display. 323 * @param question_attempt_step $step the current step. 324 * @param object $context the context the attempt belongs to. 325 * @return string the response prepared for display. 326 */ 327 protected function prepare_response($name, question_attempt $qa, 328 question_attempt_step $step, $context) { 329 if (!$step->has_qt_var($name)) { 330 return ''; 331 } 332 333 $formatoptions = new stdClass(); 334 $formatoptions->para = false; 335 return format_text($step->get_qt_var($name), $step->get_qt_var($name . 'format'), 336 $formatoptions); 337 } 338 339 /** 340 * Prepare the response for editing. 341 * @param string $name the variable name this input edits. 342 * @param question_attempt_step $step the current step. 343 * @param object $context the context the attempt belongs to. 344 * @return string the response prepared for display. 345 */ 346 protected function prepare_response_for_editing($name, 347 question_attempt_step $step, $context) { 348 return array(0, $step->get_qt_var($name)); 349 } 350 351 /** 352 * @param object $context the context the attempt belongs to. 353 * @return array options for the editor. 354 */ 355 protected function get_editor_options($context) { 356 // Disable the text-editor autosave because quiz has it's own auto save function. 357 return array('context' => $context, 'autosave' => false); 358 } 359 360 /** 361 * @param object $context the context the attempt belongs to. 362 * @param int $draftitemid draft item id. 363 * @return array filepicker options for the editor. 364 */ 365 protected function get_filepicker_options($context, $draftitemid) { 366 return array('return_types' => FILE_INTERNAL | FILE_EXTERNAL); 367 } 368 369 /** 370 * @param string $inputname input field name. 371 * @param int $draftitemid draft file area itemid. 372 * @return string HTML for the filepicker, if used. 373 */ 374 protected function filepicker_html($inputname, $draftitemid) { 375 return ''; 376 } 377 } 378 379 380 /** 381 * An essay format renderer for essays where the student should use the HTML 382 * editor with the file picker. 383 * 384 * @copyright 2011 The Open University 385 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 386 */ 387 class qtype_essay_format_editorfilepicker_renderer extends qtype_essay_format_editor_renderer { 388 protected function class_name() { 389 return 'qtype_essay_editorfilepicker'; 390 } 391 392 protected function prepare_response($name, question_attempt $qa, 393 question_attempt_step $step, $context) { 394 if (!$step->has_qt_var($name)) { 395 return ''; 396 } 397 398 $formatoptions = new stdClass(); 399 $formatoptions->para = false; 400 $text = $qa->rewrite_response_pluginfile_urls($step->get_qt_var($name), 401 $context->id, 'answer', $step); 402 return format_text($text, $step->get_qt_var($name . 'format'), $formatoptions); 403 } 404 405 protected function prepare_response_for_editing($name, 406 question_attempt_step $step, $context) { 407 return $step->prepare_response_files_draft_itemid_with_text( 408 $name, $context->id, $step->get_qt_var($name)); 409 } 410 411 /** 412 * Get editor options for question response text area. 413 * @param object $context the context the attempt belongs to. 414 * @return array options for the editor. 415 */ 416 protected function get_editor_options($context) { 417 return question_utils::get_editor_options($context); 418 } 419 420 /** 421 * Get the options required to configure the filepicker for one of the editor 422 * toolbar buttons. 423 * @deprecated since 3.5 424 * @param mixed $acceptedtypes array of types of '*'. 425 * @param int $draftitemid the draft area item id. 426 * @param object $context the context. 427 * @return object the required options. 428 */ 429 protected function specific_filepicker_options($acceptedtypes, $draftitemid, $context) { 430 debugging('qtype_essay_format_editorfilepicker_renderer::specific_filepicker_options() is deprecated, ' . 431 'use question_utils::specific_filepicker_options() instead.', DEBUG_DEVELOPER); 432 433 $filepickeroptions = new stdClass(); 434 $filepickeroptions->accepted_types = $acceptedtypes; 435 $filepickeroptions->return_types = FILE_INTERNAL | FILE_EXTERNAL; 436 $filepickeroptions->context = $context; 437 $filepickeroptions->env = 'filepicker'; 438 439 $options = initialise_filepicker($filepickeroptions); 440 $options->context = $context; 441 $options->client_id = uniqid(); 442 $options->env = 'editor'; 443 $options->itemid = $draftitemid; 444 445 return $options; 446 } 447 448 /** 449 * @param object $context the context the attempt belongs to. 450 * @param int $draftitemid draft item id. 451 * @return array filepicker options for the editor. 452 */ 453 protected function get_filepicker_options($context, $draftitemid) { 454 return question_utils::get_filepicker_options($context, $draftitemid); 455 } 456 457 protected function filepicker_html($inputname, $draftitemid) { 458 $nonjspickerurl = new moodle_url('/repository/draftfiles_manager.php', array( 459 'action' => 'browse', 460 'env' => 'editor', 461 'itemid' => $draftitemid, 462 'subdirs' => false, 463 'maxfiles' => -1, 464 'sesskey' => sesskey(), 465 )); 466 467 return html_writer::empty_tag('input', array('type' => 'hidden', 468 'name' => $inputname . ':itemid', 'value' => $draftitemid)) . 469 html_writer::tag('noscript', html_writer::tag('div', 470 html_writer::tag('object', '', array('type' => 'text/html', 471 'data' => $nonjspickerurl, 'height' => 160, 'width' => 600, 472 'style' => 'border: 1px solid #000;')))); 473 } 474 } 475 476 477 /** 478 * An essay format renderer for essays where the student should use a plain 479 * input box, but with a normal, proportional font. 480 * 481 * @copyright 2011 The Open University 482 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 483 */ 484 class qtype_essay_format_plain_renderer extends plugin_renderer_base { 485 /** 486 * @return string the HTML for the textarea. 487 */ 488 protected function textarea($response, $lines, $attributes) { 489 $attributes['class'] = $this->class_name() . ' qtype_essay_response form-control'; 490 $attributes['rows'] = $lines; 491 $attributes['cols'] = 60; 492 return html_writer::tag('textarea', s($response), $attributes); 493 } 494 495 protected function class_name() { 496 return 'qtype_essay_plain'; 497 } 498 499 public function response_area_read_only($name, $qa, $step, $lines, $context) { 500 $id = $qa->get_qt_field_name($name) . '_id'; 501 502 $output = html_writer::tag('label', get_string('answertext', 'qtype_essay'), ['class' => 'sr-only', 'for' => $id]); 503 $output .= $this->textarea($step->get_qt_var($name), $lines, ['id' => $id, 'readonly' => 'readonly']); 504 return $output; 505 } 506 507 public function response_area_input($name, $qa, $step, $lines, $context) { 508 $inputname = $qa->get_qt_field_name($name); 509 $id = $inputname . '_id'; 510 511 $output = html_writer::tag('label', get_string('answertext', 'qtype_essay'), ['class' => 'sr-only', 'for' => $id]); 512 $output .= $this->textarea($step->get_qt_var($name), $lines, ['name' => $inputname, 'id' => $id]); 513 $output .= html_writer::empty_tag('input', ['type' => 'hidden', 'name' => $inputname . 'format', 'value' => FORMAT_PLAIN]); 514 515 return $output; 516 } 517 } 518 519 520 /** 521 * An essay format renderer for essays where the student should use a plain 522 * input box with a monospaced font. You might use this, for example, for a 523 * question where the students should type computer code. 524 * 525 * @copyright 2011 The Open University 526 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 527 */ 528 class qtype_essay_format_monospaced_renderer extends qtype_essay_format_plain_renderer { 529 protected function class_name() { 530 return 'qtype_essay_monospaced'; 531 } 532 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body