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