Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * Guest access plugin.
19
 *
20
 * This plugin does not add any entries into the user_enrolments table,
21
 * the access control is granted on the fly via the tricks in require_login().
22
 *
23
 * @package    enrol_guest
24
 * @copyright  2010 Petr Skoda  {@link http://skodak.org}
25
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26
 */
27
 
1441 ariadna 28
use core\output\single_button;
29
use core_enrol\output\enrol_page;
1 efrain 30
 
31
/**
32
 * Class enrol_guest_plugin
33
 *
34
 * @copyright  2010 Petr Skoda  {@link http://skodak.org}
35
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36
 */
37
class enrol_guest_plugin extends enrol_plugin {
38
 
39
    /**
40
     * Returns optional enrolment information icons.
41
     *
42
     * This is used in course list for quick overview of enrolment options.
43
     *
44
     * We are not using single instance parameter because sometimes
45
     * we might want to prevent icon repetition when multiple instances
46
     * of one type exist. One instance may also produce several icons.
47
     *
48
     * @param array $instances all enrol instances of this type in one course
49
     * @return array of pix_icon
50
     */
51
    public function get_info_icons(array $instances) {
52
        foreach ($instances as $instance) {
53
            if ($instance->password !== '') {
54
                return array(new pix_icon('withpassword', get_string('guestaccess_withpassword', 'enrol_guest'), 'enrol_guest'));
55
            } else {
56
                return array(new pix_icon('withoutpassword', get_string('guestaccess_withoutpassword', 'enrol_guest'), 'enrol_guest'));
57
            }
58
        }
59
    }
60
 
61
    /**
62
     * Enrol a user using a given enrolment instance.
63
     *
64
     * @param stdClass $instance
65
     * @param int $userid
66
     * @param null $roleid
67
     * @param int $timestart
68
     * @param int $timeend
69
     * @param null $status
70
     * @param null $recovergrades
71
     */
72
    public function enrol_user(stdClass $instance, $userid, $roleid = null, $timestart = 0, $timeend = 0, $status = null, $recovergrades = null) {
73
        // no real enrolments here!
74
        return;
75
    }
76
 
77
    /**
78
     * Enrol a user from a given enrolment instance.
79
     *
80
     * @param stdClass $instance
81
     * @param int $userid
82
     */
83
    public function unenrol_user(stdClass $instance, $userid) {
84
        // nothing to do, we never enrol here!
85
        return;
86
    }
87
 
88
    /**
89
     * Attempt to automatically gain temporary guest access to course,
90
     * calling code has to make sure the plugin and instance are active.
91
     *
92
     * @param stdClass $instance course enrol instance
93
     * @return bool|int false means no guest access, integer means end of cached time
94
     */
95
    public function try_guestaccess(stdClass $instance) {
96
        global $USER, $CFG;
97
 
98
        $allow = false;
99
 
100
        if ($instance->password === '') {
101
            $allow = true;
102
 
103
        } else if (isset($USER->enrol_guest_passwords[$instance->id])) { // this is a hack, ideally we should not add stuff to $USER...
104
            if ($USER->enrol_guest_passwords[$instance->id] === $instance->password) {
105
                $allow = true;
106
            }
107
        } else if (WS_SERVER) { // Mobile app mostly.
108
            $storedpass = get_user_preferences('enrol_guest_ws_password_'. $instance->id);
109
            // We check first if there is a supplied password.
110
            if (!is_null($storedpass)) {
111
                $allow = $storedpass === $instance->password;
112
 
113
                if (!$allow) {
114
                    // Reset, probably the course password was changed.
115
                    unset_user_preference('enrol_guest_ws_password_' . $instance->id);
116
                }
117
            }
118
        }
119
 
120
        if ($allow) {
121
            // Temporarily assign them some guest role for this context
122
            $context = context_course::instance($instance->courseid);
123
            load_temp_course_role($context, $CFG->guestroleid);
124
            return ENROL_MAX_TIMESTAMP;
125
        }
126
 
127
        return false;
128
    }
129
 
130
    /**
131
     * Returns true if the current user can add a new instance of enrolment plugin in course.
132
     * @param int $courseid
133
     * @return boolean
134
     */
135
    public function can_add_instance($courseid) {
136
        global $DB;
137
 
138
        $context = context_course::instance($courseid, MUST_EXIST);
139
 
140
        if (!has_capability('moodle/course:enrolconfig', $context) or !has_capability('enrol/guest:config', $context)) {
141
            return false;
142
        }
143
 
144
        if ($DB->record_exists('enrol', array('courseid'=>$courseid, 'enrol'=>'guest'))) {
145
            return false;
146
        }
147
 
148
        return true;
149
    }
150
 
151
    /**
1441 ariadna 152
     * Enrol a user using the guest enrolment method
1 efrain 153
     *
154
     * @param stdClass $instance
1441 ariadna 155
     * @param string $guestpassword
156
     * @return void
1 efrain 157
     */
1441 ariadna 158
    public function mark_user_as_enrolled(stdClass $instance, string $guestpassword): void {
159
        global $USER, $CFG;
160
 
161
        // Add guest role.
162
        $context = \core\context\course::instance($instance->courseid);
163
        $USER->enrol_guest_passwords[$instance->id] = $guestpassword;
164
        if (isset($USER->enrol['tempguest'][$instance->courseid])) {
165
            remove_temp_course_roles($context);
166
        }
167
        load_temp_course_role($context, $CFG->guestroleid);
168
        $USER->enrol['tempguest'][$instance->courseid] = ENROL_MAX_TIMESTAMP;
169
    }
170
 
171
    #[\Override]
1 efrain 172
    public function enrol_page_hook(stdClass $instance) {
1441 ariadna 173
        global $CFG, $OUTPUT, $SESSION, $USER, $PAGE;
1 efrain 174
 
175
        if ($instance->password === '') {
176
            return null;
177
        }
178
 
179
        if (isset($USER->enrol['tempguest'][$instance->courseid]) and $USER->enrol['tempguest'][$instance->courseid] > time()) {
180
            // no need to show the guest access when user can already enter course as guest
181
            return null;
182
        }
183
 
1441 ariadna 184
        $title = $this->get_instance_name($instance);
185
        $notification = new \core\output\notification(get_string('passwordrequired', 'enrol_guest'), 'info', false);
186
        $notification->set_extra_classes(['mb-0']);
187
        $button = new single_button(
188
            $PAGE->url,
189
            get_string('loginguest', 'moodle'),
190
            'get',
191
            single_button::BUTTON_PRIMARY,
192
            [
193
                'data-id' => $instance->courseid,
194
                'data-instance' => $instance->id,
195
                'data-form' => enrol_guest\form\enrol_form::class,
196
                'data-title' => $title,
197
            ]);
198
        $PAGE->requires->js_call_amd('enrol_guest/enrol_page', 'initEnrol', [$instance->id]);
199
        $enrolpage = new enrol_page(
200
            instance: $instance,
201
            header: $title,
202
            body: $OUTPUT->render($notification),
203
            buttons: [$button]);
204
        return $OUTPUT->render($enrolpage);
1 efrain 205
    }
206
 
207
    /**
208
     * Called after updating/inserting course.
209
     *
210
     * @param bool $inserted true if course just inserted
211
     * @param object $course
212
     * @param object $data form data
213
     * @return void
214
     */
215
    public function course_updated($inserted, $course, $data) {
216
        global $DB;
217
 
218
        if ($inserted) {
219
            if (isset($data->enrol_guest_status_0)) {
220
                $fields = array('status'=>$data->enrol_guest_status_0);
221
                if ($fields['status'] == ENROL_INSTANCE_ENABLED) {
222
                    $fields['password'] = $data->enrol_guest_password_0;
223
                } else {
224
                    if ($this->get_config('requirepassword')) {
225
                        $fields['password'] = generate_password(20);
226
                    }
227
                }
228
                $this->add_instance($course, $fields);
229
            } else {
230
                if ($this->get_config('defaultenrol')) {
231
                    $this->add_default_instance($course);
232
                }
233
            }
234
 
235
        } else {
236
            $instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'enrol'=>'guest'));
237
            foreach ($instances as $instance) {
238
                $i = $instance->id;
239
 
240
                if (isset($data->{'enrol_guest_status_'.$i})) {
241
                    $reset = ($instance->status != $data->{'enrol_guest_status_'.$i});
242
 
243
                    $instance->status       = $data->{'enrol_guest_status_'.$i};
244
                    $instance->timemodified = time();
245
                    if ($instance->status == ENROL_INSTANCE_ENABLED) {
246
                        if ($instance->password !== $data->{'enrol_guest_password_'.$i}) {
247
                            $reset = true;
248
                        }
249
                        $instance->password = $data->{'enrol_guest_password_'.$i};
250
                    }
251
                    $DB->update_record('enrol', $instance);
252
                    \core\event\enrol_instance_updated::create_from_record($instance)->trigger();
253
 
254
                    if ($reset) {
255
                        $context = context_course::instance($course->id);
256
                        $context->mark_dirty();
257
                    }
258
                }
259
            }
260
        }
261
    }
