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 * @package moodlecore 19 * @subpackage backup-moodle2 20 * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com} 21 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 22 */ 23 24 25 defined('MOODLE_INTERNAL') || die(); 26 27 require_once($CFG->dirroot . '/question/type/multianswer/questiontype.php'); 28 /** 29 * restore plugin class that provides the necessary information 30 * needed to restore one multianswer qtype plugin 31 * 32 * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com} 33 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 34 */ 35 class restore_qtype_multianswer_plugin extends restore_qtype_plugin { 36 37 /** 38 * Returns the paths to be handled by the plugin at question level 39 */ 40 protected function define_question_plugin_structure() { 41 $paths = array(); 42 43 // This qtype uses question_answers, add them. 44 $this->add_question_question_answers($paths); 45 46 // Add own qtype stuff. 47 $elename = 'multianswer'; 48 $elepath = $this->get_pathfor('/multianswer'); 49 $paths[] = new restore_path_element($elename, $elepath); 50 51 return $paths; // And we return the interesting paths. 52 } 53 54 /** 55 * Process the qtype/multianswer element 56 */ 57 public function process_multianswer($data) { 58 global $DB; 59 60 $data = (object)$data; 61 $oldid = $data->id; 62 63 // Detect if the question is created or mapped. 64 $oldquestionid = $this->get_old_parentid('question'); 65 $newquestionid = $this->get_new_parentid('question'); 66 $questioncreated = $this->get_mappingid('question_created', $oldquestionid) ? true : false; 67 68 // If the question has been created by restore, we need to create its 69 // question_multianswer too. 70 if ($questioncreated) { 71 // Adjust some columns. 72 $data->question = $newquestionid; 73 // Note: multianswer->sequence is a list of question->id values. We aren't 74 // recoding them here (because some questions can be missing yet). Instead 75 // we'll perform the recode in the {@link after_execute} method of the plugin 76 // that gets executed once all questions have been created. 77 // Insert record. 78 $newitemid = $DB->insert_record('question_multianswer', $data); 79 // Create mapping (need it for after_execute recode of sequence). 80 $this->set_mapping('question_multianswer', $oldid, $newitemid); 81 } 82 } 83 84 /** 85 * This method is executed once the whole restore_structure_step 86 * this step is part of ({@link restore_create_categories_and_questions}) 87 * has ended processing the whole xml structure. Its name is: 88 * "after_execute_" + connectionpoint ("question") 89 * 90 * For multianswer qtype we use it to restore the sequence column, 91 * containing one list of question ids 92 */ 93 public function after_execute_question() { 94 global $DB; 95 // Now that all the questions have been restored, let's process 96 // the created question_multianswer sequences (list of question ids). 97 $rs = $DB->get_recordset_sql(" 98 SELECT qma.id, qma.sequence 99 FROM {question_multianswer} qma 100 JOIN {backup_ids_temp} bi ON bi.newitemid = qma.question 101 WHERE bi.backupid = ? 102 AND bi.itemname = 'question_created'", 103 array($this->get_restoreid())); 104 foreach ($rs as $rec) { 105 $sequencearr = preg_split('/,/', $rec->sequence, -1, PREG_SPLIT_NO_EMPTY); 106 if (substr_count($rec->sequence, ',') + 1 != count($sequencearr)) { 107 $this->task->log('Invalid sequence found in restored multianswer question ' . $rec->id, backup::LOG_WARNING); 108 } 109 110 foreach ($sequencearr as $key => $question) { 111 $sequencearr[$key] = $this->get_mappingid('question', $question); 112 } 113 $sequence = implode(',', $sequencearr); 114 $DB->set_field('question_multianswer', 'sequence', $sequence, 115 array('id' => $rec->id)); 116 if (!empty($sequence)) { 117 // Get relevant data indexed by positionkey from the multianswers table. 118 $wrappedquestions = $DB->get_records_list('question', 'id', 119 explode(',', $sequence), 'id ASC'); 120 foreach ($wrappedquestions as $wrapped) { 121 if ($wrapped->qtype == 'multichoice') { 122 question_bank::get_qtype($wrapped->qtype)->get_question_options($wrapped); 123 if (isset($wrapped->options->shuffleanswers)) { 124 preg_match('/'.ANSWER_REGEX.'/s', $wrapped->questiontext, $answerregs); 125 if (isset($answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE]) && 126 $answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE] !== '') { 127 $wrapped->options->shuffleanswers = 0; 128 $DB->set_field_select('qtype_multichoice_options', 'shuffleanswers', '0', "id =:select", 129 array('select' => $wrapped->options->id) ); 130 } 131 } 132 } 133 } 134 } 135 } 136 $rs->close(); 137 } 138 139 public function recode_response($questionid, $sequencenumber, array $response) { 140 global $DB; 141 142 $qtypes = $DB->get_records_menu('question', array('parent' => $questionid), 143 '', 'id, qtype'); 144 145 $sequence = $DB->get_field('question_multianswer', 'sequence', 146 array('question' => $questionid)); 147 148 $fakestep = new question_attempt_step_read_only($response); 149 150 foreach (explode(',', $sequence) as $key => $subqid) { 151 $i = $key + 1; 152 153 $substep = new question_attempt_step_subquestion_adapter($fakestep, 'sub' . $i . '_'); 154 $recodedresponse = $this->step->questions_recode_response_data($qtypes[$subqid], 155 $subqid, $sequencenumber, $substep->get_all_data()); 156 157 foreach ($recodedresponse as $name => $value) { 158 $response[$substep->add_prefix($name)] = $value; 159 } 160 } 161 162 return $response; 163 } 164 165 /** 166 * Given one question_states record, return the answer 167 * recoded pointing to all the restored stuff for multianswer questions 168 * 169 * answer is one comma separated list of hypen separated pairs 170 * containing sequence (pointing to questions sequence in question_multianswer) 171 * and mixed answers. We'll delegate 172 * the recoding of answers to the proper qtype 173 */ 174 public function recode_legacy_state_answer($state) { 175 global $DB; 176 $answer = $state->answer; 177 $resultarr = array(); 178 // Get sequence of questions. 179 $sequence = $DB->get_field('question_multianswer', 'sequence', 180 array('question' => $state->question)); 181 $sequencearr = explode(',', $sequence); 182 // Let's process each pair. 183 foreach (explode(',', $answer) as $pair) { 184 $pairarr = explode('-', $pair); 185 $sequenceid = $pairarr[0]; 186 $subanswer = $pairarr[1]; 187 // Calculate the questionid based on sequenceid. 188 // Note it is already one *new* questionid that doesn't need mapping. 189 $questionid = $sequencearr[$sequenceid - 1]; 190 // Fetch qtype of the question (needed for delegation). 191 $questionqtype = $DB->get_field('question', 'qtype', array('id' => $questionid)); 192 // Delegate subanswer recode to proper qtype, faking one question_states record. 193 $substate = new stdClass(); 194 $substate->question = $questionid; 195 $substate->answer = $subanswer; 196 $newanswer = $this->step->restore_recode_legacy_answer($substate, $questionqtype); 197 $resultarr[] = implode('-', array($sequenceid, $newanswer)); 198 } 199 return implode(',', $resultarr); 200 } 201 202 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body