From dab140fa456b25ffc6d125e83c7c5dc9b2c13b73 Mon Sep 17 00:00:00 2001 From: Cameron Ball Date: Thu, 25 Jul 2024 16:41:52 +0800 Subject: [PATCH] MDL-82587 mod_quiz: Add slot version updated event --- .../classes/event/slot_version_updated.php | 127 ++++++++++++++++++ .../external/submit_question_version.php | 30 +---- mod/quiz/classes/structure.php | 31 +++++ mod/quiz/lang/en/quiz.php | 1 + mod/quiz/tests/event/events_test.php | 28 ++++ mod/quiz/version.php | 2 +- 6 files changed, 195 insertions(+), 24 deletions(-) create mode 100644 mod/quiz/classes/event/slot_version_updated.php diff --git a/mod/quiz/classes/event/slot_version_updated.php b/mod/quiz/classes/event/slot_version_updated.php new file mode 100644 index 0000000000000..7feab5a523f6c --- /dev/null +++ b/mod/quiz/classes/event/slot_version_updated.php @@ -0,0 +1,127 @@ +. + +declare(strict_types=1); + +namespace mod_quiz\event; + +use core\exception\coding_exception; +use core\url; + +/** + * The question version of a slot has changed. + * + * @property-read array $other { + * Extra information about event. + * + * - int quizid: the id of the quiz. + * - int previousversion: the previous question version. + * - int newversion: the new question version. + * } + * + * @package mod_quiz + * @copyright 2024 Catalyst IT Australia Pty Ltd + * @author Cameron Ball + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class slot_version_updated extends \core\event\base { + + /** + * Initialise the event. + */ + protected function init(): void { + $this->data['objecttable'] = 'quiz_slots'; + $this->data['crud'] = 'u'; + $this->data['edulevel'] = self::LEVEL_TEACHING; + } + + /** + * Get event name. + * + * @return string + */ + public static function get_name(): string { + return get_string('eventslotversionupdated', 'mod_quiz'); + } + + /** + * Get event description. + * + * @return string + */ + public function get_description(): string { + return "The user with id '$this->userid' updated the slot with id '{$this->objectid}' " . + "belonging to the quiz with course module id '$this->contextinstanceid'. " . + "Its question version was changed from '{$this->other['previousversion']}' to '{$this->other['newversion']}'."; + } + + /** + * Get associated URL. + * + * @return url + */ + public function get_url(): url { + return new url('/mod/quiz/edit.php', ['cmid' => $this->contextinstanceid]); + } + + /** + * Validate event data. + */ + protected function validate_data(): void { + parent::validate_data(); + + if (!isset($this->objectid)) { + throw new coding_exception('The \'objectid\' value must be set.'); + } + + if (!isset($this->contextinstanceid)) { + throw new coding_exception('The \'contextinstanceid\' value must be set.'); + } + + if (!isset($this->other['quizid'])) { + throw new coding_exception('The \'quizid\' value must be set in other.'); + } + + if (!isset($this->other['previousversion'])) { + throw new coding_exception('The \'previousversion\' value must be set in other.'); + } + + if (!isset($this->other['newversion'])) { + throw new coding_exception('The \'newversion\' value must be set in other.'); + } + } + + /** + * Get object ID mapping. + * + * @return array + */ + public static function get_objectid_mapping(): array { + return ['db' => 'quiz_slots', 'restore' => 'quiz_question_instance']; + } + + /** + * Get other mapping. + * + * @return array + */ + public static function get_other_mapping(): array { + $othermapped = []; + $othermapped['quizid'] = ['db' => 'quiz', 'restore' => 'quiz']; + + return $othermapped; + } +} diff --git a/mod/quiz/classes/external/submit_question_version.php b/mod/quiz/classes/external/submit_question_version.php index 5facc34a8e7b1..70d9dcab69748 100644 --- a/mod/quiz/classes/external/submit_question_version.php +++ b/mod/quiz/classes/external/submit_question_version.php @@ -22,11 +22,12 @@ require_once($CFG->dirroot . '/question/engine/datalib.php'); require_once($CFG->libdir . '/questionlib.php'); +use core\context\module; use core_external\external_api; use core_external\external_function_parameters; use core_external\external_single_structure; use core_external\external_value; -use stdClass; +use mod_quiz\quiz_settings; /** * External api for changing the question version in the quiz. @@ -59,32 +60,15 @@ public static function execute_parameters(): external_function_parameters { */ public static function execute(int $slotid, int $newversion): array { global $DB; - $params = [ - 'slotid' => $slotid, - 'newversion' => $newversion - ]; - $params = self::validate_parameters(self::execute_parameters(), $params); - $response = []; - // Get the required data. - $referencedata = $DB->get_record('question_references', - ['itemid' => $params['slotid'], 'component' => 'mod_quiz', 'questionarea' => 'slot']); - $slotdata = $DB->get_record('quiz_slots', ['id' => $slotid]); + $params = self::validate_parameters(self::execute_parameters(), ['slotid' => $slotid, 'newversion' => $newversion]); + $slot = $DB->get_record('quiz_slots', ['id' => $slotid], '*', MUST_EXIST); - // Capability check. - [, $cm] = get_course_and_cm_from_instance($slotdata->quizid, 'quiz'); - $context = \context_module::instance($cm->id); + $context = module::instance(get_course_and_cm_from_instance($slot->quizid, 'quiz')[1]->id); self::validate_context($context); require_capability('mod/quiz:manage', $context); - $reference = new stdClass(); - $reference->id = $referencedata->id; - if ($params['newversion'] === 0) { - $reference->version = null; - } else { - $reference->version = $params['newversion']; - } - $response['result'] = $DB->update_record('question_references', $reference); - return $response; + $quizobj = quiz_settings::create($slot->quizid); + return ['result' => $quizobj->get_structure()->update_slot_version($slot->id, $params['newversion'])]; } /** diff --git a/mod/quiz/classes/structure.php b/mod/quiz/classes/structure.php index 15a72d372d145..c448a395483c6 100644 --- a/mod/quiz/classes/structure.php +++ b/mod/quiz/classes/structure.php @@ -24,6 +24,7 @@ use mod_quiz\event\quiz_grade_item_updated; use mod_quiz\event\slot_grade_item_updated; use mod_quiz\event\slot_mark_updated; +use mod_quiz\event\slot_version_updated; use mod_quiz\question\bank\qbank_helper; use mod_quiz\question\qubaids_for_quiz; use stdClass; @@ -1168,6 +1169,36 @@ public function update_slot_maxmark($slot, $maxmark) { return true; } + /** + * Update slot question version. + * + * @param int $id ID of row from the quiz_slots table. + * @param int $newversion The new question version for the slot. + * @return bool + */ + public function update_slot_version(int $id, int $newversion): bool { + global $DB; + + $slot = $this->get_slot_by_id($id); + $context = $this->quizobj->get_context(); + $params = ['usingcontextid' => $context->id, 'component' => 'mod_quiz', 'questionarea' => 'slot', 'itemid' => $slot->id]; + $reference = $DB->get_record('question_references', $params, '*', MUST_EXIST); + $oldversion = $reference->version; + $reference->version = $newversion === 0 ? null : $newversion; + + slot_version_updated::create([ + 'context' => $this->quizobj->get_context(), + 'objectid' => $slot->id, + 'other' => [ + 'quizid' => $this->get_quizid(), + 'previousversion' => $oldversion ?? get_string('alwayslatest', 'quiz'), + 'newversion' => $reference->version ?? get_string('alwayslatest', 'quiz'), + ], + ])->trigger(); + + return $DB->update_record('question_references', $reference); + } + /** * Change which grade this slot contributes to, for quizzes with multiple grades. * diff --git a/mod/quiz/lang/en/quiz.php b/mod/quiz/lang/en/quiz.php index 25d56edff470e..f7909ff2efa05 100644 --- a/mod/quiz/lang/en/quiz.php +++ b/mod/quiz/lang/en/quiz.php @@ -390,6 +390,7 @@ $string['eventslotdisplayedquestionnumberupdated'] = 'Slot displayed question number updated'; $string['eventslotgradeitemupdated'] = 'Slot grade item updated'; $string['eventslotmarkupdated'] = 'Slot mark updated'; +$string['eventslotversionupdated'] = 'Slot version updated'; $string['eventslotmoved'] = 'Slot moved'; $string['eventslotrequirepreviousupdated'] = 'Slot require previous updated'; $string['everynquestions'] = 'Every {$a} questions'; diff --git a/mod/quiz/tests/event/events_test.php b/mod/quiz/tests/event/events_test.php index a13a48dbc3919..381e5139cd6ae 100644 --- a/mod/quiz/tests/event/events_test.php +++ b/mod/quiz/tests/event/events_test.php @@ -28,6 +28,7 @@ use mod_quiz\quiz_attempt; use mod_quiz\quiz_settings; use context_module; +use mod_quiz\external\submit_question_version; /** * Unit tests for quiz events. @@ -68,6 +69,10 @@ protected function prepare_quiz() { $saq = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]); $numq = $questiongenerator->create_question('numerical', null, ['category' => $cat->id]); + // Update the numq question so it has multiple versions. Needed to test + // the version updated event. + $questiongenerator->update_question($numq, null, ['name' => 'Second version of numq']); + // Add them to the quiz. quiz_add_quiz_question($saq->id, $quiz); quiz_add_quiz_question($numq->id, $quiz); @@ -1202,6 +1207,29 @@ public function test_slot_mark_updated(): void { $this->assertEventContextNotUsed($event); } + /** + * Test the slot version updated event. + * + * @covers \mod_quiz\external\submit_question_version + */ + public function test_slot_version_updated(): void { + $quizobj = $this->prepare_quiz(); + $this->setAdminUser(); + + $quizobj->preload_questions(); + [, $numqslotid] = array_column($quizobj->get_questions(null, false), 'slotid'); + + $sink = $this->redirectEvents(); + submit_question_version::execute($numqslotid, 2); + $events = $sink->get_events(); + $event = reset($events); + + // Check that the event data is valid. + $this->assertInstanceOf('\mod_quiz\event\slot_version_updated', $event); + $this->assertEquals($quizobj->get_context(), $event->get_context()); + $this->assertEventContextNotUsed($event); + } + /** * Test quiz_grade_item_created. * diff --git a/mod/quiz/version.php b/mod/quiz/version.php index fa22a93ba4087..c0b9aa1a833ed 100644 --- a/mod/quiz/version.php +++ b/mod/quiz/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2024051700; +$plugin->version = 2024072500; $plugin->requires = 2024041600; $plugin->component = 'mod_quiz';