From 3801662e974aa2191e8e93a2972523d50a632084 Mon Sep 17 00:00:00 2001 From: Peter Burnett Date: Tue, 30 Mar 2021 15:39:07 +1000 Subject: [PATCH] MDL-71198 backup: Change times on restore file duplication --- backup/util/dbops/restore_dbops.class.php | 31 ++++++----- lib/classes/task/fix_file_timestamps_task.php | 38 ++++++++++++++ lib/db/upgrade.php | 9 ++++ lib/db/upgradelib.php | 35 +++++++++++++ lib/tests/upgradelib_test.php | 52 +++++++++++++++++++ version.php | 2 +- 6 files changed, 153 insertions(+), 14 deletions(-) create mode 100644 lib/classes/task/fix_file_timestamps_task.php diff --git a/backup/util/dbops/restore_dbops.class.php b/backup/util/dbops/restore_dbops.class.php index 6734921faa0..f56de9be243 100644 --- a/backup/util/dbops/restore_dbops.class.php +++ b/backup/util/dbops/restore_dbops.class.php @@ -1019,21 +1019,26 @@ abstract class restore_dbops { continue; } + // Updated the times of the new record. + // The file record should reflect when the file entered the system, + // and when this record was created. + $time = time(); + // The file record to restore. $file_record = array( - 'contextid' => $newcontextid, - 'component' => $component, - 'filearea' => $filearea, - 'itemid' => $rec->newitemid, - 'filepath' => $file->filepath, - 'filename' => $file->filename, - 'timecreated' => $file->timecreated, - 'timemodified'=> $file->timemodified, - 'userid' => $mappeduserid, - 'source' => $file->source, - 'author' => $file->author, - 'license' => $file->license, - 'sortorder' => $file->sortorder + 'contextid' => $newcontextid, + 'component' => $component, + 'filearea' => $filearea, + 'itemid' => $rec->newitemid, + 'filepath' => $file->filepath, + 'filename' => $file->filename, + 'timecreated' => $time, + 'timemodified' => $time, + 'userid' => $mappeduserid, + 'source' => $file->source, + 'author' => $file->author, + 'license' => $file->license, + 'sortorder' => $file->sortorder ); if (empty($file->repositoryid)) { diff --git a/lib/classes/task/fix_file_timestamps_task.php b/lib/classes/task/fix_file_timestamps_task.php new file mode 100644 index 00000000000..9693165f689 --- /dev/null +++ b/lib/classes/task/fix_file_timestamps_task.php @@ -0,0 +1,38 @@ +. + +namespace core\task; + +defined('MOODLE_INTERNAL') || die(); +require_once($CFG->libdir . '/db/upgradelib.php'); + +/** + * Retroactively fixes file timestamps that are older than the containing folder record. + * + * @package core + * @author Peter Burnett + * @copyright Catalyst IT, 2021 + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class fix_file_timestamps_task extends adhoc_task { + + /** + * Run the adhoc task and fix the file timestamps. + */ + public function execute() { + upgrade_fix_file_timestamps(); + } +} diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index b3844f7c69e..27c73a7c7c2 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -4506,5 +4506,14 @@ privatefiles,moodle|/user/files.php'; upgrade_main_savepoint(true, 2022051000.00); } + if ($oldversion < 2022052500.00) { + // Start an adhoc task to fix the file timestamps of restored files. + $task = new core\task\fix_file_timestamps_task(); + \core\task\manager::queue_adhoc_task($task); + + // Main savepoint reached. + upgrade_main_savepoint(true, 2022052500.00); + } + return true; } diff --git a/lib/db/upgradelib.php b/lib/db/upgradelib.php index 4a39f08ee9e..b043b25d175 100644 --- a/lib/db/upgradelib.php +++ b/lib/db/upgradelib.php @@ -1597,3 +1597,38 @@ function upgrade_block_set_my_user_parent_context( $dbman->drop_table($xmldbtable); } + +/** + * Fix the timestamps for files where their timestamps are older + * than the directory listing that they are contained in. + */ +function upgrade_fix_file_timestamps() { + global $DB; + + // Due to incompatability in SQL syntax for updates with joins, + // These will be updated in a select + separate update. + $sql = "SELECT f.id, f2.timecreated + FROM {files} f + JOIN {files} f2 + ON f2.contextid = f.contextid + AND f2.filepath = f.filepath + AND f2.component = f.component + AND f2.filearea = f.filearea + AND f2.itemid = f.itemid + AND f2.filename = '.' + WHERE f2.timecreated > f.timecreated"; + + $recordset = $DB->get_recordset_sql($sql); + + if (!$recordset->valid()) { + $recordset->close(); + return; + } + + foreach ($recordset as $record) { + $record->timemodified = $record->timecreated; + $DB->update_record('files', $record); + } + + $recordset->close(); +} diff --git a/lib/tests/upgradelib_test.php b/lib/tests/upgradelib_test.php index 3ed46f76f56..58ad609172c 100644 --- a/lib/tests/upgradelib_test.php +++ b/lib/tests/upgradelib_test.php @@ -1671,4 +1671,56 @@ calendar,core_calendar|/calendar/view.php?view=month', $this->assertEquals($expectedmenu, $newcustomusermenu); } + + /** + * Test that file timestamps are corrected for copied files. + */ + public function test_upgrade_fix_file_timestamps() { + global $DB; + $this->resetAfterTest(); + + // Add 2 files for testing, one with edited old timestamps. + $origtime = time(); + $new = [ + 'contextid' => 123, + 'component' => 'mod_label', + 'filearea' => 'intro', + 'itemid' => 0, + 'filepath' => '/', + 'filename' => 'file.txt', + ]; + $old = [ + 'contextid' => 321, + 'component' => 'mod_label', + 'filearea' => 'intro', + 'itemid' => 0, + 'filepath' => '/', + 'filename' => 'file.txt', + ]; + + // Create the file records. This will create a directory listing with the current time. + $fs = get_file_storage(); + $newfile = $fs->create_file_from_string($new, 'new'); + $oldfile = $fs->create_file_from_string($old, 'old'); + + // Manually set the timestamps to use on files. + $DB->set_field('files', 'timecreated', $origtime, ['id' => $newfile->get_id()]); + $DB->set_field('files', 'timemodified', $origtime, ['id' => $newfile->get_id()]); + $DB->set_field('files', 'timecreated', 1, ['id' => $oldfile->get_id()]); + $DB->set_field('files', 'timemodified', 1, ['id' => $oldfile->get_id()]); + + upgrade_fix_file_timestamps(); + + // Check nothing changed on the new file. + $updatednew = $DB->get_record('files', ['id' => $newfile->get_id()]); + $this->assertEquals($origtime, $updatednew->timecreated); + $this->assertEquals($origtime, $updatednew->timemodified); + + // Confirm that the file with old timestamps has been fixed. + $updatedold = $DB->get_record('files', ['id' => $oldfile->get_id()]); + $this->assertNotEquals(1, $updatedold->timecreated); + $this->assertNotEquals(1, $updatedold->timemodified); + $this->assertTrue($updatedold->timecreated >= $origtime); + $this->assertTrue($updatedold->timemodified >= $origtime); + } } diff --git a/version.php b/version.php index 49a0dbd7b89..370148d659f 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2022051900.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2022052500.00; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes. $release = '4.1dev (Build: 20220519)'; // Human-friendly version name