262
 
263
    /**
264
     * Add new instance of enrol plugin.
265
     * @param object $course
266
     * @param array instance fields
267
     * @return int id of new instance, null if can not be created
268
     */
1441 ariadna 269
    public function add_instance($course, ?array $fields = NULL) {
1 efrain 270
        $fields = (array)$fields;
271
 
272
        if (!isset($fields['password'])) {
273
            $fields['password'] = '';
274
        }
275
 
276
        return parent::add_instance($course, $fields);
277
    }
278
 
279
    /**
280
     * Add new instance of enrol plugin with default settings.
281
     * @param object $course
282
     * @return int id of new instance
283
     */
284
    public function add_default_instance($course) {
285
        $fields = array('status'=>$this->get_config('status'));
286
 
287
        if ($this->get_config('requirepassword')) {
288
            $fields['password'] = generate_password(20);
289
        }
290
 
291
        return $this->add_instance($course, $fields);
292
    }
293
 
294
    /**
295
     * Restore instance and map settings.
296
     *
297
     * @param restore_enrolments_structure_step $step
298
     * @param stdClass $data
299
     * @param stdClass $course
300
     * @param int $oldid
301
     */
302
    public function restore_instance(restore_enrolments_structure_step $step, stdClass $data, $course, $oldid) {
303
        global $DB;
304
 
305
        if (!$DB->record_exists('enrol', array('courseid' => $data->courseid, 'enrol' => $this->get_name()))) {
306
            $this->add_instance($course, (array)$data);
307
        }
308
 
309
        // No need to set mapping, we do not restore users or roles here.
310
        $step->set_mapping('enrol', $oldid, 0);
311
    }
