diff --git a/backup/moodle2/backup_stepslib.php b/backup/moodle2/backup_stepslib.php index 436f19f36dd..9a32da1531e 100644 --- a/backup/moodle2/backup_stepslib.php +++ b/backup/moodle2/backup_stepslib.php @@ -930,8 +930,11 @@ class backup_gradebook_structure_step extends backup_structure_step { $grade_setting = new backup_nested_element('grade_setting', 'id', array( 'name', 'value')); + $gradebook_attributes = new backup_nested_element('attributes', null, array('calculations_freeze')); // Build the tree + $gradebook->add_child($gradebook_attributes); + $gradebook->add_child($grade_categories); $grade_categories->add_child($grade_category); @@ -946,14 +949,15 @@ class backup_gradebook_structure_step extends backup_structure_step { $gradebook->add_child($grade_settings); $grade_settings->add_child($grade_setting); + // Define sources + // Add attribute with gradebook calculation freeze date if needed. + $attributes = new stdClass(); $gradebookcalculationfreeze = get_config('core', 'gradebook_calculations_freeze_' . $this->get_courseid()); if ($gradebookcalculationfreeze) { - $gradebook->add_attributes(array('calculations_freeze')); - $gradebook->get_attribute('calculations_freeze')->set_value($gradebookcalculationfreeze); + $attributes->calculations_freeze = $gradebookcalculationfreeze; } - - // Define sources + $gradebook_attributes->set_source_array([$attributes]); //Include manual, category and the course grade item $grade_items_sql ="SELECT * FROM {grade_items} diff --git a/backup/moodle2/restore_stepslib.php b/backup/moodle2/restore_stepslib.php index dfe5cea30e3..3473004f9ac 100644 --- a/backup/moodle2/restore_stepslib.php +++ b/backup/moodle2/restore_stepslib.php @@ -121,6 +121,21 @@ class restore_gradebook_structure_step extends restore_structure_step { return false; } + // Identify the backup we're dealing with. + $backuprelease = floatval($this->get_task()->get_info()->backup_release); // The major version: 2.9, 3.0, ... + $backupbuild = 0; + preg_match('/(\d{8})/', $this->get_task()->get_info()->moodle_release, $matches); + if (!empty($matches[1])) { + $backupbuild = (int) $matches[1]; // The date of Moodle build at the time of the backup. + } + + // On older versions the freeze value has to be converted. + // We do this from here as it is happening right before the file is read. + // This only targets the backup files that can contain the legacy freeze. + if ($backupbuild > 20150618 && ($backuprelease < 3.0 || $backupbuild < 20160527)) { + $this->rewrite_step_backup_file_for_legacy_freeze($fullpath); + } + // Arrived here, execute the step return true; } @@ -129,7 +144,7 @@ class restore_gradebook_structure_step extends restore_structure_step { $paths = array(); $userinfo = $this->task->get_setting_value('users'); - $paths[] = new restore_path_element('gradebook', '/gradebook'); + $paths[] = new restore_path_element('attributes', '/gradebook/attributes'); $paths[] = new restore_path_element('grade_category', '/gradebook/grade_categories/grade_category'); $paths[] = new restore_path_element('grade_item', '/gradebook/grade_items/grade_item'); if ($userinfo) { @@ -141,7 +156,7 @@ class restore_gradebook_structure_step extends restore_structure_step { return $paths; } - protected function process_gradebook($data) { + protected function process_attributes($data) { // For non-merge restore types: // Unset 'gradebook_calculations_freeze_' in the course and replace with the one from the backup. $target = $this->get_task()->get_target(); @@ -581,6 +596,85 @@ class restore_gradebook_structure_step extends restore_structure_step { } } } + + /** + * Rewrite step definition to handle the legacy freeze attribute. + * + * In previous backups the calculations_freeze property was stored as an attribute of the + * top level node . The backup API, however, do not process grandparent nodes. + * It only processes definitive children, and their parent attributes. + * + * We had: + * + * + * + * + * 1 + * ... + * + * + * ... + * + * + * And this method will convert it to: + * + * + * + * 20160511 + * + * + * + * 1 + * ... + * + * + * ... + * + * + * Note that we cannot just load the XML file in memory as it could potentially be huge. + * We can also completely ignore if the node is already in the backup + * file as it never existed before. + * + * @param string $filepath The absolute path to the XML file. + * @return void + */ + protected function rewrite_step_backup_file_for_legacy_freeze($filepath) { + $foundnode = false; + $newfile = make_request_directory(true) . DIRECTORY_SEPARATOR . 'file.xml'; + $fr = fopen($filepath, 'r'); + $fw = fopen($newfile, 'w'); + if ($fr && $fw) { + while (($line = fgets($fr, 4096)) !== false) { + if (!$foundnode && strpos($line, ' + + 20160511 + + + + 1 + + + diff --git a/backup/moodle2/tests/fixtures/rewrite_step_backup_file_for_legacy_freeze/1.test b/backup/moodle2/tests/fixtures/rewrite_step_backup_file_for_legacy_freeze/1.test new file mode 100644 index 00000000000..830eca68bc9 --- /dev/null +++ b/backup/moodle2/tests/fixtures/rewrite_step_backup_file_for_legacy_freeze/1.test @@ -0,0 +1,7 @@ + + + + 1 + + + diff --git a/backup/moodle2/tests/fixtures/rewrite_step_backup_file_for_legacy_freeze/2.expectation b/backup/moodle2/tests/fixtures/rewrite_step_backup_file_for_legacy_freeze/2.expectation new file mode 100644 index 00000000000..b4f21d48205 --- /dev/null +++ b/backup/moodle2/tests/fixtures/rewrite_step_backup_file_for_legacy_freeze/2.expectation @@ -0,0 +1,10 @@ + + + 20160511 + + + + 1 + + + diff --git a/backup/moodle2/tests/fixtures/rewrite_step_backup_file_for_legacy_freeze/2.test b/backup/moodle2/tests/fixtures/rewrite_step_backup_file_for_legacy_freeze/2.test new file mode 100644 index 00000000000..04f3b63eef7 --- /dev/null +++ b/backup/moodle2/tests/fixtures/rewrite_step_backup_file_for_legacy_freeze/2.test @@ -0,0 +1,7 @@ + + + + 1 + + + diff --git a/backup/moodle2/tests/fixtures/rewrite_step_backup_file_for_legacy_freeze/3.expectation b/backup/moodle2/tests/fixtures/rewrite_step_backup_file_for_legacy_freeze/3.expectation new file mode 100644 index 00000000000..c61f19d99a3 --- /dev/null +++ b/backup/moodle2/tests/fixtures/rewrite_step_backup_file_for_legacy_freeze/3.expectation @@ -0,0 +1,10 @@ + + + 20160511 + + + + 1 + + + diff --git a/backup/moodle2/tests/fixtures/rewrite_step_backup_file_for_legacy_freeze/3.test b/backup/moodle2/tests/fixtures/rewrite_step_backup_file_for_legacy_freeze/3.test new file mode 100644 index 00000000000..39c46bc57c6 --- /dev/null +++ b/backup/moodle2/tests/fixtures/rewrite_step_backup_file_for_legacy_freeze/3.test @@ -0,0 +1,7 @@ + + + + 1 + + + diff --git a/backup/moodle2/tests/fixtures/rewrite_step_backup_file_for_legacy_freeze/4.expectation b/backup/moodle2/tests/fixtures/rewrite_step_backup_file_for_legacy_freeze/4.expectation new file mode 100644 index 00000000000..71070aa82e3 --- /dev/null +++ b/backup/moodle2/tests/fixtures/rewrite_step_backup_file_for_legacy_freeze/4.expectation @@ -0,0 +1,7 @@ + + + + 1 + + + diff --git a/backup/moodle2/tests/fixtures/rewrite_step_backup_file_for_legacy_freeze/4.test b/backup/moodle2/tests/fixtures/rewrite_step_backup_file_for_legacy_freeze/4.test new file mode 100644 index 00000000000..71070aa82e3 --- /dev/null +++ b/backup/moodle2/tests/fixtures/rewrite_step_backup_file_for_legacy_freeze/4.test @@ -0,0 +1,7 @@ + + + + 1 + + + diff --git a/backup/moodle2/tests/restore_gradebook_structure_step_test.php b/backup/moodle2/tests/restore_gradebook_structure_step_test.php new file mode 100644 index 00000000000..26856de9597 --- /dev/null +++ b/backup/moodle2/tests/restore_gradebook_structure_step_test.php @@ -0,0 +1,92 @@ +. + +/** + * Test for restore_stepslib. + * + * @package core_backup + * @copyright 2016 Andrew Nicols + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php'); +require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php'); +require_once($CFG->libdir . '/completionlib.php'); + +/** + * Test for restore_stepslib. + * + * @package core_backup + * @copyright 2016 Andrew Nicols + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class core_backup_restore_gradebook_structure_step_testcase extends advanced_testcase { + + /** + * Provide tests for rewrite_step_backup_file_for_legacy_freeze based upon fixtures. + * + * @return array + */ + public function rewrite_step_backup_file_for_legacy_freeze_provider() { + $fixturesdir = realpath(__DIR__ . '/fixtures/rewrite_step_backup_file_for_legacy_freeze/'); + $tests = []; + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($fixturesdir), + \RecursiveIteratorIterator::LEAVES_ONLY); + + foreach ($iterator as $sourcefile) { + $pattern = '/\.test$/'; + if (!preg_match($pattern, $sourcefile)) { + continue; + } + + $expectfile = preg_replace($pattern, '.expectation', $sourcefile); + $test = array($sourcefile, $expectfile); + $tests[basename($sourcefile)] = $test; + } + + return $tests; + } + + /** + * @dataProvider rewrite_step_backup_file_for_legacy_freeze_provider + * @param string $source The source file to test + * @param string $expected The expected result of the transformation + */ + public function test_rewrite_step_backup_file_for_legacy_freeze($source, $expected) { + $restore = $this->getMockBuilder('\restore_gradebook_structure_step') + ->setMethods(null) + ->disableOriginalConstructor() + ->getMock() + ; + + // Copy the file somewhere as the rewrite_step_backup_file_for_legacy_freeze will write the file. + $dir = make_request_directory(true); + $filepath = $dir . DIRECTORY_SEPARATOR . 'file.xml'; + copy($source, $filepath); + + $rc = new \ReflectionClass('\restore_gradebook_structure_step'); + $rcm = $rc->getMethod('rewrite_step_backup_file_for_legacy_freeze'); + $rcm->setAccessible(true); + $rcm->invoke($restore, $filepath); + + // Check the result. + $this->assertFileEquals($expected, $filepath); + } +}