├── .github └── workflows │ └── ci.yml ├── README.md ├── auth.php ├── classes ├── form │ ├── enrolkey_cohort_form.php │ ├── enrolkey_profile_form.php │ ├── enrolkey_redirect_form.php │ ├── enrolkey_signup_form.php │ └── unsuspend_form.php ├── output │ └── renderer.php ├── persistent │ ├── enrolkey_cohort_mapping.php │ ├── enrolkey_profile_mapping.php │ └── enrolkey_redirect_mapping.php ├── privacy │ └── provider.php ├── table │ └── enrolkey_available_table.php └── utility.php ├── db ├── install.xml └── upgrade.php ├── edit_cohort.php ├── edit_profile.php ├── edit_redirect.php ├── lang └── en │ └── auth_enrolkey.php ├── lib.php ├── manage.php ├── renderer.php ├── settings.php ├── templates └── enrolkey_profiletable.mustache ├── tests ├── auth_test.php └── reset_password_hook_test.php ├── unsuspend.php ├── version.php └── view.php /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # .github/workflows/ci.yml 2 | name: ci 3 | 4 | on: [push, pull_request] 5 | 6 | jobs: 7 | ci: 8 | uses: catalyst/catalyst-moodle-workflows/.github/workflows/ci.yml@main 9 | # Required if you plan to publish (uncomment the below) 10 | secrets: 11 | moodle_org_token: ${{ secrets.MOODLE_ORG_TOKEN }} 12 | with: 13 | release_branches: master 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Moodle Enrolment key based self-registration 2 | 3 | ![GitHub Workflow Status (branch)](https://img.shields.io/github/actions/workflow/status/catalyst/moodle-auth_enrolkey/ci.yml?branch=MOODLE_35_STABLE) 4 | 5 | This auth plugin combines the best of both email based signup and self enrolment keys into a streamlined process making it much faster for students to get into a course. For the student it saves around 9-10 clicks and avoids context switching between a browser and their email client where they can become easily become disengaged or run into issues if their email is unavailable. 6 | 7 | This is mostly a clone of the Email-based self-registration plugin that also enrols a user into available courses based on a token supplied. When a user enters a valid token it will automatically enrol them into the course that token was specified for, and then take them directly to that course. Also an optional bonus setting: because they have demonstrated knowledge of a secret code we know they are a real human so we let them straight in without forcing them to confirm their email. For some use cases where they may login and complete their course in a single session we may not ever care about their email being valid. 8 | 9 | Courses that provide self enrolment can restrict access to them with a key. If the signup token matches any course enrolment key then the new user will be enrolled into those courses. 10 | 11 | Found in the Moodle plugins directory at [https://moodle.org/plugins/auth_enrolkey](https://moodle.org/plugins/auth_enrolkey) 12 | 13 | This also has a companion block plugin: 14 | 15 | https://github.com/catalyst/moodle-block_enrolkey 16 | 17 | https://moodle.org/plugins/block_enrolkey 18 | 19 | * [Branches](#branches) 20 | * [Installation](#installation) 21 | * [Setup](#setup) 22 | * [Settings](#settings) 23 | * [Admin Usage](#admin-usage) 24 | * [Client Usage](#client-usage) 25 | * [TODO](#todo) 26 | * [Support](#support) 27 | 28 | 29 | Branches 30 | -------- 31 | 32 | | Moodle version | Branch | PHP | 33 | |-------------------| ---------------- | ----- | 34 | | Moodle 3.5 to 4.5 | MOODLE_35_STABLE | 7.1+ | 35 | 36 | Installation 37 | ------------ 38 | 39 | Add the plugin to /auth/enrolkey/ 40 | 41 | Run the Moodle upgrade. 42 | 43 | Setup 44 | ----- 45 | First enable the Enrolment key based self-registration plugin for use. 46 | 47 | `Site administration > Plugins > Authentication > Manage Authentication` 48 | 49 | On the same page that you manage authentication options, scroll down to the common settings and select `Enrolment key based self-registration` in the Self Registration drop down list for `registerauth`. 50 | 51 | Enable the Self enrolment plugin. 52 | 53 | `Site administration > Plugins > Enrolments > Manage enrol plugins` 54 | 55 | Settings 56 | -------- 57 | 58 | It is possible to force the the enrolment key as a required element for signing up. 59 | 60 | Users can be forced to confirm their account via email even when entering a valid token. 61 | 62 | Admin Usage 63 | ----------- 64 | 65 | ## Course enrolment keys 66 | 67 | Enable a new Self enrolment method in the course required. 68 | 69 | Moodle 3.5 to 3.11, `Course administration > Users > Enrolment methods > Add method > Self enrolment` 70 | 71 | Moodle 4.0+, `Participants > Enrolments > Enrolment methods > Add method > Self enrolment` 72 | 73 | The Enrolment key field that is visible will be used for the automatic enrolment on signup. 74 | 75 | When creating new Self enrolment method, provide a custom instance name that is descriptive enough to determine how and when this specific instance will be used for enrolling. 76 | 77 | Please add additional Self enrolment methods for the course that you require automatic enrolment access for. 78 | 79 | ## Group enrolment keys 80 | 81 | If you select `Use group enrolment keys: Yes` during creating of a new Self enrolment method, it enables a functionality of automatic adding self enrolled students to the groups created in the course. 82 | 83 | In order for that to work, create a group in the course and specify `Enrolment key`. 84 | 85 | Please note, that this key must be different from the course enrollment key which you set in the course Self enrolment method. 86 | 87 | As a result, students will be able to subscribe using the group enrolment key. That will enrol them to the course where the group was created and also will add them to that group. 88 | 89 | Client Usage 90 | ------------ 91 | 92 | When a user tries to log in they are given the option to create an account. 93 | 94 | The user can enter a token into the field name `Enrolment key`. 95 | 96 | There is an optional setting that can force this field to be required. 97 | 98 | If the token matches **any** valid instance of self enrolment, then the user will be enrolled to those courses. 99 | 100 | TODO 101 | ---- 102 | 103 | Add option to bypass view.php, this may not be required if only enroling into one course. 104 | 105 | Support 106 | ------- 107 | For any issue with the plugin, please log the in the github repository here: 108 | 109 | https://github.com/catalyst/moodle-auth_enrolkey/issues 110 | 111 | Please note our time is limited, so if you need urgent support or want to 112 | sponsor a new feature then please contact Catalyst IT Australia: 113 | 114 | https://www.catalyst-au.net/contact-us 115 | 116 | 117 | 118 | This plugin was developed by Catalyst IT Australia: 119 | 120 | https://www.catalyst-au.net/ 121 | 122 | Catalyst IT 123 | 124 | -------------------------------------------------------------------------------- /auth.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Authentication Plugin: Enrolment key based self-registration. 19 | * 20 | * @package auth_enrolkey 21 | * @copyright 2016 Nicholas Hoobin (nicholashoobin@catalyst-au.net) 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die; 26 | 27 | require_once($CFG->libdir . '/authlib.php'); 28 | 29 | use auth_enrolkey\utility; 30 | 31 | // If totara cohort lib exists, import it. 32 | if (file_exists($CFG->dirroot . '/totara/cohort/lib.php')) { 33 | require_once($CFG->dirroot . '/totara/cohort/lib.php'); 34 | } 35 | 36 | /** 37 | * Enrolment key based self-registration. 38 | * @copyright 2016 Nicholas Hoobin (nicholashoobin@catalyst-au.net) 39 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 40 | */ 41 | class auth_plugin_enrolkey extends auth_plugin_base { 42 | 43 | /** 44 | * Constructor. 45 | */ 46 | public function __construct() { 47 | $this->authtype = 'enrolkey'; 48 | $this->config = get_config('auth_enrolkey'); 49 | } 50 | 51 | /** 52 | * Returns true if the username and password work and false if they are 53 | * wrong or don't exist. 54 | * 55 | * @param string $username The username 56 | * @param string $password The password 57 | * @return bool Authentication success or failure. 58 | */ 59 | public function user_login($username, $password) { 60 | global $CFG, $DB; 61 | if ($user = $DB->get_record('user', ['username' => $username, 'mnethostid' => $CFG->mnet_localhost_id])) { 62 | return validate_internal_user_password($user, $password); 63 | } 64 | 65 | return false; 66 | } 67 | 68 | /** 69 | * Moodle hook 'pre_user_login_hook'. 70 | * 71 | * @param object $user 72 | * @throws coding_exception 73 | */ 74 | public function pre_user_login_hook(&$user) { 75 | if (!get_config('auth_enrolkey', 'unsuspendaccounts')) { 76 | // The option to unsuspend is not enabled. Do not redirect. 77 | return; 78 | } 79 | 80 | // Password is passed via the login.php page. 81 | $password = optional_param('password', '', PARAM_RAW); 82 | $valid = validate_internal_user_password($user, $password); 83 | 84 | if ($user->auth != $this->authtype) { 85 | return; 86 | } 87 | 88 | if (!empty($user->suspended)) { 89 | $failurereason = AUTH_LOGIN_SUSPENDED; 90 | 91 | // Trigger login failed event. 92 | $event = \core\event\user_login_failed::create([ 93 | 'userid' => $user->id, 94 | 'other' => ['username' => $user->username, 'reason' => $failurereason], 95 | ]); 96 | $event->trigger(); 97 | 98 | // Oh no. This user is suspended, but the password is all good. Lets take them to a self un-suspend page. 99 | if ($valid) { 100 | redirect(new moodle_url('/auth/enrolkey/unsuspend.php')); 101 | } 102 | } 103 | } 104 | 105 | /** 106 | * Method for changing password in the system 107 | * @param stdClass $user 108 | * @param string $newpassword 109 | * @return bool 110 | */ 111 | public function user_update_password($user, $newpassword) { 112 | $user = get_complete_user_data('id', $user->id); 113 | return update_internal_user_password($user, $newpassword); 114 | } 115 | 116 | /** 117 | * Adds this authentication method to the self registration list. 118 | * 119 | */ 120 | public function can_signup() { 121 | return true; 122 | } 123 | 124 | /** 125 | * Returns true if this authentication plugin can change the user's 126 | * password. 127 | * 128 | * @return bool 129 | */ 130 | public function can_change_password() { 131 | return true; 132 | } 133 | 134 | /** 135 | * Returns true if the user can reset their password. 136 | * 137 | * @return bool 138 | */ 139 | public function can_reset_password() { 140 | return true; 141 | } 142 | 143 | /** 144 | * Sign up a new user ready for confirmation. 145 | * Password is passed in plaintext. 146 | * @param object $user 147 | * @param bool $notify 148 | * @throws coding_exception 149 | * @throws dml_exception 150 | * @throws moodle_exception 151 | */ 152 | public function user_signup($user, $notify=true) { 153 | global $CFG, $DB, $SESSION, $USER, $PAGE, $OUTPUT; 154 | require_once($CFG->dirroot . '/user/profile/lib.php'); 155 | require_once($CFG->dirroot . '/user/lib.php'); 156 | require_once($CFG->dirroot . '/enrol/self/lib.php'); 157 | 158 | $user->password = hash_internal_user_password($user->password); 159 | 160 | // These are currently not present in the user object. 161 | $user->currentlogin = time(); 162 | $user->picture = 0; 163 | $user->imagealt = 0; 164 | $user->deleted = 0; 165 | $emailconfirmation = get_config('auth_enrolkey', 'emailconfirmation'); 166 | // Default setting confirmation not required. 167 | $user->policyagreed = 1; 168 | $user->confirmed = 1; 169 | if ('1' === $emailconfirmation) { 170 | // No access until account confirmed via email. 171 | $user->policyagreed = 0; 172 | $user->confirmed = 0; 173 | } else if ('2' === $emailconfirmation) { 174 | // Access to course, but confirmation required before next login attempt. 175 | $user->confirmed = 0; 176 | } 177 | $user->lastip = getremoteaddr(); 178 | $user->id = user_create_user($user, false, false); 179 | 180 | // Save any custom profile field information. 181 | profile_save_data($user); 182 | 183 | // Trigger event. 184 | \core\event\user_created::create_from_userid($user->id)->trigger(); 185 | if ($notify) { 186 | if (!send_confirmation_email($user)) { 187 | // TODO make this more resilient? Email shouldn't be critical here. 188 | throw new \moodle_exception('noemail', 'auth_enrolkey'); 189 | } 190 | } 191 | 192 | if (PHPUNIT_TEST) { 193 | $USER->username = $user->username; 194 | $USER->id = $user->id; 195 | $USER->email = $user->email; 196 | } else { 197 | complete_user_login($user); 198 | } 199 | $USER->loggedin = true; 200 | $USER->site = $CFG->wwwroot; 201 | set_moodle_cookie($USER->username); 202 | list($availableenrolids, $errors) = $this->enrol_user($user->signup_token, $notify); 203 | if (!$notify) { 204 | return; 205 | } 206 | 207 | if (!empty($availableenrolids) && $user->confirmed === 0 && $user->policyagreed === 0) { 208 | $this->email_confirmation($user->email); 209 | } 210 | 211 | // If there were errors detected, output on target page. 212 | foreach ($errors as $courseid => $errmsg) { 213 | $course = get_course($courseid); 214 | \core\notification::error( 215 | get_string('errorenrolling', 'auth_enrolkey', ['course' => $course->fullname, 'err' => $errmsg]) 216 | ); 217 | } 218 | 219 | // New Enrolkey hook, if configured will redirect the user based on the enrolkey used. 220 | \auth_enrolkey\persistent\enrolkey_redirect_mapping::redirect_during_signup($availableenrolids); 221 | 222 | // If no courses found (empty key) go to dashboard. 223 | if (empty($availableenrolids)) { 224 | redirect(new moodle_url('/my/')); 225 | } else { 226 | redirect(new moodle_url("/auth/enrolkey/view.php", ['ids' => implode(',', $availableenrolids)])); 227 | } 228 | } 229 | 230 | /** 231 | * Enrols a $USER with valid a $enrolkey 232 | * @param string $enrolkey 233 | * @param bool $notify 234 | * @return array 235 | */ 236 | public function enrol_user(string $enrolkey, bool $notify = true) : array { 237 | global $DB, $USER; 238 | 239 | /** @var enrol_self_plugin $enrol */ 240 | $enrol = enrol_get_plugin('self'); 241 | $enrolplugins = $this->get_enrol_plugins($DB, $enrolkey); 242 | $availableenrolids = []; 243 | $errors = []; 244 | foreach ($enrolplugins as $enrolplugin) { 245 | if ($enrol->can_self_enrol($enrolplugin) === true) { 246 | $data = new stdClass(); 247 | $data->enrolpassword = $enrolplugin->enrolmentkey ?? $enrolplugin->password; 248 | $enrol->enrol_self($enrolplugin, $data); 249 | $availableenrolids[] = $enrolplugin->id; 250 | } else { 251 | // Store error to output. 252 | $errors[$enrolplugin->courseid] = $enrol->can_self_enrol($enrolplugin); 253 | } 254 | } 255 | 256 | if (!empty($availableenrolids)) { 257 | utility::update_user($USER, $availableenrolids); 258 | } 259 | 260 | return [$availableenrolids, $errors]; 261 | } 262 | 263 | /** 264 | * Prints helpful instructions in login/index.php 265 | */ 266 | public function loginpage_hook() { 267 | global $CFG; 268 | 269 | if ($CFG->registerauth == $this->authtype && empty($CFG->auth_instructions)) { 270 | $url = '/login/signup.php'; 271 | $CFG->auth_instructions = get_string('signup_auth_instructions', 'auth_enrolkey', $url); 272 | } 273 | 274 | } 275 | 276 | /** 277 | * Returns true if plugin allows confirming of new users. 278 | * 279 | * @return bool 280 | */ 281 | public function can_confirm() { 282 | return true; 283 | } 284 | 285 | /** 286 | * Confirm the new user as registered. 287 | * 288 | * @param string $username 289 | * @param string $confirmsecret 290 | */ 291 | public function user_confirm($username, $confirmsecret) { 292 | global $DB; 293 | $user = get_complete_user_data('username', $username); 294 | 295 | if (!empty($user)) { 296 | if ($user->auth != $this->authtype) { 297 | return AUTH_CONFIRM_ERROR; 298 | } else if ($user->secret === $confirmsecret && $user->confirmed) { 299 | return AUTH_CONFIRM_ALREADY; 300 | } else if ($user->secret === $confirmsecret) { // They have provided the secret key to get in. 301 | $DB->set_field('user', 'confirmed', 1, ['id' => $user->id]); 302 | return AUTH_CONFIRM_OK; 303 | } 304 | } else { 305 | return AUTH_CONFIRM_ERROR; 306 | } 307 | } 308 | 309 | /** 310 | * Returns true if plugin can be manually set. 311 | * 312 | * @return bool 313 | */ 314 | public function can_be_manually_set() { 315 | return true; 316 | } 317 | 318 | /** 319 | * Return a form to capture user details for account creation. 320 | * This is used in /login/signup.php. 321 | * @return moodle_form A form which edits a record from the user table. 322 | */ 323 | public function signup_form() { 324 | return new \auth_enrolkey\form\enrolkey_signup_form(null, null, 'post', '', ['autocomplete' => 'on']); 325 | } 326 | 327 | /** 328 | * Returns user to a logout page with notice for email confirmation. 329 | * 330 | * @param string $email 331 | * @throws coding_exception 332 | */ 333 | private function email_confirmation(string $email = '') { 334 | global $PAGE, $OUTPUT, $CFG, $USER; 335 | require_logout(); 336 | $emailconfirm = get_string('emailconfirm'); 337 | $PAGE->navbar->add($emailconfirm); 338 | $PAGE->set_title($emailconfirm); 339 | $PAGE->set_heading($PAGE->course->fullname); 340 | echo $OUTPUT->header(); 341 | $email = $USER->email ?? $email; 342 | notice(get_string('emailconfirmsent', '', $email), "$CFG->wwwroot/index.php"); 343 | } 344 | 345 | /** 346 | * Obtains a list of enrolment plugins which use the enrolkey. 347 | * 348 | * @param moodle_database $db 349 | * @param string $enrolkey 350 | * @return array 351 | */ 352 | private function get_enrol_plugins(moodle_database $db, string $enrolkey) : array { 353 | // Password is the Enrolment key that is specified in the Self enrolment instance. 354 | $enrolplugins = $db->get_records('enrol', ['enrol' => 'self', 'password' => $enrolkey]); 355 | 356 | return array_merge($enrolplugins, $db->get_records_sql(" 357 | SELECT e.*, g.enrolmentkey 358 | FROM {groups} g 359 | JOIN {enrol} e ON e.courseid = g.courseid 360 | AND e.enrol = 'self' 361 | AND e.customint1 = 1 362 | WHERE g.enrolmentkey = ? 363 | ", [$enrolkey])); 364 | } 365 | } 366 | -------------------------------------------------------------------------------- /classes/form/enrolkey_cohort_form.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * This contains the auth_enrolkey cohort form. 19 | * 20 | * @package auth_enrolkey 21 | * @copyright 2021 Nicholas Hoobin 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | namespace auth_enrolkey\form; 26 | 27 | use core\persistent; 28 | 29 | defined('MOODLE_INTERNAL') || die(); 30 | 31 | require_once($CFG->libdir.'/formslib.php'); 32 | 33 | /** 34 | * Cohort form. 35 | * 36 | * @package auth_enrolkey 37 | * @copyright 2021 Nicholas Hoobin 38 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 39 | */ 40 | class enrolkey_cohort_form extends \moodleform { 41 | 42 | /** 43 | * Define the form - called by parent constructor 44 | */ 45 | public function definition() { 46 | $mform = $this->_form; 47 | 48 | $cohortlist = $this->_customdata['cohorts']; 49 | 50 | $cohortnames = []; 51 | foreach ($cohortlist['cohorts'] as $cohort) { 52 | $cohortnames[$cohort->id] = $cohort->name . ' (' . $cohort->idnumber . ')'; 53 | } 54 | $options = [ 55 | 'multiple' => true, 56 | 'noselectionstring' => get_string('label_cohortselect_empty', 'auth_enrolkey'), 57 | ]; 58 | $mform->addElement('autocomplete', 'cohortids', get_string('label_cohortselect', 'auth_enrolkey'), $cohortnames, $options); 59 | $mform->addHelpButton('cohortids', 'label_cohortselect', 'auth_enrolkey'); 60 | 61 | $this->add_action_buttons(true); 62 | } 63 | 64 | /** 65 | * Helper function to set the form fields. 66 | * 67 | * @param persistent[] $currentdata 68 | */ 69 | public function set_autocomplete_data($currentdata) { 70 | $list = []; 71 | 72 | foreach ($currentdata as $id => $persistent) { 73 | $list[] = $id; 74 | } 75 | 76 | $this->set_data(['cohortids' => $list]); 77 | } 78 | } 79 | 80 | -------------------------------------------------------------------------------- /classes/form/enrolkey_profile_form.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * This contains the auth_enrolkey profile form. 19 | * 20 | * @package auth_enrolkey 21 | * @copyright 2021 Nicholas Hoobin 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | namespace auth_enrolkey\form; 26 | 27 | use core\persistent; 28 | use core_user; 29 | 30 | defined('MOODLE_INTERNAL') || die(); 31 | 32 | require_once($CFG->libdir.'/formslib.php'); 33 | require_once($CFG->dirroot.'/user/editlib.php'); 34 | 35 | 36 | /** 37 | * Profile form. 38 | * 39 | * @package auth_enrolkey 40 | * @copyright 2021 Nicholas Hoobin 41 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 42 | */ 43 | class enrolkey_profile_form extends \moodleform { 44 | 45 | /** 46 | * Define the form - called by parent constructor 47 | */ 48 | public function definition() { 49 | global $USER; 50 | 51 | $mform = $this->_form; 52 | 53 | // Customisable profile fields. 54 | profile_definition($mform, 0); 55 | 56 | // Moodle optional fields. 57 | $mform->addElement('header', 'moodle_optional', get_string('optional', 'form')); 58 | $mform->setExpanded('moodle_optional', false); 59 | 60 | $mform->addElement('text', 'url', get_string('webpage'), 'maxlength="255" size="50"'); 61 | $mform->setType('url', core_user::get_property_type('url')); 62 | 63 | $removedfields = []; 64 | if (defined('core_user::REMOVED_FIELDS')) { 65 | $removedfields = core_user::REMOVED_FIELDS; 66 | } 67 | 68 | // Fields that might not exist, such as in Totara 13+. 69 | if (!isset($removedfields['icq'])) { 70 | $mform->addElement('text', 'icq', get_string('icqnumber'), 'maxlength="15" size="25"'); 71 | $mform->setType('icq', core_user::get_property_type('icq')); 72 | $mform->setForceLtr('icq'); 73 | } 74 | 75 | $mform->addElement('text', 'skype', get_string('skypeid'), 'maxlength="50" size="25"'); 76 | $mform->setType('skype', core_user::get_property_type('skype')); 77 | $mform->setForceLtr('skype'); 78 | 79 | // Fields that might not exist, such as in Totara 13+. 80 | if (!isset($removedfields['aim'])) { 81 | $mform->addElement('text', 'aim', get_string('aimid'), 'maxlength="50" size="25"'); 82 | $mform->setType('aim', core_user::get_property_type('aim')); 83 | $mform->setForceLtr('aim'); 84 | } 85 | 86 | // Fields that might not exist, such as in Totara 13+. 87 | if (!isset($removedfields['yahoo'])) { 88 | $mform->addElement('text', 'yahoo', get_string('yahooid'), 'maxlength="50" size="25"'); 89 | $mform->setType('yahoo', core_user::get_property_type('yahoo')); 90 | $mform->setForceLtr('yahoo'); 91 | } 92 | 93 | // Fields that might not exist, such as in Totara 13+. 94 | if (!isset($removedfields['msn'])) { 95 | $mform->addElement('text', 'msn', get_string('msnid'), 'maxlength="50" size="25"'); 96 | $mform->setType('msn', core_user::get_property_type('msn')); 97 | $mform->setForceLtr('msn'); 98 | } 99 | 100 | $mform->addElement('text', 'idnumber', get_string('idnumber'), 'maxlength="255" size="25"'); 101 | $mform->setType('idnumber', core_user::get_property_type('idnumber')); 102 | 103 | $mform->addElement('text', 'institution', get_string('institution'), 'maxlength="255" size="25"'); 104 | $mform->setType('institution', core_user::get_property_type('institution')); 105 | 106 | $mform->addElement('text', 'department', get_string('department'), 'maxlength="255" size="25"'); 107 | $mform->setType('department', core_user::get_property_type('department')); 108 | 109 | $mform->addElement('text', 'phone1', get_string('phone1'), 'maxlength="20" size="25"'); 110 | $mform->setType('phone1', core_user::get_property_type('phone1')); 111 | $mform->setForceLtr('phone1'); 112 | 113 | $mform->addElement('text', 'phone2', get_string('phone2'), 'maxlength="20" size="25"'); 114 | $mform->setType('phone2', core_user::get_property_type('phone2')); 115 | $mform->setForceLtr('phone2'); 116 | 117 | $mform->addElement('text', 'address', get_string('address'), 'maxlength="255" size="25"'); 118 | $mform->setType('address', core_user::get_property_type('address')); 119 | 120 | $group = []; 121 | $group[] = $mform->createElement('submit', 'submitbutton', get_string('submit')); 122 | $group[] = $mform->createElement('submit', 'resetbutton', get_string('reset')); 123 | $group[] = $mform->createElement('cancel'); 124 | $mform->addGroup($group, 'buttons', '', ' ', false); 125 | $mform->closeHeaderBefore('buttons'); 126 | 127 | // Some of the headers are dynamically named, so lets iterate through them to set their visibility. 128 | foreach ($mform->_elements as $ele) { 129 | if ($ele instanceof \MoodleQuickForm_header) { 130 | $mform->setExpanded($ele->getName(), true); 131 | } 132 | } 133 | } 134 | 135 | /** 136 | * Helper function to set the form fields. 137 | * 138 | * @param persistent[] $currentdata 139 | */ 140 | public function set_autocomplete_data($currentdata) { 141 | $toset = []; 142 | 143 | foreach ($currentdata as $id => $persistent) { 144 | $key = $persistent->get('profilefieldname'); 145 | $data = $persistent->get('profilefielddata'); 146 | 147 | $toset[$key] = $data; 148 | } 149 | 150 | $this->set_data($toset); 151 | } 152 | } 153 | 154 | -------------------------------------------------------------------------------- /classes/form/enrolkey_redirect_form.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * This contains the auth_enrolkey url redirect form. 19 | * 20 | * @package auth_enrolkey 21 | * @copyright 2021 Nicholas Hoobin 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | namespace auth_enrolkey\form; 26 | 27 | use core\form\persistent; 28 | 29 | /** 30 | * Redirect form. 31 | * 32 | * @package auth_enrolkey 33 | * @copyright 2021 Nicholas Hoobin 34 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 | */ 36 | class enrolkey_redirect_form extends persistent { 37 | 38 | /** @var string $persistentclass */ 39 | protected static $persistentclass = 'auth_enrolkey\\persistent\\enrolkey_redirect_mapping'; 40 | 41 | /** 42 | * Define the form - called by parent constructor 43 | */ 44 | public function definition() { 45 | $mform = $this->_form; 46 | $persistent = $this->get_persistent(); 47 | 48 | // External. 49 | $mform->addElement('text', 'url', get_string('label_redirection', 'auth_enrolkey'), ['size' => '100']); 50 | $mform->setType('url', PARAM_TEXT); 51 | $mform->addHelpButton('url', 'label_redirection', 'auth_enrolkey'); 52 | 53 | $mform->addElement('hidden', 'enrolid', $persistent->get('enrolid')); 54 | $mform->setType('enrolid', PARAM_INT); 55 | 56 | $this->add_action_buttons(true); 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /classes/form/enrolkey_signup_form.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Signup form that provides additional enrolment key field. 19 | * 20 | * @package auth_enrolkey 21 | * @copyright 2016 Nicholas Hoobin (nicholashoobin@catalyst-au.net) 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | /** 26 | * A new signup form that extends login_signup_form. 27 | * 28 | * This provides an additional enrolment key field that will be validated upon signup. 29 | * 30 | * @copyright 2016 Nicholas Hoobin (nicholashoobin@catalyst-au.net) 31 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 32 | */ 33 | namespace auth_enrolkey\form; 34 | 35 | use auth_enrolkey\utility; 36 | use moodle_url; 37 | 38 | defined('MOODLE_INTERNAL') || die; 39 | 40 | require_once($CFG->dirroot . '/login/signup_form.php'); 41 | 42 | /** 43 | * Class for the enrolkey signup form. 44 | * 45 | * @package auth_enrolkey 46 | * @copyright 2021 Nicholas Hoobin 47 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 48 | */ 49 | class enrolkey_signup_form extends \login_signup_form { 50 | 51 | /** 52 | * Creates the Moodle singup form, calls parent::definition(); 53 | */ 54 | public function definition() { 55 | global $CFG; 56 | 57 | // Generates the default signup form. 58 | parent::definition(); 59 | 60 | $mform = $this->_form; 61 | 62 | $element = $mform->createElement('text', 'signup_token', get_string('signup_field_title', 'auth_enrolkey')); 63 | 64 | // View https://docs.moodle.org/dev/lib/formslib.php_Form_Definition#setType for more types. 65 | $mform->setType('signup_token', PARAM_TEXT); 66 | $token = optional_param('signup_token', '', PARAM_TEXT); 67 | if (!empty($token)) { 68 | $mform->setDefault('signup_token', $token); 69 | } 70 | 71 | // Make the course token field visible earlier. 72 | $mform->insertElementBefore($element, 'email'); 73 | 74 | if ($this->signup_token_required()) { 75 | $mform->addRule('signup_token', get_string('signup_missing', 'auth_enrolkey'), 'required', null, 'client'); 76 | } 77 | 78 | if ($this->signup_captcha_enabled()) { 79 | $mform->addElement('recaptcha', 'recaptcha_element', get_string('security_question', 'auth')); 80 | $mform->addHelpButton('recaptcha_element', 'recaptcha', 'auth'); 81 | $mform->closeHeaderBefore('recaptcha_element'); 82 | } 83 | } 84 | 85 | /** 86 | * Returns an array with fields that are invalid during signup. 87 | * This is used in /login/signup.php. 88 | * 89 | * @param array $data array of ("fieldname"=>value) of submitted data 90 | * @param array $files array of uploaded files "element_name"=>tmp_file_path 91 | * @return array of "element_name"=>"error_description" if there are errors, 92 | * or an empty array if everything is OK (true allowed for backwards compatibility too). 93 | */ 94 | public function validation($data, $files) { 95 | global $CFG; 96 | 97 | $errors = parent::validation($data, $files); 98 | 99 | $signuptoken = $data['signup_token']; 100 | 101 | if ($signuptoken !== '') { 102 | // For any case where the token is populated, perform a lookup. 103 | $tokenisvalid = $this->check_database_for_signuptoken($signuptoken); 104 | 105 | if ($tokenisvalid === false) { 106 | $errors['signup_token'] = get_string('signup_token_invalid', 'auth_enrolkey'); 107 | } 108 | } else { 109 | // The form submission is an empty string, double check if the token is required. 110 | if ($this->signup_token_required()) { 111 | $errors['signup_token'] = get_string('signup_missing', 'auth_enrolkey'); 112 | } 113 | } 114 | 115 | if (get_config('auth_enrolkey', 'unsuspendaccounts') && utility::search_for_suspended_user($data)) { 116 | $stringdata = (object) ['href' => (new moodle_url('/auth/enrolkey/unsuspend.php'))->out()]; 117 | if (empty($CFG->createuserwithemail)) { 118 | $errors['username'] = $errors['username'] . get_string('suspendeduseratsignup', 'auth_enrolkey', $stringdata); 119 | } else { 120 | $errors['email'] = $errors['email'] . get_string('suspendeduseratsignup', 'auth_enrolkey', $stringdata); 121 | } 122 | } 123 | 124 | return $errors; 125 | } 126 | 127 | /** 128 | * Checks the enrolment records for any matching self enrolment key. 129 | * 130 | * @param string $token Returns true on success. False on failure. 131 | * @return bool 132 | */ 133 | private function check_database_for_signuptoken($token) { 134 | global $DB; 135 | 136 | $selfenrolinstance = false; 137 | 138 | $instances = $DB->get_records('enrol', ['password' => $token, 'enrol' => 'self']); 139 | 140 | // There may be more than one enrolment instance configured with various dates to check against. 141 | foreach ($instances as $instance) { 142 | // There may be things that prevent self enrol, such as requiring a capability, or full course. 143 | // This should not be a blocker to account creation. The creation should pass, then report the error. 144 | if ($instance->status == ENROL_INSTANCE_ENABLED) { 145 | $selfenrolinstance = true; 146 | } 147 | } 148 | 149 | // Lookup group enrol keys. 150 | $instances = $DB->get_records_sql(" 151 | SELECT e.* 152 | FROM {groups} g 153 | JOIN {enrol} e ON e.courseid = g.courseid 154 | AND e.enrol = 'self' 155 | AND e.customint1 = 1 156 | WHERE g.enrolmentkey = ? 157 | ", [$token]); 158 | foreach ($instances as $instance) { 159 | if ($instance->status == ENROL_INSTANCE_ENABLED) { 160 | $selfenrolinstance = true; 161 | } 162 | } 163 | 164 | return $selfenrolinstance; 165 | } 166 | 167 | /** 168 | * Returns if the enrolment key field is required. 169 | * @return bool 170 | */ 171 | public function signup_token_required() { 172 | return get_config('auth_enrolkey', 'tokenrequired'); 173 | } 174 | 175 | /** 176 | * Returns whether or not the captcha element is enabled, and the admin settings fulfil its requirements. 177 | * @return bool 178 | */ 179 | public function signup_captcha_enabled() { 180 | global $CFG; 181 | return !empty($CFG->recaptchapublickey) && !empty($CFG->recaptchaprivatekey) && get_config('auth_enrolkey', 'recaptcha'); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /classes/form/unsuspend_form.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Form which will unsuspend users with valid enrolkeys. 19 | * 20 | * @package auth_enrolkey 21 | * @copyright 2021 Nicholas Hoobin 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | /** 26 | * Form which will unsuspend users with valid enrolkeys. 27 | * 28 | * This provides an additional enrolment key field that will be validated upon signup. 29 | * 30 | * @copyright 2021 Nicholas Hoobin 31 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 32 | */ 33 | namespace auth_enrolkey\form; 34 | 35 | use auth_enrolkey\utility; 36 | use core_user; 37 | 38 | /** 39 | * Class for the unsuspend form. 40 | * 41 | * @package auth_enrolkey 42 | * @copyright 2021 Nicholas Hoobin 43 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 44 | */ 45 | class unsuspend_form extends \moodleform { 46 | 47 | /** 48 | * Creates the Moodle singup form, calls parent::definition(); 49 | */ 50 | public function definition() { 51 | global $CFG; 52 | 53 | $mform = $this->_form; 54 | 55 | $mform->addElement('text', 'signup_token', get_string('signup_field_title', 'auth_enrolkey')); 56 | 57 | $mform->setType('signup_token', PARAM_TEXT); 58 | $token = optional_param('signup_token', '', PARAM_TEXT); 59 | if (!empty($token)) { 60 | $mform->setDefault('signup_token', $token); 61 | } 62 | 63 | $mform->addRule('signup_token', get_string('signup_missing', 'auth_enrolkey'), 'required', null, 'client'); 64 | 65 | if (empty($CFG->createuserwithemail)) { 66 | $mform->addElement('text', 'username', get_string('username'), 'maxlength="100" size="12" autocapitalize="none"'); 67 | $mform->setType('username', PARAM_RAW); 68 | $mform->addRule('username', get_string('missingusername'), 'required', null, 'client'); 69 | } else { 70 | $mform->addElement('text', 'email', get_string('email'), 'maxlength="100" size="25"'); 71 | $mform->setType('email', core_user::get_property_type('email')); 72 | $mform->addRule('email', get_string('missingemail'), 'required', null, 'client'); 73 | $mform->setForceLtr('email'); 74 | } 75 | 76 | $mform->addElement('passwordunmask', 'password', get_string('password'), 'size="12"'); 77 | $mform->setType('password', core_user::get_property_type('password')); 78 | $mform->addRule('password', get_string('missingpassword'), 'required', null, 'client'); 79 | 80 | if (empty($CFG->createuserwithemail)) { 81 | $mform->addElement('text', 'email', get_string('email'), 'maxlength="100" size="25"'); 82 | $mform->setType('email', core_user::get_property_type('email')); 83 | $mform->addRule('email', get_string('missingemail'), 'required', null, 'client'); 84 | $mform->setForceLtr('email'); 85 | 86 | $mform->addElement('text', 'email2', get_string('emailagain'), 'maxlength="100" size="25"'); 87 | $mform->setType('email2', core_user::get_property_type('email')); 88 | $mform->addRule('email2', get_string('missingemail'), 'required', null, 'client'); 89 | $mform->setForceLtr('email2'); 90 | } 91 | 92 | $this->add_action_buttons(true, get_string('enrolkeyuse', 'auth_enrolkey')); 93 | } 94 | 95 | /** 96 | * Returns an array with fields that are invalid during signup. 97 | * This is used in /auth/enrolkey/unsuspend.php. 98 | * 99 | * @param array $data array of ("fieldname"=>value) of submitted data 100 | * @param array $files array of uploaded files "element_name"=>tmp_file_path 101 | * @return array of "element_name"=>"error_description" if there are errors, 102 | * or an empty array if everything is OK (true allowed for backwards compatibility too). 103 | */ 104 | public function validation($data, $files) { 105 | $errors = parent::validation($data, $files); 106 | 107 | $signuptoken = $data['signup_token']; 108 | $tokenisvalid = false; 109 | 110 | if ($signuptoken !== '') { 111 | // For any case where the token is populated, perform a lookup. 112 | $tokenisvalid = $this->check_database_for_signuptoken($signuptoken); 113 | 114 | if ($tokenisvalid === false) { 115 | $errors['signup_token'] = get_string('signup_token_invalid', 'auth_enrolkey'); 116 | } 117 | } else { 118 | // The form submission is an empty string, double check if the token is required. 119 | if ($this->signup_token_required()) { 120 | $errors['signup_token'] = get_string('signup_missing', 'auth_enrolkey'); 121 | } 122 | } 123 | 124 | // This is the check to un-suspend users. This user must exist, be suspended, and not deleted. 125 | // With a valid enrolkey token, and a user that exists, we will bypass the username/email form validation errors. 126 | // The next major function which is called will be auth_enrolkey user_signup(). 127 | if ($tokenisvalid && get_config('auth_enrolkey', 'unsuspendaccounts')) { 128 | $errors = $this->check_for_suspended_user_with_post_data($data, $errors); 129 | } 130 | 131 | return $errors; 132 | } 133 | 134 | /** 135 | * During the user signup page, the POST data for username and email is compared to the DB. 136 | * 137 | * @param array $data 138 | * @param array $errors 139 | * @return array 140 | */ 141 | private function check_for_suspended_user_with_post_data($data, $errors) { 142 | global $CFG; 143 | 144 | $user = utility::search_for_suspended_user($data); 145 | // A user exists with the same email and username. 146 | if (!$user) { 147 | if (empty($CFG->createuserwithemail)) { 148 | $errors['username'] = get_string('invalidusername'); 149 | } else { 150 | $errors['email'] = get_string('invalidemail'); 151 | } 152 | } 153 | 154 | if (!validate_internal_user_password($user, $data['password'])) { 155 | // Fail internal mform validation. Do not prompt an issue with the password. 156 | $errors['non_element00'] = 'invalid'; 157 | } 158 | 159 | // Else can't sign up, whatever $errors is returning will fail. eg, the same username, or email. 160 | return $errors; 161 | } 162 | 163 | /** 164 | * Checks the enrolment records for any matching self enrolment key. 165 | * 166 | * @param string $token Returns true on success. False on failure. 167 | * @return bool 168 | */ 169 | private function check_database_for_signuptoken($token) { 170 | global $DB; 171 | 172 | $selfenrolinstance = false; 173 | 174 | $instances = $DB->get_records('enrol', ['password' => $token, 'enrol' => 'self']); 175 | 176 | // There may be more than one enrolment instance configured with various dates to check against. 177 | foreach ($instances as $instance) { 178 | // There may be things that prevent self enrol, such as requiring a capability, or full course. 179 | // This should not be a blocker to account creation. The creation should pass, then report the error. 180 | if ($instance->status == ENROL_INSTANCE_ENABLED) { 181 | $selfenrolinstance = true; 182 | } 183 | } 184 | 185 | // Lookup group enrol keys. 186 | $instances = $DB->get_records_sql(" 187 | SELECT e.* 188 | FROM {groups} g 189 | JOIN {enrol} e ON e.courseid = g.courseid 190 | AND e.enrol = 'self' 191 | AND e.customint1 = 1 192 | WHERE g.enrolmentkey = ? 193 | ", [$token]); 194 | foreach ($instances as $instance) { 195 | if ($instance->status == ENROL_INSTANCE_ENABLED) { 196 | $selfenrolinstance = true; 197 | } 198 | } 199 | 200 | return $selfenrolinstance; 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /classes/output/renderer.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Auth Enrolkey renderer. 19 | * 20 | * @package auth_enrolkey 21 | * @copyright 2021 Nicholas Hoobin 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | namespace auth_enrolkey\output; 26 | 27 | 28 | defined('MOODLE_INTERNAL') || die; 29 | 30 | use auth_enrolkey\table\enrolkey_available_table; 31 | // We should extend the legacy renderer to maintain compatability with old style render function. 32 | require_once($CFG->dirroot . '/auth/enrolkey/renderer.php'); 33 | 34 | /** 35 | * Auth Enrolkey renederer. 36 | * 37 | * @package auth_enrolkey 38 | * @copyright 2021 Nicholas Hoobin 39 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 40 | */ 41 | class renderer extends \auth_enrolkey_renderer { 42 | 43 | /** 44 | * Render the HTML for the student quiz table. 45 | * 46 | * @param string $baseurl the base url to render the table on. 47 | * @return string $output HTML for the table. 48 | */ 49 | public static function render_available_table($baseurl) { 50 | $renderable = new enrolkey_available_table($baseurl); 51 | ob_start(); 52 | $renderable->out(50, false); 53 | $output = ob_get_contents(); 54 | ob_end_clean(); 55 | 56 | return $output; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /classes/persistent/enrolkey_cohort_mapping.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Class for mapping enrolkey to cohorts. 19 | * 20 | * @package auth_enrolkey 21 | * @copyright 2021 Nicholas Hoobin 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | namespace auth_enrolkey\persistent; 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | require_once($CFG->dirroot . '/cohort/lib.php'); 29 | 30 | use core\persistent; 31 | 32 | /** 33 | * Class for mapping enrolkey to corhots. 34 | * 35 | * @package auth_enrolkey 36 | * @copyright 2021 Nicholas Hoobin 37 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 38 | */ 39 | class enrolkey_cohort_mapping extends persistent { 40 | 41 | /** Table name for the persistent. */ 42 | const TABLE = 'auth_enrolkey_cohort'; 43 | 44 | /** 45 | * Return the definition of the properties of this model. 46 | * 47 | * @return array 48 | */ 49 | protected static function define_properties() { 50 | return [ 51 | 'enrolid' => ['type' => PARAM_INT], 52 | 'cohortid' => ['type' => PARAM_INT], 53 | ]; 54 | } 55 | 56 | /** 57 | * Get the records for the enrolid. 58 | * 59 | * @param int $enrolid The enrolid 60 | * @return enrolkey_cohort_mapping[] 61 | */ 62 | public static function get_records_by_enrolid($enrolid) { 63 | $records = self::get_records(['enrolid' => $enrolid]); 64 | 65 | $result = []; 66 | foreach ($records as $persistent) { 67 | $result[$persistent->get('cohortid')] = $persistent; 68 | } 69 | 70 | return $result; 71 | } 72 | 73 | /** 74 | * During auth.php->user_signup, this adds the user to the associated cohorts. 75 | * 76 | * @param \stdClass $user 77 | * @param array $availableenrolids 78 | */ 79 | public static function add_cohorts_during_signup($user, $availableenrolids) { 80 | foreach ($availableenrolids as $enrolid) { 81 | $records = self::get_records_by_enrolid($enrolid); 82 | 83 | foreach ($records as $cohortid => $persistent) { 84 | cohort_add_member($cohortid, $user->id); 85 | } 86 | } 87 | } 88 | 89 | /** 90 | * Returns moodle_url for managing the cohort. 91 | * 92 | * @return \moodle_url 93 | */ 94 | public function get_moodle_url() { 95 | $params = ['id' => $this->get('cohortid')]; 96 | $moodleurl = new \moodle_url('/cohort/view.php', $params); 97 | return $moodleurl; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /classes/persistent/enrolkey_profile_mapping.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Class for mapping enrolkey to profile fields. 19 | * 20 | * @package auth_enrolkey 21 | * @copyright 2021 Nicholas Hoobin 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | namespace auth_enrolkey\persistent; 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | require_once($CFG->dirroot . '/user/profile/lib.php'); 29 | 30 | use core\persistent; 31 | /** 32 | * Class for mapping enrolkey to corhots. 33 | * 34 | * @package auth_enrolkey 35 | * @copyright 2021 Nicholas Hoobin 36 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 | */ 38 | class enrolkey_profile_mapping extends persistent { 39 | 40 | /** Table name for the persistent. */ 41 | const TABLE = 'auth_enrolkey_profile'; 42 | 43 | /** 44 | * Return the definition of the properties of this model. 45 | * 46 | * @return array 47 | */ 48 | protected static function define_properties() { 49 | return [ 50 | 'enrolid' => ['type' => PARAM_INT], 51 | 'profilefieldname' => ['type' => PARAM_TEXT], 52 | 'profilefielddata' => ['type' => PARAM_TEXT], 53 | ]; 54 | } 55 | 56 | /** 57 | * Get the records for the enrolid. 58 | * 59 | * @param int $enrolid The enrolid 60 | * @return enrolkey_profile_mapping[] 61 | */ 62 | public static function get_records_by_enrolid($enrolid) { 63 | $records = self::get_records(['enrolid' => $enrolid]); 64 | 65 | $result = []; 66 | foreach ($records as $persistent) { 67 | $result[$persistent->get('profilefieldname')] = $persistent; 68 | } 69 | 70 | return $result; 71 | } 72 | 73 | /** 74 | * During auth.php->user_signup, this adds the forced user profile fields. 75 | * 76 | * @param \stdClass $user 77 | * @param array $availableenrolids 78 | */ 79 | public static function add_fields_during_signup($user, $availableenrolids) { 80 | // Obtain the existing user profile fields. 81 | $userfields = (array) profile_user_record($user->id); 82 | 83 | foreach ($userfields as $field => $value) { 84 | $key = 'profile_field_' . $field; 85 | $user->$key = $value; 86 | } 87 | 88 | foreach ($availableenrolids as $enid) { 89 | $records = self::get_records_by_enrolid($enid); 90 | foreach ($records as $persistent) { 91 | $field = $persistent->get('profilefieldname'); 92 | $data = $persistent->get_readable_value(); 93 | 94 | // Prevent saving null data to the profile fields. 95 | if (!is_null($data)) { 96 | $user->$field = $data; 97 | } 98 | } 99 | } 100 | 101 | // Do it. 102 | profile_save_data($user, true); 103 | } 104 | 105 | /** 106 | * Returns the field name without the prefix 'profile_field_'. 107 | * 108 | * @return string 109 | */ 110 | public function get_readable_name() { 111 | return str_replace('profile_field_', '', $this->get('profilefieldname')); 112 | } 113 | 114 | /** 115 | * Returns the data assocaited with a profile field. This may also return the value selected in the multipart 116 | * dropdown. 117 | * 118 | * @return mixed 119 | */ 120 | public function get_readable_value() { 121 | global $DB; 122 | 123 | $key = $this->get('profilefieldname'); 124 | $value = $this->get('profilefielddata'); 125 | 126 | $shortname = str_replace('profile_field_', '', $key); 127 | 128 | $select = $DB->sql_compare_text('shortname') . ' = ' . $DB->sql_compare_text(':shortname'); 129 | $select .= ' AND ' . $DB->sql_compare_text('datatype') . ' = ' . $DB->sql_compare_text(':datatype'); 130 | $params = [ 131 | 'shortname' => $shortname, 132 | 'datatype' => 'menu', 133 | ]; 134 | 135 | $record = $DB->get_record_select('user_info_field', $select, $params); 136 | 137 | if ($record) { 138 | // The param1 is a \n delimited list of dropdown choices. 139 | $values = explode("\n", $record->param1); 140 | if (array_key_exists((int) $value, $values)) { 141 | return $values[$value]; 142 | } 143 | } 144 | 145 | // Else. 146 | return $value; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /classes/persistent/enrolkey_redirect_mapping.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Class for mapping enrolkey to redirection urls. 19 | * 20 | * @package auth_enrolkey 21 | * @copyright 2021 Nicholas Hoobin 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | namespace auth_enrolkey\persistent; 25 | 26 | use core\persistent; 27 | /** 28 | * Class for mapping enrolkey to redirection urls. 29 | * 30 | * @package auth_enrolkey 31 | * @copyright 2021 Nicholas Hoobin 32 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 33 | */ 34 | class enrolkey_redirect_mapping extends persistent { 35 | 36 | /** Table name for the persistent. */ 37 | const TABLE = 'auth_enrolkey_redirect'; 38 | 39 | /** 40 | * Return the definition of the properties of this model. 41 | * 42 | * @return array 43 | */ 44 | protected static function define_properties() { 45 | return [ 46 | 'enrolid' => ['type' => PARAM_INT], 47 | 'url' => [ 48 | 'default' => '', 49 | 'type' => PARAM_URL, 50 | ], 51 | ]; 52 | } 53 | 54 | /** 55 | * Get the record for the enrol, or creates a new record if the enrolid does not match a row. 56 | * 57 | * @param int $enrolid The enrolid 58 | * @return enrolkey_redirect_mapping 59 | */ 60 | public static function get_record_by_enrolid($enrolid) { 61 | $persistent = self::get_record(['enrolid' => $enrolid]); 62 | 63 | if (!$persistent) { 64 | // Create the record for this enrolid if it does not exist. 65 | $persistent = new enrolkey_redirect_mapping(0, (object) ['enrolid' => $enrolid]); 66 | } 67 | return $persistent; 68 | } 69 | 70 | /** 71 | * During auth.php->user_signup, this redirects the user to a specified url. 72 | * 73 | * @param array $availableenrolids 74 | */ 75 | public static function redirect_during_signup($availableenrolids) { 76 | // TODO: Redirect weight. 77 | foreach ($availableenrolids as $enid) { 78 | $persistent = self::get_record_by_enrolid($enid); 79 | $url = $persistent->get('url'); 80 | if ($url != '') { 81 | redirect($persistent->get_moodle_url()); 82 | } 83 | } 84 | } 85 | 86 | /** 87 | * Returns moodle_url for value saved. 88 | * 89 | * @return \moodle_url 90 | */ 91 | public function get_moodle_url() { 92 | $moodleurl = new \moodle_url($this->get('url')); 93 | return $moodleurl; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /classes/privacy/provider.php: -------------------------------------------------------------------------------- 1 | . 16 | /** 17 | * Privacy provider. 18 | * 19 | * @package auth_enrolkey 20 | * @author Jason Lian (jasonlian@catalyst-au.net) 21 | * @copyright 2021 Catalyst IT 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | namespace auth_enrolkey\privacy; 25 | use core_privacy\local\metadata\collection; 26 | use core_privacy\local\request\approved_contextlist; 27 | use core_privacy\local\request\approved_userlist; 28 | use core_privacy\local\request\contextlist; 29 | use core_privacy\local\request\userlist; 30 | use core_privacy\local\request\writer; 31 | 32 | /** 33 | * Class provider 34 | */ 35 | class provider implements \core_privacy\local\metadata\provider, 36 | \core_privacy\local\request\core_userlist_provider, 37 | \core_privacy\local\request\plugin\provider { 38 | 39 | /** 40 | * Returns metadata about this plugin's privacy policy. 41 | * 42 | * @param collection $collection The initialised collection to add items to. 43 | * @return collection A listing of user data stored through this system. 44 | */ 45 | public static function get_metadata(collection $collection): collection { 46 | $collection->add_database_table( 47 | 'auth_enrolkey_redirect', 48 | ['usermodified' => 'privacy:metadata:auth_enrolkey_redirect:usermodified'], 49 | 'privacy:metadata:auth_enrolkey_redirect' 50 | ); 51 | $collection->add_database_table( 52 | 'auth_enrolkey_profile', 53 | ['usermodified' => 'privacy:metadata:auth_enrolkey_profile:usermodified'], 54 | 'privacy:metadata:auth_enrolkey_profile' 55 | ); 56 | $collection->add_database_table( 57 | 'auth_enrolkey_cohort', 58 | ['usermodified' => 'privacy:metadata:auth_enrolkey_cohort:usermodified'], 59 | 'privacy:metadata:auth_enrolkey_cohort' 60 | ); 61 | 62 | return $collection; 63 | } 64 | 65 | /** 66 | * Get the list of contexts that contain user information for the given user. 67 | * 68 | * @param int $userid the userid to search. 69 | * @return contextlist the contexts in which data is contained. 70 | */ 71 | public static function get_contexts_for_userid(int $userid): contextlist { 72 | $sql = "SELECT ctx.id FROM {context} ctx 73 | JOIN {auth_enrolkey_redirect} aer 74 | ON aer.usermodified = ctx.instanceid 75 | JOIN {auth_enrolkey_profile} aep 76 | ON aep.usermodified = ctx.instanceid 77 | JOIN {auth_enrolkey_cohort} aec 78 | ON aec.usermodified = ctx.instanceid 79 | WHERE ctx.instanceid = :userid 80 | AND ctx.contextlevel = :contextlevel"; 81 | $params = [ 82 | 'contextlevel' => CONTEXT_USER, 83 | 'userid' => $userid, 84 | ]; 85 | $contextlist = new contextlist(); 86 | $contextlist->add_from_sql($sql, $params); 87 | 88 | return $contextlist; 89 | } 90 | 91 | /** 92 | * Gets the list of users who have data with a context. 93 | * 94 | * @param userlist $userlist the userlist containing users who have data in this context. 95 | */ 96 | public static function get_users_in_context(userlist $userlist) { 97 | $context = $userlist->get_context(); 98 | 99 | if (!$context instanceof \context_user) { 100 | return; 101 | } 102 | 103 | // If current context is user, all users are contained within, get all users. 104 | $params = [ 105 | 'contextlevel' => CONTEXT_USER, 106 | 'contextid' => $context->id, 107 | ]; 108 | 109 | $sql = "SELECT usermodified as userid FROM {auth_enrolkey_redirect} aer 110 | JOIN {context} ctx 111 | ON ctx.instanceid = aer.usermodified 112 | AND ctx.contextlevel = :contextlevel 113 | WHERE aer.usermodified = :contextid"; 114 | $userlist->add_from_sql('userid', $sql, $params); 115 | 116 | $sql = "SELECT usermodified as userid FROM {auth_enrolkey_profile} aep 117 | JOIN {context} ctx 118 | ON ctx.instanceid = aep.usermodified 119 | AND ctx.contextlevel = :contextlevel 120 | WHERE aep.usermodified = :contextid"; 121 | $userlist->add_from_sql('userid', $sql, $params); 122 | 123 | $sql = "SELECT usermodified AS userid FROM {auth_enrolkey_cohort} aec 124 | JOIN {context} ctx 125 | ON ctx.instanceid = aec.usermodified 126 | AND ctx.contextlevel = :contextlevel 127 | WHERE aec.usermodified = :contextid"; 128 | $userlist->add_from_sql('userid', $sql, $params); 129 | } 130 | 131 | /** 132 | * Exports all data stored in provided contexts for user. 133 | * 134 | * @param approved_contextlist $contextlist the list of contexts to export for. 135 | */ 136 | public static function export_user_data(approved_contextlist $contextlist) { 137 | global $DB; 138 | 139 | // Export auth enrolkey linked accounts. 140 | $userid = $contextlist->get_user()->id; 141 | $context = \context_user::instance($userid); 142 | $params = [ 143 | 'contextlevel' => CONTEXT_USER, 144 | 'contextid' => $context->id, 145 | 'userid' => $userid, 146 | ]; 147 | $sql = "SELECT * FROM {user} u 148 | JOIN {context} ctx 149 | ON ctx.instanceid = u.id 150 | AND ctx.contextlevel = :contextlevel 151 | JOIN {auth_enrolkey_redirect} aer 152 | ON aer.usermodified = ue.userid 153 | JOIN {auth_enrolkey_profile} aep 154 | ON aep.usermodified = ue.userid 155 | JOIN {auth_enrolkey_cohort} aec 156 | ON aec.usermodified = ue.userid 157 | WHERE u.id = :userid"; 158 | if ($users = $DB->get_records_sql($sql, $params)) { 159 | foreach ($users as $user) { 160 | $data = (object) [ 161 | 'timecreated' => transform::datetime($user->timecreated), 162 | 'timemodified' => transform::datetime($user->timemodified), 163 | 'username' => $user->username, 164 | 'email' => $user->email, 165 | ]; 166 | writer::with_context($context)->export_data( 167 | [ 168 | get_string('privacy:metadata:auth_enrolkey', 'auth_enrolkey'), 169 | $user->id, 170 | ], 171 | $data 172 | ); 173 | } 174 | } 175 | } 176 | 177 | /** 178 | * Deletes data for all users in context. 179 | * 180 | * @param context $context The context to delete for. 181 | */ 182 | public static function delete_data_for_all_users_in_context(\context $context) { 183 | if ($context->contextlevel != CONTEXT_USER) { 184 | return; 185 | } 186 | static::delete_user_data($context->instanceid); 187 | } 188 | 189 | /** 190 | * Delete multiple users within a single context. 191 | * 192 | * @param approved_userlist $userlist The approved context and user information to delete information for. 193 | */ 194 | public static function delete_data_for_users(approved_userlist $userlist) { 195 | $context = $userlist->get_context(); 196 | 197 | if ($context instanceof \context_user) { 198 | static::delete_user_data($context->instanceid); 199 | } 200 | } 201 | 202 | /** 203 | * Delete all user data for this user only. 204 | * 205 | * @param approved_contextlist $contextlist The list of approved contexts for a user. 206 | */ 207 | public static function delete_data_for_user(approved_contextlist $contextlist) { 208 | if (empty($contextlist->count())) { 209 | return; 210 | } 211 | $userid = $contextlist->get_user()->id; 212 | foreach ($contextlist->get_contexts() as $context) { 213 | if ($context->contextlevel != CONTEXT_USER) { 214 | continue; 215 | } 216 | if ($context->instanceid == $userid) { 217 | // The $context->instanceid gives you the user ID. 218 | static::delete_user_data($context->instanceid); 219 | } 220 | } 221 | } 222 | 223 | /** 224 | * This does the deletion of user data for auth/enrolkey. 225 | * 226 | * @param int $userid The user ID 227 | */ 228 | protected static function delete_user_data(int $userid) { 229 | global $DB; 230 | 231 | // The $context->instanceid gives you the user ID. 232 | $DB->delete_records('auth_enrolkey_redirect', ['usermodified' => $userid]); 233 | $DB->delete_records('auth_enrolkey_profile', ['usermodified' => $userid]); 234 | $DB->delete_records('auth_enrolkey_cohort', ['usermodified' => $userid]); 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /classes/table/enrolkey_available_table.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Renderable table for listing available enrolkeys. 19 | * 20 | * @package auth_enrolkey 21 | * @copyright 2021 Nicholas Hoobin 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | namespace auth_enrolkey\table; 26 | 27 | defined('MOODLE_INTERNAL') || die; 28 | 29 | require_once($CFG->libdir . '/tablelib.php'); 30 | 31 | use auth_enrolkey\persistent\enrolkey_cohort_mapping; 32 | use auth_enrolkey\persistent\enrolkey_profile_mapping; 33 | use auth_enrolkey\persistent\enrolkey_redirect_mapping; 34 | use moodle_url; 35 | use table_sql; 36 | use renderable; 37 | 38 | /** 39 | * Renderable table for quiz dashboard users. 40 | * 41 | * @package auth_enrolkey 42 | * @copyright 2021 Nicholas Hoobin 43 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 44 | */ 45 | class enrolkey_available_table extends table_sql implements renderable { 46 | 47 | /** 48 | * report_table constructor. 49 | * 50 | * @param string $baseurl Base URL of the page that contains the table. 51 | * 52 | * @throws \coding_exception 53 | */ 54 | public function __construct(string $baseurl) { 55 | parent::__construct('local_enrolkey_available_table'); 56 | 57 | $this->set_attribute('id', 'local_enrolkey_available_table'); 58 | $this->set_attribute('class', 'generaltable generalbox'); 59 | $this->downloadable = false; 60 | $this->define_baseurl($baseurl); 61 | $this->allcohorts = cohort_get_all_cohorts(0, 5000, ''); 62 | 63 | $fields = "en.id, 64 | en.password, 65 | en.name as enrolkeyname, 66 | en.courseid, 67 | en.timecreated, 68 | en.timemodified, 69 | cc.fullname AS coursefullname, 70 | cc.shortname, 71 | cc.id AS courseid"; 72 | $from = '{enrol} en 73 | LEFT JOIN {course} cc ON en.courseid = cc.id'; 74 | 75 | $where = '1 = 1'; 76 | $where .= " AND en.password != '' 77 | AND en.password IS NOT NULL "; 78 | 79 | $this->set_sql($fields, $from, $where, []); 80 | 81 | // Define the headers and columns. 82 | $headers = []; 83 | $columns = []; 84 | 85 | $headers[] = get_string('th_enrolkeyname', 'auth_enrolkey'); 86 | $columns[] = 'enrolkeyname'; 87 | 88 | $headers[] = get_string('th_fullname', 'auth_enrolkey'); 89 | $columns[] = 'coursefullname'; 90 | 91 | $headers[] = get_string('th_cohorts', 'auth_enrolkey'); 92 | $columns[] = 'cohorts'; 93 | 94 | $headers[] = get_string('th_profilefields', 'auth_enrolkey'); 95 | $columns[] = 'profilefields'; 96 | 97 | $headers[] = get_string('th_redirecturl', 'auth_enrolkey'); 98 | $columns[] = 'redirecturl'; 99 | 100 | $this->define_columns($columns); 101 | $this->define_headers($headers); 102 | 103 | // Setup pagination. 104 | $this->pageable(false); 105 | $this->sortable(false); 106 | $this->collapsible(false); 107 | $this->column_nosort = ['actions']; 108 | } 109 | 110 | /** 111 | * Get content for cohorts column. 112 | * Displays a summary of the rules. 113 | * 114 | * @param \stdClass $row 115 | * @return string html used to display the field. 116 | */ 117 | public function col_cohorts($row) { 118 | global $OUTPUT; 119 | 120 | $content = ''; 121 | 122 | $cohortlist = enrolkey_cohort_mapping::get_records_by_enrolid($row->id); 123 | 124 | foreach ($cohortlist as $cohortid => $persistent) { 125 | if (array_key_exists($cohortid, $this->allcohorts['cohorts'])) { 126 | $cohort = $this->allcohorts['cohorts'][$cohortid]; 127 | $cohortname = $cohort->name . ' (' . $cohort->idnumber . ')'; 128 | $url = $persistent->get_moodle_url(); 129 | $link = \html_writer::link($url, $cohortname); 130 | $content .= \html_writer::div($link); 131 | } 132 | } 133 | 134 | $icon = $OUTPUT->render(new \pix_icon('i/edit', '')) 135 | . get_string('edit_cohort', 'auth_enrolkey') 136 | . \html_writer::empty_tag('br'); 137 | $url = new moodle_url('edit_cohort.php', ['id' => $row->id]); 138 | $content .= \html_writer::link($url, $icon, [ 139 | 'class' => 'action-icon edit', 140 | 'id' => 'admin-enrolkey-edit-' . $row->id, 141 | 'data-toggle' => 'tooltip', 142 | 'data-placement' => 'top', 143 | 'title' => 'Edit', 144 | ]); 145 | 146 | return $content; 147 | } 148 | 149 | /** 150 | * Get content for profilefields column. 151 | * Displays a summary of the rules. 152 | * 153 | * @param \stdClass $row 154 | * @return string html used to display the field. 155 | */ 156 | public function col_profilefields($row) { 157 | global $OUTPUT; 158 | 159 | $content = ''; 160 | 161 | $persistents = enrolkey_profile_mapping::get_records_by_enrolid($row->id); 162 | $fields = []; 163 | 164 | foreach ($persistents as $persistent) { 165 | $fields[] = [ 166 | 'name' => $persistent->get_readable_name(), 167 | 'value' => $persistent->get_readable_value(), 168 | ]; 169 | } 170 | 171 | $context = [ 172 | 'data' => ['fields' => $fields], 173 | ]; 174 | 175 | $content .= $OUTPUT->render_from_template('auth_enrolkey/enrolkey_profiletable', $context); 176 | 177 | $icon = $OUTPUT->render(new \pix_icon('i/edit', '')) 178 | . get_string('edit_profile', 'auth_enrolkey') 179 | . \html_writer::empty_tag('br'); 180 | $url = new moodle_url('edit_profile.php', ['id' => $row->id]); 181 | $content .= \html_writer::link($url, $icon, [ 182 | 'class' => 'action-icon edit', 183 | 'id' => 'admin-enrolkey-profile-' . $row->id, 184 | 'data-toggle' => 'tooltip', 185 | 'data-placement' => 'top', 186 | 'title' => 'Profile', 187 | ]); 188 | 189 | return $content; 190 | } 191 | 192 | /** 193 | * Get content for redirecturl column. 194 | * Displays a summary of the rules. 195 | * 196 | * @param \stdClass $row 197 | * @return string html used to display the field. 198 | */ 199 | public function col_redirecturl($row) { 200 | global $OUTPUT; 201 | 202 | $content = ''; 203 | 204 | $persistent = enrolkey_redirect_mapping::get_record_by_enrolid($row->id); 205 | $url = $persistent->get_moodle_url(); 206 | if ($url != '') { 207 | $content .= \html_writer::link($url, $url) 208 | . \html_writer::empty_tag('br'); 209 | } 210 | 211 | $icon = $OUTPUT->render(new \pix_icon('i/edit', '')) 212 | . get_string('edit_redirect', 'auth_enrolkey') 213 | . \html_writer::empty_tag('br'); 214 | $editurl = new moodle_url('edit_redirect.php', ['id' => $row->id]); 215 | $content .= \html_writer::link($editurl, $icon, [ 216 | 'class' => 'action-icon edit', 217 | 'id' => 'admin-enrolkey-redirect-' . $row->id, 218 | 'data-toggle' => 'tooltip', 219 | 'data-placement' => 'top', 220 | 'title' => 'Redirect', 221 | ]); 222 | 223 | return $content; 224 | } 225 | 226 | /** 227 | * Get content for enrolkeyname column. 228 | * 229 | * @param \stdClass $row 230 | * @return string html used to display the field. 231 | */ 232 | public function col_enrolkeyname($row) { 233 | $param = [ 234 | 'courseid' => $row->courseid, 235 | 'id' => $row->id, 236 | 'type' => 'self', 237 | ]; 238 | $url = new moodle_url('/enrol/editinstance.php', $param); 239 | 240 | return \html_writer::link($url, $row->enrolkeyname); 241 | } 242 | 243 | /** 244 | * Get content for coursefullname column. 245 | * 246 | * @param \stdClass $row 247 | * @return string html used to display the field. 248 | */ 249 | public function col_coursefullname($row) { 250 | $param = [ 251 | 'id' => $row->courseid, 252 | ]; 253 | $url = new moodle_url('/course/view.php', $param); 254 | 255 | return \html_writer::link($url, $row->coursefullname); 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /classes/utility.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Helper class for auth_enrolkey 19 | * 20 | * @package auth_enrolkey 21 | * @copyright 2021 Nicholas Hoobin 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | namespace auth_enrolkey; 26 | 27 | use moodle_database; 28 | 29 | /** 30 | * Helper class for auth_enrolkey 31 | * 32 | * @package auth_enrolkey 33 | * @copyright 2021 Nicholas Hoobin 34 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 | */ 36 | class utility { 37 | 38 | /** 39 | * Find the username / email combination of a user that is, not deleted, but suspended. 40 | * 41 | * @param array $data 42 | * @return \stdClass|bool The user record, or false. 43 | */ 44 | public static function search_for_suspended_user($data) { 45 | global $DB, $CFG; 46 | 47 | $params = [ 48 | 'suspended' => 1, 49 | 'deleted' => 0, 50 | 'mnethostid' => $CFG->mnet_localhost_id, 51 | 'auth' => 'enrolkey', 52 | ]; 53 | 54 | if (!empty($data['email'])) { 55 | $params['email'] = $data['email']; 56 | } 57 | 58 | if (!empty($data['username'])) { 59 | $params['username'] = $data['username']; 60 | } 61 | 62 | return $DB->get_record('user', $params, '*'); 63 | } 64 | 65 | /** 66 | * Given a $user object, this will unsuspend them. 67 | * 68 | * @param stdClass $user 69 | * @return bool Returns true if the user is unsuspended. 70 | */ 71 | public static function unsuspend_user($user) { 72 | if ($user->suspended == 1) { 73 | $user->suspended = 0; 74 | user_update_user($user, false, true); 75 | return true; 76 | } 77 | 78 | return false; 79 | } 80 | 81 | /** 82 | * Unsuspends the and enrols the $USER with the $enrolkey 83 | * 84 | * @param string $enrolkey 85 | * @param bool $checkuserenrolment 86 | * @return array 87 | */ 88 | public static function unsuspend_and_enrol_user(string $enrolkey, bool $checkuserenrolment = true) : array { 89 | global $DB; 90 | 91 | /** @var enrol_self_plugin $enrol */ 92 | $enrol = enrol_get_plugin('self'); 93 | $enrolplugins = self::get_enrol_plugins($DB, $enrolkey); 94 | $availableenrolids = []; 95 | $errors = []; 96 | foreach ($enrolplugins as $enrolplugin) { 97 | if (self::can_self_enrol($enrolplugin, $checkuserenrolment) === true) { 98 | $data = new \stdClass(); 99 | $data->enrolpassword = $enrolplugin->enrolmentkey ?? $enrolplugin->password; 100 | $enrol->enrol_self($enrolplugin, $data); 101 | $availableenrolids[] = $enrolplugin->id; 102 | } else { 103 | // Store error to output. 104 | $errors[$enrolplugin->courseid] = $enrol->can_self_enrol($enrolplugin); 105 | } 106 | } 107 | return [$availableenrolids, $errors]; 108 | } 109 | 110 | /** 111 | * Returns the list of enrolkey plugins which use the $enrolkey 112 | * 113 | * @param moodle_database $db 114 | * @param string $enrolkey 115 | * @return array 116 | */ 117 | public static function get_enrol_plugins(moodle_database $db, string $enrolkey) : array { 118 | // Password is the Enrolment key that is specified in the Self enrolment instance. 119 | $enrolplugins = $db->get_records('enrol', ['enrol' => 'self', 'password' => $enrolkey]); 120 | 121 | return array_merge($enrolplugins, $db->get_records_sql(" 122 | SELECT e.*, g.enrolmentkey 123 | FROM {groups} g 124 | JOIN {enrol} e ON e.courseid = g.courseid 125 | AND e.enrol = 'self' 126 | AND e.customint1 = 1 127 | WHERE g.enrolmentkey = ? 128 | ", [$enrolkey])); 129 | } 130 | 131 | /** 132 | * Checks if user can self enrol. Copied from enrol/self/lib.php. Modified to remove the check if user is already enroled 133 | * 134 | * @param \stdClass $instance enrolment instance 135 | * @param bool $checkuserenrolment if true will check if user enrolment is inactive. 136 | * used by navigation to improve performance. 137 | * @return bool|string true if successful, else error message or false. 138 | */ 139 | public static function can_self_enrol(\stdClass $instance, $checkuserenrolment = true) { 140 | global $CFG, $DB, $USER; 141 | 142 | if ($checkuserenrolment) { 143 | if (isguestuser() || !isloggedin()) { 144 | // Can not enrol guests or unauthenticated users. 145 | return get_string('noguestaccess', 'enrol') . ' ' . \html_writer::link( 146 | get_login_url(), 147 | get_string('login', 'core'), 148 | ['class' => 'btn btn-default'] 149 | ); 150 | } 151 | // Check if user is already enroled. 152 | if ($DB->get_record('user_enrolments', ['userid' => $USER->id, 'enrolid' => $instance->id])) { 153 | return get_string('canntenrol', 'enrol_self'); 154 | } 155 | } 156 | 157 | if ($instance->status != ENROL_INSTANCE_ENABLED) { 158 | return get_string('canntenrol', 'enrol_self'); 159 | } 160 | 161 | if ($instance->enrolstartdate != 0 && $instance->enrolstartdate > time()) { 162 | return get_string('canntenrolearly', 'enrol_self', userdate($instance->enrolstartdate)); 163 | } 164 | 165 | if ($instance->enrolenddate != 0 && $instance->enrolenddate < time()) { 166 | return get_string('canntenrollate', 'enrol_self', userdate($instance->enrolenddate)); 167 | } 168 | 169 | if (!$instance->customint6) { 170 | // New enrols not allowed. 171 | return get_string('canntenrol', 'enrol_self'); 172 | } 173 | 174 | if ($checkuserenrolment) { 175 | if ($DB->record_exists('user_enrolments', ['userid' => $USER->id, 'enrolid' => $instance->id])) { 176 | return get_string('canntenrol', 'enrol_self'); 177 | } 178 | } 179 | 180 | if ($instance->customint3 > 0) { 181 | // Max enrol limit specified. 182 | $count = $DB->count_records('user_enrolments', ['enrolid' => $instance->id]); 183 | if ($count >= $instance->customint3) { 184 | // Bad luck, no more self enrolments here. 185 | return get_string('maxenrolledreached', 'enrol_self'); 186 | } 187 | } 188 | 189 | if ($instance->customint5) { 190 | require_once("$CFG->dirroot/cohort/lib.php"); 191 | if (!cohort_is_member($instance->customint5, $USER->id)) { 192 | $cohort = $DB->get_record('cohort', ['id' => $instance->customint5]); 193 | if (!$cohort) { 194 | return null; 195 | } 196 | $a = format_string($cohort->name, true, ['context' => context::instance_by_id($cohort->contextid)]); 197 | return markdown_to_html(get_string('cohortnonmemberinfo', 'enrol_self', $a)); 198 | } 199 | } 200 | 201 | return true; 202 | } 203 | 204 | /** 205 | * Updates user profile and cohort according to the set enrolkey mappings. 206 | * 207 | * @param \stdClass $user the user to update 208 | * @param array $availableenrolids the enrol ids for the enrolkey used 209 | */ 210 | public static function update_user($user, $availableenrolids) { 211 | // Update user profile fields based on the enrolkey used. 212 | \auth_enrolkey\persistent\enrolkey_profile_mapping::add_fields_during_signup($user, $availableenrolids); 213 | 214 | // Assign this user to corhots based on the enrolkey used. 215 | \auth_enrolkey\persistent\enrolkey_cohort_mapping::add_cohorts_during_signup($user, $availableenrolids); 216 | 217 | // If enabled, run a cohort sync to force dynamic cohorts to update. 218 | if (get_config('auth_enrolkey', 'totaracohortsync') && 219 | function_exists('totara_cohort_check_and_update_dynamic_cohort_members')) { 220 | $trace = new \null_progress_trace(); 221 | // This may be a perfomance hog. 222 | totara_cohort_check_and_update_dynamic_cohort_members(null, $trace); 223 | } 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /db/install.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 |
50 |
51 |
52 | -------------------------------------------------------------------------------- /db/upgrade.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Enrolkey authentication plugin upgrade code 19 | * 20 | * @package auth_enrolkey 21 | * @copyright 2021 Nicholas Hoobin 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | /** 26 | * Upgrade function 27 | * 28 | * @param int $oldversion the version we are upgrading from 29 | * @return bool result 30 | */ 31 | function xmldb_auth_enrolkey_upgrade($oldversion) { 32 | global $DB; 33 | 34 | $dbman = $DB->get_manager(); 35 | 36 | if ($oldversion < 2021021700.01) { 37 | // Define table auth_enrolkey_redirect to be created. 38 | $table = new xmldb_table('auth_enrolkey_redirect'); 39 | 40 | // Adding fields to table auth_enrolkey_redirect. 41 | $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 42 | $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '18', null, XMLDB_NOTNULL, null, null); 43 | $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '18', null, XMLDB_NOTNULL, null, null); 44 | $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '18', null, XMLDB_NOTNULL, null, null); 45 | $table->add_field('enrolid', XMLDB_TYPE_INTEGER, '18', null, null, null, null); 46 | $table->add_field('url', XMLDB_TYPE_TEXT, null, null, null, null, null); 47 | 48 | // Adding keys to table auth_enrolkey_redirect. 49 | $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); 50 | $table->add_key('usermodified_key', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); 51 | 52 | // Conditionally launch create table for auth_enrolkey_redirect. 53 | if (!$dbman->table_exists($table)) { 54 | $dbman->create_table($table); 55 | } 56 | 57 | // Enrolkey savepoint reached. 58 | upgrade_plugin_savepoint(true, 2021021700.01, 'auth', 'enrolkey'); 59 | } 60 | 61 | if ($oldversion < 2021021700.02) { 62 | // Define table auth_enrolkey_profile to be created. 63 | $table = new xmldb_table('auth_enrolkey_profile'); 64 | 65 | // Adding fields to table auth_enrolkey_cohort. 66 | $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 67 | $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '18', null, XMLDB_NOTNULL, null, null); 68 | $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '18', null, XMLDB_NOTNULL, null, null); 69 | $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '18', null, XMLDB_NOTNULL, null, null); 70 | $table->add_field('enrolid', XMLDB_TYPE_INTEGER, '18', null, null, null, null); 71 | $table->add_field('profilefieldname', XMLDB_TYPE_TEXT, null, null, null, null, null); 72 | $table->add_field('profilefielddata', XMLDB_TYPE_TEXT, null, null, null, null, null); 73 | 74 | // Adding keys to table auth_enrolkey_redirect. 75 | $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); 76 | $table->add_key('usermodified_key', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); 77 | 78 | // Conditionally launch create table for auth_enrolkey_redirect. 79 | if (!$dbman->table_exists($table)) { 80 | $dbman->create_table($table); 81 | } 82 | 83 | // Enrolkey savepoint reached. 84 | upgrade_plugin_savepoint(true, 2021021700.02, 'auth', 'enrolkey'); 85 | } 86 | 87 | if ($oldversion < 2021021700.03) { 88 | // Define table auth_enrolkey_cohort to be created. 89 | $table = new xmldb_table('auth_enrolkey_cohort'); 90 | 91 | // Adding fields to table auth_enrolkey_cohort. 92 | $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 93 | $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '18', null, XMLDB_NOTNULL, null, null); 94 | $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '18', null, XMLDB_NOTNULL, null, null); 95 | $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '18', null, XMLDB_NOTNULL, null, null); 96 | $table->add_field('enrolid', XMLDB_TYPE_INTEGER, '18', null, null, null, null); 97 | $table->add_field('cohortid', XMLDB_TYPE_INTEGER, '18', null, null, null, null); 98 | 99 | // Adding keys to table auth_enrolkey_redirect. 100 | $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); 101 | $table->add_key('usermodified_key', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); 102 | 103 | // Conditionally launch create table for auth_enrolkey_redirect. 104 | if (!$dbman->table_exists($table)) { 105 | $dbman->create_table($table); 106 | } 107 | 108 | // Enrolkey savepoint reached. 109 | upgrade_plugin_savepoint(true, 2021021700.03, 'auth', 'enrolkey'); 110 | } 111 | 112 | return true; 113 | } 114 | -------------------------------------------------------------------------------- /edit_cohort.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Configure Enrolkey cohort mapping 19 | * 20 | * @package auth_enrolkey 21 | * @author Nicholas Hoobin 22 | * @copyright 2021 Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | 27 | use auth_enrolkey\form\enrolkey_cohort_form; 28 | use auth_enrolkey\persistent\enrolkey_cohort_mapping; 29 | 30 | require_once(__DIR__.'/../../config.php'); 31 | require_once($CFG->libdir.'/adminlib.php'); 32 | 33 | admin_externalpage_setup('auth_enrolkey_manage'); 34 | 35 | $id = required_param('id', PARAM_INT); 36 | $baseurl = new moodle_url('/auth/enrolkey/edit_cohort.php', ['id' => $id]); 37 | 38 | $PAGE->set_url($baseurl); 39 | $PAGE->set_title(get_string('title_cohort', 'auth_enrolkey')); 40 | $output = $PAGE->get_renderer('auth_enrolkey'); 41 | 42 | $cohortlist = cohort_get_all_cohorts(0, 5000, ''); 43 | 44 | $records = enrolkey_cohort_mapping::get_records_by_enrolid($id); 45 | 46 | $customdata = ['cohorts' => $cohortlist]; 47 | $form = new enrolkey_cohort_form($baseurl, $customdata); 48 | 49 | // Get the data. This ensures that the form was validated. 50 | if ($form && $form->is_cancelled()) { 51 | redirect(new moodle_url('/auth/enrolkey/manage.php')); 52 | } else if (($data = $form->get_data())) { 53 | $savedcohortids = []; 54 | 55 | foreach ($data->cohortids as $cid) { 56 | $savedcohortids[$cid] = $cid; 57 | 58 | // The cohort is not in out list of records, so we will create it. 59 | if (!array_key_exists($cid, $records)) { 60 | $pdata = (object) [ 61 | 'enrolid' => $id, 62 | 'cohortid' => $cid, 63 | ]; 64 | $persistent = new enrolkey_cohort_mapping(0, $pdata); 65 | $persistent->save(); 66 | 67 | // Add to the list of records which is used later. 68 | $records[$cid] = $persistent; 69 | } 70 | } 71 | 72 | // Cleanup older persistents. 73 | foreach ($records as $cid => $persistent) { 74 | if (!array_key_exists($cid, $savedcohortids)) { 75 | $persistent->delete(); 76 | } 77 | } 78 | 79 | redirect(new moodle_url('/auth/enrolkey/manage.php')); 80 | } 81 | 82 | echo $output->header(); 83 | echo $output->heading(get_string('title_cohort', 'auth_enrolkey')); 84 | $form->set_autocomplete_data($records); 85 | $form->display(); 86 | echo $output->footer(); 87 | -------------------------------------------------------------------------------- /edit_profile.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Configure Enrolkey profile mapping 19 | * 20 | * @package auth_enrolkey 21 | * @author Nicholas Hoobin 22 | * @copyright 2021 Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | 27 | use auth_enrolkey\form\enrolkey_profile_form; 28 | use auth_enrolkey\persistent\enrolkey_profile_mapping; 29 | 30 | require_once(__DIR__.'/../../config.php'); 31 | require_once($CFG->libdir.'/adminlib.php'); 32 | 33 | admin_externalpage_setup('auth_enrolkey_manage'); 34 | 35 | $enrolid = required_param('id', PARAM_INT); 36 | $baseurl = new moodle_url('/auth/enrolkey/edit_profile.php', ['id' => $enrolid]); 37 | 38 | $PAGE->set_url($baseurl); 39 | $PAGE->set_title(get_string('title_profile', 'auth_enrolkey')); 40 | $output = $PAGE->get_renderer('auth_enrolkey'); 41 | 42 | $records = enrolkey_profile_mapping::get_records_by_enrolid($enrolid); 43 | 44 | $customdata = ['currentdata' => $records]; 45 | $form = new enrolkey_profile_form($baseurl, $customdata); 46 | 47 | // Get the data. This ensures that the form was validated. 48 | if ($form && $form->is_cancelled()) { 49 | redirect(new moodle_url('/auth/enrolkey/manage.php')); 50 | } else if (optional_param('resetbutton', 0, PARAM_ALPHA)) { 51 | foreach ($records as $persistent) { 52 | $persistent->delete(); 53 | unset($records, $persistent); 54 | } 55 | redirect($baseurl); 56 | } else if (($data = $form->get_data())) { 57 | $ignore = [ 58 | 'submitbutton', 59 | 'cancelbutton', 60 | 'resetbutton', 61 | ]; 62 | 63 | foreach ($data as $key => $value) { 64 | // Ignore the buttons. 65 | if (in_array($key, $ignore)) { 66 | continue; 67 | } 68 | 69 | // Ignore the headers. 70 | if (preg_match("/^mform_isexpanded_id.*/", $key, $matches)) { 71 | continue; 72 | } 73 | 74 | if ($value == '') { 75 | // Delete the persistent if it exists. 76 | if (array_key_exists($key, $records)) { 77 | $persistent = $records[$key]; 78 | $persistent->delete(); 79 | } 80 | } else if (is_array($value)) { 81 | // Special case for 'interests' tag list and potentially others. 82 | // TODO: Revisit this? 83 | null; 84 | } else { 85 | // Update an existing persistent record. 86 | if (array_key_exists($key, $records)) { 87 | $persistent = $records[$key]; 88 | $persistent->set('profilefielddata', $value); 89 | $persistent->update(); 90 | } else { 91 | // Create the persistent. 92 | $pdata = (object) [ 93 | 'enrolid' => $enrolid, 94 | 'profilefieldname' => $key, 95 | 'profilefielddata' => $value, 96 | ]; 97 | $persistent = new enrolkey_profile_mapping(0, $pdata); 98 | $persistent->save(); 99 | } 100 | } 101 | } 102 | 103 | redirect(new moodle_url('/auth/enrolkey/manage.php')); 104 | } 105 | 106 | 107 | echo $output->header(); 108 | echo $output->heading(get_string('title_profile', 'auth_enrolkey')); 109 | $form->set_autocomplete_data($records); 110 | $form->display(); 111 | echo $output->footer(); 112 | -------------------------------------------------------------------------------- /edit_redirect.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Configure Enrolkey redirect mapping 19 | * 20 | * @package auth_enrolkey 21 | * @author Nicholas Hoobin 22 | * @copyright 2021 Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | use auth_enrolkey\form\enrolkey_redirect_form; 27 | use auth_enrolkey\persistent\enrolkey_redirect_mapping; 28 | 29 | require_once(__DIR__.'/../../config.php'); 30 | require_once($CFG->libdir.'/adminlib.php'); 31 | 32 | admin_externalpage_setup('auth_enrolkey_manage'); 33 | 34 | $id = required_param('id', PARAM_INT); 35 | $baseurl = new moodle_url('/auth/enrolkey/edit_redirect.php', ['id' => $id]); 36 | 37 | $PAGE->set_url($baseurl); 38 | $PAGE->set_title(get_string('title_redirect', 'auth_enrolkey')); 39 | $PAGE->set_heading(get_string('title_redirect', 'auth_enrolkey')); 40 | $output = $PAGE->get_renderer('auth_enrolkey'); 41 | 42 | $persistent = enrolkey_redirect_mapping::get_record_by_enrolid($id); 43 | 44 | // Create the form instance. We need to use the current URL and the custom data. 45 | $customdata = [ 46 | 'persistent' => $persistent, 47 | 'enrolid' => $id, 48 | ]; 49 | 50 | $form = new enrolkey_redirect_form($baseurl, $customdata); 51 | 52 | // Get the data. This ensures that the form was validated. 53 | if ($form && $form->is_cancelled()) { 54 | redirect(new moodle_url('/auth/enrolkey/manage.php')); 55 | } else if (($data = $form->get_data())) { 56 | try { 57 | if (empty($data->id)) { 58 | $persistent = enrolkey_redirect_mapping::get_record_by_enrolid($id); 59 | $persistent->set('url', $data->url); 60 | $persistent->create(); 61 | } else { 62 | $persistent->from_record($data); 63 | if ($data->url == '') { 64 | $persistent->delete(); 65 | } else { 66 | $persistent->update(); 67 | } 68 | } 69 | \core\notification::success(get_string('changessaved')); 70 | } catch (Exception $e) { 71 | \core\notification::error($e->getMessage()); 72 | } 73 | 74 | redirect(new moodle_url('/auth/enrolkey/manage.php')); 75 | } 76 | 77 | echo $output->header(); 78 | echo $output->heading(get_string('title_redirect', 'auth_enrolkey')); 79 | $form->display(); 80 | echo $output->footer(); 81 | -------------------------------------------------------------------------------- /lang/en/auth_enrolkey.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Strings for component 'auth_enrolkey', language 'en'. 19 | * 20 | * @package auth_enrolkey 21 | * @copyright 2016 Nicholas Hoobin (nicholashoobin@catalyst-au.net) 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | $string['cohortsync'] = 'Sync audiences on signup'; 26 | $string['cohortsync_description'] = 'Sync system audiences when a user signs up. This removes the delay from cron running and doing this task. Warning: this may cause the signup process to be slower.'; 27 | $string['description'] = 'This provides Enrolment key based self-registration'; 28 | $string['edit_profile'] = 'Edit fields'; 29 | $string['edit_redirect'] = 'Edit URL'; 30 | $string['enrolkeyuse'] = 'Use new enrolment key'; 31 | $string['errorenrolling'] = 'There was an error enrolling in course \'{$a->course}\'. The error message is: {$a->err}'; 32 | $string['heading_unsuspend'] = 'Your account may be suspended, please enter an enrolment key'; 33 | $string['label_cohortselect'] = 'Select cohorts'; 34 | $string['label_cohortselect_help'] = 'Search cohort names and IDs in this field.'; 35 | $string['label_cohortselect_empty'] = 'No cohorts selected'; 36 | $string['label_redirection'] = 'Redirection URL'; 37 | $string['label_redirection_help'] = 'The URL entered here will redirect the user at the end of their self sign-up.
38 | This field will accept absolute and relative urls.
39 | Please remember to include the initial slash / when using a relative URL. 40 |
    41 |
  • Relative: /course/view.php?id=5
  • 42 |
  • Absolute: http://perhaps.your.intranet/some/page
  • 43 |
44 | 45 | '; 46 | $string['settings_heading'] = 'General settings'; 47 | $string['settings_content'] = '

Enrolment key based self-registration enables a user to create their own account via a \'Create new account\' button on the login page. The user then receives an email containing a secure link to a page where they can confirm their account. Future logins just check the username and password against the stored values in the Moodle database.

During self-registration if an enrolment key is entered in the enrolment key field then it will proceed to automatically enrol the new user into any course that it matches. The keys are enabled in (Course administration > Users > Enrolment methods > Add method > Self enrolment).

Note: In addition to enabling the plugin, Enrolment key based self-registration must also be selected from the self registration drop-down menu on the \'Manage authentication\' page.

'; 48 | $string['settings_visible_description'] = 'Adds a new form element to the sign-up page for self-registration users. This will be checked against available enrolment keys and enrol the user to the matching courses'; 49 | $string['settings_required_description'] = 'The enrolment key will be a required field for validation'; 50 | $string['settings_visible_title'] = 'Enable enrolment key element'; 51 | $string['settings_required_title'] = 'Require enrolment key for validation'; 52 | $string['settings_email_title'] = 'Require email confirmation'; 53 | $string['settings_email_description'] = 'Force users to confirm their account with an email before accessing enrolled courses. 54 |
    55 |
  • No - No email confirmation required.
  • 56 |
  • Yes - Access will be granted after users confirm their account via email.
  • 57 |
  • Partial - Initial access is granted. However, users must confirm their account via email before next login attempt.
  • 58 |
59 | '; 60 | $string['settings_partial'] = 'Partial'; 61 | $string['signup_failure'] = 'Opps! Something went wrong, and you may not have been enrolled properly. Go to Home'; 62 | $string['signup_field_title'] = 'Enrolment key'; 63 | $string['signup_token_invalid'] = 'The enrolment key you have entered is invalid'; 64 | $string['signup_missing'] = 'Missing enrolment key'; 65 | $string['menumanage'] = 'Manage enrolkey cohort rules'; 66 | $string['menusettings'] = 'Enrolkey settings'; 67 | $string['noemail'] = 'Tried to send you an email but failed!'; 68 | $string['signup_view'] = 'Course enrolment'; 69 | $string['signup_view_message_basic'] = 'You have been enrolled as a {$a->role} into the course \'{$a->course}\''; 70 | $string['signup_view_message_basic_dates'] = 'You have enrolled into {$a->course} as a {$a->role}. href}>Click here to view the course.
Course starts: {$a->startdate}
Course ends: {$a->enddate}'; 71 | $string['signup_view_message_basic_dates_startonly'] = 'You have enrolled into {$a->course} as a {$a->role}. href}>Click here to view the course.
Course starts: {$a->startdate}'; 72 | $string['signup_view_message_basic_dates_endonly'] = 'You have enrolled into {$a->course} as a {$a->role}. href}>Click here to view the course.
Course ends: {$a->enddate}'; 73 | $string['signup_auth_instructions'] = 'Hi! For full access to courses you\'ll need to take 74 | a minute to create a new account for yourself on this web site. 75 | Each of the individual courses may also have a one-time 76 | "enrolment key", which you can use during this sign up: 77 |
    78 |
  1. Fill out the New Account form with your details.
  2. 79 |
  3. You will be prompted for an "enrolment key" - use the one 80 | that your teacher has given you. This will "enrol" you in the 81 | course.
  4. 82 |
  5. Your account will be created and you will be logged in.
  6. 83 |
  7. You can now access the full course for this session.
  8. 84 |
  9. An email has also been immediately sent to your email address.
  10. 85 |
  11. Read your email, and click on the web link it contains.
  12. 86 |
  13. From now on you will only need to enter your personal 87 | username and password (in the form on this page) to log in 88 | and access any course you have enrolled in.
  14. 89 |
