Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "apis/plugintypes/quizaccess" page. #1209

Merged
merged 1 commit into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion data/main/components.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 4 additions & 0 deletions docs/apis/plugintypes/quizaccess/_examples/rule.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<!-- markdownlint-disable first-line-heading -->
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.
120 changes: 120 additions & 0 deletions docs/apis/plugintypes/quizaccess/_examples/rule.php
Original file line number Diff line number Diff line change
@@ -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 [];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<!-- markdownlint-disable first-line-heading -->
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.
100 changes: 100 additions & 0 deletions docs/apis/plugintypes/quizaccess/_examples/rule_overridable.php
Original file line number Diff line number Diff line change
@@ -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];
}
}
86 changes: 86 additions & 0 deletions docs/apis/plugintypes/quizaccess/index.md
Original file line number Diff line number Diff line change
@@ -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.

<details>
<summary>View an example directory layout for the `quizaccess_delaybetweenattempts` plugin.</summary>

```console
mod/quiz/accessrule/delaybetweenattempts
├── classes
│   └── privacy
│   └── provider.php
├── lang
│   └── en
│   └── quizaccess_delaybetweenattempts.php
├── tests
│   └── rule_test.php
├── rule.php
└── version.php
```

</details>

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';

<ComponentFileSummary
required
filepath="/rule.php"
summary="Rule definition class"
plugintype="quizaccessrule"
pluginname="pluginname"
example={RuleFile}
description={RuleDescription}
/>

import RuleOverridableFile from '!!raw-loader!./_examples/rule_overridable.php';
import RuleOverridableDescription from './_examples/rule_overridable.md';

<ComponentFileSummary
filepath="/rule.php"
summary="Rule definition class with override"
plugintype="quizaccessrule"
pluginname="pluginname"
example={RuleOverridableFile}
description={RuleOverridableDescription}
/>

:::info

Implementing `rule_overridable` is not required but can enhance the usability of the rule.

:::
1 change: 1 addition & 0 deletions project-words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ privatefiles
protectusernames
qeupgradehelper
quizaccess
quizaccessrule
randomsamatch
recordset
recordsets
Expand Down
Loading