See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 39 and 401]
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 * Question type class for the matching question type. 19 * 20 * @package qtype_match 21 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 require_once($CFG->libdir . '/questionlib.php'); 29 require_once($CFG->dirroot . '/question/engine/lib.php'); 30 31 32 /** 33 * The matching question type class. 34 * 35 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} 36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 */ 38 class qtype_match extends question_type { 39 40 public function get_question_options($question) { 41 global $DB; 42 parent::get_question_options($question); 43 $question->options = $DB->get_record('qtype_match_options', 44 array('questionid' => $question->id)); 45 $question->options->subquestions = $DB->get_records('qtype_match_subquestions', 46 array('questionid' => $question->id), 'id ASC'); 47 return true; 48 } 49 50 public function save_defaults_for_new_questions(stdClass $fromform): void { 51 parent::save_defaults_for_new_questions($fromform); 52 $this->set_default_value('shuffleanswers', $fromform->shuffleanswers); 53 } 54 55 public function save_question_options($question) { 56 global $DB; 57 $context = $question->context; 58 $result = new stdClass(); 59 60 $oldsubquestions = $DB->get_records('qtype_match_subquestions', 61 array('questionid' => $question->id), 'id ASC'); 62 63 // Insert all the new question & answer pairs. 64 foreach ($question->subquestions as $key => $questiontext) { 65 if ($questiontext['text'] == '' && trim($question->subanswers[$key]) == '') { 66 continue; 67 } 68 if ($questiontext['text'] != '' && trim($question->subanswers[$key]) == '') { 69 $result->notice = get_string('nomatchinganswer', 'qtype_match', $questiontext); 70 } 71 72 // Update an existing subquestion if possible. 73 $subquestion = array_shift($oldsubquestions); 74 if (!$subquestion) { 75 $subquestion = new stdClass(); 76 $subquestion->questionid = $question->id; 77 $subquestion->questiontext = ''; 78 $subquestion->answertext = ''; 79 $subquestion->id = $DB->insert_record('qtype_match_subquestions', $subquestion); 80 } 81 82 $subquestion->questiontext = $this->import_or_save_files($questiontext, 83 $context, 'qtype_match', 'subquestion', $subquestion->id); 84 $subquestion->questiontextformat = $questiontext['format']; 85 $subquestion->answertext = trim($question->subanswers[$key]); 86 87 $DB->update_record('qtype_match_subquestions', $subquestion); 88 } 89 90 // Delete old subquestions records. 91 $fs = get_file_storage(); 92 foreach ($oldsubquestions as $oldsub) { 93 $fs->delete_area_files($context->id, 'qtype_match', 'subquestion', $oldsub->id); 94 $DB->delete_records('qtype_match_subquestions', array('id' => $oldsub->id)); 95 } 96 97 // Save the question options. 98 $options = $DB->get_record('qtype_match_options', array('questionid' => $question->id)); 99 if (!$options) { 100 $options = new stdClass(); 101 $options->questionid = $question->id; 102 $options->correctfeedback = ''; 103 $options->partiallycorrectfeedback = ''; 104 $options->incorrectfeedback = ''; 105 $options->id = $DB->insert_record('qtype_match_options', $options); 106 } 107 108 $options->shuffleanswers = $question->shuffleanswers; 109 $options = $this->save_combined_feedback_helper($options, $question, $context, true); 110 $DB->update_record('qtype_match_options', $options); 111 112 $this->save_hints($question, true); 113 114 if (!empty($result->notice)) { 115 return $result; 116 } 117 118 return true; 119 } 120 121 protected function initialise_question_instance(question_definition $question, $questiondata) { 122 parent::initialise_question_instance($question, $questiondata); 123 124 $question->shufflestems = $questiondata->options->shuffleanswers; 125 $this->initialise_combined_feedback($question, $questiondata, true); 126 127 $question->stems = array(); 128 $question->choices = array(); 129 $question->right = array(); 130 131 foreach ($questiondata->options->subquestions as $matchsub) { 132 $key = array_search($matchsub->answertext, $question->choices, true); 133 if ($key === false) { 134 $key = $matchsub->id; 135 $question->choices[$key] = $matchsub->answertext; 136 } 137 138 if ($matchsub->questiontext !== '') { 139 $question->stems[$matchsub->id] = $matchsub->questiontext; 140 $question->stemformat[$matchsub->id] = $matchsub->questiontextformat; 141 $question->right[$matchsub->id] = $key; 142 } 143 } 144 } 145 146 protected function make_hint($hint) { 147 return question_hint_with_parts::load_from_record($hint); 148 } 149 150 public function delete_question($questionid, $contextid) { 151 global $DB; 152 $DB->delete_records('qtype_match_options', array('questionid' => $questionid)); 153 $DB->delete_records('qtype_match_subquestions', array('questionid' => $questionid)); 154 155 parent::delete_question($questionid, $contextid); 156 } 157 158 public function get_random_guess_score($questiondata) { 159 $q = $this->make_question($questiondata); 160 return 1 / count($q->choices); 161 } 162 163 public function get_possible_responses($questiondata) { 164 $subqs = array(); 165 166 $q = $this->make_question($questiondata); 167 168 foreach ($q->stems as $stemid => $stem) { 169 170 $responses = array(); 171 foreach ($q->choices as $choiceid => $choice) { 172 $responses[$choiceid] = new question_possible_response( 173 $q->html_to_text($stem, $q->stemformat[$stemid]) . ': ' . $choice, 174 ($choiceid == $q->right[$stemid]) / count($q->stems)); 175 } 176 $responses[null] = question_possible_response::no_response(); 177 178 $subqs[$stemid] = $responses; 179 } 180 181 return $subqs; 182 } 183 184 public function move_files($questionid, $oldcontextid, $newcontextid) { 185 global $DB; 186 $fs = get_file_storage(); 187 188 parent::move_files($questionid, $oldcontextid, $newcontextid); 189 190 $subquestionids = $DB->get_records_menu('qtype_match_subquestions', 191 array('questionid' => $questionid), 'id', 'id,1'); 192 foreach ($subquestionids as $subquestionid => $notused) { 193 $fs->move_area_files_to_new_context($oldcontextid, 194 $newcontextid, 'qtype_match', 'subquestion', $subquestionid); 195 } 196 197 $this->move_files_in_combined_feedback($questionid, $oldcontextid, $newcontextid); 198 $this->move_files_in_hints($questionid, $oldcontextid, $newcontextid); 199 } 200 201 protected function delete_files($questionid, $contextid) { 202 global $DB; 203 $fs = get_file_storage(); 204 205 parent::delete_files($questionid, $contextid); 206 207 $subquestionids = $DB->get_records_menu('qtype_match_subquestions', 208 array('questionid' => $questionid), 'id', 'id,1'); 209 foreach ($subquestionids as $subquestionid => $notused) { 210 $fs->delete_area_files($contextid, 'qtype_match', 'subquestion', $subquestionid); 211 } 212 213 $this->delete_files_in_combined_feedback($questionid, $contextid); 214 $this->delete_files_in_hints($questionid, $contextid); 215 } 216 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body