312
 
313
    /**
314
     * Is it possible to delete enrol instance via standard UI?
315
     *
316
     * @param object $instance
317
     * @return bool
318
     */
319
    public function can_delete_instance($instance) {
320
        $context = context_course::instance($instance->courseid);
321
        return has_capability('enrol/guest:config', $context);
322
    }
323
 
324
    /**
325
     * Is it possible to hide/show enrol instance via standard UI?
326
     *
327
     * @param stdClass $instance
328
     * @return bool
329
     */
330
    public function can_hide_show_instance($instance) {
331
        $context = context_course::instance($instance->courseid);
332
        if (!has_capability('enrol/guest:config', $context)) {
333
            return false;
334
        }
335
 
336
        // If the instance is currently disabled, before it can be enabled, we must check whether the password meets the
337
        // password policies.
338
        if ($instance->status == ENROL_INSTANCE_DISABLED) {
339
            if ($this->get_config('requirepassword')) {
340
                if (empty($instance->password)) {
341
                    return false;
342
                }
343
            }
344
 
345
            // Only check the password if it is set.
346
            if (!empty($instance->password) && $this->get_config('usepasswordpolicy')) {
347
                if (!check_password_policy($instance->password, $errmsg)) {
348
                    return false;
349
                }
350
            }
351
        }
352
 
353
        return true;
354
    }
355
 
356
    /**
357
     * Get default settings for enrol_guest.
358
     *
359
     * @return array
360
     */
361
    public function get_instance_defaults() {
362
        $fields = array();
363
        $fields['status']          = $this->get_config('status');
364
        return $fields;
365
    }
