MDL-85334 quiz: Adds record snapshots to slot deleted event
Ensures critical question data is preserved when a quiz slot is deleted. This is especially useful when the slot-related information is no longer available. It adds snapshots for question and random question references to the event when present.
This commit is contained in:
@@ -32,6 +32,8 @@ namespace mod_quiz\event;
|
||||
*
|
||||
* - int quizid: the id of the quiz.
|
||||
* - int slotnumber: the slot number in quiz.
|
||||
* - int questionreferenceid: (optional) the question reference id for the slot.
|
||||
* - int questionsetreferenceid: (optional) the question set reference id for the slot (if added via random cat).
|
||||
* }
|
||||
*
|
||||
* @package mod_quiz
|
||||
@@ -79,6 +81,10 @@ class slot_deleted extends \core\event\base {
|
||||
if (!isset($this->other['slotnumber'])) {
|
||||
throw new \coding_exception('The \'slotnumber\' value must be set in other.');
|
||||
}
|
||||
// At least one of questionreferenceid or questionsetreferenceid must be set.
|
||||
if (!isset($this->other['questionreferenceid']) && !isset($this->other['questionsetreferenceid'])) {
|
||||
throw new \coding_exception('Either \'questionreferenceid\' or \'questionsetreferenceid\' must be set in other.');
|
||||
}
|
||||
}
|
||||
|
||||
public static function get_objectid_mapping() {
|
||||
@@ -88,7 +94,8 @@ class slot_deleted extends \core\event\base {
|
||||
public static function get_other_mapping() {
|
||||
$othermapped = [];
|
||||
$othermapped['quizid'] = ['db' => 'quiz', 'restore' => 'quiz'];
|
||||
|
||||
$othermapped['questionreferenceid'] = ['db' => 'question_references', 'restore' => 'question_references'];
|
||||
$othermapped['questionsetreferenceid'] = ['db' => 'question_set_references', 'restore' => 'question_set_references'];
|
||||
return $othermapped;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1096,7 +1096,9 @@ class structure {
|
||||
|
||||
$this->refresh_page_numbers_and_update_db();
|
||||
|
||||
$trans->allow_commit();
|
||||
// Safely extract reference IDs if available.
|
||||
$questionreferenceid = $questionreference->id ?? null;
|
||||
$questionsetreferenceid = $questionsetreference->id ?? null;
|
||||
|
||||
// Log slot deleted event.
|
||||
$event = \mod_quiz\event\slot_deleted::create([
|
||||
@@ -1105,9 +1107,22 @@ class structure {
|
||||
'other' => [
|
||||
'quizid' => $this->get_quizid(),
|
||||
'slotnumber' => $slotnumber,
|
||||
]
|
||||
'questionreferenceid' => $questionreferenceid,
|
||||
'questionsetreferenceid' => $questionsetreferenceid,
|
||||
],
|
||||
]);
|
||||
$event->add_record_snapshot('quiz_slots', $slot);
|
||||
|
||||
// Add snapshots for question and random question references when available.
|
||||
if ($questionreference) {
|
||||
$event->add_record_snapshot('question_references', $questionreference);
|
||||
}
|
||||
if ($questionsetreference) {
|
||||
$event->add_record_snapshot('question_set_references', $questionsetreference);
|
||||
}
|
||||
|
||||
$event->trigger();
|
||||
$trans->allow_commit();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1190,7 +1190,8 @@ final class events_test extends \advanced_testcase {
|
||||
'other' => [
|
||||
'quizid' => $quizobj->get_quizid(),
|
||||
'slotnumber' => 1,
|
||||
]
|
||||
'questionreferenceid' => 2,
|
||||
],
|
||||
];
|
||||
$event = \mod_quiz\event\slot_deleted::create($params);
|
||||
|
||||
@@ -1203,9 +1204,30 @@ final class events_test extends \advanced_testcase {
|
||||
// Check that the event data is valid.
|
||||
$this->assertInstanceOf('\mod_quiz\event\slot_deleted', $event);
|
||||
$this->assertEquals(context_module::instance($quizobj->get_cmid()), $event->get_context());
|
||||
$this->assertEquals(2, $event->other['questionreferenceid']);
|
||||
$this->assertEventContextNotUsed($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test validation failure when neither questionreferenceid nor questionsetreferenceid is provided.
|
||||
*/
|
||||
public function test_slot_deleted_no_questionreferenceids(): void {
|
||||
$quizobj = $this->prepare_quiz();
|
||||
|
||||
$params = [
|
||||
'objectid' => 1,
|
||||
'context' => \context_module::instance($quizobj->get_cmid()),
|
||||
'other' => [
|
||||
'quizid' => $quizobj->get_quizid(),
|
||||
'slotnumber' => 1,
|
||||
],
|
||||
];
|
||||
|
||||
$this->expectException(\coding_exception::class);
|
||||
$this->expectExceptionMessage('Either \'questionreferenceid\' or \'questionsetreferenceid\' must be set in other.');
|
||||
slot_deleted::create($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the slot mark updated event.
|
||||
*
|
||||
|
||||
@@ -653,8 +653,36 @@ final class structure_test extends \advanced_testcase {
|
||||
]);
|
||||
$structure = structure::create_for_quiz($quizobj);
|
||||
|
||||
// Set up event monitoring.
|
||||
$sink = $this->redirectEvents();
|
||||
|
||||
$structure->remove_slot(2);
|
||||
|
||||
// Get the event.
|
||||
$events = $sink->get_events();
|
||||
$sink->close();
|
||||
$this->assertCount(1, $events);
|
||||
$event = reset($events);
|
||||
|
||||
// Verify it's the right type of event.
|
||||
$this->assertInstanceOf(\mod_quiz\event\slot_deleted::class, $event);
|
||||
|
||||
// Get the quiz_slot snapshot.
|
||||
$slotid = $event->objectid;
|
||||
$quizslotsnapshot = $event->get_record_snapshot('quiz_slots', $slotid);
|
||||
$this->assertNotNull($quizslotsnapshot);
|
||||
|
||||
// Get the snapshot for question_references.
|
||||
$questionreference = $event->other['questionreferenceid'];
|
||||
if ($questionreference) {
|
||||
$qreferencesnapshot = $event->get_record_snapshot('question_references', $questionreference);
|
||||
$this->assertNotNull($qreferencesnapshot);
|
||||
}
|
||||
|
||||
// Should NOT have the snapshot for question_set_references.
|
||||
$questionsetreference = $event->other['questionsetreferenceid'];
|
||||
$this->assertNull($questionsetreference);
|
||||
|
||||
$structure = structure::create_for_quiz($quizobj);
|
||||
$this->assert_quiz_layout([
|
||||
['TF1', 1, 'truefalse'],
|
||||
@@ -684,10 +712,38 @@ final class structure_test extends \advanced_testcase {
|
||||
WHERE qs.quizid = ?
|
||||
AND qsr.component = ?
|
||||
AND qsr.questionarea = ?';
|
||||
|
||||
$randomq = $DB->get_record_sql($sql, [$quizobj->get_quizid(), 'mod_quiz', 'slot']);
|
||||
// Set up event monitoring.
|
||||
$sink = $this->redirectEvents();
|
||||
|
||||
$structure->remove_slot(2);
|
||||
|
||||
// Get the event.
|
||||
$events = $sink->get_events();
|
||||
$sink->close();
|
||||
$this->assertCount(1, $events);
|
||||
$event = reset($events);
|
||||
|
||||
// Verify it's the right type of event.
|
||||
$this->assertInstanceOf(\mod_quiz\event\slot_deleted::class, $event);
|
||||
|
||||
// Get the quiz_slot snapshot.
|
||||
$slotid = $event->objectid;
|
||||
$quizslotsnapshot = $event->get_record_snapshot('quiz_slots', $slotid);
|
||||
$this->assertNotNull($quizslotsnapshot);
|
||||
|
||||
// Should NOT have the snapshot for question_references.
|
||||
$questionreference = $event->other['questionreferenceid'];
|
||||
$this->assertNull($questionreference);
|
||||
|
||||
// Get the snapshot for question_set_references.
|
||||
$questionsetreference = $event->other['questionsetreferenceid'];
|
||||
if ($questionsetreference) {
|
||||
$qsetreferencesnapshot = $event->get_record_snapshot('question_set_references', $questionsetreference);
|
||||
$this->assertNotNull($qsetreferencesnapshot);
|
||||
}
|
||||
|
||||
$structure = structure::create_for_quiz($quizobj);
|
||||
$this->assert_quiz_layout([
|
||||
['TF1', 1, 'truefalse'],
|
||||
|
||||
Reference in New Issue
Block a user