diff --git a/mod/quiz/accessrule/seb/classes/seb_quiz_settings.php b/mod/quiz/accessrule/seb/classes/seb_quiz_settings.php index 1e338b497869a..057635cf544cf 100644 --- a/mod/quiz/accessrule/seb/classes/seb_quiz_settings.php +++ b/mod/quiz/accessrule/seb/classes/seb_quiz_settings.php @@ -34,6 +34,7 @@ use lang_string; use moodle_exception; use moodle_url; +use mod_quiz\quiz_settings; defined('MOODLE_INTERNAL') || die(); @@ -196,11 +197,39 @@ protected static function define_properties(): array { * @return false|\quizaccess_seb\seb_quiz_settings */ public static function get_by_quiz_id(int $quizid) { - if ($data = self::get_quiz_settings_cache()->get($quizid)) { - return new static(0, $data); + global $USER; + // Skip cache if quiz has overrides for user. + if (!($hasoverrides = quiz_has_user_overrides($quizid))) { + if ($data = self::get_quiz_settings_cache()->get($quizid)) { + return new static(0, $data); + } } - return self::get_record(['quizid' => $quizid]); + $quiz = self::get_record(['quizid' => $quizid]); + + // Overwrite settings from override manager if available. + if ($hasoverrides) { + // Create blank seb_quiz_settings instance if none exists. + if (!$quiz) { + $record = new \stdClass(); + $record->quizid = $quizid; + $record->cmid = get_coursemodule_from_instance('quiz', $quizid)->id; + $quiz = $quiz ?: new self(0, $record); + } + + $settings = quiz_settings::create_for_cmid($quiz->get('cmid'), $USER->id)->get_quiz(); + // If overriding enabled, overwrite seb settings. + if (isset($settings->enableseboverride) && !!$settings->enableseboverride) { + $prefix = 'seb_'; + foreach (array_keys(self::properties_definition()) as $key) { + if (isset($settings->{$prefix.$key})) { + $quiz->set($key, $settings->{$prefix.$key}); + } + } + } + } + + return $quiz; } /** @@ -210,10 +239,11 @@ public static function get_by_quiz_id(int $quizid) { * @return string|null */ public static function get_config_by_quiz_id(int $quizid): ?string { - $config = self::get_config_cache()->get($quizid); - - if ($config !== false) { - return $config; + // Skip cache if quiz has overrides for user. + if (quiz_has_user_overrides($quizid)) { + if ($config = self::get_config_cache()->get($quizid) !== false) { + return $config; + } } $config = null; diff --git a/mod/quiz/accessrule/seb/classes/settings_provider.php b/mod/quiz/accessrule/seb/classes/settings_provider.php index f7a8ffdaa10f4..8983920b8d9ee 100644 --- a/mod/quiz/accessrule/seb/classes/settings_provider.php +++ b/mod/quiz/accessrule/seb/classes/settings_provider.php @@ -188,16 +188,17 @@ protected static function add_seb_header_element(\mod_quiz_mod_form $quizform, \ * @param \MoodleQuickForm $mform the wrapped MoodleQuickForm. */ protected static function add_seb_usage_options(\mod_quiz_mod_form $quizform, \MoodleQuickForm $mform) { + $options = self::get_requiresafeexambrowser_options($quizform->get_context()); $element = $mform->createElement( 'select', 'seb_requiresafeexambrowser', get_string('seb_requiresafeexambrowser', 'quizaccess_seb'), - self::get_requiresafeexambrowser_options($quizform->get_context()) + $options ); self::insert_element($quizform, $mform, $element); self::set_type($quizform, $mform, 'seb_requiresafeexambrowser', PARAM_INT); - self::set_default($quizform, $mform, 'seb_requiresafeexambrowser', self::USE_SEB_NO); + self::set_default($quizform, $mform, 'seb_requiresafeexambrowser', array_key_first($options)); self::add_help_button($quizform, $mform, 'seb_requiresafeexambrowser'); if (self::is_conflicting_permissions($quizform->get_context())) { @@ -549,6 +550,11 @@ public static function is_conflicting_permissions(\context $context) { return true; } + if (!self::can_unrequire($context) && + $settings->get('requiresafeexambrowser') == self::USE_SEB_NO) { + return true; + } + return false; } @@ -559,7 +565,11 @@ public static function is_conflicting_permissions(\context $context) { * @return array */ public static function get_requiresafeexambrowser_options(\context $context): array { - $options[self::USE_SEB_NO] = get_string('no'); + $options = []; + + if (self::can_unrequire($context) || self::is_conflicting_permissions($context)) { + $options[self::USE_SEB_NO] = get_string('no'); + } if (self::can_configure_manually($context) || self::is_conflicting_permissions($context)) { $options[self::USE_SEB_CONFIG_MANUALLY] = get_string('seb_use_manually', 'quizaccess_seb'); @@ -584,7 +594,7 @@ public static function get_requiresafeexambrowser_options(\context $context): ar * Returns a list of templates. * @return array */ - protected static function get_template_options(): array { + public static function get_template_options(): array { $templates = []; $records = template::get_records(['enabled' => 1], 'name'); if ($records) { @@ -785,6 +795,26 @@ public static function can_change_seb_allowedbrowserexamkeys(\context $context): return has_capability('quizaccess/seb:manage_seb_allowedbrowserexamkeys', $context); } + /** + * Check if the current user can unrequire SEB from quiz. + * + * @param \context $context Context to check access in. + * @return bool + */ + public static function can_unrequire(\context $context): bool { + return has_capability('quizaccess/seb:manage_seb_unrequiresafeexambrowser', $context); + } + + /** + * Check if the current user can unrequire SEB from quiz in the override menu. + * + * @param \context $context Context to check access in. + * @return bool + */ + public static function can_override_unrequire(\context $context): bool { + return has_capability('quizaccess/seb:override_seb_unrequiresafeexambrowser', $context); + } + /** * Check if the current user can config SEB manually. * diff --git a/mod/quiz/accessrule/seb/db/access.php b/mod/quiz/accessrule/seb/db/access.php index d79df40695928..f0dcfc2d3e757 100644 --- a/mod/quiz/accessrule/seb/db/access.php +++ b/mod/quiz/accessrule/seb/db/access.php @@ -49,6 +49,22 @@ 'editingteacher' => CAP_ALLOW ] ], + 'quizaccess/seb:manage_seb_unrequiresafeexambrowser' => [ + 'captype' => 'write', + 'contextlevel' => CONTEXT_MODULE, + 'archetypes' => [ + 'manager' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW + ] + ], + 'quizaccess/seb:override_seb_unrequiresafeexambrowser' => [ + 'captype' => 'write', + 'contextlevel' => CONTEXT_MODULE, + 'archetypes' => [ + 'manager' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW + ] + ], 'quizaccess/seb:manage_seb_templateid' => [ 'captype' => 'read', 'contextlevel' => CONTEXT_MODULE, diff --git a/mod/quiz/accessrule/seb/lang/en/quizaccess_seb.php b/mod/quiz/accessrule/seb/lang/en/quizaccess_seb.php index c340f295a2f2d..8e233fb979b4e 100644 --- a/mod/quiz/accessrule/seb/lang/en/quizaccess_seb.php +++ b/mod/quiz/accessrule/seb/lang/en/quizaccess_seb.php @@ -106,6 +106,8 @@ $string['seb:manage_seb_regexallowed'] = 'Change SEB quiz setting: Regex expressions allowed'; $string['seb:manage_seb_regexblocked'] = 'Change SEB quiz setting: Regex expressions blocked'; $string['seb:manage_seb_requiresafeexambrowser'] = 'Change SEB quiz setting: Require Safe Exam Browser'; +$string['seb:manage_seb_unrequiresafeexambrowser'] = 'Change SEB quiz setting: Do not require Safe Exam Browser'; +$string['seb:override_seb_unrequiresafeexambrowser'] = 'Override SEB quiz setting: Do not require Safe Exam Browser'; $string['seb:manage_seb_showkeyboardlayout'] = 'Change SEB quiz setting: Show keyboard layout'; $string['seb:manage_seb_showreloadbutton'] = 'Change SEB quiz setting: Show reload button'; $string['seb:manage_seb_showsebtaskbar'] = 'Change SEB quiz setting: Show task bar'; diff --git a/mod/quiz/accessrule/seb/tests/settings_provider_test.php b/mod/quiz/accessrule/seb/tests/settings_provider_test.php index 821d1c9e36aad..0626fc7dbb5d1 100644 --- a/mod/quiz/accessrule/seb/tests/settings_provider_test.php +++ b/mod/quiz/accessrule/seb/tests/settings_provider_test.php @@ -656,6 +656,15 @@ public function test_get_requiresafeexambrowser_options($settingcapability): voi $options = settings_provider::get_requiresafeexambrowser_options($this->context); + $this->assertCount(1, $options); + $this->assertFalse(array_key_exists(settings_provider::USE_SEB_CONFIG_MANUALLY, $options)); + $this->assertFalse(array_key_exists(settings_provider::USE_SEB_TEMPLATE, $options)); + $this->assertFalse(array_key_exists(settings_provider::USE_SEB_UPLOAD_CONFIG, $options)); + $this->assertTrue(array_key_exists(settings_provider::USE_SEB_CLIENT_CONFIG, $options)); + $this->assertFalse(array_key_exists(settings_provider::USE_SEB_NO, $options)); + + assign_capability('quizaccess/seb:manage_seb_unrequiresafeexambrowser', CAP_ALLOW, $this->roleid, $this->context->id); + $options = settings_provider::get_requiresafeexambrowser_options($this->context); $this->assertCount(2, $options); $this->assertFalse(array_key_exists(settings_provider::USE_SEB_CONFIG_MANUALLY, $options)); $this->assertFalse(array_key_exists(settings_provider::USE_SEB_TEMPLATE, $options)); diff --git a/mod/quiz/accessrule/seb/version.php b/mod/quiz/accessrule/seb/version.php index 93d40beabaa19..312231bc2648e 100644 --- a/mod/quiz/accessrule/seb/version.php +++ b/mod/quiz/accessrule/seb/version.php @@ -25,7 +25,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2024042200; +$plugin->version = 2024073101; $plugin->requires = 2024041600; $plugin->component = 'quizaccess_seb'; $plugin->maturity = MATURITY_STABLE; diff --git a/mod/quiz/classes/form/edit_override_form.php b/mod/quiz/classes/form/edit_override_form.php index 4f5c7c6786074..829efbeaaad03 100644 --- a/mod/quiz/classes/form/edit_override_form.php +++ b/mod/quiz/classes/form/edit_override_form.php @@ -23,6 +23,7 @@ use moodle_url; use moodleform; use stdClass; +use quizaccess_seb\{seb_quiz_settings,settings_provider}; defined('MOODLE_INTERNAL') || die(); @@ -59,6 +60,9 @@ class edit_override_form extends moodleform { /** @var int overrideid, if provided. */ protected int $overrideid; + /** @var array array of seb settings to override. */ + protected array $sebdata; + /** * Constructor. * @@ -80,6 +84,7 @@ public function __construct(moodle_url $submiturl, $this->groupid = empty($override->groupid) ? 0 : $override->groupid; $this->userid = empty($override->userid) ? 0 : $override->userid; $this->overrideid = $override->id ?? 0; + $this->sebdata = empty($override->sebdata) ? [] : unserialize($override->sebdata); parent::__construct($submiturl); } @@ -224,6 +229,9 @@ protected function definition() { $mform->addHelpButton('attempts', 'attempts', 'quiz'); $mform->setDefault('attempts', $this->quiz->attempts); + // SEB override settings. + $this->display_seb_settings($mform); + // Submit buttons. $mform->addElement('submit', 'resetbutton', get_string('reverttodefaults', 'quiz')); @@ -239,6 +247,164 @@ protected function definition() { $mform->closeHeaderBefore('buttonbar'); } + /** + * Add SEB settings to the form. + * + * @param \MoodleQuickForm $mform + * @return void + */ + protected function display_seb_settings($mform) { + $mform->addElement('header', 'seb', get_string('seb', 'quizaccess_seb')); + + $mform->addElement('checkbox', 'enableseboverride', get_string('enabled', 'quizaccess_seb')); + $mform->setDefault('enableseboverride', $this->sebdata['enableseboverride'] ?? false); + + // "Require the use of Safe Exam Browser". + if (settings_provider::can_override_unrequire($this->context)) { + $requireseboptions[settings_provider::USE_SEB_NO] = get_string('no'); + } + + if (settings_provider::can_configure_manually($this->context) || self::is_conflicting_permissions($this->context)) { + $requireseboptions[settings_provider::USE_SEB_CONFIG_MANUALLY] = get_string('seb_use_manually', 'quizaccess_seb'); + } + + if (settings_provider::can_use_seb_template($this->context) || self::is_conflicting_permissions($this->context)) { + if (!empty(settings_provider::get_template_options())) { + $requireseboptions[settings_provider::USE_SEB_TEMPLATE] = get_string('seb_use_template', 'quizaccess_seb'); + } + } + + $requireseboptions[settings_provider::USE_SEB_CLIENT_CONFIG] = get_string('seb_use_client', 'quizaccess_seb'); + + $mform->addElement( + 'select', + 'seb_requiresafeexambrowser', + get_string('seb_requiresafeexambrowser', 'quizaccess_seb'), + $requireseboptions + ); + + $mform->setType('seb_requiresafeexambrowser', PARAM_INT); + $mform->setDefault('seb_requiresafeexambrowser', $this->sebdata['seb_requiresafeexambrowser'] ?? $this->quiz->seb_requiresafeexambrowser ?? 0); + $mform->addHelpButton('seb_requiresafeexambrowser', 'seb_requiresafeexambrowser', 'quizaccess_seb'); + $mform->disabledIf('seb_requiresafeexambrowser', 'enableseboverride'); + + if (settings_provider::is_conflicting_permissions($this->context)) { + $mform->freeze('seb_requiresafeexambrowser'); + } + + // "Safe Exam Browser config template". + if (settings_provider::can_use_seb_template($this->context) || settings_provider::is_conflicting_permissions($this->context)) { + $element = $mform->addElement( + 'select', + 'seb_templateid', + get_string('seb_templateid', 'quizaccess_seb'), + settings_provider::get_template_options() + ); + } else { + $element = $mform->addElement('hidden', 'seb_templateid'); + } + + $mform->setType('seb_templateid', PARAM_INT); + $mform->setDefault('seb_templateid', $this->sebdata['seb_templateid'] ?? $this->quiz->seb_templateid ?? 0); + $mform->addHelpButton('seb_templateid', 'seb_templateid', 'quizaccess_seb'); + $mform->disabledIf('seb_templateid', 'enableseboverride'); + + if (settings_provider::is_conflicting_permissions($this->context)) { + $mform->freeze('seb_templateid'); + } + + // "Show Safe Exam browser download button". + if (settings_provider::can_change_seb_showsebdownloadlink($this->context)) { + $mform->addElement('selectyesno', + 'seb_showsebdownloadlink', + get_string('seb_showsebdownloadlink', 'quizaccess_seb') + ); + + $mform->setType('seb_showsebdownloadlink', PARAM_BOOL); + $mform->setDefault('seb_showsebdownloadlink', $this->sebdata['seb_showsebdownloadlink'] ?? $this->quiz->seb_showsebdownloadlink ?? 1); + $mform->addHelpButton('seb_showsebdownloadlink', 'seb_showsebdownloadlink', 'quizaccess_seb'); + $mform->disabledIf('seb_showsebdownloadlink', 'enableseboverride'); + } + + // Manual config elements. + $defaults = settings_provider::get_seb_config_element_defaults(); + $types = settings_provider::get_seb_config_element_types(); + + foreach (settings_provider::get_seb_config_elements() as $name => $type) { + if (!settings_provider::can_manage_seb_config_setting($name, $this->context)) { + $type = 'hidden'; + } + + $mform->addElement($type, $name, get_string($name, 'quizaccess_seb')); + + $mform->addHelpButton($name, $name, 'quizaccess_seb'); + $mform->setType('seb_showsebdownloadlink', PARAM_BOOL); + $mform->setDefault('seb_showsebdownloadlink', $this->sebdata['seb_showsebdownloadlink'] ?? $this->quiz->seb_showsebdownloadlink ?? 1); + $mform->disabledIf($name, 'enableseboverride'); + + if (isset($defaults[$name])) { + $mform->setDefault($name, $this->sebdata[$name] ?? $this->quiz->{$name} ?? $defaults[$name]); + } + + if (isset($types[$name])) { + $mform->setType($name, $types[$name]); + } + } + + if (settings_provider::can_change_seb_allowedbrowserexamkeys($this->context)) { + $mform->addElement('textarea', + 'seb_allowedbrowserexamkeys', + get_string('seb_allowedbrowserexamkeys', 'quizaccess_seb') + ); + + $mform->setType('seb_allowedbrowserexamkeys', PARAM_RAW); + $mform->setDefault('seb_allowedbrowserexamkeys', $this->sebdata['seb_allowedbrowserexamkeys'] ?? $this->quiz->seb_allowedbrowserexamkeys ?? ''); + $mform->addHelpButton('seb_allowedbrowserexamkeys', 'seb_allowedbrowserexamkeys', 'quizaccess_seb'); + $mform->disabledIf('seb_allowedbrowserexamkeys', 'enableseboverride'); + } + + // Hideifs. + foreach (settings_provider::get_quiz_hideifs() as $elname => $rules) { + if ($mform->elementExists($elname)) { + foreach ($rules as $hideif) { + $mform->hideIf( + $hideif->get_element(), + $hideif->get_dependantname(), + $hideif->get_condition(), + $hideif->get_dependantvalue() + ); + } + } + } + + // Lock elements. + if (settings_provider::is_conflicting_permissions($this->context)) { + // Freeze common quiz settings. + $mform->addElement('enableseboverride'); + $mform->freeze('seb_requiresafeexambrowser'); + $mform->freeze('seb_templateid'); + $mform->freeze('seb_showsebdownloadlink'); + $mform->freeze('seb_allowedbrowserexamkeys'); + + $quizsettings = seb_quiz_settings::get_by_quiz_id((int) $this->quiz->id); + + // Remove template ID if not using template for this quiz. + if (empty($quizsettings) || $quizsettings->get('requiresafeexambrowser') != settings_provider::USE_SEB_TEMPLATE) { + $mform->removeElement('seb_templateid'); + } + + // Freeze all SEB specific settings. + foreach (settings_provider::get_seb_config_elements() as $element => $type) { + if ($mform->elementExists($element)) { + $mform->freeze($element); + } + } + } + + // Close header before next field. + $mform->closeHeaderBefore('resetbutton'); + } + /** * Get a user's name and identity ready to display. * diff --git a/mod/quiz/classes/local/override_manager.php b/mod/quiz/classes/local/override_manager.php index a79c739b1a56f..70794ae731f2c 100644 --- a/mod/quiz/classes/local/override_manager.php +++ b/mod/quiz/classes/local/override_manager.php @@ -31,8 +31,43 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class override_manager { + /** @var array quiz SEB setting keys that can be overwritten **/ + private const OVERRIDEABLE_QUIZ_SEB_SETTINGS = [ + 'enableseboverride', + 'seb_activateurlfiltering', + 'seb_allowedbrowserexamkeys', + 'seb_allowreloadinexam', + 'seb_allowspellchecking', + 'seb_allowuserquitseb', + 'seb_enableaudiocontrol', + 'seb_expressionsallowed', + 'seb_expressionsblocked', + 'seb_filterembeddedcontent', + 'seb_linkquitseb', + 'seb_muteonstartup', + 'seb_quitpassword', + 'seb_regexallowed', + 'seb_regexblocked', + 'seb_requiresafeexambrowser', + 'seb_showkeyboardlayout', + 'seb_showreloadbutton', + 'seb_showsebdownloadlink', + 'seb_showsebtaskbar', + 'seb_showtime', + 'seb_showwificontrol', + 'seb_templateid', + 'seb_userconfirmquit', + ]; + /** @var array quiz setting keys that can be overwritten **/ - private const OVERRIDEABLE_QUIZ_SETTINGS = ['timeopen', 'timeclose', 'timelimit', 'attempts', 'password']; + private const OVERRIDEABLE_QUIZ_SETTINGS = [ + 'timeopen', + 'timeclose', + 'timelimit', + 'attempts', + 'password', + ...self::OVERRIDEABLE_QUIZ_SEB_SETTINGS + ]; /** * Create override manager @@ -88,10 +123,12 @@ public function validate_data(array $formdata): array { // Ensure at least one of the overrideable settings is set. $keysthatareset = array_map(function ($key) use ($formdata) { return isset($formdata->$key) && !is_null($formdata->$key); - }, self::OVERRIDEABLE_QUIZ_SETTINGS); + }, array_diff(self::OVERRIDEABLE_QUIZ_SETTINGS, self::OVERRIDEABLE_QUIZ_SEB_SETTINGS)); if (!in_array(true, $keysthatareset)) { - $errors['general'][] = new \lang_string('nooverridedata', 'quiz'); + if (!(isset($formdata->enableseboverride) && !is_null($formdata->enableseboverride))) { + $errors['general'][] = new \lang_string('nooverridedata', 'quiz'); + } } // Ensure quiz is a valid quiz. @@ -238,6 +275,12 @@ public function save_override(array $formdata): int { // Extract only the necessary data. $datatoset = $this->parse_formdata($formdata); + + // Create sebdata field value. + $sebdata = array_intersect_key($datatoset, array_flip(self::OVERRIDEABLE_QUIZ_SEB_SETTINGS)); + $sebdata = serialize($sebdata); + + $datatoset['sebdata'] = $sebdata; $datatoset['quiz'] = $this->quiz->id; // Validate the data is OK. @@ -556,12 +599,36 @@ private function clear_unused_values(array $formdata): array { // If the formdata is empty, set it to null. // This avoids putting 0, false, or '' into the DB since the override logic expects null. - // Attempts is the exception, it can have a integer value of '0', so we use is_numeric instead. - if ($key != 'attempts' && empty($formdata[$key])) { + // Attempts and some SEB options are the exception, it can have a integer value of '0', so we use is_numeric instead. + $valsupposedtobenumeric = in_array( + $key, + [ + 'attempts', + 'enableseboverride', + 'seb_activateurlfiltering', + 'seb_allowedbrowserexamkeys', + 'seb_allowreloadinexam', + 'seb_allowspellchecking', + 'seb_allowuserquitseb', + 'seb_enableaudiocontrol', + 'seb_muteonstartup', + 'seb_requiresafeexambrowser', + 'seb_showkeyboardlayout', + 'seb_showreloadbutton', + 'seb_showsebdownloadlink', + 'seb_showsebtaskbar', + 'seb_showtime', + 'seb_showwificontrol', + 'seb_userconfirmquit', + ] + ); + + if (!$valsupposedtobenumeric && empty($formdata[$key])) { $formdata[$key] = null; } - if ($key == 'attempts' && !is_numeric($formdata[$key])) { + // If it is attempts and it isn't 0, 1, 2, 3 etc. + if ($valsupposedtobenumeric && !is_numeric($formdata[$key])) { $formdata[$key] = null; } } diff --git a/mod/quiz/db/install.xml b/mod/quiz/db/install.xml index 8a7d8ee9f304f..8420cf3d6c5bc 100644 --- a/mod/quiz/db/install.xml +++ b/mod/quiz/db/install.xml @@ -132,6 +132,7 @@ + diff --git a/mod/quiz/db/upgrade.php b/mod/quiz/db/upgrade.php index 4748c3ad20011..feafe30e900b5 100644 --- a/mod/quiz/db/upgrade.php +++ b/mod/quiz/db/upgrade.php @@ -132,6 +132,21 @@ function xmldb_quiz_upgrade($oldversion) { upgrade_mod_savepoint(true, 2023112402, 'quiz'); } + if ($oldversion < 2024072400) { + + // Define field sebdata to be added to quiz_overrides. + $table = new xmldb_table('quiz_overrides'); + $field = new xmldb_field('sebdata', XMLDB_TYPE_TEXT, null, null, null, null, null, 'password'); + + // Conditionally launch add field quizgradeitemid. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Quiz savepoint reached. + upgrade_mod_savepoint(true, 2024072400, 'quiz'); + } + // Automatically generated Moodle v4.4.0 release upgrade line. // Put any upgrade step following this. diff --git a/mod/quiz/lib.php b/mod/quiz/lib.php index 375f3cc9e40b4..efab7764ae6a4 100644 --- a/mod/quiz/lib.php +++ b/mod/quiz/lib.php @@ -219,6 +219,46 @@ function quiz_delete_instance($id) { return true; } +/** + * Checks if the user has overrides for the quiz whether individually or in a group. + * + * @param int $quizid The quiz object. + * @return bool + */ +function quiz_has_user_overrides($quizid) { + global $DB, $USER; + $userid = $USER->id; + + $quiz = $DB->get_record('quiz', ['id' => $quizid]); + + // No quiz, no override. + if (!$quiz) { + return false; + } + + // Check for user override. + $useroverride = $DB->record_exists('quiz_overrides', ['quiz' => $quiz->id, 'userid' => $userid]); + if ($useroverride) { + return true; + } + + // Check for group overrides. + $groupings = groups_get_user_groups($quiz->course, $userid); + if (!empty($groupings[0])) { + list($extra, $params) = $DB->get_in_or_equal(array_values($groupings[0])); + $sql = "SELECT * FROM {quiz_overrides} + WHERE groupid $extra AND quiz = ?"; + $params[] = $quiz->id; + $gpoverrides = $DB->record_exists_sql($sql, $params); + + if ($gpoverrides) { + return true; + } + } + + return false; +} + /** * Updates a quiz object with override information for a user. * @@ -326,6 +366,14 @@ function quiz_update_effective_access($quiz, $userid) { } } + // Merge SEB override settings if available. + $seboverride = isset($override->sebdata) ? unserialize($override->sebdata) : null; + if (!empty($seboverride) && !!$seboverride['enableseboverride']) { + foreach ($seboverride as $key => $value) { + $quiz->{$key} = $value; + } + } + return $quiz; } diff --git a/mod/quiz/overrideedit.php b/mod/quiz/overrideedit.php index 878e263770562..a9f7379d5dc81 100644 --- a/mod/quiz/overrideedit.php +++ b/mod/quiz/overrideedit.php @@ -72,6 +72,15 @@ // Editing an override. $data = clone $override; + // Unpack SEB settings into data object. + $data->sebdata = unserialize($data->sebdata); + if ($data->sebdata->enableseboverride) { + foreach($data->sebdata as $sebkey => $sebval) { + $data->{$sebkey} = $sebval; + } + } + unset($data->sebdata); + if ($override->groupid) { if (!groups_group_visible($override->groupid, $course, $cm)) { throw new \moodle_exception('invalidoverrideid', 'quiz'); diff --git a/mod/quiz/overrides.php b/mod/quiz/overrides.php index be467be62435f..6392d72286e30 100644 --- a/mod/quiz/overrides.php +++ b/mod/quiz/overrides.php @@ -23,6 +23,7 @@ */ use mod_quiz\quiz_settings; +use quizaccess_seb\settings_provider; require_once(__DIR__ . '/../../config.php'); require_once($CFG->dirroot.'/mod/quiz/lib.php'); @@ -229,6 +230,15 @@ get_string('enabled', 'quiz') : get_string('none', 'quiz'); } + // Safe exam browser. + if (isset($override->sebdata)) { + $sebdata = unserialize($override->sebdata); + if (!!$sebdata['enableseboverride']) { + $fields[] = get_string('seb_requiresafeexambrowser', 'quizaccess_seb'); + $values[] = settings_provider::get_requiresafeexambrowser_options($context)[$sebdata['seb_requiresafeexambrowser']]; + } + } + // Prepare the information about who this override applies to. $extranamebit = $active ? '' : '*'; $usercells = []; diff --git a/mod/quiz/version.php b/mod/quiz/version.php index fa22a93ba4087..8d648feba1090 100644 --- a/mod/quiz/version.php +++ b/mod/quiz/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2024051700; +$plugin->version = 2024072400; $plugin->requires = 2024041600; $plugin->component = 'mod_quiz';