366
 
367
    /**
368
     * Return information for enrolment instance containing list of parameters required
369
     * for enrolment, name of enrolment plugin etc.
370
     *
371
     * @param stdClass $instance enrolment instance
372
     * @return stdClass instance info.
373
     * @since Moodle 3.1
374
     */
375
    public function get_enrol_info(stdClass $instance) {
376
 
377
        $instanceinfo = new stdClass();
378
        $instanceinfo->id = $instance->id;
379
        $instanceinfo->courseid = $instance->courseid;
380
        $instanceinfo->type = $this->get_name();
381
        $instanceinfo->name = $this->get_instance_name($instance);
382
        $instanceinfo->status = $instance->status == ENROL_INSTANCE_ENABLED;
383
 
384
        // Specifics enrolment method parameters.
385
        $instanceinfo->requiredparam = new stdClass();
386
        $instanceinfo->requiredparam->passwordrequired = !empty($instance->password);
387
 
388
        // If the plugin is enabled, return the URL for obtaining more information.
389
        if ($instanceinfo->status) {
390
            $instanceinfo->wsfunction = 'enrol_guest_get_instance_info';
391
        }
392
        return $instanceinfo;
393
    }
394
 
395
    /**
396
     * Return an array of valid options for the status.
397
     *
398
     * @return array
399
     */
400
    protected function get_status_options() {
401
        $options = array(ENROL_INSTANCE_ENABLED  => get_string('yes'),
402
                         ENROL_INSTANCE_DISABLED => get_string('no'));
403
        return $options;
404
    }
405
 
406
    /**
407
     * Add elements to the edit instance form.
408
     *
409
     * @param stdClass $instance
410
     * @param MoodleQuickForm $mform
411
     * @param context $context
412
     * @return bool
413
     */
414
    public function edit_instance_form($instance, MoodleQuickForm $mform, $context) {
415
        global $CFG;
416
 
417
        $options = $this->get_status_options();
418
        $mform->addElement('select', 'status', get_string('status', 'enrol_guest'), $options);
419
        $mform->addHelpButton('status', 'status', 'enrol_guest');
420
        $mform->setDefault('status', $this->get_config('status'));
421
        $mform->setAdvanced('status', $this->get_config('status_adv'));
422
 
423
        $mform->addElement('passwordunmask', 'password', get_string('password', 'enrol_guest'));
424
        $mform->addHelpButton('password', 'password', 'enrol_guest');
425
 
426
        // If we have a new instance and the password is required - make sure it is set. For existing
427
        // instances we do not force the password to be required as it may have been set to empty before
428
        // the password was required. We check in the validation function whether this check is required
429
        // for existing instances.
430
        if (empty($instance->id) && $this->get_config('requirepassword')) {
431
            $mform->addRule('password', get_string('required'), 'required', null);
432
        }
433
    }
434
 
435
    /**
436
     * We are a good plugin and don't invent our own UI/validation code path.
437
     *
438
     * @return boolean
439
     */
440
    public function use_standard_editing_ui() {
441
        return true;
442
    }
443
 
444
    /**
445
     * Perform custom validation of the data used to edit the instance.
446
     *
447
     * @param array $data array of ("fieldname"=>value) of submitted data
448
     * @param array $files array of uploaded files "element_name"=>tmp_file_path
449
     * @param object $instance The instance loaded from the DB
450
     * @param context $context The context of the instance we are editing
451
     * @return array of "element_name"=>"error_description" if there are errors,
452
     *         or an empty array if everything is OK.
453
     * @return void
454
     */
