[ Index ]

PHP Cross Reference of Moodle 1.9.3 [Build 15-Oct-2008]

title

Body

[close]

/question/ -> upgrade.php (source)

   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 &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
  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  ?>


Generated: Wed Jan 14 11:33:29 2009 Cross-referenced by PHPXref 0.7