-
Bug
-
Resolution: Unresolved
-
Minor
-
None
-
4.5
-
MOODLE_405_STABLE
Note, the report below was created in the time of Moodle 4.x, and things have changed significantly in Moodle 5+.
The way shared question bank work has changed, and the way categories are moved has changed. The suggests 'fix' is no longer necessarily right.
It may be that a better solution is that when someone is moving a category that is the target of a question set reference, they get a warning in the UI before doing the move. Rather than having Moodle siletly try to fix the data behind the scenes, in a way that might not acutally be what the user wants.
✅ Reproduction Steps
- Create a course category structure like this:
Category A
└── Category B
└── Course C - Go to Category A and create a question category (e.g., Random Questions A).
- Add a few actual questions to that category (e.g., multiple-choice or true/false questions).
- Go to Course C and create a quiz.
- In the quiz:
- Add a random question that selects questions from the Random Questions A category (optionally include subcategories).
- Verify that the mdl_question_set_references table has an entry:
- questionscontextid = context of Category A
- filtercondition contains array with category ids, and original category ID is there
- Now, move the question category (Random Questions A) from Category A into Course C.
- Recheck the database:
- The question category's context (mdl_question_categories.contextid) is now Course C ✅
- But the questionscontextid in mdl_question_set_references still points to Category A ❌
- Create a backup of Course C.
- Observe that the backup includes unintended contexts and question categories, such as:
- Category A
- Unrelated question bank entries
🐞 Actual Result:
- questionscontextid remains set to the old context (Category A).
- Backups include unrelated higher-level category data.
- This may cause confusion, oversized backups, or restoration issues.
🔎 Expected Result:
- After moving the question category, Moodle should update questionscontextid in mdl_question_set_references to match the new context.
- Backups should only include Course C and the correct (new) question context.
🧨 Impact
- Incorrect backups: When a question category is moved between contexts (e.g., from a course category to a course), the questionscontextid in mdl_question_set_references is not updated.
- As a result, course backups include unrelated higher-level category contexts (e.g., Category A), pulling in unrelated question bank entries.
- This leads to:
- Larger and bloated backups
- Unintended exposure of unrelated content (especially critical in multi-tenant setups or shared Moodle environments)
- Restore conflicts when restoring into a different site or course structure
- Confusing behavior for admins and teachers, as the backup appears to include unrelated data
*👨🏻🔧 The fix (code)*
The move_question_set_references function is probably outdated. I saw that it works for the old approach (maybe migration also?) of keeping filter data. The new approach with a filter instead of {{questioncategoryid }}is not handled at all.
⚠ I'm unsure how this code should behave when multiple categories are inside the filter. There is room for improvement, I guess. It was enough for our instance, though.
function move_question_set_references(int $oldcategoryid, int $newcatgoryid, |
int $oldcontextid, int $newcontextid, bool $delete = false): void { |
global $DB; |
|
if ($delete || $oldcontextid !== $newcontextid) { |
$setreferences = $DB->get_recordset('question_set_references', ['questionscontextid' => $oldcontextid]); |
foreach ($setreferences as $setreference) { |
$filter = json_decode($setreference->filtercondition); |
|
if (isset($filter->filter->category->values) && is_array($filter->filter->category->values)) { |
$categoryvalues = $filter->filter->category->values; |
|
if((int)$categoryvalues[0] === $oldcategoryid){ |
$setreference->questionscontextid = $newcontextid; |
$DB->update_record('question_set_references', $setreference); |
}
|
}else if (isset($filter->questioncategoryid)) { |
if ((int)$filter->questioncategoryid === $oldcategoryid) { |
$setreference->questionscontextid = $newcontextid; |
if ($oldcategoryid !== $newcatgoryid) { |
$filter->questioncategoryid = $newcatgoryid; |
$setreference->filtercondition = json_encode($filter); |
}
|
$DB->update_record('question_set_references', $setreference); |
}
|
}
|
}
|
$setreferences->close(); |
}
|
}
|
*👨🏻🔧 The fix (DB)*
If the category has already been moved in the past, it will only be fixed if you move it back to the old context, apply the code fix, and move it one more time to a new place.
That's a bit hard to achieve. So I wrote some SQL to fix it globally.
⚠ Be extra cautious. It may not work with multiple categories in the filter.
UPDATE
|
mdl_question_set_references qsr
|
JOIN mdl_question_categories qc ON JSON_UNQUOTE( |
JSON_EXTRACT(
|
qsr.filtercondition,
|
'$.filter.category.values[0]' |
)
|
) = qc.id
|
SET
|
qsr.questionscontextid = qc.contextid
|
where
|
filtercondition like "%values%" |
- has a non-specific relationship to
-
MDL-82772 Column "questionscontextid" in mdl_question_set_references does not update when filter conditions are changed
-
- Closed
-