Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.
/lib/db/ -> upgrade.php (source)

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

   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 < 2021052500.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, 2021052500.01);
1935      }
1936  
1937      if ($oldversion < 2021052500.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, 2021052500.02);
1950      }
1951  
1952      if ($oldversion < 2021052500.04) {
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, 2021052500.04);
1990      }
1991  
1992      if ($oldversion < 2021052500.05) {
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, 2021052500.05);
2036      }
2037  
2038      if ($oldversion < 2021052500.06) {
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, 2021052500.06);
2059      }
2060  
2061      if ($oldversion < 2021052500.13) {
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, 2021052500.13);
2069      }
2070  
2071      if ($oldversion < 2021052500.15) {
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, 2021052500.15);
2083      }
2084  
2085      if ($oldversion < 2021052500.19) {
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, 2021052500.19);
2113      }
2114  
2115      if ($oldversion < 2021052500.20) {
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, 2021052500.20);
2137      }
2138  
2139      if ($oldversion < 2021052500.24) {
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, 2021052500.24);
2158      }
2159  
2160      if ($oldversion < 2021052500.26) {
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, 2021052500.26);
2170      }
2171  
2172      if ($oldversion < 2021052500.27) {
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, 2021052500.27);
2192      }
2193  
2194      if ($oldversion < 2021052500.29) {
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, 2021052500.29);
2228      }
2229  
2230      if ($oldversion < 2021052500.30) {
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, 2021052500.30);
2239      }
2240  
2241      if ($oldversion < 2021052500.32) {
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, 2021052500.32);
2252      }
2253  
2254      if ($oldversion < 2021052500.33) {
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, 2021052500.33);
2293      }
2294  
2295      if ($oldversion < 2021052500.36) {
2296          // Define table payment_accounts to be created.
2297          $table = new xmldb_table('payment_accounts');
2298  
2299          // Adding fields to table payment_accounts.
2300          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2301          $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
2302          $table->add_field('idnumber', XMLDB_TYPE_CHAR, '100', null, null, null, null);
2303          $table->add_field('contextid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
2304          $table->add_field('enabled', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0');
2305          $table->add_field('archived', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0');
2306          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
2307          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
2308  
2309          // Adding keys to table payment_accounts.
2310          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
2311  
2312          // Conditionally launch create table for payment_accounts.
2313          if (!$dbman->table_exists($table)) {
2314              $dbman->create_table($table);
2315          }
2316  
2317          // Define table payment_gateways to be created.
2318          $table = new xmldb_table('payment_gateways');
2319  
2320          // Adding fields to table payment_gateways.
2321          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2322          $table->add_field('accountid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
2323          $table->add_field('gateway', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
2324          $table->add_field('enabled', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1');
2325          $table->add_field('config', XMLDB_TYPE_TEXT, null, null, null, null, null);
2326          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
2327          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
2328  
2329          // Adding keys to table payment_gateways.
2330          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
2331          $table->add_key('accountid', XMLDB_KEY_FOREIGN, ['accountid'], 'payment_accounts', ['id']);
2332  
2333          // Conditionally launch create table for payment_gateways.
2334          if (!$dbman->table_exists($table)) {
2335              $dbman->create_table($table);
2336          }
2337  
2338          // Define table payments to be created.
2339          $table = new xmldb_table('payments');
2340  
2341          // Adding fields to table payments.
2342          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2343          $table->add_field('component', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
2344          $table->add_field('paymentarea', XMLDB_TYPE_CHAR, '50', null, XMLDB_NOTNULL, null, null);
2345          $table->add_field('itemid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
2346          $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
2347          $table->add_field('amount', XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, null);
2348          $table->add_field('currency', XMLDB_TYPE_CHAR, '3', null, XMLDB_NOTNULL, null, null);
2349          $table->add_field('accountid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
2350          $table->add_field('gateway', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
2351          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2352          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2353  
2354          // Adding keys to table payments.
2355          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
2356          $table->add_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']);
2357          $table->add_key('accountid', XMLDB_KEY_FOREIGN, ['accountid'], 'payment_accounts', ['id']);
2358  
2359          // Adding indexes to table payments.
2360          $table->add_index('gateway', XMLDB_INDEX_NOTUNIQUE, ['gateway']);
2361          $table->add_index('component-paymentarea-itemid', XMLDB_INDEX_NOTUNIQUE, ['component', 'paymentarea', 'itemid']);
2362  
2363          // Conditionally launch create table for payments.
2364          if (!$dbman->table_exists($table)) {
2365              $dbman->create_table($table);
2366          }
2367  
2368          // Main savepoint reached.
2369          upgrade_main_savepoint(true, 2021052500.36);
2370      }
2371  
2372      if ($oldversion < 2021052500.42) {
2373          // Get all lessons that are set with a completion criteria of 'requires grade' but with no grade type set.
2374          $sql = "SELECT cm.id
2375                    FROM {course_modules} cm
2376                    JOIN {lesson} l ON l.id = cm.instance
2377                    JOIN {modules} m ON m.id = cm.module
2378                   WHERE m.name = :name AND cm.completiongradeitemnumber IS NOT NULL AND l.grade = :grade";
2379  
2380          do {
2381              if ($invalidconfigrations = $DB->get_records_sql($sql, ['name' => 'lesson', 'grade' => 0], 0, 1000)) {
2382                  list($insql, $inparams) = $DB->get_in_or_equal(array_keys($invalidconfigrations), SQL_PARAMS_NAMED);
2383                  $DB->set_field_select('course_modules', 'completiongradeitemnumber', null, "id $insql", $inparams);
2384              }
2385          } while ($invalidconfigrations);
2386  
2387          upgrade_main_savepoint(true, 2021052500.42);
2388      }
2389  
2390      if ($oldversion < 2021052500.55) {
2391          $DB->delete_records_select('event', "eventtype = 'category' AND categoryid = 0 AND userid <> 0");
2392  
2393          upgrade_main_savepoint(true, 2021052500.55);
2394      }
2395  
2396      if ($oldversion < 2021052500.59) {
2397          // Define field visibility to be added to contentbank_content.
2398          $table = new xmldb_table('contentbank_content');
2399          $field = new xmldb_field('visibility', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1', 'contextid');
2400  
2401          // Conditionally launch add field visibility.
2402          if (!$dbman->field_exists($table, $field)) {
2403              $dbman->add_field($table, $field);
2404          }
2405  
2406          // Main savepoint reached.
2407          upgrade_main_savepoint(true, 2021052500.59);
2408      }
2409  
2410      if ($oldversion < 2021052500.60) {
2411  
2412          // We are going to remove the field 'hidepicture' from the groups
2413          // so we need to remove the pictures from those groups. But we prevent
2414          // the execution twice because this could be executed again when upgrading
2415          // to different versions.
2416          if ($dbman->field_exists('groups', 'hidepicture')) {
2417  
2418              $sql = "SELECT g.id, g.courseid, ctx.id AS contextid
2419                         FROM {groups} g
2420                         JOIN {context} ctx
2421                           ON ctx.instanceid = g.courseid
2422                          AND ctx.contextlevel = :contextlevel
2423                        WHERE g.hidepicture = 1";
2424  
2425              // Selecting all the groups that have hide picture enabled, and organising them by context.
2426              $groupctx = [];
2427              $records = $DB->get_recordset_sql($sql, ['contextlevel' => CONTEXT_COURSE]);
2428              foreach ($records as $record) {
2429                  if (!isset($groupctx[$record->contextid])) {
2430                      $groupctx[$record->contextid] = [];
2431                  }
2432                  $groupctx[$record->contextid][] = $record->id;
2433              }
2434              $records->close();
2435  
2436              // Deleting the group files.
2437              $fs = get_file_storage();
2438              foreach ($groupctx as $contextid => $groupids) {
2439                  list($in, $inparams) = $DB->get_in_or_equal($groupids, SQL_PARAMS_NAMED);
2440                  $fs->delete_area_files_select($contextid, 'group', 'icon', $in, $inparams);
2441              }
2442  
2443              // Updating the database to remove picture from all those groups.
2444              $sql = "UPDATE {groups} SET picture = :pic WHERE hidepicture = :hide";
2445              $DB->execute($sql, ['pic' => 0, 'hide' => 1]);
2446          }
2447  
2448          // Define field hidepicture to be dropped from groups.
2449          $table = new xmldb_table('groups');
2450          $field = new xmldb_field('hidepicture');
2451  
2452          // Conditionally launch drop field hidepicture.
2453          if ($dbman->field_exists($table, $field)) {
2454              $dbman->drop_field($table, $field);
2455          }
2456  
2457          // Main savepoint reached.
2458          upgrade_main_savepoint(true, 2021052500.60);
2459      }
2460  
2461      if ($oldversion < 2021052500.64) {
2462          // Get all the external backpacks and update the sortorder column, to avoid repeated/wrong values. As sortorder was not
2463          // used since now, the id column will be the criteria to follow for re-ordering them with a valid value.
2464          $i = 1;
2465          $records = $DB->get_records('badge_external_backpack', null, 'id ASC');
2466          foreach ($records as $record) {
2467              $record->sortorder = $i++;
2468              $DB->update_record('badge_external_backpack', $record);
2469          }
2470  
2471          upgrade_main_savepoint(true, 2021052500.64);
2472      }
2473  
2474      if ($oldversion < 2021052500.67) {
2475          // The $CFG->badges_site_backpack setting has been removed because it's not required anymore. From now, the default backpack
2476          // will be the one with lower sortorder value.
2477          unset_config('badges_site_backpack');
2478  
2479          upgrade_main_savepoint(true, 2021052500.67);
2480      }
2481  
2482      if ($oldversion < 2021052500.69) {
2483  
2484          // Define field type to be added to oauth2_issuer.
2485          $table = new xmldb_table('oauth2_issuer');
2486          $field = new xmldb_field('servicetype', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'requireconfirmation');
2487  
2488          // Conditionally launch add field type.
2489          if (!$dbman->field_exists($table, $field)) {
2490              $dbman->add_field($table, $field);
2491          }
2492  
2493          // Set existing values to the proper servicetype value.
2494          // It's not critical if the servicetype column doesn't contain the proper value for Google, Microsoft, Facebook or
2495          // Nextcloud services because, for now, this value is used for services using different discovery method.
2496          // However, let's try to upgrade it using the default value for the baseurl or image. If any of these default values
2497          // have been changed, the servicetype column will remain NULL.
2498          $recordset = $DB->get_recordset('oauth2_issuer');
2499          foreach ($recordset as $record) {
2500              if ($record->baseurl == 'https://accounts.google.com/') {
2501                  $record->servicetype = 'google';
2502                  $DB->update_record('oauth2_issuer', $record);
2503              } else if ($record->image == 'https://www.microsoft.com/favicon.ico') {
2504                  $record->servicetype = 'microsoft';
2505                  $DB->update_record('oauth2_issuer', $record);
2506              } else if ($record->image == 'https://facebookbrand.com/wp-content/uploads/2016/05/flogo_rgb_hex-brc-site-250.png') {
2507                  $record->servicetype = 'facebook';
2508                  $DB->update_record('oauth2_issuer', $record);
2509              } else if ($record->image == 'https://nextcloud.com/wp-content/themes/next/assets/img/common/favicon.png?x16328') {
2510                  $record->servicetype = 'nextcloud';
2511                  $DB->update_record('oauth2_issuer', $record);
2512              }
2513          }
2514          $recordset->close();
2515  
2516          // Main savepoint reached.
2517          upgrade_main_savepoint(true, 2021052500.69);
2518      }
2519  
2520      if ($oldversion < 2021052500.74) {
2521          // Define field 'showactivitydates' to be added to course table.
2522          $table = new xmldb_table('course');
2523          $field = new xmldb_field('showactivitydates', XMLDB_TYPE_INTEGER, '1', null,
2524              XMLDB_NOTNULL, null, '0', 'originalcourseid');
2525  
2526          if (!$dbman->field_exists($table, $field)) {
2527              $dbman->add_field($table, $field);
2528          }
2529  
2530          // Main savepoint reached.
2531          upgrade_main_savepoint(true, 2021052500.74);
2532      }
2533  
2534      if ($oldversion < 2021052500.75) {
2535          // Define field 'showcompletionconditions' to be added to course.
2536          $table = new xmldb_table('course');
2537          $field = new xmldb_field('showcompletionconditions', XMLDB_TYPE_INTEGER, '1', null,
2538              XMLDB_NOTNULL, null, '1', 'completionnotify');
2539  
2540          if (!$dbman->field_exists($table, $field)) {
2541              $dbman->add_field($table, $field);
2542          }
2543  
2544          // Main savepoint reached.
2545          upgrade_main_savepoint(true, 2021052500.75);
2546      }
2547  
2548      if ($oldversion < 2021052500.78) {
2549  
2550          // Define field enabled to be added to h5p_libraries.
2551          $table = new xmldb_table('h5p_libraries');
2552          $field = new xmldb_field('enabled', XMLDB_TYPE_INTEGER, '1', null, null, null, '1', 'example');
2553  
2554          // Conditionally launch add field enabled.
2555          if (!$dbman->field_exists($table, $field)) {
2556              $dbman->add_field($table, $field);
2557          }
2558  
2559          // Main savepoint reached.
2560          upgrade_main_savepoint(true, 2021052500.78);
2561      }
2562  
2563      if ($oldversion < 2021052500.83) {
2564  
2565          // Define field loginpagename to be added to oauth2_issuer.
2566          $table = new xmldb_table('oauth2_issuer');
2567          $field = new xmldb_field('loginpagename', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'servicetype');
2568  
2569          // Conditionally launch add field loginpagename.
2570          if (!$dbman->field_exists($table, $field)) {
2571              $dbman->add_field($table, $field);
2572          }
2573  
2574          // Main savepoint reached.
2575          upgrade_main_savepoint(true, 2021052500.83);
2576      }
2577  
2578      if ($oldversion < 2021052500.84) {
2579          require_once($CFG->dirroot . '/user/profile/field/social/upgradelib.php');
2580          $table = new xmldb_table('user');
2581          $tablecolumns = ['icq', 'skype', 'aim', 'yahoo', 'msn', 'url'];
2582  
2583          foreach ($tablecolumns as $column) {
2584              $field = new xmldb_field($column);
2585              if ($dbman->field_exists($table, $field)) {
2586                  user_profile_social_moveto_profilefield($column);
2587                  $dbman->drop_field($table, $field);
2588              }
2589          }
2590  
2591          // Update all module availability if it relies on the old user fields.
2592          user_profile_social_update_module_availability();
2593  
2594          // Remove field mapping for oauth2.
2595          $DB->delete_records('oauth2_user_field_mapping', array('internalfield' => 'url'));
2596  
2597          // Main savepoint reached.
2598          upgrade_main_savepoint(true, 2021052500.84);
2599      }
2600  
2601      if ($oldversion < 2021052500.85) {
2602          require_once($CFG->libdir . '/db/upgradelib.php');
2603  
2604          // Check if this site has executed the problematic upgrade steps.
2605          $needsfixing = upgrade_calendar_site_status(false);
2606  
2607          // Only queue the task if this site has been affected by the problematic upgrade step.
2608          if ($needsfixing) {
2609  
2610              // Create adhoc task to search and recover orphaned calendar events.
2611              $record = new \stdClass();
2612              $record->classname = '\core\task\calendar_fix_orphaned_events';
2613  
2614              // Next run time based from nextruntime computation in \core\task\manager::queue_adhoc_task().
2615              $nextruntime = time() - 1;
2616              $record->nextruntime = $nextruntime;
2617              $DB->insert_record('task_adhoc', $record);
2618          }
2619  
2620          // Main savepoint reached.
2621          upgrade_main_savepoint(true, 2021052500.85);
2622      }
2623  
2624      if ($oldversion < 2021052500.87) {
2625          // Changing the default of field showcompletionconditions on table course to 0.
2626          $table = new xmldb_table('course');
2627          $field = new xmldb_field('showcompletionconditions', XMLDB_TYPE_INTEGER, '1', null, null, null, null, 'showactivitydates');
2628  
2629          // Launch change of nullability for field showcompletionconditions.
2630          $dbman->change_field_notnull($table, $field);
2631  
2632          // Launch change of default for field showcompletionconditions.
2633          $dbman->change_field_default($table, $field);
2634  
2635          // Set showcompletionconditions to null for courses which don't track completion.
2636          $sql = "UPDATE {course}
2637                     SET showcompletionconditions = null
2638                   WHERE enablecompletion <> 1";
2639          $DB->execute($sql);
2640  
2641          // Main savepoint reached.
2642          upgrade_main_savepoint(true, 2021052500.87);
2643      }
2644  
2645      if ($oldversion < 2021052500.90) {
2646          // Remove usemodchooser user preference for every user.
2647          $DB->delete_records('user_preferences', ['name' => 'usemodchooser']);
2648  
2649          // Main savepoint reached.
2650          upgrade_main_savepoint(true, 2021052500.90);
2651      }
2652  
2653      if ($oldversion < 2021060200.00) {
2654  
2655          // Define index name (not unique) to be added to user_preferences.
2656          $table = new xmldb_table('user_preferences');
2657          $index = new xmldb_index('name', XMLDB_INDEX_NOTUNIQUE, ['name']);
2658  
2659          // Conditionally launch add index name.
2660          if (!$dbman->index_exists($table, $index)) {
2661              $dbman->add_index($table, $index);
2662          }
2663  
2664          // Main savepoint reached.
2665          upgrade_main_savepoint(true, 2021060200.00);
2666      }
2667  
2668      if ($oldversion < 2021060900.00) {
2669          // Update the externalfield to be larger.
2670          $table = new xmldb_table('oauth2_user_field_mapping');
2671          $field = new xmldb_field('externalfield', XMLDB_TYPE_CHAR, '500', null, XMLDB_NOTNULL, false, null, 'issuerid');
2672          $dbman->change_field_type($table, $field);
2673  
2674          // Main savepoint reached.
2675          upgrade_main_savepoint(true, 2021060900.00);
2676      }
2677  
2678      if ($oldversion < 2021072800.01) {
2679          // Define table reportbuilder_report to be created.
2680          $table = new xmldb_table('reportbuilder_report');
2681  
2682          // Adding fields to table reportbuilder_report.
2683          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2684          $table->add_field('source', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
2685          $table->add_field('type', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0');
2686          $table->add_field('contextid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
2687          $table->add_field('component', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
2688          $table->add_field('area', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
2689          $table->add_field('itemid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2690          $table->add_field('usercreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2691          $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2692          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2693          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2694  
2695          // Adding keys to table reportbuilder_report.
2696          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
2697          $table->add_key('usercreated', XMLDB_KEY_FOREIGN, ['usercreated'], 'user', ['id']);
2698          $table->add_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']);
2699          $table->add_key('contextid', XMLDB_KEY_FOREIGN, ['contextid'], 'context', ['id']);
2700  
2701          // Conditionally launch create table for reportbuilder_report.
2702          if (!$dbman->table_exists($table)) {
2703              $dbman->create_table($table);
2704          }
2705  
2706          // Main savepoint reached.
2707          upgrade_main_savepoint(true, 2021072800.01);
2708      }
2709  
2710      if ($oldversion < 2021090200.01) {
2711          // Remove qformat_webct (unless it has manually been added back).
2712          if (!file_exists($CFG->dirroot . '/question/format/webct/format.php')) {
2713              unset_all_config_for_plugin('qformat_webct');
2714          }
2715  
2716          // Main savepoint reached.
2717          upgrade_main_savepoint(true, 2021090200.01);
2718      }
2719  
2720      if ($oldversion < 2021091100.01) {
2721          // If message_jabber is no longer present, remove it.
2722          if (!file_exists($CFG->dirroot . '/message/output/jabber/message_output_jabber.php')) {
2723              // Remove Jabber from the notification plugins list.
2724              $DB->delete_records('message_processors', ['name' => 'jabber']);
2725  
2726              // Remove user preference settings.
2727              $DB->delete_records('user_preferences', ['name' => 'message_processor_jabber_jabberid']);
2728              $sql = 'SELECT *
2729                      FROM {user_preferences} up
2730                      WHERE ' . $DB->sql_like('up.name', ':name', false, false) . ' AND ' .
2731                          $DB->sql_like('up.value', ':value', false, false);
2732              $params = [
2733                  'name' => 'message_provider_%',
2734                  'value' => '%jabber%',
2735              ];
2736              $jabbersettings = $DB->get_recordset_sql($sql, $params);
2737              foreach ($jabbersettings as $jabbersetting) {
2738                  // Remove 'jabber' from the value.
2739                  $jabbersetting->value = implode(',', array_diff(explode(',', $jabbersetting->value), ['jabber']));
2740                  $DB->update_record('user_preferences', $jabbersetting);
2741              }
2742              $jabbersettings->close();
2743  
2744              // Clean config settings.
2745              unset_config('jabberhost');
2746              unset_config('jabberserver');
2747              unset_config('jabberusername');
2748              unset_config('jabberpassword');
2749              unset_config('jabberport');
2750  
2751              // Remove default notification preferences.
2752              $like = $DB->sql_like('name', '?', true, true, false, '|');
2753              $params = [$DB->sql_like_escape('jabber_provider_', '|') . '%'];
2754              $DB->delete_records_select('config_plugins', $like, $params);
2755  
2756              // Clean config config settings.
2757              unset_all_config_for_plugin('message_jabber');
2758          }
2759  
2760          upgrade_main_savepoint(true, 2021091100.01);
2761      }
2762  
2763      if ($oldversion < 2021091100.02) {
2764          // Set the description field to HTML format for the Default course category.
2765          $category = $DB->get_record('course_categories', ['id' => 1]);
2766  
2767          if (!empty($category) && $category->descriptionformat == FORMAT_MOODLE) {
2768              // Format should be changed only if it's still set to FORMAT_MOODLE.
2769              if (!is_null($category->description)) {
2770                  // If description is not empty, format the content to HTML.
2771                  $category->description = format_text($category->description, FORMAT_MOODLE);
2772              }
2773              $category->descriptionformat = FORMAT_HTML;
2774              $DB->update_record('course_categories', $category);
2775          }
2776  
2777          // Main savepoint reached.
2778          upgrade_main_savepoint(true, 2021091100.02);
2779      }
2780  
2781      if ($oldversion < 2021091700.01) {
2782          // Default 'off' for existing sites as this is the behaviour they had earlier.
2783          set_config('enroladminnewcourse', false);
2784  
2785          // Main savepoint reached.
2786          upgrade_main_savepoint(true, 2021091700.01);
2787      }
2788  
2789      if ($oldversion < 2021091700.02) {
2790          // If portfolio_picasa is no longer present, remove it.
2791          if (!file_exists($CFG->dirroot . '/portfolio/picasa/version.php')) {
2792              $instance = $DB->get_record('portfolio_instance', ['plugin' => 'picasa']);
2793              if (!empty($instance)) {
2794                  // Remove all records from portfolio_instance_config.
2795                  $DB->delete_records('portfolio_instance_config', ['instance' => $instance->id]);
2796                  // Remove all records from portfolio_instance_user.
2797                  $DB->delete_records('portfolio_instance_user', ['instance' => $instance->id]);
2798                  // Remove all records from portfolio_log.
2799                  $DB->delete_records('portfolio_log', ['portfolio' => $instance->id]);
2800                  // Remove all records from portfolio_tempdata.
2801                  $DB->delete_records('portfolio_tempdata', ['instance' => $instance->id]);
2802                  // Remove the record from the portfolio_instance table.
2803                  $DB->delete_records('portfolio_instance', ['id' => $instance->id]);
2804              }
2805  
2806              // Clean config.
2807              unset_all_config_for_plugin('portfolio_picasa');
2808          }
2809  
2810          upgrade_main_savepoint(true, 2021091700.02);
2811      }
2812  
2813      if ($oldversion < 2021091700.03) {
2814          // If repository_picasa is no longer present, remove it.
2815          if (!file_exists($CFG->dirroot . '/repository/picasa/version.php')) {
2816              $instance = $DB->get_record('repository', ['type' => 'picasa']);
2817              if (!empty($instance)) {
2818                  // Remove all records from repository_instance_config table.
2819                  $DB->delete_records('repository_instance_config', ['instanceid' => $instance->id]);
2820                  // Remove all records from repository_instances table.
2821                  $DB->delete_records('repository_instances', ['typeid' => $instance->id]);
2822                  // Remove the record from the repository table.
2823                  $DB->delete_records('repository', ['id' => $instance->id]);
2824              }
2825  
2826              // Clean config.
2827              unset_all_config_for_plugin('picasa');
2828  
2829              // Remove orphaned files.
2830              upgrade_delete_orphaned_file_records();
2831          }
2832  
2833          upgrade_main_savepoint(true, 2021091700.03);
2834      }
2835  
2836      if ($oldversion < 2021091700.04) {
2837          // Remove media_swf (unless it has manually been added back).
2838          if (!file_exists($CFG->dirroot . '/media/player/swf/classes/plugin.php')) {
2839              unset_all_config_for_plugin('media_swf');
2840          }
2841  
2842          upgrade_main_savepoint(true, 2021091700.04);
2843      }
2844  
2845      if ($oldversion < 2021092400.01) {
2846          // If tool_health is no longer present, remove it.
2847          if (!file_exists($CFG->dirroot . '/admin/tool/health/version.php')) {
2848              // Clean config.
2849              unset_all_config_for_plugin('tool_health');
2850          }
2851  
2852          // Main savepoint reached.
2853          upgrade_main_savepoint(true, 2021092400.01);
2854      }
2855  
2856      if ($oldversion < 2021092400.03) {
2857          // Remove repository_picasa configuration (unless it has manually been added back).
2858          if (!file_exists($CFG->dirroot . '/repository/picasa/version.php')) {
2859              unset_all_config_for_plugin('repository_picasa');
2860          }
2861  
2862          upgrade_main_savepoint(true, 2021092400.03);
2863      }
2864  
2865      if ($oldversion < 2021100300.01) {
2866          // Remove repository_skydrive (unless it has manually been added back).
2867          if (!file_exists($CFG->dirroot . '/repository/skydrive/lib.php')) {
2868              unset_all_config_for_plugin('repository_skydrive');
2869          }
2870  
2871          // Main savepoint reached.
2872          upgrade_main_savepoint(true, 2021100300.01);
2873      }
2874  
2875      if ($oldversion < 2021100300.02) {
2876          // Remove filter_censor (unless it has manually been added back).
2877          if (!file_exists($CFG->dirroot . '/filter/censor/filter.php')) {
2878              unset_all_config_for_plugin('filter_censor');
2879          }
2880  
2881          // Main savepoint reached.
2882          upgrade_main_savepoint(true, 2021100300.02);
2883      }
2884  
2885      if ($oldversion < 2021100600.01) {
2886          // Remove qformat_examview (unless it has manually been added back).
2887          if (!file_exists($CFG->dirroot . '/question/format/examview/format.php')) {
2888              unset_all_config_for_plugin('qformat_examview');
2889          }
2890  
2891          // Main savepoint reached.
2892          upgrade_main_savepoint(true, 2021100600.01);
2893      }
2894  
2895      if ($oldversion < 2021100600.02) {
2896          $table = new xmldb_table('course_completion_defaults');
2897  
2898          // Adding fields to table course_completion_defaults.
2899          $field = new xmldb_field('completionpassgrade', XMLDB_TYPE_INTEGER, '1', null,
2900              XMLDB_NOTNULL, null, '0', 'completionusegrade');
2901  
2902          // Conditionally launch add field for course_completion_defaults.
2903          if (!$dbman->field_exists($table, $field)) {
2904              $dbman->add_field($table, $field);
2905          }
2906  
2907          upgrade_main_savepoint(true, 2021100600.02);
2908      }
2909  
2910      if ($oldversion < 2021100600.03) {
2911          $table = new xmldb_table('course_modules');
2912  
2913          // Adding new fields to table course_module table.
2914          $field = new xmldb_field('completionpassgrade', XMLDB_TYPE_INTEGER, '1', null,
2915              XMLDB_NOTNULL, null, '0', 'completionexpected');
2916          // Conditionally launch create table for course_completion_defaults.
2917          if (!$dbman->field_exists($table, $field)) {
2918              $dbman->add_field($table, $field);
2919          }
2920  
2921          upgrade_main_savepoint(true, 2021100600.03);
2922      }
2923  
2924      if ($oldversion < 2021100600.04) {
2925          // Define index itemtype-mod-inst-course (not unique) to be added to grade_items.
2926          $table = new xmldb_table('grade_items');
2927          $index = new xmldb_index('itemtype-mod-inst-course', XMLDB_INDEX_NOTUNIQUE,
2928              ['itemtype', 'itemmodule', 'iteminstance', 'courseid']);
2929  
2930          // Conditionally launch add index itemtype-mod-inst-course.
2931          if (!$dbman->index_exists($table, $index)) {
2932              $dbman->add_index($table, $index);
2933          }
2934  
2935          // Main savepoint reached.
2936          upgrade_main_savepoint(true, 2021100600.04);
2937      }
2938  
2939      if ($oldversion < 2021101900.01) {
2940          $table = new xmldb_table('reportbuilder_report');
2941  
2942          // Define field name to be added to reportbuilder_report.
2943          $field = new xmldb_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'id');
2944          if (!$dbman->field_exists($table, $field)) {
2945              $dbman->add_field($table, $field);
2946          }
2947  
2948          // Define field conditiondata to be added to reportbuilder_report.
2949          $field = new xmldb_field('conditiondata', XMLDB_TYPE_TEXT, null, null, null, null, null, 'type');
2950          if (!$dbman->field_exists($table, $field)) {
2951              $dbman->add_field($table, $field);
2952          }
2953  
2954          // Define table reportbuilder_column to be created.
2955          $table = new xmldb_table('reportbuilder_column');
2956  
2957          // Adding fields to table reportbuilder_column.
2958          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2959          $table->add_field('reportid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2960          $table->add_field('uniqueidentifier', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
2961          $table->add_field('aggregation', XMLDB_TYPE_CHAR, '32', null, null, null, null);
2962          $table->add_field('heading', XMLDB_TYPE_CHAR, '255', null, null, null, null);
2963          $table->add_field('columnorder', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
2964          $table->add_field('sortenabled', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0');
2965          $table->add_field('sortdirection', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, null);
2966          $table->add_field('sortorder', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
2967          $table->add_field('usercreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2968          $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2969          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2970          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2971  
2972          // Adding keys to table reportbuilder_column.
2973          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
2974          $table->add_key('reportid', XMLDB_KEY_FOREIGN, ['reportid'], 'reportbuilder_report', ['id']);
2975          $table->add_key('usercreated', XMLDB_KEY_FOREIGN, ['usercreated'], 'user', ['id']);
2976          $table->add_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']);
2977  
2978          // Conditionally launch create table for reportbuilder_column.
2979          if (!$dbman->table_exists($table)) {
2980              $dbman->create_table($table);
2981          }
2982  
2983          // Define table reportbuilder_filter to be created.
2984          $table = new xmldb_table('reportbuilder_filter');
2985  
2986          // Adding fields to table reportbuilder_filter.
2987          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2988          $table->add_field('reportid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2989          $table->add_field('uniqueidentifier', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
2990          $table->add_field('heading', XMLDB_TYPE_CHAR, '255', null, null, null, null);
2991          $table->add_field('iscondition', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0');
2992          $table->add_field('filterorder', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
2993          $table->add_field('usercreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2994          $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2995          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2996          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2997  
2998          // Adding keys to table reportbuilder_filter.
2999          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
3000          $table->add_key('reportid', XMLDB_KEY_FOREIGN, ['reportid'], 'reportbuilder_report', ['id']);
3001          $table->add_key('usercreated', XMLDB_KEY_FOREIGN, ['usercreated'], 'user', ['id']);
3002          $table->add_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']);
3003  
3004          // Conditionally launch create table for reportbuilder_filter.
3005          if (!$dbman->table_exists($table)) {
3006              $dbman->create_table($table);
3007          }
3008  
3009          // Main savepoint reached.
3010          upgrade_main_savepoint(true, 2021101900.01);
3011      }
3012  
3013      if ($oldversion < 2021102600.01) {
3014          // Remove block_quiz_results (unless it has manually been added back).
3015          if (!file_exists($CFG->dirroot . '/blocks/quiz_result/block_quiz_results.php')) {
3016              // Delete instances.
3017              $instances = $DB->get_records_list('block_instances', 'blockname', ['quiz_results']);
3018              $instanceids = array_keys($instances);
3019  
3020              if (!empty($instanceids)) {
3021                  blocks_delete_instances($instanceids);
3022              }
3023  
3024              // Delete the block from the block table.
3025              $DB->delete_records('block', array('name' => 'quiz_results'));
3026  
3027              // Remove capabilities.
3028              capabilities_cleanup('block_quiz_results');
3029              // Clean config.
3030              unset_all_config_for_plugin('block_quiz_results');
3031  
3032              // Remove Moodle-level quiz_results based capabilities.
3033              $capabilitiestoberemoved = ['block/quiz_results:addinstance'];
3034              // Delete any role_capabilities for the old roles.
3035              $DB->delete_records_list('role_capabilities', 'capability', $capabilitiestoberemoved);
3036              // Delete the capability itself.
3037              $DB->delete_records_list('capabilities', 'name', $capabilitiestoberemoved);
3038          }
3039  
3040          upgrade_main_savepoint(true, 2021102600.01);
3041      }
3042  
3043      if ($oldversion < 2021102900.02) {
3044          // If portfolio_boxnet is no longer present, remove it.
3045          if (!file_exists($CFG->dirroot . '/portfolio/boxnet/version.php')) {
3046              $instance = $DB->get_record('portfolio_instance', ['plugin' => 'boxnet']);
3047              if (!empty($instance)) {
3048                  // Remove all records from portfolio_instance_config.
3049                  $DB->delete_records('portfolio_instance_config', ['instance' => $instance->id]);
3050                  // Remove all records from portfolio_instance_user.
3051                  $DB->delete_records('portfolio_instance_user', ['instance' => $instance->id]);
3052                  // Remove all records from portfolio_log.
3053                  $DB->delete_records('portfolio_log', ['portfolio' => $instance->id]);
3054                  // Remove all records from portfolio_tempdata.
3055                  $DB->delete_records('portfolio_tempdata', ['instance' => $instance->id]);
3056                  // Remove the record from the portfolio_instance table.
3057                  $DB->delete_records('portfolio_instance', ['id' => $instance->id]);
3058              }
3059  
3060              // Clean config.
3061              unset_all_config_for_plugin('portfolio_boxnet');
3062          }
3063  
3064          // If repository_boxnet is no longer present, remove it.
3065          if (!file_exists($CFG->dirroot . '/repository/boxnet/version.php')) {
3066              $instance = $DB->get_record('repository', ['type' => 'boxnet']);
3067              if (!empty($instance)) {
3068                  // Remove all records from repository_instance_config table.
3069                  $DB->delete_records('repository_instance_config', ['instanceid' => $instance->id]);
3070                  // Remove all records from repository_instances table.
3071                  $DB->delete_records('repository_instances', ['typeid' => $instance->id]);
3072                  // Remove the record from the repository table.
3073                  $DB->delete_records('repository', ['id' => $instance->id]);
3074              }
3075  
3076              // Clean config.
3077              unset_all_config_for_plugin('repository_boxnet');
3078  
3079              // The boxnet repository plugin stores some config in 'boxnet' incorrectly.
3080              unset_all_config_for_plugin('boxnet');
3081  
3082              // Remove orphaned files.
3083              upgrade_delete_orphaned_file_records();
3084          }
3085  
3086          upgrade_main_savepoint(true, 2021102900.02);
3087      }
3088  
3089      if ($oldversion < 2021110100.00) {
3090  
3091          // Define table reportbuilder_audience to be created.
3092          $table = new xmldb_table('reportbuilder_audience');
3093  
3094          // Adding fields to table reportbuilder_audience.
3095          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3096          $table->add_field('reportid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
3097          $table->add_field('classname', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
3098          $table->add_field('configdata', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
3099          $table->add_field('usercreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3100          $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3101          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3102          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3103  
3104          // Adding keys to table reportbuilder_audience.
3105          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
3106          $table->add_key('reportid', XMLDB_KEY_FOREIGN, ['reportid'], 'reportbuilder_report', ['id']);
3107          $table->add_key('usercreated', XMLDB_KEY_FOREIGN, ['usercreated'], 'user', ['id']);
3108          $table->add_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']);
3109  
3110          // Conditionally launch create table for reportbuilder_audience.
3111          if (!$dbman->table_exists($table)) {
3112              $dbman->create_table($table);
3113          }
3114  
3115          // Main savepoint reached.
3116          upgrade_main_savepoint(true, 2021110100.00);
3117      }
3118  
3119      if ($oldversion < 2021110800.02) {
3120          // Define a field 'downloadcontent' in the 'course_modules' table.
3121          $table = new xmldb_table('course_modules');
3122          $field = new xmldb_field('downloadcontent', XMLDB_TYPE_INTEGER, '1', null, null, null, 1, 'deletioninprogress');
3123  
3124          // Conditionally launch add field 'downloadcontent'.
3125          if (!$dbman->field_exists($table, $field)) {
3126              $dbman->add_field($table, $field);
3127          }
3128  
3129          // Main savepoint reached.
3130          upgrade_main_savepoint(true, 2021110800.02);
3131      }
3132  
3133      if ($oldversion < 2021110800.03) {
3134  
3135          // Define field settingsdata to be added to reportbuilder_report.
3136          $table = new xmldb_table('reportbuilder_report');
3137          $field = new xmldb_field('settingsdata', XMLDB_TYPE_TEXT, null, null, null, null, null, 'conditiondata');
3138  
3139          // Conditionally launch add field settingsdata.
3140          if (!$dbman->field_exists($table, $field)) {
3141              $dbman->add_field($table, $field);
3142          }
3143  
3144          // Main savepoint reached.
3145          upgrade_main_savepoint(true, 2021110800.03);
3146      }
3147  
3148      if ($oldversion < 2021111700.00) {
3149          $mycoursespage = new stdClass();
3150          $mycoursespage->userid = null;
3151          $mycoursespage->name = '__courses';
3152          $mycoursespage->private = 0;
3153          $mycoursespage->sortorder  = 0;
3154          $DB->insert_record('my_pages', $mycoursespage);
3155  
3156          upgrade_main_savepoint(true, 2021111700.00);
3157      }
3158  
3159      if ($oldversion < 2021111700.01) {
3160  
3161          // Define field uniquerows to be added to reportbuilder_report.
3162          $table = new xmldb_table('reportbuilder_report');
3163          $field = new xmldb_field('uniquerows', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'type');
3164  
3165          // Conditionally launch add field uniquerows.
3166          if (!$dbman->field_exists($table, $field)) {
3167              $dbman->add_field($table, $field);
3168          }
3169  
3170          // Main savepoint reached.
3171          upgrade_main_savepoint(true, 2021111700.01);
3172      }
3173  
3174      if ($oldversion < 2021120100.01) {
3175  
3176          // Get current configuration data.
3177          $currentcustomusermenuitems = str_replace(["\r\n", "\r"], "\n", $CFG->customusermenuitems);
3178          $lines = explode("\n", $currentcustomusermenuitems);
3179          $lines = array_map('trim', $lines);
3180          $calendarcustomusermenu = 'calendar,core_calendar|/calendar/view.php?view=month|i/calendar';
3181  
3182          if (!in_array($calendarcustomusermenu, $lines)) {
3183              // Add Calendar item to the menu.
3184              array_splice($lines, 1, 0, [$calendarcustomusermenu]);
3185              set_config('customusermenuitems', implode("\n", $lines));
3186          }
3187  
3188          // Main savepoint reached.
3189          upgrade_main_savepoint(true, 2021120100.01);
3190      }
3191  
3192      if ($oldversion < 2021121400.01) {
3193          // The $CFG->grade_navmethod setting has been removed because it's not required anymore. This setting was used
3194          // to set the type of navigation (tabs or dropdown box) which will be displayed in gradebook. However, these
3195          // navigation methods are no longer used and replaced with tertiary navigation.
3196          unset_config('grade_navmethod');
3197  
3198          // Main savepoint reached.
3199          upgrade_main_savepoint(true, 2021121400.01);
3200      }
3201  
3202      if ($oldversion < 2021121700.01) {
3203          // Get current support email setting value.
3204          $config = get_config('moodle', 'supportemail');
3205  
3206          // Check if support email setting is empty and then set it to null.
3207          // We must do that so the setting is displayed during the upgrade.
3208          if (empty($config)) {
3209              set_config('supportemail', null);
3210          }
3211  
3212          // Main savepoint reached.
3213          upgrade_main_savepoint(true, 2021121700.01);
3214      }
3215  
3216      if ($oldversion < 2021122100.00) {
3217          // Get current configuration data.
3218          $currentcustomusermenuitems = str_replace(["\r\n", "\r"], "\n", $CFG->customusermenuitems);
3219  
3220          // The old default customusermenuitems config for 3.11 and below.
3221          $oldcustomusermenuitems = 'grades,grades|/grade/report/mygrades.php|t/grades
3222  calendar,core_calendar|/calendar/view.php?view=month|i/calendar
3223  messages,message|/message/index.php|t/message
3224  preferences,moodle|/user/preferences.php|t/preferences';
3225  
3226          // Check if the current customusermenuitems config matches the old customusermenuitems config.
3227          $samecustomusermenuitems = $currentcustomusermenuitems == $oldcustomusermenuitems;
3228          if ($samecustomusermenuitems) {
3229              // If the site is still using the old defaults, upgrade to the new default.
3230              $newcustomusermenuitems = 'profile,moodle|/user/profile.php
3231  grades,grades|/grade/report/mygrades.php
3232  calendar,core_calendar|/calendar/view.php?view=month
3233  privatefiles,moodle|/user/files.php';
3234              // Set the new configuration back.
3235              set_config('customusermenuitems', $newcustomusermenuitems);
3236          } else {
3237              // If the site is not using the old defaults, only add necessary entries.
3238              $lines = preg_split('/\n/', $currentcustomusermenuitems, -1, PREG_SPLIT_NO_EMPTY);
3239              $lines = array_map(static function(string $line): string {
3240                  // Previous format was "<langstring>|<url>[|<pixicon>]" - pix icon is no longer supported.
3241                  $lineparts = explode('|', trim($line), 3);
3242                  // Return first two parts of line.
3243                  return implode('|', array_slice($lineparts, 0, 2));
3244              }, $lines);
3245  
3246              // Remove the Preference entry from the menu to prevent duplication
3247              // since it will be added again in user_get_user_navigation_info().
3248              $lines = array_filter($lines, function($value) {
3249                  return strpos($value, 'preferences,moodle|/user/preferences.php') === false;
3250              });
3251  
3252              $matches = preg_grep('/\|\/user\/files.php/i', $lines);
3253              if (!$matches) {
3254                  // Add the Private files entry to the menu.
3255                  $lines[] = 'privatefiles,moodle|/user/files.php';
3256              }
3257  
3258              $matches = preg_grep('/\|\/user\/profile.php/i', $lines);
3259              if (!$matches) {
3260                  // Add the Profile entry to top of the menu.
3261                  array_unshift($lines, 'profile,moodle|/user/profile.php');
3262              }
3263  
3264              // Set the new configuration back.
3265              set_config('customusermenuitems', implode("\n", $lines));
3266          }
3267  
3268          // Main savepoint reached.
3269          upgrade_main_savepoint(true, 2021122100.00);
3270      }
3271  
3272  
3273      if ($oldversion < 2021122100.01) {
3274  
3275          // Define field heading to be added to reportbuilder_audience.
3276          $table = new xmldb_table('reportbuilder_audience');
3277          $field = new xmldb_field('heading', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'reportid');
3278  
3279          // Conditionally launch add field heading.
3280          if (!$dbman->field_exists($table, $field)) {
3281              $dbman->add_field($table, $field);
3282          }
3283  
3284          // Main savepoint reached.
3285          upgrade_main_savepoint(true, 2021122100.01);
3286      }
3287  
3288      if ($oldversion < 2021122100.02) {
3289  
3290          // Define table reportbuilder_schedule to be created.
3291          $table = new xmldb_table('reportbuilder_schedule');
3292  
3293          // Adding fields to table reportbuilder_schedule.
3294          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3295          $table->add_field('reportid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3296          $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
3297          $table->add_field('enabled', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1');
3298          $table->add_field('audiences', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
3299          $table->add_field('format', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
3300          $table->add_field('subject', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
3301          $table->add_field('message', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
3302          $table->add_field('messageformat', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
3303          $table->add_field('userviewas', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3304          $table->add_field('timescheduled', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3305          $table->add_field('recurrence', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3306          $table->add_field('reportempty', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3307          $table->add_field('timelastsent', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3308          $table->add_field('timenextsend', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3309          $table->add_field('usercreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3310          $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3311          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3312          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3313  
3314          // Adding keys to table reportbuilder_schedule.
3315          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
3316          $table->add_key('reportid', XMLDB_KEY_FOREIGN, ['reportid'], 'reportbuilder_report', ['id']);
3317          $table->add_key('userviewas', XMLDB_KEY_FOREIGN, ['userviewas'], 'user', ['id']);
3318          $table->add_key('usercreated', XMLDB_KEY_FOREIGN, ['usercreated'], 'user', ['id']);
3319          $table->add_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']);
3320  
3321          // Conditionally launch create table for reportbuilder_schedule.
3322          if (!$dbman->table_exists($table)) {
3323              $dbman->create_table($table);
3324          }
3325  
3326          // Main savepoint reached.
3327          upgrade_main_savepoint(true, 2021122100.02);
3328      }
3329  
3330      if ($oldversion < 2021123000.01) {
3331          // The tool_admin_presets tables have been moved to core, because core_adminpresets component has been created, so
3332          // it can interact with the rest of core.
3333          // So the tool_admin_presetsXXX tables will be renamed to adminipresetsXXX if they exists; otherwise, they will be created.
3334  
3335          $tooltable = new xmldb_table('tool_admin_presets');
3336          $table = new xmldb_table('adminpresets');
3337          if ($dbman->table_exists($tooltable)) {
3338              $dbman->rename_table($tooltable, 'adminpresets');
3339          } else if (!$dbman->table_exists($table)) {
3340              // Adding fields to table adminpresets.
3341              $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3342              $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
3343              $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
3344              $table->add_field('comments', XMLDB_TYPE_TEXT, null, null, null, null, null);
3345              $table->add_field('site', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
3346              $table->add_field('author', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3347              $table->add_field('moodleversion', XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, null);
3348              $table->add_field('moodlerelease', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
3349              $table->add_field('iscore', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0');
3350              $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3351              $table->add_field('timeimported', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3352  
3353              // Adding keys to table adminpresets.
3354              $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
3355  
3356              // Launch create table for adminpresets.
3357              $dbman->create_table($table);
3358          }
3359  
3360          $tooltable = new xmldb_table('tool_admin_presets_it');
3361          $table = new xmldb_table('adminpresets_it');
3362          if ($dbman->table_exists($tooltable)) {
3363              $dbman->rename_table($tooltable, 'adminpresets_it');
3364          } else if (!$dbman->table_exists($table)) {
3365              // Adding fields to table adminpresets_it.
3366              $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3367              $table->add_field('adminpresetid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
3368              $table->add_field('plugin', XMLDB_TYPE_CHAR, '100', null, null, null, null);
3369              $table->add_field('name', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
3370              $table->add_field('value', XMLDB_TYPE_TEXT, null, null, null, null, null);
3371  
3372              // Adding keys to table adminpresets_it.
3373              $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
3374  
3375              // Adding indexes to table adminpresets_it.
3376              $table->add_index('adminpresetid', XMLDB_INDEX_NOTUNIQUE, ['adminpresetid']);
3377  
3378              // Launch create table for adminpresets_it.
3379              $dbman->create_table($table);
3380          }
3381  
3382          $tooltable = new xmldb_table('tool_admin_presets_it_a');
3383          $table = new xmldb_table('adminpresets_it_a');
3384          if ($dbman->table_exists($tooltable)) {
3385              $dbman->rename_table($tooltable, 'adminpresets_it_a');
3386          } else if (!$dbman->table_exists($table)) {
3387              // Adding fields to table adminpresets_it_a.
3388              $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3389              $table->add_field('itemid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
3390              $table->add_field('name', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
3391              $table->add_field('value', XMLDB_TYPE_TEXT, null, null, null, null, null);
3392  
3393              // Adding keys to table adminpresets_it_a.
3394              $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
3395  
3396              // Adding indexes to table adminpresets_it_a.
3397              $table->add_index('itemid', XMLDB_INDEX_NOTUNIQUE, ['itemid']);
3398  
3399              // Launch create table for adminpresets_it_a.
3400              $dbman->create_table($table);
3401          }
3402  
3403          $tooltable = new xmldb_table('tool_admin_presets_app');
3404          $table = new xmldb_table('adminpresets_app');
3405          if ($dbman->table_exists($tooltable)) {
3406              $dbman->rename_table($tooltable, 'adminpresets_app');
3407          } else if (!$dbman->table_exists($table)) {
3408              // Adding fields to table adminpresets_app.
3409              $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3410              $table->add_field('adminpresetid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
3411              $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
3412              $table->add_field('time', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
3413  
3414              // Adding keys to table adminpresets_app.
3415              $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
3416  
3417              // Adding indexes to table adminpresets_app.
3418              $table->add_index('adminpresetid', XMLDB_INDEX_NOTUNIQUE, ['adminpresetid']);
3419  
3420              // Launch create table for adminpresets_app.
3421              $dbman->create_table($table);
3422          }
3423  
3424          $tooltable = new xmldb_table('tool_admin_presets_app_it');
3425          $table = new xmldb_table('adminpresets_app_it');
3426          if ($dbman->table_exists($tooltable)) {
3427              $dbman->rename_table($tooltable, 'adminpresets_app_it');
3428          } else if (!$dbman->table_exists($table)) {
3429              // Adding fields to table adminpresets_app_it.
3430              $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3431              $table->add_field('adminpresetapplyid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
3432              $table->add_field('configlogid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
3433  
3434              // Adding keys to table adminpresets_app_it.
3435              $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
3436  
3437              // Adding indexes to table adminpresets_app_it.
3438              $table->add_index('configlogid', XMLDB_INDEX_NOTUNIQUE, ['configlogid']);
3439              $table->add_index('adminpresetapplyid', XMLDB_INDEX_NOTUNIQUE, ['adminpresetapplyid']);
3440  
3441              // Launch create table for adminpresets_app_it.
3442              $dbman->create_table($table);
3443          }
3444  
3445          $tooltable = new xmldb_table('tool_admin_presets_app_it_a');
3446          $table = new xmldb_table('adminpresets_app_it_a');
3447          if ($dbman->table_exists($tooltable)) {
3448              $dbman->rename_table($tooltable, 'adminpresets_app_it_a');
3449          } else if (!$dbman->table_exists($table)) {
3450              // Adding fields to table adminpresets_app_it_a.
3451              $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3452              $table->add_field('adminpresetapplyid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
3453              $table->add_field('configlogid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
3454              $table->add_field('itemname', XMLDB_TYPE_CHAR, '100', null, null, null, null);
3455  
3456              // Adding keys to table adminpresets_app_it_a.
3457              $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
3458  
3459              // Adding indexes to table adminpresets_app_it_a.
3460              $table->add_index('configlogid', XMLDB_INDEX_NOTUNIQUE, ['configlogid']);
3461              $table->add_index('adminpresetapplyid', XMLDB_INDEX_NOTUNIQUE, ['adminpresetapplyid']);
3462  
3463              // Launch create table for adminpresets_app_it_a.
3464              $dbman->create_table($table);
3465          }
3466  
3467          $tooltable = new xmldb_table('tool_admin_presets_plug');
3468          $table = new xmldb_table('adminpresets_plug');
3469          if ($dbman->table_exists($tooltable)) {
3470              $dbman->rename_table($tooltable, 'adminpresets_plug');
3471          } else if (!$dbman->table_exists($table)) {
3472              // Adding fields to table adminpresets_plug.
3473              $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3474              $table->add_field('adminpresetid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
3475              $table->add_field('plugin', XMLDB_TYPE_CHAR, '100', null, null, null, null);
3476              $table->add_field('name', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
3477              $table->add_field('enabled', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0');
3478  
3479              // Adding keys to table adminpresets_plug.
3480              $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
3481  
3482              // Adding indexes to table adminpresets_plug.
3483              $table->add_index('adminpresetid', XMLDB_INDEX_NOTUNIQUE, ['adminpresetid']);
3484  
3485              // Launch create table for adminpresets_plug.
3486              $dbman->create_table($table);
3487          }
3488  
3489          $tooltable = new xmldb_table('tool_admin_presets_app_plug');
3490          $table = new xmldb_table('adminpresets_app_plug');
3491          if ($dbman->table_exists($tooltable)) {
3492              $dbman->rename_table($tooltable, 'adminpresets_app_plug');
3493          } else if (!$dbman->table_exists($table)) {
3494              // Adding fields to table adminpresets_app_plug.
3495              $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3496              $table->add_field('adminpresetapplyid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
3497              $table->add_field('plugin', XMLDB_TYPE_CHAR, '100', null, null, null, null);
3498              $table->add_field('name', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
3499              $table->add_field('value', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0');
3500              $table->add_field('oldvalue', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0');
3501  
3502              // Adding keys to table adminpresets_app_plug.
3503              $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
3504  
3505              // Adding indexes to table adminpresets_app_plug.
3506              $table->add_index('adminpresetapplyid', XMLDB_INDEX_NOTUNIQUE, ['adminpresetapplyid']);
3507  
3508              // Launch create table for adminpresets_app_plug.
3509              if (!$dbman->table_exists($table)) {
3510                  $dbman->create_table($table);
3511              }
3512          }
3513  
3514          if ($DB->count_records('adminpresets', ['iscore' => 1]) == 0) {
3515              // Create default core site admin presets.
3516              require_once($CFG->dirroot . '/admin/presets/classes/helper.php');
3517              \core_adminpresets\helper::create_default_presets();
3518          }
3519  
3520          // Main savepoint reached.
3521          upgrade_main_savepoint(true, 2021123000.01);
3522      }
3523  
3524      if ($oldversion < 2021123000.02) {
3525          // If exists, migrate sensiblesettings admin settings from tool_admin_preset to adminpresets.
3526          if (get_config('tool_admin_presets', 'sensiblesettings') !== false) {
3527              set_config('sensiblesettings', get_config('tool_admin_presets', 'sensiblesettings'), 'adminpresets');
3528              unset_config('sensiblesettings', 'tool_admin_presets');
3529          }
3530  
3531          // Main savepoint reached.
3532          upgrade_main_savepoint(true, 2021123000.02);
3533      }
3534  
3535      if ($oldversion < 2021123000.03) {
3536          // If exists, migrate lastpresetapplied setting from tool_admin_preset to adminpresets.
3537          if (get_config('tool_admin_presets', 'lastpresetapplied') !== false) {
3538              set_config('lastpresetapplied', get_config('tool_admin_presets', 'lastpresetapplied'), 'adminpresets');
3539              unset_config('lastpresetapplied', 'tool_admin_presets');
3540          }
3541  
3542          // Main savepoint reached.
3543          upgrade_main_savepoint(true, 2021123000.03);
3544      }
3545  
3546      if ($oldversion < 2022011100.01) {
3547          // The following blocks have been hidden by default, so they shouldn't be enabled in the Full core preset: Course/site
3548          // summary, RSS feeds, Self completion and Feedback.
3549          $params = ['name' => get_string('fullpreset', 'core_adminpresets')];
3550          $fullpreset = $DB->get_record_select('adminpresets', 'name = :name and iscore > 0', $params);
3551  
3552          if (!$fullpreset) {
3553              // Full admin preset might have been created using the English name.
3554              $name = get_string_manager()->get_string('fullpreset', 'core_adminpresets', null, 'en');
3555              $params['name'] = $name;
3556              $fullpreset = $DB->get_record_select('adminpresets', 'name = :name and iscore > 0', $params);
3557          }
3558          if (!$fullpreset) {
3559              // We tried, but we didn't find full by name. Let's find a core preset that sets 'usecomments' setting to 1.
3560              $sql = "SELECT preset.*
3561                        FROM {adminpresets} preset
3562                  INNER JOIN {adminpresets_it} it ON preset.id = it.adminpresetid
3563                       WHERE it.name = :name AND it.value = :value AND preset.iscore > 0";
3564              $params = ['name' => 'usecomments', 'value' => '1'];
3565              $fullpreset = $DB->get_record_sql($sql, $params);
3566          }
3567  
3568          if ($fullpreset) {
3569              $blocknames = ['course_summary', 'feedback', 'rss_client', 'selfcompletion'];
3570              list($blocksinsql, $blocksinparams) = $DB->get_in_or_equal($blocknames);
3571  
3572              // Remove entries from the adminpresets_app_plug table (in case the preset has been applied).
3573              $appliedpresets = $DB->get_records('adminpresets_app', ['adminpresetid' => $fullpreset->id], '', 'id');
3574              if ($appliedpresets) {
3575                  list($appsinsql, $appsinparams) = $DB->get_in_or_equal(array_keys($appliedpresets));
3576                  $sql = "adminpresetapplyid $appsinsql AND plugin='block' AND name $blocksinsql";
3577                  $params = array_merge($appsinparams, $blocksinparams);
3578                  $DB->delete_records_select('adminpresets_app_plug', $sql, $params);
3579              }
3580  
3581              // Remove entries for these blocks from the adminpresets_plug table.
3582              $sql = "adminpresetid = ? AND plugin='block' AND name $blocksinsql";
3583              $params = array_merge([$fullpreset->id], $blocksinparams);
3584              $DB->delete_records_select('adminpresets_plug', $sql, $params);
3585          }
3586  
3587          // Main savepoint reached.
3588          upgrade_main_savepoint(true, 2022011100.01);
3589      }
3590  
3591      if ($oldversion < 2022012100.02) {
3592          // Migrate default message output config.
3593          $preferences = get_config('message');
3594  
3595          $treatedprefs = [];
3596  
3597          foreach ($preferences as $preference => $value) {
3598              // Extract provider and preference name from the setting name.
3599              // Example name: airnotifier_provider_enrol_imsenterprise_imsenterprise_enrolment_permitted
3600              // Provider: airnotifier
3601              // Preference: enrol_imsenterprise_imsenterprise_enrolment_permitted.
3602              $providerparts = explode('_provider_', $preference);
3603              if (count($providerparts) <= 1) {
3604                  continue;
3605              }
3606  
3607              $provider = $providerparts[0];
3608              $preference = $providerparts[1];
3609  
3610              // Extract and remove last part of the preference previously extracted: ie. permitted.
3611              $parts = explode('_', $preference);
3612              $key = array_pop($parts);
3613  
3614              if (in_array($key, ['permitted', 'loggedin', 'loggedoff'])) {
3615                  if ($key == 'permitted') {
3616                      // We will use provider name instead of permitted.
3617                      $key = $provider;
3618                  } else {
3619                      // Logged in and logged off values are a csv of the enabled providers.
3620                      $value = explode(',', $value);
3621                  }
3622  
3623                  // Join the rest of the parts: ie enrol_imsenterprise_imsenterprise_enrolment.
3624                  $prefname = implode('_', $parts);
3625  
3626                  if (!isset($treatedprefs[$prefname])) {
3627                      $treatedprefs[$prefname] = [];
3628                  }
3629  
3630                  // Save the value with the selected key.
3631                  $treatedprefs[$prefname][$key] = $value;
3632              }
3633          }
3634  
3635          // Now take every preference previous treated and its values.
3636          foreach ($treatedprefs as $prefname => $values) {
3637              $enabled = []; // List of providers enabled for each preference.
3638  
3639              // Enable if one of those is enabled.
3640              $loggedin = isset($values['loggedin']) ? $values['loggedin'] : [];
3641              foreach ($loggedin as $provider) {
3642                  $enabled[$provider] = 1;
3643              }
3644              $loggedoff = isset($values['loggedoff']) ? $values['loggedoff'] : [];
3645              foreach ($loggedoff as $provider) {
3646                  $enabled[$provider] = 1;
3647              }
3648  
3649              // Do not treat those values again.
3650              unset($values['loggedin']);
3651              unset($values['loggedoff']);
3652  
3653              // Translate rest of values coming from permitted "key".
3654              foreach ($values as $provider => $value) {
3655                  $locked = false;
3656  
3657                  switch ($value) {
3658                      case 'forced':
3659                          // Provider is enabled by force.
3660                          $enabled[$provider] = 1;
3661                          $locked = true;
3662                          break;
3663                      case 'disallowed':
3664                          // Provider is disabled by force.
3665                          unset($enabled[$provider]);
3666                          $locked = true;
3667                          break;
3668                      default:
3669                          // Provider is not forced (permitted) or invalid values.
3670                  }
3671  
3672                  // Save locked.
3673                  if ($locked) {
3674                      set_config($provider.'_provider_'.$prefname.'_locked', 1, 'message');
3675                  } else {
3676                      set_config($provider.'_provider_'.$prefname.'_locked', 0, 'message');
3677                  }
3678                  // Remove old value.
3679                  unset_config($provider.'_provider_'.$prefname.'_permitted', 'message');
3680              }
3681  
3682              // Save the new values.
3683              $value = implode(',', array_keys($enabled));
3684              set_config('message_provider_'.$prefname.'_enabled', $value, 'message');
3685              // Remove old values.
3686              unset_config('message_provider_'.$prefname.'_loggedin', 'message');
3687              unset_config('message_provider_'.$prefname.'_loggedoff', 'message');
3688          }
3689  
3690          // Migrate user preferences. ie merging message_provider_moodle_instantmessage_loggedoff with
3691          // message_provider_moodle_instantmessage_loggedin to message_provider_moodle_instantmessage_enabled.
3692  
3693          $allrecordsloggedoff = $DB->sql_like('name', ':loggedoff');
3694          $total = $DB->count_records_select(
3695              'user_preferences',
3696              $allrecordsloggedoff,
3697              ['loggedoff' => 'message_provider_%_loggedoff']
3698          );
3699          $i = 0;
3700          if ($total == 0) {
3701              $total = 1; // Avoid division by zero.
3702          }
3703  
3704          // Show a progress bar.
3705          $pbar = new progress_bar('upgradeusernotificationpreferences', 500, true);
3706          $pbar->update($i, $total, "Upgrading user notifications preferences - $i/$total.");
3707  
3708          // We're migrating provider per provider to reduce memory usage.
3709          $providers = $DB->get_records('message_providers', null, 'name');
3710          foreach ($providers as $provider) {
3711              // 60 minutes to migrate each provider.
3712              upgrade_set_timeout(3600);
3713              $componentproviderbase = 'message_provider_'.$provider->component.'_'.$provider->name;
3714  
3715              $loggedinname = $componentproviderbase.'_loggedin';
3716              $loggedoffname = $componentproviderbase.'_loggedoff';
3717  
3718              // Change loggedin to enabled.
3719              $enabledname = $componentproviderbase.'_enabled';
3720              $DB->set_field('user_preferences', 'name', $enabledname, ['name' => $loggedinname]);
3721  
3722              $selectparams = [
3723                  'enabled' => $enabledname,
3724                  'loggedoff' => $loggedoffname,
3725              ];
3726              $sql = 'SELECT m1.id loggedoffid, m1.value as loggedoff, m2.value as enabled, m2.id as enabledid
3727                  FROM
3728                      (SELECT id, userid, value FROM {user_preferences} WHERE name = :loggedoff) m1
3729                  LEFT JOIN
3730                      (SELECT id, userid, value FROM {user_preferences} WHERE name = :enabled) m2
3731                      ON m1.userid = m2.userid';
3732  
3733              while (($rs = $DB->get_recordset_sql($sql, $selectparams, 0, 1000)) && $rs->valid()) {
3734                  // 10 minutes for every chunk.
3735                  upgrade_set_timeout(600);
3736  
3737                  $deleterecords = [];
3738                  $changename = [];
3739                  $changevalue = []; // Multidimensional array with possible values as key to reduce SQL queries.
3740                  foreach ($rs as $record) {
3741                      if (empty($record->enabledid)) {
3742                          // Enabled does not exists, change the name.
3743                          $changename[] = $record->loggedoffid;
3744                      } else if ($record->enabledid != $record->loggedoff) {
3745                          // Exist and values differ (checked on SQL), update the enabled record.
3746  
3747                          if ($record->enabled != 'none' && !empty($record->enabled)) {
3748                              $enabledvalues = explode(',', $record->enabled);
3749                          } else {
3750                              $enabledvalues = [];
3751                          }
3752  
3753                          if ($record->loggedoff != 'none' && !empty($record->loggedoff)) {
3754                              $loggedoffvalues = explode(',', $record->loggedoff);
3755                          } else {
3756                              $loggedoffvalues = [];
3757                          }
3758  
3759                          $values = array_unique(array_merge($enabledvalues, $loggedoffvalues));
3760                          sort($values);
3761  
3762                          $newvalue = empty($values) ? 'none' : implode(',', $values);
3763                          if (!isset($changevalue[$newvalue])) {
3764                              $changevalue[$newvalue] = [];
3765                          }
3766                          $changevalue[$newvalue][] = $record->enabledid;
3767  
3768                          $deleterecords[] = $record->loggedoffid;
3769                      } else {
3770                          // They are the same, just delete loggedoff one.
3771                          $deleterecords[] = $record->loggedoffid;
3772                      }
3773                      $i++;
3774                  }
3775                  $rs->close();
3776  
3777                  // Commit the changes.
3778                  if (!empty($changename)) {
3779                      $changenameparams = [
3780                          'name' => $loggedoffname,
3781                      ];
3782                      $changenameselect = 'name = :name AND id IN (' . implode(',', $changename) . ')';
3783                      $DB->set_field_select('user_preferences', 'name', $enabledname, $changenameselect, $changenameparams);
3784                  }
3785  
3786                  if (!empty($changevalue)) {
3787                      $changevalueparams = [
3788                          'name' => $enabledname,
3789                      ];
3790                      foreach ($changevalue as $value => $ids) {
3791                          $changevalueselect = 'name = :name AND id IN (' . implode(',', $ids) . ')';
3792                          $DB->set_field_select('user_preferences', 'value', $value, $changevalueselect, $changevalueparams);
3793                      }
3794                  }
3795  
3796                  if (!empty($deleterecords)) {
3797                      $deleteparams = [
3798                          'name' => $loggedoffname,
3799                      ];
3800                      $deleteselect = 'name = :name AND id IN (' . implode(',', $deleterecords) . ')';
3801                      $DB->delete_records_select('user_preferences', $deleteselect, $deleteparams);
3802                  }
3803  
3804                  // Update progress.
3805                  $pbar->update($i, $total, "Upgrading user notifications preferences - $i/$total.");
3806              }
3807              $rs->close();
3808  
3809              // Delete the rest of loggedoff values (that are equal than enabled).
3810              $deleteparams = [
3811                  'name' => $loggedoffname,
3812              ];
3813              $deleteselect = 'name = :name';
3814              $i += $DB->count_records_select('user_preferences', $deleteselect, $deleteparams);
3815              $DB->delete_records_select('user_preferences', $deleteselect, $deleteparams);
3816  
3817              // Update progress.
3818              $pbar->update($i, $total, "Upgrading user notifications preferences - $i/$total.");
3819          }
3820  
3821          core_plugin_manager::reset_caches();
3822  
3823          // Delete the orphan records.
3824          $allrecordsparams = ['loggedin' => 'message_provider_%_loggedin', 'loggedoff' => 'message_provider_%_loggedoff'];
3825          $allrecordsloggedin = $DB->sql_like('name', ':loggedin');
3826          $allrecordsloggedinoffsql = "$allrecordsloggedin OR $allrecordsloggedoff";
3827          $DB->delete_records_select('user_preferences', $allrecordsloggedinoffsql, $allrecordsparams);
3828  
3829          // Update progress.
3830          $pbar->update($total, $total, "Upgrading user notifications preferences - $total/$total.");
3831  
3832          upgrade_main_savepoint(true, 2022012100.02);
3833      }
3834  
3835      // Introduce question versioning to core.
3836      // First, create the new tables.
3837      if ($oldversion < 2022020200.01) {
3838          // Define table question_bank_entries to be created.
3839          $table = new xmldb_table('question_bank_entries');
3840  
3841          // Adding fields to table question_bank_entries.
3842          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3843          $table->add_field('questioncategoryid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 0);
3844          $table->add_field('idnumber', XMLDB_TYPE_CHAR, '100', null, null, null, null);
3845          $table->add_field('ownerid', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
3846  
3847          // Adding keys to table question_bank_entries.
3848          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
3849          $table->add_key('questioncategoryid', XMLDB_KEY_FOREIGN, ['questioncategoryid'], 'question_categories', ['id']);
3850          $table->add_key('ownerid', XMLDB_KEY_FOREIGN, ['ownerid'], 'user', ['id']);
3851  
3852          // Conditionally launch create table for question_bank_entries.
3853          if (!$dbman->table_exists($table)) {
3854              $dbman->create_table($table);
3855          }
3856  
3857          // Create category id and id number index.
3858          $index = new xmldb_index('categoryidnumber', XMLDB_INDEX_UNIQUE, ['questioncategoryid', 'idnumber']);
3859  
3860          // Conditionally launch add index categoryidnumber.
3861          if (!$dbman->index_exists($table, $index)) {
3862              $dbman->add_index($table, $index);
3863          }
3864  
3865          // Define table question_versions to be created.
3866          $table = new xmldb_table('question_versions');
3867  
3868          // Adding fields to table question_versions.
3869          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3870          $table->add_field('questionbankentryid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 0);
3871          $table->add_field('version', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 1);
3872          $table->add_field('questionid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 0);
3873          $table->add_field('status', XMLDB_TYPE_CHAR, '10', null, XMLDB_NOTNULL, null, 'ready');
3874  
3875          // Adding keys to table question_versions.
3876          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
3877          $table->add_key('questionbankentryid', XMLDB_KEY_FOREIGN, ['questionbankentryid'], 'question_bank_entries', ['id']);
3878          $table->add_key('questionid', XMLDB_KEY_FOREIGN, ['questionid'], 'question', ['id']);
3879  
3880          // Conditionally launch create table for question_versions.
3881          if (!$dbman->table_exists($table)) {
3882              $dbman->create_table($table);
3883          }
3884  
3885          // Define table question_references to be created.
3886          $table = new xmldb_table('question_references');
3887  
3888          // Adding fields to table question_references.
3889          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3890          $table->add_field('usingcontextid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 0);
3891          $table->add_field('component', XMLDB_TYPE_CHAR, '100', null, null, null, null);
3892          $table->add_field('questionarea', XMLDB_TYPE_CHAR, '50', null, null, null, null);
3893          $table->add_field('itemid', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
3894          $table->add_field('questionbankentryid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 0);
3895          $table->add_field('version', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
3896  
3897          // Adding keys to table question_references.
3898          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
3899          $table->add_key('usingcontextid', XMLDB_KEY_FOREIGN, ['usingcontextid'], 'context', ['id']);
3900          $table->add_key('questionbankentryid', XMLDB_KEY_FOREIGN, ['questionbankentryid'], 'question_bank_entries', ['id']);
3901  
3902          // Conditionally launch create table for question_references.
3903          if (!$dbman->table_exists($table)) {
3904              $dbman->create_table($table);
3905          }
3906  
3907          // Define table question_set_references to be created.
3908          $table = new xmldb_table('question_set_references');
3909  
3910          // Adding fields to table question_set_references.
3911          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3912          $table->add_field('usingcontextid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 0);
3913          $table->add_field('component', XMLDB_TYPE_CHAR, '100', null, null, null, null);
3914          $table->add_field('questionarea', XMLDB_TYPE_CHAR, '50', null, null, null, null);
3915          $table->add_field('itemid', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
3916          $table->add_field('questionscontextid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 0);
3917          $table->add_field('filtercondition', XMLDB_TYPE_TEXT, null, null, null, null, null);
3918  
3919          // Adding keys to table question_set_references.
3920          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
3921          $table->add_key('usingcontextid', XMLDB_KEY_FOREIGN, ['usingcontextid'], 'context', ['id']);
3922          $table->add_key('questionscontextid', XMLDB_KEY_FOREIGN, ['questionscontextid'], 'context', ['id']);
3923  
3924          // Conditionally launch create table for question_set_references.
3925          if (!$dbman->table_exists($table)) {
3926              $dbman->create_table($table);
3927          }
3928  
3929          // Main savepoint reached.
3930          upgrade_main_savepoint(true, 2022020200.01);
3931      }
3932  
3933      if ($oldversion < 2022020200.02) {
3934          // Define a new temporary field in the question_bank_entries tables.
3935          // Creating temporary field questionid to populate the data in question version table.
3936          // This will make sure the appropriate question id is inserted the version table without making any complex joins.
3937          $table = new xmldb_table('question_bank_entries');
3938          $field = new xmldb_field('questionid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL);
3939          if (!$dbman->field_exists($table, $field)) {
3940              $dbman->add_field($table, $field);
3941          }
3942  
3943          $transaction = $DB->start_delegated_transaction();
3944          upgrade_set_timeout(3600);
3945          // Create the data for the question_bank_entries table with, including the new temporary field.
3946          $sql = <<<EOF
3947              INSERT INTO {question_bank_entries}
3948                  (questionid, questioncategoryid, idnumber, ownerid)
3949              SELECT id, category, idnumber, createdby
3950              FROM {question} q
3951              EOF;
3952  
3953          // Inserting question_bank_entries data.
3954          $DB->execute($sql);
3955  
3956          $transaction->allow_commit();
3957  
3958          // Main savepoint reached.
3959          upgrade_main_savepoint(true, 2022020200.02);
3960      }
3961  
3962      if ($oldversion < 2022020200.03) {
3963          $transaction = $DB->start_delegated_transaction();
3964          upgrade_set_timeout(3600);
3965          // Create the question_versions using that temporary field.
3966          $sql = <<<EOF
3967              INSERT INTO {question_versions}
3968                  (questionbankentryid, questionid, status)
3969              SELECT
3970                  qbe.id,
3971                  q.id,
3972                  CASE
3973                      WHEN q.hidden > 0 THEN 'hidden'
3974                      ELSE 'ready'
3975                  END
3976              FROM {question_bank_entries} qbe
3977              INNER JOIN {question} q ON qbe.questionid = q.id
3978              EOF;
3979  
3980          // Inserting question_versions data.
3981          $DB->execute($sql);
3982  
3983          $transaction->allow_commit();
3984  
3985          // Dropping temporary field questionid.
3986          $table = new xmldb_table('question_bank_entries');
3987          $field = new xmldb_field('questionid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL);
3988          if ($dbman->field_exists($table, $field)) {
3989              $dbman->drop_field($table, $field);
3990          }
3991  
3992          // Main savepoint reached.
3993          upgrade_main_savepoint(true, 2022020200.03);
3994      }
3995  
3996      if ($oldversion < 2022020200.04) {
3997          $transaction = $DB->start_delegated_transaction();
3998          upgrade_set_timeout(3600);
3999          // Create the base data for the random questions in the set_references table.
4000          // This covers most of the hard work in one go.
4001          $concat = $DB->sql_concat("'{\"questioncategoryid\":\"'", 'q.category', "'\",\"includingsubcategories\":\"'",
4002              'qs.includingsubcategories', "'\"}'");
4003          $sql = <<<EOF
4004              INSERT INTO {question_set_references}
4005              (usingcontextid, component, questionarea, itemid, questionscontextid, filtercondition)
4006              SELECT
4007                  c.id,
4008                  'mod_quiz',
4009                  'slot',
4010                  qs.id,
4011                  qc.contextid,
4012                  $concat
4013              FROM {question} q
4014              INNER JOIN {quiz_slots} qs on q.id = qs.questionid
4015              INNER JOIN {course_modules} cm ON cm.instance = qs.quizid AND cm.module = :quizmoduleid
4016              INNER JOIN {context} c ON cm.id = c.instanceid AND c.contextlevel = :contextmodule
4017              INNER JOIN {question_categories} qc ON qc.id = q.category
4018              WHERE q.qtype = :random
4019              EOF;
4020  
4021          // Inserting question_set_references data.
4022          $DB->execute($sql, [
4023              'quizmoduleid' => $DB->get_field('modules', 'id', ['name' => 'quiz']),
4024              'contextmodule' => CONTEXT_MODULE,
4025              'random' => 'random',
4026          ]);
4027  
4028          $transaction->allow_commit();
4029  
4030          // Main savepoint reached.
4031          upgrade_main_savepoint(true, 2022020200.04);
4032      }
4033  
4034      if ($oldversion < 2022020200.05) {
4035          $transaction = $DB->start_delegated_transaction();
4036          upgrade_set_timeout(3600);
4037  
4038          // Count all the slot tags to be migrated (for progress bar).
4039          $total = $DB->count_records('quiz_slot_tags');
4040          $pbar = new progress_bar('migratequestiontags', 1000, true);
4041          $i = 0;
4042          // Updating slot_tags for random question tags.
4043          // Now fetch any quiz slot tags and update those slot details into the question_set_references.
4044          $slottags = $DB->get_recordset('quiz_slot_tags', [], 'slotid ASC');
4045  
4046          $tagstrings = [];
4047          $lastslot = null;
4048          $runinsert = function (int $lastslot, array $tagstrings) use ($DB) {
4049              $conditiondata = $DB->get_field('question_set_references', 'filtercondition',
4050                  ['itemid' => $lastslot, 'component' => 'mod_quiz', 'questionarea' => 'slot']);
4051  
4052              // It is possible to have leftover tags in the database, without a corresponding
4053              // slot, because of an old bugs (e.g. MDL-76193). Therefore, if the slot is not found,
4054              // we can safely discard these tags.
4055              if (!empty($conditiondata)) {
4056                  $condition = json_decode($conditiondata);
4057                  $condition->tags = $tagstrings;
4058                  $DB->set_field('question_set_references', 'filtercondition', json_encode($condition),
4059                          ['itemid' => $lastslot, 'component' => 'mod_quiz', 'questionarea' => 'slot']);
4060              }
4061          };
4062  
4063          foreach ($slottags as $tag) {
4064              upgrade_set_timeout(3600);
4065              if ($lastslot && $tag->slotid != $lastslot) {
4066                  if (!empty($tagstrings)) {
4067                      // Insert the data.
4068                      $runinsert($lastslot, $tagstrings);
4069                  }
4070                  // Prepare for the next slot id.
4071                  $tagstrings = [];
4072              }
4073  
4074              $lastslot = $tag->slotid;
4075              $tagstrings[] = "{$tag->tagid},{$tag->tagname}";
4076              // Update progress.
4077              $i++;
4078              $pbar->update($i, $total, "Migrating question tags - $i/$total.");
4079          }
4080          if ($tagstrings) {
4081              $runinsert($lastslot, $tagstrings);
4082          }
4083          $slottags->close();
4084  
4085          $transaction->allow_commit();
4086          // Main savepoint reached.
4087          upgrade_main_savepoint(true, 2022020200.05);
4088      }
4089  
4090      if ($oldversion < 2022020200.06) {
4091          $transaction = $DB->start_delegated_transaction();
4092          upgrade_set_timeout(3600);
4093          // Create question_references record for each question.
4094          // Except if qtype is random. That case is handled by question_set_reference.
4095          $sql = "INSERT INTO {question_references}
4096                          (usingcontextid, component, questionarea, itemid, questionbankentryid)
4097                   SELECT c.id, 'mod_quiz', 'slot', qs.id, qv.questionbankentryid
4098                     FROM {question} q
4099                     JOIN {question_versions} qv ON q.id = qv.questionid
4100                     JOIN {quiz_slots} qs ON q.id = qs.questionid
4101                     JOIN {modules} m ON m.name = 'quiz'
4102                     JOIN {course_modules} cm ON cm.module = m.id AND cm.instance = qs.quizid
4103                     JOIN {context} c ON c.instanceid = cm.id AND c.contextlevel = " . CONTEXT_MODULE . "
4104                    WHERE q.qtype <> 'random'";
4105  
4106          // Inserting question_references data.
4107          $DB->execute($sql);
4108  
4109          $transaction->allow_commit();
4110          // Main savepoint reached.
4111          upgrade_main_savepoint(true, 2022020200.06);
4112      }
4113  
4114      // Finally, drop fields from question table.
4115      if ($oldversion < 2022020200.07) {
4116          // Define fields to be dropped from questions.
4117          $table = new xmldb_table('question');
4118  
4119          $field = new xmldb_field('version');
4120          // Conditionally launch drop field version.
4121          if ($dbman->field_exists($table, $field)) {
4122              $dbman->drop_field($table, $field);
4123          }
4124  
4125          $field = new xmldb_field('hidden');
4126          // Conditionally launch drop field hidden.
4127          if ($dbman->field_exists($table, $field)) {
4128              $dbman->drop_field($table, $field);
4129          }
4130  
4131          // Define index categoryidnumber (not unique) to be dropped form question.
4132          $index = new xmldb_index('categoryidnumber', XMLDB_INDEX_UNIQUE, ['category', 'idnumber']);
4133  
4134          // Conditionally launch drop index categoryidnumber.
4135          if ($dbman->index_exists($table, $index)) {
4136              $dbman->drop_index($table, $index);
4137          }
4138  
4139          // Define key category (foreign) to be dropped form questions.
4140          $key = new xmldb_key('category', XMLDB_KEY_FOREIGN, ['category'], 'question_categories', ['id']);
4141  
4142          // Launch drop key category.
4143          $dbman->drop_key($table, $key);
4144  
4145          $field = new xmldb_field('idnumber');
4146          // Conditionally launch drop field idnumber.
4147          if ($dbman->field_exists($table, $field)) {
4148              $dbman->drop_field($table, $field);
4149          }
4150  
4151          $field = new xmldb_field('category');
4152          // Conditionally launch drop field category.
4153          if ($dbman->field_exists($table, $field)) {
4154              $dbman->drop_field($table, $field);
4155          }
4156  
4157          // Main savepoint reached.
4158          upgrade_main_savepoint(true, 2022020200.07);
4159      }
4160  
4161      if ($oldversion < 2022021100.01) {
4162          $sql = "SELECT preset.*
4163                    FROM {adminpresets} preset
4164              INNER JOIN {adminpresets_it} it ON preset.id = it.adminpresetid
4165                   WHERE it.name = :name AND it.value = :value AND preset.iscore > 0";
4166          // Some settings and plugins have been added/removed to the Starter and Full preset. Add them to the core presets if
4167          // they haven't been included yet.
4168          $params = ['name' => get_string('starterpreset', 'core_adminpresets'), 'iscore' => 1];
4169          $starterpreset = $DB->get_record('adminpresets', $params);
4170          if (!$starterpreset) {
4171              // Starter admin preset might have been created using the English name.
4172              $name = get_string_manager()->get_string('starterpreset', 'core_adminpresets', null, 'en');
4173              $params['name'] = $name;
4174              $starterpreset = $DB->get_record('adminpresets', $params);
4175          }
4176          if (!$starterpreset) {
4177              // We tried, but we didn't find starter by name. Let's find a core preset that sets 'usecomments' setting to 0.
4178              $params = ['name' => 'usecomments', 'value' => '0'];
4179              $starterpreset = $DB->get_record_sql($sql, $params);
4180          }
4181  
4182          $params = ['name' => get_string('fullpreset', 'core_adminpresets')];
4183          $fullpreset = $DB->get_record_select('adminpresets', 'name = :name and iscore > 0', $params);
4184          if (!$fullpreset) {
4185              // Full admin preset might have been created using the English name.
4186              $name = get_string_manager()->get_string('fullpreset', 'core_adminpresets', null, 'en');
4187              $params['name'] = $name;
4188              $fullpreset = $DB->get_record_select('adminpresets', 'name = :name and iscore > 0', $params);
4189          }
4190          if (!$fullpreset) {
4191              // We tried, but we didn't find full by name. Let's find a core preset that sets 'usecomments' setting to 1.
4192              $params = ['name' => 'usecomments', 'value' => '1'];
4193              $fullpreset = $DB->get_record_sql($sql, $params);
4194          }
4195  
4196          $settings = [
4197              // Settings. Hide Guest login button for Starter preset (and back to show for Full).
4198              [
4199                  'presetid' => $starterpreset->id,
4200                  'plugin' => 'none',
4201                  'name' => 'guestloginbutton',
4202                  'value' => '0',
4203              ],
4204              [
4205                  'presetid' => $fullpreset->id,
4206                  'plugin' => 'none',
4207                  'name' => 'guestloginbutton',
4208                  'value' => '1',
4209              ],
4210              // Settings. Set Activity chooser tabs to "Starred, All, Recommended"(1) for Starter and back it to default(0) for Full.
4211              [
4212                  'presetid' => $starterpreset->id,
4213                  'plugin' => 'none',
4214                  'name' => 'activitychoosertabmode',
4215                  'value' => '1',
4216              ],
4217              [
4218                  'presetid' => $fullpreset->id,
4219                  'plugin' => 'none',
4220                  'name' => 'activitychoosertabmode',
4221                  'value' => '0',
4222              ],
4223          ];
4224          foreach ($settings as $notused => $setting) {
4225              $params = ['adminpresetid' => $setting['presetid'], 'plugin' => $setting['plugin'], 'name' => $setting['name']];
4226              if (!$DB->record_exists('adminpresets_it', $params)) {
4227                  $record = new \stdClass();
4228                  $record->adminpresetid = $setting['presetid'];
4229                  $record->plugin = $setting['plugin'];
4230                  $record->name = $setting['name'];
4231                  $record->value = $setting['value'];
4232                  $DB->insert_record('adminpresets_it', $record);
4233              }
4234          }
4235  
4236          $plugins = [
4237              // Plugins. Blocks. Disable/enable Online users, Recently accessed courses and Starred courses.
4238              [
4239                  'presetid' => $starterpreset->id,
4240                  'plugin' => 'block',
4241                  'name' => 'online_users',
4242                  'enabled' => '0',
4243              ],
4244              [
4245                  'presetid' => $fullpreset->id,
4246                  'plugin' => 'block',
4247                  'name' => 'online_users',
4248                  'enabled' => '1',
4249              ],
4250              [
4251                  'presetid' => $starterpreset->id,
4252                  'plugin' => 'block',
4253                  'name' => 'recentlyaccessedcourses',
4254                  'enabled' => '0',
4255              ],
4256              [
4257                  'presetid' => $fullpreset->id,
4258                  'plugin' => 'block',
4259                  'name' => 'recentlyaccessedcourses',
4260                  'enabled' => '1',
4261              ],
4262              [
4263                  'presetid' => $starterpreset->id,
4264                  'plugin' => 'block',
4265                  'name' => 'starredcourses',
4266                  'enabled' => '0',
4267              ],
4268              [
4269                  'presetid' => $fullpreset->id,
4270                  'plugin' => 'block',
4271                  'name' => 'starredcourses',
4272                  'enabled' => '1',
4273              ],
4274              // Plugins. Enrolments. Disable/enable Guest access.
4275              [
4276                  'presetid' => $starterpreset->id,
4277                  'plugin' => 'enrol',
4278                  'name' => 'guest',
4279                  'enabled' => '0',
4280              ],
4281              [
4282                  'presetid' => $fullpreset->id,
4283                  'plugin' => 'enrol',
4284                  'name' => 'guest',
4285                  'enabled' => '1',
4286              ],
4287          ];
4288          foreach ($plugins as $notused => $plugin) {
4289              $params = ['adminpresetid' => $plugin['presetid'], 'plugin' => $plugin['plugin'], 'name' => $plugin['name']];
4290              if (!$DB->record_exists('adminpresets_plug', $params)) {
4291                  $record = new \stdClass();
4292                  $record->adminpresetid = $plugin['presetid'];
4293                  $record->plugin = $plugin['plugin'];
4294                  $record->name = $plugin['name'];
4295                  $record->enabled = $plugin['enabled'];
4296                  $DB->insert_record('adminpresets_plug', $record);
4297              }
4298          }
4299  
4300          // Settings: Remove customusermenuitems setting from Starter and Full presets.
4301          $sql = "(adminpresetid = ? OR adminpresetid = ?) AND plugin = 'none' AND name = 'customusermenuitems'";
4302          $params = [$starterpreset->id, $fullpreset->id];
4303          $DB->delete_records_select('adminpresets_it', $sql, $params);
4304  
4305          // Plugins. Question types. Re-enable Description and Essay for Starter.
4306          $sql = "(adminpresetid = ? OR adminpresetid = ?) AND plugin = 'qtype' AND (name = 'description' OR name = 'essay')";
4307          $DB->delete_records_select('adminpresets_plug', $sql, $params);
4308  
4309          // Main savepoint reached.
4310          upgrade_main_savepoint(true, 2022021100.01);
4311  
4312      }
4313  
4314      if ($oldversion < 2022021100.02) {
4315          $table = new xmldb_table('task_scheduled');
4316  
4317          // Changing precision of field minute on table task_scheduled to (200).
4318          $field = new xmldb_field('minute', XMLDB_TYPE_CHAR, '200', null, XMLDB_NOTNULL, null, null, 'blocking');
4319          $dbman->change_field_precision($table, $field);
4320          // Changing precision of field hour on table task_scheduled to (70).
4321          $field = new xmldb_field('hour', XMLDB_TYPE_CHAR, '70', null, XMLDB_NOTNULL, null, null, 'minute');
4322          $dbman->change_field_precision($table, $field);
4323          // Changing precision of field day on table task_scheduled to (90).
4324          $field = new xmldb_field('day', XMLDB_TYPE_CHAR, '90', null, XMLDB_NOTNULL, null, null, 'hour');
4325          $dbman->change_field_precision($table, $field);
4326          // Changing precision of field month on table task_scheduled to (30).
4327          $field = new xmldb_field('month', XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, null, 'day');
4328          $dbman->change_field_precision($table, $field);
4329  
4330          // Main savepoint reached.
4331          upgrade_main_savepoint(true, 2022021100.02);
4332      }
4333  
4334      if ($oldversion < 2022022600.01) {
4335          // Get all processor and existing preferences.
4336          $processors = $DB->get_records('message_processors');
4337          $providers = $DB->get_records('message_providers', null, '', 'id, name, component');
4338          $existingpreferences = get_config('message');
4339  
4340          foreach ($processors as $processor) {
4341              foreach ($providers as $provider) {
4342                  // Setting default preference name.
4343                  $componentproviderbase = $provider->component . '_' . $provider->name;
4344                  $preferencename = $processor->name.'_provider_'.$componentproviderbase.'_locked';
4345                  // If we do not have this setting yet, set it to 0.
4346                  if (!isset($existingpreferences->{$preferencename})) {
4347                      set_config($preferencename, 0, 'message');
4348                  }
4349              }
4350          }
4351  
4352          upgrade_main_savepoint(true, 2022022600.01);
4353      }
4354  
4355      if ($oldversion < 2022030100.00) {
4356          $sql = "SELECT preset.*
4357                    FROM {adminpresets} preset
4358              INNER JOIN {adminpresets_it} it ON preset.id = it.adminpresetid
4359                   WHERE it.name = :name AND it.value = :value AND preset.iscore > 0";
4360  
4361          $name = get_string('starterpreset', 'core_adminpresets');
4362          $params = ['name' => $name, 'iscore' => 1];
4363          $starterpreset = $DB->get_record('adminpresets', $params);
4364          if (!$starterpreset) {
4365              // Starter admin preset might have been created using the English name. Let's change it to current language.
4366              $englishname = get_string_manager()->get_string('starterpreset', 'core_adminpresets', null, 'en');
4367              $params['name'] = $englishname;
4368              $starterpreset = $DB->get_record('adminpresets', $params);
4369          }
4370          if (!$starterpreset) {
4371              // We tried, but we didn't find starter by name. Let's find a core preset that sets 'usecomments' setting to 0.
4372              $params = ['name' => 'usecomments', 'value' => '0'];
4373              $starterpreset = $DB->get_record_sql($sql, $params);
4374          }
4375          // The iscore field is already 1 for starterpreset, so we don't need to change it.
4376          // We only need to update the name and comment in case they are different to current language strings.
4377          if ($starterpreset && $starterpreset->name != $name) {
4378              $starterpreset->name = $name;
4379              $starterpreset->comments = get_string('starterpresetdescription', 'core_adminpresets');
4380              $DB->update_record('adminpresets', $starterpreset);
4381          }
4382  
4383          // Let's mark Full admin presets with current FULL_PRESETS value and change the name to current language.
4384          $name = get_string('fullpreset', 'core_adminpresets');
4385          $params = ['name' => $name];
4386          $fullpreset = $DB->get_record_select('adminpresets', 'name = :name and iscore > 0', $params);
4387          if (!$fullpreset) {
4388              // Full admin preset might have been created using the English name.
4389              $englishname = get_string_manager()->get_string('fullpreset', 'core_adminpresets', null, 'en');
4390              $params['name'] = $englishname;
4391              $fullpreset = $DB->get_record_select('adminpresets', 'name = :name and iscore > 0', $params);
4392          }
4393          if (!$fullpreset) {
4394              // We tried, but we didn't find full by name. Let's find a core preset that sets 'usecomments' setting to 1.
4395              $params = ['name' => 'usecomments', 'value' => '1'];
4396              $fullpreset = $DB->get_record_sql($sql, $params);
4397          }
4398          if ($fullpreset) {
4399              // We need to update iscore field value, whether the name is the same or not.
4400              $fullpreset->name = $name;
4401              $fullpreset->comments = get_string('fullpresetdescription', 'core_adminpresets');
4402              $fullpreset->iscore = 2;
4403              $DB->update_record('adminpresets', $fullpreset);
4404  
4405              // We are applying again changes made on 2022011100.01 upgrading step because of MDL-73953 bug.
4406              $blocknames = ['course_summary', 'feedback', 'rss_client', 'selfcompletion'];
4407              list($blocksinsql, $blocksinparams) = $DB->get_in_or_equal($blocknames);
4408  
4409              // Remove entries from the adminpresets_app_plug table (in case the preset has been applied).
4410              $appliedpresets = $DB->get_records('adminpresets_app', ['adminpresetid' => $fullpreset->id], '', 'id');
4411              if ($appliedpresets) {
4412                  list($appsinsql, $appsinparams) = $DB->get_in_or_equal(array_keys($appliedpresets));
4413                  $sql = "adminpresetapplyid $appsinsql AND plugin='block' AND name $blocksinsql";
4414                  $params = array_merge($appsinparams, $blocksinparams);
4415                  $DB->delete_records_select('adminpresets_app_plug', $sql, $params);
4416              }
4417  
4418              // Remove entries for these blocks from the adminpresets_plug table.
4419              $sql = "adminpresetid = ? AND plugin='block' AND name $blocksinsql";
4420              $params = array_merge([$fullpreset->id], $blocksinparams);
4421              $DB->delete_records_select('adminpresets_plug', $sql, $params);
4422          }
4423  
4424          // Main savepoint reached.
4425          upgrade_main_savepoint(true, 2022030100.00);
4426      }
4427  
4428      if ($oldversion < 2022031100.01) {
4429          $reportsusermenuitem = 'reports,core_reportbuilder|/reportbuilder/index.php';
4430          upgrade_add_item_to_usermenu($reportsusermenuitem);
4431          // Main savepoint reached.
4432          upgrade_main_savepoint(true, 2022031100.01);
4433      }
4434  
4435      if ($oldversion < 2022032200.01) {
4436  
4437          // Define index to be added to question_references.
4438          $table = new xmldb_table('question_references');
4439          $index = new xmldb_index('context-component-area-itemid', XMLDB_INDEX_UNIQUE,
4440              ['usingcontextid', 'component', 'questionarea', 'itemid']);
4441  
4442          // Conditionally launch add field id.
4443          if (!$dbman->index_exists($table, $index)) {
4444              $dbman->add_index($table, $index);
4445          }
4446  
4447          // Main savepoint reached.
4448          upgrade_main_savepoint(true, 2022032200.01);
4449      }
4450  
4451      if ($oldversion < 2022032200.02) {
4452  
4453          // Define index to be added to question_references.
4454          $table = new xmldb_table('question_set_references');
4455          $index = new xmldb_index('context-component-area-itemid', XMLDB_INDEX_UNIQUE,
4456              ['usingcontextid', 'component', 'questionarea', 'itemid']);
4457  
4458          // Conditionally launch add field id.
4459          if (!$dbman->index_exists($table, $index)) {
4460              $dbman->add_index($table, $index);
4461          }
4462  
4463          // Main savepoint reached.
4464          upgrade_main_savepoint(true, 2022032200.02);
4465      }
4466  
4467      if ($oldversion < 2022041200.01) {
4468  
4469          // The original default admin presets "sensible settings" (those that should be treated as sensitive).
4470          $originalsensiblesettings = 'recaptchapublickey@@none, recaptchaprivatekey@@none, googlemapkey3@@none, ' .
4471              'secretphrase@@url, cronremotepassword@@none, smtpuser@@none, smtppass@none, proxypassword@@none, ' .
4472              'quizpassword@@quiz, allowedip@@none, blockedip@@none, dbpass@@logstore_database, messageinbound_hostpass@@none, ' .
4473              'bind_pw@@auth_cas, pass@@auth_db, bind_pw@@auth_ldap, dbpass@@enrol_database, bind_pw@@enrol_ldap, ' .
4474              'server_password@@search_solr, ssl_keypassword@@search_solr, alternateserver_password@@search_solr, ' .
4475              'alternatessl_keypassword@@search_solr, test_password@@cachestore_redis, password@@mlbackend_python';
4476  
4477          // Check if the current config matches the original default, upgrade to new default if so.
4478          if (get_config('adminpresets', 'sensiblesettings') === $originalsensiblesettings) {
4479              $newsensiblesettings = "{$originalsensiblesettings}, badges_badgesalt@@none, calendar_exportsalt@@none";
4480              set_config('sensiblesettings', $newsensiblesettings, 'adminpresets');
4481          }
4482  
4483          // Main savepoint reached.
4484          upgrade_main_savepoint(true, 2022041200.01);
4485      }
4486  
4487      // Automatically generated Moodle v4.0.0 release upgrade line.
4488      // Put any upgrade step following this.
4489  
4490      if ($oldversion < 2022041900.03) {
4491          // Social custom fields could had been created linked to category id = 1. Let's check category 1 exists.
4492          if (!$DB->get_record('user_info_category', ['id' => 1])) {
4493              // Let's check if we have any custom field linked to category id = 1.
4494              $fields = $DB->get_records('user_info_field', ['categoryid' => 1]);
4495              if (!empty($fields)) {
4496                  $categoryid = $DB->get_field_sql('SELECT min(id) from {user_info_category}');
4497                  foreach ($fields as $field) {
4498                      $field->categoryid = $categoryid;
4499                      $DB->update_record('user_info_field', $field);
4500                  }
4501              }
4502          }
4503  
4504          // Main savepoint reached.
4505          upgrade_main_savepoint(true, 2022041900.03);
4506      }
4507  
4508      if ($oldversion < 2022041901.05) {
4509  
4510          // Changing precision of field hidden on table grade_categories to (10).
4511          $table = new xmldb_table('grade_categories');
4512          $field = new xmldb_field('hidden', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'timemodified');
4513  
4514          // Launch change of precision for field hidden.
4515          $dbman->change_field_precision($table, $field);
4516  
4517          // Changing precision of field hidden on table grade_categories_history to (10).
4518          $table = new xmldb_table('grade_categories_history');
4519          $field = new xmldb_field('hidden', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'aggregatesubcats');
4520  
4521          // Launch change of precision for field hidden.
4522          $dbman->change_field_precision($table, $field);
4523  
4524          // Main savepoint reached.
4525          upgrade_main_savepoint(true, 2022041901.05);
4526      }
4527  
4528      if ($oldversion < 2022041901.07) {
4529          // Iterate over custom user menu items configuration, removing pix icon references.
4530          $customusermenuitems = str_replace(["\r\n", "\r"], "\n", $CFG->customusermenuitems);
4531  
4532          $lines = preg_split('/\n/', $customusermenuitems, -1, PREG_SPLIT_NO_EMPTY);
4533          $lines = array_map(static function(string $line): string {
4534              // Previous format was "<langstring>|<url>[|<pixicon>]" - pix icon is no longer supported.
4535              $lineparts = explode('|', trim($line), 3);
4536              // Return first two parts of line.
4537              return implode('|', array_slice($lineparts, 0, 2));
4538          }, $lines);
4539  
4540          set_config('customusermenuitems', implode("\n", $lines));
4541  
4542          upgrade_main_savepoint(true, 2022041901.07);
4543      }
4544  
4545      if ($oldversion < 2022041904.03) {
4546  
4547          // Remove any orphaned tag instance records (pointing to non-existing context).
4548          $DB->delete_records_select('tag_instance', 'NOT EXISTS (
4549              SELECT ctx.id FROM {context} ctx WHERE ctx.id = {tag_instance}.contextid
4550          )');
4551  
4552          // Main savepoint reached.
4553          upgrade_main_savepoint(true, 2022041904.03);
4554      }
4555  
4556      if ($oldversion < 2022041904.14) {
4557          $table = new xmldb_table('h5p');
4558          $indexpathnamehash = new xmldb_index('pathnamehash_idx', XMLDB_INDEX_NOTUNIQUE, ['pathnamehash']);
4559  
4560          if (!$dbman->index_exists($table, $indexpathnamehash)) {
4561              $dbman->add_index($table, $indexpathnamehash);
4562          }
4563          // Main savepoint reached.
4564          upgrade_main_savepoint(true, 2022041904.14);
4565      }
4566  
4567      if ($oldversion < 2022041905.07) {
4568  
4569          // Remove any orphaned role assignment records (pointing to non-existing roles).
4570          $DB->delete_records_select('role_assignments', 'NOT EXISTS (
4571              SELECT r.id FROM {role} r WHERE r.id = {role_assignments}.roleid
4572          )');
4573  
4574          // Main savepoint reached.
4575          upgrade_main_savepoint(true, 2022041905.07);
4576      }
4577  
4578      if ($oldversion < 2022041908.02) {
4579  
4580          // Remove any orphaned role assignment records (pointing to non-existing roles).
4581          $DB->set_field('task_scheduled', 'disabled', 1, ['classname' => '\core\task\question_stats_cleanup_task']);
4582  
4583          // Main savepoint reached.
4584          upgrade_main_savepoint(true, 2022041908.02);
4585      }
4586  
4587      return true;
4588  }