See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 and 403]
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_question_options($question) { 51 global $DB; 52 $context = $question->context; 53 $result = new stdClass(); 54 55 $oldsubquestions = $DB->get_records('qtype_match_subquestions', 56 array('questionid' => $question->id), 'id ASC'); 57 58 // Insert all the new question & answer pairs. 59 foreach ($question->subquestions as $key => $questiontext) { 60 if ($questiontext['text'] == '' && trim($question->subanswers[$key]) == '') { 61 continue; 62 } 63 if ($questiontext['text'] != '' && trim($question->subanswers[$key]) == '') { 64 $result->notice = get_string('nomatchinganswer', 'qtype_match', $questiontext); 65 } 66 67 // Update an existing subquestion if possible. 68 $subquestion = array_shift($oldsubquestions); 69 if (!$subquestion) { 70 $subquestion = new stdClass(); 71 $subquestion->questionid = $question->id; 72 $subquestion->questiontext = ''; 73 $subquestion->answertext = ''; 74 $subquestion->id = $DB->insert_record('qtype_match_subquestions', $subquestion); 75 } 76 77 $subquestion->questiontext = $this->import_or_save_files($questiontext, 78 $context, 'qtype_match', 'subquestion', $subquestion->id); 79 $subquestion->questiontextformat = $questiontext['format']; 80 $subquestion->answertext = trim($question->subanswers[$key]); 81 82 $DB->update_record('qtype_match_subquestions', $subquestion); 83 } 84 85 // Delete old subquestions records. 86 $fs = get_file_storage(); 87 foreach ($oldsubquestions as $oldsub) { 88 $fs->delete_area_files($context->id, 'qtype_match', 'subquestion', $oldsub->id); 89 $DB->delete_records('qtype_match_subquestions', array('id' => $oldsub->id)); 90 } 91 92 // Save the question options. 93 $options = $DB->get_record('qtype_match_options', array('questionid' => $question->id)); 94 if (!$options) { 95 $options = new stdClass(); 96 $options->questionid = $question->id; 97 $options->correctfeedback = ''; 98 $options->partiallycorrectfeedback = ''; 99 $options->incorrectfeedback = ''; 100 $options->id = $DB->insert_record('qtype_match_options', $options); 101 } 102 103 $options->shuffleanswers = $question->shuffleanswers; 104 $options = $this->save_combined_feedback_helper($options, $question, $context, true); 105 $DB->update_record('qtype_match_options', $options); 106 107 $this->save_hints($question, true); 108 109 if (!empty($result->notice)) { 110 return $result; 111 } 112 113 return true; 114 } 115 116 protected function initialise_question_instance(question_definition $question, $questiondata) { 117 parent::initialise_question_instance($question, $questiondata); 118 119 $question->shufflestems = $questiondata->options->shuffleanswers; 120 $this->initialise_combined_feedback($question, $questiondata, true); 121 122 $question->stems = array(); 123 $question->choices = array(); 124 $question->right = array(); 125 126 foreach ($questiondata->options->subquestions as $matchsub) { 127 $key = array_search($matchsub->answertext, $question->choices, true); 128 if ($key === false) { 129 $key = $matchsub->id; 130 $question->choices[$key] = $matchsub->answertext; 131 } 132 133 if ($matchsub->questiontext !== '') { 134 $question->stems[$matchsub->id] = $matchsub->questiontext; 135 $question->stemformat[$matchsub->id] = $matchsub->questiontextformat; 136 $question->right[$matchsub->id] = $key; 137 } 138 } 139 } 140 141 protected function make_hint($hint) { 142 return question_hint_with_parts::load_from_record($hint); 143 } 144 145 public function delete_question($questionid, $contextid) { 146 global $DB; 147 $DB->delete_records('qtype_match_options', array('questionid' => $questionid)); 148 $DB->delete_records('qtype_match_subquestions', array('questionid' => $questionid)); 149 150 parent::delete_question($questionid, $contextid); 151 } 152 153 public function get_random_guess_score($questiondata) { 154 $q = $this->make_question($questiondata); 155 return 1 / count($q->choices); 156 } 157 158 public function get_possible_responses($questiondata) { 159 $subqs = array(); 160 161 $q = $this->make_question($questiondata); 162 163 foreach ($q->stems as $stemid => $stem) { 164 165 $responses = array(); 166 foreach ($q->choices as $choiceid => $choice) { 167 $responses[$choiceid] = new question_possible_response( 168 $q->html_to_text($stem, $q->stemformat[$stemid]) . ': ' . $choice, 169 ($choiceid == $q->right[$stemid]) / count($q->stems)); 170 } 171 $responses[null] = question_possible_response::no_response(); 172 173 $subqs[$stemid] = $responses; 174 } 175 176 return $subqs; 177 } 178 179 public function move_files($questionid, $oldcontextid, $newcontextid) { 180 global $DB; 181 $fs = get_file_storage(); 182 183 parent::move_files($questionid, $oldcontextid, $newcontextid); 184 185 $subquestionids = $DB->get_records_menu('qtype_match_subquestions', 186 array('questionid' => $questionid), 'id', 'id,1'); 187 foreach ($subquestionids as $subquestionid => $notused) { 188 $fs->move_area_files_to_new_context($oldcontextid, 189 $newcontextid, 'qtype_match', 'subquestion', $subquestionid); 190 } 191 192 $this->move_files_in_combined_feedback($questionid, $oldcontextid, $newcontextid); 193 $this->move_files_in_hints($questionid, $oldcontextid, $newcontextid); 194 } 195 196 protected function delete_files($questionid, $contextid) { 197 global $DB; 198 $fs = get_file_storage(); 199 200 parent::delete_files($questionid, $contextid); 201 202 $subquestionids = $DB->get_records_menu('qtype_match_subquestions', 203 array('questionid' => $questionid), 'id', 'id,1'); 204 foreach ($subquestionids as $subquestionid => $notused) { 205 $fs->delete_area_files($contextid, 'qtype_match', 'subquestion', $subquestionid); 206 } 207 208 $this->delete_files_in_combined_feedback($questionid, $contextid); 209 $this->delete_files_in_hints($questionid, $contextid); 210 } 211 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body