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 definition 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 require_once($CFG->dirroot . '/question/type/questionbase.php'); 30 31 /** 32 * Represents an essay question. 33 * 34 * @copyright 2009 The Open University 35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 36 */ 37 class qtype_essay_question extends question_with_responses { 38 39 public $responseformat; 40 41 /** @var int Indicates whether an inline response is required ('0') or optional ('1') */ 42 public $responserequired; 43 44 public $responsefieldlines; 45 public $attachments; 46 47 /** @var int maximum file size in bytes */ 48 public $maxbytes; 49 50 /** @var int The number of attachments required for a response to be complete. */ 51 public $attachmentsrequired; 52 53 public $graderinfo; 54 public $graderinfoformat; 55 public $responsetemplate; 56 public $responsetemplateformat; 57 58 /** @var array The string array of file types accepted upon file submission. */ 59 public $filetypeslist; 60 61 public function make_behaviour(question_attempt $qa, $preferredbehaviour) { 62 return question_engine::make_behaviour('manualgraded', $qa, $preferredbehaviour); 63 } 64 65 /** 66 * @param moodle_page the page we are outputting to. 67 * @return qtype_essay_format_renderer_base the response-format-specific renderer. 68 */ 69 public function get_format_renderer(moodle_page $page) { 70 return $page->get_renderer('qtype_essay', 'format_' . $this->responseformat); 71 } 72 73 public function get_expected_data() { 74 if ($this->responseformat == 'editorfilepicker') { 75 $expecteddata = array('answer' => question_attempt::PARAM_RAW_FILES); 76 } else { 77 $expecteddata = array('answer' => PARAM_RAW); 78 } 79 $expecteddata['answerformat'] = PARAM_ALPHANUMEXT; 80 if ($this->attachments != 0) { 81 $expecteddata['attachments'] = question_attempt::PARAM_FILES; 82 } 83 return $expecteddata; 84 } 85 86 public function summarise_response(array $response) { 87 $output = null; 88 89 if (isset($response['answer'])) { 90 $output .= question_utils::to_plain_text($response['answer'], 91 $response['answerformat'], array('para' => false)); 92 } 93 94 if (isset($response['attachments']) && $response['attachments']) { 95 $attachedfiles = []; 96 foreach ($response['attachments']->get_files() as $file) { 97 $attachedfiles[] = $file->get_filename() . ' (' . display_size($file->get_filesize()) . ')'; 98 } 99 if ($attachedfiles) { 100 $output .= get_string('attachedfiles', 'qtype_essay', implode(', ', $attachedfiles)); 101 } 102 } 103 return $output; 104 } 105 106 public function un_summarise_response(string $summary) { 107 if (!empty($summary)) { 108 return ['answer' => text_to_html($summary)]; 109 } else { 110 return []; 111 } 112 } 113 114 public function get_correct_response() { 115 return null; 116 } 117 118 public function is_complete_response(array $response) { 119 // Determine if the given response has online text and attachments. 120 $hasinlinetext = array_key_exists('answer', $response) && ($response['answer'] !== ''); 121 $hasattachments = array_key_exists('attachments', $response) 122 && $response['attachments'] instanceof question_response_files; 123 124 // Determine the number of attachments present. 125 if ($hasattachments) { 126 // Check the filetypes. 127 $filetypesutil = new \core_form\filetypes_util(); 128 $allowlist = $filetypesutil->normalize_file_types($this->filetypeslist); 129 $wrongfiles = array(); 130 foreach ($response['attachments']->get_files() as $file) { 131 if (!$filetypesutil->is_allowed_file_type($file->get_filename(), $allowlist)) { 132 $wrongfiles[] = $file->get_filename(); 133 } 134 } 135 if ($wrongfiles) { // At least one filetype is wrong. 136 return false; 137 } 138 $attachcount = count($response['attachments']->get_files()); 139 } else { 140 $attachcount = 0; 141 } 142 143 // Determine if we have /some/ content to be graded. 144 $hascontent = $hasinlinetext || ($attachcount > 0); 145 146 // Determine if we meet the optional requirements. 147 $meetsinlinereq = $hasinlinetext || (!$this->responserequired) || ($this->responseformat == 'noinline'); 148 $meetsattachmentreq = ($attachcount >= $this->attachmentsrequired); 149 150 // The response is complete iff all of our requirements are met. 151 return $hascontent && $meetsinlinereq && $meetsattachmentreq; 152 } 153 154 public function is_gradable_response(array $response) { 155 // Determine if the given response has online text and attachments. 156 if (array_key_exists('answer', $response) && ($response['answer'] !== '')) { 157 return true; 158 } else if (array_key_exists('attachments', $response) 159 && $response['attachments'] instanceof question_response_files) { 160 return true; 161 } else { 162 return false; 163 } 164 } 165 166 public function is_same_response(array $prevresponse, array $newresponse) { 167 if (array_key_exists('answer', $prevresponse) && $prevresponse['answer'] !== $this->responsetemplate) { 168 $value1 = (string) $prevresponse['answer']; 169 } else { 170 $value1 = ''; 171 } 172 if (array_key_exists('answer', $newresponse) && $newresponse['answer'] !== $this->responsetemplate) { 173 $value2 = (string) $newresponse['answer']; 174 } else { 175 $value2 = ''; 176 } 177 return $value1 === $value2 && ($this->attachments == 0 || 178 question_utils::arrays_same_at_key_missing_is_blank( 179 $prevresponse, $newresponse, 'attachments')); 180 } 181 182 public function check_file_access($qa, $options, $component, $filearea, $args, $forcedownload) { 183 if ($component == 'question' && $filearea == 'response_attachments') { 184 // Response attachments visible if the question has them. 185 return $this->attachments != 0; 186 187 } else if ($component == 'question' && $filearea == 'response_answer') { 188 // Response attachments visible if the question has them. 189 return $this->responseformat === 'editorfilepicker'; 190 191 } else if ($component == 'qtype_essay' && $filearea == 'graderinfo') { 192 return $options->manualcomment && $args[0] == $this->id; 193 194 } else { 195 return parent::check_file_access($qa, $options, $component, 196 $filearea, $args, $forcedownload); 197 } 198 } 199 200 /** 201 * Return the question settings that define this question as structured data. 202 * 203 * @param question_attempt $qa the current attempt for which we are exporting the settings. 204 * @param question_display_options $options the question display options which say which aspects of the question 205 * should be visible. 206 * @return mixed structure representing the question settings. In web services, this will be JSON-encoded. 207 */ 208 public function get_question_definition_for_external_rendering(question_attempt $qa, question_display_options $options) { 209 // This is a partial implementation, returning only the most relevant question settings for now, 210 // ideally, we should return as much as settings as possible (depending on the state and display options). 211 212 $settings = [ 213 'responseformat' => $this->responseformat, 214 'responserequired' => $this->responserequired, 215 'responsefieldlines' => $this->responsefieldlines, 216 'attachments' => $this->attachments, 217 'attachmentsrequired' => $this->attachmentsrequired, 218 'maxbytes' => $this->maxbytes, 219 'filetypeslist' => $this->filetypeslist, 220 'responsetemplate' => $this->responsetemplate, 221 'responsetemplateformat' => $this->responsetemplateformat, 222 ]; 223 224 return $settings; 225 } 226 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body