diff --git a/data/main/components.json b/data/main/components.json index c11288c46..eff9d4bd3 100644 --- a/data/main/components.json +++ b/data/main/components.json @@ -44,7 +44,8 @@ "local": "local", "h5plib": "h5p\/h5plib", "paygw": "payment\/gateway", - "smsgateway": "sms/gateway" + "smsgateway": "sms/gateway", + "quizaccessrule": "mod\/quiz\/accessrule" }, "subsystems": { "ai": "ai", diff --git a/docs/apis/plugintypes/quizaccess/_examples/rule.md b/docs/apis/plugintypes/quizaccess/_examples/rule.md new file mode 100644 index 000000000..f8be96b09 --- /dev/null +++ b/docs/apis/plugintypes/quizaccess/_examples/rule.md @@ -0,0 +1,4 @@ + +The rule class defines the behaviour of your rule and how it will restrict access for users attempting a quiz. + +Please refer to the inline phpdocs of the [mod_quiz::access_rule_base class](https://github.com/moodle/moodle/blob/main/mod/quiz/classes/local/access_rule_base.php) for detailed descriptions of the functions and meaning. diff --git a/docs/apis/plugintypes/quizaccess/_examples/rule.php b/docs/apis/plugintypes/quizaccess/_examples/rule.php new file mode 100644 index 000000000..3732aac8f --- /dev/null +++ b/docs/apis/plugintypes/quizaccess/_examples/rule.php @@ -0,0 +1,120 @@ +use mod_quiz\form\edit_override_form; +use mod_quiz\form\preflight_check_form; +use mod_quiz\quiz_settings; +use mod_quiz\local\access_rule_base; +use mod_quiz_mod_form; +use MoodleQuickForm; + +class quizaccess_pluginname extends access_rule_base { + + /** + * Below are methods inherited from mod_quiz\local\access_rule_base. All of these functions, + * are optional to rewrite - although it depends on the behaviour of your rule which will + * determine which functions should be reimplemented. + */ + + public function __construct($quizobj, $timenow) { + $this->quizobj = $quizobj; + $this->quiz = $quizobj->get_quiz(); + $this->timenow = $timenow; + } + + public static function make(quiz_settings $quizobj, $timenow, $canignoretimelimits) { + return null; + } + + public function prevent_new_attempt($numprevattempts, $lastattempt) { + return false; + } + + public function prevent_access() { + return false; + } + + public function is_preflight_check_required($attemptid) { + return false; + } + + public function add_preflight_check_form_fields(preflight_check_form $quizform, + MoodleQuickForm $mform, $attemptid) { + // Do nothing by default. + } + + public function validate_preflight_check($data, $files, $errors, $attemptid) { + return $errors; + } + + public function notify_preflight_check_passed($attemptid) { + // Do nothing by default. + } + + public function current_attempt_finished() { + // Do nothing by default. + } + + public function description() { + return ''; + } + + public function is_finished($numprevattempts, $lastattempt) { + return false; + } + + public function end_time($attempt) { + return false; + } + + public function time_left_display($attempt, $timenow) { + $endtime = $this->end_time($attempt); + if ($endtime === false) { + return false; + } + return $endtime - $timenow; + } + + public function attempt_must_be_in_popup() { + return false; + } + + public function get_popup_options() { + return []; + } + + public function setup_attempt_page($page) { + // Do nothing by default. + } + + public function get_superceded_rules() { + return []; + } + + public static function add_settings_form_fields( + mod_quiz_mod_form $quizform, MoodleQuickForm $mform) { + // By default do nothing. + } + + public static function validate_settings_form_fields(array $errors, + array $data, $files, mod_quiz_mod_form $quizform) { + return $errors; + } + + public static function get_browser_security_choices() { + return []; + } + + public static function save_settings($quiz) { + // By default do nothing. + } + + public static function delete_settings($quiz) { + // By default do nothing. + } + + public static function get_settings_sql($quizid) { + return ['', '', []]; + } + + public static function get_extra_settings($quizid) { + return []; + } +} diff --git a/docs/apis/plugintypes/quizaccess/_examples/rule_overridable.md b/docs/apis/plugintypes/quizaccess/_examples/rule_overridable.md new file mode 100644 index 000000000..51d8c2b7f --- /dev/null +++ b/docs/apis/plugintypes/quizaccess/_examples/rule_overridable.md @@ -0,0 +1,4 @@ + +Most quiz settings can be overridden on a per user and/or group level and you can extend this ability to your rule as well. To make your rule overridable, you must implement the `rule_overridable` interface in your rule class definition. + +Please refer to the inline phpdocs of the [mod_quiz::rule_overridable interface](https://github.com/moodle/moodle/blob/main/mod/quiz/classes/local/rule_overridable.php) for detailed descriptions of the functions and meaning. diff --git a/docs/apis/plugintypes/quizaccess/_examples/rule_overridable.php b/docs/apis/plugintypes/quizaccess/_examples/rule_overridable.php new file mode 100644 index 000000000..58c57ed5d --- /dev/null +++ b/docs/apis/plugintypes/quizaccess/_examples/rule_overridable.php @@ -0,0 +1,100 @@ +use mod_quiz\form\edit_override_form; +use mod_quiz\local\access_rule_base; +use mod_quiz\local\rule_overridable; +use MoodleQuickForm; + +class quizaccess_pluginname extends access_rule_base implements rule_overridable { + + /** + * All of the below rule_overridable interface functions will need to be implemented. + */ + + public static function add_override_form_fields(edit_override_form $quizform, MoodleQuickForm $mform): void { + // Use the $mform to add the rule override fields... + $mform->addElement( + 'select', + 'plgn_setting1', + get_string('plgn_setting1', 'quizaccess_pluginname'), + ['A', 'B', 'C'], + ); + + $mform->addElement( + 'select', + 'plgn_setting2', + get_string('plgn_setting2', 'quizaccess_pluginname'), + ['1', '2', '3'], + ); + } + + public static function get_override_form_section_header(): array { + // Return the label and content of the section header in an array. + return ['name' => 'pluginname', 'title' => get_string('pluginname', 'quizaccess_pluginname')]; + } + + public static function get_override_form_section_expand(edit_override_form $quizform): bool { + // Determine if rule section in override form should load expanded. + // Should typically return true if the quiz has existing rule settings. + global $DB; + return $DB->record_exists('quizaccess_pluginname', ['quiz' => $quizform->get_quiz()->id]); + } + + public static function validate_override_form_fields(array $errors, + array $data, array $files, edit_override_form $quizform): array { + // Check and push to $errors array... + return $errors; + } + + public static function save_override_settings(array $override): void { + // Save $override data to plugin settings table... + global $DB; + + $plgnoverride = (object)[ + 'overrideid' => $override['overrideid'], + 'setting1' => $override['plgnm_setting1'], + 'setting2' => $override['plgnm_setting2'], + ]; + + if ($plgnoverrideid = $DB->get_field('quizaccess_pluginname_overrides', 'id', ['overrideid' => $override['overrideid']])) { + $plgnoverride->id = $plgnoverrideid; + $DB->update_record('quizaccess_pluginname_overrides', $plgnoverride); + } else { + $DB->insert_record('quizaccess_pluginname_overrides', $plgnoverride); + } + } + + public static function delete_override_settings($quizid, $overrides): void { + // Remove $overrides from $quiz. + global $DB; + $ids = array_column($overrides, 'id'); + list($insql, $inparams) = $DB->get_in_or_equal($ids); + $DB->delete_records_select('quizaccess_pluginname_overrides', "id $insql", $inparams); + } + + public static function get_override_setting_keys(): array { + // Return string array of all override form setting keys. + return ['plgnm_setting1', 'plgnm_setting2']; + } + + public static function get_override_required_setting_keys(): array { + // Return string array of override form setting keys that are required. + return ['plgnm_setting1']; + } + + public static function get_override_settings_sql($overridetablename): array { + // Return an array of selects, joins and parameters to be used to query relevant rule overrides... + return [ + "plgnm.setting1 plgnm_setting1, plgnm.setting2 plgnm_setting2", + "LEFT JOIN {quizaccess_pluginname_overrides} plgnm ON plgnm.overrideid = {$overridetablename}.id", + [], + ]; + } + + public static function add_override_table_fields($override, $fields, $values, $context): array { + // Extend the override table view by adding fields and values that display the rule's overrides. + if (!empty($override->plgnm_setting1)) { + $fields[] = get_string('pluginname', 'quizaccess_pluginname'); + $values[] = "{$override->plgnm_setting1}, {$override->plgnm_setting2}"; + } + return [$fields, $values]; + } +} diff --git a/docs/apis/plugintypes/quizaccess/index.md b/docs/apis/plugintypes/quizaccess/index.md new file mode 100644 index 000000000..8c9b33f1b --- /dev/null +++ b/docs/apis/plugintypes/quizaccess/index.md @@ -0,0 +1,86 @@ +--- +title: Quiz access rule sub-plugins +tags: + - Quiz + - Access + - Rule + - Subplugin + - Plugintype + - Override +--- + +import { ComponentFileSummary } from '../../../_utils'; + +Quiz access rule sub-plugins extend the ability to add conditions a user must meet to attempt a given quiz. + +The following rules are readily available as part of Moodle core: + +- `quizaccess_delaybetweenattempts` +- `quizaccess_ipaddress` +- `quizaccess_numattempts` +- `quizaccess_offlineattempts` +- `quizaccess_openclosedate` +- `quizaccess_password` +- `quizaccess_seb` +- `quizaccess_securewindow` +- `quizaccess_timelimit` + +## File structure + +Quiz access rule sub-plugins are located in the `/mod/quiz/accessrule` directory. A plugin should not include any custom files outside of it's own plugin folder. + +Each plugin is in a separate subdirectory and consists of a number of mandatory files and any other files the developer is going to use. + +
+ View an example directory layout for the `quizaccess_delaybetweenattempts` plugin. + +```console +mod/quiz/accessrule/delaybetweenattempts +├── classes +│   └── privacy +│   └── provider.php +├── lang +│   └── en +│   └── quizaccess_delaybetweenattempts.php +├── tests +│   └── rule_test.php +├── rule.php +└── version.php +``` + +
+ +Some of the important files for the format plugintype are described below. See the [common plugin files](../commonfiles) documentation for details of other files which may be useful in your plugin. + +### rule.php + +import RuleFile from '!!raw-loader!./_examples/rule.php'; +import RuleDescription from './_examples/rule.md'; + + + +import RuleOverridableFile from '!!raw-loader!./_examples/rule_overridable.php'; +import RuleOverridableDescription from './_examples/rule_overridable.md'; + + + +:::info + +Implementing `rule_overridable` is not required but can enhance the usability of the rule. + +::: diff --git a/project-words.txt b/project-words.txt index 2516b1690..3732fdf7d 100644 --- a/project-words.txt +++ b/project-words.txt @@ -264,6 +264,7 @@ privatefiles protectusernames qeupgradehelper quizaccess +quizaccessrule randomsamatch recordset recordsets