return $tables;
}
-/**
- * Remove all signed numbers from current database and change
- * text fields to long texts - mysql only.
- */
-function upgrade_mysql_fix_unsigned_and_lob_columns() {
- // We are not using standard API for changes of column
- // because everything 'signed'-related will be removed soon.
-
- // If anybody already has numbers higher than signed limit the execution stops
- // and tables must be fixed manually before continuing upgrade.
-
- global $DB;
-
- if ($DB->get_dbfamily() !== 'mysql') {
- return;
- }
-
- $pbar = new progress_bar('mysqlconvertunsignedlobs', 500, true);
-
- $prefix = $DB->get_prefix();
- $tables = upgrade_mysql_get_supported_tables();
-
- $tablecount = count($tables);
- $i = 0;
- foreach ($tables as $table) {
- $i++;
-
- $changes = array();
-
- $sql = "SHOW COLUMNS FROM `{{$table}}`";
- $rs = $DB->get_recordset_sql($sql);
- foreach ($rs as $column) {
- $column = (object)array_change_key_case((array)$column, CASE_LOWER);
- if (stripos($column->type, 'unsigned') !== false) {
- $maxvalue = 0;
- if (preg_match('/^int/i', $column->type)) {
- $maxvalue = 2147483647;
- } else if (preg_match('/^medium/i', $column->type)) {
- $maxvalue = 8388607;
- } else if (preg_match('/^smallint/i', $column->type)) {
- $maxvalue = 32767;
- } else if (preg_match('/^tinyint/i', $column->type)) {
- $maxvalue = 127;
- }
- if ($maxvalue) {
- // Make sure nobody is abusing our integer ranges - moodle int sizes are in digits, not bytes!!!
- $invalidcount = $DB->get_field_sql("SELECT COUNT('x') FROM `{{$table}}` WHERE `$column->field` > :maxnumber", array('maxnumber'=>$maxvalue));
- if ($invalidcount) {
- throw new moodle_exception('notlocalisederrormessage', 'error', new moodle_url('/admin/'), "Database table '{$table}'' contains unsigned column '{$column->field}' with $invalidcount values that are out of allowed range, upgrade can not continue.");
- }
- }
- $type = preg_replace('/unsigned/i', 'signed', $column->type);
- $notnull = ($column->null === 'NO') ? 'NOT NULL' : 'NULL';
- $default = (!is_null($column->default) and $column->default !== '') ? "DEFAULT '$column->default'" : '';
- $autoinc = (stripos($column->extra, 'auto_increment') !== false) ? 'AUTO_INCREMENT' : '';
- // Primary and unique not necessary here, change_database_structure does not add prefix.
- $changes[] = "MODIFY COLUMN `$column->field` $type $notnull $default $autoinc";
-
- } else if ($column->type === 'tinytext' or $column->type === 'mediumtext' or $column->type === 'text') {
- $notnull = ($column->null === 'NO') ? 'NOT NULL' : 'NULL';
- $default = (!is_null($column->default) and $column->default !== '') ? "DEFAULT '$column->default'" : '';
- // Primary, unique and inc are not supported for texts.
- $changes[] = "MODIFY COLUMN `$column->field` LONGTEXT $notnull $default";
-
- } else if ($column->type === 'tinyblob' or $column->type === 'mediumblob' or $column->type === 'blob') {
- $notnull = ($column->null === 'NO') ? 'NOT NULL' : 'NULL';
- $default = (!is_null($column->default) and $column->default !== '') ? "DEFAULT '$column->default'" : '';
- // Primary, unique and inc are not supported for blobs.
- $changes[] = "MODIFY COLUMN `$column->field` LONGBLOB $notnull $default";
- }
-
- }
- $rs->close();
-
- if ($changes) {
- // Set appropriate timeout - 1 minute per thousand of records should be enough, min 60 minutes just in case.
- $count = $DB->count_records($table, array());
- $timeout = ($count/1000)*60;
- $timeout = ($timeout < 60*60) ? 60*60 : (int)$timeout;
- upgrade_set_timeout($timeout);
-
- $sql = "ALTER TABLE `{$prefix}$table` ".implode(', ', $changes);
- $DB->change_database_structure($sql);
- }
-
- $pbar->update($i, $tablecount, "Converted unsigned/lob columns in MySQL database - $i/$tablecount.");
- }
-}
-
-/**
- * Migrate NTEXT to NVARCHAR(MAX).
- */
-function upgrade_mssql_nvarcharmax() {
- global $DB;
-
- if ($DB->get_dbfamily() !== 'mssql') {
- return;
- }
-
- $pbar = new progress_bar('mssqlconvertntext', 500, true);
-
- $prefix = $DB->get_prefix();
- $tables = $DB->get_tables(false);
-
- $tablecount = count($tables);
- $i = 0;
- foreach ($tables as $table) {
- $i++;
-
- $columns = array();
-
- $sql = "SELECT column_name
- FROM INFORMATION_SCHEMA.COLUMNS
- WHERE table_name = '{{$table}}' AND UPPER(data_type) = 'NTEXT'";
- $rs = $DB->get_recordset_sql($sql);
- foreach ($rs as $column) {
- $columns[] = $column->column_name;
- }
- $rs->close();
-
- if ($columns) {
- // Set appropriate timeout - 1 minute per thousand of records should be enough, min 60 minutes just in case.
- $count = $DB->count_records($table, array());
- $timeout = ($count/1000)*60;
- $timeout = ($timeout < 60*60) ? 60*60 : (int)$timeout;
- upgrade_set_timeout($timeout);
-
- $updates = array();
- foreach ($columns as $column) {
- // Change the definition.
- $sql = "ALTER TABLE {$prefix}$table ALTER COLUMN $column NVARCHAR(MAX)";
- $DB->change_database_structure($sql);
- $updates[] = "$column = $column";
- }
-
- // Now force the migration of text data to new optimised storage.
- $sql = "UPDATE {{$table}} SET ".implode(', ', $updates);
- $DB->execute($sql);
- }
-
- $pbar->update($i, $tablecount, "Converted NTEXT to NVARCHAR(MAX) columns in MS SQL Server database - $i/$tablecount.");
- }
-}
-
-/**
- * Migrate IMAGE to VARBINARY(MAX).
- */
-function upgrade_mssql_varbinarymax() {
- global $DB;
-
- if ($DB->get_dbfamily() !== 'mssql') {
- return;
- }
-
- $pbar = new progress_bar('mssqlconvertimage', 500, true);
-
- $prefix = $DB->get_prefix();
- $tables = $DB->get_tables(false);
-
- $tablecount = count($tables);
- $i = 0;
- foreach ($tables as $table) {
- $i++;
-
- $columns = array();
-
- $sql = "SELECT column_name
- FROM INFORMATION_SCHEMA.COLUMNS
- WHERE table_name = '{{$table}}' AND UPPER(data_type) = 'IMAGE'";
- $rs = $DB->get_recordset_sql($sql);
- foreach ($rs as $column) {
- $columns[] = $column->column_name;
- }
- $rs->close();
-
- if ($columns) {
- // Set appropriate timeout - 1 minute per thousand of records should be enough, min 60 minutes just in case.
- $count = $DB->count_records($table, array());
- $timeout = ($count/1000)*60;
- $timeout = ($timeout < 60*60) ? 60*60 : (int)$timeout;
- upgrade_set_timeout($timeout);
-
- foreach ($columns as $column) {
- // Change the definition.
- $sql = "ALTER TABLE {$prefix}$table ALTER COLUMN $column VARBINARY(MAX)";
- $DB->change_database_structure($sql);
- }
-
- // Binary columns should not be used, do not waste time optimising the storage.
- }
-
- $pbar->update($i, $tablecount, "Converted IMAGE to VARBINARY(MAX) columns in MS SQL Server database - $i/$tablecount.");
- }
-}
-
-/**
- * This upgrade script fixes the mismatches between DB fields course_modules.section
- * and course_sections.sequence. It makes sure that each module is included
- * in the sequence of at least one section.
- * Note that this script is different from admin/cli/fix_course_sortorder.php
- * in the following ways:
- * 1. It does not fix the cases when module appears several times in section(s) sequence(s) -
- * it will be done automatically on the next viewing of the course.
- * 2. It does not remove non-existing modules from section sequences - administrator
- * has to run the CLI script to do it.
- * 3. When this script finds an orphaned module it adds it to the section but makes hidden
- * where CLI script does not change the visiblity specified in the course_modules table.
- */
-function upgrade_course_modules_sequences() {
- global $DB;
-
- // Find all modules that point to the section which does not point back to this module.
- $sequenceconcat = $DB->sql_concat("','", "s.sequence", "','");
- $moduleconcat = $DB->sql_concat("'%,'", "m.id", "',%'");
- $sql = "SELECT m.id, m.course, m.section, s.sequence
- FROM {course_modules} m LEFT OUTER JOIN {course_sections} s
- ON m.course = s.course and m.section = s.id
- WHERE s.sequence IS NULL OR ($sequenceconcat NOT LIKE $moduleconcat)
- ORDER BY m.course";
- $rs = $DB->get_recordset_sql($sql);
- $sections = null;
- foreach ($rs as $cm) {
- if (!isset($sections[$cm->course])) {
- // Retrieve all sections for the course (only once for each corrupt course).
- $sections = array($cm->course =>
- $DB->get_records('course_sections', array('course' => $cm->course),
- 'section', 'id, section, sequence, visible'));
- if (empty($sections[$cm->course])) {
- // Very odd - the course has a module in it but has no sections. Create 0-section.
- $newsection = array('sequence' => '', 'section' => 0, 'visible' => 1);
- $newsection['id'] = $DB->insert_record('course_sections',
- $newsection + array('course' => $cm->course, 'summary' => '', 'summaryformat' => FORMAT_HTML));
- $sections[$cm->course] = array($newsection['id'] => (object)$newsection);
- }
- }
- // Attempt to find the section that has this module in it's sequence.
- // If there are several of them, pick the last because this is what get_fast_modinfo() does.
- $sectionid = null;
- foreach ($sections[$cm->course] as $section) {
- if (!empty($section->sequence) && in_array($cm->id, preg_split('/,/', $section->sequence))) {
- $sectionid = $section->id;
- }
- }
- if ($sectionid) {
- // Found the section. Update course_module to point to the correct section.
- $params = array('id' => $cm->id, 'section' => $sectionid);
- if (!$sections[$cm->course][$sectionid]->visible) {
- $params['visible'] = 0;
- }
- $DB->update_record('course_modules', $params);
- } else {
- // No section in the course has this module in it's sequence.
- if (isset($sections[$cm->course][$cm->section])) {
- // Try to add module to the section it points to (if it is valid).
- $sectionid = $cm->section;
- } else {
- // Section not found. Just add to the first available section.
- reset($sections[$cm->course]);
- $sectionid = key($sections[$cm->course]);
- }
- $newsequence = ltrim($sections[$cm->course][$sectionid]->sequence . ',' . $cm->id, ',');
- $sections[$cm->course][$sectionid]->sequence = $newsequence;
- $DB->update_record('course_sections', array('id' => $sectionid, 'sequence' => $newsequence));
- // Make module invisible because it was not displayed at all before this upgrade script.
- $DB->update_record('course_modules', array('id' => $cm->id, 'section' => $sectionid, 'visible' => 0, 'visibleold' => 0));
- }
- }
- $rs->close();
- unset($sections);
-
- // Note that we don't need to reset course cache here because it is reset automatically after upgrade.
-}
-
-/**
- * Updates a single item (course module or course section) to transfer the
- * availability settings from the old to the new format.
- *
- * Note: We do not convert groupmembersonly for modules at present. If we did,
- * $groupmembersonly would be set to the groupmembersonly option for the
- * module. Since we don't, it will be set to 0 for modules, and 1 for sections
- * if they have a grouping.
- *
- * @param int $groupmembersonly 1 if activity has groupmembersonly option
- * @param int $groupingid Grouping id (0 = none)
- * @param int $availablefrom Available from time (0 = none)
- * @param int $availableuntil Available until time (0 = none)
- * @param int $showavailability Show availability (1) or hide activity entirely
- * @param array $availrecs Records from course_modules/sections_availability
- * @param array $fieldrecs Records from course_modules/sections_avail_fields
- */
-function upgrade_availability_item($groupmembersonly, $groupingid,
- $availablefrom, $availableuntil, $showavailability,
- array $availrecs, array $fieldrecs) {
- global $CFG, $DB;
- $conditions = array();
- $shows = array();
-
- // Group members only condition (if enabled).
- if ($CFG->enablegroupmembersonly && $groupmembersonly) {
- if ($groupingid) {
- $conditions[] = '{"type":"grouping"' .
- ($groupingid ? ',"id":' . $groupingid : '') . '}';
- } else {
- // No grouping specified, so allow any group.
- $conditions[] = '{"type":"group"}';
- }
- // Group members only condition was not displayed to students.
- $shows[] = 'false';
-
- // In the unlikely event that the site had enablegroupmembers only
- // but NOT enableavailability, we need to turn this on now.
- if (!$CFG->enableavailability) {
- set_config('enableavailability', 1);
- }
- }
-
- // Date conditions.
- if ($availablefrom) {
- $conditions[] = '{"type":"date","d":">=","t":' . $availablefrom . '}';
- $shows[] = $showavailability ? 'true' : 'false';
- }
- if ($availableuntil) {
- $conditions[] = '{"type":"date","d":"<","t":' . $availableuntil . '}';
- // Until dates never showed to students.
- $shows[] = 'false';
- }
-
- // Conditions from _availability table.
- foreach ($availrecs as $rec) {
- if (!empty($rec->sourcecmid)) {
- // Completion condition.
- $conditions[] = '{"type":"completion","cm":' . $rec->sourcecmid .
- ',"e":' . $rec->requiredcompletion . '}';
- } else {
- // Grade condition.
- $minmax = '';
- if (!empty($rec->grademin)) {
- $minmax .= ',"min":' . sprintf('%.5f', $rec->grademin);
- }
- if (!empty($rec->grademax)) {
- $minmax .= ',"max":' . sprintf('%.5f', $rec->grademax);
- }
- $conditions[] = '{"type":"grade","id":' . $rec->gradeitemid . $minmax . '}';
- }
- $shows[] = $showavailability ? 'true' : 'false';
- }
-
- // Conditions from _fields table.
- foreach ($fieldrecs as $rec) {
- if (isset($rec->userfield)) {
- // Standard field.
- $fieldbit = ',"sf":' . json_encode($rec->userfield);
- } else {
- // Custom field.
- $fieldbit = ',"cf":' . json_encode($rec->shortname);
- }
- // Value is not included for certain operators.
- switch($rec->operator) {
- case 'isempty':
- case 'isnotempty':
- $valuebit = '';
- break;
-
- default:
- $valuebit = ',"v":' . json_encode($rec->value);
- break;
- }
- $conditions[] = '{"type":"profile","op":"' . $rec->operator . '"' .
- $fieldbit . $valuebit . '}';
- $shows[] = $showavailability ? 'true' : 'false';
- }
-
- // If there are some conditions, set them into database.
- if ($conditions) {
- return '{"op":"&","showc":[' . implode(',', $shows) . '],' .
- '"c":[' . implode(',', $conditions) . ']}';
- } else {
- return null;
- }
-}
-
/**
* Using data for a single course-module that has groupmembersonly enabled,
* returns the new availability value that incorporates the correct
$this->assertFalse(upgrade_stale_php_files_present());
}
- /**
- * Test the {@link upgrade_grade_item_fix_sortorder() function with
- * faked duplicate sortorder data.
- */
- public function test_upgrade_grade_item_fix_sortorder() {
- global $DB;
-
- $this->resetAfterTest(true);
-
- // The purpose of this test is to make sure that after upgrade script
- // there is no duplicates in the field grade_items.sortorder (for each course)
- // and the result of query "SELECT id FROM grade_items WHERE courseid=? ORDER BY sortorder, id" does not change.
- $sequencesql = 'SELECT id FROM {grade_items} WHERE courseid=? ORDER BY sortorder, id';
-
- // Each set is used for filling the db with fake data and will be representing the result of query:
- // "SELECT sortorder from {grade_items} WHERE courseid=? ORDER BY id".
- $testsets = array(
- // Items that need no action.
- array(1,2,3),
- array(5,6,7),
- array(7,6,1,3,2,5),
- // Items with sortorder duplicates
- array(1,2,2,3,3,4,5),
- // Only one sortorder duplicate.
- array(1,1),
- array(3,3),
- // Non-sequential sortorders with one or multiple duplicates.
- array(3,3,7,5,6,6,9,10,8,3),
- array(7,7,3),
- array(3,4,5,3,5,4,7,1)
- );
- $origsequences = array();
-
- // Generate the data and remember the initial sequence or items.
- foreach ($testsets as $testset) {
- $course = $this->getDataGenerator()->create_course();
- foreach ($testset as $sortorder) {
- $this->insert_fake_grade_item_sortorder($course->id, $sortorder);
- }
- $DB->get_records('grade_items');
- $origsequences[$course->id] = $DB->get_fieldset_sql($sequencesql, array($course->id));
- }
-
- $duplicatedetectionsql = "SELECT courseid, sortorder
- FROM {grade_items}
- GROUP BY courseid, sortorder
- HAVING COUNT(id) > 1";
-
- // Verify there are duplicates before we start the fix.
- $dupes = $DB->record_exists_sql($duplicatedetectionsql);
- $this->assertTrue($dupes);
-
- // Do the work.
- upgrade_grade_item_fix_sortorder();
-
- // Verify that no duplicates are left in the database.
- $dupes = $DB->record_exists_sql($duplicatedetectionsql);
- $this->assertFalse($dupes);
-
- // Verify that sequences are exactly the same as they were before upgrade script.
- $idx = 0;
- foreach ($origsequences as $courseid => $origsequence) {
- if (count(($testsets[$idx])) == count(array_unique($testsets[$idx]))) {
- // If there were no duplicates for this course verify that sortorders are not modified.
- $newsortorders = $DB->get_fieldset_sql("SELECT sortorder from {grade_items} WHERE courseid=? ORDER BY id", array($courseid));
- $this->assertEquals($testsets[$idx], $newsortorders);
- }
- $newsequence = $DB->get_fieldset_sql($sequencesql, array($courseid));
- $this->assertEquals($origsequence, $newsequence,
- "Sequences do not match for test set $idx : ".join(',', $testsets[$idx]));
- $idx++;
- }
- }
-
/**
* Populate some fake grade items into the database with specified
* sortorder and course id.
return $DB->get_record('grade_items', array('id' => $item->id));
}
- public function test_upgrade_fix_missing_root_folders() {
- global $DB, $SITE;
-
- $this->resetAfterTest(true);
-
- // Setup some broken data...
- // Create two resources (and associated file areas).
- $this->setAdminUser();
- $resource1 = $this->getDataGenerator()->get_plugin_generator('mod_resource')
- ->create_instance(array('course' => $SITE->id));
- $resource2 = $this->getDataGenerator()->get_plugin_generator('mod_resource')
- ->create_instance(array('course' => $SITE->id));
-
- // Delete the folder record of resource1 to simulate broken data.
- $context = context_module::instance($resource1->cmid);
- $selectargs = array('contextid' => $context->id,
- 'component' => 'mod_resource',
- 'filearea' => 'content',
- 'itemid' => 0);
-
- // Verify file records exist.
- $areafilecount = $DB->count_records('files', $selectargs);
- $this->assertNotEmpty($areafilecount);
-
- // Delete the folder record.
- $folderrecord = $selectargs;
- $folderrecord['filepath'] = '/';
- $folderrecord['filename'] = '.';
-
- // Get previous folder record.
- $oldrecord = $DB->get_record('files', $folderrecord);
- $DB->delete_records('files', $folderrecord);
-
- // Verify the folder record has been removed.
- $newareafilecount = $DB->count_records('files', $selectargs);
- $this->assertSame($newareafilecount, $areafilecount - 1);
-
- $this->assertFalse($DB->record_exists('files', $folderrecord));
-
- // Run the upgrade step!
- upgrade_fix_missing_root_folders();
-
- // Verify the folder record has been restored.
- $newareafilecount = $DB->count_records('files', $selectargs);
- $this->assertSame($newareafilecount, $areafilecount);
-
- $newrecord = $DB->get_record('files', $folderrecord, '*', MUST_EXIST);
- // Verify the hash is correctly created.
- $this->assertSame($oldrecord->pathnamehash, $newrecord->pathnamehash);
- }
-
public function test_upgrade_fix_missing_root_folders_draft() {
global $DB, $SITE;
$this->assertEquals($originalhash, $newhash);
}
- /**
- * Tests the upgrade of an individual course-module or section from the
- * old to new availability system. (This test does not use the database
- * so it can run any time.)
- */
- public function test_upgrade_availability_item() {
- global $CFG;
- $this->resetAfterTest();
-
- // This function is in the other upgradelib.
- require_once($CFG->libdir . '/db/upgradelib.php');
-
- // Groupmembersonly (or nothing). Show option on but ignored.
- // Note: This $CFG option doesn't exist any more but we are testing the
- // upgrade function so it did exist then...
- $CFG->enablegroupmembersonly = 0;
- $this->assertNull(
- upgrade_availability_item(1, 0, 0, 0, 1, array(), array()));
- $CFG->enablegroupmembersonly = 1;
- $this->assertNull(
- upgrade_availability_item(0, 0, 0, 0, 1, array(), array()));
- $this->assertEquals(
- '{"op":"&","showc":[false],"c":[{"type":"group"}]}',
- upgrade_availability_item(1, 0, 0, 0, 1, array(), array()));
- $this->assertEquals(
- '{"op":"&","showc":[false],"c":[{"type":"grouping","id":4}]}',
- upgrade_availability_item(1, 4, 0, 0, 1, array(), array()));
-
- // Dates (with show/hide options - until date always hides).
- $this->assertEquals(
- '{"op":"&","showc":[true],"c":[{"type":"date","d":">=","t":996}]}',
- upgrade_availability_item(0, 0, 996, 0, 1, array(), array()));
- $this->assertEquals(
- '{"op":"&","showc":[false],"c":[{"type":"date","d":">=","t":997}]}',
- upgrade_availability_item(0, 0, 997, 0, 0, array(), array()));
- $this->assertEquals(
- '{"op":"&","showc":[false],"c":[{"type":"date","d":"<","t":998}]}',
- upgrade_availability_item(0, 0, 0, 998, 1, array(), array()));
- $this->assertEquals(
- '{"op":"&","showc":[true,false],"c":[' .
- '{"type":"date","d":">=","t":995},{"type":"date","d":"<","t":999}]}',
- upgrade_availability_item(0, 0, 995, 999, 1, array(), array()));
-
- // Grade (show option works as normal).
- $availrec = (object)array(
- 'sourcecmid' => null, 'requiredcompletion' => null,
- 'gradeitemid' => 13, 'grademin' => null, 'grademax' => null);
- $this->assertEquals(
- '{"op":"&","showc":[true],"c":[{"type":"grade","id":13}]}',
- upgrade_availability_item(0, 0, 0, 0, 1, array($availrec), array()));
- $availrec->grademin = 4.1;
- $this->assertEquals(
- '{"op":"&","showc":[false],"c":[{"type":"grade","id":13,"min":4.10000}]}',
- upgrade_availability_item(0, 0, 0, 0, 0, array($availrec), array()));
- $availrec->grademax = 9.9;
- $this->assertEquals(
- '{"op":"&","showc":[true],"c":[{"type":"grade","id":13,"min":4.10000,"max":9.90000}]}',
- upgrade_availability_item(0, 0, 0, 0, 1, array($availrec), array()));
- $availrec->grademin = null;
- $this->assertEquals(
- '{"op":"&","showc":[true],"c":[{"type":"grade","id":13,"max":9.90000}]}',
- upgrade_availability_item(0, 0, 0, 0, 1, array($availrec), array()));
-
- // Completion (show option normal).
- $availrec->grademax = null;
- $availrec->gradeitemid = null;
- $availrec->sourcecmid = 666;
- $availrec->requiredcompletion = 1;
- $this->assertEquals(
- '{"op":"&","showc":[true],"c":[{"type":"completion","cm":666,"e":1}]}',
- upgrade_availability_item(0, 0, 0, 0, 1, array($availrec), array()));
- $this->assertEquals(
- '{"op":"&","showc":[false],"c":[{"type":"completion","cm":666,"e":1}]}',
- upgrade_availability_item(0, 0, 0, 0, 0, array($availrec), array()));
-
- // Profile conditions (custom/standard field, values/not, show option normal).
- $fieldrec = (object)array('userfield' => 'email', 'operator' => 'isempty',
- 'value' => '', 'shortname' => null);
- $this->assertEquals(
- '{"op":"&","showc":[true],"c":[{"type":"profile","op":"isempty","sf":"email"}]}',
- upgrade_availability_item(0, 0, 0, 0, 1, array(), array($fieldrec)));
- $fieldrec->value = '@';
- $fieldrec->operator = 'contains';
- $this->assertEquals(
- '{"op":"&","showc":[true],"c":[{"type":"profile","op":"contains","sf":"email","v":"@"}]}',
- upgrade_availability_item(0, 0, 0, 0, 1, array(), array($fieldrec)));
- $fieldrec->operator = 'isnotempty';
- $fieldrec->userfield = null;
- $fieldrec->shortname = 'frogtype';
- $this->assertEquals(
- '{"op":"&","showc":[false],"c":[{"type":"profile","op":"isnotempty","cf":"frogtype"}]}',
- upgrade_availability_item(0, 0, 0, 0, 0, array(), array($fieldrec)));
-
- // Everything at once.
- $this->assertEquals('{"op":"&","showc":[false,true,false,true,true,true],' .
- '"c":[{"type":"grouping","id":13},' .
- '{"type":"date","d":">=","t":990},' .
- '{"type":"date","d":"<","t":991},' .
- '{"type":"grade","id":665,"min":70.00000},' .
- '{"type":"completion","cm":42,"e":2},' .
- '{"type":"profile","op":"isempty","sf":"email"}]}',
- upgrade_availability_item(1, 13, 990, 991, 1, array(
- (object)array('sourcecmid' => null, 'gradeitemid' => 665, 'grademin' => 70),
- (object)array('sourcecmid' => 42, 'gradeitemid' => null, 'requiredcompletion' => 2)
- ), array(
- (object)array('userfield' => 'email', 'shortname' => null, 'operator' => 'isempty'),
- )));
- }
-
/**
* Test upgrade minmaxgrade step.
*/
$this->resetAfterTest(true);
+ require_once($CFG->libdir . '/db/upgradelib.php');
+
$c = array();
$a = array();
$gi = array();
*/
public function test_upgrade_calculated_grade_items_freeze() {
global $DB, $CFG;
+
$this->resetAfterTest();
+ require_once($CFG->libdir . '/db/upgradelib.php');
+
// Create a user.
$user = $this->getDataGenerator()->create_user();
function test_upgrade_calculated_grade_items_regrade() {
global $DB, $CFG;
+
$this->resetAfterTest();
+ require_once($CFG->libdir . '/db/upgradelib.php');
+
// Create a user.
$user = $this->getDataGenerator()->create_user();
}
public function test_upgrade_course_tags() {
- global $DB;
+ global $DB, $CFG;
+
$this->resetAfterTest();
+ require_once($CFG->libdir . '/db/upgradelib.php');
+
// Running upgrade script when there are no tags.
upgrade_course_tags();
$this->assertFalse($DB->record_exists('tag_instance', array()));
=== 3.1 ===
+* The following functions, previously used (exclusively) by upgrade steps are not available
+ anymore because of the upgrade cleanup performed for this version. See MDL-51580 for more info:
+ - upgrade_mysql_fix_unsigned_and_lob_columns()
+ - upgrade_course_completion_remove_duplicates()
+ - upgrade_save_orphaned_questions()
+ - upgrade_rename_old_backup_files_using_shortname()
+ - upgrade_mssql_nvarcharmax()
+ - upgrade_mssql_varbinarymax()
+ - upgrade_fix_missing_root_folders()
+ - upgrade_course_modules_sequences()
+ - upgrade_grade_item_fix_sortorder()
+ - upgrade_availability_item()
+
* Plugins can extend the navigation for user by declaring the following callback:
<frankenstyle>_extend_navigation_user(navigation_node $parentnode, stdClass $user,
context_user $context, stdClass $course,
return $profile;
}
-
-/**
- * This function finds duplicate records (based on combinations of fields that should be unique)
- * and then progamatically generated a "most correct" version of the data, update and removing
- * records as appropriate
- *
- * Thanks to Dan Marsden for help
- *
- * @param string $table Table name
- * @param array $uniques Array of field names that should be unique
- * @param array $fieldstocheck Array of fields to generate "correct" data from (optional)
- * @return void
- */
-function upgrade_course_completion_remove_duplicates($table, $uniques, $fieldstocheck = array()) {
- global $DB;
-
- // Find duplicates
- $sql_cols = implode(', ', $uniques);
-
- $sql = "SELECT {$sql_cols} FROM {{$table}} GROUP BY {$sql_cols} HAVING (count(id) > 1)";
- $duplicates = $DB->get_recordset_sql($sql, array());
-
- // Loop through duplicates
- foreach ($duplicates as $duplicate) {
- $pointer = 0;
-
- // Generate SQL for finding records with these duplicate uniques
- $sql_select = implode(' = ? AND ', $uniques).' = ?'; // builds "fieldname = ? AND fieldname = ?"
- $uniq_values = array();
- foreach ($uniques as $u) {
- $uniq_values[] = $duplicate->$u;
- }
-
- $sql_order = implode(' DESC, ', $uniques).' DESC'; // builds "fieldname DESC, fieldname DESC"
-
- // Get records with these duplicate uniques
- $records = $DB->get_records_select(
- $table,
- $sql_select,
- $uniq_values,
- $sql_order
- );
-
- // Loop through and build a "correct" record, deleting the others
- $needsupdate = false;
- $origrecord = null;
- foreach ($records as $record) {
- $pointer++;
- if ($pointer === 1) { // keep 1st record but delete all others.
- $origrecord = $record;
- } else {
- // If we have fields to check, update original record
- if ($fieldstocheck) {
- // we need to keep the "oldest" of all these fields as the valid completion record.
- // but we want to ignore null values
- foreach ($fieldstocheck as $f) {
- if ($record->$f && (($origrecord->$f > $record->$f) || !$origrecord->$f)) {
- $origrecord->$f = $record->$f;
- $needsupdate = true;
- }
- }
- }
- $DB->delete_records($table, array('id' => $record->id));
- }
- }
- if ($needsupdate || isset($origrecord->reaggregate)) {
- // If this table has a reaggregate field, update to force recheck on next cron run
- if (isset($origrecord->reaggregate)) {
- $origrecord->reaggregate = time();
- }
- $DB->update_record($table, $origrecord);
- }
- }
-}
-
-/**
- * Find questions missing an existing category and associate them with
- * a category which purpose is to gather them.
- *
- * @return void
- */
-function upgrade_save_orphaned_questions() {
- global $DB;
-
- // Looking for orphaned questions
- $orphans = $DB->record_exists_select('question',
- 'NOT EXISTS (SELECT 1 FROM {question_categories} WHERE {question_categories}.id = {question}.category)');
- if (!$orphans) {
- return;
- }
-
- // Generate a unique stamp for the orphaned questions category, easier to identify it later on
- $uniquestamp = "unknownhost+120719170400+orphan";
- $systemcontext = context_system::instance();
-
- // Create the orphaned category at system level
- $cat = $DB->get_record('question_categories', array('stamp' => $uniquestamp,
- 'contextid' => $systemcontext->id));
- if (!$cat) {
- $cat = new stdClass();
- $cat->parent = 0;
- $cat->contextid = $systemcontext->id;
- $cat->name = get_string('orphanedquestionscategory', 'question');
- $cat->info = get_string('orphanedquestionscategoryinfo', 'question');
- $cat->sortorder = 999;
- $cat->stamp = $uniquestamp;
- $cat->id = $DB->insert_record("question_categories", $cat);
- }
-
- // Set a category to those orphans
- $params = array('catid' => $cat->id);
- $DB->execute('UPDATE {question} SET category = :catid WHERE NOT EXISTS
- (SELECT 1 FROM {question_categories} WHERE {question_categories}.id = {question}.category)', $params);
-}
-
-/**
- * Rename old backup files to current backup files.
- *
- * When added the setting 'backup_shortname' (MDL-28657) the backup file names did not contain the id of the course.
- * Further we fixed that behaviour by forcing the id to be always present in the file name (MDL-33812).
- * This function will explore the backup directory and attempt to rename the previously created files to include
- * the id in the name. Doing this will put them back in the process of deleting the excess backups for each course.
- *
- * This function manually recreates the file name, instead of using
- * {@link backup_plan_dbops::get_default_backup_filename()}, use it carefully if you're using it outside of the
- * usual upgrade process.
- *
- * @see backup_cron_automated_helper::remove_excess_backups()
- * @link http://tracker.moodle.org/browse/MDL-35116
- * @return void
- * @since Moodle 2.4
- */
-function upgrade_rename_old_backup_files_using_shortname() {
- global $CFG;
- $dir = get_config('backup', 'backup_auto_destination');
- $useshortname = get_config('backup', 'backup_shortname');
- if (empty($dir) || !is_dir($dir) || !is_writable($dir)) {
- return;
- }
-
- require_once($CFG->dirroot.'/backup/util/includes/backup_includes.php');
- $backupword = str_replace(' ', '_', core_text::strtolower(get_string('backupfilename')));
- $backupword = trim(clean_filename($backupword), '_');
- $filename = $backupword . '-' . backup::FORMAT_MOODLE . '-' . backup::TYPE_1COURSE . '-';
- $regex = '#^'.preg_quote($filename, '#').'.*\.mbz$#';
- $thirtyapril = strtotime('30 April 2012 00:00');
-
- // Reading the directory.
- if (!$files = scandir($dir)) {
- return;
- }
- foreach ($files as $file) {
- // Skip directories and files which do not start with the common prefix.
- // This avoids working on files which are not related to this issue.
- if (!is_file($dir . '/' . $file) || !preg_match($regex, $file)) {
- continue;
- }
-
- // Extract the information from the XML file.
- try {
- $bcinfo = backup_general_helper::get_backup_information_from_mbz($dir . '/' . $file);
- } catch (backup_helper_exception $e) {
- // Some error while retrieving the backup informations, skipping...
- continue;
- }
-
- // Make sure this a course backup.
- if ($bcinfo->format !== backup::FORMAT_MOODLE || $bcinfo->type !== backup::TYPE_1COURSE) {
- continue;
- }
-
- // Skip the backups created before the short name option was initially introduced (MDL-28657).
- // This was integrated on the 2nd of May 2012. Let's play safe with timezone and use the 30th of April.
- if ($bcinfo->backup_date < $thirtyapril) {
- continue;
- }
-
- // Let's check if the file name contains the ID where it is supposed to be, if it is the case then
- // we will skip the file. Of course it could happen that the course ID is identical to the course short name
- // even though really unlikely, but then renaming this file is not necessary. If the ID is not found in the
- // file name then it was probably the short name which was used.
- $idfilename = $filename . $bcinfo->original_course_id . '-';
- $idregex = '#^'.preg_quote($idfilename, '#').'.*\.mbz$#';
- if (preg_match($idregex, $file)) {
- continue;
- }
-
- // Generating the file name manually. We do not use backup_plan_dbops::get_default_backup_filename() because
- // it will query the database to get some course information, and the course could not exist any more.
- $newname = $filename . $bcinfo->original_course_id . '-';
- if ($useshortname) {
- $shortname = str_replace(' ', '_', $bcinfo->original_course_shortname);
- $shortname = core_text::strtolower(trim(clean_filename($shortname), '_'));
- $newname .= $shortname . '-';
- }
-
- $backupdateformat = str_replace(' ', '_', get_string('backupnameformat', 'langconfig'));
- $date = userdate($bcinfo->backup_date, $backupdateformat, 99, false);
- $date = core_text::strtolower(trim(clean_filename($date), '_'));
- $newname .= $date;
-
- if (isset($bcinfo->root_settings['users']) && !$bcinfo->root_settings['users']) {
- $newname .= '-nu';
- } else if (isset($bcinfo->root_settings['anonymize']) && $bcinfo->root_settings['anonymize']) {
- $newname .= '-an';
- }
- $newname .= '.mbz';
-
- // Final check before attempting the renaming.
- if ($newname == $file || file_exists($dir . '/' . $newname)) {
- continue;
- }
- @rename($dir . '/' . $file, $dir . '/' . $newname);
- }
-}
-
-/**
- * Detect duplicate grade item sortorders and resort the
- * items to remove them.
- */
-function upgrade_grade_item_fix_sortorder() {
- global $DB;
-
- // The simple way to fix these sortorder duplicates would be simply to resort each
- // affected course. But in order to reduce the impact of this upgrade step we're trying
- // to do it more efficiently by doing a series of update statements rather than updating
- // every single grade item in affected courses.
-
- $sql = "SELECT DISTINCT g1.courseid
- FROM {grade_items} g1
- JOIN {grade_items} g2 ON g1.courseid = g2.courseid
- WHERE g1.sortorder = g2.sortorder AND g1.id != g2.id
- ORDER BY g1.courseid ASC";
- foreach ($DB->get_fieldset_sql($sql) as $courseid) {
- $transaction = $DB->start_delegated_transaction();
- $items = $DB->get_records('grade_items', array('courseid' => $courseid), '', 'id, sortorder, sortorder AS oldsort');
-
- // Get all duplicates in course order, highest sort order, and higest id first so that we can make space at the
- // bottom higher end of the sort orders and work down by id.
- $sql = "SELECT DISTINCT g1.id, g1.sortorder
- FROM {grade_items} g1
- JOIN {grade_items} g2 ON g1.courseid = g2.courseid
- WHERE g1.sortorder = g2.sortorder AND g1.id != g2.id AND g1.courseid = :courseid
- ORDER BY g1.sortorder DESC, g1.id DESC";
-
- // This is the O(N*N) like the database version we're replacing, but at least the constants are a billion times smaller...
- foreach ($DB->get_records_sql($sql, array('courseid' => $courseid)) as $duplicate) {
- foreach ($items as $item) {
- if ($item->sortorder > $duplicate->sortorder || ($item->sortorder == $duplicate->sortorder && $item->id > $duplicate->id)) {
- $item->sortorder += 1;
- }
- }
- }
- foreach ($items as $item) {
- if ($item->sortorder != $item->oldsort) {
- $DB->update_record('grade_items', array('id' => $item->id, 'sortorder' => $item->sortorder));
- }
- }
-
- $transaction->allow_commit();
- }
-}
-
-/**
- * Detect file areas with missing root directory records and add them.
- */
-function upgrade_fix_missing_root_folders() {
- global $DB, $USER;
-
- $transaction = $DB->start_delegated_transaction();
-
- $sql = "SELECT contextid, component, filearea, itemid
- FROM {files}
- WHERE (component <> 'user' OR filearea <> 'draft')
- GROUP BY contextid, component, filearea, itemid
- HAVING MAX(CASE WHEN filename = '.' AND filepath = '/' THEN 1 ELSE 0 END) = 0";
-
- $rs = $DB->get_recordset_sql($sql);
- $defaults = array('filepath' => '/',
- 'filename' => '.',
- 'userid' => 0, // Don't rely on any particular user for these system records.
- 'filesize' => 0,
- 'timecreated' => time(),
- 'timemodified' => time(),
- 'contenthash' => sha1(''));
- foreach ($rs as $r) {
- $pathhash = sha1("/$r->contextid/$r->component/$r->filearea/$r->itemid/.");
- $DB->insert_record('files', (array)$r + $defaults +
- array('pathnamehash' => $pathhash));
- }
- $rs->close();
- $transaction->allow_commit();
-}
-
/**
* Detect draft file areas with missing root directory records and add them.
*/
+++ /dev/null
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Assignment upgrade script.
- *
- * @package mod_assignment
- * @copyright 2013 Damyon Wiese
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-/**
- * Inform admins about assignments that still need upgrading.
- */
-function mod_assignment_pending_upgrades_notification($count) {
- $admins = get_admins();
-
- if (empty($admins)) {
- return;
- }
-
- $a = new stdClass;
- $a->count = $count;
- $a->docsurl = get_docs_url('Assignment_upgrade_tool');
- foreach ($admins as $admin) {
- $message = new stdClass();
- $message->component = 'moodle';
- $message->name = 'notices';
- $message->userfrom = \core_user::get_noreply_user();
- $message->userto = $admin;
- $message->smallmessage = get_string('pendingupgrades_message_small', 'mod_assignment');
- $message->subject = get_string('pendingupgrades_message_subject', 'mod_assignment');
- $message->fullmessage = get_string('pendingupgrades_message_content', 'mod_assignment', $a);
- $message->fullmessagehtml = get_string('pendingupgrades_message_content', 'mod_assignment', $a);
- $message->fullmessageformat = FORMAT_PLAIN;
- $message->notification = 1;
- message_send($message);
- }
-}
+++ /dev/null
-<?php
-// This file is part of Book module for Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Book module upgrade related helper functions
- *
- * @package mod_book
- * @copyright 2010 Petr Skoda {@link http://skodak.org}
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die;
-
-/**
- * Migrate book files stored in moddata folders.
- *
- * Please note it was a big mistake to store the files there in the first place!
- *
- * @param stdClass $book
- * @param stdClass $context
- * @param string $path
- * @return void
- */
-function mod_book_migrate_moddata_dir_to_legacy($book, $context, $path) {
- global $OUTPUT, $CFG;
-
- $base = "$CFG->dataroot/$book->course/$CFG->moddata/book/$book->id";
- $fulldir = $base.$path;
-
- if (!is_dir($fulldir)) {
- // does not exist
- return;
- }
-
- $fs = get_file_storage();
- $items = new DirectoryIterator($fulldir);
-
- foreach ($items as $item) {
- if ($item->isDot()) {
- unset($item); // release file handle
- continue;
- }
-
- if ($item->isLink()) {
- // do not follow symlinks - they were never supported in moddata, sorry
- unset($item); // release file handle
- continue;
- }
-
- if ($item->isFile()) {
- if (!$item->isReadable()) {
- echo $OUTPUT->notification(" File not readable, skipping: ".$fulldir.$item->getFilename());
- unset($item); // release file handle
- continue;
- }
-
- $filepath = clean_param("/$CFG->moddata/book/$book->id".$path, PARAM_PATH);
- $filename = clean_param($item->getFilename(), PARAM_FILE);
-
- if ($filename === '') {
- // unsupported chars, sorry
- unset($item); // release file handle
- continue;
- }
-
- if (core_text::strlen($filepath) > 255) {
- echo $OUTPUT->notification(" File path longer than 255 chars, skipping: ".$fulldir.$item->getFilename());
- unset($item); // release file handle
- continue;
- }
-
- if (!$fs->file_exists($context->id, 'course', 'legacy', '0', $filepath, $filename)) {
- $file_record = array('contextid'=>$context->id, 'component'=>'course', 'filearea'=>'legacy', 'itemid'=>0, 'filepath'=>$filepath, 'filename'=>$filename,
- 'timecreated'=>$item->getCTime(), 'timemodified'=>$item->getMTime());
- $fs->create_file_from_pathname($file_record, $fulldir.$item->getFilename());
- }
- $oldpathname = $fulldir.$item->getFilename();
- unset($item); // release file handle
- @unlink($oldpathname);
-
- } else {
- // migrate recursively all subdirectories
- $oldpathname = $base.$item->getFilename().'/';
- $subpath = $path.$item->getFilename().'/';
- unset($item); // release file handle
- mod_book_migrate_moddata_dir_to_legacy($book, $context, $subpath);
- @rmdir($oldpathname); // deletes dir if empty
- }
- }
- unset($items); // release file handles
-}
-
-/**
- * Migrate legacy files in intro and chapters
- * @return void
- */
-function mod_book_migrate_all_areas() {
- global $DB, $OUTPUT;
-
- $rsbooks = $DB->get_recordset('book');
- foreach($rsbooks as $book) {
- upgrade_set_timeout(360); // set up timeout, may also abort execution
- $cm = get_coursemodule_from_instance('book', $book->id);
- if (empty($cm) || empty($cm->id)) {
- echo $OUTPUT->notification("Course module not found, skipping: {$book->name}");
- continue;
- }
- $context = context_module::instance($cm->id);
- mod_book_migrate_area($book, 'intro', 'book', $book->course, $context, 'mod_book', 'intro', 0);
-
- $rschapters = $DB->get_recordset('book_chapters', array('bookid'=>$book->id));
- foreach ($rschapters as $chapter) {
- mod_book_migrate_area($chapter, 'content', 'book_chapters', $book->course, $context, 'mod_book', 'chapter', $chapter->id);
- }
- $rschapters->close();
- }
- $rsbooks->close();
-}
-
-/**
- * Migrate one area, this should be probably part of moodle core...
- *
- * @param stdClass $record object to migrate files (book, chapter)
- * @param string $field field in the record we are going to migrate
- * @param string $table DB table containing the information to migrate
- * @param int $courseid id of the course the book module belongs to
- * @param context_module $context context of the book module
- * @param string $component component to be used for the migrated files
- * @param string $filearea filearea to be used for the migrated files
- * @param int $itemid id to be used for the migrated files
- * @return void
- */
-function mod_book_migrate_area($record, $field, $table, $courseid, $context, $component, $filearea, $itemid) {
- global $CFG, $DB;
-
- $fs = get_file_storage();
-
- foreach(array(get_site()->id, $courseid) as $cid) {
- $matches = null;
- $ooldcontext = context_course::instance($cid);
- if (preg_match_all("|$CFG->wwwroot/file.php(\?file=)?/$cid(/[^\s'\"&\?#]+)|", $record->$field, $matches)) {
- $file_record = array('contextid'=>$context->id, 'component'=>$component, 'filearea'=>$filearea, 'itemid'=>$itemid);
- foreach ($matches[2] as $i=>$filepath) {
- if (!$file = $fs->get_file_by_hash(sha1("/$ooldcontext->id/course/legacy/0".$filepath))) {
- continue;
- }
- try {
- if (!$newfile = $fs->get_file_by_hash(sha1("/$context->id/$component/$filearea/$itemid".$filepath))) {
- $fs->create_file_from_storedfile($file_record, $file);
- }
- $record->$field = str_replace($matches[0][$i], '@@PLUGINFILE@@'.$filepath, $record->$field);
- } catch (Exception $ex) {
- // ignore problems
- }
- $DB->set_field($table, $field, $record->$field, array('id'=>$record->id));
- }
- }
- }
-}
\ No newline at end of file
This files describes API changes in the book code.
+=== 3.1 ===
+
+* The following functions, previously used (exclusively) by upgrade steps are not available
+ anymore because of the upgrade cleanup performed for this version. See MDL-51580 for more info:
+ - mod_book_migrate_moddata_dir_to_legacy()
+ - mod_book_migrate_all_areas()
+ - mod_book_migrate_area()
+
=== 3.0 ===
* External function mod_book_external::get_books_by_courses returned parameter "name" has been changed to PARAM_RAW,
=== 2.7 ===
* bogus legacy log calls were removed
-* \mod_book\event\chapter_deleted::set_legacy_logdata() was removed
\ No newline at end of file
+* \mod_book\event\chapter_deleted::set_legacy_logdata() was removed
+++ /dev/null
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Locallib.
- *
- * @package portfolio_boxnet
- * @copyright 2013 Frédéric Massart
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-/**
- * Send a message to the admin in regard with the APIv1 migration.
- *
- * @return void
- */
-function portfolio_boxnet_admin_upgrade_notification() {
- $admins = get_admins();
-
- if (empty($admins)) {
- return;
- }
- $a = new stdClass();
- $a->docsurl = get_docs_url('Box.net_APIv1_migration');
-
- foreach ($admins as $admin) {
- $message = new stdClass();
- $message->component = 'moodle';
- $message->name = 'notices';
- $message->userfrom = get_admin();
- $message->userto = $admin;
- $message->smallmessage = get_string('apiv1migration_message_small', 'portfolio_boxnet');
- $message->subject = get_string('apiv1migration_message_subject', 'portfolio_boxnet');
- $message->fullmessage = get_string('apiv1migration_message_content', 'portfolio_boxnet', $a);
- $message->fullmessagehtml = get_string('apiv1migration_message_content', 'portfolio_boxnet', $a);
- $message->fullmessageformat = FORMAT_PLAIN;
- $message->notification = 1;
- message_send($message);
- }
-}
+++ /dev/null
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Googledocs portfolio upgrade script.
- *
- * @package portfolio_googledocs
- * @copyright 2013 Dan Poltawski <dan@moodle.com>
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-/**
- * Inform admins about setup required for googledocs change.
- */
-function portfolio_googledocs_admin_upgrade_notification() {
- $admins = get_admins();
-
- if (empty($admins)) {
- return;
- }
- $a = new stdClass;
- $a->docsurl = get_docs_url('Google_OAuth_2.0_setup');
-
- foreach ($admins as $admin) {
- $message = new stdClass();
- $message->component = 'moodle';
- $message->name = 'notices';
- $message->userfrom = get_admin();
- $message->userto = $admin;
- $message->smallmessage = get_string('oauth2upgrade_message_small', 'portfolio_googledocs');
- $message->subject = get_string('oauth2upgrade_message_subject', 'portfolio_googledocs');
- $message->fullmessage = get_string('oauth2upgrade_message_content', 'portfolio_googledocs', $a);
- $message->fullmessagehtml = get_string('oauth2upgrade_message_content', 'portfolio_googledocs', $a);
- $message->fullmessageformat = FORMAT_PLAIN;
- $message->notification = 1;
- message_send($message);
- }
-}
+++ /dev/null
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Picasa portfolio upgrade script.
- *
- * @package portfolio_picasa
- * @copyright 2013 Dan Poltawski <dan@moodle.com>
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-/**
- * Inform admins about setup required for picasa change.
- */
-function portfolio_picasa_admin_upgrade_notification() {
- $admins = get_admins();
-
- if (empty($admins)) {
- return;
- }
- $a = new stdClass;
- $a->docsurl = get_docs_url('Google_OAuth_2.0_setup');
-
- foreach ($admins as $admin) {
- $message = new stdClass();
- $message->component = 'moodle';
- $message->name = 'notices';
- $message->userfrom = get_admin();
- $message->userto = $admin;
- $message->smallmessage = get_string('oauth2upgrade_message_small', 'portfolio_picasa');
- $message->subject = get_string('oauth2upgrade_message_subject', 'portfolio_picasa');
- $message->fullmessage = get_string('oauth2upgrade_message_content', 'portfolio_picasa', $a);
- $message->fullmessagehtml = get_string('oauth2upgrade_message_content', 'portfolio_picasa', $a);
- $message->fullmessageformat = FORMAT_PLAIN;
- $message->notification = 1;
- message_send($message);
- }
-}
This files describes API changes in /portfolio/ portfolio system,
information provided here is intended especially for developers.
+=== 3.1 ===
+
+* The following functions, previously used (exclusively) by upgrade steps are not available
+ anymore because of the upgrade cleanup performed for this version. See MDL-51580 for more info:
+ - portfolio_picasa_admin_upgrade_notification()
+ - portfolio_googledocs_admin_upgrade_notification()
+ - portfolio_boxnet_admin_upgrade_notification()
+
=== 2.4 ===
The set_callback_options function's third parameter has been changed from a file path
This files describes API changes for question type plugins.
+=== 3.1 ===
+
+* The following functions, previously used (exclusively) by upgrade steps are not available
+ anymore because of the upgrade cleanup performed for this version. See MDL-51580 for more info:
+ - qtype_essay_convert_to_html()
+
=== 2.7 ===
+ We have added a new method to the question_type base class 'break_down_stats_and_response_analysis_by_variant'. By default it
returns true. If your question type does not have variants of question instances then you can ignore this method as it only
+++ /dev/null
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Locallib.
- *
- * @package repository_alfresco
- * @copyright 2014 Frédéric Massart
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-/**
- * Send a message to the admin in regard with the APIv1 migration.
- *
- * @return void
- */
-function repository_alfresco_admin_security_key_notice() {
- $admins = get_admins();
-
- if (empty($admins)) {
- return;
- }
-
- foreach ($admins as $admin) {
- $message = new stdClass();
- $message->component = 'moodle';
- $message->name = 'notices';
- $message->userfrom = get_admin();
- $message->userto = $admin;
- $message->smallmessage = get_string('security_key_notice_message_small', 'repository_alfresco');
- $message->subject = get_string('security_key_notice_message_subject', 'repository_alfresco');
- $message->fullmessage = get_string('security_key_notice_message_content', 'repository_alfresco');
- $message->fullmessagehtml = get_string('security_key_notice_message_content', 'repository_alfresco');
- $message->fullmessageformat = FORMAT_PLAIN;
- $message->notification = 1;
- message_send($message);
- }
-}
+++ /dev/null
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Locallib.
- *
- * @package repository_boxnet
- * @copyright 2013 Frédéric Massart
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-/**
- * Send a message to the admin in regard with the APIv1 migration.
- *
- * @return void
- */
-function repository_boxnet_admin_upgrade_notification() {
- $admins = get_admins();
-
- if (empty($admins)) {
- return;
- }
- $a = new stdClass();
- $a->docsurl = get_docs_url('Box.net_APIv1_migration');
-
- foreach ($admins as $admin) {
- $message = new stdClass();
- $message->component = 'moodle';
- $message->name = 'notices';
- $message->userfrom = get_admin();
- $message->userto = $admin;
- $message->smallmessage = get_string('apiv1migration_message_small', 'repository_boxnet');
- $message->subject = get_string('apiv1migration_message_subject', 'repository_boxnet');
- $message->fullmessage = get_string('apiv1migration_message_content', 'repository_boxnet', $a);
- $message->fullmessagehtml = get_string('apiv1migration_message_content', 'repository_boxnet', $a);
- $message->fullmessageformat = FORMAT_PLAIN;
- $message->notification = 1;
- message_send($message);
- }
-}
+++ /dev/null
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Googledocs repository upgrade script.
- *
- * @package repository_googledocs
- * @copyright 2013 Dan Poltawski <dan@moodle.com>
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-/**
- * Inform admins about setup required for googledocs change.
- */
-function repository_googledocs_admin_upgrade_notification() {
- $admins = get_admins();
-
- if (empty($admins)) {
- return;
- }
-
- $a = new stdClass;
- $a->docsurl = get_docs_url('Google_OAuth_2.0_setup');
- foreach ($admins as $admin) {
- $message = new stdClass();
- $message->component = 'moodle';
- $message->name = 'notices';
- $message->userfrom = get_admin();
- $message->userto = $admin;
- $message->smallmessage = get_string('oauth2upgrade_message_small', 'repository_googledocs');
- $message->subject = get_string('oauth2upgrade_message_subject', 'repository_googledocs');
- $message->fullmessage = get_string('oauth2upgrade_message_content', 'repository_googledocs', $a);
- $message->fullmessagehtml = get_string('oauth2upgrade_message_content', 'repository_googledocs', $a);
- $message->fullmessageformat = FORMAT_PLAIN;
- $message->notification = 1;
- message_send($message);
- }
-}
+++ /dev/null
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Picasa repository upgrade script.
- *
- * @package repository_picasa
- * @copyright 2013 Dan Poltawski <dan@moodle.com>
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-/**
- * Inform admins about setup required for google picasa change.
- */
-function repository_picasa_admin_upgrade_notification() {
- $admins = get_admins();
-
- if (empty($admins)) {
- return;
- }
- $a = new stdClass;
- $a->docsurl = get_docs_url('Google_OAuth_2.0_setup');
-
- foreach ($admins as $admin) {
- $message = new stdClass();
- $message->component = 'moodle';
- $message->name = 'notices';
- $message->userfrom = get_admin();
- $message->userto = $admin;
- $message->smallmessage = get_string('oauth2upgrade_message_small', 'repository_picasa');
- $message->subject = get_string('oauth2upgrade_message_subject', 'repository_picasa');
- $message->fullmessage = get_string('oauth2upgrade_message_content', 'repository_picasa', $a);
- $message->fullmessagehtml = get_string('oauth2upgrade_message_content', 'repository_picasa', $a);
- $message->fullmessageformat = FORMAT_PLAIN;
- $message->notification = 1;
- message_send($message);
- }
-}
details of the repository API are available on Moodle docs:
http://docs.moodle.org/dev/Repository_API
+=== 3.1 ===
+
+* The following functions, previously used (exclusively) by upgrade steps are not available
+ anymore because of the upgrade cleanup performed for this version. See MDL-51580 for more info:
+ - repository_picasa_admin_upgrade_notification()
+ - repository_googledocs_admin_upgrade_notification()
+ - repository_boxnet_admin_upgrade_notification()
+ - repository_alfresco_admin_security_key_notice()
+
=== 2.8 ===
* Repositories working with Moodle files must replace serialize() with json_encode() in the