455
    public function edit_instance_validation($data, $files, $instance, $context) {
456
        $errors = array();
457
 
458
        $checkpassword = false;
459
 
460
        if ($data['id']) {
461
            // Check the password if we are enabling the plugin again.
462
            if (($instance->status == ENROL_INSTANCE_DISABLED) && ($data['status'] == ENROL_INSTANCE_ENABLED)) {
463
                $checkpassword = true;
464
            }
465
 
466
            // Check the password if the instance is enabled and the password has changed.
467
            if (($data['status'] == ENROL_INSTANCE_ENABLED) && ($instance->password !== $data['password'])) {
468
                $checkpassword = true;
469
            }
470
        } else {
471
            $checkpassword = true;
472
        }
473
 
474
        if ($checkpassword) {
475
            $require = $this->get_config('requirepassword');
476
            $policy  = $this->get_config('usepasswordpolicy');
477
            if ($require && trim($data['password']) === '') {
478
                $errors['password'] = get_string('required');
479
            } else if (!empty($data['password']) && $policy) {
480
                $errmsg = '';
481
                if (!check_password_policy($data['password'], $errmsg)) {
482
                    $errors['password'] = $errmsg;
483
                }
484
            }
485
        }
486
 
487
        $validstatus = array_keys($this->get_status_options());
488
        $tovalidate = array(
489
            'status' => $validstatus
490
        );
491
        $typeerrors = $this->validate_param_types($data, $tovalidate);
492
        $errors = array_merge($errors, $typeerrors);
493
 
494
        return $errors;
495
    }
496
 
497
    /**
498
     * Check if enrolment plugin is supported in csv course upload.
499
     *
500
     * @return bool
501
     */
502
    public function is_csv_upload_supported(): bool {
503
        return true;
504
    }
505
 
506
    /**
507
     * Finds matching instances for a given course.
508
     *
509
     * @param array $enrolmentdata enrolment data.
510
     * @param int $courseid Course ID.
511
     * @return stdClass|null Matching instance
512
     */
513
    public function find_instance(array $enrolmentdata, int $courseid): ?stdClass {
514
 
515
        $instances = enrol_get_instances($courseid, false);
516
        $instance = null;
517
        foreach ($instances as $i) {
518
            if ($i->enrol == 'guest') {
519
                // There can be only one guest enrol instance so find first available.
520
                $instance = $i;
521
                break;
522
            }
523
        }
524
        return $instance;
525
    }
526
 
527
    /**
528
     * Fill custom fields data for a given enrolment plugin.
529
     *
530
     * @param array $enrolmentdata enrolment data.
531
     * @param int $courseid Course ID.
532
     * @return array Updated enrolment data with custom fields info.
533
     */
534
    public function fill_enrol_custom_fields(array $enrolmentdata, int $courseid): array {
535
        return $enrolmentdata + ['password' => ''];
536
    }
537
 
538
    /**
539
     * Updates enrol plugin instance with provided data.
540
     * @param int $courseid Course ID.
541
     * @param array $enrolmentdata enrolment data.
542
     * @param stdClass $instance Instance to update.
543
     *
544
     * @return stdClass updated instance
545
     */
546
    public function update_enrol_plugin_data(int $courseid, array $enrolmentdata, stdClass $instance): stdClass {
547
        if (!empty($enrolmentdata['password'])) {
548
            $instance->password = $enrolmentdata['password'];
549
        }
550
        return parent::update_enrol_plugin_data($courseid, $enrolmentdata, $instance);
551
    }
552
 
553
    /**
554
     * Check if data is valid for a given enrolment plugin
555
     *
556
     * @param array $enrolmentdata enrolment data to validate.
557
     * @param int|null $courseid Course ID.
558
     * @return array Errors
559
     */
560
    public function validate_enrol_plugin_data(array $enrolmentdata, ?int $courseid = null): array {
561
        // If password is omitted or empty in csv it will be generated automatically if it is a required policy.
562
 
563
        $errors = parent::validate_enrol_plugin_data($enrolmentdata, $courseid);
564
 
565
        $policy = $this->get_config('usepasswordpolicy');
566
        if (!empty($enrolmentdata['password']) && $policy) {
567
            $errarray = get_password_policy_errors($enrolmentdata['password']);
568
            foreach ($errarray as $i => $err) {
569
                $errors['enrol_guest' . $i] = $err;
570
            }
571
        }
572
        return $errors;
573
    }
574
}
575
 
576
/**
577
 * Get icon mapping for font-awesome.
578
 */
579
function enrol_guest_get_fontawesome_icon_map() {
580
    return [
1441 ariadna 581
        'enrol_guest:withoutpassword' => 'fa-lock-open',
582
        'enrol_guest:withpassword' => 'fa-lock',
1 efrain 583
    ];
584
}