Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.
   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  namespace mod_quiz\local;
  18  
  19  use mod_quiz\form\preflight_check_form;
  20  use mod_quiz_mod_form;
  21  use moodle_page;
  22  use MoodleQuickForm;
  23  use mod_quiz\quiz_settings;
  24  use stdClass;
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  require_once($CFG->dirroot . '/mod/quiz/locallib.php');
  29  
  30  
  31  /**
  32   * Base class for rules that restrict the ability to attempt a quiz.
  33   *
  34   * Quiz access rule plugins must sublclass this one to form their main 'rule' class.
  35   * Most of the methods are defined in a slightly unnatural way because we either
  36   * want to say that access is allowed, or explain the reason why it is block.
  37   * Therefore instead of is_access_allowed(...) we have prevent_access(...) that
  38   * return false if access is permitted, or a string explanation (which is treated
  39   * as true) if access should be blocked. Slighly unnatural, but actually the easiest
  40   * way to implement this.
  41   *
  42   * @package   mod_quiz
  43   * @copyright 2009 Tim Hunt
  44   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  45   * @since     Moodle 2.2
  46   */
  47  abstract class access_rule_base {
  48      /** @var stdClass the quiz settings. */
  49      protected $quiz;
  50      /** @var quiz_settings the quiz object. */
  51      protected $quizobj;
  52      /** @var int the time to use as 'now'. */
  53      protected $timenow;
  54  
  55      /**
  56       * Create an instance of this rule for a particular quiz.
  57       *
  58       * @param quiz_settings $quizobj information about the quiz in question.
  59       * @param int $timenow the time that should be considered as 'now'.
  60       */
  61      public function __construct($quizobj, $timenow) {
  62          $this->quizobj = $quizobj;
  63          $this->quiz = $quizobj->get_quiz();
  64          $this->timenow = $timenow;
  65      }
  66  
  67      /**
  68       * Return an appropriately configured instance of this rule, if it is applicable
  69       * to the given quiz, otherwise return null.
  70       *
  71       * @param quiz_settings $quizobj information about the quiz in question.
  72       * @param int $timenow the time that should be considered as 'now'.
  73       * @param bool $canignoretimelimits whether the current user is exempt from
  74       *      time limits by the mod/quiz:ignoretimelimits capability.
  75       * @return self|null the rule, if applicable, else null.
  76       */
  77      public static function make(quiz_settings $quizobj, $timenow, $canignoretimelimits) {
  78          return null;
  79      }
  80  
  81      /**
  82       * Whether a user should be allowed to start a new attempt at this quiz now.
  83       *
  84       * @param int $numprevattempts the number of previous attempts this user has made.
  85       * @param stdClass $lastattempt information about the user's last completed attempt.
  86       * @return string false if access should be allowed, a message explaining the
  87       *      reason if access should be prevented.
  88       */
  89      public function prevent_new_attempt($numprevattempts, $lastattempt) {
  90          return false;
  91      }
  92  
  93      /**
  94       * Whether the user should be blocked from starting a new attempt or continuing
  95       * an attempt now.
  96       * @return string false if access should be allowed, a message explaining the
  97       *      reason if access should be prevented.
  98       */
  99      public function prevent_access() {
 100          return false;
 101      }
 102  
 103      /**
 104       * Does this rule require a UI check with the user before an attempt is started?
 105       *
 106       * @param int|null $attemptid the id of the current attempt, if there is one,
 107       *      otherwise null.
 108       * @return bool whether a check is required before the user starts/continues
 109       *      their attempt.
 110       */
 111      public function is_preflight_check_required($attemptid) {
 112          return false;
 113      }
 114  
 115      /**
 116       * Add any field you want to pre-flight check form. You should only do
 117       * something here if {@see is_preflight_check_required()} returned true.
 118       *
 119       * @param preflight_check_form $quizform the form being built.
 120       * @param MoodleQuickForm $mform The wrapped MoodleQuickForm.
 121       * @param int|null $attemptid the id of the current attempt, if there is one,
 122       *      otherwise null.
 123       */
 124      public function add_preflight_check_form_fields(preflight_check_form $quizform,
 125              MoodleQuickForm $mform, $attemptid) {
 126          // Do nothing by default.
 127      }
 128  
 129      /**
 130       * Validate the pre-flight check form submission. You should only do
 131       * something here if {@see is_preflight_check_required()} returned true.
 132       *
 133       * If the form validates, the user will be allowed to continue.
 134       *
 135       * @param array $data the submitted form data.
 136       * @param array $files any files in the submission.
 137       * @param array $errors the list of validation errors that is being built up.
 138       * @param int|null $attemptid the id of the current attempt, if there is one,
 139       *      otherwise null.
 140       * @return array the update $errors array;
 141       */
 142      public function validate_preflight_check($data, $files, $errors, $attemptid) {
 143          return $errors;
 144      }
 145  
 146      /**
 147       * The pre-flight check has passed. This is a chance to record that fact in
 148       * some way.
 149       * @param int|null $attemptid the id of the current attempt, if there is one,
 150       *      otherwise null.
 151       */
 152      public function notify_preflight_check_passed($attemptid) {
 153          // Do nothing by default.
 154      }
 155  
 156      /**
 157       * This is called when the current attempt at the quiz is finished. This is
 158       * used, for example by the password rule, to clear the flag in the session.
 159       */
 160      public function current_attempt_finished() {
 161          // Do nothing by default.
 162      }
 163  
 164      /**
 165       * Return a brief summary of this rule, to show to users, if required.
 166       *
 167       * This information is show shown, for example, on the quiz view page, to explain this
 168       * restriction. There is no obligation to return anything. If it is not appropriate to
 169       * tell students about this rule, then just return ''.
 170       *
 171       * @return string a message, or array of messages, explaining the restriction
 172       *         (may be '' if no message is appropriate).
 173       */
 174      public function description() {
 175          return '';
 176      }
 177  
 178      /**
 179       * Is the current user unable to start any more attempts in future, because of this rule?
 180       *
 181       * If this rule can determine that this user will never be allowed another attempt at
 182       * this quiz, for example because the last possible start time is past, or all attempts
 183       * have been used up, then return true. This is used to know whether to display a
 184       * final grade on the view page. This will only be called if there is not a currently
 185       * active attempt for this user.
 186       *
 187       * @param int $numprevattempts the number of previous attempts this user has made.
 188       * @param stdClass $lastattempt information about the user's last completed attempt.
 189       * @return bool true if this rule means that this user will never be allowed another
 190       * attempt at this quiz.
 191       */
 192      public function is_finished($numprevattempts, $lastattempt) {
 193          return false;
 194      }
 195  
 196      /**
 197       * Time by which, according to this rule, the user has to finish their attempt.
 198       *
 199       * @param stdClass $attempt the current attempt
 200       * @return int|false the attempt close time, or false if there is no close time.
 201       */
 202      public function end_time($attempt) {
 203          return false;
 204      }
 205  
 206      /**
 207       * If the user should be shown a different amount of time than $timenow - $this->end_time(), then
 208       * override this method.  This is useful if the time remaining is large enough to be omitted.
 209       * @param stdClass $attempt the current attempt
 210       * @param int $timenow the time now. We don't use $this->timenow, so we can
 211       * give the user a more accurate indication of how much time is left.
 212       * @return mixed the time left in seconds (can be negative) or false if there is no limit.
 213       */
 214      public function time_left_display($attempt, $timenow) {
 215          $endtime = $this->end_time($attempt);
 216          if ($endtime === false) {
 217              return false;
 218          }
 219          return $endtime - $timenow;
 220      }
 221  
 222      /**
 223       * Does this rule requires the attempt (and review) to be displayed in a pop-up window?
 224       *
 225       * @return bool true if it does.
 226       */
 227      public function attempt_must_be_in_popup() {
 228          return false;
 229      }
 230  
 231      /**
 232       * Any options required when showing the attempt in a pop-up.
 233       *
 234       * @return array any options that are required for showing the attempt page
 235       *      in a popup window.
 236       */
 237      public function get_popup_options() {
 238          return [];
 239      }
 240  
 241      /**
 242       * Sets up the attempt (review or summary) page with any special extra
 243       * properties required by this rule. securewindow rule is an example of where
 244       * this is used.
 245       *
 246       * @param moodle_page $page the page object to initialise.
 247       */
 248      public function setup_attempt_page($page) {
 249          // Do nothing by default.
 250      }
 251  
 252      /**
 253       * It is possible for one rule to override other rules.
 254       *
 255       * The aim is that third-party rules should be able to replace sandard rules
 256       * if they want. See, for example MDL-13592.
 257       *
 258       * @return array plugin names of other rules that this one replaces.
 259       *      For example ['ipaddress', 'password'].
 260       */
 261      public function get_superceded_rules() {
 262          return [];
 263      }
 264  
 265      /**
 266       * Add any fields that this rule requires to the quiz settings form. This
 267       * method is called from {@see mod_quiz_mod_form::definition()}, while the
 268       * security seciton is being built.
 269       * @param mod_quiz_mod_form $quizform the quiz settings form that is being built.
 270       * @param MoodleQuickForm $mform the wrapped MoodleQuickForm.
 271       */
 272      public static function add_settings_form_fields(
 273              mod_quiz_mod_form $quizform, MoodleQuickForm $mform) {
 274          // By default do nothing.
 275      }
 276  
 277      /**
 278       * Validate the data from any form fields added using {@see add_settings_form_fields()}.
 279       * @param array $errors the errors found so far.
 280       * @param array $data the submitted form data.
 281       * @param array $files information about any uploaded files.
 282       * @param mod_quiz_mod_form $quizform the quiz form object.
 283       * @return array $errors the updated $errors array.
 284       */
 285      public static function validate_settings_form_fields(array $errors,
 286              array $data, $files, mod_quiz_mod_form $quizform) {
 287  
 288          return $errors;
 289      }
 290  
 291      /**
 292       * Get any options this rule adds to the 'Browser security' quiz setting.
 293       *
 294       * @return array key => lang string any choices to add to the quiz Browser
 295       *      security settings menu.
 296       */
 297      public static function get_browser_security_choices() {
 298          return [];
 299      }
 300  
 301      /**
 302       * Save any submitted settings when the quiz settings form is submitted. This
 303       * is called from {@see quiz_after_add_or_update()} in lib.php.
 304       * @param stdClass $quiz the data from the quiz form, including $quiz->id
 305       *      which is the id of the quiz being saved.
 306       */
 307      public static function save_settings($quiz) {
 308          // By default do nothing.
 309      }
 310  
 311      /**
 312       * Delete any rule-specific settings when the quiz is deleted. This is called
 313       * from {@see quiz_delete_instance()} in lib.php.
 314       * @param stdClass $quiz the data from the database, including $quiz->id
 315       *      which is the id of the quiz being deleted.
 316       * @since Moodle 2.7.1, 2.6.4, 2.5.7
 317       */
 318      public static function delete_settings($quiz) {
 319          // By default do nothing.
 320      }
 321  
 322      /**
 323       * Return the bits of SQL needed to load all the settings from all the access
 324       * plugins in one DB query. The easiest way to understand what you need to do
 325       * here is probably to read the code of {@see access_manager::load_settings()}.
 326       *
 327       * If you have some settings that cannot be loaded in this way, then you can
 328       * use the {@see get_extra_settings()} method instead, but that has
 329       * performance implications.
 330       *
 331       * @param int $quizid the id of the quiz we are loading settings for. This
 332       *     can also be accessed as quiz.id in the SQL. (quiz is a table alisas for {quiz}.)
 333       * @return array with three elements:
 334       *     1. fields: any fields to add to the select list. These should be alised
 335       *        if neccessary so that the field name starts the name of the plugin.
 336       *     2. joins: any joins (should probably be LEFT JOINS) with other tables that
 337       *        are needed.
 338       *     3. params: array of placeholder values that are needed by the SQL. You must
 339       *        used named placeholders, and the placeholder names should start with the
 340       *        plugin name, to avoid collisions.
 341       */
 342      public static function get_settings_sql($quizid) {
 343          return ['', '', []];
 344      }
 345  
 346      /**
 347       * You can use this method to load any extra settings your plugin has that
 348       * cannot be loaded efficiently with get_settings_sql().
 349       * @param int $quizid the quiz id.
 350       * @return array setting value name => value. The value names should all
 351       *      start with the name of your plugin to avoid collisions.
 352       */
 353      public static function get_extra_settings($quizid) {
 354          return [];
 355      }
 356  }