'; 90 | $string['suspendeduseratsignup'] = '
Perhaps your account exists but was suspended? Please sign up with a new enrolkey here'; 91 | $string['th_cohorts'] = 'Assigned cohorts'; 92 | $string['th_enrolkeyname'] = 'Enrolkey name'; 93 | $string['th_fullname'] = 'Course fullname'; 94 | $string['th_profilefields'] = 'Profile fields'; 95 | $string['th_redirecturl'] = 'Redirection URL'; 96 | $string['title_cohort'] = 'Edit cohort assignment'; 97 | $string['title_profile'] = 'Edit profile fields'; 98 | $string['title_redirect'] = 'Edit redirection URL'; 99 | $string['title_unsuspend'] = 'Suspended account'; 100 | $string['edit_cohort'] = 'Edit assignment'; 101 | $string['unsuspendaccounts'] = 'Un-suspend accounts with a valid enrolkey'; 102 | $string['unsuspendaccounts_description'] = 'On the login, if a user is suspended, and is using the enrolkey authentication type, redirect them to an intermediate page which asks for their username, password and enrolkey to un-suspend them.'; 103 | $string['recaptcha'] = 'Adds a visual/audio confirmation form element to the sign-up page for self-registering users. This protects your site against spammers and contributes to a worthwhile cause. See http://www.google.com/recaptcha for more details.'; 104 | $string['recaptcha_key'] = 'Enable reCAPTCHA element'; 105 | $string['pluginname'] = 'Enrolment key based self-registration'; 106 | $string['privacy:metadata'] = 'The auth enrolkey plugin does not store any personal data.'; 107 | $string['privacy:metadata:auth_enrolkey_redirect'] = 'The auth enrolkey redirect.'; 108 | $string['privacy:metadata:auth_enrolkey_profile'] = 'The auth enrolkey plugin profile.'; 109 | $string['privacy:metadata:auth_enrolkey_cohort'] = 'The auth enrolkey plugin cohort.'; 110 | $string['privacy:metadata:auth_enrolkey'] = 'The auth enrolkey plugin.'; 111 | $string['privacy:metadata:auth_enrolkey_redirect:usermodified'] = 'The auth enrolkey redirect user id.'; 112 | $string['privacy:metadata:auth_enrolkey_profile:usermodified'] = 'The auth enrolkey profile user id'; 113 | $string['privacy:metadata:auth_enrolkey_cohort:usermodified'] = 'The auth enrolkey cohort user id'; 114 | -------------------------------------------------------------------------------- /lib.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Enrolkey core hooks 19 | * 20 | * @package auth_enrolkey 21 | * @copyright 2023 Matthew Hilton 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die; 26 | 27 | use auth_enrolkey\utility; 28 | require_once($CFG->dirroot . '/login/lib.php'); 29 | 30 | /** 31 | * Post forgot password request hook 32 | * 33 | * This is to send password reset emails to suspended users based on if a config is enabled. 34 | * 35 | * @param object $data password forget request data 36 | */ 37 | function auth_enrolkey_post_forgot_password_requests($data) { 38 | // This config allows suspended users to unsuspend their accounts if they provide a valid enrolkey. 39 | // However, if they forget their password, they cannot login to do this. 40 | // So we hook in here and if the config is enabled and they ARE suspended, we send them a password email anyway. 41 | if (empty(get_config('auth_enrolkey', 'unsuspendaccounts'))) { 42 | return; 43 | } 44 | 45 | $user = utility::search_for_suspended_user((array) $data); 46 | 47 | if (empty($user) || $user->auth != "enrolkey") { 48 | return; 49 | } 50 | 51 | // Make the user appear unsuspended so the email is successfully sent. 52 | $user->suspended = 0; 53 | 54 | $resetrecord = core_login_generate_password_reset($user); 55 | send_password_change_confirmation_email($user, $resetrecord); 56 | } 57 | 58 | /** 59 | * Post password set hook 60 | * 61 | * This is used to logout users who reset their password while being suspended. 62 | * Otherwise they are logged in, but still suspended. 63 | * We log them out so they can use the unsuspend.php page in conjunction with their enrolkey. 64 | * @param object $data 65 | */ 66 | function auth_enrolkey_post_set_password_requests($data) { 67 | global $USER; 68 | 69 | if ($USER->auth != 'enrolkey' || empty($USER->suspended)) { 70 | return; 71 | } 72 | 73 | // Log them out immediately. 74 | // Required since resetting password logs you in, 75 | // but the user is still suspended, so they get stuck in a halfway state. 76 | require_logout(); 77 | 78 | // Redirect them to the unsuspend page afterwards. 79 | // So they can enter their new details and enrolkey and unsuspend themselves. 80 | if (!PHPUNIT_TEST) { 81 | redirect(new moodle_url('/auth/enrolkey/unsuspend.php')); 82 | } 83 | } 84 | 85 | -------------------------------------------------------------------------------- /manage.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Configure Enrolkey mapping 19 | * 20 | * @package auth_enrolkey 21 | * @author Nicholas Hoobin 22 | * @copyright 2021 Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | 27 | require_once(__DIR__.'/../../config.php'); 28 | require_once($CFG->libdir.'/adminlib.php'); 29 | 30 | 31 | admin_externalpage_setup('auth_enrolkey_manage'); 32 | $baseurl = new moodle_url('/auth/enrolkey/manage.php'); 33 | 34 | $PAGE->set_url($baseurl); 35 | $output = $PAGE->get_renderer('auth_enrolkey'); 36 | 37 | echo $output->header(); 38 | 39 | $o = $output->render_available_table($baseurl); 40 | echo $o; 41 | 42 | echo $output->footer(); 43 | -------------------------------------------------------------------------------- /renderer.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Enrolment key renderer 19 | * 20 | * @package auth_enrolkey 21 | * @copyright 2018 Darko Miletic (darko.miletic@gmail.com) 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | /** 26 | * Class auth_enrolkey_renderer 27 | * 28 | * @package auth_enrolkey 29 | * @copyright 2018 Darko Miletic (darko.miletic@gmail.com) 30 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 31 | */ 32 | class auth_enrolkey_renderer extends plugin_renderer_base { 33 | 34 | /** 35 | * Render the signup form. 36 | * 37 | * @param login_signup_form $form 38 | * @return bool|string 39 | * @throws moodle_exception 40 | */ 41 | public function render_enrolkey_signup_form($form) { 42 | $context = $form->export_for_template($this); 43 | 44 | return $this->render_from_template('core/signup_form_layout', $context); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /settings.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Enrolment key based self-registration settings page 19 | * 20 | * @package auth_enrolkey 21 | * @copyright 2016 Nicholas Hoobin (nicholashoobin@catalyst-au.net) 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die; 26 | 27 | if ($hassiteconfig) { 28 | require_once($CFG->dirroot . '/auth/enrolkey/auth.php'); 29 | 30 | $options = [get_string('no'), get_string('yes')]; 31 | 32 | $settings->visiblename = get_string('menusettings', 'auth_enrolkey'); 33 | 34 | $settings->add(new admin_setting_heading('auth_enrolkey_heading', get_string('settings_heading', 'auth_enrolkey'), 35 | get_string('settings_content', 'auth_enrolkey'))); 36 | 37 | $settings->add(new admin_setting_configselect('auth_enrolkey/tokenrequired', 38 | get_string('settings_required_title', 'auth_enrolkey'), 39 | get_string('settings_required_description', 'auth_enrolkey'), 1, $options)); 40 | 41 | $settings->add(new admin_setting_configselect('auth_enrolkey/recaptcha', 42 | get_string('recaptcha_key', 'auth_enrolkey'), 43 | get_string('recaptcha', 'auth_enrolkey'), 0, $options)); 44 | 45 | $optionspartial = $options; 46 | $optionspartial[] = get_string('settings_partial', 'auth_enrolkey'); 47 | $settings->add(new admin_setting_configselect('auth_enrolkey/emailconfirmation', 48 | get_string('settings_email_title', 'auth_enrolkey'), 49 | get_string('settings_email_description', 'auth_enrolkey'), 0, $optionspartial)); 50 | 51 | $settings->add(new admin_setting_configselect('auth_enrolkey/unsuspendaccounts', 52 | get_string('unsuspendaccounts', 'auth_enrolkey'), 53 | get_string('unsuspendaccounts_description', 'auth_enrolkey'), 0, $options)); 54 | 55 | if (function_exists('totara_cohort_check_and_update_dynamic_cohort_members')) { 56 | $settings->add(new admin_setting_configcheckbox('auth_enrolkey/totaracohortsync', 57 | get_string('cohortsync', 'auth_enrolkey'), 58 | get_string('cohortsync_description', 'auth_enrolkey'), 0)); 59 | } 60 | 61 | if (moodle_major_version() >= '3.3') { 62 | $authplugin = get_auth_plugin('enrolkey'); 63 | display_auth_lock_options($settings, $authplugin->authtype, $authplugin->userfields, 64 | get_string('auth_fieldlocks_help', 'auth'), false, false, $authplugin->get_custom_user_profile_fields()); 65 | } 66 | 67 | $authplugin = get_auth_plugin('enrolkey'); 68 | display_auth_lock_options($settings, $authplugin->authtype, $authplugin->userfields, 69 | '', true, true, $authplugin->get_custom_user_profile_fields()); 70 | 71 | // Create category for Enrolkey. 72 | $ADMIN->add('authsettings', new admin_category('auth_enrolkey', get_string('pluginname', 'auth_enrolkey'))); 73 | // Add settings page toconfigure defaults. 74 | $ADMIN->add('auth_enrolkey', $settings); 75 | // Clear '$settings' to prevent adding again our site category. 76 | $settings = null; 77 | // Add options. 78 | $ADMIN->add('auth_enrolkey', 79 | new admin_externalpage( 80 | 'auth_enrolkey_manage', 81 | get_string('menumanage', 'auth_enrolkey'), 82 | new moodle_url($CFG->wwwroot.'/auth/enrolkey/manage.php') 83 | ) 84 | ); 85 | } 86 | -------------------------------------------------------------------------------- /templates/enrolkey_profiletable.mustache: -------------------------------------------------------------------------------- 1 | {{! 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 . 16 | }} 17 | {{! 18 | @template enrolkey_profiletable 19 | 20 | Profile table template for enrolkey. 21 | 22 | The purpose of this template is to render the profile table. 23 | 24 | Classes required for JS: 25 | * none 26 | 27 | Data attributes required for JS: 28 | * none 29 | 30 | Context variables required for this template: 31 | * data 32 | 33 | Example context (json): 34 | { 35 | "data": { 36 | "fields": [{ 37 | "name": "Name", 38 | "value": "Value" 39 | }] 40 | } 41 | } 42 | }} 43 | {{#data}} 44 | 45 | {{#fields}} 46 | 47 | 48 | 49 | 50 | {{/fields}} 51 |
{{ name }}{{ value }}
52 | {{/data}} -------------------------------------------------------------------------------- /tests/auth_test.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Enrolkey authentication tests. 19 | * 20 | * @package auth_enrolkey 21 | * @copyright 2016 Nicholas Hoobin (nicholashoobin@catalyst-au.net) 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | namespace auth_enrolkey; 26 | 27 | use auth_enrolkey\persistent\enrolkey_cohort_mapping; 28 | use auth_enrolkey\persistent\enrolkey_profile_mapping; 29 | 30 | defined('MOODLE_INTERNAL') || die(); 31 | 32 | global $CFG; 33 | require_once($CFG->dirroot . '/auth/enrolkey/auth.php'); 34 | require_once($CFG->dirroot . '/cohort/lib.php'); 35 | require_once($CFG->dirroot . '/user/profile/lib.php'); 36 | 37 | /** 38 | * Token Authentication tests. 39 | * @copyright 2016 Nicholas Hoobin (nicholashoobin@catalyst-au.net) 40 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 41 | */ 42 | class auth_test extends \advanced_testcase { 43 | 44 | /** 45 | * Test test_auth_enrolkey() 46 | */ 47 | public function test_auth_enrolkey() { 48 | global $DB, $CFG; 49 | 50 | $this->resetAfterTest(true); 51 | 52 | $tokenauth = get_auth_plugin('enrolkey'); 53 | $selfenrol = enrol_get_plugin('self'); 54 | $user = $this->getDataGenerator()->create_user(); 55 | $this->setUser($user); 56 | 57 | $course1 = $this->getDataGenerator()->create_course(); 58 | $course2 = $this->getDataGenerator()->create_course(); 59 | $course3 = $this->getDataGenerator()->create_course(); 60 | $course4 = $this->getDataGenerator()->create_course(); 61 | $course5 = $this->getDataGenerator()->create_course(); 62 | $course6 = $this->getDataGenerator()->create_course(); 63 | $course7 = $this->getDataGenerator()->create_course(); 64 | $course8 = $this->getDataGenerator()->create_course(); 65 | 66 | // Additional for the test db that exists. 67 | $this->assertEquals(9, $DB->count_records('course')); 68 | 69 | $context1 = \context_course::instance($course1->id); 70 | $context2 = \context_course::instance($course2->id); 71 | $context3 = \context_course::instance($course3->id); 72 | $context4 = \context_course::instance($course4->id); 73 | $context5 = \context_course::instance($course5->id); 74 | $context6 = \context_course::instance($course6->id); 75 | $context7 = \context_course::instance($course7->id); 76 | $context8 = \context_course::instance($course8->id); 77 | 78 | $this->assertEquals(8, $DB->count_records('enrol', ['enrol' => 'self'])); 79 | 80 | $instance1 = $DB->get_record('enrol', ['courseid' => $course1->id, 'enrol' => 'self'], '*', MUST_EXIST); 81 | $instance2 = $DB->get_record('enrol', ['courseid' => $course2->id, 'enrol' => 'self'], '*', MUST_EXIST); 82 | $instance3 = $DB->get_record('enrol', ['courseid' => $course3->id, 'enrol' => 'self'], '*', MUST_EXIST); 83 | $instance4 = $DB->get_record('enrol', ['courseid' => $course4->id, 'enrol' => 'self'], '*', MUST_EXIST); 84 | $instance5 = $DB->get_record('enrol', ['courseid' => $course5->id, 'enrol' => 'self'], '*', MUST_EXIST); 85 | $instance6 = $DB->get_record('enrol', ['courseid' => $course6->id, 'enrol' => 'self'], '*', MUST_EXIST); 86 | $instance7 = $DB->get_record('enrol', ['courseid' => $course7->id, 'enrol' => 'self'], '*', MUST_EXIST); 87 | $instance8 = $DB->get_record('enrol', ['courseid' => $course8->id, 'enrol' => 'self'], '*', MUST_EXIST); 88 | 89 | $instance1->password = ''; 90 | $instance2->password = 'key_1'; 91 | $instance3->password = 'key_1'; 92 | $instance4->password = 'key_2'; 93 | $instance5->password = 'key_2'; 94 | $instance6->password = 'key_1'; 95 | $instance7->password = 'key_1'; 96 | $instance8->password = 'key_1'; 97 | 98 | $DB->update_record('enrol', $instance1); 99 | $DB->update_record('enrol', $instance2); 100 | $DB->update_record('enrol', $instance3); 101 | $DB->update_record('enrol', $instance4); 102 | $DB->update_record('enrol', $instance5); 103 | $DB->update_record('enrol', $instance6); 104 | $DB->update_record('enrol', $instance7); 105 | $DB->update_record('enrol', $instance8); 106 | 107 | $selfenrol->update_status($instance1, ENROL_INSTANCE_ENABLED); 108 | $selfenrol->update_status($instance2, ENROL_INSTANCE_ENABLED); 109 | $selfenrol->update_status($instance3, ENROL_INSTANCE_ENABLED); 110 | $selfenrol->update_status($instance4, ENROL_INSTANCE_ENABLED); 111 | $selfenrol->update_status($instance5, ENROL_INSTANCE_ENABLED); 112 | $selfenrol->update_status($instance6, ENROL_INSTANCE_ENABLED); 113 | $selfenrol->update_status($instance7, ENROL_INSTANCE_ENABLED); 114 | $selfenrol->update_status($instance8, ENROL_INSTANCE_DISABLED); 115 | 116 | $this->assertTrue($selfenrol->can_self_enrol($instance1)); 117 | $this->assertTrue($selfenrol->can_self_enrol($instance2)); 118 | $this->assertTrue($selfenrol->can_self_enrol($instance3)); 119 | $this->assertTrue($selfenrol->can_self_enrol($instance4)); 120 | $this->assertTrue($selfenrol->can_self_enrol($instance5)); 121 | $this->assertTrue($selfenrol->can_self_enrol($instance6)); 122 | $this->assertTrue($selfenrol->can_self_enrol($instance7)); 123 | $this->assertContains('Enrolment is disabled or inactive', [$selfenrol->can_self_enrol($instance8)]); 124 | 125 | $this->assertTrue($DB->record_exists('enrol', ['courseid' => $course1->id, 'enrol' => 'self', 'password' => ''])); 126 | $this->assertTrue($DB->record_exists('enrol', ['courseid' => $course2->id, 'enrol' => 'self', 'password' => 'key_1'])); 127 | $this->assertTrue($DB->record_exists('enrol', ['courseid' => $course3->id, 'enrol' => 'self', 'password' => 'key_1'])); 128 | $this->assertTrue($DB->record_exists('enrol', ['courseid' => $course4->id, 'enrol' => 'self', 'password' => 'key_2'])); 129 | $this->assertTrue($DB->record_exists('enrol', ['courseid' => $course5->id, 'enrol' => 'self', 'password' => 'key_2'])); 130 | $this->assertTrue($DB->record_exists('enrol', ['courseid' => $course6->id, 'enrol' => 'self', 'password' => 'key_1'])); 131 | $this->assertTrue($DB->record_exists('enrol', ['courseid' => $course7->id, 'enrol' => 'self', 'password' => 'key_1'])); 132 | $this->assertTrue($DB->record_exists('enrol', ['courseid' => $course8->id, 'enrol' => 'self', 'password' => 'key_1'])); 133 | 134 | // During create_user() it will insert the user into the database, we just want to generate the user object. 135 | $user1 = $this->getDataGenerator()->create_user(); 136 | $user2 = $this->getDataGenerator()->create_user(); 137 | $user3 = $this->getDataGenerator()->create_user(); 138 | 139 | $user1->signup_token = 'key_1'; 140 | $user2->signup_token = 'key_2'; 141 | $user3->signup_token = 'key_1'; 142 | 143 | // So we will remove the user record from the database. As we want to test $auth->user_signup(). 144 | $user1record = ['username' => $user1->username, 'mnethostid' => $user1->mnethostid]; 145 | $user2record = ['username' => $user2->username, 'mnethostid' => $user2->mnethostid]; 146 | $user3record = ['username' => $user3->username, 'mnethostid' => $user3->mnethostid]; 147 | 148 | $DB->delete_records('user', $user1record); 149 | $DB->delete_records('user', $user2record); 150 | $DB->delete_records('user', $user3record); 151 | 152 | // Testing that the user no longer exists, this would give a unique key restraint error if you tried it add it. 153 | $this->assertEquals(0, $DB->record_exists('user', $user1record)); 154 | $this->assertEquals(0, $DB->record_exists('user', $user2record)); 155 | $this->assertEquals(0, $DB->record_exists('user', $user3record)); 156 | 157 | // This hack is for email testin in travis. 158 | $debug = $CFG->debug; 159 | unset($CFG->debug); 160 | 161 | // Now signing up correctly. No email notification (false). 162 | $sink = $this->redirectEvents(); 163 | $tokenauth->user_signup($user1, false); 164 | $tokenauth->user_signup($user2, false); 165 | 166 | // Even though we don't send emails, the 'self' plugin may. 167 | $sink->close(); 168 | $CFG->debug = $debug; 169 | 170 | // User 1 should be enrolled into course 2 and 3. 171 | $this->assertFalse(is_enrolled($context1, $user1, '')); 172 | $this->assertTrue(is_enrolled($context2, $user1, '')); 173 | $this->assertTrue(is_enrolled($context3, $user1, '')); 174 | $this->assertFalse(is_enrolled($context4, $user1, '')); 175 | 176 | $this->assertFalse(is_enrolled($context8, $user1, '')); 177 | 178 | // User 2 should be enrolled into course 4. 179 | $this->assertFalse(is_enrolled($context1, $user2, '')); 180 | $this->assertFalse(is_enrolled($context2, $user2, '')); 181 | $this->assertFalse(is_enrolled($context3, $user2, '')); 182 | $this->assertTrue(is_enrolled($context4, $user2, '')); 183 | 184 | $this->assertFalse(is_enrolled($context8, $user2, '')); 185 | } 186 | 187 | public function test_group_enrolkey() { 188 | $this->resetAfterTest(true); 189 | global $DB, $CFG; 190 | 191 | // Generate users for test. 192 | $user1 = $this->getDataGenerator()->create_user(); 193 | $user2 = $this->getDataGenerator()->create_user(); 194 | $user3 = $this->getDataGenerator()->create_user(); 195 | 196 | // Setup course and enrolment. 197 | $course = $this->getDataGenerator()->create_course(); 198 | $context = \context_course::instance($course->id); 199 | // Create selfenrolment instance. 200 | $instance = $DB->get_record('enrol', ['courseid' => $course->id, 'enrol' => 'self'], '*', MUST_EXIST); 201 | $instance->password = 'key'; 202 | // Set customint that controls groupkeys. 203 | $instance->customint1 = 1; 204 | 205 | // Register instance with plugin. 206 | $selfenrol = enrol_get_plugin('self'); 207 | $DB->update_record('enrol', $instance); 208 | $selfenrol->update_status($instance, ENROL_INSTANCE_ENABLED); 209 | // Check self-enrolment is setup properly. 210 | $this->setUser($user1); 211 | $this->assertTrue($selfenrol->can_self_enrol($instance)); 212 | 213 | // Create group enrolment. 214 | $group = $this->getDataGenerator()->create_group(['courseid' => $course->id, 'enrolmentkey' => 'groupkey']); 215 | 216 | // Remove users from database to re-enrol. 217 | // So we will remove the user record from the database. As we want to test $auth->user_signup(). 218 | $user1record = ['username' => $user1->username, 'mnethostid' => $user1->mnethostid]; 219 | $user2record = ['username' => $user2->username, 'mnethostid' => $user2->mnethostid]; 220 | $user3record = ['username' => $user3->username, 'mnethostid' => $user3->mnethostid]; 221 | 222 | $DB->delete_records('user', $user1record); 223 | $DB->delete_records('user', $user2record); 224 | $DB->delete_records('user', $user3record); 225 | 226 | // Self signup to course. 227 | $user1->signup_token = 'key'; 228 | // Self signup to group. 229 | $user2->signup_token = 'groupkey'; 230 | // Self signup non-valid key. 231 | $user3->signup_token = 'fakekey'; 232 | 233 | // Setup plugin to enrol. 234 | $tokenauth = get_auth_plugin('enrolkey'); 235 | 236 | // Now signing up correctly. No email notification (false). 237 | $sink = $this->redirectEvents(); 238 | $tokenauth->user_signup($user1, false); 239 | $this->setUser($user2); 240 | $tokenauth->user_signup($user2, false); 241 | $this->setUser($user3); 242 | $tokenauth->user_signup($user3, false); 243 | 244 | // Even though we don't send emails, the 'self' plugin may. 245 | $sink->close(); 246 | 247 | // Check that $user1 is enrolled in $course but not $group. 248 | $this->assertTrue(is_enrolled($context, $user1, '')); 249 | $this->assertFalse(groups_is_member($group->id, $user1->id)); 250 | 251 | // Check that $user2 is enrolled in $course and in $group. 252 | $this->assertTrue(is_enrolled($context, $user2, '')); 253 | $this->assertTrue(groups_is_member($group->id, $user2->id)); 254 | 255 | // Check that $user3 is a valid user, but not enrolled in $course or $group. 256 | $this->assertTrue($DB->record_exists('user', ['id' => $user3->id])); 257 | $this->assertFalse(is_enrolled($context, $user3, '')); 258 | $this->assertFalse(groups_is_member($group->id, $user3->id)); 259 | } 260 | 261 | public function test_add_cohorts_during_signup() { 262 | $this->resetAfterTest(true); 263 | global $DB; 264 | 265 | // Setup the cohort data structure. 266 | $cohort = new \stdClass(); 267 | $cohort->contextid = \context_system::instance()->id; 268 | $cohort->name = 'test cohort'; 269 | $cohort->idnumber = 'testid'; 270 | $cohort->description = 'test cohort desc'; 271 | $cohort->descriptionformat = FORMAT_HTML; 272 | $cohortid = cohort_add_cohort($cohort); 273 | $this->assertNotEmpty($cohortid); 274 | 275 | // Build the persistent to use with cohort mapping. 276 | $cdata = [ 277 | 'enrolid' => 1, 278 | 'cohortid' => $cohortid, 279 | ]; 280 | $cohortmapping = new enrolkey_cohort_mapping(0, (object) $cdata); 281 | $cohortmapping->save(); 282 | $this->assertTrue($DB->record_exists('auth_enrolkey_cohort', $cdata)); 283 | 284 | $user1 = $this->getDataGenerator()->create_user(); 285 | 286 | $availableenrolids = [1]; 287 | enrolkey_cohort_mapping::add_cohorts_during_signup($user1, $availableenrolids); 288 | 289 | $this->assertTrue($DB->record_exists('cohort_members', ['cohortid' => $cohort->id, 'userid' => $user1->id])); 290 | } 291 | 292 | public function test_add_fields_during_signup() { 293 | $this->resetAfterTest(true); 294 | global $DB; 295 | 296 | // Create user info fields. 297 | if (!$DB->record_exists('user_info_category', [])) { 298 | // Copied from user/profile/index.php. 299 | $defaultcategory = new \stdClass(); 300 | $defaultcategory->name = 'Default category'; 301 | $defaultcategory->sortorder = 1; 302 | 303 | $DB->insert_record('user_info_category', $defaultcategory); 304 | } 305 | 306 | $field = [ 307 | 'shortname' => 'test1', 308 | 'name' => 'test field 1', 309 | 'categoryid' => 1, 310 | 'datatype' => 'text', 311 | ]; 312 | $DB->insert_record('user_info_field', (object) $field); 313 | 314 | // Build the persistent to use with cohort mapping. 315 | $pdata = [ 316 | 'enrolid' => 1, 317 | 'profilefieldname' => 'profile_field_test1', 318 | 'profilefielddata' => 'this is a string', 319 | ]; 320 | $fieldmapping = new enrolkey_profile_mapping(0, (object) $pdata); 321 | $fieldmapping->save(); 322 | 323 | $select = $DB->sql_compare_text('profilefieldname') . ' = ' . $DB->sql_compare_text(':profilefieldname'); 324 | $select .= ' AND ' . $DB->sql_compare_text('profilefielddata') . ' = ' . $DB->sql_compare_text(':profilefielddata'); 325 | $this->assertTrue($DB->record_exists_select('auth_enrolkey_profile', $select, $pdata)); 326 | 327 | $user1 = $this->getDataGenerator()->create_user(); 328 | $user1->profile_field_test1 = 'this data is force overwritten'; 329 | profile_save_data($user1, true); 330 | 331 | $availableenrolids = [1]; 332 | enrolkey_profile_mapping::add_fields_during_signup($user1, $availableenrolids); 333 | 334 | // Obtain the fieldid reference. 335 | $field = $DB->get_record('user_info_field', ['shortname' => 'test1']); 336 | $params = [ 337 | 'fieldid' => $field->id, 338 | 'userid' => $user1->id, 339 | 'data' => 'this is a string', 340 | ]; 341 | 342 | // Check to see if the data is saved. 343 | $select = $DB->sql_compare_text('data') . ' = ' . $DB->sql_compare_text(':data'); 344 | $select .= ' AND fieldid = :fieldid'; 345 | $select .= ' AND userid = :userid'; 346 | $this->assertTrue($DB->record_exists_select('user_info_data', $select, $params)); 347 | } 348 | } 349 | -------------------------------------------------------------------------------- /tests/reset_password_hook_test.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | 18 | namespace auth_enrolkey; 19 | 20 | use advanced_testcase; 21 | 22 | defined('MOODLE_INTERNAL') || die(); 23 | global $CFG; 24 | require_once($CFG->dirroot . '/auth/enrolkey/lib.php'); 25 | 26 | /** 27 | * Enrolkey password reset hook tests. 28 | * 29 | * @package auth_enrolkey 30 | * @copyright 2023 Catalyst IT 31 | * @author Matthew Hilton 32 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 33 | */ 34 | class reset_password_hook_test extends advanced_testcase { 35 | /** @var object test user **/ 36 | private $user; 37 | 38 | /** 39 | * Sets up tests 40 | */ 41 | public function setUp(): void { 42 | global $DB; 43 | $this->resetAfterTest(true); 44 | 45 | // Create a user who signs into the site with enrolkey. 46 | $enrolkey = get_auth_plugin('enrolkey'); 47 | 48 | // Generate a user, but then delete them so we can use the enrolkey to sign them up. 49 | $user = $this->getDataGenerator()->create_user(); 50 | $user->signup_token = 'token1'; 51 | 52 | $DB->delete_records('user', ['id' => $user->id]); 53 | $enrolkey->user_signup($user, false); 54 | $user = $DB->get_record('user', ['username' => $user->username]); 55 | $DB->update_record('user', ['id' => $user->id, 'auth' => 'enrolkey']); 56 | 57 | // Suspend them and re-fetch the record. 58 | $DB->update_record('user', ['id' => $user->id, 'suspended' => 1]); 59 | $user = \core_user::get_user($user->id); 60 | 61 | $this->user = $user; 62 | } 63 | 64 | /** 65 | * Tears down tests 66 | */ 67 | public function tearDown(): void { 68 | delete_user($this->user); 69 | $this->user = null; 70 | parent::tearDown(); 71 | } 72 | 73 | /** 74 | * Tests auth_enrolkey_post_forgot_password_requests function 75 | */ 76 | public function test_auth_enrolkey_post_forgot_password_requests() { 77 | global $DB; 78 | 79 | $sink = $this->redirectEmails(); 80 | $formdata = ['username' => $this->user->username]; 81 | 82 | // Initially the config to allow suspended enrolkey 83 | // users to get password reset emails is disabled. 84 | // So if we call this, we expect it to fail / do nothing. 85 | set_config('unsuspendaccounts', 0, 'auth_enrolkey'); 86 | auth_enrolkey_post_forgot_password_requests($formdata); 87 | $this->assertCount(0, $sink->get_messages()); 88 | 89 | // Confirm the user is still suspended. 90 | $this->assertEquals(1, $DB->get_field('user', 'suspended', ['id' => $this->user->id])); 91 | 92 | // But if the config is enabled, they should successfully receive an email. 93 | set_config('unsuspendaccounts', 1, 'auth_enrolkey'); 94 | auth_enrolkey_post_forgot_password_requests($formdata); 95 | $this->assertCount(1, $sink->get_messages()); 96 | 97 | // Confirm the user is still suspended. 98 | // The user is still required to use unsuspend.php to unsuspend themselves. 99 | // This only gives them their password back. 100 | $this->assertEquals(1, $DB->get_field('user', 'suspended', ['id' => $this->user->id])); 101 | } 102 | 103 | /** 104 | * Tests auth_enrolkey_post_set_password_requests function 105 | */ 106 | public function test_auth_enrolkey_post_set_password_requests() { 107 | global $USER; 108 | 109 | $randomuser = $this->getDataGenerator()->create_user(); 110 | 111 | // For non-enrolkey users, this does nothing. 112 | $this->setUser($randomuser); 113 | 114 | // Confirm user is logged in. 115 | $this->assertTrue(!empty($USER->id)); 116 | 117 | auth_enrolkey_post_set_password_requests([]); 118 | 119 | // Confirm user is still logged in. 120 | $this->assertTrue(!empty($USER->id)); 121 | 122 | // Now set as enrolkey user, this will log them out. 123 | $this->setUser($this->user); 124 | 125 | // Confirm user is logged in. 126 | $this->assertTrue(!empty($USER->id)); 127 | 128 | auth_enrolkey_post_set_password_requests([]); 129 | 130 | // Confirm user is now logged out. 131 | $this->assertTrue(empty($USER->id)); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /unsuspend.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Unsuspend user page with a valid enrolkey. 19 | * 20 | * @package auth_enrolkey 21 | * @author Nicholas Hoobin 22 | * @copyright 2021 Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | 27 | use auth_enrolkey\form\unsuspend_form; 28 | use auth_enrolkey\utility; 29 | 30 | require_once(__DIR__.'/../../config.php'); 31 | 32 | $context = context_system::instance(); 33 | 34 | $baseurl = new moodle_url('/auth/enrolkey/unsuspend.php'); 35 | 36 | if (!get_config('auth_enrolkey', 'unsuspendaccounts') || isloggedin()) { 37 | redirect(new moodle_url('/')); 38 | } 39 | 40 | $PAGE->set_url($baseurl); 41 | $PAGE->set_pagelayout('login'); 42 | $PAGE->set_title(get_string('title_unsuspend', 'auth_enrolkey')); 43 | $PAGE->set_heading(get_string('heading_unsuspend', 'auth_enrolkey')); 44 | $PAGE->set_context($context); 45 | $output = $PAGE->get_renderer('auth_enrolkey'); 46 | 47 | $form = new unsuspend_form($baseurl); 48 | 49 | if ($form->is_cancelled()) { 50 | require_logout(); 51 | redirect(new moodle_url('/')); 52 | } else if ($form->is_submitted() && $form->is_validated()) { 53 | $valid = false; 54 | $data = $form->get_data(); 55 | 56 | // At this stage we can say, due to mform validation the user exists, is suspended and the password checks out. 57 | // Lets just double check these things anyway. 58 | 59 | $user = utility::search_for_suspended_user((array) $data); 60 | if ($user) { 61 | $valid = validate_internal_user_password($user, $data->password); 62 | } 63 | 64 | if ($valid) { 65 | // Setting the userid can imitate logging in. Be careful with this. 66 | // This is used with the enrol_self() call. 67 | $USER->id = $user->id; 68 | try { 69 | list($availableenrolids, $errors) = utility::unsuspend_and_enrol_user($data->signup_token, false); 70 | 71 | // Only enrol a user to enrolkeys and courses which they are not already enrolled in. 72 | if (!empty($availableenrolids)) { 73 | complete_user_login($user); 74 | utility::unsuspend_user($user); 75 | 76 | // They are now unsuspended. We can actually called the real auth login function. 77 | if (authenticate_user_login($user->username, $data->password)) { 78 | utility::update_user($user, $availableenrolids); 79 | 80 | \auth_enrolkey\persistent\enrolkey_redirect_mapping::redirect_during_signup($availableenrolids); 81 | 82 | // Default redirect. 83 | redirect(new moodle_url("/auth/enrolkey/view.php", ['ids' => implode(',', $availableenrolids)])); 84 | } 85 | } 86 | } catch (Exception $e) { 87 | require_logout(); 88 | } 89 | } 90 | // Well, we're not really logged in at all. 91 | $USER->id = 0; 92 | } 93 | 94 | echo $output->header(); 95 | echo $output->heading($PAGE->heading); 96 | $form->display(); 97 | echo $output->footer(); 98 | -------------------------------------------------------------------------------- /version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Version information. 19 | * 20 | * @package auth_enrolkey 21 | * @copyright 2016 Nicholas Hoobin (nicholashoobin@catalyst-au.net) 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die; 26 | 27 | $plugin->version = 2022011200; // The current plugin version (Date: YYYYMMDDXX). 28 | $plugin->release = 2022011200; // Match release exactly to version. 29 | $plugin->requires = 2014051200; // Requires this Moodle version (2.7+). 30 | $plugin->component = 'auth_enrolkey'; // Full name of the plugin (used for diagnostics). 31 | $plugin->maturity = MATURITY_STABLE; 32 | $plugin->supported = [35, 405]; // A range of branch numbers of supported moodle versions. 33 | -------------------------------------------------------------------------------- /view.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Post signup page to notify user of courses enrolled via enrolment keys. 19 | * 20 | * @package auth_enrolkey 21 | * @copyright 2016 Nicholas Hoobin (nicholashoobin@catalyst-au.net) 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | require_once('../../config.php'); 26 | 27 | require_login(); 28 | 29 | $availableenrolids = required_param('ids', PARAM_TEXT); 30 | $availableenrolids = explode(',', $availableenrolids); 31 | 32 | $PAGE->set_url(new moodle_url('/auth/enrolkey/view.php')); 33 | $PAGE->set_course($SITE); 34 | $PAGE->set_title(get_string('pluginname', 'auth_enrolkey')); 35 | $PAGE->set_heading(get_string('signup_view', 'auth_enrolkey')); 36 | 37 | echo $OUTPUT->header(); 38 | 39 | if (!empty($availableenrolids)) { 40 | foreach ($availableenrolids as $enrolid) { 41 | $plugin = $DB->get_record('enrol', ['enrol' => 'self', 'id' => $enrolid]); 42 | $course = $DB->get_record('course', ['id' => $plugin->courseid]); 43 | 44 | $coursecontext = context_course::instance($plugin->courseid); 45 | $rolenames = role_get_names($coursecontext, ROLENAME_ALIAS, true); 46 | 47 | $data = new stdClass(); 48 | $data->course = $course->fullname; 49 | $data->enrolinstance = $plugin->name; 50 | $data->role = $rolenames[$plugin->roleid]; 51 | $data->startdate = date('Y-m-d H:i', $plugin->enrolstartdate); 52 | $data->enddate = date('Y-m-d H:i', $plugin->enrolenddate); 53 | $data->href = (new moodle_url('/course/view.php', ['id' => $plugin->courseid]))->out(); 54 | 55 | if ($plugin->enrolstartdate > 0 && $plugin->enrolenddate > 0) { 56 | // The course had both a start and end date. 57 | $successoutput = get_string('signup_view_message_basic_dates', 'auth_enrolkey', $data); 58 | } else if ($plugin->enrolstartdate > 0 && $plugin->enrolenddate == 0) { 59 | // The course only has a start date set. 60 | $successoutput = get_string('signup_view_message_basic_dates_startonly', 'auth_enrolkey', $data); 61 | } else if ($plugin->enrolstartdate == 0 && $plugin->enrolenddate > 0) { 62 | // The course only has a start date set. 63 | $successoutput = get_string('signup_view_message_basic_dates_endonly', 'auth_enrolkey', $data); 64 | } else { 65 | // The course has no date restrictions. 66 | $successoutput = get_string('signup_view_message_basic', 'auth_enrolkey', $data); 67 | } 68 | 69 | echo $OUTPUT->notification($successoutput, 'notifysuccess'); 70 | } 71 | } else { 72 | $data = new stdClass(); 73 | $data->href = (new moodle_url('/'))->out(); 74 | $failure = get_string('signup_failure', 'auth_enrolkey', $data); 75 | echo $OUTPUT->notification($failure, 'notifyfailure'); 76 | } 77 | 78 | echo $OUTPUT->footer(); 79 | 80 | --------------------------------------------------------------------------------