| [ Index ] |
PHP Cross Reference of Moodle 1.9.3 [Build 15-Oct-2008] |
[Summary view] [Print] [Text view]
1 <?php // $Id$ 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 © 2007 The Open University 8 * @author T.J.Hunt@open.ac.uk 9 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License 10 * @package questionbank 11 */ 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. 16 * 17 * Therefore, we want to check whether any rqp questions exist in the database 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 20 * manually reinstall the rqp question type from contrib. 21 * 22 * @param $result the result object that can be modified. 23 * @return null if the test is irrelevant, or true or false depending on whether the test passes. 24 */ 25 function 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 } 35 36 function question_remove_rqp_qtype() { 37 global $CFG; 38 39 $result = true; 40 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); 45 46 $table = new XMLDBTable('question_rqp'); 47 $result = $result && drop_table($table); 48 49 $table = new XMLDBTable('question_rqp_types'); 50 $result = $result && drop_table($table); 51 52 $table = new XMLDBTable('question_rqp_servers'); 53 $result = $result && drop_table($table); 54 55 $result = $result && unset_config('qtype_rqp_version'); 56 } 57 58 return $result; 59 } 60 61 function 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 } 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 */ 78 function question_random_check($result){ 79 global $CFG; 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; 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"; 93 $result->setFeedbackStr(array('questioncwqpfscheck', 'admin', $a)); 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 */ 101 function 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. 106 if ($qqis = get_records_sql("SELECT q.* FROM {$CFG->prefix}question q LEFT JOIN ". 107 "{$CFG->prefix}quiz_question_instances qqi ". 108 "ON q.id = qqi.question WHERE q.qtype='random' AND qqi.question IS NULL")){ 109 $qqilist = join(array_keys($qqis), ','); 110 $result = $result && delete_records_select('question', "id IN ($qqilist)"); 111 } 112 return $result; 113 } 114 function 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 ". 122 "FROM {$CFG->prefix}question q, {$CFG->prefix}question_categories qc ". 123 "WHERE q.qtype='random' AND qc.id = q.category AND ". 124 sql_compare_text('q.questiontext'). " = '1'")){ 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 147 function 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 172 function question_category_next_parent_in($contextid, $question_categories, $id, $already_seen = array()){ 173 // Recursively look for an ancestor category of the given category that 174 // belongs to context $contextid. (In a lot of cases, the parent will be 175 // the one.) If there is none, return 0, meaning the top level. 176 $already_seen[] = $id; 177 178 $nextparent = $question_categories[$id]->parent; 179 if ($nextparent == 0) { 180 // Hit the top level, we are done. 181 return 0; 182 } else if (!array_key_exists($nextparent, $question_categories)) { 183 // The category hierarchy must have been screwed up before, in that 184 // we have run out of categories to search, but without reaching the 185 // top level. Repair the situation by returning 0, meaning top level. 186 notify(get_string('upgradeproblemunknowncategory', 'question', $question_categories[$id])); 187 return 0; 188 } else if (in_array($nextparent, $already_seen)) { 189 // The category hierarchy must have been screwed up before, in that 190 // we have just found a loop in the category 'tree'. That should, 191 // of course, be impossible, but it did acutally happen in at least once. 192 // Repair the situation by returning 0, meaning top level. 193 notify(get_string('upgradeproblemcategoryloop', 'question', implode(', ', $already_seen))); 194 return 0; 195 } else if ($contextid == $question_categories[$nextparent]->contextid) { 196 // Found a suitable category, we are done. 197 return $nextparent; 198 } else { 199 // The immediate parent is not in the same context, so look further up. 200 return question_category_next_parent_in($contextid, $question_categories, $nextparent, $already_seen); 201 } 202 } 203 204 /** 205 * Check that either category parent is 0 or a category shared in the same context. 206 * Fix any categories to point to grand or grand grand parent etc in the same context or 0. 207 */ 208 function question_category_checking($question_categories){ 209 //make an array that is easier to search 210 $newparents = array(); 211 foreach ($question_categories as $id => $category){ 212 $newparents[$id] = question_category_next_parent_in($category->contextid, $question_categories, $id); 213 } 214 foreach (array_keys($question_categories) as $id){ 215 $question_categories[$id]->parent = $newparents[$id]; 216 } 217 return $question_categories; 218 } 219 220 function question_upgrade_context_etc(){ 221 global $CFG; 222 $result = true; 223 $result = $result && question_delete_unused_random(); 224 225 $question_categories = get_records('question_categories'); 226 if ($question_categories) { 227 //prepare content for new db structure 228 $tofix = question_cwqpfs_to_update($question_categories); 229 foreach ($tofix as $catid => $publish){ 230 $question_categories[$catid]->publish = $publish; 231 } 232 233 foreach ($question_categories as $id => $question_category){ 234 $course = $question_categories[$id]->course; 235 unset($question_categories[$id]->course); 236 if ($question_categories[$id]->publish){ 237 $context = get_context_instance(CONTEXT_SYSTEM); 238 //new name with old course name in brackets 239 $coursename = get_field('course', 'shortname', 'id', $course); 240 $question_categories[$id]->name .= " ($coursename)"; 241 } else { 242 $context = get_context_instance(CONTEXT_COURSE, $course); 243 } 244 $question_categories[$id]->contextid = $context->id; 245 unset($question_categories[$id]->publish); 246 } 247 248 $question_categories = question_category_checking($question_categories); 249 } 250 251 /// Define index course (not unique) to be dropped form question_categories 252 $table = new XMLDBTable('question_categories'); 253 $index = new XMLDBIndex('course'); 254 $index->setAttributes(XMLDB_INDEX_NOTUNIQUE, array('course')); 255 256 /// Launch drop index course 257 $result = $result && drop_index($table, $index); 258 259 /// Define field course to be dropped from question_categories 260 $field = new XMLDBField('course'); 261 262 /// Launch drop field course 263 $result = $result && drop_field($table, $field); 264 265 /// Define field context to be added to question_categories 266 $field = new XMLDBField('contextid'); 267 $field->setAttributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0', 'name'); 268 $field->comment = 'context that this category is shared in'; 269 270 /// Launch add field context 271 $result = $result && add_field($table, $field); 272 273 /// Define index context (not unique) to be added to question_categories 274 $index = new XMLDBIndex('contextid'); 275 $index->setAttributes(XMLDB_INDEX_NOTUNIQUE, array('contextid')); 276 $index->comment = 'links to context table'; 277 278 /// Launch add index context 279 $result = $result && add_index($table, $index); 280 281 $field = new XMLDBField('publish'); 282 283 /// Launch drop field publish 284 $result = $result && drop_field($table, $field); 285 286 287 /// update table contents with previously calculated new contents. 288 if ($question_categories) { 289 foreach ($question_categories as $question_category) { 290 $question_category->name = addslashes($question_category->name); 291 $question_category->info = addslashes($question_category->info); 292 if (!$result = $result && update_record('question_categories', $question_category)){ 293 notify(get_string('upgradeproblemcouldnotupdatecategory', 'question', $question_category)); 294 } 295 } 296 } 297 298 /// Define field timecreated to be added to question 299 $table = new XMLDBTable('question'); 300 $field = new XMLDBField('timecreated'); 301 $field->setAttributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0', 'hidden'); 302 303 /// Launch add field timecreated 304 $result = $result && add_field($table, $field); 305 306 /// Define field timemodified to be added to question 307 $table = new XMLDBTable('question'); 308 $field = new XMLDBField('timemodified'); 309 $field->setAttributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0', 'timecreated'); 310 311 /// Launch add field timemodified 312 $result = $result && add_field($table, $field); 313 314 /// Define field createdby to be added to question 315 $table = new XMLDBTable('question'); 316 $field = new XMLDBField('createdby'); 317 $field->setAttributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null, null, null, 'timemodified'); 318 319 /// Launch add field createdby 320 $result = $result && add_field($table, $field); 321 322 /// Define field modifiedby to be added to question 323 $table = new XMLDBTable('question'); 324 $field = new XMLDBField('modifiedby'); 325 $field->setAttributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null, null, null, 'createdby'); 326 327 /// Launch add field modifiedby 328 $result = $result && add_field($table, $field); 329 330 /// Define key createdby (foreign) to be added to question 331 $table = new XMLDBTable('question'); 332 $key = new XMLDBKey('createdby'); 333 $key->setAttributes(XMLDB_KEY_FOREIGN, array('createdby'), 'user', array('id')); 334 335 /// Launch add key createdby 336 $result = $result && add_key($table, $key); 337 338 /// Define key modifiedby (foreign) to be added to question 339 $table = new XMLDBTable('question'); 340 $key = new XMLDBKey('modifiedby'); 341 $key->setAttributes(XMLDB_KEY_FOREIGN, array('modifiedby'), 'user', array('id')); 342 343 /// Launch add key modifiedby 344 $result = $result && add_key($table, $key); 345 346 return $result; 347 } 348 349 /** 350 * In Moodle, all random questions should have question.parent set to be the same 351 * as question.id. One effect of MDL-5482 is that this will not be true for questions that 352 * were backed up then restored. The probably does not cause many problems, except occasionally, 353 * if the bogus question.parent happens to point to a multianswer question type, or when you 354 * try to do a subsequent backup. Anyway, these question.parent values should be fixed, and 355 * that is what this update does. 356 */ 357 function question_fix_random_question_parents() { 358 global $CFG; 359 return execute_sql('UPDATE ' . $CFG->prefix . 'question SET parent = id ' . 360 "WHERE qtype = 'random' AND parent <> id"); 361 } 362 363 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Wed Jan 14 11:33:29 2009 | Cross-referenced by PHPXref 0.7 |