Skip to content

Commit 5ec1484

Browse files
authored
Merge pull request #1209 from michaelkotlyar/quizaccess-override
Add "apis/plugintypes/quizaccess" page.
2 parents 267d6a0 + d0ca867 commit 5ec1484

File tree

7 files changed

+317
-1
lines changed

7 files changed

+317
-1
lines changed

data/main/components.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@
4444
"local": "local",
4545
"h5plib": "h5p\/h5plib",
4646
"paygw": "payment\/gateway",
47-
"smsgateway": "sms/gateway"
47+
"smsgateway": "sms/gateway",
48+
"quizaccessrule": "mod\/quiz\/accessrule"
4849
},
4950
"subsystems": {
5051
"ai": "ai",
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<!-- markdownlint-disable first-line-heading -->
2+
The rule class defines the behaviour of your rule and how it will restrict access for users attempting a quiz.
3+
4+
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.
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
use mod_quiz\form\edit_override_form;
2+
use mod_quiz\form\preflight_check_form;
3+
use mod_quiz\quiz_settings;
4+
use mod_quiz\local\access_rule_base;
5+
use mod_quiz_mod_form;
6+
use MoodleQuickForm;
7+
8+
class quizaccess_pluginname extends access_rule_base {
9+
10+
/**
11+
* Below are methods inherited from mod_quiz\local\access_rule_base. All of these functions,
12+
* are optional to rewrite - although it depends on the behaviour of your rule which will
13+
* determine which functions should be reimplemented.
14+
*/
15+
16+
public function __construct($quizobj, $timenow) {
17+
$this->quizobj = $quizobj;
18+
$this->quiz = $quizobj->get_quiz();
19+
$this->timenow = $timenow;
20+
}
21+
22+
public static function make(quiz_settings $quizobj, $timenow, $canignoretimelimits) {
23+
return null;
24+
}
25+
26+
public function prevent_new_attempt($numprevattempts, $lastattempt) {
27+
return false;
28+
}
29+
30+
public function prevent_access() {
31+
return false;
32+
}
33+
34+
public function is_preflight_check_required($attemptid) {
35+
return false;
36+
}
37+
38+
public function add_preflight_check_form_fields(preflight_check_form $quizform,
39+
MoodleQuickForm $mform, $attemptid) {
40+
// Do nothing by default.
41+
}
42+
43+
public function validate_preflight_check($data, $files, $errors, $attemptid) {
44+
return $errors;
45+
}
46+
47+
public function notify_preflight_check_passed($attemptid) {
48+
// Do nothing by default.
49+
}
50+
51+
public function current_attempt_finished() {
52+
// Do nothing by default.
53+
}
54+
55+
public function description() {
56+
return '';
57+
}
58+
59+
public function is_finished($numprevattempts, $lastattempt) {
60+
return false;
61+
}
62+
63+
public function end_time($attempt) {
64+
return false;
65+
}
66+
67+
public function time_left_display($attempt, $timenow) {
68+
$endtime = $this->end_time($attempt);
69+
if ($endtime === false) {
70+
return false;
71+
}
72+
return $endtime - $timenow;
73+
}
74+
75+
public function attempt_must_be_in_popup() {
76+
return false;
77+
}
78+
79+
public function get_popup_options() {
80+
return [];
81+
}
82+
83+
public function setup_attempt_page($page) {
84+
// Do nothing by default.
85+
}
86+
87+
public function get_superceded_rules() {
88+
return [];
89+
}
90+
91+
public static function add_settings_form_fields(
92+
mod_quiz_mod_form $quizform, MoodleQuickForm $mform) {
93+
// By default do nothing.
94+
}
95+
96+
public static function validate_settings_form_fields(array $errors,
97+
array $data, $files, mod_quiz_mod_form $quizform) {
98+
return $errors;
99+
}
100+
101+
public static function get_browser_security_choices() {
102+
return [];
103+
}
104+
105+
public static function save_settings($quiz) {
106+
// By default do nothing.
107+
}
108+
109+
public static function delete_settings($quiz) {
110+
// By default do nothing.
111+
}
112+
113+
public static function get_settings_sql($quizid) {
114+
return ['', '', []];
115+
}
116+
117+
public static function get_extra_settings($quizid) {
118+
return [];
119+
}
120+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<!-- markdownlint-disable first-line-heading -->
2+
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.
3+
4+
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.
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
use mod_quiz\form\edit_override_form;
2+
use mod_quiz\local\access_rule_base;
3+
use mod_quiz\local\rule_overridable;
4+
use MoodleQuickForm;
5+
6+
class quizaccess_pluginname extends access_rule_base implements rule_overridable {
7+
8+
/**
9+
* All of the below rule_overridable interface functions will need to be implemented.
10+
*/
11+
12+
public static function add_override_form_fields(edit_override_form $quizform, MoodleQuickForm $mform): void {
13+
// Use the $mform to add the rule override fields...
14+
$mform->addElement(
15+
'select',
16+
'plgn_setting1',
17+
get_string('plgn_setting1', 'quizaccess_pluginname'),
18+
['A', 'B', 'C'],
19+
);
20+
21+
$mform->addElement(
22+
'select',
23+
'plgn_setting2',
24+
get_string('plgn_setting2', 'quizaccess_pluginname'),
25+
['1', '2', '3'],
26+
);
27+
}
28+
29+
public static function get_override_form_section_header(): array {
30+
// Return the label and content of the section header in an array.
31+
return ['name' => 'pluginname', 'title' => get_string('pluginname', 'quizaccess_pluginname')];
32+
}
33+
34+
public static function get_override_form_section_expand(edit_override_form $quizform): bool {
35+
// Determine if rule section in override form should load expanded.
36+
// Should typically return true if the quiz has existing rule settings.
37+
global $DB;
38+
return $DB->record_exists('quizaccess_pluginname', ['quiz' => $quizform->get_quiz()->id]);
39+
}
40+
41+
public static function validate_override_form_fields(array $errors,
42+
array $data, array $files, edit_override_form $quizform): array {
43+
// Check and push to $errors array...
44+
return $errors;
45+
}
46+
47+
public static function save_override_settings(array $override): void {
48+
// Save $override data to plugin settings table...
49+
global $DB;
50+
51+
$plgnoverride = (object)[
52+
'overrideid' => $override['overrideid'],
53+
'setting1' => $override['plgnm_setting1'],
54+
'setting2' => $override['plgnm_setting2'],
55+
];
56+
57+
if ($plgnoverrideid = $DB->get_field('quizaccess_pluginname_overrides', 'id', ['overrideid' => $override['overrideid']])) {
58+
$plgnoverride->id = $plgnoverrideid;
59+
$DB->update_record('quizaccess_pluginname_overrides', $plgnoverride);
60+
} else {
61+
$DB->insert_record('quizaccess_pluginname_overrides', $plgnoverride);
62+
}
63+
}
64+
65+
public static function delete_override_settings($quizid, $overrides): void {
66+
// Remove $overrides from $quiz.
67+
global $DB;
68+
$ids = array_column($overrides, 'id');
69+
list($insql, $inparams) = $DB->get_in_or_equal($ids);
70+
$DB->delete_records_select('quizaccess_pluginname_overrides', "id $insql", $inparams);
71+
}
72+
73+
public static function get_override_setting_keys(): array {
74+
// Return string array of all override form setting keys.
75+
return ['plgnm_setting1', 'plgnm_setting2'];
76+
}
77+
78+
public static function get_override_required_setting_keys(): array {
79+
// Return string array of override form setting keys that are required.
80+
return ['plgnm_setting1'];
81+
}
82+
83+
public static function get_override_settings_sql($overridetablename): array {
84+
// Return an array of selects, joins and parameters to be used to query relevant rule overrides...
85+
return [
86+
"plgnm.setting1 plgnm_setting1, plgnm.setting2 plgnm_setting2",
87+
"LEFT JOIN {quizaccess_pluginname_overrides} plgnm ON plgnm.overrideid = {$overridetablename}.id",
88+
[],
89+
];
90+
}
91+
92+
public static function add_override_table_fields($override, $fields, $values, $context): array {
93+
// Extend the override table view by adding fields and values that display the rule's overrides.
94+
if (!empty($override->plgnm_setting1)) {
95+
$fields[] = get_string('pluginname', 'quizaccess_pluginname');
96+
$values[] = "{$override->plgnm_setting1}, {$override->plgnm_setting2}";
97+
}
98+
return [$fields, $values];
99+
}
100+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
---
2+
title: Quiz access rule sub-plugins
3+
tags:
4+
- Quiz
5+
- Access
6+
- Rule
7+
- Subplugin
8+
- Plugintype
9+
- Override
10+
---
11+
12+
import { ComponentFileSummary } from '../../../_utils';
13+
14+
Quiz access rule sub-plugins extend the ability to add conditions a user must meet to attempt a given quiz.
15+
16+
The following rules are readily available as part of Moodle core:
17+
18+
- `quizaccess_delaybetweenattempts`
19+
- `quizaccess_ipaddress`
20+
- `quizaccess_numattempts`
21+
- `quizaccess_offlineattempts`
22+
- `quizaccess_openclosedate`
23+
- `quizaccess_password`
24+
- `quizaccess_seb`
25+
- `quizaccess_securewindow`
26+
- `quizaccess_timelimit`
27+
28+
## File structure
29+
30+
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.
31+
32+
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.
33+
34+
<details>
35+
<summary>View an example directory layout for the `quizaccess_delaybetweenattempts` plugin.</summary>
36+
37+
```console
38+
mod/quiz/accessrule/delaybetweenattempts
39+
├── classes
40+
│   └── privacy
41+
│   └── provider.php
42+
├── lang
43+
│   └── en
44+
│   └── quizaccess_delaybetweenattempts.php
45+
├── tests
46+
│   └── rule_test.php
47+
├── rule.php
48+
└── version.php
49+
```
50+
51+
</details>
52+
53+
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.
54+
55+
### rule.php
56+
57+
import RuleFile from '!!raw-loader!./_examples/rule.php';
58+
import RuleDescription from './_examples/rule.md';
59+
60+
<ComponentFileSummary
61+
required
62+
filepath="/rule.php"
63+
summary="Rule definition class"
64+
plugintype="quizaccessrule"
65+
pluginname="pluginname"
66+
example={RuleFile}
67+
description={RuleDescription}
68+
/>
69+
70+
import RuleOverridableFile from '!!raw-loader!./_examples/rule_overridable.php';
71+
import RuleOverridableDescription from './_examples/rule_overridable.md';
72+
73+
<ComponentFileSummary
74+
filepath="/rule.php"
75+
summary="Rule definition class with override"
76+
plugintype="quizaccessrule"
77+
pluginname="pluginname"
78+
example={RuleOverridableFile}
79+
description={RuleOverridableDescription}
80+
/>
81+
82+
:::info
83+
84+
Implementing `rule_overridable` is not required but can enhance the usability of the rule.
85+
86+
:::

project-words.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ privatefiles
264264
protectusernames
265265
qeupgradehelper
266266
quizaccess
267+
quizaccessrule
267268
randomsamatch
268269
recordset
269270
recordsets

0 commit comments

Comments
 (0)