From 14228164bc8fa523eb3d4c45ce45f8fdf363fa53 Mon Sep 17 00:00:00 2001 From: Michael Kotlyar Date: Tue, 3 Sep 2024 15:04:38 +0100 Subject: [PATCH] MDL-76665 quizaccess_seb: Allow disable of SEB templates when in use. --- ...restore_quizaccess_seb_subplugin.class.php | 17 +++++- .../seb/classes/local/form/template.php | 9 ++- .../seb/classes/settings_provider.php | 23 ++++--- .../seb/tests/backup_restore_test.php | 42 +++++++++++++ .../seb/tests/behat/edit_form.feature | 32 ++++++++++ .../accessrule/seb/tests/template_test.php | 61 +++++++++++++++++++ 6 files changed, 170 insertions(+), 14 deletions(-) diff --git a/mod/quiz/accessrule/seb/backup/moodle2/restore_quizaccess_seb_subplugin.class.php b/mod/quiz/accessrule/seb/backup/moodle2/restore_quizaccess_seb_subplugin.class.php index 1cbdd677f0089..929c0043107b2 100644 --- a/mod/quiz/accessrule/seb/backup/moodle2/restore_quizaccess_seb_subplugin.class.php +++ b/mod/quiz/accessrule/seb/backup/moodle2/restore_quizaccess_seb_subplugin.class.php @@ -25,6 +25,8 @@ */ use quizaccess_seb\seb_quiz_settings; +use quizaccess_seb\settings_provider; +use quizaccess_seb\template; defined('MOODLE_INTERNAL') || die(); @@ -73,7 +75,15 @@ public function process_quizaccess_seb_quizsettings($data) { unset($data->id); $data->timecreated = $data->timemodified = time(); $data->usermodified = $USER->id; - $DB->insert_record(quizaccess_seb\seb_quiz_settings::TABLE, $data); + + // Do not use template if it is no longer enabled. + if ($data->requiresafeexambrowser == settings_provider::USE_SEB_TEMPLATE && + !$DB->record_exists(template::TABLE, ['id' => $data->templateid, 'enabled' => '1'])) { + $data->templateid = 0; + $data->requiresafeexambrowser = settings_provider::USE_SEB_NO; + } + + $DB->insert_record(seb_quiz_settings::TABLE, $data); // Process attached files. $this->add_related_files('quizaccess_seb', 'filemanager_sebconfigfile', null); @@ -112,7 +122,10 @@ public function process_quizaccess_seb_template($data) { } // Update the restored quiz settings to use restored template. - $DB->set_field(\quizaccess_seb\seb_quiz_settings::TABLE, 'templateid', $template->get('id'), ['quizid' => $quizid]); + // Check if template is enabled before using it. + if ($template->get('enabled')) { + $DB->set_field(seb_quiz_settings::TABLE, 'templateid', $template->get('id'), ['quizid' => $quizid]); + } } } diff --git a/mod/quiz/accessrule/seb/classes/local/form/template.php b/mod/quiz/accessrule/seb/classes/local/form/template.php index f1a6c8e11d291..6caa890a53917 100644 --- a/mod/quiz/accessrule/seb/classes/local/form/template.php +++ b/mod/quiz/accessrule/seb/classes/local/form/template.php @@ -62,12 +62,11 @@ protected function definition() { $mform->addElement('selectyesno', 'enabled', get_string('enabled', 'quizaccess_seb')); $mform->setType('enabled', PARAM_INT); - $this->add_action_buttons(); - - if (!empty($this->get_persistent()) && !$this->get_persistent()->can_delete()) { - $mform->hardFreezeAllVisibleExcept([]); - $mform->addElement('cancel'); + if ($this->get_persistent()->get('id')) { + $mform->hardFreezeAllVisibleExcept(['enabled']); } + + $this->add_action_buttons(); } /** diff --git a/mod/quiz/accessrule/seb/classes/settings_provider.php b/mod/quiz/accessrule/seb/classes/settings_provider.php index f7a8ffdaa10f4..63756a06388c6 100644 --- a/mod/quiz/accessrule/seb/classes/settings_provider.php +++ b/mod/quiz/accessrule/seb/classes/settings_provider.php @@ -212,12 +212,13 @@ protected static function add_seb_usage_options(\mod_quiz_mod_form $quizform, \M * @param \MoodleQuickForm $mform the wrapped MoodleQuickForm. */ protected static function add_seb_templates(\mod_quiz_mod_form $quizform, \MoodleQuickForm $mform) { - if (self::can_use_seb_template($quizform->get_context()) || self::is_conflicting_permissions($quizform->get_context())) { + $context = $quizform->get_context(); + if (self::can_use_seb_template($context) || self::is_conflicting_permissions($context)) { $element = $mform->createElement( 'select', 'seb_templateid', get_string('seb_templateid', 'quizaccess_seb'), - self::get_template_options() + self::get_template_options($context->instanceid) ); } else { $element = $mform->createElement('hidden', 'seb_templateid'); @@ -230,7 +231,7 @@ protected static function add_seb_templates(\mod_quiz_mod_form $quizform, \Moodl // In case if the user can't use templates, but the quiz is configured to use them, // we'd like to display template, but freeze it. - if (self::is_conflicting_permissions($quizform->get_context())) { + if (self::is_conflicting_permissions($context)) { self::freeze_element($quizform, $mform, 'seb_templateid'); } } @@ -566,7 +567,7 @@ public static function get_requiresafeexambrowser_options(\context $context): ar } if (self::can_use_seb_template($context) || self::is_conflicting_permissions($context)) { - if (!empty(self::get_template_options())) { + if (!empty(self::get_template_options($context->instanceid))) { $options[self::USE_SEB_TEMPLATE] = get_string('seb_use_template', 'quizaccess_seb'); } } @@ -584,10 +585,18 @@ public static function get_requiresafeexambrowser_options(\context $context): ar * Returns a list of templates. * @return array */ - protected static function get_template_options(): array { + protected static function get_template_options($cmid): array { $templates = []; - $records = template::get_records(['enabled' => 1], 'name'); - if ($records) { + $templatetable = template::TABLE; + $sebquizsettingstable = seb_quiz_settings::TABLE; + $select = "enabled = 1 + OR EXISTS ( + SELECT 1 + FROM {{$sebquizsettingstable}} + WHERE templateid = {{$templatetable}}.id + AND cmid = ? + )"; + if ($records = template::get_records_select($select, [$cmid], 'id, name')) { foreach ($records as $record) { $templates[$record->get('id')] = $record->get('name'); } diff --git a/mod/quiz/accessrule/seb/tests/backup_restore_test.php b/mod/quiz/accessrule/seb/tests/backup_restore_test.php index 3a6cb0e3db516..717c6564ec8c4 100644 --- a/mod/quiz/accessrule/seb/tests/backup_restore_test.php +++ b/mod/quiz/accessrule/seb/tests/backup_restore_test.php @@ -193,6 +193,48 @@ public function test_backup_restore_template_config(): void { $this->validate_backup_restore($newcm); } + /** + * Test backup and restore when using template when said template is disabled. + * + * @covers \quizaccess_seb\seb_quiz_settings::get_record + * @covers \restore_quizaccess_seb_subplugin::process_quizaccess_seb_quizsettings + */ + public function test_backup_restore_disabled_template_config(): void { + $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY); + + $expected = seb_quiz_settings::get_record(['quizid' => $this->quiz->id]); + $template = $this->create_template(); + $expected->set('requiresafeexambrowser', settings_provider::USE_SEB_TEMPLATE); + $expected->set('templateid', $template->get('id')); + $expected->save(); + + // Disable template. + $template->set('enabled', 0); + $template->save(); + + $this->assertEquals(1, seb_quiz_settings::count_records()); + + $newcm = $this->backup_and_restore_quiz(); + + $this->assertEquals(2, seb_quiz_settings::count_records()); + $actual = seb_quiz_settings::get_record(['quizid' => $newcm->instance]); + + // Test that the restored quiz no longer uses SEB. + $expected = seb_quiz_settings::get_record(['quizid' => $this->quiz->id]); + $this->assertEquals(0, $actual->get('templateid')); + $this->assertEquals(settings_provider::USE_SEB_NO, $actual->get('requiresafeexambrowser')); + $this->assertEquals($expected->get('showsebdownloadlink'), $actual->get('showsebdownloadlink')); + $this->assertEquals($expected->get('allowuserquitseb'), $actual->get('allowuserquitseb')); + $this->assertEquals($expected->get('quitpassword'), $actual->get('quitpassword')); + $this->assertEquals($expected->get('allowedbrowserexamkeys'), $actual->get('allowedbrowserexamkeys')); + + // Validate specific SEB config settings. + foreach (settings_provider::get_seb_config_elements() as $name => $notused) { + $name = preg_replace("/^seb_/", "", $name); + $this->assertEquals($expected->get($name), $actual->get($name)); + } + } + /** * Test backup and restore when using uploaded file. */ diff --git a/mod/quiz/accessrule/seb/tests/behat/edit_form.feature b/mod/quiz/accessrule/seb/tests/behat/edit_form.feature index dee146359ec27..cb2d225ca1717 100644 --- a/mod/quiz/accessrule/seb/tests/behat/edit_form.feature +++ b/mod/quiz/accessrule/seb/tests/behat/edit_form.feature @@ -8,6 +8,7 @@ Feature: Safe Exam Browser settings in quiz edit form And the following "activities" exist: | activity | course | section | name | | quiz | C1 | 1 | Quiz 1 | + | quiz | C1 | 1 | Quiz 2 | Scenario: Quiz setting "Require the use of Safe Exam Browser" has all types, except "Use an existing template". When I am on the "Quiz 1" "quiz activity editing" page logged in as admin @@ -232,3 +233,34 @@ Feature: Safe Exam Browser settings in quiz edit form Then I should not see "Allowed browser exam keys" Then I should not see "Safe Exam Browser config template" Then I should not see "Template 1" + + Scenario: Disable templates that are already in use and seeing their visibility in settings + Given the following "quizaccess_seb > seb templates" exist: + | name | enabled | + | Template 1 | 1 | + | Template 2 | 0 | + # Set Quiz 1 template to Template 1 + When I am on the "Quiz 1" "quiz activity editing" page logged in as admin + And I expand all fieldsets + And I set the field "Require the use of Safe Exam Browser" to "Yes – Use an existing template" + Then I should see "Safe Exam Browser config template" + And the "Safe Exam Browser config template" select box should contain "Template 1" + And the "Safe Exam Browser config template" select box should not contain "Template 2" + And I set the field "Safe Exam Browser config template" to "Template 1" + And I press "Save and return to course" + # Disable Template 1 + And I navigate to "Plugins > Activity modules > Category: Quiz > Safe Exam Browser templates" in site administration + And I click on "Edit" "link" in the "Template 1" "table_row" + And I set the field "Enabled" to "No" + And I press "Save changes" + # Check Quiz 1 is still using Template 1 + When I am on the "Quiz 1" "quiz activity editing" page logged in as admin + And I expand all fieldsets + And the field "Require the use of Safe Exam Browser" matches value "Yes – Use an existing template" + Then I should see "Template 1" + And the "Safe Exam Browser config template" select box should contain "Template 1" + And the "Safe Exam Browser config template" select box should not contain "Template 2" + # Check Quiz 3 cannot use any templates as they're all disabled + When I am on the "Quiz 2" "quiz activity editing" page logged in as admin + And I expand all fieldsets + And the "Require the use of Safe Exam Browser" select box should not contain "Yes – Use an existing template" diff --git a/mod/quiz/accessrule/seb/tests/template_test.php b/mod/quiz/accessrule/seb/tests/template_test.php index 8caab59581c97..97d59257869fb 100644 --- a/mod/quiz/accessrule/seb/tests/template_test.php +++ b/mod/quiz/accessrule/seb/tests/template_test.php @@ -16,6 +16,10 @@ namespace quizaccess_seb; +defined('MOODLE_INTERNAL') || die(); + +require_once(__DIR__ . '/test_helper_trait.php'); + /** * PHPUnit tests for template class. * @@ -25,6 +29,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class template_test extends \advanced_testcase { + use \quizaccess_seb_test_helper_trait; /** * Called before every test. @@ -140,4 +145,60 @@ public function test_cannot_delete_template_when_assigned_to_quiz(): void { $this->assertFalse($template->can_delete()); } + /** + * Test that a disabled template no longer shows up in quiz SEB settings other than quizzes already using it. + * + * @covers \quizaccess_seb\seb_quiz_settings::get_record + * @covers \quizaccess_seb\settings_provider::get_requiresafeexambrowser_options + */ + public function test_disabled_template_quiz_setting_options(): void { + // Create quiz and fetch standard SEB requirement options. + $this->setAdminUser(); + $this->course = $this->getDataGenerator()->create_course(); + + $templateoptionstr = get_string('seb_use_template', 'quizaccess_seb'); + + // Create a quiz. + $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY); + $context = \context_module::instance($this->quiz->cmid); + + // Check there is no template option (as there aren't any). + $options = settings_provider::get_requiresafeexambrowser_options($context); + $this->assertNotContainsEquals($templateoptionstr, $options); + + // Create a template. + $data = new \stdClass(); + $data->name = 'Test name'; + $data->description = 'Test description'; + $data->enabled = 1; + $data->content = file_get_contents(__DIR__ . '/fixtures/unencrypted.seb'); + $template = new template(0, $data); + $template->save(); + + // Check options now include template option. + $options = settings_provider::get_requiresafeexambrowser_options($context); + $this->assertContainsEquals($templateoptionstr, $options); + + // Set SEB setting to use template for quiz. + $settings = seb_quiz_settings::get_record(['quizid' => $this->quiz->id]); + $settings->set('templateid', $template->get('id')); + $settings->set('requiresafeexambrowser', settings_provider::USE_SEB_TEMPLATE); + $settings->save(); + + // Disable template. + $template->set('enabled', 0); + $template->save(); + + // Check option still exists on current quiz. + $options = settings_provider::get_requiresafeexambrowser_options($context); + $this->assertContainsEquals($templateoptionstr, $options); + + // Create a new quiz. + $newquiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY); + $context = \context_module::instance($newquiz->cmid); + + // Check there is no template option (as the template is now disabled). + $options = settings_provider::get_requiresafeexambrowser_options($context); + $this->assertNotContainsEquals($templateoptionstr, $options); + } }