Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.
/lib/db/ -> upgrade.php (source)

Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]

   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/>.
  16  
  17  /**
  18   * This file keeps track of upgrades to Moodle.
  19   *
  20   * Sometimes, changes between versions involve
  21   * alterations to database structures and other
  22   * major things that may break installations.
  23   *
  24   * The upgrade function in this file will attempt
  25   * to perform all the necessary actions to upgrade
  26   * your older installation to the current version.
  27   *
  28   * If there's something it cannot do itself, it
  29   * will tell you what you need to do.
  30   *
  31   * The commands in here will all be database-neutral,
  32   * using the methods of database_manager class
  33   *
  34   * Please do not forget to use upgrade_set_timeout()
  35   * before any action that may take longer time to finish.
  36   *
  37   * @package   core_install
  38   * @category  upgrade
  39   * @copyright 2006 onwards Martin Dougiamas  http://dougiamas.com
  40   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  41   */
  42  
  43  defined('MOODLE_INTERNAL') || die();
  44  
  45  /**
  46   * Main upgrade tasks to be executed on Moodle version bump
  47   *
  48   * This function is automatically executed after one bump in the Moodle core
  49   * version is detected. It's in charge of performing the required tasks
  50   * to raise core from the previous version to the next one.
  51   *
  52   * It's a collection of ordered blocks of code, named "upgrade steps",
  53   * each one performing one isolated (from the rest of steps) task. Usually
  54   * tasks involve creating new DB objects or performing manipulation of the
  55   * information for cleanup/fixup purposes.
  56   *
  57   * Each upgrade step has a fixed structure, that can be summarised as follows:
  58   *
  59   * if ($oldversion < XXXXXXXXXX.XX) {
  60   *     // Explanation of the update step, linking to issue in the Tracker if necessary
  61   *     upgrade_set_timeout(XX); // Optional for big tasks
  62   *     // Code to execute goes here, usually the XMLDB Editor will
  63   *     // help you here. See {@link http://docs.moodle.org/dev/XMLDB_editor}.
  64   *     upgrade_main_savepoint(true, XXXXXXXXXX.XX);
  65   * }
  66   *
  67   * All plugins within Moodle (modules, blocks, reports...) support the existence of
  68   * their own upgrade.php file, using the "Frankenstyle" component name as
  69   * defined at {@link http://docs.moodle.org/dev/Frankenstyle}, for example:
  70   *     - {@link xmldb_page_upgrade($oldversion)}. (modules don't require the plugintype ("mod_") to be used.
  71   *     - {@link xmldb_auth_manual_upgrade($oldversion)}.
  72   *     - {@link xmldb_workshopform_accumulative_upgrade($oldversion)}.
  73   *     - ....
  74   *
  75   * In order to keep the contents of this file reduced, it's allowed to create some helper
  76   * functions to be used here in the {@link upgradelib.php} file at the same directory. Note
  77   * that such a file must be manually included from upgrade.php, and there are some restrictions
  78   * about what can be used within it.
  79   *
  80   * For more information, take a look to the documentation available:
  81   *     - Data definition API: {@link http://docs.moodle.org/dev/Data_definition_API}
  82   *     - Upgrade API: {@link http://docs.moodle.org/dev/Upgrade_API}
  83   *
  84   * @param int $oldversion
  85   * @return bool always true
  86   */
  87  function xmldb_main_upgrade($oldversion) {
  88      global $CFG, $DB;
  89  
  90      require_once($CFG->libdir.'/db/upgradelib.php'); // Core Upgrade-related functions.
  91  
  92      $dbman = $DB->get_manager(); // Loads ddl manager and xmldb classes.
  93  
  94      // Always keep this upgrade step with version being the minimum
  95      // allowed version to upgrade from (v3.6.0 right now).
  96      if ($oldversion < 2018120300) {
  97          // Just in case somebody hacks upgrade scripts or env, we really can not continue.
  98          echo("You need to upgrade to 3.6.x or higher first!\n");
  99          exit(1);
 100          // Note this savepoint is 100% unreachable, but needed to pass the upgrade checks.
 101          upgrade_main_savepoint(true, 2018120300);
 102      }
 103  
 104      // Automatically generated Moodle v3.6.0 release upgrade line.
 105      // Put any upgrade step following this.
 106  
 107      if ($oldversion < 2018120300.01) {
 108          // Update the FB logo URL.
 109          $oldurl = 'https://facebookbrand.com/wp-content/themes/fb-branding/prj-fb-branding/assets/images/fb-art.png';
 110          $newurl = 'https://facebookbrand.com/wp-content/uploads/2016/05/flogo_rgb_hex-brc-site-250.png';
 111  
 112          $updatesql = "UPDATE {oauth2_issuer}
 113                           SET image = :newimage
 114                         WHERE " . $DB->sql_compare_text('image', 100). " = :oldimage";
 115          $params = [
 116              'newimage' => $newurl,
 117              'oldimage' => $oldurl
 118          ];
 119          $DB->execute($updatesql, $params);
 120  
 121          upgrade_main_savepoint(true, 2018120300.01);
 122      }
 123  
 124      if ($oldversion < 2018120300.02) {
 125          // Set all individual conversations to enabled.
 126          $updatesql = "UPDATE {message_conversations}
 127                           SET enabled = :enabled
 128                         WHERE type = :type";
 129          $DB->execute($updatesql, ['enabled' => 1, 'type' => 1]);
 130  
 131          upgrade_main_savepoint(true, 2018120300.02);
 132      }
 133  
 134      if ($oldversion < 2018120301.02) {
 135          upgrade_delete_orphaned_file_records();
 136          upgrade_main_savepoint(true, 2018120301.02);
 137      }
 138  
 139      if ($oldversion < 2019011500.00) {
 140          // Define table task_log to be created.
 141          $table = new xmldb_table('task_log');
 142  
 143          // Adding fields to table task_log.
 144          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 145          $table->add_field('type', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, null);
 146          $table->add_field('component', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
 147          $table->add_field('classname', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
 148          $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 149          $table->add_field('timestart', XMLDB_TYPE_NUMBER, '20, 10', null, XMLDB_NOTNULL, null, null);
 150          $table->add_field('timeend', XMLDB_TYPE_NUMBER, '20, 10', null, XMLDB_NOTNULL, null, null);
 151          $table->add_field('dbreads', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 152          $table->add_field('dbwrites', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 153          $table->add_field('result', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, null);
 154  
 155          // Adding keys to table task_log.
 156          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
 157  
 158          // Adding indexes to table task_log.
 159          $table->add_index('classname', XMLDB_INDEX_NOTUNIQUE, ['classname']);
 160          $table->add_index('timestart', XMLDB_INDEX_NOTUNIQUE, ['timestart']);
 161  
 162          // Conditionally launch create table for task_log.
 163          if (!$dbman->table_exists($table)) {
 164              $dbman->create_table($table);
 165          }
 166  
 167          // Main savepoint reached.
 168          upgrade_main_savepoint(true, 2019011500.00);
 169      }
 170  
 171      if ($oldversion < 2019011501.00) {
 172          // Define field output to be added to task_log.
 173          $table = new xmldb_table('task_log');
 174          $field = new xmldb_field('output', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null, 'result');
 175  
 176          // Conditionally launch add field output.
 177          if (!$dbman->field_exists($table, $field)) {
 178              $dbman->add_field($table, $field);
 179          }
 180  
 181          // Main savepoint reached.
 182          upgrade_main_savepoint(true, 2019011501.00);
 183      }
 184  
 185      if ($oldversion < 2019011801.00) {
 186  
 187          // Define table customfield_category to be created.
 188          $table = new xmldb_table('customfield_category');
 189  
 190          // Adding fields to table customfield_category.
 191          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 192          $table->add_field('name', XMLDB_TYPE_CHAR, '400', null, XMLDB_NOTNULL, null, null);
 193          $table->add_field('description', XMLDB_TYPE_TEXT, null, null, null, null, null);
 194          $table->add_field('descriptionformat', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
 195          $table->add_field('sortorder', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
 196          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 197          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 198          $table->add_field('component', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
 199          $table->add_field('area', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
 200          $table->add_field('itemid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
 201          $table->add_field('contextid', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
 202  
 203          // Adding keys to table customfield_category.
 204          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
 205          $table->add_key('contextid', XMLDB_KEY_FOREIGN, ['contextid'], 'context', ['id']);
 206  
 207          // Adding indexes to table customfield_category.
 208          $table->add_index('component_area_itemid', XMLDB_INDEX_NOTUNIQUE, ['component', 'area', 'itemid', 'sortorder']);
 209  
 210          // Conditionally launch create table for customfield_category.
 211          if (!$dbman->table_exists($table)) {
 212              $dbman->create_table($table);
 213          }
 214  
 215          // Define table customfield_field to be created.
 216          $table = new xmldb_table('customfield_field');
 217  
 218          // Adding fields to table customfield_field.
 219          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 220          $table->add_field('shortname', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
 221          $table->add_field('name', XMLDB_TYPE_CHAR, '400', null, XMLDB_NOTNULL, null, null);
 222          $table->add_field('type', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
 223          $table->add_field('description', XMLDB_TYPE_TEXT, null, null, null, null, null);
 224          $table->add_field('descriptionformat', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
 225          $table->add_field('sortorder', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
 226          $table->add_field('categoryid', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
 227          $table->add_field('configdata', XMLDB_TYPE_TEXT, null, null, null, null, null);
 228          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 229          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 230  
 231          // Adding keys to table customfield_field.
 232          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
 233          $table->add_key('categoryid', XMLDB_KEY_FOREIGN, ['categoryid'], 'customfield_category', ['id']);
 234  
 235          // Adding indexes to table customfield_field.
 236          $table->add_index('categoryid_sortorder', XMLDB_INDEX_NOTUNIQUE, ['categoryid', 'sortorder']);
 237  
 238          // Conditionally launch create table for customfield_field.
 239          if (!$dbman->table_exists($table)) {
 240              $dbman->create_table($table);
 241          }
 242  
 243          // Define table customfield_data to be created.
 244          $table = new xmldb_table('customfield_data');
 245  
 246          // Adding fields to table customfield_data.
 247          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 248          $table->add_field('fieldid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 249          $table->add_field('instanceid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 250          $table->add_field('intvalue', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
 251          $table->add_field('decvalue', XMLDB_TYPE_NUMBER, '10, 5', null, null, null, null);
 252          $table->add_field('shortcharvalue', XMLDB_TYPE_CHAR, '255', null, null, null, null);
 253          $table->add_field('charvalue', XMLDB_TYPE_CHAR, '1333', null, null, null, null);
 254          $table->add_field('value', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
 255          $table->add_field('valueformat', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 256          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 257          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 258          $table->add_field('contextid', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
 259  
 260          // Adding keys to table customfield_data.
 261          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
 262          $table->add_key('fieldid', XMLDB_KEY_FOREIGN, ['fieldid'], 'customfield_field', ['id']);
 263          $table->add_key('contextid', XMLDB_KEY_FOREIGN, ['contextid'], 'context', ['id']);
 264  
 265          // Adding indexes to table customfield_data.
 266          $table->add_index('instanceid-fieldid', XMLDB_INDEX_UNIQUE, ['instanceid', 'fieldid']);
 267          $table->add_index('fieldid-intvalue', XMLDB_INDEX_NOTUNIQUE, ['fieldid', 'intvalue']);
 268          $table->add_index('fieldid-shortcharvalue', XMLDB_INDEX_NOTUNIQUE, ['fieldid', 'shortcharvalue']);
 269          $table->add_index('fieldid-decvalue', XMLDB_INDEX_NOTUNIQUE, ['fieldid', 'decvalue']);
 270  
 271          // Conditionally launch create table for customfield_data.
 272          if (!$dbman->table_exists($table)) {
 273              $dbman->create_table($table);
 274          }
 275  
 276          upgrade_main_savepoint(true, 2019011801.00);
 277      }
 278  
 279      if ($oldversion < 2019011801.01) {
 280  
 281          // Delete all files that have been used in sections, which are already deleted.
 282          $sql = "SELECT DISTINCT f.itemid as sectionid, f.contextid
 283                    FROM {files} f
 284               LEFT JOIN {course_sections} s ON f.itemid = s.id
 285                   WHERE f.component = :component AND f.filearea = :filearea AND s.id IS NULL ";
 286  
 287          $params = [
 288              'component' => 'course',
 289              'filearea' => 'section'
 290          ];
 291  
 292          $stalefiles = $DB->get_recordset_sql($sql, $params);
 293  
 294          $fs = get_file_storage();
 295          foreach ($stalefiles as $stalefile) {
 296              $fs->delete_area_files($stalefile->contextid, 'course', 'section', $stalefile->sectionid);
 297          }
 298          $stalefiles->close();
 299  
 300          upgrade_main_savepoint(true, 2019011801.01);
 301      }
 302  
 303      if ($oldversion < 2019011801.02) {
 304          // Add index 'useridfrom' to the table 'notifications'.
 305          $table = new xmldb_table('notifications');
 306          $index = new xmldb_index('useridfrom', XMLDB_INDEX_NOTUNIQUE, ['useridfrom']);
 307  
 308          if (!$dbman->index_exists($table, $index)) {
 309              $dbman->add_index($table, $index);
 310          }
 311  
 312          upgrade_main_savepoint(true, 2019011801.02);
 313      }
 314  
 315      if ($oldversion < 2019011801.03) {
 316          // Remove duplicate entries from group memberships.
 317          // Find records with multiple userid/groupid combinations and find the highest ID.
 318          // Later we will remove all those entries.
 319          $sql = "
 320              SELECT MIN(id) as minid, userid, groupid
 321              FROM {groups_members}
 322              GROUP BY userid, groupid
 323              HAVING COUNT(id) > 1";
 324          if ($duplicatedrows = $DB->get_recordset_sql($sql)) {
 325              foreach ($duplicatedrows as $row) {
 326                  $DB->delete_records_select('groups_members',
 327                      'userid = :userid AND groupid = :groupid AND id <> :minid', (array)$row);
 328              }
 329          }
 330          $duplicatedrows->close();
 331  
 332          // Define key useridgroupid (unique) to be added to group_members.
 333          $table = new xmldb_table('groups_members');
 334          $key = new xmldb_key('useridgroupid', XMLDB_KEY_UNIQUE, array('userid', 'groupid'));
 335          // Launch add key useridgroupid.
 336          $dbman->add_key($table, $key);
 337          // Main savepoint reached.
 338          upgrade_main_savepoint(true, 2019011801.03);
 339      }
 340  
 341      if ($oldversion < 2019021500.01) {
 342          $insights = $DB->get_record('message_providers', ['component' => 'moodle', 'name' => 'insights']);
 343          if (!empty($insights)) {
 344              $insights->capability = null;
 345              $DB->update_record('message_providers', $insights);
 346          }
 347          upgrade_main_savepoint(true, 2019021500.01);
 348      }
 349  
 350      if ($oldversion < 2019021500.02) {
 351          // Default 'off' for existing sites as this is the behaviour they had earlier.
 352          set_config('messagingdefaultpressenter', false);
 353  
 354          // Main savepoint reached.
 355          upgrade_main_savepoint(true, 2019021500.02);
 356      }
 357  
 358      if ($oldversion < 2019030100.01) {
 359          // Create adhoc task to delete renamed My Course search area (ID core_course-mycourse).
 360          $record = new \stdClass();
 361          $record->classname = '\core\task\clean_up_deleted_search_area_task';
 362          $record->component = 'core';
 363  
 364          // Next run time based from nextruntime computation in \core\task\manager::queue_adhoc_task().
 365          $nextruntime = time() - 1;
 366          $record->nextruntime = $nextruntime;
 367          $record->customdata = json_encode('core_course-mycourse');
 368  
 369          $DB->insert_record('task_adhoc', $record);
 370  
 371          // Main savepoint reached.
 372          upgrade_main_savepoint(true, 2019030100.01);
 373      }
 374  
 375      if ($oldversion < 2019030700.01) {
 376  
 377          // Define field evaluationmode to be added to analytics_models_log.
 378          $table = new xmldb_table('analytics_models_log');
 379          $field = new xmldb_field('evaluationmode', XMLDB_TYPE_CHAR, '50', null, null, null,
 380              null, 'version');
 381  
 382          // Conditionally launch add field evaluationmode.
 383          if (!$dbman->field_exists($table, $field)) {
 384              $dbman->add_field($table, $field);
 385  
 386              $updatesql = "UPDATE {analytics_models_log}
 387                               SET evaluationmode = 'configuration'";
 388              $DB->execute($updatesql, []);
 389  
 390              // Changing nullability of field evaluationmode on table block_instances to not null.
 391              $field = new xmldb_field('evaluationmode', XMLDB_TYPE_CHAR, '50', null, XMLDB_NOTNULL,
 392                  null, null, 'version');
 393  
 394              // Launch change of nullability for field evaluationmode.
 395              $dbman->change_field_notnull($table, $field);
 396          }
 397  
 398          // Main savepoint reached.
 399          upgrade_main_savepoint(true, 2019030700.01);
 400      }
 401  
 402      if ($oldversion < 2019030800.00) {
 403          // Define table 'message_conversation_actions' to be created.
 404          // Note - I would have preferred 'message_conversation_user_actions' but due to Oracle we can't. Boo.
 405          $table = new xmldb_table('message_conversation_actions');
 406  
 407          // Adding fields to table 'message_conversation_actions'.
 408          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 409          $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 410          $table->add_field('conversationid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 411          $table->add_field('action', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 412          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 413  
 414          // Adding keys to table 'message_conversation_actions'.
 415          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
 416          $table->add_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']);
 417          $table->add_key('conversationid', XMLDB_KEY_FOREIGN, ['conversationid'], 'message_conversations', ['id']);
 418  
 419          // Conditionally launch create table for 'message_conversation_actions'.
 420          if (!$dbman->table_exists($table)) {
 421              $dbman->create_table($table);
 422          }
 423  
 424          // Main savepoint reached.
 425          upgrade_main_savepoint(true, 2019030800.00);
 426      }
 427  
 428      if ($oldversion < 2019030800.02) {
 429          // Remove any conversations and their members associated with non-existent groups.
 430          $sql = "SELECT mc.id
 431                    FROM {message_conversations} mc
 432               LEFT JOIN {groups} g
 433                      ON mc.itemid = g.id
 434                   WHERE mc.component = :component
 435                     AND mc.itemtype = :itemtype
 436                     AND g.id is NULL";
 437          $conversations = $DB->get_records_sql($sql, ['component' => 'core_group', 'itemtype' => 'groups']);
 438  
 439          if ($conversations) {
 440              $conversationids = array_keys($conversations);
 441  
 442              $DB->delete_records_list('message_conversations', 'id', $conversationids);
 443              $DB->delete_records_list('message_conversation_members', 'conversationid', $conversationids);
 444              $DB->delete_records_list('message_conversation_actions', 'conversationid', $conversationids);
 445  
 446              // Now, go through each conversation and delete any messages and related message actions.
 447              foreach ($conversationids as $conversationid) {
 448                  if ($messages = $DB->get_records('messages', ['conversationid' => $conversationid])) {
 449                      $messageids = array_keys($messages);
 450  
 451                      // Delete the actions.
 452                      list($insql, $inparams) = $DB->get_in_or_equal($messageids);
 453                      $DB->delete_records_select('message_user_actions', "messageid $insql", $inparams);
 454  
 455                      // Delete the messages.
 456                      $DB->delete_records('messages', ['conversationid' => $conversationid]);
 457                  }
 458              }
 459          }
 460  
 461          // Main savepoint reached.
 462          upgrade_main_savepoint(true, 2019030800.02);
 463      }
 464  
 465      if ($oldversion < 2019030800.03) {
 466  
 467          // Add missing indicators to course_dropout.
 468          $params = [
 469              'target' => '\core\analytics\target\course_dropout',
 470              'trained' => 0,
 471              'enabled' => 0,
 472          ];
 473          $models = $DB->get_records('analytics_models', $params);
 474          foreach ($models as $model) {
 475              $indicators = json_decode($model->indicators);
 476  
 477              $potentiallymissingindicators = [
 478                  '\core_course\analytics\indicator\completion_enabled',
 479                  '\core_course\analytics\indicator\potential_cognitive_depth',
 480                  '\core_course\analytics\indicator\potential_social_breadth',
 481                  '\core\analytics\indicator\any_access_after_end',
 482                  '\core\analytics\indicator\any_access_before_start',
 483                  '\core\analytics\indicator\any_write_action_in_course',
 484                  '\core\analytics\indicator\read_actions'
 485              ];
 486  
 487              $missing = false;
 488              foreach ($potentiallymissingindicators as $potentiallymissingindicator) {
 489                  if (!in_array($potentiallymissingindicator, $indicators)) {
 490                      // Add the missing indicator to sites upgraded before 2017072000.02.
 491                      $indicators[] = $potentiallymissingindicator;
 492                      $missing = true;
 493                  }
 494              }
 495  
 496              if ($missing) {
 497                  $model->indicators = json_encode($indicators);
 498                  $model->version = time();
 499                  $model->timemodified = time();
 500                  $DB->update_record('analytics_models', $model);
 501              }
 502          }
 503  
 504          // Add missing indicators to no_teaching.
 505          $params = [
 506              'target' => '\core\analytics\target\no_teaching',
 507          ];
 508          $models = $DB->get_records('analytics_models', $params);
 509          foreach ($models as $model) {
 510              $indicators = json_decode($model->indicators);
 511              if (!in_array('\core_course\analytics\indicator\no_student', $indicators)) {
 512                  // Add the missing indicator to sites upgraded before 2017072000.02.
 513  
 514                  $indicators[] = '\core_course\analytics\indicator\no_student';
 515  
 516                  $model->indicators = json_encode($indicators);
 517                  $model->version = time();
 518                  $model->timemodified = time();
 519                  $DB->update_record('analytics_models', $model);
 520              }
 521          }
 522  
 523          // Main savepoint reached.
 524          upgrade_main_savepoint(true, 2019030800.03);
 525      }
 526  
 527      if ($oldversion < 2019031500.01) {
 528  
 529          $defaulttimesplittings = get_config('analytics', 'timesplittings');
 530          if ($defaulttimesplittings !== false) {
 531              set_config('defaulttimesplittingsevaluation', $defaulttimesplittings, 'analytics');
 532              unset_config('timesplittings', 'analytics');
 533          }
 534  
 535          // Main savepoint reached.
 536          upgrade_main_savepoint(true, 2019031500.01);
 537      }
 538  
 539      if ($oldversion < 2019032200.02) {
 540          // The no_teaching model might have been marked as not-trained by mistake (static models are always trained).
 541          $DB->set_field('analytics_models', 'trained', 1, ['target' => '\core\analytics\target\no_teaching']);
 542          upgrade_main_savepoint(true, 2019032200.02);
 543      }
 544  
 545      if ($oldversion < 2019032900.00) {
 546  
 547          // Define table badge_competencies to be renamed to badge_alignment.
 548          $table = new xmldb_table('badge_competencies');
 549  
 550          // Be careful if this step gets run twice.
 551          if ($dbman->table_exists($table)) {
 552              $key = new xmldb_key('competenciesbadge', XMLDB_KEY_FOREIGN, ['badgeid'], 'badge', ['id']);
 553  
 554              // Launch drop key competenciesbadge.
 555              $dbman->drop_key($table, $key);
 556  
 557              $key = new xmldb_key('alignmentsbadge', XMLDB_KEY_FOREIGN, ['badgeid'], 'badge', ['id']);
 558  
 559              // Launch add key alignmentsbadge.
 560              $dbman->add_key($table, $key);
 561  
 562              // Launch rename table for badge_alignment.
 563              $dbman->rename_table($table, 'badge_alignment');
 564          }
 565  
 566          upgrade_main_savepoint(true, 2019032900.00);
 567      }
 568  
 569      if ($oldversion < 2019032900.01) {
 570          $sql = "UPDATE {task_scheduled}
 571                     SET classname = ?
 572                   WHERE component = ?
 573                     AND classname = ?";
 574          $DB->execute($sql, [
 575              '\core\task\question_preview_cleanup_task',
 576              'moodle',
 577              '\core\task\question_cron_task'
 578          ]);
 579  
 580          // Main savepoint reached.
 581          upgrade_main_savepoint(true, 2019032900.01);
 582       }
 583  
 584      if ($oldversion < 2019040200.01) {
 585          // Removing the themes BSB, Clean, More from core.
 586          // If these theme wish to be retained empty this array before upgrade.
 587          $themes = array('theme_bootstrapbase' => 'bootstrapbase',
 588                  'theme_clean' => 'clean', 'theme_more' => 'more');
 589          foreach ($themes as $key => $theme) {
 590              if (check_dir_exists($CFG->dirroot . '/theme/' . $theme, false)) {
 591                  // Ignore the themes that have been re-downloaded.
 592                  unset($themes[$key]);
 593              }
 594          }
 595          // Check we actually have themes to remove.
 596          if (count($themes) > 0) {
 597              list($insql, $inparams) = $DB->get_in_or_equal($themes, SQL_PARAMS_NAMED);
 598  
 599              // Replace the theme usage.
 600              $DB->set_field_select('course', 'theme', 'classic', "theme $insql", $inparams);
 601              $DB->set_field_select('course_categories', 'theme', 'classic', "theme $insql", $inparams);
 602              $DB->set_field_select('user', 'theme', 'classic', "theme $insql", $inparams);
 603              $DB->set_field_select('mnet_host', 'theme', 'classic', "theme $insql", $inparams);
 604              $DB->set_field_select('cohort', 'theme', 'classic', "theme $insql", $inparams);
 605  
 606              // Replace the theme configs.
 607              if (in_array(get_config('core', 'theme'), $themes)) {
 608                  set_config('theme', 'classic');
 609              }
 610              if (in_array(get_config('core', 'thememobile'), $themes)) {
 611                  set_config('thememobile', 'classic');
 612              }
 613              if (in_array(get_config('core', 'themelegacy'), $themes)) {
 614                  set_config('themelegacy', 'classic');
 615              }
 616              if (in_array(get_config('core', 'themetablet'), $themes)) {
 617                  set_config('themetablet', 'classic');
 618              }
 619  
 620              // Hacky emulation of plugin uninstallation.
 621              foreach ($themes as $key => $theme) {
 622                  unset_all_config_for_plugin($key);
 623              }
 624          }
 625  
 626          // Main savepoint reached.
 627          upgrade_main_savepoint(true, 2019040200.01);
 628      }
 629  
 630      if ($oldversion < 2019040600.02) {
 631  
 632          // Define key fileid (foreign) to be dropped form analytics_train_samples.
 633          $table = new xmldb_table('analytics_train_samples');
 634          $key = new xmldb_key('fileid', XMLDB_KEY_FOREIGN, ['fileid'], 'files', ['id']);
 635  
 636          // Launch drop key fileid.
 637          $dbman->drop_key($table, $key);
 638  
 639          // Define field fileid to be dropped from analytics_train_samples.
 640          $table = new xmldb_table('analytics_train_samples');
 641          $field = new xmldb_field('fileid');
 642  
 643          // Conditionally launch drop field fileid.
 644          if ($dbman->field_exists($table, $field)) {
 645              $dbman->drop_field($table, $field);
 646          }
 647  
 648          // Main savepoint reached.
 649          upgrade_main_savepoint(true, 2019040600.02);
 650      }
 651  
 652      if ($oldversion < 2019040600.04) {
 653          // Define field and index to be added to backup_controllers.
 654          $table = new xmldb_table('backup_controllers');
 655          $field = new xmldb_field('progress', XMLDB_TYPE_NUMBER, '15, 14', null, XMLDB_NOTNULL, null, '0', 'timemodified');
 656          $index = new xmldb_index('useritem_ix', XMLDB_INDEX_NOTUNIQUE, ['userid', 'itemid']);
 657          // Conditionally launch add field progress.
 658          if (!$dbman->field_exists($table, $field)) {
 659              $dbman->add_field($table, $field);
 660          }
 661          // Conditionally launch add index useritem_ix.
 662          if (!$dbman->index_exists($table, $index)) {
 663              $dbman->add_index($table, $index);
 664          }
 665  
 666          // Main savepoint reached.
 667          upgrade_main_savepoint(true, 2019040600.04);
 668      }
 669  
 670      if ($oldversion < 2019041000.02) {
 671  
 672          // Define field fullmessagetrust to be added to messages.
 673          $table = new xmldb_table('messages');
 674          $field = new xmldb_field('fullmessagetrust', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'timecreated');
 675  
 676          // Conditionally launch add field fullmessagetrust.
 677          if (!$dbman->field_exists($table, $field)) {
 678              $dbman->add_field($table, $field);
 679          }
 680  
 681          // Main savepoint reached.
 682          upgrade_main_savepoint(true, 2019041000.02);
 683      }
 684  
 685      if ($oldversion < 2019041300.01) {
 686          // Add the field 'name' to the 'analytics_models' table.
 687          $table = new xmldb_table('analytics_models');
 688          $field = new xmldb_field('name', XMLDB_TYPE_CHAR, '1333', null, null, null, null, 'trained');
 689  
 690          if (!$dbman->field_exists($table, $field)) {
 691              $dbman->add_field($table, $field);
 692          }
 693          // Main savepoint reached.
 694          upgrade_main_savepoint(true, 2019041300.01);
 695      }
 696  
 697      if ($oldversion < 2019041800.01) {
 698          // STEP 1. For the existing and migrated self-conversations, set the type to the new MESSAGE_CONVERSATION_TYPE_SELF, update
 699          // the convhash and star them.
 700          $sql = "SELECT mcm.conversationid, mcm.userid, MAX(mcm.id) as maxid
 701                    FROM {message_conversation_members} mcm
 702              INNER JOIN {user} u ON mcm.userid = u.id
 703                   WHERE u.deleted = 0
 704                GROUP BY mcm.conversationid, mcm.userid
 705                  HAVING COUNT(*) > 1";
 706          $selfconversationsrs = $DB->get_recordset_sql($sql);
 707          $maxids = [];
 708          foreach ($selfconversationsrs as $selfconversation) {
 709              $DB->update_record('message_conversations',
 710                  ['id' => $selfconversation->conversationid,
 711                   'type' => \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF,
 712                   'convhash' => \core_message\helper::get_conversation_hash([$selfconversation->userid])
 713                  ]
 714              );
 715  
 716              // Star the existing self-conversation.
 717              $favouriterecord = new \stdClass();
 718              $favouriterecord->component = 'core_message';
 719              $favouriterecord->itemtype = 'message_conversations';
 720              $favouriterecord->itemid = $selfconversation->conversationid;
 721              $userctx = \context_user::instance($selfconversation->userid);
 722              $favouriterecord->contextid = $userctx->id;
 723              $favouriterecord->userid = $selfconversation->userid;
 724              if (!$DB->record_exists('favourite', (array)$favouriterecord)) {
 725                  $favouriterecord->timecreated = time();
 726                  $favouriterecord->timemodified = $favouriterecord->timecreated;
 727                  $DB->insert_record('favourite', $favouriterecord);
 728              }
 729  
 730              // Set the self-conversation member with maxid to remove it later.
 731              $maxids[] = $selfconversation->maxid;
 732          }
 733          $selfconversationsrs->close();
 734  
 735          // Remove the repeated member with the higher id for all the existing self-conversations.
 736          if (!empty($maxids)) {
 737              list($insql, $inparams) = $DB->get_in_or_equal($maxids);
 738              $DB->delete_records_select('message_conversation_members', "id $insql", $inparams);
 739          }
 740  
 741          // STEP 2. Migrate existing self-conversation relying on old message tables, setting the type to the new
 742          // MESSAGE_CONVERSATION_TYPE_SELF and the convhash to the proper one. Star them also.
 743  
 744          // On the messaging legacy tables, self-conversations are only present in the 'message_read' table, so we don't need to
 745          // check the content in the 'message' table.
 746          $sql = "SELECT mr.*
 747                    FROM {message_read} mr
 748              INNER JOIN {user} u ON mr.useridfrom = u.id
 749                   WHERE mr.useridfrom = mr.useridto AND mr.notification = 0 AND u.deleted = 0";
 750          $legacyselfmessagesrs = $DB->get_recordset_sql($sql);
 751          foreach ($legacyselfmessagesrs as $message) {
 752              // Get the self-conversation or create and star it if doesn't exist.
 753              $conditions = [
 754                  'type' => \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF,
 755                  'convhash' => \core_message\helper::get_conversation_hash([$message->useridfrom])
 756              ];
 757              $selfconversation = $DB->get_record('message_conversations', $conditions);
 758              if (empty($selfconversation)) {
 759                  // Create the self-conversation.
 760                  $selfconversation = new \stdClass();
 761                  $selfconversation->type = \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF;
 762                  $selfconversation->convhash = \core_message\helper::get_conversation_hash([$message->useridfrom]);
 763                  $selfconversation->enabled = 1;
 764                  $selfconversation->timecreated = time();
 765                  $selfconversation->timemodified = $selfconversation->timecreated;
 766  
 767                  $selfconversation->id = $DB->insert_record('message_conversations', $selfconversation);
 768  
 769                  // Add user to this self-conversation.
 770                  $member = new \stdClass();
 771                  $member->conversationid = $selfconversation->id;
 772                  $member->userid = $message->useridfrom;
 773                  $member->timecreated = time();
 774  
 775                  $member->id = $DB->insert_record('message_conversation_members', $member);
 776  
 777                  // Star the self-conversation.
 778                  $favouriterecord = new \stdClass();
 779                  $favouriterecord->component = 'core_message';
 780                  $favouriterecord->itemtype = 'message_conversations';
 781                  $favouriterecord->itemid = $selfconversation->id;
 782                  $userctx = \context_user::instance($message->useridfrom);
 783                  $favouriterecord->contextid = $userctx->id;
 784                  $favouriterecord->userid = $message->useridfrom;
 785                  if (!$DB->record_exists('favourite', (array)$favouriterecord)) {
 786                      $favouriterecord->timecreated = time();
 787                      $favouriterecord->timemodified = $favouriterecord->timecreated;
 788                      $DB->insert_record('favourite', $favouriterecord);
 789                  }
 790              }
 791  
 792              // Create the object we will be inserting into the database.
 793              $tabledata = new \stdClass();
 794              $tabledata->useridfrom = $message->useridfrom;
 795              $tabledata->conversationid = $selfconversation->id;
 796              $tabledata->subject = $message->subject;
 797              $tabledata->fullmessage = $message->fullmessage;
 798              $tabledata->fullmessageformat = $message->fullmessageformat ?? FORMAT_MOODLE;
 799              $tabledata->fullmessagehtml = $message->fullmessagehtml;
 800              $tabledata->smallmessage = $message->smallmessage;
 801              $tabledata->timecreated = $message->timecreated;
 802  
 803              $messageid = $DB->insert_record('messages', $tabledata);
 804  
 805              // Check if we need to mark this message as deleted (self-conversations add this information on the
 806              // timeuserfromdeleted field.
 807              if ($message->timeuserfromdeleted) {
 808                  $mua = new \stdClass();
 809                  $mua->userid = $message->useridfrom;
 810                  $mua->messageid = $messageid;
 811                  $mua->action = \core_message\api::MESSAGE_ACTION_DELETED;
 812                  $mua->timecreated = $message->timeuserfromdeleted;
 813  
 814                  $DB->insert_record('message_user_actions', $mua);
 815              }
 816  
 817              // Mark this message as read.
 818              $mua = new \stdClass();
 819              $mua->userid = $message->useridto;
 820              $mua->messageid = $messageid;
 821              $mua->action = \core_message\api::MESSAGE_ACTION_READ;
 822              $mua->timecreated = $message->timeread;
 823  
 824              $DB->insert_record('message_user_actions', $mua);
 825  
 826              // The self-conversation message has been migrated. Delete the record from the legacy table as soon as possible
 827              // to avoid migrate it twice.
 828              $DB->delete_records('message_read', ['id' => $message->id]);
 829          }
 830          $legacyselfmessagesrs->close();
 831  
 832          // Main savepoint reached.
 833          upgrade_main_savepoint(true, 2019041800.01);
 834      }
 835  
 836      if ($oldversion < 2019042200.01) {
 837  
 838          // Define table role_sortorder to be dropped.
 839          $table = new xmldb_table('role_sortorder');
 840  
 841          // Conditionally launch drop table for role_sortorder.
 842          if ($dbman->table_exists($table)) {
 843              $dbman->drop_table($table);
 844          }
 845  
 846          // Main savepoint reached.
 847          upgrade_main_savepoint(true, 2019042200.01);
 848      }
 849  
 850      if ($oldversion < 2019042200.02) {
 851  
 852          // Let's update all (old core) targets to their new (core_course) locations.
 853          $targets = [
 854              '\core\analytics\target\course_competencies' => '\core_course\analytics\target\course_competencies',
 855              '\core\analytics\target\course_completion' => '\core_course\analytics\target\course_completion',
 856              '\core\analytics\target\course_dropout' => '\core_course\analytics\target\course_dropout',
 857              '\core\analytics\target\course_gradetopass' => '\core_course\analytics\target\course_gradetopass',
 858              '\core\analytics\target\no_teaching' => '\core_course\analytics\target\no_teaching',
 859          ];
 860  
 861          foreach ($targets as $oldclass => $newclass) {
 862              $DB->set_field('analytics_models', 'target', $newclass, ['target' => $oldclass]);
 863          }
 864  
 865          // Main savepoint reached.
 866          upgrade_main_savepoint(true, 2019042200.02);
 867      }
 868  
 869      if ($oldversion < 2019042300.01) {
 870          $sql = "UPDATE {capabilities}
 871                     SET name = ?,
 872                         contextlevel = ?
 873                   WHERE name = ?";
 874          $DB->execute($sql, ['moodle/category:viewcourselist', CONTEXT_COURSECAT, 'moodle/course:browse']);
 875  
 876          $sql = "UPDATE {role_capabilities}
 877                     SET capability = ?
 878                   WHERE capability = ?";
 879          $DB->execute($sql, ['moodle/category:viewcourselist', 'moodle/course:browse']);
 880  
 881          // Main savepoint reached.
 882          upgrade_main_savepoint(true, 2019042300.01);
 883      }
 884  
 885      if ($oldversion < 2019042300.03) {
 886  
 887          // Add new customdata field to message table.
 888          $table = new xmldb_table('message');
 889          $field = new xmldb_field('customdata', XMLDB_TYPE_TEXT, null, null, null, null, null, 'eventtype');
 890  
 891          // Conditionally launch add field output.
 892          if (!$dbman->field_exists($table, $field)) {
 893              $dbman->add_field($table, $field);
 894          }
 895  
 896          // Add new customdata field to notifications and messages table.
 897          $table = new xmldb_table('notifications');
 898          $field = new xmldb_field('customdata', XMLDB_TYPE_TEXT, null, null, null, null, null, 'timecreated');
 899  
 900          // Conditionally launch add field output.
 901          if (!$dbman->field_exists($table, $field)) {
 902              $dbman->add_field($table, $field);
 903          }
 904  
 905          $table = new xmldb_table('messages');
 906          // Conditionally launch add field output.
 907          if (!$dbman->field_exists($table, $field)) {
 908              $dbman->add_field($table, $field);
 909          }
 910  
 911          // Main savepoint reached.
 912          upgrade_main_savepoint(true, 2019042300.03);
 913      }
 914  
 915      if ($oldversion < 2019042700.01) {
 916  
 917          // Define field firstanalysis to be added to analytics_used_analysables.
 918          $table = new xmldb_table('analytics_used_analysables');
 919  
 920          // Declaring it as null initially (although it is NOT NULL).
 921          $field = new xmldb_field('firstanalysis', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'analysableid');
 922  
 923          // Conditionally launch add field firstanalysis.
 924          if (!$dbman->field_exists($table, $field)) {
 925              $dbman->add_field($table, $field);
 926  
 927              // Set existing values to the current timeanalysed value.
 928              $recordset = $DB->get_recordset('analytics_used_analysables');
 929              foreach ($recordset as $record) {
 930                  $record->firstanalysis = $record->timeanalysed;
 931                  $DB->update_record('analytics_used_analysables', $record);
 932              }
 933              $recordset->close();
 934  
 935              // Now make the field 'NOT NULL'.
 936              $field = new xmldb_field('firstanalysis', XMLDB_TYPE_INTEGER, '10',
 937                  null, XMLDB_NOTNULL, null, null, 'analysableid');
 938              $dbman->change_field_notnull($table, $field);
 939          }
 940  
 941          // Main savepoint reached.
 942          upgrade_main_savepoint(true, 2019042700.01);
 943      }
 944  
 945      if ($oldversion < 2019050300.01) {
 946          // Delete all stale favourite records which were left behind when a course was deleted.
 947          $params = ['component' => 'core_message', 'itemtype' => 'message_conversations'];
 948          $sql = "SELECT fav.id as id
 949                    FROM {favourite} fav
 950               LEFT JOIN {context} ctx ON (ctx.id = fav.contextid)
 951                   WHERE fav.component = :component
 952                         AND fav.itemtype = :itemtype
 953                         AND ctx.id IS NULL";
 954  
 955          if ($records = $DB->get_fieldset_sql($sql, $params)) {
 956              // Just for safety, delete by chunks.
 957              $chunks = array_chunk($records, 1000);
 958              foreach ($chunks as $chunk) {
 959                  list($insql, $inparams) = $DB->get_in_or_equal($chunk);
 960                  $DB->delete_records_select('favourite', "id $insql", $inparams);
 961              }
 962          }
 963  
 964          upgrade_main_savepoint(true, 2019050300.01);
 965      }
 966  
 967      if ($oldversion < 2019050600.00) {
 968  
 969          // Define field apiversion to be added to badge_backpack.
 970          $table = new xmldb_table('badge_backpack');
 971          $field = new xmldb_field('apiversion', XMLDB_TYPE_CHAR, '12', null, XMLDB_NOTNULL, null, '1.0', 'password');
 972  
 973          // Conditionally launch add field apiversion.
 974          if (!$dbman->field_exists($table, $field)) {
 975              $dbman->add_field($table, $field);
 976          }
 977  
 978          // Define table badge_external_backpack to be created.
 979          $table = new xmldb_table('badge_external_backpack');
 980  
 981          // Adding fields to table badge_external_backpack.
 982          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 983          $table->add_field('backpackapiurl', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
 984          $table->add_field('backpackweburl', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
 985          $table->add_field('apiversion', XMLDB_TYPE_CHAR, '12', null, XMLDB_NOTNULL, null, '1.0');
 986          $table->add_field('sortorder', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
 987          $table->add_field('password', XMLDB_TYPE_CHAR, '255', null, null, null, null);
 988  
 989          // Adding keys to table badge_external_backpack.
 990          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
 991          $table->add_key('backpackapiurlkey', XMLDB_KEY_UNIQUE, ['backpackapiurl']);
 992          $table->add_key('backpackweburlkey', XMLDB_KEY_UNIQUE, ['backpackweburl']);
 993  
 994          // Conditionally launch create table for badge_external_backpack.
 995          if (!$dbman->table_exists($table)) {
 996              $dbman->create_table($table);
 997          }
 998  
 999          // Define field entityid to be added to badge_external.
1000          $table = new xmldb_table('badge_external');
1001          $field = new xmldb_field('entityid', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'collectionid');
1002  
1003          // Conditionally launch add field entityid.
1004          if (!$dbman->field_exists($table, $field)) {
1005              $dbman->add_field($table, $field);
1006          }
1007  
1008          // Define table badge_external_identifier to be created.
1009          $table = new xmldb_table('badge_external_identifier');
1010  
1011          // Adding fields to table badge_external_identifier.
1012          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1013          $table->add_field('sitebackpackid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1014          $table->add_field('internalid', XMLDB_TYPE_CHAR, '128', null, XMLDB_NOTNULL, null, null);
1015          $table->add_field('externalid', XMLDB_TYPE_CHAR, '128', null, XMLDB_NOTNULL, null, null);
1016          $table->add_field('type', XMLDB_TYPE_CHAR, '16', null, XMLDB_NOTNULL, null, null);
1017  
1018          // Adding keys to table badge_external_identifier.
1019          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
1020          $table->add_key('fk_backpackid', XMLDB_KEY_FOREIGN, ['sitebackpackid'], 'badge_backpack', ['id']);
1021          $table->add_key('backpack-internal-external', XMLDB_KEY_UNIQUE, ['sitebackpackid', 'internalid', 'externalid', 'type']);
1022  
1023          // Conditionally launch create table for badge_external_identifier.
1024          if (!$dbman->table_exists($table)) {
1025              $dbman->create_table($table);
1026          }
1027  
1028          // Define field externalbackpackid to be added to badge_backpack.
1029          $table = new xmldb_table('badge_backpack');
1030          $field = new xmldb_field('externalbackpackid', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'password');
1031  
1032          // Conditionally launch add field externalbackpackid.
1033          if (!$dbman->field_exists($table, $field)) {
1034              $dbman->add_field($table, $field);
1035          }
1036  
1037          // Define key externalbackpack (foreign) to be added to badge_backpack.
1038          $key = new xmldb_key('externalbackpack', XMLDB_KEY_FOREIGN, ['externalbackpackid'], 'badge_external_backpack', ['id']);
1039  
1040          // Launch add key externalbackpack.
1041          $dbman->add_key($table, $key);
1042  
1043          $field = new xmldb_field('apiversion');
1044  
1045          // Conditionally launch drop field apiversion.
1046          if ($dbman->field_exists($table, $field)) {
1047              $dbman->drop_field($table, $field);
1048          }
1049  
1050          $field = new xmldb_field('backpackurl');
1051  
1052          // Conditionally launch drop field backpackurl.
1053          if ($dbman->field_exists($table, $field)) {
1054              $dbman->drop_field($table, $field);
1055          }
1056  
1057          // Add default backpacks.
1058          require_once($CFG->dirroot . '/badges/upgradelib.php'); // Core install and upgrade related functions only for badges.
1059          badges_install_default_backpacks();
1060  
1061          // Main savepoint reached.
1062          upgrade_main_savepoint(true, 2019050600.00);
1063      }
1064  
1065      if ($oldversion < 2019051300.01) {
1066          $DB->set_field('analytics_models', 'enabled', '1', ['target' => '\core_user\analytics\target\upcoming_activities_due']);
1067  
1068          // Main savepoint reached.
1069          upgrade_main_savepoint(true, 2019051300.01);
1070      }
1071  
1072      // Automatically generated Moodle v3.7.0 release upgrade line.
1073      // Put any upgrade step following this.
1074  
1075      if ($oldversion < 2019060600.02) {
1076          // Renaming 'opentogoogle' config to 'opentowebcrawlers'.
1077          $opentogooglevalue = get_config('core', 'opentogoogle');
1078  
1079          // Move the value over if it was previously configured.
1080          if ($opentogooglevalue !== false) {
1081              set_config('opentowebcrawlers', $opentogooglevalue);
1082          }
1083  
1084          // Remove the now unused value.
1085          unset_config('opentogoogle');
1086  
1087          // Main savepoint reached.
1088          upgrade_main_savepoint(true, 2019060600.02);
1089      }
1090  
1091      if ($oldversion < 2019062900.00) {
1092          // Debugsmtp is now only available via config.php.
1093          $DB->delete_records('config', array('name' => 'debugsmtp'));
1094  
1095          // Main savepoint reached.
1096          upgrade_main_savepoint(true, 2019062900.00);
1097      }
1098  
1099      if ($oldversion < 2019070400.01) {
1100  
1101          $basecolors = ['#81ecec', '#74b9ff', '#a29bfe', '#dfe6e9', '#00b894',
1102              '#0984e3', '#b2bec3', '#fdcb6e', '#fd79a8', '#6c5ce7'];
1103  
1104          $colornr = 1;
1105          foreach ($basecolors as $color) {
1106              set_config('coursecolor' .  $colornr, $color, 'core_admin');
1107              $colornr++;
1108          }
1109  
1110          upgrade_main_savepoint(true, 2019070400.01);
1111      }
1112  
1113      if ($oldversion < 2019072200.00) {
1114  
1115          // Define field relativedatesmode to be added to course.
1116          $table = new xmldb_table('course');
1117          $field = new xmldb_field('relativedatesmode', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'enddate');
1118  
1119          // Conditionally launch add field relativedatesmode.
1120          if (!$dbman->field_exists($table, $field)) {
1121              $dbman->add_field($table, $field);
1122          }
1123  
1124          // Main savepoint reached.
1125          upgrade_main_savepoint(true, 2019072200.00);
1126      }
1127  
1128      if ($oldversion < 2019072500.01) {
1129          // Remove the "popup" processor from the list of default processors for the messagecontactrequests notification.
1130          $oldloggedinconfig = get_config('message', 'message_provider_moodle_messagecontactrequests_loggedin');
1131          $oldloggedoffconfig = get_config('message', 'message_provider_moodle_messagecontactrequests_loggedoff');
1132          $newloggedinconfig = implode(',', array_filter(explode(',', $oldloggedinconfig), function($value) {
1133              return $value != 'popup';
1134          }));
1135          $newloggedoffconfig = implode(',', array_filter(explode(',', $oldloggedoffconfig), function($value) {
1136              return $value != 'popup';
1137          }));
1138          set_config('message_provider_moodle_messagecontactrequests_loggedin', $newloggedinconfig, 'message');
1139          set_config('message_provider_moodle_messagecontactrequests_loggedoff', $newloggedoffconfig, 'message');
1140  
1141          upgrade_main_savepoint(true, 2019072500.01);
1142      }
1143  
1144      if ($oldversion < 2019072500.03) {
1145          unset_config('httpswwwroot');
1146  
1147          upgrade_main_savepoint(true, 2019072500.03);
1148      }
1149  
1150      if ($oldversion < 2019073100.00) {
1151          // Update the empty tag instructions to null.
1152          $instructions = get_config('core', 'auth_instructions');
1153  
1154          if (trim(html_to_text($instructions)) === '') {
1155              set_config('auth_instructions', '');
1156          }
1157  
1158          // Main savepoint reached.
1159          upgrade_main_savepoint(true, 2019073100.00);
1160      }
1161  
1162      if ($oldversion < 2019083000.01) {
1163  
1164          // If block_community is no longer present, remove it.
1165          if (!file_exists($CFG->dirroot . '/blocks/community/communitycourse.php')) {
1166              // Drop table that is no longer needed.
1167              $table = new xmldb_table('block_community');
1168              if ($dbman->table_exists($table)) {
1169                  $dbman->drop_table($table);
1170              }
1171  
1172              // Delete instances.
1173              $instances = $DB->get_records_list('block_instances', 'blockname', ['community']);
1174              $instanceids = array_keys($instances);
1175  
1176              if (!empty($instanceids)) {
1177                  $DB->delete_records_list('block_positions', 'blockinstanceid', $instanceids);
1178                  $DB->delete_records_list('block_instances', 'id', $instanceids);
1179                  list($sql, $params) = $DB->get_in_or_equal($instanceids, SQL_PARAMS_NAMED);
1180                  $params['contextlevel'] = CONTEXT_BLOCK;
1181                  $DB->delete_records_select('context', "contextlevel=:contextlevel AND instanceid " . $sql, $params);
1182  
1183                  $preferences = array();
1184                  foreach ($instances as $instanceid => $instance) {
1185                      $preferences[] = 'block' . $instanceid . 'hidden';
1186                      $preferences[] = 'docked_block_instance_' . $instanceid;
1187                  }
1188                  $DB->delete_records_list('user_preferences', 'name', $preferences);
1189              }
1190  
1191              // Delete the block from the block table.
1192              $DB->delete_records('block', array('name' => 'community'));
1193  
1194              // Remove capabilities.
1195              capabilities_cleanup('block_community');
1196              // Clean config.
1197              unset_all_config_for_plugin('block_community');
1198  
1199              // Remove Moodle-level community based capabilities.
1200              $capabilitiestoberemoved = ['block/community:addinstance', 'block/community:myaddinstance'];
1201              // Delete any role_capabilities for the old roles.
1202              $DB->delete_records_list('role_capabilities', 'capability', $capabilitiestoberemoved);
1203              // Delete the capability itself.
1204              $DB->delete_records_list('capabilities', 'name', $capabilitiestoberemoved);
1205          }
1206  
1207          upgrade_main_savepoint(true, 2019083000.01);
1208      }
1209  
1210      if ($oldversion < 2019083000.02) {
1211          // Remove unused config.
1212          unset_config('enablecoursepublishing');
1213          upgrade_main_savepoint(true, 2019083000.02);
1214      }
1215  
1216      if ($oldversion < 2019083000.04) {
1217          // Delete "orphaned" subscriptions.
1218          $sql = "SELECT DISTINCT es.userid
1219                    FROM {event_subscriptions} es
1220               LEFT JOIN {user} u ON u.id = es.userid
1221                   WHERE u.deleted = 1 OR u.id IS NULL";
1222          $deletedusers = $DB->get_fieldset_sql($sql);
1223          if ($deletedusers) {
1224              list($sql, $params) = $DB->get_in_or_equal($deletedusers);
1225  
1226              // Delete orphaned subscriptions.
1227              $DB->execute("DELETE FROM {event_subscriptions} WHERE userid " . $sql, $params);
1228          }
1229  
1230          upgrade_main_savepoint(true, 2019083000.04);
1231      }
1232  
1233      if ($oldversion < 2019090500.01) {
1234  
1235          // Define index analysableid (not unique) to be added to analytics_used_analysables.
1236          $table = new xmldb_table('analytics_used_analysables');
1237          $index = new xmldb_index('analysableid', XMLDB_INDEX_NOTUNIQUE, ['analysableid']);
1238  
1239          // Conditionally launch add index analysableid.
1240          if (!$dbman->index_exists($table, $index)) {
1241              $dbman->add_index($table, $index);
1242          }
1243  
1244          // Main savepoint reached.
1245          upgrade_main_savepoint(true, 2019090500.01);
1246      }
1247  
1248      if ($oldversion < 2019092700.01) {
1249          upgrade_rename_prediction_actions_useful_incorrectly_flagged();
1250          upgrade_main_savepoint(true, 2019092700.01);
1251      }
1252  
1253      if ($oldversion < 2019100800.02) {
1254          // Rename the official moodle sites directory the site is registered with.
1255          $DB->execute("UPDATE {registration_hubs}
1256                           SET hubname = ?, huburl = ?
1257                         WHERE huburl = ?", ['moodle', 'https://stats.moodle.org', 'https://moodle.net']);
1258  
1259          // Convert the hub site specific settings to the new naming format without the hub URL in the name.
1260          $hubconfig = get_config('hub');
1261  
1262          if (!empty($hubconfig)) {
1263              foreach (upgrade_convert_hub_config_site_param_names($hubconfig, 'https://moodle.net') as $name => $value) {
1264                  set_config($name, $value, 'hub');
1265              }
1266          }
1267  
1268          upgrade_main_savepoint(true, 2019100800.02);
1269      }
1270  
1271      if ($oldversion < 2019100900.00) {
1272          // If block_participants is no longer present, remove it.
1273          if (!file_exists($CFG->dirroot . '/blocks/participants/block_participants.php')) {
1274              // Delete instances.
1275              $instances = $DB->get_records_list('block_instances', 'blockname', ['participants']);
1276              $instanceids = array_keys($instances);
1277  
1278              if (!empty($instanceids)) {
1279                  $DB->delete_records_list('block_positions', 'blockinstanceid', $instanceids);
1280                  $DB->delete_records_list('block_instances', 'id', $instanceids);
1281                  list($sql, $params) = $DB->get_in_or_equal($instanceids, SQL_PARAMS_NAMED);
1282                  $params['contextlevel'] = CONTEXT_BLOCK;
1283                  $DB->delete_records_select('context', "contextlevel=:contextlevel AND instanceid " . $sql, $params);
1284  
1285                  $preferences = array();
1286                  foreach ($instances as $instanceid => $instance) {
1287                      $preferences[] = 'block' . $instanceid . 'hidden';
1288                      $preferences[] = 'docked_block_instance_' . $instanceid;
1289                  }
1290                  $DB->delete_records_list('user_preferences', 'name', $preferences);
1291              }
1292  
1293              // Delete the block from the block table.
1294              $DB->delete_records('block', array('name' => 'participants'));
1295  
1296              // Remove capabilities.
1297              capabilities_cleanup('block_participants');
1298  
1299              // Clean config.
1300              unset_all_config_for_plugin('block_participants');
1301          }
1302  
1303          upgrade_main_savepoint(true, 2019100900.00);
1304      }
1305  
1306      if ($oldversion < 2019101600.01) {
1307  
1308          // Change the setting $CFG->requestcategoryselection into $CFG->lockrequestcategory with opposite value.
1309          set_config('lockrequestcategory', empty($CFG->requestcategoryselection));
1310  
1311          upgrade_main_savepoint(true, 2019101600.01);
1312      }
1313  
1314      if ($oldversion < 2019101800.02) {
1315  
1316          // Get the table by its previous name.
1317          $table = new xmldb_table('analytics_models');
1318          if ($dbman->table_exists($table)) {
1319  
1320              // Define field contextids to be added to analytics_models.
1321              $field = new xmldb_field('contextids', XMLDB_TYPE_TEXT, null, null, null, null, null, 'version');
1322  
1323              // Conditionally launch add field contextids.
1324              if (!$dbman->field_exists($table, $field)) {
1325                  $dbman->add_field($table, $field);
1326              }
1327          }
1328  
1329          // Main savepoint reached.
1330          upgrade_main_savepoint(true, 2019101800.02);
1331      }
1332  
1333      if ($oldversion < 2019102500.04) {
1334          // Define table h5p_libraries to be created.
1335          $table = new xmldb_table('h5p_libraries');
1336  
1337          // Adding fields to table h5p_libraries.
1338          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1339          $table->add_field('machinename', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
1340          $table->add_field('title', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
1341          $table->add_field('majorversion', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, null);
1342          $table->add_field('minorversion', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, null);
1343          $table->add_field('patchversion', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, null);
1344          $table->add_field('runnable', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, null);
1345          $table->add_field('fullscreen', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0');
1346          $table->add_field('embedtypes', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
1347          $table->add_field('preloadedjs', XMLDB_TYPE_TEXT, null, null, null, null, null);
1348          $table->add_field('preloadedcss', XMLDB_TYPE_TEXT, null, null, null, null, null);
1349          $table->add_field('droplibrarycss', XMLDB_TYPE_TEXT, null, null, null, null, null);
1350          $table->add_field('semantics', XMLDB_TYPE_TEXT, null, null, null, null, null);
1351          $table->add_field('addto', XMLDB_TYPE_TEXT, null, null, null, null, null);
1352  
1353          // Adding keys to table h5p_libraries.
1354          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
1355  
1356          // Adding indexes to table h5p_libraries.
1357          $table->add_index('machinemajorminorpatch', XMLDB_INDEX_NOTUNIQUE,
1358              ['machinename', 'majorversion', 'minorversion', 'patchversion', 'runnable']);
1359  
1360          // Conditionally launch create table for h5p_libraries.
1361          if (!$dbman->table_exists($table)) {
1362              $dbman->create_table($table);
1363          }
1364  
1365          // Define table h5p_library_dependencies to be created.
1366          $table = new xmldb_table('h5p_library_dependencies');
1367  
1368          // Adding fields to table h5p_library_dependencies.
1369          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1370          $table->add_field('libraryid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1371          $table->add_field('requiredlibraryid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1372          $table->add_field('dependencytype', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
1373  
1374          // Adding keys to table h5p_library_dependencies.
1375          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
1376          $table->add_key('libraryid', XMLDB_KEY_FOREIGN, ['libraryid'], 'h5p_libraries', ['id']);
1377          $table->add_key('requiredlibraryid', XMLDB_KEY_FOREIGN, ['requiredlibraryid'], 'h5p_libraries', ['id']);
1378  
1379          // Conditionally launch create table for h5p_library_dependencies.
1380          if (!$dbman->table_exists($table)) {
1381              $dbman->create_table($table);
1382          }
1383  
1384          // Define table h5p to be created.
1385          $table = new xmldb_table('h5p');
1386  
1387          // Adding fields to table h5p.
1388          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1389          $table->add_field('jsoncontent', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
1390          $table->add_field('mainlibraryid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1391          $table->add_field('displayoptions', XMLDB_TYPE_INTEGER, '4', null, null, null, null);
1392          $table->add_field('pathnamehash', XMLDB_TYPE_CHAR, '40', null, XMLDB_NOTNULL, null, null);
1393          $table->add_field('contenthash', XMLDB_TYPE_CHAR, '40', null, XMLDB_NOTNULL, null, null);
1394          $table->add_field('filtered', XMLDB_TYPE_TEXT, null, null, null, null, null);
1395          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1396          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1397  
1398          // Adding keys to table h5p.
1399          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
1400          $table->add_key('mainlibraryid', XMLDB_KEY_FOREIGN, ['mainlibraryid'], 'h5p_libraries', ['id']);
1401  
1402          // Conditionally launch create table for h5p.
1403          if (!$dbman->table_exists($table)) {
1404              $dbman->create_table($table);
1405          }
1406  
1407          // Define table h5p_contents_libraries to be created.
1408          $table = new xmldb_table('h5p_contents_libraries');
1409  
1410          // Adding fields to table h5p_contents_libraries.
1411          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1412          $table->add_field('h5pid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1413          $table->add_field('libraryid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1414          $table->add_field('dependencytype', XMLDB_TYPE_CHAR, '10', null, XMLDB_NOTNULL, null, null);
1415          $table->add_field('dropcss', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, null);
1416          $table->add_field('weight', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1417  
1418          // Adding keys to table h5p_contents_libraries.
1419          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
1420          $table->add_key('h5pid', XMLDB_KEY_FOREIGN, ['h5pid'], 'h5p', ['id']);
1421          $table->add_key('libraryid', XMLDB_KEY_FOREIGN, ['libraryid'], 'h5p_libraries', ['id']);
1422  
1423          // Conditionally launch create table for h5p_contents_libraries.
1424          if (!$dbman->table_exists($table)) {
1425              $dbman->create_table($table);
1426          }
1427  
1428          // Define table h5p_libraries_cachedassets to be created.
1429          $table = new xmldb_table('h5p_libraries_cachedassets');
1430  
1431          // Adding fields to table h5p_libraries_cachedassets.
1432          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1433          $table->add_field('libraryid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1434          $table->add_field('hash', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
1435  
1436          // Adding keys to table h5p_libraries_cachedassets.
1437          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
1438          $table->add_key('libraryid', XMLDB_KEY_FOREIGN, ['libraryid'], 'h5p_libraries_cachedassets', ['id']);
1439  
1440          // Conditionally launch create table for h5p_libraries_cachedassets.
1441          if (!$dbman->table_exists($table)) {
1442              $dbman->create_table($table);
1443          }
1444  
1445          // Main savepoint reached.
1446          upgrade_main_savepoint(true, 2019102500.04);
1447      }
1448  
1449      if ($oldversion < 2019103000.13) {
1450  
1451          upgrade_analytics_fix_contextids_defaults();
1452  
1453          // Main savepoint reached.
1454          upgrade_main_savepoint(true, 2019103000.13);
1455      }
1456  
1457      if ($oldversion < 2019111300.00) {
1458  
1459          // Define field coremajor to be added to h5p_libraries.
1460          $table = new xmldb_table('h5p_libraries');
1461          $field = new xmldb_field('coremajor', XMLDB_TYPE_INTEGER, '4', null, null, null, null, 'addto');
1462  
1463          // Conditionally launch add field coremajor.
1464          if (!$dbman->field_exists($table, $field)) {
1465              $dbman->add_field($table, $field);
1466          }
1467  
1468          $field = new xmldb_field('coreminor', XMLDB_TYPE_INTEGER, '4', null, null, null, null, 'coremajor');
1469  
1470          // Conditionally launch add field coreminor.
1471          if (!$dbman->field_exists($table, $field)) {
1472              $dbman->add_field($table, $field);
1473          }
1474  
1475          // Main savepoint reached.
1476          upgrade_main_savepoint(true, 2019111300.00);
1477      }
1478  
1479      // Automatically generated Moodle v3.8.0 release upgrade line.
1480      // Put any upgrade step following this.
1481  
1482      if ($oldversion < 2019120500.01) {
1483          // Delete any role assignments for roles which no longer exist.
1484          $DB->delete_records_select('role_assignments', "roleid NOT IN (SELECT id FROM {role})");
1485  
1486          // Main savepoint reached.
1487          upgrade_main_savepoint(true, 2019120500.01);
1488      }
1489  
1490      if ($oldversion < 2019121800.00) {
1491          // Upgrade MIME types for existing streaming files.
1492          $filetypes = array(
1493              '%.fmp4' => 'video/mp4',
1494              '%.ts' => 'video/MP2T',
1495              '%.mpd' => 'application/dash+xml',
1496              '%.m3u8' => 'application/x-mpegURL',
1497          );
1498  
1499          $select = $DB->sql_like('filename', '?', false);
1500          foreach ($filetypes as $extension => $mimetype) {
1501              $DB->set_field_select(
1502                  'files',
1503                  'mimetype',
1504                  $mimetype,
1505                  $select,
1506                  array($extension)
1507              );
1508          }
1509  
1510          upgrade_main_savepoint(true, 2019121800.00);
1511      }
1512  
1513      if ($oldversion < 2019122000.01) {
1514          // Clean old upgrade setting not used anymore.
1515          unset_config('linkcoursesectionsupgradescriptwasrun');
1516          upgrade_main_savepoint(true, 2019122000.01);
1517      }
1518  
1519      if ($oldversion < 2020010900.02) {
1520          $table = new xmldb_table('event');
1521  
1522          // This index will improve the performance when the Events API retrieves category and group events.
1523          $index = new xmldb_index('eventtype', XMLDB_INDEX_NOTUNIQUE, ['eventtype']);
1524          if (!$dbman->index_exists($table, $index)) {
1525              $dbman->add_index($table, $index);
1526          }
1527  
1528          // This index improves the performance of backups, deletion and visibilty changes on activities.
1529          $index = new xmldb_index('modulename-instance', XMLDB_INDEX_NOTUNIQUE, ['modulename', 'instance']);
1530          if (!$dbman->index_exists($table, $index)) {
1531              $dbman->add_index($table, $index);
1532          }
1533  
1534          upgrade_main_savepoint(true, 2020010900.02);
1535      }
1536  
1537      if ($oldversion < 2020011700.02) {
1538          // Delete all orphaned subscription events.
1539          $select = "subscriptionid IS NOT NULL
1540                     AND subscriptionid NOT IN (SELECT id from {event_subscriptions})";
1541          $DB->delete_records_select('event', $select);
1542  
1543          upgrade_main_savepoint(true, 2020011700.02);
1544      }
1545  
1546      if ($oldversion < 2020013000.01) {
1547          global $DB;
1548          // Delete any associated files.
1549          $fs = get_file_storage();
1550          $sql = "SELECT cuc.id, cuc.userid
1551                    FROM {competency_usercomp} cuc
1552               LEFT JOIN {user} u ON cuc.userid = u.id
1553                   WHERE u.deleted = 1";
1554          $usercompetencies = $DB->get_records_sql($sql);
1555          foreach ($usercompetencies as $usercomp) {
1556              $DB->delete_records('competency_evidence', ['usercompetencyid' => $usercomp->id]);
1557              $DB->delete_records('competency_usercompcourse', ['userid' => $usercomp->userid]);
1558              $DB->delete_records('competency_usercompplan', ['userid' => $usercomp->userid]);
1559              $DB->delete_records('competency_usercomp', ['userid' => $usercomp->userid]);
1560          }
1561  
1562          $sql = "SELECT cue.id, cue.userid
1563                    FROM {competency_userevidence} cue
1564               LEFT JOIN {user} u ON cue.userid = u.id
1565                   WHERE u.deleted = 1";
1566          $userevidences = $DB->get_records_sql($sql);
1567          foreach ($userevidences as $userevidence) {
1568              $DB->delete_records('competency_userevidencecomp', ['userevidenceid' => $userevidence->id]);
1569              $DB->delete_records('competency_userevidence', ['id' => $userevidence->id]);
1570  
1571              if ($record = $DB->get_record('context', ['contextlevel' => CONTEXT_USER, 'instanceid' => $userevidence->userid],
1572                      '*', IGNORE_MISSING)) {
1573                  // Delete all orphaned user evidences files.
1574                  $fs->delete_area_files($record->id, 'core_competency', 'userevidence', $userevidence->userid);
1575              }
1576          }
1577  
1578          $sql = "SELECT cp.id
1579                    FROM {competency_plan} cp
1580               LEFT JOIN {user} u ON cp.userid = u.id
1581                   WHERE u.deleted = 1";
1582          $userplans = $DB->get_records_sql($sql);
1583          foreach ($userplans as $userplan) {
1584              $DB->delete_records('competency_plancomp', ['planid' => $userplan->id]);
1585              $DB->delete_records('competency_plan', ['id' => $userplan->id]);
1586          }
1587  
1588          // Main savepoint reached.
1589          upgrade_main_savepoint(true, 2020013000.01);
1590      }
1591  
1592      if ($oldversion < 2020040200.01) {
1593          // Clean up completion criteria records referring to courses that no longer exist.
1594          $select = 'criteriatype = :type AND courseinstance NOT IN (SELECT id FROM {course})';
1595          $params = ['type' => 8]; // COMPLETION_CRITERIA_TYPE_COURSE.
1596  
1597          $DB->delete_records_select('course_completion_criteria', $select, $params);
1598  
1599          // Main savepoint reached.
1600          upgrade_main_savepoint(true, 2020040200.01);
1601      }
1602  
1603      if ($oldversion < 2020040700.00) {
1604          // Remove deprecated Mozilla OpenBadges backpack.
1605          $url = 'https://backpack.openbadges.org';
1606          $bp = $DB->get_record('badge_external_backpack', ['backpackapiurl' => $url]);
1607          if ($bp) {
1608              // Remove connections for users to this backpack.
1609              $sql = "SELECT DISTINCT bb.id
1610                        FROM {badge_backpack} bb
1611                   LEFT JOIN {badge_external} be ON be. backpackid = bb.externalbackpackid
1612                       WHERE bb.externalbackpackid = :backpackid";
1613              $params = ['backpackid' => $bp->id];
1614              $externalbackpacks = $DB->get_fieldset_sql($sql, $params);
1615              if ($externalbackpacks) {
1616                  list($sql, $params) = $DB->get_in_or_equal($externalbackpacks);
1617  
1618                  // Delete user external collections references to this backpack.
1619                  $DB->execute("DELETE FROM {badge_external} WHERE backpackid " . $sql, $params);
1620              }
1621              $DB->delete_records('badge_backpack', ['externalbackpackid' => $bp->id]);
1622  
1623              // Delete deprecated backpack entry.
1624              $DB->delete_records('badge_external_backpack', ['backpackapiurl' => $url]);
1625          }
1626  
1627          // Set active external backpack to Badgr.io.
1628          $url = 'https://api.badgr.io/v2';
1629          if ($bp = $DB->get_record('badge_external_backpack', ['backpackapiurl' => $url])) {
1630              set_config('badges_site_backpack', $bp->id);
1631          } else {
1632              unset_config('badges_site_backpack');
1633          }
1634  
1635          upgrade_main_savepoint(true, 2020040700.00);
1636      }
1637  
1638      if ($oldversion < 2020041500.00) {
1639          // Define table to store contentbank contents.
1640          $table = new xmldb_table('contentbank_content');
1641  
1642          // Adding fields to table content_bank.
1643          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1644          $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
1645          $table->add_field('contenttype', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
1646          $table->add_field('contextid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1647          $table->add_field('instanceid', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
1648          $table->add_field('configdata', XMLDB_TYPE_TEXT, null, null, null, null, null);
1649          $table->add_field('usercreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1650          $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
1651          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1652          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, null, null, '0');
1653  
1654          // Adding keys to table contentbank_content.
1655          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
1656          $table->add_key('contextid', XMLDB_KEY_FOREIGN, ['contextid'], 'context', ['id']);
1657          $table->add_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']);
1658          $table->add_key('usercreated', XMLDB_KEY_FOREIGN, ['usercreated'], 'user', ['id']);
1659  
1660          // Adding indexes to table contentbank_content.
1661          $table->add_index('name', XMLDB_INDEX_NOTUNIQUE, ['name']);
1662          $table->add_index('instance', XMLDB_INDEX_NOTUNIQUE, ['contextid', 'contenttype', 'instanceid']);
1663  
1664          if (!$dbman->table_exists($table)) {
1665              $dbman->create_table($table);
1666          }
1667  
1668          // Main savepoint reached.
1669          upgrade_main_savepoint(true, 2020041500.00);
1670      }
1671  
1672      if ($oldversion < 2020041700.01) {
1673          // Upgrade h5p MIME type for existing h5p files.
1674          $select = $DB->sql_like('filename', '?', false);
1675          $DB->set_field_select(
1676              'files',
1677              'mimetype',
1678              'application/zip.h5p',
1679              $select,
1680              array('%.h5p')
1681          );
1682  
1683          upgrade_main_savepoint(true, 2020041700.01);
1684      }
1685  
1686      if ($oldversion < 2020042800.01) {
1687          // Delete obsolete config value.
1688          unset_config('enablesafebrowserintegration');
1689          // Clean up config of the old plugin.
1690          unset_all_config_for_plugin('quizaccess_safebrowser');
1691  
1692          upgrade_main_savepoint(true, 2020042800.01);
1693      }
1694  
1695      if ($oldversion < 2020051900.01) {
1696          // Define field component to be added to event.
1697          $table = new xmldb_table('event');
1698          $field = new xmldb_field('component', XMLDB_TYPE_CHAR, '100', null, null, null, null, 'repeatid');
1699  
1700          // Conditionally launch add field component.
1701          if (!$dbman->field_exists($table, $field)) {
1702              $dbman->add_field($table, $field);
1703          }
1704  
1705          // Define index component (not unique) to be added to event.
1706          $table = new xmldb_table('event');
1707          $index = new xmldb_index('component', XMLDB_INDEX_NOTUNIQUE, ['component', 'eventtype', 'instance']);
1708  
1709          // Conditionally launch add index component.
1710          if (!$dbman->index_exists($table, $index)) {
1711              $dbman->add_index($table, $index);
1712          }
1713  
1714          // Main savepoint reached.
1715          upgrade_main_savepoint(true, 2020051900.01);
1716      }
1717  
1718      if ($oldversion < 2020052000.00) {
1719          // Define table badge_backpack_oauth2 to be created.
1720          $table = new xmldb_table('badge_backpack_oauth2');
1721  
1722          // Adding fields to table badge_backpack_oauth2.
1723          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1724          $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1725          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1726          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1727          $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1728          $table->add_field('issuerid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1729          $table->add_field('externalbackpackid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1730          $table->add_field('token', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
1731          $table->add_field('refreshtoken', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
1732          $table->add_field('expires', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
1733          $table->add_field('scope', XMLDB_TYPE_TEXT, null, null, null, null, null);
1734  
1735          // Adding keys to table badge_backpack_oauth2.
1736          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
1737          $table->add_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']);
1738          $table->add_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']);
1739          $table->add_key('issuerid', XMLDB_KEY_FOREIGN, ['issuerid'], 'oauth2_issuer', ['id']);
1740          $table->add_key('externalbackpackid', XMLDB_KEY_FOREIGN, ['externalbackpackid'], 'badge_external_backpack', ['id']);
1741          // Conditionally launch create table for badge_backpack_oauth2.
1742          if (!$dbman->table_exists($table)) {
1743              $dbman->create_table($table);
1744          }
1745  
1746          // Define field oauth2_issuerid to be added to badge_external_backpack.
1747          $tablebadgeexternalbackpack = new xmldb_table('badge_external_backpack');
1748          $fieldoauth2issuerid = new xmldb_field('oauth2_issuerid', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'password');
1749          $keybackpackoauth2key = new xmldb_key('backpackoauth2key', XMLDB_KEY_FOREIGN, ['oauth2_issuerid'], 'oauth2_issuer', ['id']);
1750  
1751          // Conditionally launch add field oauth2_issuerid.
1752          if (!$dbman->field_exists($tablebadgeexternalbackpack, $fieldoauth2issuerid)) {
1753              $dbman->add_field($tablebadgeexternalbackpack, $fieldoauth2issuerid);
1754  
1755              // Launch add key backpackoauth2key.
1756              $dbman->add_key($tablebadgeexternalbackpack, $keybackpackoauth2key);
1757          }
1758  
1759          // Define field assertion to be added to badge_external.
1760          $tablebadgeexternal = new xmldb_table('badge_external');
1761          $fieldassertion = new xmldb_field('assertion', XMLDB_TYPE_TEXT, null, null, null, null, null, 'entityid');
1762  
1763          // Conditionally launch add field assertion.
1764          if (!$dbman->field_exists($tablebadgeexternal, $fieldassertion)) {
1765              $dbman->add_field($tablebadgeexternal, $fieldassertion);
1766          }
1767  
1768          // Main savepoint reached.
1769          upgrade_main_savepoint(true, 2020052000.00);
1770      }
1771  
1772      if ($oldversion < 2020052200.01) {
1773  
1774          // Define field custom to be added to license.
1775          $table = new xmldb_table('license');
1776          $field = new xmldb_field('custom', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0');
1777  
1778          // Conditionally launch add field custom.
1779          if (!$dbman->field_exists($table, $field)) {
1780              $dbman->add_field($table, $field);
1781          }
1782  
1783          // Define field sortorder to be added to license.
1784          $field = new xmldb_field('sortorder', XMLDB_TYPE_INTEGER, '5', null, XMLDB_NOTNULL, null, '0');
1785  
1786          // Conditionally launch add field sortorder.
1787          if (!$dbman->field_exists($table, $field)) {
1788              $dbman->add_field($table, $field);
1789          }
1790  
1791          // Define index license (not unique) to be added to files.
1792          $table = new xmldb_table('files');
1793          $index = new xmldb_index('license', XMLDB_INDEX_NOTUNIQUE, ['license']);
1794  
1795          // Conditionally launch add index license.
1796          if (!$dbman->index_exists($table, $index)) {
1797              $dbman->add_index($table, $index);
1798          }
1799  
1800          // Upgrade the core license details.
1801          upgrade_core_licenses();
1802  
1803          // Main savepoint reached.
1804          upgrade_main_savepoint(true, 2020052200.01);
1805      }
1806  
1807      if ($oldversion < 2020060500.01) {
1808          // Define field moodlenetprofile to be added to user.
1809          $table = new xmldb_table('user');
1810          $field = new xmldb_field('moodlenetprofile', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'alternatename');
1811  
1812          // Conditionally launch add field moodlenetprofile.
1813          if (!$dbman->field_exists($table, $field)) {
1814              $dbman->add_field($table, $field);
1815          }
1816  
1817          // Main savepoint reached.
1818          upgrade_main_savepoint(true, 2020060500.01);
1819      }
1820  
1821      // Automatically generated Moodle v3.9.0 release upgrade line.
1822      // Put any upgrade step following this.
1823      if ($oldversion < 2020061500.02) {
1824          // Update default digital age consent map according to the current legislation on each country.
1825  
1826          // The default age of digital consent map for 38 and below.
1827          $oldageofdigitalconsentmap = implode(PHP_EOL, [
1828              '*, 16',
1829              'AT, 14',
1830              'ES, 14',
1831              'US, 13'
1832          ]);
1833  
1834          // Check if the current age of digital consent map matches the old one.
1835          if (get_config('moodle', 'agedigitalconsentmap') === $oldageofdigitalconsentmap) {
1836              // If the site is still using the old defaults, upgrade to the new default.
1837              $ageofdigitalconsentmap = implode(PHP_EOL, [
1838                  '*, 16',
1839                  'AT, 14',
1840                  'BE, 13',
1841                  'BG, 14',
1842                  'CY, 14',
1843                  'CZ, 15',
1844                  'DK, 13',
1845                  'EE, 13',
1846                  'ES, 14',
1847                  'FI, 13',
1848                  'FR, 15',
1849                  'GB, 13',
1850                  'GR, 15',
1851                  'IT, 14',
1852                  'LT, 14',
1853                  'LV, 13',
1854                  'MT, 13',
1855                  'NO, 13',
1856                  'PT, 13',
1857                  'SE, 13',
1858                  'US, 13'
1859              ]);
1860              set_config('agedigitalconsentmap', $ageofdigitalconsentmap);
1861          }
1862  
1863          upgrade_main_savepoint(true, 2020061500.02);
1864      }
1865  
1866      if ($oldversion < 2020062600.01) {
1867          // Add index to the token field in the external_tokens table.
1868          $table = new xmldb_table('external_tokens');
1869          $index = new xmldb_index('token', XMLDB_INDEX_NOTUNIQUE, ['token']);
1870  
1871          if (!$dbman->index_exists($table, $index)) {
1872              $dbman->add_index($table, $index);
1873          }
1874  
1875          upgrade_main_savepoint(true, 2020062600.01);
1876      }
1877  
1878      if ($oldversion < 2020071100.01) {
1879          // Clean up completion criteria records referring to NULL course prerequisites.
1880          $select = 'criteriatype = :type AND courseinstance IS NULL';
1881          $params = ['type' => 8]; // COMPLETION_CRITERIA_TYPE_COURSE.
1882  
1883          $DB->delete_records_select('course_completion_criteria', $select, $params);
1884  
1885          // Main savepoint reached.
1886          upgrade_main_savepoint(true, 2020071100.01);
1887      }
1888  
1889      if ($oldversion < 2020072300.01) {
1890          // Restore and set the guest user if it has been previously removed via GDPR, or set to an nonexistent
1891          // user account.
1892          $currentguestuser = $DB->get_record('user', array('id' => $CFG->siteguest));
1893  
1894          if (!$currentguestuser) {
1895              if (!$guest = $DB->get_record('user', array('username' => 'guest', 'mnethostid' => $CFG->mnet_localhost_id))) {
1896                  // Create a guest user account.
1897                  $guest = new stdClass();
1898                  $guest->auth        = 'manual';
1899                  $guest->username    = 'guest';
1900                  $guest->password    = hash_internal_user_password('guest');
1901                  $guest->firstname   = get_string('guestuser');
1902                  $guest->lastname    = ' ';
1903                  $guest->email       = 'root@localhost';
1904                  $guest->description = get_string('guestuserinfo');
1905                  $guest->mnethostid  = $CFG->mnet_localhost_id;
1906                  $guest->confirmed   = 1;
1907                  $guest->lang        = $CFG->lang;
1908                  $guest->timemodified= time();
1909                  $guest->id = $DB->insert_record('user', $guest);
1910              }
1911              // Set the guest user.
1912              set_config('siteguest', $guest->id);
1913          }
1914  
1915          // Main savepoint reached.
1916          upgrade_main_savepoint(true, 2020072300.01);
1917      }
1918  
1919      if ($oldversion < 2020081400.01) {
1920          // Delete all user evidence files from users that have been deleted.
1921          $sql = "SELECT DISTINCT f.*
1922                    FROM {files} f
1923               LEFT JOIN {context} c ON f.contextid = c.id
1924                   WHERE f.component = :component
1925                     AND f.filearea = :filearea
1926                     AND c.id IS NULL";
1927          $stalefiles = $DB->get_records_sql($sql, ['component' => 'core_competency', 'filearea' => 'userevidence']);
1928  
1929          $fs = get_file_storage();
1930          foreach ($stalefiles as $stalefile) {
1931              $fs->get_file_instance($stalefile)->delete();
1932          }
1933  
1934          upgrade_main_savepoint(true, 2020081400.01);
1935      }
1936  
1937      if ($oldversion < 2020081400.02) {
1938  
1939          // Define field timecreated to be added to task_adhoc.
1940          $table = new xmldb_table('task_adhoc');
1941          $field = new xmldb_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'blocking');
1942  
1943          // Conditionally launch add field timecreated.
1944          if (!$dbman->field_exists($table, $field)) {
1945              $dbman->add_field($table, $field);
1946          }
1947  
1948          // Main savepoint reached.
1949          upgrade_main_savepoint(true, 2020081400.02);
1950      }
1951  
1952      if ($oldversion < 2020082200.01) {
1953          // Define field metadatasettings to be added to h5p_libraries.
1954          $table = new xmldb_table('h5p_libraries');
1955          $field = new xmldb_field('metadatasettings', XMLDB_TYPE_TEXT, null, null, null, null, null, 'coreminor');
1956  
1957          // Conditionally launch add field metadatasettings.
1958          if (!$dbman->field_exists($table, $field)) {
1959              $dbman->add_field($table, $field);
1960          }
1961  
1962          // Get installed library files that have no metadata settings value.
1963          $params = [
1964              'component' => 'core_h5p',
1965              'filearea' => 'libraries',
1966              'filename' => 'library.json',
1967          ];
1968          $sql = "SELECT l.id, f.id as fileid
1969                    FROM {files} f
1970               LEFT JOIN {h5p_libraries} l ON f.itemid = l.id
1971                   WHERE f.component = :component
1972                         AND f.filearea = :filearea
1973                         AND f.filename = :filename";
1974          $libraries = $DB->get_records_sql($sql, $params);
1975  
1976          // Update metadatasettings field when the attribute is present in the library.json file.
1977          $fs = get_file_storage();
1978          foreach ($libraries as $library) {
1979              $jsonfile = $fs->get_file_by_id($library->fileid);
1980              $jsoncontent = json_decode($jsonfile->get_content());
1981              if (isset($jsoncontent->metadataSettings)) {
1982                  unset($library->fileid);
1983                  $library->metadatasettings = json_encode($jsoncontent->metadataSettings);
1984                  $DB->update_record('h5p_libraries', $library);
1985              }
1986          }
1987  
1988          // Main savepoint reached.
1989          upgrade_main_savepoint(true, 2020082200.01);
1990      }
1991  
1992      if ($oldversion < 2020082200.02) {
1993          // Define fields to be added to task_scheduled.
1994          $table = new xmldb_table('task_scheduled');
1995          $field = new xmldb_field('timestarted', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'disabled');
1996          if (!$dbman->field_exists($table, $field)) {
1997              $dbman->add_field($table, $field);
1998          }
1999          $field = new xmldb_field('hostname', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'timestarted');
2000          if (!$dbman->field_exists($table, $field)) {
2001              $dbman->add_field($table, $field);
2002          }
2003          $field = new xmldb_field('pid', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'hostname');
2004          if (!$dbman->field_exists($table, $field)) {
2005              $dbman->add_field($table, $field);
2006          }
2007  
2008          // Define fields to be added to task_adhoc.
2009          $table = new xmldb_table('task_adhoc');
2010          $field = new xmldb_field('timestarted', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'blocking');
2011          if (!$dbman->field_exists($table, $field)) {
2012              $dbman->add_field($table, $field);
2013          }
2014          $field = new xmldb_field('hostname', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'timestarted');
2015          if (!$dbman->field_exists($table, $field)) {
2016              $dbman->add_field($table, $field);
2017          }
2018          $field = new xmldb_field('pid', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'hostname');
2019          if (!$dbman->field_exists($table, $field)) {
2020              $dbman->add_field($table, $field);
2021          }
2022  
2023          // Define fields to be added to task_log.
2024          $table = new xmldb_table('task_log');
2025          $field = new xmldb_field('hostname', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'output');
2026          if (!$dbman->field_exists($table, $field)) {
2027              $dbman->add_field($table, $field);
2028          }
2029          $field = new xmldb_field('pid', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'hostname');
2030          if (!$dbman->field_exists($table, $field)) {
2031              $dbman->add_field($table, $field);
2032          }
2033  
2034          // Main savepoint reached.
2035          upgrade_main_savepoint(true, 2020082200.02);
2036      }
2037  
2038      if ($oldversion < 2020082200.03) {
2039          // Define table to store virus infected details.
2040          $table = new xmldb_table('infected_files');
2041  
2042          // Adding fields to table infected_files.
2043          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2044          $table->add_field('filename', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
2045          $table->add_field('quarantinedfile', XMLDB_TYPE_TEXT, null, null, null, null, null);
2046          $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
2047          $table->add_field('reason', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
2048          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2049  
2050          // Adding keys to table infected_files.
2051          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
2052          $table->add_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']);
2053  
2054          // Conditionally launch create table for infected_files.
2055          if (!$dbman->table_exists($table)) {
2056              $dbman->create_table($table);
2057          }
2058          upgrade_main_savepoint(true, 2020082200.03);
2059      }
2060  
2061      if ($oldversion < 2020091000.02) {
2062          // Remove all the files with component='core_h5p' and filearea='editor' because they won't be used anymore.
2063          $fs = get_file_storage();
2064          $syscontext = context_system::instance();
2065          $fs->delete_area_files($syscontext->id, 'core_h5p', 'editor');
2066  
2067          // Main savepoint reached.
2068          upgrade_main_savepoint(true, 2020091000.02);
2069      }
2070  
2071      if ($oldversion < 2020091800.01) {
2072          // Copy From id captures the id of the source course when a new course originates from a restore
2073          // of another course on the same site.
2074          $table = new xmldb_table('course');
2075          $field = new xmldb_field('originalcourseid', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
2076  
2077          if (!$dbman->field_exists($table, $field)) {
2078              $dbman->add_field($table, $field);
2079          }
2080  
2081          // Main savepoint reached.
2082          upgrade_main_savepoint(true, 2020091800.01);
2083      }
2084  
2085      if ($oldversion < 2020100200.01) {
2086          // Define table oauth2_refresh_token to be created.
2087          $table = new xmldb_table('oauth2_refresh_token');
2088  
2089          // Adding fields to table oauth2_refresh_token.
2090          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2091          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
2092          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
2093          $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
2094          $table->add_field('issuerid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
2095          $table->add_field('token', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
2096          $table->add_field('scopehash', XMLDB_TYPE_CHAR, 40, null, XMLDB_NOTNULL, null, null);
2097  
2098          // Adding keys to table oauth2_refresh_token.
2099          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
2100          $table->add_key('issueridkey', XMLDB_KEY_FOREIGN, ['issuerid'], 'oauth2_issuer', ['id']);
2101          $table->add_key('useridkey', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']);
2102  
2103          // Adding indexes to table oauth2_refresh_token.
2104          $table->add_index('userid-issuerid-scopehash', XMLDB_INDEX_UNIQUE, array('userid', 'issuerid', 'scopehash'));
2105  
2106          // Conditionally launch create table for oauth2_refresh_token.
2107          if (!$dbman->table_exists($table)) {
2108              $dbman->create_table($table);
2109          }
2110  
2111          // Main savepoint reached.
2112          upgrade_main_savepoint(true, 2020100200.01);
2113      }
2114  
2115      if ($oldversion < 2020100700.00) {
2116  
2117          // Define index modulename-instance-eventtype (not unique) to be added to event.
2118          $table = new xmldb_table('event');
2119          $index = new xmldb_index('modulename-instance-eventtype', XMLDB_INDEX_NOTUNIQUE, ['modulename', 'instance', 'eventtype']);
2120  
2121          // Conditionally launch add index modulename-instance-eventtype.
2122          if (!$dbman->index_exists($table, $index)) {
2123              $dbman->add_index($table, $index);
2124          }
2125  
2126          // Define index modulename-instance (not unique) to be dropped form event.
2127          $table = new xmldb_table('event');
2128          $index = new xmldb_index('modulename-instance', XMLDB_INDEX_NOTUNIQUE, ['modulename', 'instance']);
2129  
2130          // Conditionally launch drop index modulename-instance.
2131          if ($dbman->index_exists($table, $index)) {
2132              $dbman->drop_index($table, $index);
2133          }
2134  
2135          // Main savepoint reached.
2136          upgrade_main_savepoint(true, 2020100700.00);
2137      }
2138  
2139      if ($oldversion < 2020101300.01) {
2140          // Define fields tutorial and example to be added to h5p_libraries.
2141          $table = new xmldb_table('h5p_libraries');
2142  
2143          // Add tutorial field.
2144          $field = new xmldb_field('tutorial', XMLDB_TYPE_TEXT, null, null, null, null, null, 'metadatasettings');
2145          if (!$dbman->field_exists($table, $field)) {
2146              $dbman->add_field($table, $field);
2147          }
2148  
2149          // Add example field.
2150          $field = new xmldb_field('example', XMLDB_TYPE_TEXT, null, null, null, null, null, 'tutorial');
2151  
2152          if (!$dbman->field_exists($table, $field)) {
2153              $dbman->add_field($table, $field);
2154          }
2155  
2156          // Main savepoint reached.
2157          upgrade_main_savepoint(true, 2020101300.01);
2158      }
2159  
2160      if ($oldversion < 2020101600.01) {
2161          // Delete orphaned course_modules_completion rows; these were not deleted properly
2162          // by remove_course_contents function.
2163          $DB->delete_records_select('course_modules_completion', "
2164                  NOT EXISTS (
2165                          SELECT 1
2166                            FROM {course_modules} cm
2167                           WHERE cm.id = {course_modules_completion}.coursemoduleid
2168                  )");
2169          upgrade_main_savepoint(true, 2020101600.01);
2170      }
2171  
2172      if ($oldversion < 2020101600.02) {
2173          // Script to fix incorrect records of "hidden" field in existing grade items.
2174          $sql = "SELECT cm.instance, cm.course
2175                    FROM {course_modules} cm
2176                    JOIN {modules} m ON m.id = cm.module
2177                   WHERE m.name = :module AND cm.visible = :visible";
2178          $hidequizlist = $DB->get_recordset_sql($sql, ['module' => 'quiz', 'visible' => 0]);
2179  
2180          foreach ($hidequizlist as $hidequiz) {
2181              $params = [
2182                  'itemmodule'    => 'quiz',
2183                  'courseid'      => $hidequiz->course,
2184                  'iteminstance'  => $hidequiz->instance,
2185              ];
2186  
2187              $DB->set_field('grade_items', 'hidden', 1, $params);
2188          }
2189          $hidequizlist->close();
2190  
2191          upgrade_main_savepoint(true, 2020101600.02);
2192      }
2193  
2194      if ($oldversion < 2020102100.01) {
2195          // Get the current guest user which is also set as 'deleted'.
2196          $guestuser = $DB->get_record('user', ['id' => $CFG->siteguest, 'deleted' => 1]);
2197          // If there is a deleted guest user, reset the user to not be deleted and make sure the related
2198          // user context exists.
2199          if ($guestuser) {
2200              $guestuser->deleted = 0;
2201              $DB->update_record('user', $guestuser);
2202  
2203              // Get the guest user context.
2204              $guestusercontext = $DB->get_record('context',
2205                  ['contextlevel' => CONTEXT_USER, 'instanceid' => $guestuser->id]);
2206  
2207              // If the guest user context does not exist, create it.
2208              if (!$guestusercontext) {
2209                  $record = new stdClass();
2210                  $record->contextlevel = CONTEXT_USER;
2211                  $record->instanceid = $guestuser->id;
2212                  $record->depth = 0;
2213                  // The path is not known before insert.
2214                  $record->path = null;
2215                  $record->locked = 0;
2216  
2217                  $record->id = $DB->insert_record('context', $record);
2218  
2219                  // Update the path.
2220                  $record->path = '/' . SYSCONTEXTID . '/' . $record->id;
2221                  $record->depth = substr_count($record->path, '/');
2222                  $DB->update_record('context', $record);
2223              }
2224          }
2225  
2226          // Main savepoint reached.
2227          upgrade_main_savepoint(true, 2020102100.01);
2228      }
2229  
2230      if ($oldversion < 2020102100.02) {
2231          // Reset analytics model output dir if it's the default value.
2232          $modeloutputdir = get_config('analytics', 'modeloutputdir');
2233          if (strcasecmp($modeloutputdir, $CFG->dataroot . DIRECTORY_SEPARATOR . 'models') == 0) {
2234              set_config('modeloutputdir', '', 'analytics');
2235          }
2236  
2237          // Main savepoint reached.
2238          upgrade_main_savepoint(true, 2020102100.02);
2239      }
2240  
2241      if ($oldversion < 2020102300.01) {
2242          // Define field downloadcontent to be added to course.
2243          $table = new xmldb_table('course');
2244          $field = new xmldb_field('downloadcontent', XMLDB_TYPE_INTEGER, '1', null, null, null, null, 'visibleold');
2245  
2246          if (!$dbman->field_exists($table, $field)) {
2247              $dbman->add_field($table, $field);
2248          }
2249  
2250          // Main savepoint reached.
2251          upgrade_main_savepoint(true, 2020102300.01);
2252      }
2253  
2254      if ($oldversion < 2020102300.02) {
2255          $table = new xmldb_table('badge_backpack');
2256  
2257          // There is no key_exists, so test the equivalent index.
2258          $oldindex = new xmldb_index('backpackcredentials', XMLDB_KEY_UNIQUE, ['userid', 'externalbackpackid']);
2259          if (!$dbman->index_exists($table, $oldindex)) {
2260              // All external backpack providers/hosts are now exclusively stored in badge_external_backpack.
2261              // All credentials are stored in badge_backpack and are unique per user, backpack.
2262              $uniquekey = new xmldb_key('backpackcredentials', XMLDB_KEY_UNIQUE, ['userid', 'externalbackpackid']);
2263              $dbman->add_key($table, $uniquekey);
2264          }
2265  
2266          // Drop the password field as this is moved to badge_backpack.
2267          $table = new xmldb_table('badge_external_backpack');
2268          $field = new xmldb_field('password', XMLDB_TYPE_CHAR, '50');
2269          if ($dbman->field_exists($table, $field)) {
2270              // If there is a current backpack set then copy it across to the new structure.
2271              if ($CFG->badges_defaultissuercontact) {
2272                  // Get the currently used site backpacks.
2273                  $records = $DB->get_records_select('badge_external_backpack', "password IS NOT NULL AND password != ''");
2274                  $backpack = [
2275                      'userid' => '0',
2276                      'email' => $CFG->badges_defaultissuercontact,
2277                      'backpackuid' => -1
2278                  ];
2279  
2280                  // Create records corresponding to the site backpacks.
2281                  foreach ($records as $record) {
2282                      $backpack['password'] = $record->password;
2283                      $backpack['externalbackpackid'] = $record->id;
2284                      $DB->insert_record('badge_backpack', (object) $backpack);
2285                  }
2286              }
2287  
2288              $dbman->drop_field($table, $field);
2289          }
2290  
2291          // Main savepoint reached.
2292          upgrade_main_savepoint(true, 2020102300.02);
2293      }
2294  
2295      if ($oldversion < 2020102700.04) {
2296  
2297          // Define table payment_accounts to be created.
2298          $table = new xmldb_table('payment_accounts');
2299  
2300          // Adding fields to table payment_accounts.
2301          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2302          $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
2303          $table->add_field('idnumber', XMLDB_TYPE_CHAR, '100', null, null, null, null);
2304          $table->add_field('contextid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
2305          $table->add_field('enabled', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0');
2306          $table->add_field('archived', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0');
2307          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
2308          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
2309  
2310          // Adding keys to table payment_accounts.
2311          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
2312  
2313          // Conditionally launch create table for payment_accounts.
2314          if (!$dbman->table_exists($table)) {
2315              $dbman->create_table($table);
2316          }
2317  
2318          // Define table payment_gateways to be created.
2319          $table = new xmldb_table('payment_gateways');
2320  
2321          // Adding fields to table payment_gateways.
2322          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2323          $table->add_field('accountid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
2324          $table->add_field('gateway', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
2325          $table->add_field('enabled', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1');
2326          $table->add_field('config', XMLDB_TYPE_TEXT, null, null, null, null, null);
2327          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
2328          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
2329  
2330          // Adding keys to table payment_gateways.
2331          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
2332          $table->add_key('accountid', XMLDB_KEY_FOREIGN, ['accountid'], 'payment_accounts', ['id']);
2333  
2334          // Conditionally launch create table for payment_gateways.
2335          if (!$dbman->table_exists($table)) {
2336              $dbman->create_table($table);
2337          }
2338  
2339          // Define table payments to be created.
2340          $table = new xmldb_table('payments');
2341  
2342          // Adding fields to table payments.
2343          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2344          $table->add_field('component', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
2345          $table->add_field('paymentarea', XMLDB_TYPE_CHAR, '50', null, XMLDB_NOTNULL, null, null);
2346          $table->add_field('itemid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
2347          $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
2348          $table->add_field('amount', XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, null);
2349          $table->add_field('currency', XMLDB_TYPE_CHAR, '3', null, XMLDB_NOTNULL, null, null);
2350          $table->add_field('accountid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
2351          $table->add_field('gateway', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
2352          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2353          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2354  
2355          // Adding keys to table payments.
2356          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
2357          $table->add_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']);
2358          $table->add_key('accountid', XMLDB_KEY_FOREIGN, ['accountid'], 'payment_accounts', ['id']);
2359  
2360          // Adding indexes to table payments.
2361          $table->add_index('gateway', XMLDB_INDEX_NOTUNIQUE, ['gateway']);
2362          $table->add_index('component-paymentarea-itemid', XMLDB_INDEX_NOTUNIQUE, ['component', 'paymentarea', 'itemid']);
2363  
2364          // Conditionally launch create table for payments.
2365          if (!$dbman->table_exists($table)) {
2366              $dbman->create_table($table);
2367          }
2368  
2369          // Main savepoint reached.
2370          upgrade_main_savepoint(true, 2020102700.04);
2371      }
2372  
2373      // Automatically generated Moodle v3.10.0 release upgrade line.
2374      // Put any upgrade step following this.
2375  
2376      if ($oldversion < 2020111500.01) {
2377          // Get all lessons that are set with a completion criteria of 'requires grade' but with no grade type set.
2378          $sql = "SELECT cm.id
2379                    FROM {course_modules} cm
2380                    JOIN {lesson} l ON l.id = cm.instance
2381                    JOIN {modules} m ON m.id = cm.module
2382                   WHERE m.name = :name AND cm.completiongradeitemnumber IS NOT NULL AND l.grade = :grade";
2383  
2384          do {
2385              if ($invalidconfigrations = $DB->get_records_sql($sql, ['name' => 'lesson', 'grade' => 0], 0, 1000)) {
2386                  list($insql, $inparams) = $DB->get_in_or_equal(array_keys($invalidconfigrations), SQL_PARAMS_NAMED);
2387                  $DB->set_field_select('course_modules', 'completiongradeitemnumber', null, "id $insql", $inparams);
2388              }
2389          } while ($invalidconfigrations);
2390  
2391          upgrade_main_savepoint(true, 2020111500.01);
2392      }
2393  
2394      if ($oldversion < 2021013100.00) {
2395          $DB->delete_records_select('event', "eventtype = 'category' AND categoryid = 0 AND userid <> 0");
2396  
2397          upgrade_main_savepoint(true, 2021013100.00);
2398      }
2399  
2400      if ($oldversion < 2021021100.01) {
2401          // Define field visibility to be added to contentbank_content.
2402          $table = new xmldb_table('contentbank_content');
2403          $field = new xmldb_field('visibility', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1', 'contextid');
2404  
2405          // Conditionally launch add field visibility.
2406          if (!$dbman->field_exists($table, $field)) {
2407              $dbman->add_field($table, $field);
2408          }
2409  
2410          // Main savepoint reached.
2411          upgrade_main_savepoint(true, 2021021100.01);
2412      }
2413  
2414      if ($oldversion < 2021021600.00) {
2415  
2416          // We are going to remove the field 'hidepicture' from the groups
2417          // so we need to remove the pictures from those groups. But we prevent
2418          // the execution twice because this could be executed again when upgrading
2419          // to different versions.
2420          if ($dbman->field_exists('groups', 'hidepicture')) {
2421  
2422              $sql = "SELECT g.id, g.courseid, ctx.id AS contextid
2423                         FROM {groups} g
2424                         JOIN {context} ctx
2425                           ON ctx.instanceid = g.courseid
2426                          AND ctx.contextlevel = :contextlevel
2427                        WHERE g.hidepicture = 1";
2428  
2429              // Selecting all the groups that have hide picture enabled, and organising them by context.
2430              $groupctx = [];
2431              $records = $DB->get_recordset_sql($sql, ['contextlevel' => CONTEXT_COURSE]);
2432              foreach ($records as $record) {
2433                  if (!isset($groupctx[$record->contextid])) {
2434                      $groupctx[$record->contextid] = [];
2435                  }
2436                  $groupctx[$record->contextid][] = $record->id;
2437              }
2438              $records->close();
2439  
2440              // Deleting the group files.
2441              $fs = get_file_storage();
2442              foreach ($groupctx as $contextid => $groupids) {
2443                  list($in, $inparams) = $DB->get_in_or_equal($groupids, SQL_PARAMS_NAMED);
2444                  $fs->delete_area_files_select($contextid, 'group', 'icon', $in, $inparams);
2445              }
2446  
2447              // Updating the database to remove picture from all those groups.
2448              $sql = "UPDATE {groups} SET picture = :pic WHERE hidepicture = :hide";
2449              $DB->execute($sql, ['pic' => 0, 'hide' => 1]);
2450          }
2451  
2452          // Define field hidepicture to be dropped from groups.
2453          $table = new xmldb_table('groups');
2454          $field = new xmldb_field('hidepicture');
2455  
2456          // Conditionally launch drop field hidepicture.
2457          if ($dbman->field_exists($table, $field)) {
2458              $dbman->drop_field($table, $field);
2459          }
2460  
2461          // Main savepoint reached.
2462          upgrade_main_savepoint(true, 2021021600.00);
2463      }
2464  
2465      if ($oldversion < 2021022600.01) {
2466          // Get all the external backpacks and update the sortorder column, to avoid repeated/wrong values. As sortorder was not
2467          // used since now, the id column will be the criteria to follow for re-ordering them with a valid value.
2468          $i = 1;
2469          $records = $DB->get_records('badge_external_backpack', null, 'id ASC');
2470          foreach ($records as $record) {
2471              $record->sortorder = $i++;
2472              $DB->update_record('badge_external_backpack', $record);
2473          }
2474  
2475          upgrade_main_savepoint(true, 2021022600.01);
2476      }
2477  
2478      if ($oldversion < 2021030500.01) {
2479          // The $CFG->badges_site_backpack setting has been removed because it's not required anymore. From now, the default backpack
2480          // will be the one with lower sortorder value.
2481          unset_config('badges_site_backpack');
2482  
2483          upgrade_main_savepoint(true, 2021030500.01);
2484      }
2485  
2486      if ($oldversion < 2021031200.01) {
2487  
2488          // Define field type to be added to oauth2_issuer.
2489          $table = new xmldb_table('oauth2_issuer');
2490          $field = new xmldb_field('servicetype', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'requireconfirmation');
2491  
2492          // Conditionally launch add field type.
2493          if (!$dbman->field_exists($table, $field)) {
2494              $dbman->add_field($table, $field);
2495          }
2496  
2497          // Set existing values to the proper servicetype value.
2498          // It's not critical if the servicetype column doesn't contain the proper value for Google, Microsoft, Facebook or
2499          // Nextcloud services because, for now, this value is used for services using different discovery method.
2500          // However, let's try to upgrade it using the default value for the baseurl or image. If any of these default values
2501          // have been changed, the servicetype column will remain NULL.
2502          $recordset = $DB->get_recordset('oauth2_issuer');
2503          foreach ($recordset as $record) {
2504              if ($record->baseurl == 'https://accounts.google.com/') {
2505                  $record->servicetype = 'google';
2506                  $DB->update_record('oauth2_issuer', $record);
2507              } else if ($record->image == 'https://www.microsoft.com/favicon.ico') {
2508                  $record->servicetype = 'microsoft';
2509                  $DB->update_record('oauth2_issuer', $record);
2510              } else if ($record->image == 'https://facebookbrand.com/wp-content/uploads/2016/05/flogo_rgb_hex-brc-site-250.png') {
2511                  $record->servicetype = 'facebook';
2512                  $DB->update_record('oauth2_issuer', $record);
2513              } else if ($record->image == 'https://nextcloud.com/wp-content/themes/next/assets/img/common/favicon.png?x16328') {
2514                  $record->servicetype = 'nextcloud';
2515                  $DB->update_record('oauth2_issuer', $record);
2516              }
2517          }
2518          $recordset->close();
2519  
2520          // Main savepoint reached.
2521          upgrade_main_savepoint(true, 2021031200.01);
2522      }
2523  
2524      if ($oldversion < 2021033100.00) {
2525          // Define field 'showactivitydates' to be added to course table.
2526          $table = new xmldb_table('course');
2527          $field = new xmldb_field('showactivitydates', XMLDB_TYPE_INTEGER, '1', null,
2528              XMLDB_NOTNULL, null, '0', 'originalcourseid');
2529  
2530          if (!$dbman->field_exists($table, $field)) {
2531              $dbman->add_field($table, $field);
2532          }
2533  
2534          // Main savepoint reached.
2535          upgrade_main_savepoint(true, 2021033100.00);
2536      }
2537  
2538      if ($oldversion < 2021033100.01) {
2539          // Define field 'showcompletionconditions' to be added to course.
2540          $table = new xmldb_table('course');
2541          $field = new xmldb_field('showcompletionconditions', XMLDB_TYPE_INTEGER, '1', null,
2542              XMLDB_NOTNULL, null, '1', 'completionnotify');
2543  
2544          if (!$dbman->field_exists($table, $field)) {
2545              $dbman->add_field($table, $field);
2546          }
2547  
2548          // Main savepoint reached.
2549          upgrade_main_savepoint(true, 2021033100.01);
2550      }
2551  
2552      if ($oldversion < 2021041300.01) {
2553  
2554          // Define field enabled to be added to h5p_libraries.
2555          $table = new xmldb_table('h5p_libraries');
2556          $field = new xmldb_field('enabled', XMLDB_TYPE_INTEGER, '1', null, null, null, '1', 'example');
2557  
2558          // Conditionally launch add field enabled.
2559          if (!$dbman->field_exists($table, $field)) {
2560              $dbman->add_field($table, $field);
2561          }
2562  
2563          // Main savepoint reached.
2564          upgrade_main_savepoint(true, 2021041300.01);
2565      }
2566  
2567      if ($oldversion < 2021042100.00) {
2568  
2569          // Define field loginpagename to be added to oauth2_issuer.
2570          $table = new xmldb_table('oauth2_issuer');
2571          $field = new xmldb_field('loginpagename', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'servicetype');
2572  
2573          // Conditionally launch add field loginpagename.
2574          if (!$dbman->field_exists($table, $field)) {
2575              $dbman->add_field($table, $field);
2576          }
2577  
2578          // Main savepoint reached.
2579          upgrade_main_savepoint(true, 2021042100.00);
2580      }
2581  
2582      if ($oldversion < 2021042100.01) {
2583          require_once($CFG->dirroot . '/user/profile/field/social/upgradelib.php');
2584          $table = new xmldb_table('user');
2585          $tablecolumns = ['icq', 'skype', 'aim', 'yahoo', 'msn', 'url'];
2586  
2587          foreach ($tablecolumns as $column) {
2588              $field = new xmldb_field($column);
2589              if ($dbman->field_exists($table, $field)) {
2590                  user_profile_social_moveto_profilefield($column);
2591                  $dbman->drop_field($table, $field);
2592              }
2593          }
2594  
2595          // Update all module availability if it relies on the old user fields.
2596          user_profile_social_update_module_availability();
2597  
2598          // Remove field mapping for oauth2.
2599          $DB->delete_records('oauth2_user_field_mapping', array('internalfield' => 'url'));
2600  
2601          // Main savepoint reached.
2602          upgrade_main_savepoint(true, 2021042100.01);
2603      }
2604  
2605      if ($oldversion < 2021042100.02) {
2606          require_once($CFG->libdir . '/db/upgradelib.php');
2607  
2608          // Check if this site has executed the problematic upgrade steps.
2609          $needsfixing = upgrade_calendar_site_status(false);
2610  
2611          // Only queue the task if this site has been affected by the problematic upgrade step.
2612          if ($needsfixing) {
2613  
2614              // Create adhoc task to search and recover orphaned calendar events.
2615              $record = new \stdClass();
2616              $record->classname = '\core\task\calendar_fix_orphaned_events';
2617  
2618              // Next run time based from nextruntime computation in \core\task\manager::queue_adhoc_task().
2619              $nextruntime = time() - 1;
2620              $record->nextruntime = $nextruntime;
2621              $DB->insert_record('task_adhoc', $record);
2622          }
2623  
2624          // Main savepoint reached.
2625          upgrade_main_savepoint(true, 2021042100.02);
2626      }
2627  
2628      if ($oldversion < 2021042400.00) {
2629          // Changing the default of field showcompletionconditions on table course to 0.
2630          $table = new xmldb_table('course');
2631          $field = new xmldb_field('showcompletionconditions', XMLDB_TYPE_INTEGER, '1', null, null, null, null, 'showactivitydates');
2632  
2633          // Launch change of nullability for field showcompletionconditions.
2634          $dbman->change_field_notnull($table, $field);
2635  
2636          // Launch change of default for field showcompletionconditions.
2637          $dbman->change_field_default($table, $field);
2638  
2639          // Set showcompletionconditions to null for courses which don't track completion.
2640          $sql = "UPDATE {course}
2641                     SET showcompletionconditions = null
2642                   WHERE enablecompletion <> 1";
2643          $DB->execute($sql);
2644  
2645          // Main savepoint reached.
2646          upgrade_main_savepoint(true, 2021042400.00);
2647      }
2648  
2649      if ($oldversion < 2021043000.01) {
2650          // Remove usemodchooser user preference for every user.
2651          $DB->delete_records('user_preferences', ['name' => 'usemodchooser']);
2652  
2653          // Main savepoint reached.
2654          upgrade_main_savepoint(true, 2021043000.01);
2655      }
2656  
2657      // Automatically generated Moodle v3.11.0 release upgrade line.
2658      // Put any upgrade step following this.
2659  
2660      if ($oldversion < 2021051700.03) {
2661  
2662          // Define index name (not unique) to be added to user_preferences.
2663          $table = new xmldb_table('user_preferences');
2664          $index = new xmldb_index('name', XMLDB_INDEX_NOTUNIQUE, ['name']);
2665  
2666          // Conditionally launch add index name.
2667          if (!$dbman->index_exists($table, $index)) {
2668              $dbman->add_index($table, $index);
2669          }
2670  
2671          // Main savepoint reached.
2672          upgrade_main_savepoint(true, 2021051700.03);
2673      }
2674  
2675      if ($oldversion < 2021051700.05) {
2676          // Update the externalfield to be larger.
2677          $table = new xmldb_table('oauth2_user_field_mapping');
2678          $field = new xmldb_field('externalfield', XMLDB_TYPE_CHAR, '500', null, XMLDB_NOTNULL, false, null, 'issuerid');
2679          $dbman->change_field_type($table, $field);
2680  
2681          // Main savepoint reached.
2682          upgrade_main_savepoint(true, 2021051700.05);
2683      }
2684  
2685      if ($oldversion < 2021051706.12) {
2686          // Social custom fields could had been created linked to category id = 1. Let's check category 1 exists.
2687          if (!$DB->get_record('user_info_category', ['id' => 1])) {
2688              // Let's check if we have any custom field linked to category id = 1.
2689              $fields = $DB->get_records('user_info_field', ['categoryid' => 1]);
2690              if (!empty($fields)) {
2691                  $categoryid = $DB->get_field_sql('SELECT min(id) from {user_info_category}');
2692                  foreach ($fields as $field) {
2693                      $field->categoryid = $categoryid;
2694                      $DB->update_record('user_info_field', $field);
2695                  }
2696              }
2697          }
2698  
2699          // Main savepoint reached.
2700          upgrade_main_savepoint(true, 2021051706.12);
2701      }
2702  
2703      if ($oldversion < 2021051707.05) {
2704  
2705          // Changing precision of field hidden on table grade_categories to (10).
2706          $table = new xmldb_table('grade_categories');
2707          $field = new xmldb_field('hidden', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'timemodified');
2708  
2709          // Launch change of precision for field hidden.
2710          $dbman->change_field_precision($table, $field);
2711  
2712          // Changing precision of field hidden on table grade_categories_history to (10).
2713          $table = new xmldb_table('grade_categories_history');
2714          $field = new xmldb_field('hidden', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'aggregatesubcats');
2715  
2716          // Launch change of precision for field hidden.
2717          $dbman->change_field_precision($table, $field);
2718  
2719          // Main savepoint reached.
2720          upgrade_main_savepoint(true, 2021051707.05);
2721      }
2722  
2723      if ($oldversion < 2021051710.03) {
2724  
2725          // Remove any orphaned tag instance records (pointing to non-existing context).
2726          $DB->delete_records_select('tag_instance', 'NOT EXISTS (
2727              SELECT ctx.id FROM {context} ctx WHERE ctx.id = {tag_instance}.contextid
2728          )');
2729  
2730          // Main savepoint reached.
2731          upgrade_main_savepoint(true, 2021051710.03);
2732      }
2733  
2734      if ($oldversion < 2021051710.14) {
2735          $table = new xmldb_table('h5p');
2736          $indexpathnamehash = new xmldb_index('pathnamehash_idx', XMLDB_INDEX_NOTUNIQUE, ['pathnamehash']);
2737  
2738          if (!$dbman->index_exists($table, $indexpathnamehash)) {
2739              $dbman->add_index($table, $indexpathnamehash);
2740          }
2741          // Main savepoint reached.
2742          upgrade_main_savepoint(true, 2021051710.14);
2743      }
2744  
2745      return true;
2746  }