Proyectos de Subversion Moodle

Rev

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