MDL-86378 mod_quiz: Add override reason field
This commit is contained in:
@@ -73,7 +73,7 @@ class backup_quiz_activity_structure_step extends backup_questions_activity_stru
|
||||
|
||||
$override = new backup_nested_element('override', ['id'], [
|
||||
'userid', 'groupid', 'timeopen', 'timeclose',
|
||||
'timelimit', 'attempts', 'password']);
|
||||
'timelimit', 'attempts', 'password', 'reason', 'reasonformat']);
|
||||
|
||||
$grades = new backup_nested_element('grades');
|
||||
|
||||
|
||||
+2
-2
@@ -78,7 +78,7 @@ class overrides implements data_source_interface {
|
||||
$override = $DB->get_record(
|
||||
'quiz_overrides',
|
||||
['quiz' => $quizid, 'userid' => $userid],
|
||||
'timeopen, timeclose, timelimit, attempts, password'
|
||||
'timeopen, timeclose, timelimit, attempts, password, reason, reasonformat'
|
||||
);
|
||||
break;
|
||||
case 'g':
|
||||
@@ -86,7 +86,7 @@ class overrides implements data_source_interface {
|
||||
$override = $DB->get_record(
|
||||
'quiz_overrides',
|
||||
['quiz' => $quizid, 'groupid' => $groupid],
|
||||
'timeopen, timeclose, timelimit, attempts, password'
|
||||
'timeopen, timeclose, timelimit, attempts, password, reason, reasonformat'
|
||||
);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
namespace mod_quiz\external;
|
||||
|
||||
use core_external\external_api;
|
||||
use core_external\external_format_value;
|
||||
use core_external\external_function_parameters;
|
||||
use core_external\external_multiple_structure;
|
||||
use core_external\external_single_structure;
|
||||
@@ -85,6 +86,8 @@ class get_overrides extends external_api {
|
||||
'timelimit' => new external_value(PARAM_INT, 'Override time limit value', VALUE_DEFAULT, null),
|
||||
'attempts' => new external_value(PARAM_INT, 'Override attempts value', VALUE_DEFAULT, null),
|
||||
'password' => new external_value(PARAM_TEXT, 'Override password', VALUE_DEFAULT, null),
|
||||
'reason' => new external_value(PARAM_RAW, 'Override reason', VALUE_DEFAULT, null),
|
||||
'reasonformat' => new external_format_value('reason', VALUE_DEFAULT, null),
|
||||
]);
|
||||
|
||||
return new external_single_structure([
|
||||
|
||||
+11
-1
@@ -17,6 +17,7 @@
|
||||
namespace mod_quiz\external;
|
||||
|
||||
use core_external\external_api;
|
||||
use core_external\external_format_value;
|
||||
use core_external\external_function_parameters;
|
||||
use core_external\external_multiple_structure;
|
||||
use core_external\external_single_structure;
|
||||
@@ -46,6 +47,8 @@ class save_overrides extends external_api {
|
||||
'timelimit' => new external_value(PARAM_INT, 'Quiz override time limit', VALUE_DEFAULT, null),
|
||||
'attempts' => new external_value(PARAM_INT, 'Quiz override attempt count', VALUE_DEFAULT, null),
|
||||
'password' => new external_value(PARAM_TEXT, 'Quiz override password', VALUE_DEFAULT, null),
|
||||
'reason' => new external_value(PARAM_RAW, 'Quiz override reason', VALUE_OPTIONAL),
|
||||
'reasonformat' => new external_format_value('reason', VALUE_OPTIONAL),
|
||||
]);
|
||||
|
||||
return new external_function_parameters([
|
||||
@@ -82,7 +85,14 @@ class save_overrides extends external_api {
|
||||
);
|
||||
|
||||
// Iterate over and save all overrides.
|
||||
$ids = array_map(fn($override) => $manager->save_override($override), $overrides);
|
||||
$ids = array_map(function (array $override) use ($manager): int {
|
||||
// Ensure reasonformat is set on create when a reason is supplied but no format given.
|
||||
if (empty($override['id']) && isset($override['reason']) && !isset($override['reasonformat'])) {
|
||||
$override['reasonformat'] = FORMAT_MOODLE;
|
||||
}
|
||||
|
||||
return $manager->save_override($override);
|
||||
}, $overrides);
|
||||
|
||||
return ['ids' => $ids];
|
||||
}
|
||||
|
||||
@@ -224,6 +224,16 @@ class edit_override_form extends moodleform {
|
||||
$mform->addHelpButton('attempts', 'attempts', 'quiz');
|
||||
$mform->setDefault('attempts', $this->quiz->attempts);
|
||||
|
||||
// Reason for override.
|
||||
$editoroptions = [
|
||||
'maxfiles' => 0,
|
||||
'noclean' => false,
|
||||
'context' => $this->context,
|
||||
];
|
||||
$mform->addElement('editor', 'reason_editor', get_string('overridereason', 'quiz'), null, $editoroptions);
|
||||
$mform->setType('reason_editor', PARAM_RAW);
|
||||
$mform->addHelpButton('reason_editor', 'overridereason', 'quiz');
|
||||
|
||||
// Submit buttons.
|
||||
$mform->addElement('submit', 'resetbutton',
|
||||
get_string('reverttodefaults', 'quiz'));
|
||||
|
||||
@@ -91,7 +91,14 @@ class override_manager {
|
||||
return isset($formdata->$key) && !is_null($formdata->$key);
|
||||
}, self::OVERRIDEABLE_QUIZ_SETTINGS);
|
||||
|
||||
if (!in_array(true, $keysthatareset)) {
|
||||
$hasoverridevalues = in_array(true, $keysthatareset, true);
|
||||
|
||||
// If updating, we can also just update the reason.
|
||||
if (!empty($formdata->id) && (property_exists($formdata, 'reason') || property_exists($formdata, 'reasonformat'))) {
|
||||
$hasoverridevalues = true;
|
||||
}
|
||||
|
||||
if (!$hasoverridevalues) {
|
||||
$errors['general'][] = new \lang_string('nooverridedata', 'quiz');
|
||||
}
|
||||
|
||||
@@ -221,6 +228,14 @@ class override_manager {
|
||||
// Remove values that are the same as currently in the quiz.
|
||||
$settings = $this->clear_unused_values($settings);
|
||||
|
||||
// Pass through the optional reason fields unchanged.
|
||||
if (array_key_exists('reason', $formdata)) {
|
||||
$settings['reason'] = $formdata['reason'];
|
||||
}
|
||||
if (array_key_exists('reasonformat', $formdata) && $formdata['reasonformat'] !== null) {
|
||||
$settings['reasonformat'] = $formdata['reasonformat'];
|
||||
}
|
||||
|
||||
// Add the user / group back as applicable.
|
||||
$userorgroupdata = array_intersect_key($formdata, array_flip(['userid', 'groupid', 'quiz', 'id']));
|
||||
|
||||
|
||||
@@ -104,6 +104,7 @@ class provider implements
|
||||
'timeopen' => 'privacy:metadata:quiz_overrides:timeopen',
|
||||
'timeclose' => 'privacy:metadata:quiz_overrides:timeclose',
|
||||
'timelimit' => 'privacy:metadata:quiz_overrides:timelimit',
|
||||
'reason' => 'privacy:metadata:quiz_overrides:reason',
|
||||
], 'privacy:metadata:quiz_overrides');
|
||||
|
||||
// These define the structure of the quiz.
|
||||
@@ -255,6 +256,8 @@ class provider implements
|
||||
qo.timeopen AS override_timeopen,
|
||||
qo.timeclose AS override_timeclose,
|
||||
qo.timelimit AS override_timelimit,
|
||||
qo.reason AS override_reason,
|
||||
qo.reasonformat AS override_reasonformat,
|
||||
c.id AS contextid,
|
||||
cm.id AS cmid
|
||||
FROM {context} c
|
||||
@@ -305,6 +308,10 @@ class provider implements
|
||||
if (!empty($quizdata->override_timelimit)) {
|
||||
$quizdata->override->timelimit = $quiz->override_timelimit;
|
||||
}
|
||||
if (!empty($quiz->override_reason)) {
|
||||
$format = $quiz->override_reasonformat ?? FORMAT_MOODLE;
|
||||
$quizdata->override->reason = format_text($quiz->override_reason, $format, ['context' => $context]);
|
||||
}
|
||||
}
|
||||
|
||||
$quizdata->accessdata = (object) [];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<XMLDB PATH="mod/quiz/db" VERSION="20250414" COMMENT="XMLDB file for Moodle mod/quiz"
|
||||
<XMLDB PATH="mod/quiz/db" VERSION="20251126" COMMENT="XMLDB file for Moodle mod/quiz"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
|
||||
>
|
||||
@@ -133,6 +133,8 @@
|
||||
<FIELD NAME="timelimit" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Time limit in seconds. Can be null, in which case the quiz default is used."/>
|
||||
<FIELD NAME="attempts" TYPE="int" LENGTH="6" NOTNULL="false" SEQUENCE="false"/>
|
||||
<FIELD NAME="password" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="Quiz password. Can be null, in which case the quiz default is used."/>
|
||||
<FIELD NAME="reason" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="An optional reason explaining why this override was granted."/>
|
||||
<FIELD NAME="reasonformat" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The internal format for the override reason."/>
|
||||
</FIELDS>
|
||||
<KEYS>
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||
|
||||
@@ -84,5 +84,27 @@ function xmldb_quiz_upgrade($oldversion) {
|
||||
upgrade_mod_savepoint(true, 2026022400, 'quiz');
|
||||
}
|
||||
|
||||
if ($oldversion < 2026030600) {
|
||||
// Define field reason to be added to quiz_overrides.
|
||||
$table = new xmldb_table('quiz_overrides');
|
||||
$field = new xmldb_field('reason', XMLDB_TYPE_TEXT, null, null, null, null, null, 'password');
|
||||
|
||||
// Conditionally launch add field reason.
|
||||
if (!$dbman->field_exists($table, $field)) {
|
||||
$dbman->add_field($table, $field);
|
||||
}
|
||||
|
||||
// Define field reasonformat to be added to quiz_overrides.
|
||||
$formatfield = new xmldb_field('reasonformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'reason');
|
||||
|
||||
// Conditionally launch add field reasonformat.
|
||||
if (!$dbman->field_exists($table, $formatfield)) {
|
||||
$dbman->add_field($table, $formatfield);
|
||||
}
|
||||
|
||||
// Quiz savepoint reached.
|
||||
upgrade_mod_savepoint(true, 2026030600, 'quiz');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -684,6 +684,8 @@ $string['overridedeletegroupsure'] = 'Are you sure you want to delete the overri
|
||||
$string['overridedeleteusersure'] = 'Are you sure you want to delete the override for user {$a}?';
|
||||
$string['overridegroup'] = 'Override group';
|
||||
$string['overridegroupeventname'] = '{$a->quiz} - {$a->group}';
|
||||
$string['overridereason'] = 'Reason for override';
|
||||
$string['overridereason_help'] = 'Optionally record the reason for this override.';
|
||||
$string['overrideinvalidattempts'] = 'Attempts value must be greater than zero.';
|
||||
$string['overrideinvalidexistingid'] = 'Existing override doesn\'t exist.';
|
||||
$string['overrideinvalidgroup'] = 'Group given doesn\'t exist.';
|
||||
@@ -764,6 +766,7 @@ $string['privacy:metadata:quiz_grades:timemodified'] = 'The time that the grade
|
||||
$string['privacy:metadata:quiz_grades:userid'] = 'The user who was graded.';
|
||||
$string['privacy:metadata:quiz_overrides'] = 'Details about overrides for this quiz';
|
||||
$string['privacy:metadata:quiz_overrides:quiz'] = 'The quiz with override information';
|
||||
$string['privacy:metadata:quiz_overrides:reason'] = 'Optional notes documenting the reason for a quiz override.';
|
||||
$string['privacy:metadata:quiz_overrides:timeclose'] = 'The new close time for the quiz.';
|
||||
$string['privacy:metadata:quiz_overrides:timelimit'] = 'The new time limit for the quiz.';
|
||||
$string['privacy:metadata:quiz_overrides:timeopen'] = 'The new open time for the quiz.';
|
||||
|
||||
@@ -88,6 +88,14 @@ foreach ($keys as $key) {
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare reason editor data.
|
||||
if (isset($override->reason)) {
|
||||
$data->reason_editor = [
|
||||
'text' => $override->reason,
|
||||
'format' => $override->reasonformat ?? FORMAT_MOODLE,
|
||||
];
|
||||
}
|
||||
|
||||
// If we are duplicating an override, then clear the user/group and override id
|
||||
// since they will change.
|
||||
if ($action === 'duplicate') {
|
||||
@@ -121,6 +129,13 @@ if ($mform->is_cancelled()) {
|
||||
$fromform->id = $overrideid;
|
||||
}
|
||||
|
||||
// Extract reason and reasonformat from editor field.
|
||||
if (isset($fromform->reason_editor)) {
|
||||
$fromform->reason = $fromform->reason_editor['text'] ?? '';
|
||||
$fromform->reasonformat = $fromform->reason_editor['format'] ?? FORMAT_MOODLE;
|
||||
unset($fromform->reason_editor);
|
||||
}
|
||||
|
||||
// Process the data.
|
||||
$id = $manager->save_override((array) $fromform);
|
||||
|
||||
|
||||
@@ -229,6 +229,20 @@ foreach ($overrides as $override) {
|
||||
get_string('enabled', 'quiz') : get_string('none', 'quiz');
|
||||
}
|
||||
|
||||
// Format reason.
|
||||
if (isset($override->reason) && $override->reason !== '') {
|
||||
$formattedreason = format_text(
|
||||
$override->reason,
|
||||
$override->reasonformat ?? FORMAT_MOODLE,
|
||||
['context' => $context],
|
||||
);
|
||||
|
||||
if ($formattedreason !== '') {
|
||||
$fields[] = get_string('overridereason', 'quiz');
|
||||
$values[] = $formattedreason;
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare the information about who this override applies to.
|
||||
$extranamebit = $active ? '' : '*';
|
||||
$usercells = [];
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_quiz\backup;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . "/phpunit/classes/restore_date_testcase.php");
|
||||
|
||||
/**
|
||||
* Restore override tests.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @author Alexander Van der Bellen <[email protected]>
|
||||
* @copyright 2025 Catalyst IT Australia Pty Ltd
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
#[\PHPUnit\Framework\Attributes\CoversClass(\restore_quiz_activity_structure_step::class)]
|
||||
final class restore_override_test extends \restore_date_testcase {
|
||||
/**
|
||||
* Test restore overrides with reason.
|
||||
*/
|
||||
public function test_restore_overrides_with_reason(): void {
|
||||
global $DB, $USER;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id]);
|
||||
|
||||
$useroverride = (object) [
|
||||
'quiz' => $quiz->id,
|
||||
'userid' => $USER->id,
|
||||
'timeopen' => 100,
|
||||
'reason' => 'This is a reason',
|
||||
'reasonformat' => FORMAT_HTML,
|
||||
];
|
||||
$DB->insert_record('quiz_overrides', $useroverride);
|
||||
|
||||
// Back up and restore.
|
||||
$newcourseid = $this->backup_and_restore($course);
|
||||
$newquiz = $DB->get_record('quiz', ['course' => $newcourseid]);
|
||||
$overrides = $DB->get_records('quiz_overrides', ['quiz' => $newquiz->id]);
|
||||
|
||||
$this->assertEquals(1, count($overrides));
|
||||
$restoredoverride = reset($overrides);
|
||||
$this->assertEquals($useroverride->reason, $restoredoverride->reason);
|
||||
$this->assertEquals($useroverride->reasonformat, $restoredoverride->reasonformat);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
@mod @mod_quiz
|
||||
Feature: Quiz override reason
|
||||
In order to explain why an override was granted
|
||||
As a teacher
|
||||
I need to be able to add a reason to an override
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
| student1 | Student | 1 | student1@example.com |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
And the following "groups" exist:
|
||||
| name | course | idnumber |
|
||||
| Group 1 | C1 | G1 |
|
||||
And the following "group members" exist:
|
||||
| user | group |
|
||||
| student1 | G1 |
|
||||
And the following "activities" exist:
|
||||
| activity | name | intro | course |
|
||||
| quiz | Test quiz | Test quiz intro | C1 |
|
||||
|
||||
@javascript
|
||||
Scenario: Add a user override with a reason
|
||||
Given I am on the "Test quiz" "mod_quiz > View" page logged in as "teacher1"
|
||||
When I navigate to "Overrides" in current page administration
|
||||
And I press "Add user override"
|
||||
And I set the following fields to these values:
|
||||
| Override user | Student 1 |
|
||||
| Close the quiz | ##1 Jan 2030 08:00## |
|
||||
| Reason | Extension granted for medical reasons |
|
||||
And I press "Save"
|
||||
And I should see "Extension granted for medical reasons" in the "Reason for override" "table_row"
|
||||
And I click on "Edit" "link" in the "Student 1" "table_row"
|
||||
And the field "Reason for override" matches value "Extension granted for medical reasons"
|
||||
And I set the following fields to these values:
|
||||
| Reason | Updated reason after review |
|
||||
And I press "Save"
|
||||
Then I should see "Updated reason after review" in the "Reason for override" "table_row"
|
||||
|
||||
@javascript
|
||||
Scenario: Add a group override with a reason
|
||||
Given I am on the "Test quiz" "mod_quiz > View" page logged in as "teacher1"
|
||||
When I navigate to "Overrides" in current page administration
|
||||
And I select "Group overrides" from the "jump" singleselect
|
||||
And I press "Add group override"
|
||||
And I set the following fields to these values:
|
||||
| Override group | Group 1 |
|
||||
| Close the quiz | ##1 Jan 2030 08:00## |
|
||||
| Reason | Additional time approved for this group |
|
||||
And I press "Save"
|
||||
And I should see "Additional time approved for this group" in the "Reason for override" "table_row"
|
||||
And I click on "Edit" "link" in the "Group 1" "table_row"
|
||||
And the field "Reason for override" matches value "Additional time approved for this group"
|
||||
And I set the following fields to these values:
|
||||
| Reason | Updated group reason after review |
|
||||
And I press "Save"
|
||||
Then I should see "Updated group reason after review" in the "Reason for override" "table_row"
|
||||
+261
@@ -216,4 +216,265 @@ final class override_test extends \core_external\tests\externallib_testcase {
|
||||
$this->assertNotEmpty($result['ids']);
|
||||
$this->assertContains($id, $result['ids']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides values to test_save_reason_overrides
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function save_reason_overrides_provider(): array {
|
||||
return [
|
||||
'create with reason' => [
|
||||
'data' => [
|
||||
'reason' => 'This is a reason',
|
||||
'reasonformat' => FORMAT_HTML,
|
||||
'timeopen' => 999,
|
||||
],
|
||||
'expectedreason' => 'This is a reason',
|
||||
'expectedformat' => FORMAT_HTML,
|
||||
],
|
||||
'create with reason no format' => [
|
||||
'data' => [
|
||||
'reason' => 'This is a reason',
|
||||
'timeopen' => 999,
|
||||
],
|
||||
'expectedreason' => 'This is a reason',
|
||||
'expectedformat' => FORMAT_MOODLE,
|
||||
],
|
||||
'create with reason and different format' => [
|
||||
'data' => [
|
||||
'reason' => 'This is a reason',
|
||||
'reasonformat' => FORMAT_MOODLE,
|
||||
'timeopen' => 999,
|
||||
],
|
||||
'expectedreason' => 'This is a reason',
|
||||
'expectedformat' => FORMAT_MOODLE,
|
||||
],
|
||||
'create with reason and null format' => [
|
||||
'data' => [
|
||||
'reason' => 'This is a reason',
|
||||
'reasonformat' => null,
|
||||
'timeopen' => 999,
|
||||
],
|
||||
'expectedreason' => 'This is a reason',
|
||||
'expectedformat' => FORMAT_MOODLE,
|
||||
],
|
||||
'create with reason only (fail)' => [
|
||||
'data' => [
|
||||
'reason' => 'This is a reason',
|
||||
],
|
||||
'expectedreason' => null,
|
||||
'expectedformat' => FORMAT_HTML,
|
||||
'expectedexception' => \invalid_parameter_exception::class,
|
||||
],
|
||||
'create with format only' => [
|
||||
'data' => [
|
||||
'reasonformat' => FORMAT_MOODLE,
|
||||
],
|
||||
'expectedreason' => null,
|
||||
'expectedformat' => FORMAT_MOODLE,
|
||||
'expectedexception' => \invalid_parameter_exception::class,
|
||||
],
|
||||
'create with null reason' => [
|
||||
'data' => [
|
||||
'reason' => null,
|
||||
],
|
||||
'expectedreason' => null,
|
||||
'expectedformat' => FORMAT_HTML,
|
||||
'expectedexception' => \invalid_parameter_exception::class,
|
||||
],
|
||||
'create with no reason or format' => [
|
||||
'data' => [],
|
||||
'expectedreason' => null,
|
||||
'expectedformat' => FORMAT_HTML,
|
||||
'expectedexception' => \invalid_parameter_exception::class,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests save_overrides with reason
|
||||
*
|
||||
* @dataProvider save_reason_overrides_provider
|
||||
* @param array $data
|
||||
* @param string|null $expectedreason
|
||||
* @param int|string|null $expectedformat
|
||||
* @param string|null $expectedexception
|
||||
*/
|
||||
public function test_save_reason_overrides(
|
||||
array $data,
|
||||
?string $expectedreason,
|
||||
int|string|null $expectedformat,
|
||||
?string $expectedexception = null
|
||||
): void {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
$quiz = $this->create_quiz();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
|
||||
$data = array_merge($data, ['userid' => $user->id]);
|
||||
|
||||
$payload = [
|
||||
'quizid' => $quiz->id,
|
||||
'overrides' => [
|
||||
$data,
|
||||
],
|
||||
];
|
||||
|
||||
if ($expectedexception) {
|
||||
$this->expectException($expectedexception);
|
||||
}
|
||||
|
||||
$result = save_overrides::execute($payload);
|
||||
|
||||
if ($expectedexception) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->assertNotEmpty($result['ids']);
|
||||
$this->assertCount(1, $result['ids']);
|
||||
$overrideid = reset($result['ids']);
|
||||
|
||||
$override = $DB->get_record('quiz_overrides', ['id' => $overrideid]);
|
||||
$this->assertEquals($expectedreason, $override->reason);
|
||||
$this->assertEquals($expectedformat, $override->reasonformat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides values to test_update_reason_overrides
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function update_reason_overrides_provider(): array {
|
||||
return [
|
||||
'update reason' => [
|
||||
'initialreason' => 'Initial reason',
|
||||
'initialformat' => FORMAT_HTML,
|
||||
'data' => [
|
||||
'reason' => 'Updated reason',
|
||||
'reasonformat' => FORMAT_HTML,
|
||||
],
|
||||
'expectedreason' => 'Updated reason',
|
||||
'expectedformat' => FORMAT_HTML,
|
||||
],
|
||||
'update reason format' => [
|
||||
'initialreason' => 'Initial reason',
|
||||
'initialformat' => FORMAT_HTML,
|
||||
'data' => [
|
||||
'reason' => 'Initial reason',
|
||||
'reasonformat' => FORMAT_MOODLE,
|
||||
],
|
||||
'expectedreason' => 'Initial reason',
|
||||
'expectedformat' => FORMAT_MOODLE,
|
||||
],
|
||||
'update reason only' => [
|
||||
'initialreason' => 'Initial reason',
|
||||
'initialformat' => FORMAT_HTML,
|
||||
'data' => [
|
||||
'reason' => 'Updated reason',
|
||||
],
|
||||
'expectedreason' => 'Updated reason',
|
||||
'expectedformat' => FORMAT_HTML,
|
||||
],
|
||||
'update format only' => [
|
||||
'initialreason' => 'Initial reason',
|
||||
'initialformat' => FORMAT_HTML,
|
||||
'data' => [
|
||||
'reasonformat' => FORMAT_MOODLE,
|
||||
],
|
||||
'expectedreason' => 'Initial reason',
|
||||
'expectedformat' => FORMAT_MOODLE,
|
||||
],
|
||||
'update reason to null' => [
|
||||
'initialreason' => 'Initial reason',
|
||||
'initialformat' => FORMAT_HTML,
|
||||
'data' => [
|
||||
'reason' => null,
|
||||
],
|
||||
'expectedreason' => null,
|
||||
'expectedformat' => FORMAT_HTML,
|
||||
],
|
||||
'update format to null' => [
|
||||
'initialreason' => 'Initial reason',
|
||||
'initialformat' => FORMAT_HTML,
|
||||
'data' => [
|
||||
'reasonformat' => null,
|
||||
],
|
||||
'expectedreason' => null,
|
||||
'expectedformat' => null,
|
||||
'expectedexception' => \invalid_parameter_exception::class,
|
||||
],
|
||||
'update no changes' => [
|
||||
'initialreason' => 'Initial reason',
|
||||
'initialformat' => FORMAT_HTML,
|
||||
'data' => [],
|
||||
'expectedreason' => null,
|
||||
'expectedformat' => null,
|
||||
'expectedexception' => \invalid_parameter_exception::class,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests update_overrides with reason
|
||||
*
|
||||
* @dataProvider update_reason_overrides_provider
|
||||
* @param string|null $initialreason
|
||||
* @param int|string|null $initialformat
|
||||
* @param array $data
|
||||
* @param string|null $expectedreason
|
||||
* @param int|string|null $expectedformat
|
||||
* @param string|null $expectedexception
|
||||
*/
|
||||
public function test_update_reason_overrides(
|
||||
?string $initialreason,
|
||||
int|string|null $initialformat,
|
||||
array $data,
|
||||
?string $expectedreason,
|
||||
int|string|null $expectedformat,
|
||||
?string $expectedexception = null
|
||||
): void {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
$quiz = $this->create_quiz();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
|
||||
// Create initial override.
|
||||
$overrideid = $DB->insert_record('quiz_overrides', [
|
||||
'quiz' => $quiz->id,
|
||||
'userid' => $user->id,
|
||||
'reason' => $initialreason,
|
||||
'reasonformat' => $initialformat,
|
||||
]);
|
||||
|
||||
$data['id'] = $overrideid;
|
||||
$data['userid'] = $user->id;
|
||||
|
||||
$payload = [
|
||||
'quizid' => $quiz->id,
|
||||
'overrides' => [
|
||||
$data,
|
||||
],
|
||||
];
|
||||
|
||||
if ($expectedexception) {
|
||||
$this->expectException($expectedexception);
|
||||
}
|
||||
|
||||
save_overrides::execute($payload);
|
||||
|
||||
if ($expectedexception) {
|
||||
return;
|
||||
}
|
||||
|
||||
$override = $DB->get_record('quiz_overrides', ['id' => $overrideid]);
|
||||
$this->assertEquals($expectedreason, $override->reason);
|
||||
$this->assertEquals($expectedformat, $override->reasonformat);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,6 +148,8 @@ final class provider_test extends \core_privacy\tests\provider_testcase {
|
||||
'userid' => $user->id,
|
||||
'timeclose' => 1300,
|
||||
'timelimit' => null,
|
||||
'reason' => 'This is a reason',
|
||||
'reasonformat' => FORMAT_MOODLE,
|
||||
]);
|
||||
|
||||
// Run as the user and make an attempt on the quiz.
|
||||
@@ -177,6 +179,7 @@ final class provider_test extends \core_privacy\tests\provider_testcase {
|
||||
|
||||
$quizdata = $writer->get_data([]);
|
||||
$this->assertEquals($quizobj->get_quiz_name(), $quizdata->name);
|
||||
$this->assertEquals(format_text('This is a reason', FORMAT_MOODLE, ['context' => $context]), $quizdata->override->reason);
|
||||
|
||||
// Every module has an intro.
|
||||
$this->assertTrue(isset($quizdata->intro));
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2026022400;
|
||||
$plugin->version = 2026030600;
|
||||
$plugin->requires = 2025092600;
|
||||
$plugin->component = 'mod_quiz';
|
||||
|
||||
Reference in New Issue
Block a user