diff --git a/mod/quiz/backup/moodle2/restore_quiz_stepslib.php b/mod/quiz/backup/moodle2/restore_quiz_stepslib.php index 37713d33b75..b8d882249e9 100644 --- a/mod/quiz/backup/moodle2/restore_quiz_stepslib.php +++ b/mod/quiz/backup/moodle2/restore_quiz_stepslib.php @@ -40,6 +40,11 @@ class restore_quiz_activity_structure_step extends restore_questions_activity_st */ protected $legacyshufflequestionsoption = false; + /** + * @var array Track old question ids that need to be removed at the end of the restore. + */ + protected $oldquestionids = []; + protected function define_structure() { $paths = array(); @@ -342,8 +347,7 @@ class restore_quiz_activity_structure_step extends restore_questions_activity_st $filtercondition->includingsubcategories = $data->includingsubcategories ?? false; $questionsetreference->filtercondition = json_encode($filtercondition); $DB->insert_record('question_set_references', $questionsetreference); - // Cleanup leftover random qtype data from question table. - question_delete_question($question->questionid); + $this->oldquestionids[$question->questionid] = 1; } else { // Reference data. $questionreference = new \stdClass(); @@ -604,4 +608,12 @@ class restore_quiz_activity_structure_step extends restore_questions_activity_st 'shufflequestions' => $this->legacyshufflequestionsoption)); } } + + protected function after_restore() { + parent::after_restore(); + // Delete old random questions that have been converted to set references. + foreach (array_keys($this->oldquestionids) as $oldquestionid) { + question_delete_question($oldquestionid); + } + } } diff --git a/mod/quiz/tests/fixtures/pre-40-shared-random-question.mbz b/mod/quiz/tests/fixtures/pre-40-shared-random-question.mbz new file mode 100644 index 00000000000..9dcf49e8557 Binary files /dev/null and b/mod/quiz/tests/fixtures/pre-40-shared-random-question.mbz differ diff --git a/mod/quiz/tests/quiz_question_restore_test.php b/mod/quiz/tests/quiz_question_restore_test.php index b157099aa2e..5743ec4b2a4 100644 --- a/mod/quiz/tests/quiz_question_restore_test.php +++ b/mod/quiz/tests/quiz_question_restore_test.php @@ -380,6 +380,59 @@ class quiz_question_restore_test extends \advanced_testcase { } + /** + * Test pre 4.0 quiz restore for random question used on multiple quizzes. + * + * @covers ::process_quiz_question_legacy_instance + */ + public function test_pre_4_quiz_restore_shared_random_question() { + global $USER, $DB; + $this->resetAfterTest(); + + $backupid = 'abc'; + $backuppath = make_backup_temp_directory($backupid); + get_file_packer('application/vnd.moodle.backup')->extract_to_pathname( + __DIR__ . "/fixtures/pre-40-shared-random-question.mbz", $backuppath); + + // Do the restore to new course with default settings. + $categoryid = $DB->get_field_sql("SELECT MIN(id) FROM {course_categories}"); + $newcourseid = \restore_dbops::create_new_course('Test fullname', 'Test shortname', $categoryid); + $rc = new \restore_controller($backupid, $newcourseid, \backup::INTERACTIVE_NO, \backup::MODE_GENERAL, $USER->id, + \backup::TARGET_NEW_COURSE); + + $this->assertTrue($rc->execute_precheck()); + $rc->execute_plan(); + $rc->destroy(); + + // Get the information about the resulting course and check that it is set up correctly. + // Each quiz should contain an instance of the random question. + $modinfo = get_fast_modinfo($newcourseid); + $quizzes = $modinfo->get_instances_of('quiz'); + $this->assertCount(2, $quizzes); + foreach ($quizzes as $quiz) { + $quizobj = \quiz::create($quiz->instance); + $structure = structure::create_for_quiz($quizobj); + + // Are the correct slots returned? + $slots = $structure->get_slots(); + $this->assertCount(1, $slots); + + $quizobj->preload_questions(); + $quizobj->load_questions(); + $questions = $quizobj->get_questions(); + $this->assertCount(1, $questions); + } + + // Count the questions for course question bank. + // We should have a single question, the random question should have been deleted after the restore. + $this->assertEquals(1, $this->question_count(\context_course::instance($newcourseid)->id)); + $this->assertEquals(1, $this->question_count(\context_course::instance($newcourseid)->id, + "AND q.qtype <> 'random'")); + + // Count the questions in quiz qbank. + $this->assertEquals(0, $this->question_count($quizobj->get_context()->id)); + } + /** * Ensure that question slots are correctly backed up and restored with all properties. *