e63b7773194fda8e560714bc12388559cb8e9899
[moodle.git] / question / type / multianswer / backup / moodle2 / restore_qtype_multianswer_plugin.class.php
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/>.
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  */
25 defined('MOODLE_INTERNAL') || die();
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 {
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();
43         // This qtype uses question_answers, add them.
44         $this->add_question_question_answers($paths);
46         // Add own qtype stuff.
47         $elename = 'multianswer';
48         $elepath = $this->get_pathfor('/multianswer');
49         $paths[] = new restore_path_element($elename, $elepath);
51         return $paths; // And we return the interesting paths.
52     }
54     /**
55      * Process the qtype/multianswer element
56      */
57     public function process_multianswer($data) {
58         global $DB;
60         $data = (object)$data;
61         $oldid = $data->id;
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;
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     }
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 = explode(',', $rec->sequence);
106             foreach ($sequencearr as $key => $question) {
107                 $sequencearr[$key] = $this->get_mappingid('question', $question);
108             }
109             $sequence = implode(',', $sequencearr);
110             $DB->set_field('question_multianswer', 'sequence', $sequence,
111                     array('id' => $rec->id));
112             if (!empty($sequence)) {
113                 // Get relevant data indexed by positionkey from the multianswers table.
114                 $wrappedquestions = $DB->get_records_list('question', 'id',
115                     explode(',', $sequence), 'id ASC');
116                 foreach ($wrappedquestions as $wrapped) {
117                     if ($wrapped->qtype == 'multichoice') {
118                         question_bank::get_qtype($wrapped->qtype)->get_question_options($wrapped);
119                         if (isset($wrapped->options->shuffleanswers)) {
120                             preg_match('/'.ANSWER_REGEX.'/s', $wrapped->questiontext, $answerregs);
121                             if ($answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE] != '' ) {
122                                 $wrapped->options->shuffleanswers = 0;
123                                 $DB->set_field_select('qtype_multichoice_options', 'shuffleanswers', '0', "id =:select",
124                                     array('select' => $wrapped->options->id) );
125                             }
126                         }
127                     }
128                 }
129             }
130         }
131         $rs->close();
132     }
134     public function recode_response($questionid, $sequencenumber, array $response) {
135         global $DB;
137         $qtypes = $DB->get_records_menu('question', array('parent' => $questionid),
138                 '', 'id, qtype');
140         $sequence = $DB->get_field('question_multianswer', 'sequence',
141                 array('question' => $questionid));
143         $fakestep = new question_attempt_step_read_only($response);
145         foreach (explode(',', $sequence) as $key => $subqid) {
146             $i = $key + 1;
148             $substep = new question_attempt_step_subquestion_adapter($fakestep, 'sub' . $i . '_');
149             $recodedresponse = $this->step->questions_recode_response_data($qtypes[$subqid],
150                     $subqid, $sequencenumber, $substep->get_all_data());
152             foreach ($recodedresponse as $name => $value) {
153                 $response[$substep->add_prefix($name)] = $value;
154             }
155         }
157         return $response;
158     }
160     /**
161      * Given one question_states record, return the answer
162      * recoded pointing to all the restored stuff for multianswer questions
163      *
164      * answer is one comma separated list of hypen separated pairs
165      * containing sequence (pointing to questions sequence in question_multianswer)
166      * and mixed answers. We'll delegate
167      * the recoding of answers to the proper qtype
168      */
169     public function recode_legacy_state_answer($state) {
170         global $DB;
171         $answer = $state->answer;
172         $resultarr = array();
173         // Get sequence of questions.
174         $sequence = $DB->get_field('question_multianswer', 'sequence',
175                 array('question' => $state->question));
176         $sequencearr = explode(',', $sequence);
177         // Let's process each pair.
178         foreach (explode(',', $answer) as $pair) {
179             $pairarr = explode('-', $pair);
180             $sequenceid = $pairarr[0];
181             $subanswer = $pairarr[1];
182             // Calculate the questionid based on sequenceid.
183             // Note it is already one *new* questionid that doesn't need mapping.
184             $questionid = $sequencearr[$sequenceid - 1];
185             // Fetch qtype of the question (needed for delegation).
186             $questionqtype = $DB->get_field('question', 'qtype', array('id' => $questionid));
187             // Delegate subanswer recode to proper qtype, faking one question_states record.
188             $substate = new stdClass();
189             $substate->question = $questionid;
190             $substate->answer = $subanswer;
191             $newanswer = $this->step->restore_recode_legacy_answer($substate, $questionqtype);
192             $resultarr[] = implode('-', array($sequenceid, $newanswer));
193         }
194         return implode(',', $resultarr);
195     }