MDL-26442 restore - missing new in qtype match exception
[moodle.git] / question / type / match / backup / moodle2 / restore_qtype_match_plugin.class.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * @package    moodlecore
20  * @subpackage backup-moodle2
21  * @copyright  2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 defined('MOODLE_INTERNAL') || die();
27 /**
28  * restore plugin class that provides the necessary information
29  * needed to restore one match qtype plugin
30  */
31 class restore_qtype_match_plugin extends restore_qtype_plugin {
33     /**
34      * Returns the paths to be handled by the plugin at question level
35      */
36     protected function define_question_plugin_structure() {
38         $paths = array();
40         // Add own qtype stuff
41         $elename = 'matchoptions';
42         $elepath = $this->get_pathfor('/matchoptions'); // we used get_recommended_name() so this works
43         $paths[] = new restore_path_element($elename, $elepath);
45         $elename = 'match';
46         $elepath = $this->get_pathfor('/matches/match'); // we used get_recommended_name() so this works
47         $paths[] = new restore_path_element($elename, $elepath);
50         return $paths; // And we return the interesting paths
51     }
53     /**
54      * Process the qtype/matchoptions element
55      */
56     public function process_matchoptions($data) {
57         global $DB;
59         $data = (object)$data;
60         $oldid = $data->id;
62         // Detect if the question is created or mapped
63         $oldquestionid   = $this->get_old_parentid('question');
64         $newquestionid   = $this->get_new_parentid('question');
65         $questioncreated = $this->get_mappingid('question_created', $oldquestionid) ? true : false;
67         // If the question has been created by restore, we need to create its question_match too
68         if ($questioncreated) {
69             // Adjust some columns
70             $data->question = $newquestionid;
71             // Keep question_match->subquestions unmodified
72             // after_execute_question() will perform the remapping once all subquestions
73             // have been created
74             // Insert record
75             $newitemid = $DB->insert_record('question_match', $data);
76             // Create mapping
77             $this->set_mapping('question_match', $oldid, $newitemid);
78         } else {
79             // Nothing to remap if the question already existed
80         }
81     }
83     /**
84      * Process the qtype/matches/match element
85      */
86     public function process_match($data) {
87         global $DB;
89         $data = (object)$data;
90         $oldid = $data->id;
92         // Detect if the question is created or mapped
93         $oldquestionid   = $this->get_old_parentid('question');
94         $newquestionid   = $this->get_new_parentid('question');
95         $questioncreated = $this->get_mappingid('question_created', $oldquestionid) ? true : false;
97         // If the question has been created by restore, we need to create its question_match_sub too
98         if ($questioncreated) {
99             // Adjust some columns
100             $data->question = $newquestionid;
101             // Insert record
102             $newitemid = $DB->insert_record('question_match_sub', $data);
103             // Create mapping (there are files and states based on this)
104             $this->set_mapping('question_match_sub', $oldid, $newitemid);
106         // match questions require mapping of question_match_sub, because
107         // they are used by question_states->answer
108         } else {
109             // Look for matching subquestion (by question, questiontext and answertext)
110             $sub = $DB->get_record_select('question_match_sub',
111                 'question = ? AND '.$DB->sql_compare_text('questiontext').' = '.$DB->sql_compare_text('?').' AND answertext = ?',
112                 array($newquestionid, $data->questiontext, $data->answertext), 'id', IGNORE_MULTIPLE);
113             // Found, let's create the mapping
114             if ($sub) {
115                 $this->set_mapping('question_match_sub', $oldid, $sub->id);
116             // Something went really wrong, cannot map subquestion for one match question
117             } else {
118                 throw new restore_step_exception('error_question_match_sub_missing_in_db', $data);
119             }
120         }
121     }
123     /**
124      * This method is executed once the whole restore_structure_step,
125      * more exactly ({@link restore_create_categories_and_questions})
126      * has ended processing the whole xml structure. Its name is:
127      * "after_execute_" + connectionpoint ("question")
128      *
129      * For match qtype we use it to restore the subquestions column,
130      * containing one list of question_match_sub ids
131      */
132     public function after_execute_question() {
133         global $DB;
134         // Now that all the question_match_subs have been restored, let's process
135         // the created question_match subquestions (list of question_match_sub ids)
136         $rs = $DB->get_recordset_sql("SELECT qm.id, qm.subquestions
137                                         FROM {question_match} qm
138                                         JOIN {backup_ids_temp} bi ON bi.newitemid = qm.question
139                                        WHERE bi.backupid = ?
140                                          AND bi.itemname = 'question_created'", array($this->get_restoreid()));
141         foreach ($rs as $rec) {
142             $subquestionsarr = explode(',', $rec->subquestions);
143             foreach ($subquestionsarr as $key => $subquestion) {
144                 $subquestionsarr[$key] = $this->get_mappingid('question_match_sub', $subquestion);
145             }
146             $subquestions = implode(',', $subquestionsarr);
147             $DB->set_field('question_match', 'subquestions', $subquestions, array('id' => $rec->id));
148         }
149         $rs->close();
150     }
152     /**
153      * Given one question_states record, return the answer
154      * recoded pointing to all the restored stuff for match questions
155      *
156      * answer is one comma separated list of hypen separated pairs
157      * containing question_match_sub->id and question_match_sub->code
158      */
159     public function recode_state_answer($state) {
160         $answer = $state->answer;
161         $resultarr = array();
162         foreach (explode(',', $answer) as $pair) {
163             $pairarr = explode('-', $pair);
164             $id = $pairarr[0];
165             $code = $pairarr[1];
166             $newid = $this->get_mappingid('question_match_sub', $id);
167             $resultarr[] = implode('-', array($newid, $code));
168         }
169         return implode(',', $resultarr);
170     }
172     /**
173      * Return the contents of this qtype to be processed by the links decoder
174      */
175     static public function define_decode_contents() {
177         $contents = array();
179         $contents[] = new restore_decode_content('question_match_sub', array('questiontext'), 'question_match_sub');
181         return $contents;
182     }