Commit | Line | Data |
---|---|---|
41941110 | 1 | <?php |
41941110 EL |
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 | ||
a17b297d | 24 | |
41941110 EL |
25 | defined('MOODLE_INTERNAL') || die(); |
26 | ||
bd156853 | 27 | require_once($CFG->dirroot . '/question/type/multianswer/questiontype.php'); |
41941110 EL |
28 | /** |
29 | * restore plugin class that provides the necessary information | |
30 | * needed to restore one multianswer qtype plugin | |
f7970e3c TH |
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 | |
41941110 EL |
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() { | |
41941110 EL |
41 | $paths = array(); |
42 | ||
3d9645ae | 43 | // This qtype uses question_answers, add them. |
41941110 EL |
44 | $this->add_question_question_answers($paths); |
45 | ||
3d9645ae | 46 | // Add own qtype stuff. |
41941110 | 47 | $elename = 'multianswer'; |
59a3fcd3 | 48 | $elepath = $this->get_pathfor('/multianswer'); |
41941110 EL |
49 | $paths[] = new restore_path_element($elename, $elepath); |
50 | ||
3d9645ae | 51 | return $paths; // And we return the interesting paths. |
41941110 EL |
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 | ||
3d9645ae | 63 | // Detect if the question is created or mapped. |
41941110 EL |
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 | ||
59a3fcd3 | 68 | // If the question has been created by restore, we need to create its |
3d9645ae | 69 | // question_multianswer too. |
41941110 | 70 | if ($questioncreated) { |
3d9645ae | 71 | // Adjust some columns. |
41941110 EL |
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 | |
3d9645ae | 76 | // that gets executed once all questions have been created. |
77 | // Insert record. | |
41941110 | 78 | $newitemid = $DB->insert_record('question_multianswer', $data); |
3d9645ae | 79 | // Create mapping (need it for after_execute recode of sequence). |
41941110 | 80 | $this->set_mapping('question_multianswer', $oldid, $newitemid); |
41941110 EL |
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 | |
3d9645ae | 96 | // the created question_multianswer sequences (list of question ids). |
59a3fcd3 TH |
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())); | |
41941110 | 104 | foreach ($rs as $rec) { |
9a601adc FM |
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 | ||
41941110 EL |
110 | foreach ($sequencearr as $key => $question) { |
111 | $sequencearr[$key] = $this->get_mappingid('question', $question); | |
112 | } | |
113 | $sequence = implode(',', $sequencearr); | |
59a3fcd3 TH |
114 | $DB->set_field('question_multianswer', 'sequence', $sequence, |
115 | array('id' => $rec->id)); | |
bd156853 PP |
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); | |
abc24912 SL |
125 | if (isset($answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE]) && |
126 | $answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE] !== '') { | |
bd156853 PP |
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 | } | |
41941110 EL |
135 | } |
136 | $rs->close(); | |
137 | } | |
138 | ||
98a3898e TH |
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 | ||
41941110 EL |
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 | */ | |
18ab06ba | 174 | public function recode_legacy_state_answer($state) { |
41941110 EL |
175 | global $DB; |
176 | $answer = $state->answer; | |
177 | $resultarr = array(); | |
3d9645ae | 178 | // Get sequence of questions. |
59a3fcd3 TH |
179 | $sequence = $DB->get_field('question_multianswer', 'sequence', |
180 | array('question' => $state->question)); | |
41941110 | 181 | $sequencearr = explode(',', $sequence); |
3d9645ae | 182 | // Let's process each pair. |
41941110 EL |
183 | foreach (explode(',', $answer) as $pair) { |
184 | $pairarr = explode('-', $pair); | |
185 | $sequenceid = $pairarr[0]; | |
186 | $subanswer = $pairarr[1]; | |
3d9645ae | 187 | // Calculate the questionid based on sequenceid. |
188 | // Note it is already one *new* questionid that doesn't need mapping. | |
bd156853 | 189 | $questionid = $sequencearr[$sequenceid - 1]; |
3d9645ae | 190 | // Fetch qtype of the question (needed for delegation). |
41941110 | 191 | $questionqtype = $DB->get_field('question', 'qtype', array('id' => $questionid)); |
3d9645ae | 192 | // Delegate subanswer recode to proper qtype, faking one question_states record. |
41941110 EL |
193 | $substate = new stdClass(); |
194 | $substate->question = $questionid; | |
195 | $substate->answer = $subanswer; | |
18ab06ba | 196 | $newanswer = $this->step->restore_recode_legacy_answer($substate, $questionqtype); |
41941110 EL |
197 | $resultarr[] = implode('-', array($sequenceid, $newanswer)); |
198 | } | |
199 | return implode(',', $resultarr); | |
200 | } | |
201 | ||
202 | } |