Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 401 and 403]

   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  /**
  18   * Guest access plugin.
  19   *
  20   * This plugin does not add any entries into the user_enrolments table,
  21   * the access control is granted on the fly via the tricks in require_login().
  22   *
  23   * @package    enrol_guest
  24   * @copyright  2010 Petr Skoda  {@link http://skodak.org}
  25   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  26   */
  27  
  28  defined('MOODLE_INTERNAL') || die();
  29  
  30  /**
  31   * Class enrol_guest_plugin
  32   *
  33   * @copyright  2010 Petr Skoda  {@link http://skodak.org}
  34   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   */
  36  class enrol_guest_plugin extends enrol_plugin {
  37  
  38      /**
  39       * Returns optional enrolment information icons.
  40       *
  41       * This is used in course list for quick overview of enrolment options.
  42       *
  43       * We are not using single instance parameter because sometimes
  44       * we might want to prevent icon repetition when multiple instances
  45       * of one type exist. One instance may also produce several icons.
  46       *
  47       * @param array $instances all enrol instances of this type in one course
  48       * @return array of pix_icon
  49       */
  50      public function get_info_icons(array $instances) {
  51          foreach ($instances as $instance) {
  52              if ($instance->password !== '') {
  53                  return array(new pix_icon('withpassword', get_string('guestaccess_withpassword', 'enrol_guest'), 'enrol_guest'));
  54              } else {
  55                  return array(new pix_icon('withoutpassword', get_string('guestaccess_withoutpassword', 'enrol_guest'), 'enrol_guest'));
  56              }
  57          }
  58      }
  59  
  60      /**
  61       * Enrol a user using a given enrolment instance.
  62       *
  63       * @param stdClass $instance
  64       * @param int $userid
  65       * @param null $roleid
  66       * @param int $timestart
  67       * @param int $timeend
  68       * @param null $status
  69       * @param null $recovergrades
  70       */
  71      public function enrol_user(stdClass $instance, $userid, $roleid = null, $timestart = 0, $timeend = 0, $status = null, $recovergrades = null) {
  72          // no real enrolments here!
  73          return;
  74      }
  75  
  76      /**
  77       * Enrol a user from a given enrolment instance.
  78       *
  79       * @param stdClass $instance
  80       * @param int $userid
  81       */
  82      public function unenrol_user(stdClass $instance, $userid) {
  83          // nothing to do, we never enrol here!
  84          return;
  85      }
  86  
  87      /**
  88       * Attempt to automatically gain temporary guest access to course,
  89       * calling code has to make sure the plugin and instance are active.
  90       *
  91       * @param stdClass $instance course enrol instance
  92       * @return bool|int false means no guest access, integer means end of cached time
  93       */
  94      public function try_guestaccess(stdClass $instance) {
  95          global $USER, $CFG;
  96  
  97          $allow = false;
  98  
  99          if ($instance->password === '') {
 100              $allow = true;
 101  
 102          } else if (isset($USER->enrol_guest_passwords[$instance->id])) { // this is a hack, ideally we should not add stuff to $USER...
 103              if ($USER->enrol_guest_passwords[$instance->id] === $instance->password) {
 104                  $allow = true;
 105              }
 106          }
 107  
 108          if ($allow) {
 109              // Temporarily assign them some guest role for this context
 110              $context = context_course::instance($instance->courseid);
 111              load_temp_course_role($context, $CFG->guestroleid);
 112              return ENROL_MAX_TIMESTAMP;
 113          }
 114  
 115          return false;
 116      }
 117  
 118      /**
 119       * Returns true if the current user can add a new instance of enrolment plugin in course.
 120       * @param int $courseid
 121       * @return boolean
 122       */
 123      public function can_add_instance($courseid) {
 124          global $DB;
 125  
 126          $context = context_course::instance($courseid, MUST_EXIST);
 127  
 128          if (!has_capability('moodle/course:enrolconfig', $context) or !has_capability('enrol/guest:config', $context)) {
 129              return false;
 130          }
 131  
 132          if ($DB->record_exists('enrol', array('courseid'=>$courseid, 'enrol'=>'guest'))) {
 133              return false;
 134          }
 135  
 136          return true;
 137      }
 138  
 139      /**
 140       * Creates course enrol form, checks if form submitted
 141       * and enrols user if necessary. It can also redirect.
 142       *
 143       * @param stdClass $instance
 144       * @return string html text, usually a form in a text box
 145       */
 146      public function enrol_page_hook(stdClass $instance) {
 147          global $CFG, $OUTPUT, $SESSION, $USER;
 148  
 149          if ($instance->password === '') {
 150              return null;
 151          }
 152  
 153          if (isset($USER->enrol['tempguest'][$instance->courseid]) and $USER->enrol['tempguest'][$instance->courseid] > time()) {
 154              // no need to show the guest access when user can already enter course as guest
 155              return null;
 156          }
 157  
 158          require_once("$CFG->dirroot/enrol/guest/locallib.php");
 159          $form = new enrol_guest_enrol_form(NULL, $instance);
 160          $instanceid = optional_param('instance', 0, PARAM_INT);
 161  
 162          if ($instance->id == $instanceid) {
 163              if ($data = $form->get_data()) {
 164                  // add guest role
 165                  $context = context_course::instance($instance->courseid);
 166                  $USER->enrol_guest_passwords[$instance->id] = $data->guestpassword; // this is a hack, ideally we should not add stuff to $USER...
 167                  if (isset($USER->enrol['tempguest'][$instance->courseid])) {
 168                      remove_temp_course_roles($context);
 169                  }
 170                  load_temp_course_role($context, $CFG->guestroleid);
 171                  $USER->enrol['tempguest'][$instance->courseid] = ENROL_MAX_TIMESTAMP;
 172  
 173                  // go to the originally requested page
 174                  if (!empty($SESSION->wantsurl)) {
 175                      $destination = $SESSION->wantsurl;
 176                      unset($SESSION->wantsurl);
 177                  } else {
 178                      $destination = "$CFG->wwwroot/course/view.php?id=$instance->courseid";
 179                  }
 180                  redirect($destination);
 181              }
 182          }
 183  
 184          ob_start();
 185          $form->display();
 186          $output = ob_get_clean();
 187  
 188          return $OUTPUT->box($output, 'generalbox');
 189      }
 190  
 191      /**
 192       * Called after updating/inserting course.
 193       *
 194       * @param bool $inserted true if course just inserted
 195       * @param object $course
 196       * @param object $data form data
 197       * @return void
 198       */
 199      public function course_updated($inserted, $course, $data) {
 200          global $DB;
 201  
 202          if ($inserted) {
 203              if (isset($data->enrol_guest_status_0)) {
 204                  $fields = array('status'=>$data->enrol_guest_status_0);
 205                  if ($fields['status'] == ENROL_INSTANCE_ENABLED) {
 206                      $fields['password'] = $data->enrol_guest_password_0;
 207                  } else {
 208                      if ($this->get_config('requirepassword')) {
 209                          $fields['password'] = generate_password(20);
 210                      }
 211                  }
 212                  $this->add_instance($course, $fields);
 213              } else {
 214                  if ($this->get_config('defaultenrol')) {
 215                      $this->add_default_instance($course);
 216                  }
 217              }
 218  
 219          } else {
 220              $instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'enrol'=>'guest'));
 221              foreach ($instances as $instance) {
 222                  $i = $instance->id;
 223  
 224                  if (isset($data->{'enrol_guest_status_'.$i})) {
 225                      $reset = ($instance->status != $data->{'enrol_guest_status_'.$i});
 226  
 227                      $instance->status       = $data->{'enrol_guest_status_'.$i};
 228                      $instance->timemodified = time();
 229                      if ($instance->status == ENROL_INSTANCE_ENABLED) {
 230                          if ($instance->password !== $data->{'enrol_guest_password_'.$i}) {
 231                              $reset = true;
 232                          }
 233                          $instance->password = $data->{'enrol_guest_password_'.$i};
 234                      }
 235                      $DB->update_record('enrol', $instance);
 236                      \core\event\enrol_instance_updated::create_from_record($instance)->trigger();
 237  
 238                      if ($reset) {
 239                          $context = context_course::instance($course->id);
 240                          $context->mark_dirty();
 241                      }
 242                  }
 243              }
 244          }
 245      }
 246  
 247      /**
 248       * Add new instance of enrol plugin.
 249       * @param object $course
 250       * @param array instance fields
 251       * @return int id of new instance, null if can not be created
 252       */
 253      public function add_instance($course, array $fields = NULL) {
 254          $fields = (array)$fields;
 255  
 256          if (!isset($fields['password'])) {
 257              $fields['password'] = '';
 258          }
 259  
 260          return parent::add_instance($course, $fields);
 261      }
 262  
 263      /**
 264       * Add new instance of enrol plugin with default settings.
 265       * @param object $course
 266       * @return int id of new instance
 267       */
 268      public function add_default_instance($course) {
 269          $fields = array('status'=>$this->get_config('status'));
 270  
 271          if ($this->get_config('requirepassword')) {
 272              $fields['password'] = generate_password(20);
 273          }
 274  
 275          return $this->add_instance($course, $fields);
 276      }
 277  
 278      /**
 279       * Restore instance and map settings.
 280       *
 281       * @param restore_enrolments_structure_step $step
 282       * @param stdClass $data
 283       * @param stdClass $course
 284       * @param int $oldid
 285       */
 286      public function restore_instance(restore_enrolments_structure_step $step, stdClass $data, $course, $oldid) {
 287          global $DB;
 288  
 289          if (!$DB->record_exists('enrol', array('courseid' => $data->courseid, 'enrol' => $this->get_name()))) {
 290              $this->add_instance($course, (array)$data);
 291          }
 292  
 293          // No need to set mapping, we do not restore users or roles here.
 294          $step->set_mapping('enrol', $oldid, 0);
 295      }
 296  
 297      /**
 298       * Is it possible to delete enrol instance via standard UI?
 299       *
 300       * @param object $instance
 301       * @return bool
 302       */
 303      public function can_delete_instance($instance) {
 304          $context = context_course::instance($instance->courseid);
 305          return has_capability('enrol/guest:config', $context);
 306      }
 307  
 308      /**
 309       * Is it possible to hide/show enrol instance via standard UI?
 310       *
 311       * @param stdClass $instance
 312       * @return bool
 313       */
 314      public function can_hide_show_instance($instance) {
 315          $context = context_course::instance($instance->courseid);
 316          if (!has_capability('enrol/guest:config', $context)) {
 317              return false;
 318          }
 319  
 320          // If the instance is currently disabled, before it can be enabled, we must check whether the password meets the
 321          // password policies.
 322          if ($instance->status == ENROL_INSTANCE_DISABLED) {
 323              if ($this->get_config('requirepassword')) {
 324                  if (empty($instance->password)) {
 325                      return false;
 326                  }
 327              }
 328  
 329              // Only check the password if it is set.
 330              if (!empty($instance->password) && $this->get_config('usepasswordpolicy')) {
 331                  if (!check_password_policy($instance->password, $errmsg)) {
 332                      return false;
 333                  }
 334              }
 335          }
 336  
 337          return true;
 338      }
 339  
 340      /**
 341       * Get default settings for enrol_guest.
 342       *
 343       * @return array
 344       */
 345      public function get_instance_defaults() {
 346          $fields = array();
 347          $fields['status']          = $this->get_config('status');
 348          return $fields;
 349      }
 350  
 351      /**
 352       * Return information for enrolment instance containing list of parameters required
 353       * for enrolment, name of enrolment plugin etc.
 354       *
 355       * @param stdClass $instance enrolment instance
 356       * @return stdClass instance info.
 357       * @since Moodle 3.1
 358       */
 359      public function get_enrol_info(stdClass $instance) {
 360  
 361          $instanceinfo = new stdClass();
 362          $instanceinfo->id = $instance->id;
 363          $instanceinfo->courseid = $instance->courseid;
 364          $instanceinfo->type = $this->get_name();
 365          $instanceinfo->name = $this->get_instance_name($instance);
 366          $instanceinfo->status = $instance->status == ENROL_INSTANCE_ENABLED;
 367  
 368          // Specifics enrolment method parameters.
 369          $instanceinfo->requiredparam = new stdClass();
 370          $instanceinfo->requiredparam->passwordrequired = !empty($instance->password);
 371  
 372          // If the plugin is enabled, return the URL for obtaining more information.
 373          if ($instanceinfo->status) {
 374              $instanceinfo->wsfunction = 'enrol_guest_get_instance_info';
 375          }
 376          return $instanceinfo;
 377      }
 378  
 379      /**
 380       * Return an array of valid options for the status.
 381       *
 382       * @return array
 383       */
 384      protected function get_status_options() {
 385          $options = array(ENROL_INSTANCE_ENABLED  => get_string('yes'),
 386                           ENROL_INSTANCE_DISABLED => get_string('no'));
 387          return $options;
 388      }
 389  
 390      /**
 391       * Add elements to the edit instance form.
 392       *
 393       * @param stdClass $instance
 394       * @param MoodleQuickForm $mform
 395       * @param context $context
 396       * @return bool
 397       */
 398      public function edit_instance_form($instance, MoodleQuickForm $mform, $context) {
 399          global $CFG;
 400  
 401          $options = $this->get_status_options();
 402          $mform->addElement('select', 'status', get_string('status', 'enrol_guest'), $options);
 403          $mform->addHelpButton('status', 'status', 'enrol_guest');
 404          $mform->setDefault('status', $this->get_config('status'));
 405          $mform->setAdvanced('status', $this->get_config('status_adv'));
 406  
 407          $mform->addElement('passwordunmask', 'password', get_string('password', 'enrol_guest'));
 408          $mform->addHelpButton('password', 'password', 'enrol_guest');
 409  
 410          // If we have a new instance and the password is required - make sure it is set. For existing
 411          // instances we do not force the password to be required as it may have been set to empty before
 412          // the password was required. We check in the validation function whether this check is required
 413          // for existing instances.
 414          if (empty($instance->id) && $this->get_config('requirepassword')) {
 415              $mform->addRule('password', get_string('required'), 'required', null);
 416          }
 417      }
 418  
 419      /**
 420       * We are a good plugin and don't invent our own UI/validation code path.
 421       *
 422       * @return boolean
 423       */
 424      public function use_standard_editing_ui() {
 425          return true;
 426      }
 427  
 428      /**
 429       * Perform custom validation of the data used to edit the instance.
 430       *
 431       * @param array $data array of ("fieldname"=>value) of submitted data
 432       * @param array $files array of uploaded files "element_name"=>tmp_file_path
 433       * @param object $instance The instance loaded from the DB
 434       * @param context $context The context of the instance we are editing
 435       * @return array of "element_name"=>"error_description" if there are errors,
 436       *         or an empty array if everything is OK.
 437       * @return void
 438       */
 439      public function edit_instance_validation($data, $files, $instance, $context) {
 440          $errors = array();
 441  
 442          $checkpassword = false;
 443  
 444          if ($data['id']) {
 445              // Check the password if we are enabling the plugin again.
 446              if (($instance->status == ENROL_INSTANCE_DISABLED) && ($data['status'] == ENROL_INSTANCE_ENABLED)) {
 447                  $checkpassword = true;
 448              }
 449  
 450              // Check the password if the instance is enabled and the password has changed.
 451              if (($data['status'] == ENROL_INSTANCE_ENABLED) && ($instance->password !== $data['password'])) {
 452                  $checkpassword = true;
 453              }
 454          } else {
 455              $checkpassword = true;
 456          }
 457  
 458          if ($checkpassword) {
 459              $require = $this->get_config('requirepassword');
 460              $policy  = $this->get_config('usepasswordpolicy');
 461              if ($require && trim($data['password']) === '') {
 462                  $errors['password'] = get_string('required');
 463              } else if (!empty($data['password']) && $policy) {
 464                  $errmsg = '';
 465                  if (!check_password_policy($data['password'], $errmsg)) {
 466                      $errors['password'] = $errmsg;
 467                  }
 468              }
 469          }
 470  
 471          $validstatus = array_keys($this->get_status_options());
 472          $tovalidate = array(
 473              'status' => $validstatus
 474          );
 475          $typeerrors = $this->validate_param_types($data, $tovalidate);
 476          $errors = array_merge($errors, $typeerrors);
 477  
 478          return $errors;
 479      }
 480  
 481  
 482  }
 483  
 484  /**
 485   * Get icon mapping for font-awesome.
 486   */
 487  function enrol_guest_get_fontawesome_icon_map() {
 488      return [
 489          'enrol_guest:withpassword' => 'fa-key',
 490          'enrol_guest:withoutpassword' => 'fa-unlock-alt',
 491      ];
 492  }