Skip to content

Commit

Permalink
Merge pull request #1209 from michaelkotlyar/quizaccess-override
Browse files Browse the repository at this point in the history
Add "apis/plugintypes/quizaccess" page.
  • Loading branch information
sarjona authored Dec 18, 2024
2 parents 267d6a0 + d0ca867 commit 5ec1484
Show file tree
Hide file tree
Showing 7 changed files with 317 additions and 1 deletion.
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

0 comments on commit 5ec1484

Please sign in to comment.