diff --git a/backup/util/dbops/restore_dbops.class.php b/backup/util/dbops/restore_dbops.class.php index 9992713aeff..7ef9117e230 100644 --- a/backup/util/dbops/restore_dbops.class.php +++ b/backup/util/dbops/restore_dbops.class.php @@ -723,7 +723,7 @@ abstract class restore_dbops { // 8) Check if backup is made on Moodle >= 3.5 and there are more than one top-level category in the context. if ($after35 && $topcats > 1) { - $errors[] = get_string('restoremultipletopcats', 'questions', $contextid); + $errors[] = get_string('restoremultipletopcats', 'question', $contextid); } } diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index 2f06ce21f6b..fd89e14776f 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -2254,5 +2254,30 @@ function xmldb_main_upgrade($oldversion) { upgrade_main_savepoint(true, 2018051700.08); } + if ($oldversion < 2018051701.03) { + // Find all duplicate top level categories per context. + $duplicates = $DB->get_records_sql("SELECT qc1.* + FROM {question_categories} qc1 + JOIN {question_categories} qc2 + ON qc1.contextid = qc2.contextid AND qc1.id <> qc2.id + WHERE qc1.parent = 0 AND qc2.parent = 0 + ORDER BY qc1.contextid, qc1.id"); + + // For each context, let the first top category to remain as top category and make the rest its children. + $currentcontextid = 0; + $chosentopid = 0; + foreach ($duplicates as $duplicate) { + if ($currentcontextid != $duplicate->contextid) { + $currentcontextid = $duplicate->contextid; + $chosentopid = $duplicate->id; + } else { + $DB->set_field('question_categories', 'parent', $chosentopid, ['id' => $duplicate->id]); + } + } + + // Main savepoint reached. + upgrade_main_savepoint(true, 2018051701.03); + } + return true; } diff --git a/lib/questionlib.php b/lib/questionlib.php index 91d734c1f32..ebd67962554 100644 --- a/lib/questionlib.php +++ b/lib/questionlib.php @@ -504,7 +504,7 @@ function question_save_from_deletion($questionids, $newcontextid, $oldplace, // Make a category in the parent context to move the questions to. if (is_null($newcategory)) { $newcategory = new stdClass(); - $newcategory->parent = 0; + $newcategory->parent = question_get_top_category($newcontextid, true)->id; $newcategory->contextid = $newcontextid; $newcategory->name = get_string('questionsrescuedfrom', 'question', $oldplace); $newcategory->info = get_string('questionsrescuedfrominfo', 'question', $oldplace); diff --git a/lib/tests/questionlib_test.php b/lib/tests/questionlib_test.php index dde35cf2d62..da003ffc6a7 100644 --- a/lib/tests/questionlib_test.php +++ b/lib/tests/questionlib_test.php @@ -458,6 +458,29 @@ class core_questionlib_testcase extends advanced_testcase { $this->assertEquals(0, $DB->count_records('question_categories', $criteria)); } + /** + * This function tests the question_save_from_deletion function when it is supposed to make a new category and + * move question categories to that new category. + */ + public function test_question_save_from_deletion() { + global $DB; + $this->resetAfterTest(true); + $this->setAdminUser(); + + list($category, $course, $quiz, $qcat, $questions) = $this->setup_quiz_and_questions(); + + $context = context::instance_by_id($qcat->contextid); + + $newcat = question_save_from_deletion(array_column($questions, 'id'), + $context->get_parent_context()->id, $context->get_context_name()); + + // Verify that the newcat itself is not a tep level category. + $this->assertNotEquals(0, $newcat->parent); + + // Verify there is just a single top-level category. + $this->assertEquals(1, $DB->count_records('question_categories', ['contextid' => $qcat->contextid, 'parent' => 0])); + } + public function test_question_remove_stale_questions_from_category() { global $DB; $this->resetAfterTest(true);