MDL-14726 - rcache bit wrong in set_field
[moodle.git] / question / upgrade.php
CommitLineData
0c982226 1<?php // $Id$
bac40536 2/**
3 * This file contains dtabase upgrade code that is called from lib/db/upgrade.php,
4 * and also check methods that can be used for pre-install checks via
5 * admin/environment.php and lib/environmentlib.php.
6 *
7 * @copyright &copy; 2007 The Open University
8 * @author T.J.Hunt@open.ac.uk
9 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
0c982226 10 * @package questionbank
271e6dec 11 */
bac40536 12
13/**
14 * This test is becuase the RQP question type was included in core
15 * up to and including Moodle 1.8, and was removed before Moodle 1.9.
271e6dec 16 *
bac40536 17 * Therefore, we want to check whether any rqp questions exist in the database
271e6dec 18 * before doing the upgrade. However, the check is not relevant if that
19 * question type was never installed, or if the person has chosen to
bac40536 20 * manually reinstall the rqp question type from contrib.
271e6dec 21 *
22 * @param $result the result object that can be modified.
bac40536 23 * @return null if the test is irrelevant, or true or false depending on whether the test passes.
24 */
25function question_check_no_rqp_questions($result) {
26 global $CFG;
27
28 if (empty($CFG->qtype_rqp_version) || is_dir($CFG->dirroot . '/question/type/rqp')) {
29 return null;
30 } else {
31 $result->setStatus(count_records('question', 'qtype', 'rqp') == 0);
32 }
33 return $result;
34}
08103c93
ML
35
36function question_remove_rqp_qtype() {
efe256e4 37 global $CFG;
38
08103c93 39 $result = true;
271e6dec 40
efe256e4 41 // Only remove the question type if the code is gone.
42 if (!is_dir($CFG->dirroot . '/question/type/rqp')) {
43 $table = new XMLDBTable('question_rqp_states');
44 $result = $result && drop_table($table);
271e6dec 45
efe256e4 46 $table = new XMLDBTable('question_rqp');
47 $result = $result && drop_table($table);
271e6dec 48
efe256e4 49 $table = new XMLDBTable('question_rqp_types');
50 $result = $result && drop_table($table);
271e6dec 51
efe256e4 52 $table = new XMLDBTable('question_rqp_servers');
53 $result = $result && drop_table($table);
271e6dec 54
efe256e4 55 $result = $result && unset_config('qtype_rqp_version');
56 }
271e6dec 57
efe256e4 58 return $result;
59}
60
61function question_remove_rqp_qtype_config_string() {
62 global $CFG;
63
64 $result = true;
65
66 // An earlier, buggy version of the previous function missed out the unset_config call.
67 if (!empty($CFG->qtype_rqp_version) && !is_dir($CFG->dirroot . '/question/type/rqp')) {
68 $result = $result && unset_config('qtype_rqp_version');
69 }
271e6dec 70
71 return $result;
72}
73
74/**
75 * @param $result the result object that can be modified.
76 * @return null if the test is irrelevant, or true or false depending on whether the test passes.
77 */
78function question_random_check($result){
79 global $CFG;
fd3772dc 80 if (!empty($CFG->running_installer) //no test on first installation, no questions to test yet
81 || $CFG->version >= 2007081000){//no test after upgrade seperates question cats into contexts.
82 return null;
271e6dec 83 }
84 if (!$toupdate = question_cwqpfs_to_update()){
85 $result->setStatus(true);//pass test
86 } else {
87 //set the feedback string here and not in xml file since we need something
88 //more complex than just a string picked from admin.php lang file
89 $a = new object();
90 $a->reporturl = "{$CFG->wwwroot}/{$CFG->admin}/report/question/";
91 $lang = str_replace('_utf8', '', current_language());
92 $a->docsurl = "{$CFG->docroot}/$lang/admin/report/question/index";
afb36bca 93 $result->setFeedbackStr(array('questioncwqpfscheck', 'admin', $a));
271e6dec 94 $result->setStatus(false);//fail test
95 }
96 return $result;
97}
98/*
99 * Delete all 'random' questions that are not been used in a quiz.
100 */
101function question_delete_unused_random(){
102 global $CFG;
103 $tofix = array();
104 $result = true;
105 //delete all 'random' questions that are not been used in a quiz.
871ab9a3 106 if ($qqis = get_records_sql("SELECT q.* FROM {$CFG->prefix}question q LEFT JOIN ".
4aa80374 107 "{$CFG->prefix}quiz_question_instances qqi ".
108 "ON q.id = qqi.question WHERE q.qtype='random' AND qqi.question IS NULL")){
271e6dec 109 $qqilist = join(array_keys($qqis), ',');
110 $result = $result && delete_records_select('question', "id IN ($qqilist)");
111 }
112 return $result;
113}
114function question_cwqpfs_to_update($categories = null){
115 global $CFG;
116
117 $tofix = array();
118 $result = true;
119
120 //any cats with questions picking from subcats?
121 if (!$cwqpfs = get_records_sql_menu("SELECT DISTINCT qc.id, 1 ".
871ab9a3 122 "FROM {$CFG->prefix}question q, {$CFG->prefix}question_categories qc ".
c9bd9270 123 "WHERE q.qtype='random' AND qc.id = q.category AND ".
124 sql_compare_text('q.questiontext'). " = '1'")){
271e6dec 125 return array();
126 } else {
127 if ($categories === null){
128 $categories = get_records('question_categories');
129 }
130 $categorychildparents = array();
131 foreach ($categories as $id => $category){
132 $categorychildparents[$category->course][$id] = $category->parent;
133 }
134 foreach ($categories as $id => $category){
135 if (FALSE !== array_key_exists($category->parent, $categorychildparents[$category->course])){
136 //this is not a top level cat
137 continue;//go to next category
138 } else{
139 $tofix += question_cwqpfs_check_children($id, $categories, $categorychildparents[$category->course], $cwqpfs);
140 }
141 }
142 }
143
144 return $tofix;
145}
146
147function question_cwqpfs_check_children($checkid, $categories, $categorychildparents, $cwqpfs){
148 $tofix = array();
149 if (array_key_exists($checkid, $cwqpfs)){//cwqpfs in this cat
150 $getchildren = array();
151 $getchildren[] = $checkid;
152 //search down tree and find all children
153 while ($nextid = array_shift($getchildren)){//repeat until $getchildren
154 //empty;
155 $childids = array_keys($categorychildparents, $nextid);
156 foreach ($childids as $childid){
157 if ($categories[$childid]->publish != $categories[$checkid]->publish){
158 $tofix[$childid] = $categories[$checkid]->publish;
159 }
160 }
161 $getchildren = array_merge($getchildren, $childids);
162 }
163 } else { // check children for cwqpfs
164 $childrentocheck = array_keys($categorychildparents, $checkid);
165 foreach ($childrentocheck as $childtocheck){
166 $tofix += question_cwqpfs_check_children($childtocheck, $categories, $categorychildparents, $cwqpfs);
167 }
168 }
169 return $tofix;
170}
171
172function question_category_next_parent_in($contextid, $question_categories, $id){
173 $nextparent = $question_categories[$id]->parent;
174 if ($nextparent == 0){
175 return 0;
176 } elseif (!array_key_exists($nextparent, $question_categories)){
177 //finished searching up the category hierarchy. For some reason
178 //the top level items is not 0. We'll return 0 though.
179 return 0;
180 } elseif ($contextid == $question_categories[$nextparent]->contextid){
181 return $nextparent;
182 } else {
183 //parent is not in the same context look further up.
184 return question_category_next_parent_in($contextid, $question_categories, $nextparent);
185 }
186}
187
188
189/**
190 * Check that either category parent is 0 or a category shared in the same context.
191 * Fix any categories to point to grand or grand grand parent etc in the same context or 0.
192 */
193function question_category_checking($question_categories){
194 //make an array that is easier to search
195 $newparents = array();
196 foreach ($question_categories as $id => $category){
197 $newparents[$id] = question_category_next_parent_in($category->contextid, $question_categories, $id);
198 }
199 foreach (array_Keys($question_categories) as $id){
200 $question_categories[$id]->parent = $newparents[$id];
201 }
202 return $question_categories;
203}
204
205function question_upgrade_context_etc(){
206 global $CFG;
207 $result = true;
208 $result = $result && question_delete_unused_random();
209
210 $question_categories = get_records('question_categories');
0d6e8291 211 if ($question_categories){
212 //prepare content for new db structure
213 $tofix = question_cwqpfs_to_update($question_categories);
214 foreach ($tofix as $catid => $publish){
215 $question_categories[$catid]->publish = $publish;
216 }
271e6dec 217
0d6e8291 218 foreach ($question_categories as $id => $question_category){
219 $course = $question_categories[$id]->course;
220 unset($question_categories[$id]->course);
221 if ($question_categories[$id]->publish){
222 $context = get_context_instance(CONTEXT_SYSTEM);
223 //new name with old course name in brackets
224 $coursename = get_field('course', 'shortname', 'id', $course);
225 $question_categories[$id]->name .= " ($coursename)";
226 } else {
227 $context = get_context_instance(CONTEXT_COURSE, $course);
228 }
229 $question_categories[$id]->contextid = $context->id;
230 unset($question_categories[$id]->publish);
271e6dec 231
271e6dec 232 }
f3771632 233
0d6e8291 234 $question_categories = question_category_checking($question_categories);
271e6dec 235 }
236
271e6dec 237/// Define index course (not unique) to be dropped form question_categories
238 $table = new XMLDBTable('question_categories');
239 $index = new XMLDBIndex('course');
240 $index->setAttributes(XMLDB_INDEX_NOTUNIQUE, array('course'));
241
242/// Launch drop index course
243 $result = $result && drop_index($table, $index);
244
245/// Define field course to be dropped from question_categories
246 $field = new XMLDBField('course');
247
248/// Launch drop field course
249 $result = $result && drop_field($table, $field);
250
251/// Define field context to be added to question_categories
252 $field = new XMLDBField('contextid');
253 $field->setAttributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0', 'name');
254 $field->comment = 'context that this category is shared in';
255
256/// Launch add field context
257 $result = $result && add_field($table, $field);
258
259/// Define index context (not unique) to be added to question_categories
260 $index = new XMLDBIndex('contextid');
261 $index->setAttributes(XMLDB_INDEX_NOTUNIQUE, array('contextid'));
262 $index->comment = 'links to context table';
263
264/// Launch add index context
265 $result = $result && add_index($table, $index);
266
267 $field = new XMLDBField('publish');
268
269/// Launch drop field publish
270 $result = $result && drop_field($table, $field);
271
272
273 /// update table contents with previously calculated new contents.
0d6e8291 274 if ($question_categories){
8cf0f100 275 foreach ($question_categories as $question_category) {
276 $question_category->name = addslashes($question_category->name);
277 $question_category->info = addslashes($question_category->info);
0d6e8291 278 if (!$result = update_record('question_categories', $question_category)){
279 notify('Couldn\'t update question_categories "'. $question_category->name .'"!');
280 }
271e6dec 281 }
282 }
283
284/// Define field timecreated to be added to question
285 $table = new XMLDBTable('question');
286 $field = new XMLDBField('timecreated');
287 $field->setAttributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0', 'hidden');
288
289/// Launch add field timecreated
290 $result = $result && add_field($table, $field);
291
292/// Define field timemodified to be added to question
293 $table = new XMLDBTable('question');
294 $field = new XMLDBField('timemodified');
295 $field->setAttributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0', 'timecreated');
296
297/// Launch add field timemodified
298 $result = $result && add_field($table, $field);
299
300/// Define field createdby to be added to question
301 $table = new XMLDBTable('question');
302 $field = new XMLDBField('createdby');
303 $field->setAttributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null, null, null, 'timemodified');
304
305/// Launch add field createdby
306 $result = $result && add_field($table, $field);
307
308/// Define field modifiedby to be added to question
309 $table = new XMLDBTable('question');
310 $field = new XMLDBField('modifiedby');
311 $field->setAttributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null, null, null, 'createdby');
312
313/// Launch add field modifiedby
314 $result = $result && add_field($table, $field);
315
316/// Define key createdby (foreign) to be added to question
317 $table = new XMLDBTable('question');
318 $key = new XMLDBKey('createdby');
319 $key->setAttributes(XMLDB_KEY_FOREIGN, array('createdby'), 'user', array('id'));
320
321/// Launch add key createdby
322 $result = $result && add_key($table, $key);
323
324/// Define key modifiedby (foreign) to be added to question
325 $table = new XMLDBTable('question');
326 $key = new XMLDBKey('modifiedby');
327 $key->setAttributes(XMLDB_KEY_FOREIGN, array('modifiedby'), 'user', array('id'));
328
329/// Launch add key modifiedby
330 $result = $result && add_key($table, $key);
331
08103c93
ML
332 return $result;
333}
bac40536 334?>