Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.
/lib/db/ -> upgrade.php (source)

Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403] [Versions 402 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 https://moodledev.io/general/development/tools/xmldb}.
  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 https://moodledev.io/general/development/policies/codingstyle/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 https://moodledev.io/docs/apis/core/dml/ddl}
  82   *     - Upgrade API: {@link https://moodledev.io/docs/guides/upgrade}
  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.11.8 right now).
  96      if ($oldversion < 2021051708) {
  97          // Just in case somebody hacks upgrade scripts or env, we really can not continue.
  98          echo("You need to upgrade to 3.11.8 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, 2021051708);
 102      }
 103  
 104      if ($oldversion < 2021052500.01) {
 105          // Delete all user evidence files from users that have been deleted.
 106          $sql = "SELECT DISTINCT f.*
 107                    FROM {files} f
 108               LEFT JOIN {context} c ON f.contextid = c.id
 109                   WHERE f.component = :component
 110                     AND f.filearea = :filearea
 111                     AND c.id IS NULL";
 112          $stalefiles = $DB->get_records_sql($sql, ['component' => 'core_competency', 'filearea' => 'userevidence']);
 113  
 114          $fs = get_file_storage();
 115          foreach ($stalefiles as $stalefile) {
 116              $fs->get_file_instance($stalefile)->delete();
 117          }
 118  
 119          upgrade_main_savepoint(true, 2021052500.01);
 120      }
 121  
 122      if ($oldversion < 2021052500.02) {
 123  
 124          // Define field timecreated to be added to task_adhoc.
 125          $table = new xmldb_table('task_adhoc');
 126          $field = new xmldb_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'blocking');
 127  
 128          // Conditionally launch add field timecreated.
 129          if (!$dbman->field_exists($table, $field)) {
 130              $dbman->add_field($table, $field);
 131          }
 132  
 133          // Main savepoint reached.
 134          upgrade_main_savepoint(true, 2021052500.02);
 135      }
 136  
 137      if ($oldversion < 2021052500.04) {
 138          // Define field metadatasettings to be added to h5p_libraries.
 139          $table = new xmldb_table('h5p_libraries');
 140          $field = new xmldb_field('metadatasettings', XMLDB_TYPE_TEXT, null, null, null, null, null, 'coreminor');
 141  
 142          // Conditionally launch add field metadatasettings.
 143          if (!$dbman->field_exists($table, $field)) {
 144              $dbman->add_field($table, $field);
 145          }
 146  
 147          // Get installed library files that have no metadata settings value.
 148          $params = [
 149              'component' => 'core_h5p',
 150              'filearea' => 'libraries',
 151              'filename' => 'library.json',
 152          ];
 153          $sql = "SELECT l.id, f.id as fileid
 154                    FROM {files} f
 155               LEFT JOIN {h5p_libraries} l ON f.itemid = l.id
 156                   WHERE f.component = :component
 157                         AND f.filearea = :filearea
 158                         AND f.filename = :filename";
 159          $libraries = $DB->get_records_sql($sql, $params);
 160  
 161          // Update metadatasettings field when the attribute is present in the library.json file.
 162          $fs = get_file_storage();
 163          foreach ($libraries as $library) {
 164              $jsonfile = $fs->get_file_by_id($library->fileid);
 165              $jsoncontent = json_decode($jsonfile->get_content());
 166              if (isset($jsoncontent->metadataSettings)) {
 167                  unset($library->fileid);
 168                  $library->metadatasettings = json_encode($jsoncontent->metadataSettings);
 169                  $DB->update_record('h5p_libraries', $library);
 170              }
 171          }
 172  
 173          // Main savepoint reached.
 174          upgrade_main_savepoint(true, 2021052500.04);
 175      }
 176  
 177      if ($oldversion < 2021052500.05) {
 178          // Define fields to be added to task_scheduled.
 179          $table = new xmldb_table('task_scheduled');
 180          $field = new xmldb_field('timestarted', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'disabled');
 181          if (!$dbman->field_exists($table, $field)) {
 182              $dbman->add_field($table, $field);
 183          }
 184          $field = new xmldb_field('hostname', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'timestarted');
 185          if (!$dbman->field_exists($table, $field)) {
 186              $dbman->add_field($table, $field);
 187          }
 188          $field = new xmldb_field('pid', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'hostname');
 189          if (!$dbman->field_exists($table, $field)) {
 190              $dbman->add_field($table, $field);
 191          }
 192  
 193          // Define fields to be added to task_adhoc.
 194          $table = new xmldb_table('task_adhoc');
 195          $field = new xmldb_field('timestarted', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'blocking');
 196          if (!$dbman->field_exists($table, $field)) {
 197              $dbman->add_field($table, $field);
 198          }
 199          $field = new xmldb_field('hostname', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'timestarted');
 200          if (!$dbman->field_exists($table, $field)) {
 201              $dbman->add_field($table, $field);
 202          }
 203          $field = new xmldb_field('pid', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'hostname');
 204          if (!$dbman->field_exists($table, $field)) {
 205              $dbman->add_field($table, $field);
 206          }
 207  
 208          // Define fields to be added to task_log.
 209          $table = new xmldb_table('task_log');
 210          $field = new xmldb_field('hostname', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'output');
 211          if (!$dbman->field_exists($table, $field)) {
 212              $dbman->add_field($table, $field);
 213          }
 214          $field = new xmldb_field('pid', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'hostname');
 215          if (!$dbman->field_exists($table, $field)) {
 216              $dbman->add_field($table, $field);
 217          }
 218  
 219          // Main savepoint reached.
 220          upgrade_main_savepoint(true, 2021052500.05);
 221      }
 222  
 223      if ($oldversion < 2021052500.06) {
 224          // Define table to store virus infected details.
 225          $table = new xmldb_table('infected_files');
 226  
 227          // Adding fields to table infected_files.
 228          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 229          $table->add_field('filename', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
 230          $table->add_field('quarantinedfile', XMLDB_TYPE_TEXT, null, null, null, null, null);
 231          $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 232          $table->add_field('reason', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
 233          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
 234  
 235          // Adding keys to table infected_files.
 236          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
 237          $table->add_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']);
 238  
 239          // Conditionally launch create table for infected_files.
 240          if (!$dbman->table_exists($table)) {
 241              $dbman->create_table($table);
 242          }
 243          upgrade_main_savepoint(true, 2021052500.06);
 244      }
 245  
 246      if ($oldversion < 2021052500.13) {
 247          // Remove all the files with component='core_h5p' and filearea='editor' because they won't be used anymore.
 248          $fs = get_file_storage();
 249          $syscontext = context_system::instance();
 250          $fs->delete_area_files($syscontext->id, 'core_h5p', 'editor');
 251  
 252          // Main savepoint reached.
 253          upgrade_main_savepoint(true, 2021052500.13);
 254      }
 255  
 256      if ($oldversion < 2021052500.15) {
 257          // Copy From id captures the id of the source course when a new course originates from a restore
 258          // of another course on the same site.
 259          $table = new xmldb_table('course');
 260          $field = new xmldb_field('originalcourseid', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
 261  
 262          if (!$dbman->field_exists($table, $field)) {
 263              $dbman->add_field($table, $field);
 264          }
 265  
 266          // Main savepoint reached.
 267          upgrade_main_savepoint(true, 2021052500.15);
 268      }
 269  
 270      if ($oldversion < 2021052500.19) {
 271          // Define table oauth2_refresh_token to be created.
 272          $table = new xmldb_table('oauth2_refresh_token');
 273  
 274          // Adding fields to table oauth2_refresh_token.
 275          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 276          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 277          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 278          $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 279          $table->add_field('issuerid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 280          $table->add_field('token', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
 281          $table->add_field('scopehash', XMLDB_TYPE_CHAR, 40, null, XMLDB_NOTNULL, null, null);
 282  
 283          // Adding keys to table oauth2_refresh_token.
 284          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
 285          $table->add_key('issueridkey', XMLDB_KEY_FOREIGN, ['issuerid'], 'oauth2_issuer', ['id']);
 286          $table->add_key('useridkey', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']);
 287  
 288          // Adding indexes to table oauth2_refresh_token.
 289          $table->add_index('userid-issuerid-scopehash', XMLDB_INDEX_UNIQUE, array('userid', 'issuerid', 'scopehash'));
 290  
 291          // Conditionally launch create table for oauth2_refresh_token.
 292          if (!$dbman->table_exists($table)) {
 293              $dbman->create_table($table);
 294          }
 295  
 296          // Main savepoint reached.
 297          upgrade_main_savepoint(true, 2021052500.19);
 298      }
 299  
 300      if ($oldversion < 2021052500.20) {
 301  
 302          // Define index modulename-instance-eventtype (not unique) to be added to event.
 303          $table = new xmldb_table('event');
 304          $index = new xmldb_index('modulename-instance-eventtype', XMLDB_INDEX_NOTUNIQUE, ['modulename', 'instance', 'eventtype']);
 305  
 306          // Conditionally launch add index modulename-instance-eventtype.
 307          if (!$dbman->index_exists($table, $index)) {
 308              $dbman->add_index($table, $index);
 309          }
 310  
 311          // Define index modulename-instance (not unique) to be dropped form event.
 312          $table = new xmldb_table('event');
 313          $index = new xmldb_index('modulename-instance', XMLDB_INDEX_NOTUNIQUE, ['modulename', 'instance']);
 314  
 315          // Conditionally launch drop index modulename-instance.
 316          if ($dbman->index_exists($table, $index)) {
 317              $dbman->drop_index($table, $index);
 318          }
 319  
 320          // Main savepoint reached.
 321          upgrade_main_savepoint(true, 2021052500.20);
 322      }
 323  
 324      if ($oldversion < 2021052500.24) {
 325          // Define fields tutorial and example to be added to h5p_libraries.
 326          $table = new xmldb_table('h5p_libraries');
 327  
 328          // Add tutorial field.
 329          $field = new xmldb_field('tutorial', XMLDB_TYPE_TEXT, null, null, null, null, null, 'metadatasettings');
 330          if (!$dbman->field_exists($table, $field)) {
 331              $dbman->add_field($table, $field);
 332          }
 333  
 334          // Add example field.
 335          $field = new xmldb_field('example', XMLDB_TYPE_TEXT, null, null, null, null, null, 'tutorial');
 336  
 337          if (!$dbman->field_exists($table, $field)) {
 338              $dbman->add_field($table, $field);
 339          }
 340  
 341          // Main savepoint reached.
 342          upgrade_main_savepoint(true, 2021052500.24);
 343      }
 344  
 345      if ($oldversion < 2021052500.26) {
 346          // Delete orphaned course_modules_completion rows; these were not deleted properly
 347          // by remove_course_contents function.
 348          $DB->delete_records_select('course_modules_completion', "
 349                  NOT EXISTS (
 350                          SELECT 1
 351                            FROM {course_modules} cm
 352                           WHERE cm.id = {course_modules_completion}.coursemoduleid
 353                  )");
 354          upgrade_main_savepoint(true, 2021052500.26);
 355      }
 356  
 357      if ($oldversion < 2021052500.27) {
 358          // Script to fix incorrect records of "hidden" field in existing grade items.
 359          $sql = "SELECT cm.instance, cm.course
 360                    FROM {course_modules} cm
 361                    JOIN {modules} m ON m.id = cm.module
 362                   WHERE m.name = :module AND cm.visible = :visible";
 363          $hidequizlist = $DB->get_recordset_sql($sql, ['module' => 'quiz', 'visible' => 0]);
 364  
 365          foreach ($hidequizlist as $hidequiz) {
 366              $params = [
 367                  'itemmodule'    => 'quiz',
 368                  'courseid'      => $hidequiz->course,
 369                  'iteminstance'  => $hidequiz->instance,
 370              ];
 371  
 372              $DB->set_field('grade_items', 'hidden', 1, $params);
 373          }
 374          $hidequizlist->close();
 375  
 376          upgrade_main_savepoint(true, 2021052500.27);
 377      }
 378  
 379      if ($oldversion < 2021052500.29) {
 380          // Get the current guest user which is also set as 'deleted'.
 381          $guestuser = $DB->get_record('user', ['id' => $CFG->siteguest, 'deleted' => 1]);
 382          // If there is a deleted guest user, reset the user to not be deleted and make sure the related
 383          // user context exists.
 384          if ($guestuser) {
 385              $guestuser->deleted = 0;
 386              $DB->update_record('user', $guestuser);
 387  
 388              // Get the guest user context.
 389              $guestusercontext = $DB->get_record('context',
 390                  ['contextlevel' => CONTEXT_USER, 'instanceid' => $guestuser->id]);
 391  
 392              // If the guest user context does not exist, create it.
 393              if (!$guestusercontext) {
 394                  $record = new stdClass();
 395                  $record->contextlevel = CONTEXT_USER;
 396                  $record->instanceid = $guestuser->id;
 397                  $record->depth = 0;
 398                  // The path is not known before insert.
 399                  $record->path = null;
 400                  $record->locked = 0;
 401  
 402                  $record->id = $DB->insert_record('context', $record);
 403  
 404                  // Update the path.
 405                  $record->path = '/' . SYSCONTEXTID . '/' . $record->id;
 406                  $record->depth = substr_count($record->path, '/');
 407                  $DB->update_record('context', $record);
 408              }
 409          }
 410  
 411          // Main savepoint reached.
 412          upgrade_main_savepoint(true, 2021052500.29);
 413      }
 414  
 415      if ($oldversion < 2021052500.30) {
 416          // Reset analytics model output dir if it's the default value.
 417          $modeloutputdir = get_config('analytics', 'modeloutputdir');
 418          if (strcasecmp($modeloutputdir, $CFG->dataroot . DIRECTORY_SEPARATOR . 'models') == 0) {
 419              set_config('modeloutputdir', '', 'analytics');
 420          }
 421  
 422          // Main savepoint reached.
 423          upgrade_main_savepoint(true, 2021052500.30);
 424      }
 425  
 426      if ($oldversion < 2021052500.32) {
 427          // Define field downloadcontent to be added to course.
 428          $table = new xmldb_table('course');
 429          $field = new xmldb_field('downloadcontent', XMLDB_TYPE_INTEGER, '1', null, null, null, null, 'visibleold');
 430  
 431          if (!$dbman->field_exists($table, $field)) {
 432              $dbman->add_field($table, $field);
 433          }
 434  
 435          // Main savepoint reached.
 436          upgrade_main_savepoint(true, 2021052500.32);
 437      }
 438  
 439      if ($oldversion < 2021052500.33) {
 440          $table = new xmldb_table('badge_backpack');
 441  
 442          // There is no key_exists, so test the equivalent index.
 443          $oldindex = new xmldb_index('backpackcredentials', XMLDB_KEY_UNIQUE, ['userid', 'externalbackpackid']);
 444          if (!$dbman->index_exists($table, $oldindex)) {
 445              // All external backpack providers/hosts are now exclusively stored in badge_external_backpack.
 446              // All credentials are stored in badge_backpack and are unique per user, backpack.
 447              $uniquekey = new xmldb_key('backpackcredentials', XMLDB_KEY_UNIQUE, ['userid', 'externalbackpackid']);
 448              $dbman->add_key($table, $uniquekey);
 449          }
 450  
 451          // Drop the password field as this is moved to badge_backpack.
 452          $table = new xmldb_table('badge_external_backpack');
 453          $field = new xmldb_field('password', XMLDB_TYPE_CHAR, '50');
 454          if ($dbman->field_exists($table, $field)) {
 455              // If there is a current backpack set then copy it across to the new structure.
 456              if ($CFG->badges_defaultissuercontact) {
 457                  // Get the currently used site backpacks.
 458                  $records = $DB->get_records_select('badge_external_backpack', "password IS NOT NULL AND password != ''");
 459                  $backpack = [
 460                      'userid' => '0',
 461                      'email' => $CFG->badges_defaultissuercontact,
 462                      'backpackuid' => -1
 463                  ];
 464  
 465                  // Create records corresponding to the site backpacks.
 466                  foreach ($records as $record) {
 467                      $backpack['password'] = $record->password;
 468                      $backpack['externalbackpackid'] = $record->id;
 469                      $DB->insert_record('badge_backpack', (object) $backpack);
 470                  }
 471              }
 472  
 473              $dbman->drop_field($table, $field);
 474          }
 475  
 476          // Main savepoint reached.
 477          upgrade_main_savepoint(true, 2021052500.33);
 478      }
 479  
 480      if ($oldversion < 2021052500.36) {
 481          // Define table payment_accounts to be created.
 482          $table = new xmldb_table('payment_accounts');
 483  
 484          // Adding fields to table payment_accounts.
 485          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 486          $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
 487          $table->add_field('idnumber', XMLDB_TYPE_CHAR, '100', null, null, null, null);
 488          $table->add_field('contextid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 489          $table->add_field('enabled', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0');
 490          $table->add_field('archived', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0');
 491          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
 492          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
 493  
 494          // Adding keys to table payment_accounts.
 495          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
 496  
 497          // Conditionally launch create table for payment_accounts.
 498          if (!$dbman->table_exists($table)) {
 499              $dbman->create_table($table);
 500          }
 501  
 502          // Define table payment_gateways to be created.
 503          $table = new xmldb_table('payment_gateways');
 504  
 505          // Adding fields to table payment_gateways.
 506          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 507          $table->add_field('accountid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 508          $table->add_field('gateway', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
 509          $table->add_field('enabled', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1');
 510          $table->add_field('config', XMLDB_TYPE_TEXT, null, null, null, null, null);
 511          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 512          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 513  
 514          // Adding keys to table payment_gateways.
 515          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
 516          $table->add_key('accountid', XMLDB_KEY_FOREIGN, ['accountid'], 'payment_accounts', ['id']);
 517  
 518          // Conditionally launch create table for payment_gateways.
 519          if (!$dbman->table_exists($table)) {
 520              $dbman->create_table($table);
 521          }
 522  
 523          // Define table payments to be created.
 524          $table = new xmldb_table('payments');
 525  
 526          // Adding fields to table payments.
 527          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 528          $table->add_field('component', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
 529          $table->add_field('paymentarea', XMLDB_TYPE_CHAR, '50', null, XMLDB_NOTNULL, null, null);
 530          $table->add_field('itemid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 531          $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 532          $table->add_field('amount', XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, null);
 533          $table->add_field('currency', XMLDB_TYPE_CHAR, '3', null, XMLDB_NOTNULL, null, null);
 534          $table->add_field('accountid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 535          $table->add_field('gateway', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
 536          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
 537          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
 538  
 539          // Adding keys to table payments.
 540          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
 541          $table->add_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']);
 542          $table->add_key('accountid', XMLDB_KEY_FOREIGN, ['accountid'], 'payment_accounts', ['id']);
 543  
 544          // Adding indexes to table payments.
 545          $table->add_index('gateway', XMLDB_INDEX_NOTUNIQUE, ['gateway']);
 546          $table->add_index('component-paymentarea-itemid', XMLDB_INDEX_NOTUNIQUE, ['component', 'paymentarea', 'itemid']);
 547  
 548          // Conditionally launch create table for payments.
 549          if (!$dbman->table_exists($table)) {
 550              $dbman->create_table($table);
 551          }
 552  
 553          // Main savepoint reached.
 554          upgrade_main_savepoint(true, 2021052500.36);
 555      }
 556  
 557      if ($oldversion < 2021052500.42) {
 558          // Get all lessons that are set with a completion criteria of 'requires grade' but with no grade type set.
 559          $sql = "SELECT cm.id
 560                    FROM {course_modules} cm
 561                    JOIN {lesson} l ON l.id = cm.instance
 562                    JOIN {modules} m ON m.id = cm.module
 563                   WHERE m.name = :name AND cm.completiongradeitemnumber IS NOT NULL AND l.grade = :grade";
 564  
 565          do {
 566              if ($invalidconfigrations = $DB->get_records_sql($sql, ['name' => 'lesson', 'grade' => 0], 0, 1000)) {
 567                  list($insql, $inparams) = $DB->get_in_or_equal(array_keys($invalidconfigrations), SQL_PARAMS_NAMED);
 568                  $DB->set_field_select('course_modules', 'completiongradeitemnumber', null, "id $insql", $inparams);
 569              }
 570          } while ($invalidconfigrations);
 571  
 572          upgrade_main_savepoint(true, 2021052500.42);
 573      }
 574  
 575      if ($oldversion < 2021052500.55) {
 576          $DB->delete_records_select('event', "eventtype = 'category' AND categoryid = 0 AND userid <> 0");
 577  
 578          upgrade_main_savepoint(true, 2021052500.55);
 579      }
 580  
 581      if ($oldversion < 2021052500.59) {
 582          // Define field visibility to be added to contentbank_content.
 583          $table = new xmldb_table('contentbank_content');
 584          $field = new xmldb_field('visibility', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1', 'contextid');
 585  
 586          // Conditionally launch add field visibility.
 587          if (!$dbman->field_exists($table, $field)) {
 588              $dbman->add_field($table, $field);
 589          }
 590  
 591          // Main savepoint reached.
 592          upgrade_main_savepoint(true, 2021052500.59);
 593      }
 594  
 595      if ($oldversion < 2021052500.60) {
 596  
 597          // We are going to remove the field 'hidepicture' from the groups
 598          // so we need to remove the pictures from those groups. But we prevent
 599          // the execution twice because this could be executed again when upgrading
 600          // to different versions.
 601          if ($dbman->field_exists('groups', 'hidepicture')) {
 602  
 603              $sql = "SELECT g.id, g.courseid, ctx.id AS contextid
 604                         FROM {groups} g
 605                         JOIN {context} ctx
 606                           ON ctx.instanceid = g.courseid
 607                          AND ctx.contextlevel = :contextlevel
 608                        WHERE g.hidepicture = 1";
 609  
 610              // Selecting all the groups that have hide picture enabled, and organising them by context.
 611              $groupctx = [];
 612              $records = $DB->get_recordset_sql($sql, ['contextlevel' => CONTEXT_COURSE]);
 613              foreach ($records as $record) {
 614                  if (!isset($groupctx[$record->contextid])) {
 615                      $groupctx[$record->contextid] = [];
 616                  }
 617                  $groupctx[$record->contextid][] = $record->id;
 618              }
 619              $records->close();
 620  
 621              // Deleting the group files.
 622              $fs = get_file_storage();
 623              foreach ($groupctx as $contextid => $groupids) {
 624                  list($in, $inparams) = $DB->get_in_or_equal($groupids, SQL_PARAMS_NAMED);
 625                  $fs->delete_area_files_select($contextid, 'group', 'icon', $in, $inparams);
 626              }
 627  
 628              // Updating the database to remove picture from all those groups.
 629              $sql = "UPDATE {groups} SET picture = :pic WHERE hidepicture = :hide";
 630              $DB->execute($sql, ['pic' => 0, 'hide' => 1]);
 631          }
 632  
 633          // Define field hidepicture to be dropped from groups.
 634          $table = new xmldb_table('groups');
 635          $field = new xmldb_field('hidepicture');
 636  
 637          // Conditionally launch drop field hidepicture.
 638          if ($dbman->field_exists($table, $field)) {
 639              $dbman->drop_field($table, $field);
 640          }
 641  
 642          // Main savepoint reached.
 643          upgrade_main_savepoint(true, 2021052500.60);
 644      }
 645  
 646      if ($oldversion < 2021052500.64) {
 647          // Get all the external backpacks and update the sortorder column, to avoid repeated/wrong values. As sortorder was not
 648          // used since now, the id column will be the criteria to follow for re-ordering them with a valid value.
 649          $i = 1;
 650          $records = $DB->get_records('badge_external_backpack', null, 'id ASC');
 651          foreach ($records as $record) {
 652              $record->sortorder = $i++;
 653              $DB->update_record('badge_external_backpack', $record);
 654          }
 655  
 656          upgrade_main_savepoint(true, 2021052500.64);
 657      }
 658  
 659      if ($oldversion < 2021052500.67) {
 660          // The $CFG->badges_site_backpack setting has been removed because it's not required anymore. From now, the default backpack
 661          // will be the one with lower sortorder value.
 662          unset_config('badges_site_backpack');
 663  
 664          upgrade_main_savepoint(true, 2021052500.67);
 665      }
 666  
 667      if ($oldversion < 2021052500.69) {
 668  
 669          // Define field type to be added to oauth2_issuer.
 670          $table = new xmldb_table('oauth2_issuer');
 671          $field = new xmldb_field('servicetype', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'requireconfirmation');
 672  
 673          // Conditionally launch add field type.
 674          if (!$dbman->field_exists($table, $field)) {
 675              $dbman->add_field($table, $field);
 676          }
 677  
 678          // Set existing values to the proper servicetype value.
 679          // It's not critical if the servicetype column doesn't contain the proper value for Google, Microsoft, Facebook or
 680          // Nextcloud services because, for now, this value is used for services using different discovery method.
 681          // However, let's try to upgrade it using the default value for the baseurl or image. If any of these default values
 682          // have been changed, the servicetype column will remain NULL.
 683          $recordset = $DB->get_recordset('oauth2_issuer');
 684          foreach ($recordset as $record) {
 685              if ($record->baseurl == 'https://accounts.google.com/') {
 686                  $record->servicetype = 'google';
 687                  $DB->update_record('oauth2_issuer', $record);
 688              } else if ($record->image == 'https://www.microsoft.com/favicon.ico') {
 689                  $record->servicetype = 'microsoft';
 690                  $DB->update_record('oauth2_issuer', $record);
 691              } else if ($record->image == 'https://facebookbrand.com/wp-content/uploads/2016/05/flogo_rgb_hex-brc-site-250.png') {
 692                  $record->servicetype = 'facebook';
 693                  $DB->update_record('oauth2_issuer', $record);
 694              } else if ($record->image == 'https://nextcloud.com/wp-content/themes/next/assets/img/common/favicon.png?x16328') {
 695                  $record->servicetype = 'nextcloud';
 696                  $DB->update_record('oauth2_issuer', $record);
 697              }
 698          }
 699          $recordset->close();
 700  
 701          // Main savepoint reached.
 702          upgrade_main_savepoint(true, 2021052500.69);
 703      }
 704  
 705      if ($oldversion < 2021052500.74) {
 706          // Define field 'showactivitydates' to be added to course table.
 707          $table = new xmldb_table('course');
 708          $field = new xmldb_field('showactivitydates', XMLDB_TYPE_INTEGER, '1', null,
 709              XMLDB_NOTNULL, null, '0', 'originalcourseid');
 710  
 711          if (!$dbman->field_exists($table, $field)) {
 712              $dbman->add_field($table, $field);
 713          }
 714  
 715          // Main savepoint reached.
 716          upgrade_main_savepoint(true, 2021052500.74);
 717      }
 718  
 719      if ($oldversion < 2021052500.75) {
 720          // Define field 'showcompletionconditions' to be added to course.
 721          $table = new xmldb_table('course');
 722          $field = new xmldb_field('showcompletionconditions', XMLDB_TYPE_INTEGER, '1', null,
 723              XMLDB_NOTNULL, null, '1', 'completionnotify');
 724  
 725          if (!$dbman->field_exists($table, $field)) {
 726              $dbman->add_field($table, $field);
 727          }
 728  
 729          // Main savepoint reached.
 730          upgrade_main_savepoint(true, 2021052500.75);
 731      }
 732  
 733      if ($oldversion < 2021052500.78) {
 734  
 735          // Define field enabled to be added to h5p_libraries.
 736          $table = new xmldb_table('h5p_libraries');
 737          $field = new xmldb_field('enabled', XMLDB_TYPE_INTEGER, '1', null, null, null, '1', 'example');
 738  
 739          // Conditionally launch add field enabled.
 740          if (!$dbman->field_exists($table, $field)) {
 741              $dbman->add_field($table, $field);
 742          }
 743  
 744          // Main savepoint reached.
 745          upgrade_main_savepoint(true, 2021052500.78);
 746      }
 747  
 748      if ($oldversion < 2021052500.83) {
 749  
 750          // Define field loginpagename to be added to oauth2_issuer.
 751          $table = new xmldb_table('oauth2_issuer');
 752          $field = new xmldb_field('loginpagename', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'servicetype');
 753  
 754          // Conditionally launch add field loginpagename.
 755          if (!$dbman->field_exists($table, $field)) {
 756              $dbman->add_field($table, $field);
 757          }
 758  
 759          // Main savepoint reached.
 760          upgrade_main_savepoint(true, 2021052500.83);
 761      }
 762  
 763      if ($oldversion < 2021052500.84) {
 764          require_once($CFG->dirroot . '/user/profile/field/social/upgradelib.php');
 765          $table = new xmldb_table('user');
 766          $tablecolumns = ['icq', 'skype', 'aim', 'yahoo', 'msn', 'url'];
 767  
 768          foreach ($tablecolumns as $column) {
 769              $field = new xmldb_field($column);
 770              if ($dbman->field_exists($table, $field)) {
 771                  user_profile_social_moveto_profilefield($column);
 772                  $dbman->drop_field($table, $field);
 773              }
 774          }
 775  
 776          // Update all module availability if it relies on the old user fields.
 777          user_profile_social_update_module_availability();
 778  
 779          // Remove field mapping for oauth2.
 780          $DB->delete_records('oauth2_user_field_mapping', array('internalfield' => 'url'));
 781  
 782          // Main savepoint reached.
 783          upgrade_main_savepoint(true, 2021052500.84);
 784      }
 785  
 786      if ($oldversion < 2021052500.85) {
 787          require_once($CFG->libdir . '/db/upgradelib.php');
 788  
 789          // Check if this site has executed the problematic upgrade steps.
 790          $needsfixing = upgrade_calendar_site_status(false);
 791  
 792          // Only queue the task if this site has been affected by the problematic upgrade step.
 793          if ($needsfixing) {
 794  
 795              // Create adhoc task to search and recover orphaned calendar events.
 796              $record = new \stdClass();
 797              $record->classname = '\core\task\calendar_fix_orphaned_events';
 798  
 799              // Next run time based from nextruntime computation in \core\task\manager::queue_adhoc_task().
 800              $nextruntime = time() - 1;
 801              $record->nextruntime = $nextruntime;
 802              $DB->insert_record('task_adhoc', $record);
 803          }
 804  
 805          // Main savepoint reached.
 806          upgrade_main_savepoint(true, 2021052500.85);
 807      }
 808  
 809      if ($oldversion < 2021052500.87) {
 810          // Changing the default of field showcompletionconditions on table course to 0.
 811          $table = new xmldb_table('course');
 812          $field = new xmldb_field('showcompletionconditions', XMLDB_TYPE_INTEGER, '1', null, null, null, null, 'showactivitydates');
 813  
 814          // Launch change of nullability for field showcompletionconditions.
 815          $dbman->change_field_notnull($table, $field);
 816  
 817          // Launch change of default for field showcompletionconditions.
 818          $dbman->change_field_default($table, $field);
 819  
 820          // Set showcompletionconditions to null for courses which don't track completion.
 821          $sql = "UPDATE {course}
 822                     SET showcompletionconditions = null
 823                   WHERE enablecompletion <> 1";
 824          $DB->execute($sql);
 825  
 826          // Main savepoint reached.
 827          upgrade_main_savepoint(true, 2021052500.87);
 828      }
 829  
 830      if ($oldversion < 2021052500.90) {
 831          // Remove usemodchooser user preference for every user.
 832          $DB->delete_records('user_preferences', ['name' => 'usemodchooser']);
 833  
 834          // Main savepoint reached.
 835          upgrade_main_savepoint(true, 2021052500.90);
 836      }
 837  
 838      if ($oldversion < 2021060200.00) {
 839  
 840          // Define index name (not unique) to be added to user_preferences.
 841          $table = new xmldb_table('user_preferences');
 842          $index = new xmldb_index('name', XMLDB_INDEX_NOTUNIQUE, ['name']);
 843  
 844          // Conditionally launch add index name.
 845          if (!$dbman->index_exists($table, $index)) {
 846              $dbman->add_index($table, $index);
 847          }
 848  
 849          // Main savepoint reached.
 850          upgrade_main_savepoint(true, 2021060200.00);
 851      }
 852  
 853      if ($oldversion < 2021060900.00) {
 854          // Update the externalfield to be larger.
 855          $table = new xmldb_table('oauth2_user_field_mapping');
 856          $field = new xmldb_field('externalfield', XMLDB_TYPE_CHAR, '500', null, XMLDB_NOTNULL, false, null, 'issuerid');
 857          $dbman->change_field_type($table, $field);
 858  
 859          // Main savepoint reached.
 860          upgrade_main_savepoint(true, 2021060900.00);
 861      }
 862  
 863      if ($oldversion < 2021072800.01) {
 864          // Define table reportbuilder_report to be created.
 865          $table = new xmldb_table('reportbuilder_report');
 866  
 867          // Adding fields to table reportbuilder_report.
 868          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 869          $table->add_field('source', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
 870          $table->add_field('type', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0');
 871          $table->add_field('contextid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
 872          $table->add_field('component', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
 873          $table->add_field('area', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
 874          $table->add_field('itemid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
 875          $table->add_field('usercreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
 876          $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
 877          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
 878          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
 879  
 880          // Adding keys to table reportbuilder_report.
 881          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
 882          $table->add_key('usercreated', XMLDB_KEY_FOREIGN, ['usercreated'], 'user', ['id']);
 883          $table->add_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']);
 884          $table->add_key('contextid', XMLDB_KEY_FOREIGN, ['contextid'], 'context', ['id']);
 885  
 886          // Conditionally launch create table for reportbuilder_report.
 887          if (!$dbman->table_exists($table)) {
 888              $dbman->create_table($table);
 889          }
 890  
 891          // Main savepoint reached.
 892          upgrade_main_savepoint(true, 2021072800.01);
 893      }
 894  
 895      if ($oldversion < 2021090200.01) {
 896          // Remove qformat_webct (unless it has manually been added back).
 897          if (!file_exists($CFG->dirroot . '/question/format/webct/format.php')) {
 898              unset_all_config_for_plugin('qformat_webct');
 899          }
 900  
 901          // Main savepoint reached.
 902          upgrade_main_savepoint(true, 2021090200.01);
 903      }
 904  
 905      if ($oldversion < 2021091100.01) {
 906          // If message_jabber is no longer present, remove it.
 907          if (!file_exists($CFG->dirroot . '/message/output/jabber/message_output_jabber.php')) {
 908              // Remove Jabber from the notification plugins list.
 909              $DB->delete_records('message_processors', ['name' => 'jabber']);
 910  
 911              // Remove user preference settings.
 912              $DB->delete_records('user_preferences', ['name' => 'message_processor_jabber_jabberid']);
 913              $sql = 'SELECT *
 914                      FROM {user_preferences} up
 915                      WHERE ' . $DB->sql_like('up.name', ':name', false, false) . ' AND ' .
 916                          $DB->sql_like('up.value', ':value', false, false);
 917              $params = [
 918                  'name' => 'message_provider_%',
 919                  'value' => '%jabber%',
 920              ];
 921              $jabbersettings = $DB->get_recordset_sql($sql, $params);
 922              foreach ($jabbersettings as $jabbersetting) {
 923                  // Remove 'jabber' from the value.
 924                  $jabbersetting->value = implode(',', array_diff(explode(',', $jabbersetting->value), ['jabber']));
 925                  $DB->update_record('user_preferences', $jabbersetting);
 926              }
 927              $jabbersettings->close();
 928  
 929              // Clean config settings.
 930              unset_config('jabberhost');
 931              unset_config('jabberserver');
 932              unset_config('jabberusername');
 933              unset_config('jabberpassword');
 934              unset_config('jabberport');
 935  
 936              // Remove default notification preferences.
 937              $like = $DB->sql_like('name', '?', true, true, false, '|');
 938              $params = [$DB->sql_like_escape('jabber_provider_', '|') . '%'];
 939              $DB->delete_records_select('config_plugins', $like, $params);
 940  
 941              // Clean config config settings.
 942              unset_all_config_for_plugin('message_jabber');
 943          }
 944  
 945          upgrade_main_savepoint(true, 2021091100.01);
 946      }
 947  
 948      if ($oldversion < 2021091100.02) {
 949          // Set the description field to HTML format for the Default course category.
 950          $category = $DB->get_record('course_categories', ['id' => 1]);
 951  
 952          if (!empty($category) && $category->descriptionformat == FORMAT_MOODLE) {
 953              // Format should be changed only if it's still set to FORMAT_MOODLE.
 954              if (!is_null($category->description)) {
 955                  // If description is not empty, format the content to HTML.
 956                  $category->description = format_text($category->description, FORMAT_MOODLE);
 957              }
 958              $category->descriptionformat = FORMAT_HTML;
 959              $DB->update_record('course_categories', $category);
 960          }
 961  
 962          // Main savepoint reached.
 963          upgrade_main_savepoint(true, 2021091100.02);
 964      }
 965  
 966      if ($oldversion < 2021091700.01) {
 967          // Default 'off' for existing sites as this is the behaviour they had earlier.
 968          set_config('enroladminnewcourse', false);
 969  
 970          // Main savepoint reached.
 971          upgrade_main_savepoint(true, 2021091700.01);
 972      }
 973  
 974      if ($oldversion < 2021091700.02) {
 975          // If portfolio_picasa is no longer present, remove it.
 976          if (!file_exists($CFG->dirroot . '/portfolio/picasa/version.php')) {
 977              $instance = $DB->get_record('portfolio_instance', ['plugin' => 'picasa']);
 978              if (!empty($instance)) {
 979                  // Remove all records from portfolio_instance_config.
 980                  $DB->delete_records('portfolio_instance_config', ['instance' => $instance->id]);
 981                  // Remove all records from portfolio_instance_user.
 982                  $DB->delete_records('portfolio_instance_user', ['instance' => $instance->id]);
 983                  // Remove all records from portfolio_log.
 984                  $DB->delete_records('portfolio_log', ['portfolio' => $instance->id]);
 985                  // Remove all records from portfolio_tempdata.
 986                  $DB->delete_records('portfolio_tempdata', ['instance' => $instance->id]);
 987                  // Remove the record from the portfolio_instance table.
 988                  $DB->delete_records('portfolio_instance', ['id' => $instance->id]);
 989              }
 990  
 991              // Clean config.
 992              unset_all_config_for_plugin('portfolio_picasa');
 993          }
 994  
 995          upgrade_main_savepoint(true, 2021091700.02);
 996      }
 997  
 998      if ($oldversion < 2021091700.03) {
 999          // If repository_picasa is no longer present, remove it.
1000          if (!file_exists($CFG->dirroot . '/repository/picasa/version.php')) {
1001              $instance = $DB->get_record('repository', ['type' => 'picasa']);
1002              if (!empty($instance)) {
1003                  // Remove all records from repository_instance_config table.
1004                  $DB->delete_records('repository_instance_config', ['instanceid' => $instance->id]);
1005                  // Remove all records from repository_instances table.
1006                  $DB->delete_records('repository_instances', ['typeid' => $instance->id]);
1007                  // Remove the record from the repository table.
1008                  $DB->delete_records('repository', ['id' => $instance->id]);
1009              }
1010  
1011              // Clean config.
1012              unset_all_config_for_plugin('picasa');
1013  
1014              // Remove orphaned files.
1015              upgrade_delete_orphaned_file_records();
1016          }
1017  
1018          upgrade_main_savepoint(true, 2021091700.03);
1019      }
1020  
1021      if ($oldversion < 2021091700.04) {
1022          // Remove media_swf (unless it has manually been added back).
1023          if (!file_exists($CFG->dirroot . '/media/player/swf/classes/plugin.php')) {
1024              unset_all_config_for_plugin('media_swf');
1025          }
1026  
1027          upgrade_main_savepoint(true, 2021091700.04);
1028      }
1029  
1030      if ($oldversion < 2021092400.01) {
1031          // If tool_health is no longer present, remove it.
1032          if (!file_exists($CFG->dirroot . '/admin/tool/health/version.php')) {
1033              // Clean config.
1034              unset_all_config_for_plugin('tool_health');
1035          }
1036  
1037          // Main savepoint reached.
1038          upgrade_main_savepoint(true, 2021092400.01);
1039      }
1040  
1041      if ($oldversion < 2021092400.03) {
1042          // Remove repository_picasa configuration (unless it has manually been added back).
1043          if (!file_exists($CFG->dirroot . '/repository/picasa/version.php')) {
1044              unset_all_config_for_plugin('repository_picasa');
1045          }
1046  
1047          upgrade_main_savepoint(true, 2021092400.03);
1048      }
1049  
1050      if ($oldversion < 2021100300.01) {
1051          // Remove repository_skydrive (unless it has manually been added back).
1052          if (!file_exists($CFG->dirroot . '/repository/skydrive/lib.php')) {
1053              unset_all_config_for_plugin('repository_skydrive');
1054          }
1055  
1056          // Main savepoint reached.
1057          upgrade_main_savepoint(true, 2021100300.01);
1058      }
1059  
1060      if ($oldversion < 2021100300.02) {
1061          // Remove filter_censor (unless it has manually been added back).
1062          if (!file_exists($CFG->dirroot . '/filter/censor/filter.php')) {
1063              unset_all_config_for_plugin('filter_censor');
1064          }
1065  
1066          // Main savepoint reached.
1067          upgrade_main_savepoint(true, 2021100300.02);
1068      }
1069  
1070      if ($oldversion < 2021100600.01) {
1071          // Remove qformat_examview (unless it has manually been added back).
1072          if (!file_exists($CFG->dirroot . '/question/format/examview/format.php')) {
1073              unset_all_config_for_plugin('qformat_examview');
1074          }
1075  
1076          // Main savepoint reached.
1077          upgrade_main_savepoint(true, 2021100600.01);
1078      }
1079  
1080      if ($oldversion < 2021100600.02) {
1081          $table = new xmldb_table('course_completion_defaults');
1082  
1083          // Adding fields to table course_completion_defaults.
1084          $field = new xmldb_field('completionpassgrade', XMLDB_TYPE_INTEGER, '1', null,
1085              XMLDB_NOTNULL, null, '0', 'completionusegrade');
1086  
1087          // Conditionally launch add field for course_completion_defaults.
1088          if (!$dbman->field_exists($table, $field)) {
1089              $dbman->add_field($table, $field);
1090          }
1091  
1092          upgrade_main_savepoint(true, 2021100600.02);
1093      }
1094  
1095      if ($oldversion < 2021100600.03) {
1096          $table = new xmldb_table('course_modules');
1097  
1098          // Adding new fields to table course_module table.
1099          $field = new xmldb_field('completionpassgrade', XMLDB_TYPE_INTEGER, '1', null,
1100              XMLDB_NOTNULL, null, '0', 'completionexpected');
1101          // Conditionally launch create table for course_completion_defaults.
1102          if (!$dbman->field_exists($table, $field)) {
1103              $dbman->add_field($table, $field);
1104          }
1105  
1106          upgrade_main_savepoint(true, 2021100600.03);
1107      }
1108  
1109      if ($oldversion < 2021100600.04) {
1110          // Define index itemtype-mod-inst-course (not unique) to be added to grade_items.
1111          $table = new xmldb_table('grade_items');
1112          $index = new xmldb_index('itemtype-mod-inst-course', XMLDB_INDEX_NOTUNIQUE,
1113              ['itemtype', 'itemmodule', 'iteminstance', 'courseid']);
1114  
1115          // Conditionally launch add index itemtype-mod-inst-course.
1116          if (!$dbman->index_exists($table, $index)) {
1117              $dbman->add_index($table, $index);
1118          }
1119  
1120          // Main savepoint reached.
1121          upgrade_main_savepoint(true, 2021100600.04);
1122      }
1123  
1124      if ($oldversion < 2021101900.01) {
1125          $table = new xmldb_table('reportbuilder_report');
1126  
1127          // Define field name to be added to reportbuilder_report.
1128          $field = new xmldb_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'id');
1129          if (!$dbman->field_exists($table, $field)) {
1130              $dbman->add_field($table, $field);
1131          }
1132  
1133          // Define field conditiondata to be added to reportbuilder_report.
1134          $field = new xmldb_field('conditiondata', XMLDB_TYPE_TEXT, null, null, null, null, null, 'type');
1135          if (!$dbman->field_exists($table, $field)) {
1136              $dbman->add_field($table, $field);
1137          }
1138  
1139          // Define table reportbuilder_column to be created.
1140          $table = new xmldb_table('reportbuilder_column');
1141  
1142          // Adding fields to table reportbuilder_column.
1143          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1144          $table->add_field('reportid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1145          $table->add_field('uniqueidentifier', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
1146          $table->add_field('aggregation', XMLDB_TYPE_CHAR, '32', null, null, null, null);
1147          $table->add_field('heading', XMLDB_TYPE_CHAR, '255', null, null, null, null);
1148          $table->add_field('columnorder', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1149          $table->add_field('sortenabled', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0');
1150          $table->add_field('sortdirection', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, null);
1151          $table->add_field('sortorder', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
1152          $table->add_field('usercreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1153          $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1154          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1155          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1156  
1157          // Adding keys to table reportbuilder_column.
1158          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
1159          $table->add_key('reportid', XMLDB_KEY_FOREIGN, ['reportid'], 'reportbuilder_report', ['id']);
1160          $table->add_key('usercreated', XMLDB_KEY_FOREIGN, ['usercreated'], 'user', ['id']);
1161          $table->add_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']);
1162  
1163          // Conditionally launch create table for reportbuilder_column.
1164          if (!$dbman->table_exists($table)) {
1165              $dbman->create_table($table);
1166          }
1167  
1168          // Define table reportbuilder_filter to be created.
1169          $table = new xmldb_table('reportbuilder_filter');
1170  
1171          // Adding fields to table reportbuilder_filter.
1172          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1173          $table->add_field('reportid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1174          $table->add_field('uniqueidentifier', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
1175          $table->add_field('heading', XMLDB_TYPE_CHAR, '255', null, null, null, null);
1176          $table->add_field('iscondition', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0');
1177          $table->add_field('filterorder', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1178          $table->add_field('usercreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1179          $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1180          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1181          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1182  
1183          // Adding keys to table reportbuilder_filter.
1184          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
1185          $table->add_key('reportid', XMLDB_KEY_FOREIGN, ['reportid'], 'reportbuilder_report', ['id']);
1186          $table->add_key('usercreated', XMLDB_KEY_FOREIGN, ['usercreated'], 'user', ['id']);
1187          $table->add_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']);
1188  
1189          // Conditionally launch create table for reportbuilder_filter.
1190          if (!$dbman->table_exists($table)) {
1191              $dbman->create_table($table);
1192          }
1193  
1194          // Main savepoint reached.
1195          upgrade_main_savepoint(true, 2021101900.01);
1196      }
1197  
1198      if ($oldversion < 2021102600.01) {
1199          // Remove block_quiz_results (unless it has manually been added back).
1200          if (!file_exists($CFG->dirroot . '/blocks/quiz_result/block_quiz_results.php')) {
1201              // Delete instances.
1202              $instances = $DB->get_records_list('block_instances', 'blockname', ['quiz_results']);
1203              $instanceids = array_keys($instances);
1204  
1205              if (!empty($instanceids)) {
1206                  blocks_delete_instances($instanceids);
1207              }
1208  
1209              // Delete the block from the block table.
1210              $DB->delete_records('block', array('name' => 'quiz_results'));
1211  
1212              // Remove capabilities.
1213              capabilities_cleanup('block_quiz_results');
1214              // Clean config.
1215              unset_all_config_for_plugin('block_quiz_results');
1216  
1217              // Remove Moodle-level quiz_results based capabilities.
1218              $capabilitiestoberemoved = ['block/quiz_results:addinstance'];
1219              // Delete any role_capabilities for the old roles.
1220              $DB->delete_records_list('role_capabilities', 'capability', $capabilitiestoberemoved);
1221              // Delete the capability itself.
1222              $DB->delete_records_list('capabilities', 'name', $capabilitiestoberemoved);
1223          }
1224  
1225          upgrade_main_savepoint(true, 2021102600.01);
1226      }
1227  
1228      if ($oldversion < 2021102900.02) {
1229          // If portfolio_boxnet is no longer present, remove it.
1230          if (!file_exists($CFG->dirroot . '/portfolio/boxnet/version.php')) {
1231              $instance = $DB->get_record('portfolio_instance', ['plugin' => 'boxnet']);
1232              if (!empty($instance)) {
1233                  // Remove all records from portfolio_instance_config.
1234                  $DB->delete_records('portfolio_instance_config', ['instance' => $instance->id]);
1235                  // Remove all records from portfolio_instance_user.
1236                  $DB->delete_records('portfolio_instance_user', ['instance' => $instance->id]);
1237                  // Remove all records from portfolio_log.
1238                  $DB->delete_records('portfolio_log', ['portfolio' => $instance->id]);
1239                  // Remove all records from portfolio_tempdata.
1240                  $DB->delete_records('portfolio_tempdata', ['instance' => $instance->id]);
1241                  // Remove the record from the portfolio_instance table.
1242                  $DB->delete_records('portfolio_instance', ['id' => $instance->id]);
1243              }
1244  
1245              // Clean config.
1246              unset_all_config_for_plugin('portfolio_boxnet');
1247          }
1248  
1249          // If repository_boxnet is no longer present, remove it.
1250          if (!file_exists($CFG->dirroot . '/repository/boxnet/version.php')) {
1251              $instance = $DB->get_record('repository', ['type' => 'boxnet']);
1252              if (!empty($instance)) {
1253                  // Remove all records from repository_instance_config table.
1254                  $DB->delete_records('repository_instance_config', ['instanceid' => $instance->id]);
1255                  // Remove all records from repository_instances table.
1256                  $DB->delete_records('repository_instances', ['typeid' => $instance->id]);
1257                  // Remove the record from the repository table.
1258                  $DB->delete_records('repository', ['id' => $instance->id]);
1259              }
1260  
1261              // Clean config.
1262              unset_all_config_for_plugin('repository_boxnet');
1263  
1264              // The boxnet repository plugin stores some config in 'boxnet' incorrectly.
1265              unset_all_config_for_plugin('boxnet');
1266  
1267              // Remove orphaned files.
1268              upgrade_delete_orphaned_file_records();
1269          }
1270  
1271          upgrade_main_savepoint(true, 2021102900.02);
1272      }
1273  
1274      if ($oldversion < 2021110100.00) {
1275  
1276          // Define table reportbuilder_audience to be created.
1277          $table = new xmldb_table('reportbuilder_audience');
1278  
1279          // Adding fields to table reportbuilder_audience.
1280          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1281          $table->add_field('reportid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1282          $table->add_field('classname', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
1283          $table->add_field('configdata', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
1284          $table->add_field('usercreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1285          $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1286          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1287          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1288  
1289          // Adding keys to table reportbuilder_audience.
1290          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
1291          $table->add_key('reportid', XMLDB_KEY_FOREIGN, ['reportid'], 'reportbuilder_report', ['id']);
1292          $table->add_key('usercreated', XMLDB_KEY_FOREIGN, ['usercreated'], 'user', ['id']);
1293          $table->add_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']);
1294  
1295          // Conditionally launch create table for reportbuilder_audience.
1296          if (!$dbman->table_exists($table)) {
1297              $dbman->create_table($table);
1298          }
1299  
1300          // Main savepoint reached.
1301          upgrade_main_savepoint(true, 2021110100.00);
1302      }
1303  
1304      if ($oldversion < 2021110800.02) {
1305          // Define a field 'downloadcontent' in the 'course_modules' table.
1306          $table = new xmldb_table('course_modules');
1307          $field = new xmldb_field('downloadcontent', XMLDB_TYPE_INTEGER, '1', null, null, null, 1, 'deletioninprogress');
1308  
1309          // Conditionally launch add field 'downloadcontent'.
1310          if (!$dbman->field_exists($table, $field)) {
1311              $dbman->add_field($table, $field);
1312          }
1313  
1314          // Main savepoint reached.
1315          upgrade_main_savepoint(true, 2021110800.02);
1316      }
1317  
1318      if ($oldversion < 2021110800.03) {
1319  
1320          // Define field settingsdata to be added to reportbuilder_report.
1321          $table = new xmldb_table('reportbuilder_report');
1322          $field = new xmldb_field('settingsdata', XMLDB_TYPE_TEXT, null, null, null, null, null, 'conditiondata');
1323  
1324          // Conditionally launch add field settingsdata.
1325          if (!$dbman->field_exists($table, $field)) {
1326              $dbman->add_field($table, $field);
1327          }
1328  
1329          // Main savepoint reached.
1330          upgrade_main_savepoint(true, 2021110800.03);
1331      }
1332  
1333      if ($oldversion < 2021111700.00) {
1334          $mycoursespage = new stdClass();
1335          $mycoursespage->userid = null;
1336          $mycoursespage->name = '__courses';
1337          $mycoursespage->private = 0;
1338          $mycoursespage->sortorder  = 0;
1339          $DB->insert_record('my_pages', $mycoursespage);
1340  
1341          upgrade_main_savepoint(true, 2021111700.00);
1342      }
1343  
1344      if ($oldversion < 2021111700.01) {
1345  
1346          // Define field uniquerows to be added to reportbuilder_report.
1347          $table = new xmldb_table('reportbuilder_report');
1348          $field = new xmldb_field('uniquerows', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'type');
1349  
1350          // Conditionally launch add field uniquerows.
1351          if (!$dbman->field_exists($table, $field)) {
1352              $dbman->add_field($table, $field);
1353          }
1354  
1355          // Main savepoint reached.
1356          upgrade_main_savepoint(true, 2021111700.01);
1357      }
1358  
1359      if ($oldversion < 2021120100.01) {
1360  
1361          // Get current configuration data.
1362          $currentcustomusermenuitems = str_replace(["\r\n", "\r"], "\n", $CFG->customusermenuitems);
1363          $lines = explode("\n", $currentcustomusermenuitems);
1364          $lines = array_map('trim', $lines);
1365          $calendarcustomusermenu = 'calendar,core_calendar|/calendar/view.php?view=month|i/calendar';
1366  
1367          if (!in_array($calendarcustomusermenu, $lines)) {
1368              // Add Calendar item to the menu.
1369              array_splice($lines, 1, 0, [$calendarcustomusermenu]);
1370              set_config('customusermenuitems', implode("\n", $lines));
1371          }
1372  
1373          // Main savepoint reached.
1374          upgrade_main_savepoint(true, 2021120100.01);
1375      }
1376  
1377      if ($oldversion < 2021121400.01) {
1378          // The $CFG->grade_navmethod setting has been removed because it's not required anymore. This setting was used
1379          // to set the type of navigation (tabs or dropdown box) which will be displayed in gradebook. However, these
1380          // navigation methods are no longer used and replaced with tertiary navigation.
1381          unset_config('grade_navmethod');
1382  
1383          // Main savepoint reached.
1384          upgrade_main_savepoint(true, 2021121400.01);
1385      }
1386  
1387      if ($oldversion < 2021121700.01) {
1388          // Get current support email setting value.
1389          $config = get_config('moodle', 'supportemail');
1390  
1391          // Check if support email setting is empty and then set it to null.
1392          // We must do that so the setting is displayed during the upgrade.
1393          if (empty($config)) {
1394              set_config('supportemail', null);
1395          }
1396  
1397          // Main savepoint reached.
1398          upgrade_main_savepoint(true, 2021121700.01);
1399      }
1400  
1401      if ($oldversion < 2021122100.00) {
1402          // Get current configuration data.
1403          $currentcustomusermenuitems = str_replace(["\r\n", "\r"], "\n", $CFG->customusermenuitems);
1404  
1405          // The old default customusermenuitems config for 3.11 and below.
1406          $oldcustomusermenuitems = 'grades,grades|/grade/report/mygrades.php|t/grades
1407  calendar,core_calendar|/calendar/view.php?view=month|i/calendar
1408  messages,message|/message/index.php|t/message
1409  preferences,moodle|/user/preferences.php|t/preferences';
1410  
1411          // Check if the current customusermenuitems config matches the old customusermenuitems config.
1412          $samecustomusermenuitems = $currentcustomusermenuitems == $oldcustomusermenuitems;
1413          if ($samecustomusermenuitems) {
1414              // If the site is still using the old defaults, upgrade to the new default.
1415              $newcustomusermenuitems = 'profile,moodle|/user/profile.php
1416  grades,grades|/grade/report/mygrades.php
1417  calendar,core_calendar|/calendar/view.php?view=month
1418  privatefiles,moodle|/user/files.php';
1419              // Set the new configuration back.
1420              set_config('customusermenuitems', $newcustomusermenuitems);
1421          } else {
1422              // If the site is not using the old defaults, only add necessary entries.
1423              $lines = preg_split('/\n/', $currentcustomusermenuitems, -1, PREG_SPLIT_NO_EMPTY);
1424              $lines = array_map(static function(string $line): string {
1425                  // Previous format was "<langstring>|<url>[|<pixicon>]" - pix icon is no longer supported.
1426                  $lineparts = explode('|', trim($line), 3);
1427                  // Return first two parts of line.
1428                  return implode('|', array_slice($lineparts, 0, 2));
1429              }, $lines);
1430  
1431              // Remove the Preference entry from the menu to prevent duplication
1432              // since it will be added again in user_get_user_navigation_info().
1433              $lines = array_filter($lines, function($value) {
1434                  return strpos($value, 'preferences,moodle|/user/preferences.php') === false;
1435              });
1436  
1437              $matches = preg_grep('/\|\/user\/files.php/i', $lines);
1438              if (!$matches) {
1439                  // Add the Private files entry to the menu.
1440                  $lines[] = 'privatefiles,moodle|/user/files.php';
1441              }
1442  
1443              $matches = preg_grep('/\|\/user\/profile.php/i', $lines);
1444              if (!$matches) {
1445                  // Add the Profile entry to top of the menu.
1446                  array_unshift($lines, 'profile,moodle|/user/profile.php');
1447              }
1448  
1449              // Set the new configuration back.
1450              set_config('customusermenuitems', implode("\n", $lines));
1451          }
1452  
1453          // Main savepoint reached.
1454          upgrade_main_savepoint(true, 2021122100.00);
1455      }
1456  
1457  
1458      if ($oldversion < 2021122100.01) {
1459  
1460          // Define field heading to be added to reportbuilder_audience.
1461          $table = new xmldb_table('reportbuilder_audience');
1462          $field = new xmldb_field('heading', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'reportid');
1463  
1464          // Conditionally launch add field heading.
1465          if (!$dbman->field_exists($table, $field)) {
1466              $dbman->add_field($table, $field);
1467          }
1468  
1469          // Main savepoint reached.
1470          upgrade_main_savepoint(true, 2021122100.01);
1471      }
1472  
1473      if ($oldversion < 2021122100.02) {
1474  
1475          // Define table reportbuilder_schedule to be created.
1476          $table = new xmldb_table('reportbuilder_schedule');
1477  
1478          // Adding fields to table reportbuilder_schedule.
1479          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1480          $table->add_field('reportid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1481          $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
1482          $table->add_field('enabled', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1');
1483          $table->add_field('audiences', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
1484          $table->add_field('format', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
1485          $table->add_field('subject', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
1486          $table->add_field('message', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
1487          $table->add_field('messageformat', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1488          $table->add_field('userviewas', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1489          $table->add_field('timescheduled', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1490          $table->add_field('recurrence', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1491          $table->add_field('reportempty', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1492          $table->add_field('timelastsent', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1493          $table->add_field('timenextsend', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1494          $table->add_field('usercreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1495          $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1496          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1497          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1498  
1499          // Adding keys to table reportbuilder_schedule.
1500          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
1501          $table->add_key('reportid', XMLDB_KEY_FOREIGN, ['reportid'], 'reportbuilder_report', ['id']);
1502          $table->add_key('userviewas', XMLDB_KEY_FOREIGN, ['userviewas'], 'user', ['id']);
1503          $table->add_key('usercreated', XMLDB_KEY_FOREIGN, ['usercreated'], 'user', ['id']);
1504          $table->add_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']);
1505  
1506          // Conditionally launch create table for reportbuilder_schedule.
1507          if (!$dbman->table_exists($table)) {
1508              $dbman->create_table($table);
1509          }
1510  
1511          // Main savepoint reached.
1512          upgrade_main_savepoint(true, 2021122100.02);
1513      }
1514  
1515      if ($oldversion < 2021123000.01) {
1516          // The tool_admin_presets tables have been moved to core, because core_adminpresets component has been created, so
1517          // it can interact with the rest of core.
1518          // So the tool_admin_presetsXXX tables will be renamed to adminipresetsXXX if they exists; otherwise, they will be created.
1519  
1520          $tooltable = new xmldb_table('tool_admin_presets');
1521          $table = new xmldb_table('adminpresets');
1522          if ($dbman->table_exists($tooltable)) {
1523              $dbman->rename_table($tooltable, 'adminpresets');
1524          } else if (!$dbman->table_exists($table)) {
1525              // Adding fields to table adminpresets.
1526              $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1527              $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1528              $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
1529              $table->add_field('comments', XMLDB_TYPE_TEXT, null, null, null, null, null);
1530              $table->add_field('site', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
1531              $table->add_field('author', XMLDB_TYPE_CHAR, '255', null, null, null, null);
1532              $table->add_field('moodleversion', XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, null);
1533              $table->add_field('moodlerelease', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
1534              $table->add_field('iscore', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0');
1535              $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1536              $table->add_field('timeimported', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1537  
1538              // Adding keys to table adminpresets.
1539              $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
1540  
1541              // Launch create table for adminpresets.
1542              $dbman->create_table($table);
1543          }
1544  
1545          $tooltable = new xmldb_table('tool_admin_presets_it');
1546          $table = new xmldb_table('adminpresets_it');
1547          if ($dbman->table_exists($tooltable)) {
1548              $dbman->rename_table($tooltable, 'adminpresets_it');
1549          } else if (!$dbman->table_exists($table)) {
1550              // Adding fields to table adminpresets_it.
1551              $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1552              $table->add_field('adminpresetid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1553              $table->add_field('plugin', XMLDB_TYPE_CHAR, '100', null, null, null, null);
1554              $table->add_field('name', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
1555              $table->add_field('value', XMLDB_TYPE_TEXT, null, null, null, null, null);
1556  
1557              // Adding keys to table adminpresets_it.
1558              $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
1559  
1560              // Adding indexes to table adminpresets_it.
1561              $table->add_index('adminpresetid', XMLDB_INDEX_NOTUNIQUE, ['adminpresetid']);
1562  
1563              // Launch create table for adminpresets_it.
1564              $dbman->create_table($table);
1565          }
1566  
1567          $tooltable = new xmldb_table('tool_admin_presets_it_a');
1568          $table = new xmldb_table('adminpresets_it_a');
1569          if ($dbman->table_exists($tooltable)) {
1570              $dbman->rename_table($tooltable, 'adminpresets_it_a');
1571          } else if (!$dbman->table_exists($table)) {
1572              // Adding fields to table adminpresets_it_a.
1573              $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1574              $table->add_field('itemid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1575              $table->add_field('name', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
1576              $table->add_field('value', XMLDB_TYPE_TEXT, null, null, null, null, null);
1577  
1578              // Adding keys to table adminpresets_it_a.
1579              $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
1580  
1581              // Adding indexes to table adminpresets_it_a.
1582              $table->add_index('itemid', XMLDB_INDEX_NOTUNIQUE, ['itemid']);
1583  
1584              // Launch create table for adminpresets_it_a.
1585              $dbman->create_table($table);
1586          }
1587  
1588          $tooltable = new xmldb_table('tool_admin_presets_app');
1589          $table = new xmldb_table('adminpresets_app');
1590          if ($dbman->table_exists($tooltable)) {
1591              $dbman->rename_table($tooltable, 'adminpresets_app');
1592          } else if (!$dbman->table_exists($table)) {
1593              // Adding fields to table adminpresets_app.
1594              $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1595              $table->add_field('adminpresetid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1596              $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1597              $table->add_field('time', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1598  
1599              // Adding keys to table adminpresets_app.
1600              $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
1601  
1602              // Adding indexes to table adminpresets_app.
1603              $table->add_index('adminpresetid', XMLDB_INDEX_NOTUNIQUE, ['adminpresetid']);
1604  
1605              // Launch create table for adminpresets_app.
1606              $dbman->create_table($table);
1607          }
1608  
1609          $tooltable = new xmldb_table('tool_admin_presets_app_it');
1610          $table = new xmldb_table('adminpresets_app_it');
1611          if ($dbman->table_exists($tooltable)) {
1612              $dbman->rename_table($tooltable, 'adminpresets_app_it');
1613          } else if (!$dbman->table_exists($table)) {
1614              // Adding fields to table adminpresets_app_it.
1615              $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1616              $table->add_field('adminpresetapplyid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1617              $table->add_field('configlogid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1618  
1619              // Adding keys to table adminpresets_app_it.
1620              $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
1621  
1622              // Adding indexes to table adminpresets_app_it.
1623              $table->add_index('configlogid', XMLDB_INDEX_NOTUNIQUE, ['configlogid']);
1624              $table->add_index('adminpresetapplyid', XMLDB_INDEX_NOTUNIQUE, ['adminpresetapplyid']);
1625  
1626              // Launch create table for adminpresets_app_it.
1627              $dbman->create_table($table);
1628          }
1629  
1630          $tooltable = new xmldb_table('tool_admin_presets_app_it_a');
1631          $table = new xmldb_table('adminpresets_app_it_a');
1632          if ($dbman->table_exists($tooltable)) {
1633              $dbman->rename_table($tooltable, 'adminpresets_app_it_a');
1634          } else if (!$dbman->table_exists($table)) {
1635              // Adding fields to table adminpresets_app_it_a.
1636              $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1637              $table->add_field('adminpresetapplyid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1638              $table->add_field('configlogid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1639              $table->add_field('itemname', XMLDB_TYPE_CHAR, '100', null, null, null, null);
1640  
1641              // Adding keys to table adminpresets_app_it_a.
1642              $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
1643  
1644              // Adding indexes to table adminpresets_app_it_a.
1645              $table->add_index('configlogid', XMLDB_INDEX_NOTUNIQUE, ['configlogid']);
1646              $table->add_index('adminpresetapplyid', XMLDB_INDEX_NOTUNIQUE, ['adminpresetapplyid']);
1647  
1648              // Launch create table for adminpresets_app_it_a.
1649              $dbman->create_table($table);
1650          }
1651  
1652          $tooltable = new xmldb_table('tool_admin_presets_plug');
1653          $table = new xmldb_table('adminpresets_plug');
1654          if ($dbman->table_exists($tooltable)) {
1655              $dbman->rename_table($tooltable, 'adminpresets_plug');
1656          } else if (!$dbman->table_exists($table)) {
1657              // Adding fields to table adminpresets_plug.
1658              $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1659              $table->add_field('adminpresetid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1660              $table->add_field('plugin', XMLDB_TYPE_CHAR, '100', null, null, null, null);
1661              $table->add_field('name', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
1662              $table->add_field('enabled', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0');
1663  
1664              // Adding keys to table adminpresets_plug.
1665              $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
1666  
1667              // Adding indexes to table adminpresets_plug.
1668              $table->add_index('adminpresetid', XMLDB_INDEX_NOTUNIQUE, ['adminpresetid']);
1669  
1670              // Launch create table for adminpresets_plug.
1671              $dbman->create_table($table);
1672          }
1673  
1674          $tooltable = new xmldb_table('tool_admin_presets_app_plug');
1675          $table = new xmldb_table('adminpresets_app_plug');
1676          if ($dbman->table_exists($tooltable)) {
1677              $dbman->rename_table($tooltable, 'adminpresets_app_plug');
1678          } else if (!$dbman->table_exists($table)) {
1679              // Adding fields to table adminpresets_app_plug.
1680              $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1681              $table->add_field('adminpresetapplyid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
1682              $table->add_field('plugin', XMLDB_TYPE_CHAR, '100', null, null, null, null);
1683              $table->add_field('name', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
1684              $table->add_field('value', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0');
1685              $table->add_field('oldvalue', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0');
1686  
1687              // Adding keys to table adminpresets_app_plug.
1688              $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
1689  
1690              // Adding indexes to table adminpresets_app_plug.
1691              $table->add_index('adminpresetapplyid', XMLDB_INDEX_NOTUNIQUE, ['adminpresetapplyid']);
1692  
1693              // Launch create table for adminpresets_app_plug.
1694              if (!$dbman->table_exists($table)) {
1695                  $dbman->create_table($table);
1696              }
1697          }
1698  
1699          if ($DB->count_records('adminpresets', ['iscore' => 1]) == 0) {
1700              // Create default core site admin presets.
1701              require_once($CFG->dirroot . '/admin/presets/classes/helper.php');
1702              \core_adminpresets\helper::create_default_presets();
1703          }
1704  
1705          // Main savepoint reached.
1706          upgrade_main_savepoint(true, 2021123000.01);
1707      }
1708  
1709      if ($oldversion < 2021123000.02) {
1710          // If exists, migrate sensiblesettings admin settings from tool_admin_preset to adminpresets.
1711          if (get_config('tool_admin_presets', 'sensiblesettings') !== false) {
1712              set_config('sensiblesettings', get_config('tool_admin_presets', 'sensiblesettings'), 'adminpresets');
1713              unset_config('sensiblesettings', 'tool_admin_presets');
1714          }
1715  
1716          // Main savepoint reached.
1717          upgrade_main_savepoint(true, 2021123000.02);
1718      }
1719  
1720      if ($oldversion < 2021123000.03) {
1721          // If exists, migrate lastpresetapplied setting from tool_admin_preset to adminpresets.
1722          if (get_config('tool_admin_presets', 'lastpresetapplied') !== false) {
1723              set_config('lastpresetapplied', get_config('tool_admin_presets', 'lastpresetapplied'), 'adminpresets');
1724              unset_config('lastpresetapplied', 'tool_admin_presets');
1725          }
1726  
1727          // Main savepoint reached.
1728          upgrade_main_savepoint(true, 2021123000.03);
1729      }
1730  
1731      if ($oldversion < 2022011100.01) {
1732          // The following blocks have been hidden by default, so they shouldn't be enabled in the Full core preset: Course/site
1733          // summary, RSS feeds, Self completion and Feedback.
1734          $params = ['name' => get_string('fullpreset', 'core_adminpresets')];
1735          $fullpreset = $DB->get_record_select('adminpresets', 'name = :name and iscore > 0', $params);
1736  
1737          if (!$fullpreset) {
1738              // Full admin preset might have been created using the English name.
1739              $name = get_string_manager()->get_string('fullpreset', 'core_adminpresets', null, 'en');
1740              $params['name'] = $name;
1741              $fullpreset = $DB->get_record_select('adminpresets', 'name = :name and iscore > 0', $params);
1742          }
1743          if (!$fullpreset) {
1744              // We tried, but we didn't find full by name. Let's find a core preset that sets 'usecomments' setting to 1.
1745              $sql = "SELECT preset.*
1746                        FROM {adminpresets} preset
1747                  INNER JOIN {adminpresets_it} it ON preset.id = it.adminpresetid
1748                       WHERE it.name = :name AND it.value = :value AND preset.iscore > 0";
1749              $params = ['name' => 'usecomments', 'value' => '1'];
1750              $fullpreset = $DB->get_record_sql($sql, $params);
1751          }
1752  
1753          if ($fullpreset) {
1754              $blocknames = ['course_summary', 'feedback', 'rss_client', 'selfcompletion'];
1755              list($blocksinsql, $blocksinparams) = $DB->get_in_or_equal($blocknames);
1756  
1757              // Remove entries from the adminpresets_app_plug table (in case the preset has been applied).
1758              $appliedpresets = $DB->get_records('adminpresets_app', ['adminpresetid' => $fullpreset->id], '', 'id');
1759              if ($appliedpresets) {
1760                  list($appsinsql, $appsinparams) = $DB->get_in_or_equal(array_keys($appliedpresets));
1761                  $sql = "adminpresetapplyid $appsinsql AND plugin='block' AND name $blocksinsql";
1762                  $params = array_merge($appsinparams, $blocksinparams);
1763                  $DB->delete_records_select('adminpresets_app_plug', $sql, $params);
1764              }
1765  
1766              // Remove entries for these blocks from the adminpresets_plug table.
1767              $sql = "adminpresetid = ? AND plugin='block' AND name $blocksinsql";
1768              $params = array_merge([$fullpreset->id], $blocksinparams);
1769              $DB->delete_records_select('adminpresets_plug', $sql, $params);
1770          }
1771  
1772          // Main savepoint reached.
1773          upgrade_main_savepoint(true, 2022011100.01);
1774      }
1775  
1776      if ($oldversion < 2022012100.02) {
1777          // Migrate default message output config.
1778          $preferences = get_config('message');
1779  
1780          $treatedprefs = [];
1781  
1782          foreach ($preferences as $preference => $value) {
1783              // Extract provider and preference name from the setting name.
1784              // Example name: airnotifier_provider_enrol_imsenterprise_imsenterprise_enrolment_permitted
1785              // Provider: airnotifier
1786              // Preference: enrol_imsenterprise_imsenterprise_enrolment_permitted.
1787              $providerparts = explode('_provider_', $preference);
1788              if (count($providerparts) <= 1) {
1789                  continue;
1790              }
1791  
1792              $provider = $providerparts[0];
1793              $preference = $providerparts[1];
1794  
1795              // Extract and remove last part of the preference previously extracted: ie. permitted.
1796              $parts = explode('_', $preference);
1797              $key = array_pop($parts);
1798  
1799              if (in_array($key, ['permitted', 'loggedin', 'loggedoff'])) {
1800                  if ($key == 'permitted') {
1801                      // We will use provider name instead of permitted.
1802                      $key = $provider;
1803                  } else {
1804                      // Logged in and logged off values are a csv of the enabled providers.
1805                      $value = explode(',', $value);
1806                  }
1807  
1808                  // Join the rest of the parts: ie enrol_imsenterprise_imsenterprise_enrolment.
1809                  $prefname = implode('_', $parts);
1810  
1811                  if (!isset($treatedprefs[$prefname])) {
1812                      $treatedprefs[$prefname] = [];
1813                  }
1814  
1815                  // Save the value with the selected key.
1816                  $treatedprefs[$prefname][$key] = $value;
1817              }
1818          }
1819  
1820          // Now take every preference previous treated and its values.
1821          foreach ($treatedprefs as $prefname => $values) {
1822              $enabled = []; // List of providers enabled for each preference.
1823  
1824              // Enable if one of those is enabled.
1825              $loggedin = isset($values['loggedin']) ? $values['loggedin'] : [];
1826              foreach ($loggedin as $provider) {
1827                  $enabled[$provider] = 1;
1828              }
1829              $loggedoff = isset($values['loggedoff']) ? $values['loggedoff'] : [];
1830              foreach ($loggedoff as $provider) {
1831                  $enabled[$provider] = 1;
1832              }
1833  
1834              // Do not treat those values again.
1835              unset($values['loggedin']);
1836              unset($values['loggedoff']);
1837  
1838              // Translate rest of values coming from permitted "key".
1839              foreach ($values as $provider => $value) {
1840                  $locked = false;
1841  
1842                  switch ($value) {
1843                      case 'forced':
1844                          // Provider is enabled by force.
1845                          $enabled[$provider] = 1;
1846                          $locked = true;
1847                          break;
1848                      case 'disallowed':
1849                          // Provider is disabled by force.
1850                          unset($enabled[$provider]);
1851                          $locked = true;
1852                          break;
1853                      default:
1854                          // Provider is not forced (permitted) or invalid values.
1855                  }
1856  
1857                  // Save locked.
1858                  if ($locked) {
1859                      set_config($provider.'_provider_'.$prefname.'_locked', 1, 'message');
1860                  } else {
1861                      set_config($provider.'_provider_'.$prefname.'_locked', 0, 'message');
1862                  }
1863                  // Remove old value.
1864                  unset_config($provider.'_provider_'.$prefname.'_permitted', 'message');
1865              }
1866  
1867              // Save the new values.
1868              $value = implode(',', array_keys($enabled));
1869              set_config('message_provider_'.$prefname.'_enabled', $value, 'message');
1870              // Remove old values.
1871              unset_config('message_provider_'.$prefname.'_loggedin', 'message');
1872              unset_config('message_provider_'.$prefname.'_loggedoff', 'message');
1873          }
1874  
1875          // Migrate user preferences. ie merging message_provider_moodle_instantmessage_loggedoff with
1876          // message_provider_moodle_instantmessage_loggedin to message_provider_moodle_instantmessage_enabled.
1877  
1878          $allrecordsloggedoff = $DB->sql_like('name', ':loggedoff');
1879          $total = $DB->count_records_select(
1880              'user_preferences',
1881              $allrecordsloggedoff,
1882              ['loggedoff' => 'message_provider_%_loggedoff']
1883          );
1884          $i = 0;
1885          if ($total == 0) {
1886              $total = 1; // Avoid division by zero.
1887          }
1888  
1889          // Show a progress bar.
1890          $pbar = new progress_bar('upgradeusernotificationpreferences', 500, true);
1891          $pbar->update($i, $total, "Upgrading user notifications preferences - $i/$total.");
1892  
1893          // We're migrating provider per provider to reduce memory usage.
1894          $providers = $DB->get_records('message_providers', null, 'name');
1895          foreach ($providers as $provider) {
1896              // 60 minutes to migrate each provider.
1897              upgrade_set_timeout(3600);
1898              $componentproviderbase = 'message_provider_'.$provider->component.'_'.$provider->name;
1899  
1900              $loggedinname = $componentproviderbase.'_loggedin';
1901              $loggedoffname = $componentproviderbase.'_loggedoff';
1902  
1903              // Change loggedin to enabled.
1904              $enabledname = $componentproviderbase.'_enabled';
1905              $DB->set_field('user_preferences', 'name', $enabledname, ['name' => $loggedinname]);
1906  
1907              $selectparams = [
1908                  'enabled' => $enabledname,
1909                  'loggedoff' => $loggedoffname,
1910              ];
1911              $sql = 'SELECT m1.id loggedoffid, m1.value as loggedoff, m2.value as enabled, m2.id as enabledid
1912                  FROM
1913                      (SELECT id, userid, value FROM {user_preferences} WHERE name = :loggedoff) m1
1914                  LEFT JOIN
1915                      (SELECT id, userid, value FROM {user_preferences} WHERE name = :enabled) m2
1916                      ON m1.userid = m2.userid';
1917  
1918              while (($rs = $DB->get_recordset_sql($sql, $selectparams, 0, 1000)) && $rs->valid()) {
1919                  // 10 minutes for every chunk.
1920                  upgrade_set_timeout(600);
1921  
1922                  $deleterecords = [];
1923                  $changename = [];
1924                  $changevalue = []; // Multidimensional array with possible values as key to reduce SQL queries.
1925                  foreach ($rs as $record) {
1926                      if (empty($record->enabledid)) {
1927                          // Enabled does not exists, change the name.
1928                          $changename[] = $record->loggedoffid;
1929                      } else if ($record->enabledid != $record->loggedoff) {
1930                          // Exist and values differ (checked on SQL), update the enabled record.
1931  
1932                          if ($record->enabled != 'none' && !empty($record->enabled)) {
1933                              $enabledvalues = explode(',', $record->enabled);
1934                          } else {
1935                              $enabledvalues = [];
1936                          }
1937  
1938                          if ($record->loggedoff != 'none' && !empty($record->loggedoff)) {
1939                              $loggedoffvalues = explode(',', $record->loggedoff);
1940                          } else {
1941                              $loggedoffvalues = [];
1942                          }
1943  
1944                          $values = array_unique(array_merge($enabledvalues, $loggedoffvalues));
1945                          sort($values);
1946  
1947                          $newvalue = empty($values) ? 'none' : implode(',', $values);
1948                          if (!isset($changevalue[$newvalue])) {
1949                              $changevalue[$newvalue] = [];
1950                          }
1951                          $changevalue[$newvalue][] = $record->enabledid;
1952  
1953                          $deleterecords[] = $record->loggedoffid;
1954                      } else {
1955                          // They are the same, just delete loggedoff one.
1956                          $deleterecords[] = $record->loggedoffid;
1957                      }
1958                      $i++;
1959                  }
1960                  $rs->close();
1961  
1962                  // Commit the changes.
1963                  if (!empty($changename)) {
1964                      $changenameparams = [
1965                          'name' => $loggedoffname,
1966                      ];
1967                      $changenameselect = 'name = :name AND id IN (' . implode(',', $changename) . ')';
1968                      $DB->set_field_select('user_preferences', 'name', $enabledname, $changenameselect, $changenameparams);
1969                  }
1970  
1971                  if (!empty($changevalue)) {
1972                      $changevalueparams = [
1973                          'name' => $enabledname,
1974                      ];
1975                      foreach ($changevalue as $value => $ids) {
1976                          $changevalueselect = 'name = :name AND id IN (' . implode(',', $ids) . ')';
1977                          $DB->set_field_select('user_preferences', 'value', $value, $changevalueselect, $changevalueparams);
1978                      }
1979                  }
1980  
1981                  if (!empty($deleterecords)) {
1982                      $deleteparams = [
1983                          'name' => $loggedoffname,
1984                      ];
1985                      $deleteselect = 'name = :name AND id IN (' . implode(',', $deleterecords) . ')';
1986                      $DB->delete_records_select('user_preferences', $deleteselect, $deleteparams);
1987                  }
1988  
1989                  // Update progress.
1990                  $pbar->update($i, $total, "Upgrading user notifications preferences - $i/$total.");
1991              }
1992              $rs->close();
1993  
1994              // Delete the rest of loggedoff values (that are equal than enabled).
1995              $deleteparams = [
1996                  'name' => $loggedoffname,
1997              ];
1998              $deleteselect = 'name = :name';
1999              $i += $DB->count_records_select('user_preferences', $deleteselect, $deleteparams);
2000              $DB->delete_records_select('user_preferences', $deleteselect, $deleteparams);
2001  
2002              // Update progress.
2003              $pbar->update($i, $total, "Upgrading user notifications preferences - $i/$total.");
2004          }
2005  
2006          core_plugin_manager::reset_caches();
2007  
2008          // Delete the orphan records.
2009          $allrecordsparams = ['loggedin' => 'message_provider_%_loggedin', 'loggedoff' => 'message_provider_%_loggedoff'];
2010          $allrecordsloggedin = $DB->sql_like('name', ':loggedin');
2011          $allrecordsloggedinoffsql = "$allrecordsloggedin OR $allrecordsloggedoff";
2012          $DB->delete_records_select('user_preferences', $allrecordsloggedinoffsql, $allrecordsparams);
2013  
2014          // Update progress.
2015          $pbar->update($total, $total, "Upgrading user notifications preferences - $total/$total.");
2016  
2017          upgrade_main_savepoint(true, 2022012100.02);
2018      }
2019  
2020      // Introduce question versioning to core.
2021      // First, create the new tables.
2022      if ($oldversion < 2022020200.01) {
2023          // Define table question_bank_entries to be created.
2024          $table = new xmldb_table('question_bank_entries');
2025  
2026          // Adding fields to table question_bank_entries.
2027          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2028          $table->add_field('questioncategoryid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 0);
2029          $table->add_field('idnumber', XMLDB_TYPE_CHAR, '100', null, null, null, null);
2030          $table->add_field('ownerid', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
2031  
2032          // Adding keys to table question_bank_entries.
2033          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
2034          $table->add_key('questioncategoryid', XMLDB_KEY_FOREIGN, ['questioncategoryid'], 'question_categories', ['id']);
2035          $table->add_key('ownerid', XMLDB_KEY_FOREIGN, ['ownerid'], 'user', ['id']);
2036  
2037          // Conditionally launch create table for question_bank_entries.
2038          if (!$dbman->table_exists($table)) {
2039              $dbman->create_table($table);
2040          }
2041  
2042          // Create category id and id number index.
2043          $index = new xmldb_index('categoryidnumber', XMLDB_INDEX_UNIQUE, ['questioncategoryid', 'idnumber']);
2044  
2045          // Conditionally launch add index categoryidnumber.
2046          if (!$dbman->index_exists($table, $index)) {
2047              $dbman->add_index($table, $index);
2048          }
2049  
2050          // Define table question_versions to be created.
2051          $table = new xmldb_table('question_versions');
2052  
2053          // Adding fields to table question_versions.
2054          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2055          $table->add_field('questionbankentryid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 0);
2056          $table->add_field('version', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 1);
2057          $table->add_field('questionid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 0);
2058          $table->add_field('status', XMLDB_TYPE_CHAR, '10', null, XMLDB_NOTNULL, null, 'ready');
2059  
2060          // Adding keys to table question_versions.
2061          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
2062          $table->add_key('questionbankentryid', XMLDB_KEY_FOREIGN, ['questionbankentryid'], 'question_bank_entries', ['id']);
2063          $table->add_key('questionid', XMLDB_KEY_FOREIGN, ['questionid'], 'question', ['id']);
2064  
2065          // Conditionally launch create table for question_versions.
2066          if (!$dbman->table_exists($table)) {
2067              $dbman->create_table($table);
2068          }
2069  
2070          // Define table question_references to be created.
2071          $table = new xmldb_table('question_references');
2072  
2073          // Adding fields to table question_references.
2074          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2075          $table->add_field('usingcontextid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 0);
2076          $table->add_field('component', XMLDB_TYPE_CHAR, '100', null, null, null, null);
2077          $table->add_field('questionarea', XMLDB_TYPE_CHAR, '50', null, null, null, null);
2078          $table->add_field('itemid', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
2079          $table->add_field('questionbankentryid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 0);
2080          $table->add_field('version', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
2081  
2082          // Adding keys to table question_references.
2083          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
2084          $table->add_key('usingcontextid', XMLDB_KEY_FOREIGN, ['usingcontextid'], 'context', ['id']);
2085          $table->add_key('questionbankentryid', XMLDB_KEY_FOREIGN, ['questionbankentryid'], 'question_bank_entries', ['id']);
2086  
2087          // Conditionally launch create table for question_references.
2088          if (!$dbman->table_exists($table)) {
2089              $dbman->create_table($table);
2090          }
2091  
2092          // Define table question_set_references to be created.
2093          $table = new xmldb_table('question_set_references');
2094  
2095          // Adding fields to table question_set_references.
2096          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2097          $table->add_field('usingcontextid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 0);
2098          $table->add_field('component', XMLDB_TYPE_CHAR, '100', null, null, null, null);
2099          $table->add_field('questionarea', XMLDB_TYPE_CHAR, '50', null, null, null, null);
2100          $table->add_field('itemid', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
2101          $table->add_field('questionscontextid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 0);
2102          $table->add_field('filtercondition', XMLDB_TYPE_TEXT, null, null, null, null, null);
2103  
2104          // Adding keys to table question_set_references.
2105          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
2106          $table->add_key('usingcontextid', XMLDB_KEY_FOREIGN, ['usingcontextid'], 'context', ['id']);
2107          $table->add_key('questionscontextid', XMLDB_KEY_FOREIGN, ['questionscontextid'], 'context', ['id']);
2108  
2109          // Conditionally launch create table for question_set_references.
2110          if (!$dbman->table_exists($table)) {
2111              $dbman->create_table($table);
2112          }
2113  
2114          // Main savepoint reached.
2115          upgrade_main_savepoint(true, 2022020200.01);
2116      }
2117  
2118      if ($oldversion < 2022020200.02) {
2119          // Define a new temporary field in the question_bank_entries tables.
2120          // Creating temporary field questionid to populate the data in question version table.
2121          // This will make sure the appropriate question id is inserted the version table without making any complex joins.
2122          $table = new xmldb_table('question_bank_entries');
2123          $field = new xmldb_field('questionid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL);
2124          if (!$dbman->field_exists($table, $field)) {
2125              $dbman->add_field($table, $field);
2126          }
2127  
2128          $transaction = $DB->start_delegated_transaction();
2129          upgrade_set_timeout(3600);
2130          // Create the data for the question_bank_entries table with, including the new temporary field.
2131          $sql = <<<EOF
2132              INSERT INTO {question_bank_entries}
2133                  (questionid, questioncategoryid, idnumber, ownerid)
2134              SELECT id, category, idnumber, createdby
2135              FROM {question} q
2136              EOF;
2137  
2138          // Inserting question_bank_entries data.
2139          $DB->execute($sql);
2140  
2141          $transaction->allow_commit();
2142  
2143          // Main savepoint reached.
2144          upgrade_main_savepoint(true, 2022020200.02);
2145      }
2146  
2147      if ($oldversion < 2022020200.03) {
2148          $transaction = $DB->start_delegated_transaction();
2149          upgrade_set_timeout(3600);
2150          // Create the question_versions using that temporary field.
2151          $sql = <<<EOF
2152              INSERT INTO {question_versions}
2153                  (questionbankentryid, questionid, status)
2154              SELECT
2155                  qbe.id,
2156                  q.id,
2157                  CASE
2158                      WHEN q.hidden > 0 THEN 'hidden'
2159                      ELSE 'ready'
2160                  END
2161              FROM {question_bank_entries} qbe
2162              INNER JOIN {question} q ON qbe.questionid = q.id
2163              EOF;
2164  
2165          // Inserting question_versions data.
2166          $DB->execute($sql);
2167  
2168          $transaction->allow_commit();
2169  
2170          // Dropping temporary field questionid.
2171          $table = new xmldb_table('question_bank_entries');
2172          $field = new xmldb_field('questionid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL);
2173          if ($dbman->field_exists($table, $field)) {
2174              $dbman->drop_field($table, $field);
2175          }
2176  
2177          // Main savepoint reached.
2178          upgrade_main_savepoint(true, 2022020200.03);
2179      }
2180  
2181      if ($oldversion < 2022020200.04) {
2182          $transaction = $DB->start_delegated_transaction();
2183          upgrade_set_timeout(3600);
2184          // Create the base data for the random questions in the set_references table.
2185          // This covers most of the hard work in one go.
2186          $concat = $DB->sql_concat("'{\"questioncategoryid\":\"'", 'q.category', "'\",\"includingsubcategories\":\"'",
2187              'qs.includingsubcategories', "'\"}'");
2188          $sql = <<<EOF
2189              INSERT INTO {question_set_references}
2190              (usingcontextid, component, questionarea, itemid, questionscontextid, filtercondition)
2191              SELECT
2192                  c.id,
2193                  'mod_quiz',
2194                  'slot',
2195                  qs.id,
2196                  qc.contextid,
2197                  $concat
2198              FROM {question} q
2199              INNER JOIN {quiz_slots} qs on q.id = qs.questionid
2200              INNER JOIN {course_modules} cm ON cm.instance = qs.quizid AND cm.module = :quizmoduleid
2201              INNER JOIN {context} c ON cm.id = c.instanceid AND c.contextlevel = :contextmodule
2202              INNER JOIN {question_categories} qc ON qc.id = q.category
2203              WHERE q.qtype = :random
2204              EOF;
2205  
2206          // Inserting question_set_references data.
2207          $DB->execute($sql, [
2208              'quizmoduleid' => $DB->get_field('modules', 'id', ['name' => 'quiz']),
2209              'contextmodule' => CONTEXT_MODULE,
2210              'random' => 'random',
2211          ]);
2212  
2213          $transaction->allow_commit();
2214  
2215          // Main savepoint reached.
2216          upgrade_main_savepoint(true, 2022020200.04);
2217      }
2218  
2219      if ($oldversion < 2022020200.05) {
2220          $transaction = $DB->start_delegated_transaction();
2221          upgrade_set_timeout(3600);
2222  
2223          // Count all the slot tags to be migrated (for progress bar).
2224          $total = $DB->count_records('quiz_slot_tags');
2225          $pbar = new progress_bar('migratequestiontags', 1000, true);
2226          $i = 0;
2227          // Updating slot_tags for random question tags.
2228          // Now fetch any quiz slot tags and update those slot details into the question_set_references.
2229          $slottags = $DB->get_recordset('quiz_slot_tags', [], 'slotid ASC');
2230  
2231          $tagstrings = [];
2232          $lastslot = null;
2233          $runinsert = function (int $lastslot, array $tagstrings) use ($DB) {
2234              $conditiondata = $DB->get_field('question_set_references', 'filtercondition',
2235                  ['itemid' => $lastslot, 'component' => 'mod_quiz', 'questionarea' => 'slot']);
2236  
2237              // It is possible to have leftover tags in the database, without a corresponding
2238              // slot, because of an old bugs (e.g. MDL-76193). Therefore, if the slot is not found,
2239              // we can safely discard these tags.
2240              if (!empty($conditiondata)) {
2241                  $condition = json_decode($conditiondata);
2242                  $condition->tags = $tagstrings;
2243                  $DB->set_field('question_set_references', 'filtercondition', json_encode($condition),
2244                          ['itemid' => $lastslot, 'component' => 'mod_quiz', 'questionarea' => 'slot']);
2245              }
2246          };
2247  
2248          foreach ($slottags as $tag) {
2249              upgrade_set_timeout(3600);
2250              if ($lastslot && $tag->slotid != $lastslot) {
2251                  if (!empty($tagstrings)) {
2252                      // Insert the data.
2253                      $runinsert($lastslot, $tagstrings);
2254                  }
2255                  // Prepare for the next slot id.
2256                  $tagstrings = [];
2257              }
2258  
2259              $lastslot = $tag->slotid;
2260              $tagstrings[] = "{$tag->tagid},{$tag->tagname}";
2261              // Update progress.
2262              $i++;
2263              $pbar->update($i, $total, "Migrating question tags - $i/$total.");
2264          }
2265          if ($tagstrings) {
2266              $runinsert($lastslot, $tagstrings);
2267          }
2268          $slottags->close();
2269  
2270          $transaction->allow_commit();
2271          // Main savepoint reached.
2272          upgrade_main_savepoint(true, 2022020200.05);
2273      }
2274  
2275      if ($oldversion < 2022020200.06) {
2276          $transaction = $DB->start_delegated_transaction();
2277          upgrade_set_timeout(3600);
2278          // Create question_references record for each question.
2279          // Except if qtype is random. That case is handled by question_set_reference.
2280          $sql = "INSERT INTO {question_references}
2281                          (usingcontextid, component, questionarea, itemid, questionbankentryid)
2282                   SELECT c.id, 'mod_quiz', 'slot', qs.id, qv.questionbankentryid
2283                     FROM {question} q
2284                     JOIN {question_versions} qv ON q.id = qv.questionid
2285                     JOIN {quiz_slots} qs ON q.id = qs.questionid
2286                     JOIN {modules} m ON m.name = 'quiz'
2287                     JOIN {course_modules} cm ON cm.module = m.id AND cm.instance = qs.quizid
2288                     JOIN {context} c ON c.instanceid = cm.id AND c.contextlevel = " . CONTEXT_MODULE . "
2289                    WHERE q.qtype <> 'random'";
2290  
2291          // Inserting question_references data.
2292          $DB->execute($sql);
2293  
2294          $transaction->allow_commit();
2295          // Main savepoint reached.
2296          upgrade_main_savepoint(true, 2022020200.06);
2297      }
2298  
2299      // Finally, drop fields from question table.
2300      if ($oldversion < 2022020200.07) {
2301          // Define fields to be dropped from questions.
2302          $table = new xmldb_table('question');
2303  
2304          $field = new xmldb_field('version');
2305          // Conditionally launch drop field version.
2306          if ($dbman->field_exists($table, $field)) {
2307              $dbman->drop_field($table, $field);
2308          }
2309  
2310          $field = new xmldb_field('hidden');
2311          // Conditionally launch drop field hidden.
2312          if ($dbman->field_exists($table, $field)) {
2313              $dbman->drop_field($table, $field);
2314          }
2315  
2316          // Define index categoryidnumber (not unique) to be dropped form question.
2317          $index = new xmldb_index('categoryidnumber', XMLDB_INDEX_UNIQUE, ['category', 'idnumber']);
2318  
2319          // Conditionally launch drop index categoryidnumber.
2320          if ($dbman->index_exists($table, $index)) {
2321              $dbman->drop_index($table, $index);
2322          }
2323  
2324          // Define key category (foreign) to be dropped form questions.
2325          $key = new xmldb_key('category', XMLDB_KEY_FOREIGN, ['category'], 'question_categories', ['id']);
2326  
2327          // Launch drop key category.
2328          $dbman->drop_key($table, $key);
2329  
2330          $field = new xmldb_field('idnumber');
2331          // Conditionally launch drop field idnumber.
2332          if ($dbman->field_exists($table, $field)) {
2333              $dbman->drop_field($table, $field);
2334          }
2335  
2336          $field = new xmldb_field('category');
2337          // Conditionally launch drop field category.
2338          if ($dbman->field_exists($table, $field)) {
2339              $dbman->drop_field($table, $field);
2340          }
2341  
2342          // Main savepoint reached.
2343          upgrade_main_savepoint(true, 2022020200.07);
2344      }
2345  
2346      if ($oldversion < 2022021100.01) {
2347          $sql = "SELECT preset.*
2348                    FROM {adminpresets} preset
2349              INNER JOIN {adminpresets_it} it ON preset.id = it.adminpresetid
2350                   WHERE it.name = :name AND it.value = :value AND preset.iscore > 0";
2351          // Some settings and plugins have been added/removed to the Starter and Full preset. Add them to the core presets if
2352          // they haven't been included yet.
2353          $params = ['name' => get_string('starterpreset', 'core_adminpresets'), 'iscore' => 1];
2354          $starterpreset = $DB->get_record('adminpresets', $params);
2355          if (!$starterpreset) {
2356              // Starter admin preset might have been created using the English name.
2357              $name = get_string_manager()->get_string('starterpreset', 'core_adminpresets', null, 'en');
2358              $params['name'] = $name;
2359              $starterpreset = $DB->get_record('adminpresets', $params);
2360          }
2361          if (!$starterpreset) {
2362              // We tried, but we didn't find starter by name. Let's find a core preset that sets 'usecomments' setting to 0.
2363              $params = ['name' => 'usecomments', 'value' => '0'];
2364              $starterpreset = $DB->get_record_sql($sql, $params);
2365          }
2366  
2367          $params = ['name' => get_string('fullpreset', 'core_adminpresets')];
2368          $fullpreset = $DB->get_record_select('adminpresets', 'name = :name and iscore > 0', $params);
2369          if (!$fullpreset) {
2370              // Full admin preset might have been created using the English name.
2371              $name = get_string_manager()->get_string('fullpreset', 'core_adminpresets', null, 'en');
2372              $params['name'] = $name;
2373              $fullpreset = $DB->get_record_select('adminpresets', 'name = :name and iscore > 0', $params);
2374          }
2375          if (!$fullpreset) {
2376              // We tried, but we didn't find full by name. Let's find a core preset that sets 'usecomments' setting to 1.
2377              $params = ['name' => 'usecomments', 'value' => '1'];
2378              $fullpreset = $DB->get_record_sql($sql, $params);
2379          }
2380  
2381          $settings = [
2382              // Settings. Hide Guest login button for Starter preset (and back to show for Full).
2383              [
2384                  'presetid' => $starterpreset->id,
2385                  'plugin' => 'none',
2386                  'name' => 'guestloginbutton',
2387                  'value' => '0',
2388              ],
2389              [
2390                  'presetid' => $fullpreset->id,
2391                  'plugin' => 'none',
2392                  'name' => 'guestloginbutton',
2393                  'value' => '1',
2394              ],
2395              // Settings. Set Activity chooser tabs to "Starred, All, Recommended"(1) for Starter and back it to default(0) for Full.
2396              [
2397                  'presetid' => $starterpreset->id,
2398                  'plugin' => 'none',
2399                  'name' => 'activitychoosertabmode',
2400                  'value' => '1',
2401              ],
2402              [
2403                  'presetid' => $fullpreset->id,
2404                  'plugin' => 'none',
2405                  'name' => 'activitychoosertabmode',
2406                  'value' => '0',
2407              ],
2408          ];
2409          foreach ($settings as $notused => $setting) {
2410              $params = ['adminpresetid' => $setting['presetid'], 'plugin' => $setting['plugin'], 'name' => $setting['name']];
2411              if (!$DB->record_exists('adminpresets_it', $params)) {
2412                  $record = new \stdClass();
2413                  $record->adminpresetid = $setting['presetid'];
2414                  $record->plugin = $setting['plugin'];
2415                  $record->name = $setting['name'];
2416                  $record->value = $setting['value'];
2417                  $DB->insert_record('adminpresets_it', $record);
2418              }
2419          }
2420  
2421          $plugins = [
2422              // Plugins. Blocks. Disable/enable Online users, Recently accessed courses and Starred courses.
2423              [
2424                  'presetid' => $starterpreset->id,
2425                  'plugin' => 'block',
2426                  'name' => 'online_users',
2427                  'enabled' => '0',
2428              ],
2429              [
2430                  'presetid' => $fullpreset->id,
2431                  'plugin' => 'block',
2432                  'name' => 'online_users',
2433                  'enabled' => '1',
2434              ],
2435              [
2436                  'presetid' => $starterpreset->id,
2437                  'plugin' => 'block',
2438                  'name' => 'recentlyaccessedcourses',
2439                  'enabled' => '0',
2440              ],
2441              [
2442                  'presetid' => $fullpreset->id,
2443                  'plugin' => 'block',
2444                  'name' => 'recentlyaccessedcourses',
2445                  'enabled' => '1',
2446              ],
2447              [
2448                  'presetid' => $starterpreset->id,
2449                  'plugin' => 'block',
2450                  'name' => 'starredcourses',
2451                  'enabled' => '0',
2452              ],
2453              [
2454                  'presetid' => $fullpreset->id,
2455                  'plugin' => 'block',
2456                  'name' => 'starredcourses',
2457                  'enabled' => '1',
2458              ],
2459              // Plugins. Enrolments. Disable/enable Guest access.
2460              [
2461                  'presetid' => $starterpreset->id,
2462                  'plugin' => 'enrol',
2463                  'name' => 'guest',
2464                  'enabled' => '0',
2465              ],
2466              [
2467                  'presetid' => $fullpreset->id,
2468                  'plugin' => 'enrol',
2469                  'name' => 'guest',
2470                  'enabled' => '1',
2471              ],
2472          ];
2473          foreach ($plugins as $notused => $plugin) {
2474              $params = ['adminpresetid' => $plugin['presetid'], 'plugin' => $plugin['plugin'], 'name' => $plugin['name']];
2475              if (!$DB->record_exists('adminpresets_plug', $params)) {
2476                  $record = new \stdClass();
2477                  $record->adminpresetid = $plugin['presetid'];
2478                  $record->plugin = $plugin['plugin'];
2479                  $record->name = $plugin['name'];
2480                  $record->enabled = $plugin['enabled'];
2481                  $DB->insert_record('adminpresets_plug', $record);
2482              }
2483          }
2484  
2485          // Settings: Remove customusermenuitems setting from Starter and Full presets.
2486          $sql = "(adminpresetid = ? OR adminpresetid = ?) AND plugin = 'none' AND name = 'customusermenuitems'";
2487          $params = [$starterpreset->id, $fullpreset->id];
2488          $DB->delete_records_select('adminpresets_it', $sql, $params);
2489  
2490          // Plugins. Question types. Re-enable Description and Essay for Starter.
2491          $sql = "(adminpresetid = ? OR adminpresetid = ?) AND plugin = 'qtype' AND (name = 'description' OR name = 'essay')";
2492          $DB->delete_records_select('adminpresets_plug', $sql, $params);
2493  
2494          // Main savepoint reached.
2495          upgrade_main_savepoint(true, 2022021100.01);
2496  
2497      }
2498  
2499      if ($oldversion < 2022021100.02) {
2500          $table = new xmldb_table('task_scheduled');
2501  
2502          // Changing precision of field minute on table task_scheduled to (200).
2503          $field = new xmldb_field('minute', XMLDB_TYPE_CHAR, '200', null, XMLDB_NOTNULL, null, null, 'blocking');
2504          $dbman->change_field_precision($table, $field);
2505          // Changing precision of field hour on table task_scheduled to (70).
2506          $field = new xmldb_field('hour', XMLDB_TYPE_CHAR, '70', null, XMLDB_NOTNULL, null, null, 'minute');
2507          $dbman->change_field_precision($table, $field);
2508          // Changing precision of field day on table task_scheduled to (90).
2509          $field = new xmldb_field('day', XMLDB_TYPE_CHAR, '90', null, XMLDB_NOTNULL, null, null, 'hour');
2510          $dbman->change_field_precision($table, $field);
2511          // Changing precision of field month on table task_scheduled to (30).
2512          $field = new xmldb_field('month', XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, null, 'day');
2513          $dbman->change_field_precision($table, $field);
2514  
2515          // Main savepoint reached.
2516          upgrade_main_savepoint(true, 2022021100.02);
2517      }
2518  
2519      if ($oldversion < 2022022600.01) {
2520          // Get all processor and existing preferences.
2521          $processors = $DB->get_records('message_processors');
2522          $providers = $DB->get_records('message_providers', null, '', 'id, name, component');
2523          $existingpreferences = get_config('message');
2524  
2525          foreach ($processors as $processor) {
2526              foreach ($providers as $provider) {
2527                  // Setting default preference name.
2528                  $componentproviderbase = $provider->component . '_' . $provider->name;
2529                  $preferencename = $processor->name.'_provider_'.$componentproviderbase.'_locked';
2530                  // If we do not have this setting yet, set it to 0.
2531                  if (!isset($existingpreferences->{$preferencename})) {
2532                      set_config($preferencename, 0, 'message');
2533                  }
2534              }
2535          }
2536  
2537          upgrade_main_savepoint(true, 2022022600.01);
2538      }
2539  
2540      if ($oldversion < 2022030100.00) {
2541          $sql = "SELECT preset.*
2542                    FROM {adminpresets} preset
2543              INNER JOIN {adminpresets_it} it ON preset.id = it.adminpresetid
2544                   WHERE it.name = :name AND it.value = :value AND preset.iscore > 0";
2545  
2546          $name = get_string('starterpreset', 'core_adminpresets');
2547          $params = ['name' => $name, 'iscore' => 1];
2548          $starterpreset = $DB->get_record('adminpresets', $params);
2549          if (!$starterpreset) {
2550              // Starter admin preset might have been created using the English name. Let's change it to current language.
2551              $englishname = get_string_manager()->get_string('starterpreset', 'core_adminpresets', null, 'en');
2552              $params['name'] = $englishname;
2553              $starterpreset = $DB->get_record('adminpresets', $params);
2554          }
2555          if (!$starterpreset) {
2556              // We tried, but we didn't find starter by name. Let's find a core preset that sets 'usecomments' setting to 0.
2557              $params = ['name' => 'usecomments', 'value' => '0'];
2558              $starterpreset = $DB->get_record_sql($sql, $params);
2559          }
2560          // The iscore field is already 1 for starterpreset, so we don't need to change it.
2561          // We only need to update the name and comment in case they are different to current language strings.
2562          if ($starterpreset && $starterpreset->name != $name) {
2563              $starterpreset->name = $name;
2564              $starterpreset->comments = get_string('starterpresetdescription', 'core_adminpresets');
2565              $DB->update_record('adminpresets', $starterpreset);
2566          }
2567  
2568          // Let's mark Full admin presets with current FULL_PRESETS value and change the name to current language.
2569          $name = get_string('fullpreset', 'core_adminpresets');
2570          $params = ['name' => $name];
2571          $fullpreset = $DB->get_record_select('adminpresets', 'name = :name and iscore > 0', $params);
2572          if (!$fullpreset) {
2573              // Full admin preset might have been created using the English name.
2574              $englishname = get_string_manager()->get_string('fullpreset', 'core_adminpresets', null, 'en');
2575              $params['name'] = $englishname;
2576              $fullpreset = $DB->get_record_select('adminpresets', 'name = :name and iscore > 0', $params);
2577          }
2578          if (!$fullpreset) {
2579              // We tried, but we didn't find full by name. Let's find a core preset that sets 'usecomments' setting to 1.
2580              $params = ['name' => 'usecomments', 'value' => '1'];
2581              $fullpreset = $DB->get_record_sql($sql, $params);
2582          }
2583          if ($fullpreset) {
2584              // We need to update iscore field value, whether the name is the same or not.
2585              $fullpreset->name = $name;
2586              $fullpreset->comments = get_string('fullpresetdescription', 'core_adminpresets');
2587              $fullpreset->iscore = 2;
2588              $DB->update_record('adminpresets', $fullpreset);
2589  
2590              // We are applying again changes made on 2022011100.01 upgrading step because of MDL-73953 bug.
2591              $blocknames = ['course_summary', 'feedback', 'rss_client', 'selfcompletion'];
2592              list($blocksinsql, $blocksinparams) = $DB->get_in_or_equal($blocknames);
2593  
2594              // Remove entries from the adminpresets_app_plug table (in case the preset has been applied).
2595              $appliedpresets = $DB->get_records('adminpresets_app', ['adminpresetid' => $fullpreset->id], '', 'id');
2596              if ($appliedpresets) {
2597                  list($appsinsql, $appsinparams) = $DB->get_in_or_equal(array_keys($appliedpresets));
2598                  $sql = "adminpresetapplyid $appsinsql AND plugin='block' AND name $blocksinsql";
2599                  $params = array_merge($appsinparams, $blocksinparams);
2600                  $DB->delete_records_select('adminpresets_app_plug', $sql, $params);
2601              }
2602  
2603              // Remove entries for these blocks from the adminpresets_plug table.
2604              $sql = "adminpresetid = ? AND plugin='block' AND name $blocksinsql";
2605              $params = array_merge([$fullpreset->id], $blocksinparams);
2606              $DB->delete_records_select('adminpresets_plug', $sql, $params);
2607          }
2608  
2609          // Main savepoint reached.
2610          upgrade_main_savepoint(true, 2022030100.00);
2611      }
2612  
2613      if ($oldversion < 2022031100.01) {
2614          $reportsusermenuitem = 'reports,core_reportbuilder|/reportbuilder/index.php';
2615          upgrade_add_item_to_usermenu($reportsusermenuitem);
2616          // Main savepoint reached.
2617          upgrade_main_savepoint(true, 2022031100.01);
2618      }
2619  
2620      if ($oldversion < 2022032200.01) {
2621  
2622          // Define index to be added to question_references.
2623          $table = new xmldb_table('question_references');
2624          $index = new xmldb_index('context-component-area-itemid', XMLDB_INDEX_UNIQUE,
2625              ['usingcontextid', 'component', 'questionarea', 'itemid']);
2626  
2627          // Conditionally launch add field id.
2628          if (!$dbman->index_exists($table, $index)) {
2629              $dbman->add_index($table, $index);
2630          }
2631  
2632          // Main savepoint reached.
2633          upgrade_main_savepoint(true, 2022032200.01);
2634      }
2635  
2636      if ($oldversion < 2022032200.02) {
2637  
2638          // Define index to be added to question_references.
2639          $table = new xmldb_table('question_set_references');
2640          $index = new xmldb_index('context-component-area-itemid', XMLDB_INDEX_UNIQUE,
2641              ['usingcontextid', 'component', 'questionarea', 'itemid']);
2642  
2643          // Conditionally launch add field id.
2644          if (!$dbman->index_exists($table, $index)) {
2645              $dbman->add_index($table, $index);
2646          }
2647  
2648          // Main savepoint reached.
2649          upgrade_main_savepoint(true, 2022032200.02);
2650      }
2651  
2652      if ($oldversion < 2022041200.01) {
2653  
2654          // The original default admin presets "sensible settings" (those that should be treated as sensitive).
2655          $originalsensiblesettings = 'recaptchapublickey@@none, recaptchaprivatekey@@none, googlemapkey3@@none, ' .
2656              'secretphrase@@url, cronremotepassword@@none, smtpuser@@none, smtppass@none, proxypassword@@none, ' .
2657              'quizpassword@@quiz, allowedip@@none, blockedip@@none, dbpass@@logstore_database, messageinbound_hostpass@@none, ' .
2658              'bind_pw@@auth_cas, pass@@auth_db, bind_pw@@auth_ldap, dbpass@@enrol_database, bind_pw@@enrol_ldap, ' .
2659              'server_password@@search_solr, ssl_keypassword@@search_solr, alternateserver_password@@search_solr, ' .
2660              'alternatessl_keypassword@@search_solr, test_password@@cachestore_redis, password@@mlbackend_python';
2661  
2662          // Check if the current config matches the original default, upgrade to new default if so.
2663          if (get_config('adminpresets', 'sensiblesettings') === $originalsensiblesettings) {
2664              $newsensiblesettings = "{$originalsensiblesettings}, badges_badgesalt@@none, calendar_exportsalt@@none";
2665              set_config('sensiblesettings', $newsensiblesettings, 'adminpresets');
2666          }
2667  
2668          // Main savepoint reached.
2669          upgrade_main_savepoint(true, 2022041200.01);
2670      }
2671  
2672      // Automatically generated Moodle v4.0.0 release upgrade line.
2673      // Put any upgrade step following this.
2674  
2675      if ($oldversion < 2022042900.01) {
2676          // Social custom fields could had been created linked to category id = 1. Let's check category 1 exists.
2677          if (!$DB->get_record('user_info_category', ['id' => 1])) {
2678              // Let's check if we have any custom field linked to category id = 1.
2679              $fields = $DB->get_records('user_info_field', ['categoryid' => 1]);
2680              if (!empty($fields)) {
2681                  $categoryid = $DB->get_field_sql('SELECT min(id) from {user_info_category}');
2682                  foreach ($fields as $field) {
2683                      $field->categoryid = $categoryid;
2684                      $DB->update_record('user_info_field', $field);
2685                  }
2686              }
2687          }
2688  
2689          // Main savepoint reached.
2690          upgrade_main_savepoint(true, 2022042900.01);
2691      }
2692  
2693      if ($oldversion < 2022051000.00) {
2694          // Add index to the sid field in the external_tokens table.
2695          $table = new xmldb_table('external_tokens');
2696          $index = new xmldb_index('sid', XMLDB_INDEX_NOTUNIQUE, ['sid']);
2697  
2698          if (!$dbman->index_exists($table, $index)) {
2699              $dbman->add_index($table, $index);
2700          }
2701  
2702          upgrade_main_savepoint(true, 2022051000.00);
2703      }
2704  
2705      if ($oldversion < 2022052500.00) {
2706          // Start an adhoc task to fix the file timestamps of restored files.
2707          $task = new core\task\fix_file_timestamps_task();
2708          \core\task\manager::queue_adhoc_task($task);
2709  
2710          // Main savepoint reached.
2711          upgrade_main_savepoint(true, 2022052500.00);
2712      }
2713  
2714      if ($oldversion < 2022052700.01) {
2715  
2716          // Define index timestarted_idx (not unique) to be added to task_adhoc.
2717          $table = new xmldb_table('task_adhoc');
2718          $index = new xmldb_index('timestarted_idx', XMLDB_INDEX_NOTUNIQUE, ['timestarted']);
2719  
2720          // Conditionally launch add index timestarted_idx.
2721          if (!$dbman->index_exists($table, $index)) {
2722              $dbman->add_index($table, $index);
2723          }
2724  
2725          // Main savepoint reached.
2726          upgrade_main_savepoint(true, 2022052700.01);
2727      }
2728  
2729      if ($oldversion < 2022052700.02) {
2730  
2731          // Define index filename (not unique) to be added to files.
2732          $table = new xmldb_table('files');
2733          $index = new xmldb_index('filename', XMLDB_INDEX_NOTUNIQUE, ['filename']);
2734  
2735          // Conditionally launch add index filename.
2736          if (!$dbman->index_exists($table, $index)) {
2737              $dbman->add_index($table, $index);
2738          }
2739  
2740          // Main savepoint reached.
2741          upgrade_main_savepoint(true, 2022052700.02);
2742      }
2743  
2744      if ($oldversion < 2022060300.01) {
2745  
2746          // Changing precision of field hidden on table grade_categories to (10).
2747          $table = new xmldb_table('grade_categories');
2748          $field = new xmldb_field('hidden', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'timemodified');
2749  
2750          // Launch change of precision for field hidden.
2751          $dbman->change_field_precision($table, $field);
2752  
2753          // Changing precision of field hidden on table grade_categories_history to (10).
2754          $table = new xmldb_table('grade_categories_history');
2755          $field = new xmldb_field('hidden', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'aggregatesubcats');
2756  
2757          // Launch change of precision for field hidden.
2758          $dbman->change_field_precision($table, $field);
2759  
2760          // Main savepoint reached.
2761          upgrade_main_savepoint(true, 2022060300.01);
2762      }
2763  
2764      if ($oldversion < 2022061000.01) {
2765          // Iterate over custom user menu items configuration, removing pix icon references.
2766          $customusermenuitems = str_replace(["\r\n", "\r"], "\n", $CFG->customusermenuitems);
2767  
2768          $lines = preg_split('/\n/', $customusermenuitems, -1, PREG_SPLIT_NO_EMPTY);
2769          $lines = array_map(static function(string $line): string {
2770              // Previous format was "<langstring>|<url>[|<pixicon>]" - pix icon is no longer supported.
2771              $lineparts = explode('|', trim($line), 3);
2772              // Return first two parts of line.
2773              return implode('|', array_slice($lineparts, 0, 2));
2774          }, $lines);
2775  
2776          set_config('customusermenuitems', implode("\n", $lines));
2777  
2778          upgrade_main_savepoint(true, 2022061000.01);
2779      }
2780  
2781      if ($oldversion < 2022061500.00) {
2782          // Remove drawer-open-nav user preference for every user.
2783          $DB->delete_records('user_preferences', ['name' => 'drawer-open-nav']);
2784  
2785          // Main savepoint reached.
2786          upgrade_main_savepoint(true, 2022061500.00);
2787  
2788      }
2789  
2790      if ($oldversion < 2022072900.00) {
2791          // Call the helper function that updates the foreign keys and indexes in MDL-49795.
2792          upgrade_add_foreign_key_and_indexes();
2793  
2794          // Main savepoint reached.
2795          upgrade_main_savepoint(true, 2022072900.00);
2796      }
2797  
2798      if ($oldversion < 2022081200.01) {
2799  
2800          // Define field lang to be added to course_modules.
2801          $table = new xmldb_table('course_modules');
2802          $field = new xmldb_field('lang', XMLDB_TYPE_CHAR, '30', null, null, null, null, 'downloadcontent');
2803  
2804          // Conditionally launch add field lang.
2805          if (!$dbman->field_exists($table, $field)) {
2806              $dbman->add_field($table, $field);
2807          }
2808  
2809          // Main savepoint reached.
2810          upgrade_main_savepoint(true, 2022081200.01);
2811      }
2812  
2813      if ($oldversion < 2022091000.01) {
2814          $table = new xmldb_table('h5p');
2815          $indexpathnamehash = new xmldb_index('pathnamehash_idx', XMLDB_INDEX_NOTUNIQUE, ['pathnamehash']);
2816  
2817          if (!$dbman->index_exists($table, $indexpathnamehash)) {
2818              $dbman->add_index($table, $indexpathnamehash);
2819          }
2820          // Main savepoint reached.
2821          upgrade_main_savepoint(true, 2022091000.01);
2822      }
2823  
2824      if ($oldversion < 2022092200.01) {
2825  
2826          // Remove any orphaned tag instance records (pointing to non-existing context).
2827          $DB->delete_records_select('tag_instance', 'NOT EXISTS (
2828              SELECT ctx.id FROM {context} ctx WHERE ctx.id = {tag_instance}.contextid
2829          )');
2830  
2831          // Main savepoint reached.
2832          upgrade_main_savepoint(true, 2022092200.01);
2833      }
2834  
2835      if ($oldversion < 2022101400.01) {
2836          $table = new xmldb_table('competency_modulecomp');
2837          $field = new xmldb_field('overridegrade', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'ruleoutcome');
2838  
2839          if (!$dbman->field_exists($table, $field)) {
2840              $dbman->add_field($table, $field);
2841          }
2842  
2843          // Main savepoint reached.
2844          upgrade_main_savepoint(true, 2022101400.01);
2845      }
2846  
2847      if ($oldversion < 2022101400.03) {
2848          // Define table to store completion viewed.
2849          $table = new xmldb_table('course_modules_viewed');
2850  
2851          // Adding fields to table course_modules_viewed.
2852          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2853          $table->add_field('coursemoduleid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'id');
2854          $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'coursemoduleid');
2855          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'userid');
2856  
2857          // Adding keys to table course_modules_viewed.
2858          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
2859  
2860          // Adding indexes to table course_modules_viewed.
2861          $table->add_index('coursemoduleid', XMLDB_INDEX_NOTUNIQUE, ['coursemoduleid']);
2862          $table->add_index('userid-coursemoduleid', XMLDB_INDEX_UNIQUE, ['userid', 'coursemoduleid']);
2863  
2864          if (!$dbman->table_exists($table)) {
2865              $dbman->create_table($table);
2866          }
2867  
2868          // Main savepoint reached.
2869          upgrade_main_savepoint(true, 2022101400.03);
2870      }
2871  
2872      if ($oldversion < 2022101400.04) {
2873          // Add legacy data to the new table.
2874          $transaction = $DB->start_delegated_transaction();
2875          upgrade_set_timeout(3600);
2876          $sql = "INSERT INTO {course_modules_viewed}
2877                              (userid, coursemoduleid, timecreated)
2878                       SELECT userid, coursemoduleid, timemodified
2879                         FROM {course_modules_completion}
2880                        WHERE viewed = 1";
2881          $DB->execute($sql);
2882          $transaction->allow_commit();
2883  
2884          // Main savepoint reached.
2885          upgrade_main_savepoint(true, 2022101400.04);
2886      }
2887  
2888      if ($oldversion < 2022101400.05) {
2889          // Define field viewed to be dropped from course_modules_completion.
2890          $table = new xmldb_table('course_modules_completion');
2891          $field = new xmldb_field('viewed');
2892  
2893          // Conditionally launch drop field viewed.
2894          if ($dbman->field_exists($table, $field)) {
2895              $dbman->drop_field($table, $field);
2896          }
2897  
2898          // Main savepoint reached.
2899          upgrade_main_savepoint(true, 2022101400.05);
2900      }
2901  
2902      if ($oldversion < 2022102800.01) {
2903          // For sites with "contact site support" already available (4.0.x), maintain existing functionality.
2904          if ($oldversion >= 2022041900.00) {
2905              set_config('supportavailability', CONTACT_SUPPORT_ANYONE);
2906          } else {
2907              // Sites which did not previously have the "contact site support" feature default to it requiring authentication.
2908              set_config('supportavailability', CONTACT_SUPPORT_AUTHENTICATED);
2909          }
2910  
2911          // Main savepoint reached.
2912          upgrade_main_savepoint(true, 2022102800.01);
2913      }
2914  
2915      if ($oldversion < 2022110600.00) {
2916          // If webservice_xmlrpc isn't any longer installed, remove its configuration,
2917          // capabilities and presence in other settings.
2918          if (!file_exists($CFG->dirroot . '/webservice/xmlrpc/version.php')) {
2919              // No DB structures to delete in this plugin.
2920  
2921              // Remove capabilities.
2922              capabilities_cleanup('webservice_xmlrpc');
2923  
2924              // Remove own configuration.
2925              unset_all_config_for_plugin('webservice_xmlrpc');
2926  
2927              // Remove it from the enabled protocols if it was there.
2928              $protos = get_config('core', 'webserviceprotocols');
2929              $protoarr = explode(',', $protos);
2930              $protoarr = array_filter($protoarr, function($ele) {
2931                  return trim($ele) !== 'xmlrpc';
2932              });
2933              $protos = implode(',', $protoarr);
2934              set_config('webserviceprotocols', $protos);
2935          }
2936  
2937          // Main savepoint reached.
2938          upgrade_main_savepoint(true, 2022110600.00);
2939      }
2940  
2941      // Automatically generated Moodle v4.1.0 release upgrade line.
2942      // Put any upgrade step following this.
2943  
2944      if ($oldversion < 2022120900.01) {
2945  
2946          // Remove any orphaned role assignment records (pointing to non-existing roles).
2947          $DB->delete_records_select('role_assignments', 'NOT EXISTS (
2948              SELECT r.id FROM {role} r WHERE r.id = {role_assignments}.roleid
2949          )');
2950  
2951          // Main savepoint reached.
2952          upgrade_main_savepoint(true, 2022120900.01);
2953      }
2954  
2955      if ($oldversion < 2022121600.01) {
2956          // Define index blocknameindex (not unique) to be added to block_instances.
2957          $table = new xmldb_table('block_instances');
2958          $index = new xmldb_index('blocknameindex', XMLDB_INDEX_NOTUNIQUE, ['blockname']);
2959  
2960          // Conditionally launch add index blocknameindex.
2961          if (!$dbman->index_exists($table, $index)) {
2962              $dbman->add_index($table, $index);
2963          }
2964          // Main savepoint reached.
2965          upgrade_main_savepoint(true, 2022121600.01);
2966      }
2967  
2968      if ($oldversion < 2023010300.00) {
2969          // The useexternalyui setting has been removed.
2970          unset_config('useexternalyui');
2971  
2972          // Main savepoint reached.
2973          upgrade_main_savepoint(true, 2023010300.00);
2974      }
2975  
2976      if ($oldversion < 2023020800.00) {
2977          // If cachestore_memcached is no longer present, remove it.
2978          if (!file_exists($CFG->dirroot . '/cache/stores/memcached/version.php')) {
2979              // Clean config.
2980              unset_all_config_for_plugin('cachestore_memcached');
2981          }
2982  
2983          // Main savepoint reached.
2984          upgrade_main_savepoint(true, 2023020800.00);
2985      }
2986  
2987      if ($oldversion < 2023021700.01) {
2988          // Define field pdfexportfont to be added to course.
2989          $table = new xmldb_table('course');
2990          $field = new xmldb_field('pdfexportfont', XMLDB_TYPE_CHAR, '50', null, false, false, null, 'showcompletionconditions');
2991  
2992          // Conditionally launch add field pdfexportfont.
2993          if (!$dbman->field_exists($table, $field)) {
2994              $dbman->add_field($table, $field);
2995          }
2996  
2997          // Main savepoint reached.
2998          upgrade_main_savepoint(true, 2023021700.01);
2999      }
3000  
3001      if ($oldversion < 2023022000.00) {
3002          // Remove grade_report_showquickfeedback, grade_report_enableajax, grade_report_showeyecons,
3003          // grade_report_showlocks, grade_report_showanalysisicon preferences for every user.
3004          $DB->delete_records('user_preferences', ['name' => 'grade_report_showquickfeedback']);
3005          $DB->delete_records('user_preferences', ['name' => 'grade_report_enableajax']);
3006          $DB->delete_records('user_preferences', ['name' => 'grade_report_showeyecons']);
3007          $DB->delete_records('user_preferences', ['name' => 'grade_report_showlocks']);
3008          $DB->delete_records('user_preferences', ['name' => 'grade_report_showanalysisicon']);
3009  
3010          // The grade_report_showquickfeedback, grade_report_enableajax, grade_report_showeyecons,
3011          // grade_report_showlocks, grade_report_showanalysisicon settings have been removed.
3012          unset_config('grade_report_showquickfeedback');
3013          unset_config('grade_report_enableajax');
3014          unset_config('grade_report_showeyecons');
3015          unset_config('grade_report_showlocks');
3016          unset_config('grade_report_showanalysisicon');
3017  
3018          // Main savepoint reached.
3019          upgrade_main_savepoint(true, 2023022000.00);
3020      }
3021  
3022      if ($oldversion < 2023030300.01) {
3023          $sql = "SELECT preset.*
3024                    FROM {adminpresets} preset
3025              INNER JOIN {adminpresets_it} it ON preset.id = it.adminpresetid
3026                   WHERE it.name = :name AND it.value = :value AND preset.iscore > 0";
3027          // Some settings and plugins have been added/removed to the Starter and Full preset. Add them to the core presets if
3028          // they haven't been included yet.
3029          $params = ['name' => get_string('starterpreset', 'core_adminpresets'), 'iscore' => 1];
3030          $starterpreset = $DB->get_record('adminpresets', $params);
3031          if (!$starterpreset) {
3032              // Starter admin preset might have been created using the English name.
3033              $name = get_string_manager()->get_string('starterpreset', 'core_adminpresets', null, 'en');
3034              $params['name'] = $name;
3035              $starterpreset = $DB->get_record('adminpresets', $params);
3036          }
3037          if (!$starterpreset) {
3038              // We tried, but we didn't find starter by name. Let's find a core preset that sets 'usecomments' setting to 0.
3039              $params = ['name' => 'usecomments', 'value' => '0'];
3040              $starterpreset = $DB->get_record_sql($sql, $params);
3041          }
3042  
3043          $params = ['name' => get_string('fullpreset', 'core_adminpresets')];
3044          $fullpreset = $DB->get_record_select('adminpresets', 'name = :name and iscore > 0', $params);
3045          if (!$fullpreset) {
3046              // Full admin preset might have been created using the English name.
3047              $name = get_string_manager()->get_string('fullpreset', 'core_adminpresets', null, 'en');
3048              $params['name'] = $name;
3049              $fullpreset = $DB->get_record_select('adminpresets', 'name = :name and iscore > 0', $params);
3050          }
3051          if (!$fullpreset) {
3052              // We tried, but we didn't find full by name. Let's find a core preset that sets 'usecomments' setting to 1.
3053              $params = ['name' => 'usecomments', 'value' => '1'];
3054              $fullpreset = $DB->get_record_sql($sql, $params);
3055          }
3056  
3057          $settings = [
3058              // Settings. Set Activity chooser tabs to "Starred, Recommended, All"(5) for Starter and back it to default(3) for Full.
3059              [
3060                  'presetid' => $starterpreset->id,
3061                  'plugin' => 'none',
3062                  'name' => 'activitychoosertabmode',
3063                  'value' => '4',
3064              ],
3065              [
3066                  'presetid' => $fullpreset->id,
3067                  'plugin' => 'none',
3068                  'name' => 'activitychoosertabmode',
3069                  'value' => '3',
3070              ],
3071          ];
3072          foreach ($settings as $notused => $setting) {
3073              $params = ['adminpresetid' => $setting['presetid'], 'plugin' => $setting['plugin'], 'name' => $setting['name']];
3074              if (!$record = $DB->get_record('adminpresets_it', $params)) {
3075                  $record = new \stdClass();
3076                  $record->adminpresetid = $setting['presetid'];
3077                  $record->plugin = $setting['plugin'];
3078                  $record->name = $setting['name'];
3079                  $record->value = $setting['value'];
3080                  $DB->insert_record('adminpresets_it', $record);
3081              } else {
3082                  $record->value = $setting['value'];
3083                  $DB->update_record('adminpresets_it', $record);
3084              }
3085          }
3086  
3087          // Main savepoint reached.
3088          upgrade_main_savepoint(true, 2023030300.01);
3089      }
3090  
3091      if ($oldversion < 2023030300.02) {
3092          // If cachestore_mongodb is no longer present, remove it.
3093          if (!file_exists($CFG->dirroot . '/cache/stores/mongodb/version.php')) {
3094              // Clean config.
3095              unset_all_config_for_plugin('cachestore_mongodb');
3096          }
3097  
3098          // Main savepoint reached.
3099          upgrade_main_savepoint(true, 2023030300.02);
3100      }
3101  
3102      if ($oldversion < 2023030300.03) {
3103          // If editor_tinymce is no longer present, remove it.
3104          if (!file_exists($CFG->dirroot . '/lib/editor/tinymce/version.php')) {
3105              // Clean config.
3106              uninstall_plugin('editor', 'tinymce');
3107              $DB->delete_records('user_preferences', [
3108                  'name' => 'htmleditor',
3109                  'value' => 'tinymce',
3110              ]);
3111  
3112              if ($editors = get_config('core', 'texteditors')) {
3113                  $editors = array_flip(explode(',', $editors));
3114                  unset($editors['tinymce']);
3115                  set_config('texteditors', implode(',', array_flip($editors)));
3116              }
3117          }
3118          upgrade_main_savepoint(true, 2023030300.03);
3119      }
3120  
3121      if ($oldversion < 2023031000.02) {
3122          // If editor_tinymce is no longer present, remove it's sub-plugins too.
3123          if (!file_exists($CFG->dirroot . '/lib/editor/tinymce/version.php')) {
3124              $DB->delete_records_select(
3125                  'config_plugins',
3126                  $DB->sql_like('plugin', ':plugin'),
3127                  ['plugin' => $DB->sql_like_escape('tinymce_') . '%']
3128              );
3129          }
3130  
3131          // Main savepoint reached.
3132          upgrade_main_savepoint(true, 2023031000.02);
3133      }
3134  
3135      if ($oldversion < 2023031400.01) {
3136          // Define field id to be added to groups.
3137          $table = new xmldb_table('groups');
3138          $field = new xmldb_field('visibility', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'picture');
3139  
3140          // Conditionally launch add field visibility.
3141          if (!$dbman->field_exists($table, $field)) {
3142              $dbman->add_field($table, $field);
3143          }
3144  
3145          // Define field participation to be added to groups.
3146          $field = new xmldb_field('participation', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1', 'visibility');
3147  
3148          // Conditionally launch add field participation.
3149          if (!$dbman->field_exists($table, $field)) {
3150              $dbman->add_field($table, $field);
3151          }
3152  
3153          // Main savepoint reached.
3154          upgrade_main_savepoint(true, 2023031400.01);
3155      }
3156  
3157      if ($oldversion < 2023031400.02) {
3158  
3159          // Define table xapi_states to be created.
3160          $table = new xmldb_table('xapi_states');
3161  
3162          // Adding fields to table xapi_states.
3163          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3164          $table->add_field('component', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
3165          $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
3166          $table->add_field('itemid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
3167          $table->add_field('stateid', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
3168          $table->add_field('statedata', XMLDB_TYPE_TEXT, null, null, null, null, null);
3169          $table->add_field('registration', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3170          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
3171          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
3172  
3173          // Adding keys to table xapi_states.
3174          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
3175  
3176          // Adding indexes to table xapi_states.
3177          $table->add_index('component-itemid', XMLDB_INDEX_NOTUNIQUE, ['component', 'itemid']);
3178          $table->add_index('userid', XMLDB_INDEX_NOTUNIQUE, ['userid']);
3179          $table->add_index('timemodified', XMLDB_INDEX_NOTUNIQUE, ['timemodified']);
3180  
3181          // Conditionally launch create table for xapi_states.
3182          if (!$dbman->table_exists($table)) {
3183              $dbman->create_table($table);
3184          }
3185  
3186          if (!isset($CFG->xapicleanupperiod)) {
3187              set_config('xapicleanupperiod', WEEKSECS * 8);
3188          }
3189  
3190          // Main savepoint reached.
3191          upgrade_main_savepoint(true, 2023031400.02);
3192      }
3193  
3194      if ($oldversion < 2023040600.01) {
3195          // If logstore_legacy is no longer present, remove it.
3196          if (!file_exists($CFG->dirroot . '/admin/tool/log/store/legacy/version.php')) {
3197              uninstall_plugin('logstore', 'legacy');
3198          }
3199  
3200          // Main savepoint reached.
3201          upgrade_main_savepoint(true, 2023040600.01);
3202      }
3203  
3204      if ($oldversion < 2023041100.00) {
3205          // Add public key field to user_devices table.
3206          $table = new xmldb_table('user_devices');
3207          $field = new xmldb_field('publickey', XMLDB_TYPE_TEXT, null, null, null, null, null, 'uuid');
3208  
3209          if (!$dbman->field_exists($table, $field)) {
3210              $dbman->add_field($table, $field);
3211          }
3212  
3213          // Main savepoint reached.
3214          upgrade_main_savepoint(true, 2023041100.00);
3215      }
3216  
3217      if ($oldversion < 2023042000.00) {
3218          // If mod_assignment is no longer present, remove it.
3219          if (!file_exists($CFG->dirroot . '/mod/assignment/version.php')) {
3220              // Delete all mod_assignment grade_grades orphaned data.
3221              $DB->delete_records_select(
3222                  'grade_grades', "itemid IN (SELECT id FROM {grade_items} WHERE itemtype = 'mod' AND itemmodule = 'assignment')"
3223              );
3224  
3225              // Delete all mod_assignment grade_grades_history orphaned data.
3226              $DB->delete_records('grade_grades_history', ['source' => 'mod/assignment']);
3227  
3228              // Delete all mod_assignment grade_items orphaned data.
3229              $DB->delete_records('grade_items', ['itemtype' => 'mod', 'itemmodule' => 'assignment']);
3230  
3231              // Delete all mod_assignment grade_items_history orphaned data.
3232              $DB->delete_records('grade_items_history', ['itemtype' => 'mod', 'itemmodule' => 'assignment']);
3233  
3234              // Delete core mod_assignment subplugins.
3235              uninstall_plugin('assignment', 'offline');
3236              uninstall_plugin('assignment', 'online');
3237              uninstall_plugin('assignment', 'upload');
3238              uninstall_plugin('assignment', 'uploadsingle');
3239  
3240              // Delete other mod_assignment subplugins.
3241              $pluginnamelike = $DB->sql_like('plugin', ':pluginname');
3242              $subplugins = $DB->get_fieldset_select('config_plugins', 'plugin', "$pluginnamelike AND name = :name", [
3243                  'pluginname' => $DB->sql_like_escape('assignment_') . '%',
3244                  'name' => 'version',
3245              ]);
3246              foreach ($subplugins as $subplugin) {
3247                  [$plugin, $subpluginname] = explode('_', $subplugin, 2);
3248                  uninstall_plugin($plugin, $subpluginname);
3249              }
3250  
3251              // Delete mod_assignment.
3252              uninstall_plugin('mod', 'assignment');
3253          }
3254  
3255          // Main savepoint reached.
3256          upgrade_main_savepoint(true, 2023042000.00);
3257      }
3258  
3259      // Automatically generated Moodle v4.2.0 release upgrade line.
3260      // Put any upgrade step following this.
3261  
3262      if ($oldversion < 2023051500.00) {
3263          // Define communication table.
3264          $table = new xmldb_table('communication');
3265  
3266          // Adding fields to table communication.
3267          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE);
3268          $table->add_field('instanceid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'id');
3269          $table->add_field('component', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null, 'instanceid');
3270          $table->add_field('instancetype', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null, 'component');
3271          $table->add_field('provider', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null, 'instancerype');
3272          $table->add_field('roomname', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'provider');
3273          $table->add_field('avatarfilename', XMLDB_TYPE_CHAR, '100', null, null, null, null, 'roomname');
3274          $table->add_field('active', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, 1, 'avatarfilename');
3275  
3276          // Add key.
3277          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
3278  
3279          // Conditionally launch create table for communication.
3280          if (!$dbman->table_exists($table)) {
3281              $dbman->create_table($table);
3282          }
3283  
3284          // Define communication user table.
3285          $table = new xmldb_table('communication_user');
3286  
3287          // Adding fields to table communication.
3288          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE);
3289          $table->add_field('commid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'id');
3290          $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'commid');
3291          $table->add_field('synced', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, 0, 'userid');
3292          $table->add_field('deleted', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, 0, 'synced');
3293  
3294          // Add keys.
3295          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
3296          $table->add_key('commid', XMLDB_KEY_FOREIGN, ['commid'], 'communication', ['id']);
3297          $table->add_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']);
3298  
3299          // Conditionally launch create table for communication.
3300          if (!$dbman->table_exists($table)) {
3301              $dbman->create_table($table);
3302          }
3303  
3304          // Main savepoint reached.
3305          upgrade_main_savepoint(true, 2023051500.00);
3306      }
3307  
3308      if ($oldversion < 2023062200.00) {
3309          // Remove device specific fields for themes from config table.
3310          unset_config('thememobile');
3311          unset_config('themelegacy');
3312          unset_config('themetablet');
3313  
3314          upgrade_main_savepoint(true, 2023062200.00);
3315      }
3316  
3317      if ($oldversion < 2023062700.01) {
3318          // Define field name to be added to external_tokens.
3319          $table = new xmldb_table('external_tokens');
3320          $field = new xmldb_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'lastaccess');
3321          // Conditionally launch add field name.
3322          if (!$dbman->field_exists($table, $field)) {
3323              $dbman->add_field($table, $field);
3324          }
3325          // Update the old external tokens.
3326          $sql = 'UPDATE {external_tokens}
3327                     SET name = ' . $DB->sql_concat(
3328                         // We only need the prefix, so leave the third param with an empty string.
3329                             "'" . get_string('tokennameprefix', 'webservice', '') . "'",
3330                             "id");
3331          $DB->execute($sql);
3332          // Main savepoint reached.
3333          upgrade_main_savepoint(true, 2023062700.01);
3334      }
3335  
3336      if ($oldversion < 2023062900.01) {
3337  
3338          // Define field avatarsynced to be added to communication.
3339          $table = new xmldb_table('communication');
3340          $field = new xmldb_field('avatarsynced', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, 0, 'active');
3341  
3342          // Conditionally launch add field avatarsynced.
3343          if (!$dbman->field_exists($table, $field)) {
3344              $dbman->add_field($table, $field);
3345          }
3346  
3347          // Main savepoint reached.
3348          upgrade_main_savepoint(true, 2023062900.01);
3349      }
3350  
3351      if ($oldversion < 2023080100.00) {
3352          // Upgrade yaml mime type for existing yaml and yml files.
3353          $filetypes = [
3354              '%.yaml' => 'application/yaml',
3355              '%.yml' => 'application/yaml,'
3356          ];
3357  
3358          $select = $DB->sql_like('filename', '?', false);
3359          foreach ($filetypes as $extension => $mimetype) {
3360              $DB->set_field_select(
3361                  'files',
3362                  'mimetype',
3363                  $mimetype,
3364                  $select,
3365                  [$extension]
3366              );
3367          }
3368  
3369          // Main savepoint reached.
3370          upgrade_main_savepoint(true, 2023080100.00);
3371      }
3372  
3373      if ($oldversion < 2023081500.00) {
3374          upgrade_core_licenses();
3375          upgrade_main_savepoint(true, 2023081500.00);
3376      }
3377  
3378      if ($oldversion < 2023081800.01) {
3379          // Remove enabledevicedetection and devicedetectregex from config table.
3380          unset_config('enabledevicedetection');
3381          unset_config('devicedetectregex');
3382          // Main savepoint reached.
3383          upgrade_main_savepoint(true, 2023081800.01);
3384      }
3385  
3386      if ($oldversion < 2023082200.01) {
3387          // Some MIME icons have been removed and replaced with existing icons. They need to be upgraded for custom MIME types.
3388          $replacedicons = [
3389              'avi' => 'video',
3390              'base' => 'database',
3391              'bmp' => 'image',
3392              'html' => 'markup',
3393              'jpeg' => 'image',
3394              'mov' => 'video',
3395              'mp3' => 'audio',
3396              'mpeg' => 'video',
3397              'png' => 'image',
3398              'quicktime' => 'video',
3399              'tiff' => 'image',
3400              'wav' => 'audio',
3401              'wmv' => 'video',
3402          ];
3403  
3404          $custom = [];
3405          if (!empty($CFG->customfiletypes)) {
3406              if (array_key_exists('customfiletypes', $CFG->config_php_settings)) {
3407                  // It's set in config.php, so the MIME icons can't be upgraded automatically.
3408                  echo("\nYou need to manually check customfiletypes in config.php because some MIME icons have been removed!\n");
3409              } else {
3410                  // It's a JSON string in the config table.
3411                  $custom = json_decode($CFG->customfiletypes);
3412              }
3413          }
3414  
3415          $changed = false;
3416          foreach ($custom as $customentry) {
3417              if (!empty($customentry->icon) && array_key_exists($customentry->icon, $replacedicons)) {
3418                  $customentry->icon = $replacedicons[$customentry->icon];
3419                  $changed = true;
3420              }
3421          }
3422  
3423          if ($changed) {
3424              // Save the new customfiletypes.
3425              set_config('customfiletypes', json_encode($custom));
3426          }
3427  
3428          // Main savepoint reached.
3429          upgrade_main_savepoint(true, 2023082200.01);
3430      }
3431  
3432      if ($oldversion < 2023082200.02) {
3433          // Some MIME icons have been removed. They need to be replaced to 'unknown' for custom MIME types.
3434          $removedicons = array_flip([
3435              'clip-353',
3436              'edit',
3437              'env',
3438              'explore',
3439              'folder-open',
3440              'help',
3441              'move',
3442              'parent',
3443          ]);
3444  
3445          $custom = [];
3446          if (!empty($CFG->customfiletypes)) {
3447              if (array_key_exists('customfiletypes', $CFG->config_php_settings)) {
3448                  // It's set in config.php, so the MIME icons can't be upgraded automatically.
3449                  echo("\nYou need to manually check customfiletypes in config.php because some MIME icons have been removed!\n");
3450              } else {
3451                  // It's a JSON string in the config table.
3452                  $custom = json_decode($CFG->customfiletypes);
3453              }
3454          }
3455  
3456          $changed = false;
3457          foreach ($custom as $customentry) {
3458              if (!empty($customentry->icon) && array_key_exists($customentry->icon, $removedicons)) {
3459                  // The icon has been removed, so set it to unknown.
3460                  $customentry->icon = 'unknown';
3461                  $changed = true;
3462              }
3463          }
3464  
3465          if ($changed) {
3466              // Save the new customfiletypes.
3467              set_config('customfiletypes', json_encode($custom));
3468          }
3469  
3470          // Main savepoint reached.
3471          upgrade_main_savepoint(true, 2023082200.02);
3472      }
3473  
3474      if ($oldversion < 2023082200.04) {
3475  
3476          // Remove any non-unique filters/conditions.
3477          $duplicates = $DB->get_records_sql("
3478              SELECT MIN(id) AS id, reportid, uniqueidentifier, iscondition
3479                FROM {reportbuilder_filter}
3480            GROUP BY reportid, uniqueidentifier, iscondition
3481              HAVING COUNT(*) > 1");
3482  
3483          foreach ($duplicates as $duplicate) {
3484              $DB->delete_records_select(
3485                  'reportbuilder_filter',
3486                  'id <> :id AND reportid = :reportid AND uniqueidentifier = :uniqueidentifier AND iscondition = :iscondition',
3487                  (array) $duplicate
3488              );
3489          }
3490  
3491          // Define index report-filter (unique) to be added to reportbuilder_filter.
3492          $table = new xmldb_table('reportbuilder_filter');
3493          $index = new xmldb_index('report-filter', XMLDB_INDEX_UNIQUE, ['reportid', 'uniqueidentifier', 'iscondition']);
3494  
3495          // Conditionally launch add index report-filter.
3496          if (!$dbman->index_exists($table, $index)) {
3497              $dbman->add_index($table, $index);
3498          }
3499  
3500          // Main savepoint reached.
3501          upgrade_main_savepoint(true, 2023082200.04);
3502      }
3503  
3504      if ($oldversion < 2023082600.02) {
3505          // Get all the ids of users who still have md5 hashed passwords.
3506          if ($DB->sql_regex_supported()) {
3507              // If the database supports regex, we can add an exact check for md5.
3508              $condition = 'password ' . $DB->sql_regex() . ' :pattern';
3509              $params = ['pattern' => "^[a-fA-F0-9]{32}$"];
3510          } else {
3511              // Otherwise, we need to use a NOT LIKE condition and rule out bcrypt.
3512              $condition = $DB->sql_like('password', ':pattern', true, false, true);
3513              $params = ['pattern' => '$2y$%'];
3514          }
3515  
3516          // Regardless of database regex support we check the hash length which should be enough.
3517          // But extra regex or like matching makes sure.
3518          $sql = "SELECT id FROM {user} WHERE " . $DB->sql_length('password') . " = 32 AND $condition";
3519          $userids = $DB->get_fieldset_sql($sql, $params);
3520  
3521          // Update the password for each user with a new SHA-512 hash.
3522          // Users won't know this password, but they can reset it. This is a security measure,
3523          // in case the database is compromised or the hash has been leaked elsewhere.
3524          foreach ($userids as $userid) {
3525              $password = base64_encode(random_bytes(24)); // Generate a new password for the user.
3526  
3527              $user = new \stdClass();
3528              $user->id = $userid;
3529              $user->password = hash_internal_user_password($password);
3530              $DB->update_record('user', $user, true);
3531          }
3532  
3533          // Main savepoint reached.
3534          upgrade_main_savepoint(true, 2023082600.02);
3535      }
3536  
3537      if ($oldversion < 2023082600.03) {
3538          // The previous default configuration had a typo, check for its presence and correct if necessary.
3539          $sensiblesettings = get_config('adminpresets', 'sensiblesettings');
3540          if (strpos($sensiblesettings, 'smtppass@none') !== false) {
3541              $newsensiblesettings = str_replace('smtppass@none', 'smtppass@@none', $sensiblesettings);
3542              set_config('sensiblesettings', $newsensiblesettings, 'adminpresets');
3543          }
3544  
3545          // Main savepoint reached.
3546          upgrade_main_savepoint(true, 2023082600.03);
3547      }
3548  
3549      if ($oldversion < 2023082600.05) {
3550          unset_config('completiondefault');
3551  
3552          // Main savepoint reached.
3553          upgrade_main_savepoint(true, 2023082600.05);
3554      }
3555  
3556      if ($oldversion < 2023090100.00) {
3557          // Upgrade MIME type for existing PSD files.
3558          $DB->set_field_select(
3559              'files',
3560              'mimetype',
3561              'image/vnd.adobe.photoshop',
3562              $DB->sql_like('filename', '?', false),
3563              ['%.psd']
3564          );
3565  
3566          // Main savepoint reached.
3567          upgrade_main_savepoint(true, 2023090100.00);
3568      }
3569  
3570      if ($oldversion < 2023090200.01) {
3571  
3572          // Define table moodlenet_share_progress to be created.
3573          $table = new xmldb_table('moodlenet_share_progress');
3574  
3575          // Adding fields to table moodlenet_share_progress.
3576          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3577          $table->add_field('type', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, null);
3578          $table->add_field('courseid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
3579          $table->add_field('cmid', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
3580          $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
3581          $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
3582          $table->add_field('resourceurl', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3583          $table->add_field('status', XMLDB_TYPE_INTEGER, '2', null, null, null, null);
3584  
3585          // Adding keys to table moodlenet_share_progress.
3586          $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
3587  
3588          // Conditionally launch create table for moodlenet_share_progress.
3589          if (!$dbman->table_exists($table)) {
3590              $dbman->create_table($table);
3591          }
3592  
3593          // Main savepoint reached.
3594          upgrade_main_savepoint(true, 2023090200.01);
3595      }
3596  
3597      if ($oldversion < 2023091300.03) {
3598          // Delete all the searchanywhere prefs in user_preferences table.
3599          $DB->delete_records('user_preferences', ['name' => 'userselector_searchanywhere']);
3600          // Main savepoint reached.
3601          upgrade_main_savepoint(true, 2023091300.03);
3602      }
3603  
3604      if ($oldversion < 2023100400.01) {
3605          // Delete datakey with datavalue -1.
3606          $DB->delete_records('messageinbound_datakeys', ['datavalue' => '-1']);
3607          // Main savepoint reached.
3608          upgrade_main_savepoint(true, 2023100400.01);
3609      }
3610  
3611      if ($oldversion < 2023100400.03) {
3612          // Define field id to be added to communication.
3613          $table = new xmldb_table('communication');
3614  
3615          // Add the field and allow it to be nullable.
3616          // We need to backfill data before setting it to NOT NULL.
3617          $field = new xmldb_field(
3618              name: 'contextid',
3619              type: XMLDB_TYPE_INTEGER,
3620              precision: '10',
3621              notnull: null,
3622              previous: 'id',
3623          );
3624  
3625          // Conditionally launch add field id.
3626          if (!$dbman->field_exists($table, $field)) {
3627              $dbman->add_field($table, $field);
3628          }
3629  
3630          // Fill the existing data.
3631          $sql = <<<EOF
3632                      SELECT comm.id, c.id AS contextid
3633                        FROM {communication} comm
3634                  INNER JOIN {context} c ON c.instanceid = comm.instanceid AND c.contextlevel = :contextcourse
3635                       WHERE comm.contextid IS NULL
3636                         AND comm.instancetype = :instancetype
3637          EOF;
3638          $rs = $DB->get_recordset_sql(
3639              sql: $sql,
3640              params: [
3641                  'contextcourse' => CONTEXT_COURSE,
3642                  'instancetype' => 'coursecommunication',
3643              ],
3644          );
3645          foreach ($rs as $comm) {
3646              $DB->set_field(
3647                  table: 'communication',
3648                  newfield: 'contextid',
3649                  newvalue: $comm->contextid,
3650                  conditions: [
3651                      'id' => $comm->id,
3652                  ],
3653              );
3654          }
3655          $rs->close();
3656  
3657          $systemcontext = \core\context\system::instance();
3658          $DB->set_field_select(
3659              table: 'communication',
3660              newfield: 'contextid',
3661              newvalue: $systemcontext->id,
3662              select: 'contextid IS NULL',
3663          );
3664  
3665          // Now make it NOTNULL.
3666          $field = new xmldb_field(
3667              name: 'contextid',
3668              type: XMLDB_TYPE_INTEGER,
3669              precision: '10',
3670              notnull:  XMLDB_NOTNULL,
3671          );
3672          $dbman->change_field_notnull($table, $field);
3673  
3674          // Add the contextid constraint.
3675          $key = new xmldb_key('contextid', XMLDB_KEY_FOREIGN, ['contextid'], 'context', ['id']);
3676          $dbman->add_key($table, $key);
3677  
3678          // Main savepoint reached.
3679          upgrade_main_savepoint(true, 2023100400.03);
3680      }
3681  
3682      // Automatically generated Moodle v4.3.0 release upgrade line.
3683      // Put any upgrade step following this.
3684  
3685      return true;
3686  }