Proyectos de Subversion Moodle

Rev

Rev 11 | | 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
 * Self enrolment plugin.
19
 *
20
 * @package    enrol_self
21
 * @copyright  2010 Petr Skoda  {@link http://skodak.org}
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
1441 ariadna 25
use core\output\single_button;
26
use core_enrol\output\enrol_page;
27
 
1 efrain 28
/**
29
 * Self enrolment plugin implementation.
30
 * @author Petr Skoda
31
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
32
 */
33
class enrol_self_plugin extends enrol_plugin {
34
 
35
    protected $lasternoller = null;
36
    protected $lasternollerinstanceid = 0;
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
        $key = false;
52
        $nokey = false;
53
        foreach ($instances as $instance) {
54
            if ($this->can_self_enrol($instance, false) !== true) {
55
                // User can not enrol himself.
56
                // Note that we do not check here if user is already enrolled for performance reasons -
57
                // such check would execute extra queries for each course in the list of courses and
58
                // would hide self-enrolment icons from guests.
59
                continue;
60
            }
61
            if ($instance->password or $instance->customint1) {
62
                $key = true;
63
            } else {
64
                $nokey = true;
65
            }
66
        }
67
        $icons = array();
68
        if ($nokey) {
69
            $icons[] = new pix_icon('withoutkey', get_string('pluginname', 'enrol_self'), 'enrol_self');
70
        }
71
        if ($key) {
72
            $icons[] = new pix_icon('withkey', get_string('pluginname', 'enrol_self'), 'enrol_self');
73
        }
74
        return $icons;
75
    }
76
 
77
    /**
78
     * Returns localised name of enrol instance
79
     *
80
     * @param stdClass $instance (null is accepted too)
81
     * @return string
82
     */
83
    public function get_instance_name($instance) {
84
        global $DB;
85
 
86
        if (empty($instance->name)) {
87
            if (!empty($instance->roleid) and $role = $DB->get_record('role', array('id'=>$instance->roleid))) {
88
                $role = ' (' . role_get_name($role, context_course::instance($instance->courseid, IGNORE_MISSING)) . ')';
89
            } else {
90
                $role = '';
91
            }
92
            $enrol = $this->get_name();
93
            return get_string('pluginname', 'enrol_'.$enrol) . $role;
94
        } else {
95
            return format_string($instance->name);
96
        }
97
    }
98
 
99
    public function roles_protected() {
100
        // Users may tweak the roles later.
101
        return false;
102
    }
103
 
104
    public function allow_unenrol(stdClass $instance) {
105
        // Users with unenrol cap may unenrol other users manually manually.
106
        return true;
107
    }
108
 
109
    public function allow_manage(stdClass $instance) {
110
        // Users with manage cap may tweak period and status.
111
        return true;
112
    }
113
 
114
    public function show_enrolme_link(stdClass $instance) {
115
 
116
        if (true !== $this->can_self_enrol($instance, false)) {
117
            return false;
118
        }
119
 
120
        return true;
121
    }
122
 
123
    /**
124
     * Return true if we can add a new instance to this course.
125
     *
126
     * @param int $courseid
127
     * @return boolean
128
     */
129
    public function can_add_instance($courseid) {
130
        $context = context_course::instance($courseid, MUST_EXIST);
131
 
132
        if (!has_capability('moodle/course:enrolconfig', $context) or !has_capability('enrol/self:config', $context)) {
133
            return false;
134
        }
135
 
136
        return true;
137
    }
138
 
139
    /**
1441 ariadna 140
     * Returns edit icons for the page with list of instances.
141
     *
142
     * @param stdClass $instance
143
     * @return array
144
     */
145
    public function get_action_icons(stdClass $instance): array {
146
        global $OUTPUT;
147
 
148
        $context = context_course::instance($instance->courseid);
149
 
150
        $icons = [];
151
        if (has_any_capability(['enrol/self:config', 'moodle/course:editcoursewelcomemessage'], $context)) {
152
            $linkparams = [
153
                'courseid' => $instance->courseid,
154
                'id' => $instance->id,
155
                'type' => $instance->enrol,
156
            ];
157
            $editlink = new moodle_url('/enrol/editinstance.php', $linkparams);
158
            $icon = new pix_icon('t/edit', get_string('edit'), 'core', ['class' => 'iconsmall']);
159
            $icons[] = $OUTPUT->action_icon($editlink, $icon);
160
        }
161
 
162
        return $icons;
163
    }
164
 
165
    /**
1 efrain 166
     * Self enrol user to course
167
     *
168
     * @param stdClass $instance enrolment instance
169
     * @param stdClass $data data needed for enrolment.
170
     */
171
    public function enrol_self(stdClass $instance, $data = null) {
172
        global $DB, $USER, $CFG;
173
 
174
        // Don't enrol user if password is not passed when required.
175
        if ($instance->password && !isset($data->enrolpassword)) {
176
            return;
177
        }
178
 
179
        $timestart = time();
180
        if ($instance->enrolperiod) {
181
            $timeend = $timestart + $instance->enrolperiod;
182
        } else {
183
            $timeend = 0;
184
        }
185
 
186
        $this->enrol_user($instance, $USER->id, $instance->roleid, $timestart, $timeend);
187
 
188
        \core\notification::success(get_string('youenrolledincourse', 'enrol'));
189
 
190
        // Test whether the password is also used as a group key.
191
        if ($instance->password && $instance->customint1) {
192
            $groups = $DB->get_records('groups', array('courseid'=>$instance->courseid), 'id', 'id, enrolmentkey');
193
            foreach ($groups as $group) {
194
                if (empty($group->enrolmentkey)) {
195
                    continue;
196
                }
197
                if ($group->enrolmentkey === $data->enrolpassword) {
198
                    // Add user to group.
199
                    require_once($CFG->dirroot.'/group/lib.php');
200
                    groups_add_member($group->id, $USER->id);
201
                    break;
202
                }
203
            }
204
        }
205
    }
206
 
1441 ariadna 207
    #[\Override]
1 efrain 208
    public function enrol_page_hook(stdClass $instance) {
1441 ariadna 209
        global $CFG, $OUTPUT, $USER, $PAGE;
1 efrain 210
 
1441 ariadna 211
        $buttonurl = null;
212
        $buttontext = '';
213
        $buttonattrs = [];
214
        $body = '';
215
        $title = $this->get_instance_name($instance);
1 efrain 216
 
217
        $enrolstatus = $this->can_self_enrol($instance);
1441 ariadna 218
        if ($enrolstatus === true) {
219
            if ($instance->password) {
220
                // Self-enrolment with password. Display a button to open a form in a modal.
221
                $body = get_string('enrolkeyrequired', 'enrol_self');
222
                $buttonurl = $PAGE->url;
223
                $buttonattrs = [
224
                    'data-id' => $instance->courseid,
225
                    'data-instance' => $instance->id,
226
                    'data-form' => enrol_self\form\enrol_form::class,
227
                    'data-title' => $title,
228
                ];
229
                $PAGE->requires->js_call_amd('enrol_self/enrol_page', 'initEnrol', [$instance->id]);
230
            } else {
231
                // Self-enrolment without password. Display a button to self enrol. If button is pressed - enrol the user.
232
                if (optional_param('action', null, PARAM_TEXT) === 'enrol' && confirm_sesskey()) {
233
                    $this->enrol_self($instance, (object)[]);
234
                    return '';
1 efrain 235
                }
1441 ariadna 236
                $body = get_string('nopassword', 'enrol_self');
237
                $buttonurl = new moodle_url($PAGE->url, ['action' => 'enrol', 'sesskey' => sesskey()]);
1 efrain 238
            }
1441 ariadna 239
            $buttontext = get_string('enrolme', 'enrol_self');
240
        } else if (isguestuser()) {
241
            // User is not logged in. Display a button to login.
242
            $buttonurl = new moodle_url(get_login_url());
243
            $body = get_string('noguestaccess', 'enrol');
244
            $buttontext = get_string('continue');
245
        } else if (!$enrolstatus) {
246
            // No reason why user can not use this method, do not display anything.
247
            return '';
1 efrain 248
        } else {
1441 ariadna 249
            $body = $enrolstatus;
1 efrain 250
        }
251
 
1441 ariadna 252
        $notification = new \core\output\notification($body, 'info', false);
253
        $notification->set_extra_classes(['mb-0']);
254
        $enrolpage = new enrol_page(
255
            instance: $instance,
256
            header: $title,
257
            body: $OUTPUT->render($notification),
258
            buttons: $buttonurl ?
259
                [new single_button($buttonurl, $buttontext, 'get', single_button::BUTTON_PRIMARY, $buttonattrs)] :
260
                []);
261
        return $OUTPUT->render($enrolpage);
1 efrain 262
    }
263
 
264
    /**
265
     * Checks if user can self enrol.
266
     *
267
     * @param stdClass $instance enrolment instance
268
     * @param bool $checkuserenrolment if true will check if user enrolment is inactive.
269
     *             used by navigation to improve performance.
270
     * @return bool|string true if successful, else error message or false.
271
     */
272
    public function can_self_enrol(stdClass $instance, $checkuserenrolment = true) {
273
        global $DB, $OUTPUT, $USER;
274
 
275
        if ($checkuserenrolment) {
276
            if (isguestuser()) {
277
                // Can not enrol guest.
278
                return get_string('noguestaccess', 'enrol') . $OUTPUT->continue_button(get_login_url());
279
            }
280
            // Check if user is already enroled.
281
            if ($DB->get_record('user_enrolments', array('userid' => $USER->id, 'enrolid' => $instance->id))) {
282
                return get_string('canntenrol', 'enrol_self');
283
            }
284
        }
285
 
286
        // Check if self enrolment is available right now for users.
287
        $result = $this->is_self_enrol_available($instance);
288
        if ($result !== true) {
289
            return $result;
290
        }
291
 
292
        // Check if user has the capability to enrol in this context.
293
        if (!has_capability('enrol/self:enrolself', context_course::instance($instance->courseid))) {
294
            return get_string('canntenrol', 'enrol_self');
295
        }
296
 
297
        return true;
298
    }
299
 
300
    /**
301
     * Does this plugin support some way to self enrol?
302
     * This function doesn't check user capabilities. Use can_self_enrol to check capabilities.
303
     *
304
     * @param stdClass $instance enrolment instance
1441 ariadna 305
     * @return bool|string - true means "Enrol me in this course" link could be available
1 efrain 306
     */
307
    public function is_self_enrol_available(stdClass $instance) {
308
        global $CFG, $DB, $USER;
309
 
310
        if ($instance->status != ENROL_INSTANCE_ENABLED) {
311
            return get_string('canntenrol', 'enrol_self');
312
        }
313
 
314
        if ($instance->enrolstartdate != 0 and $instance->enrolstartdate > time()) {
315
            return get_string('canntenrolearly', 'enrol_self', userdate($instance->enrolstartdate));
316
        }
317
 
318
        if ($instance->enrolenddate != 0 and $instance->enrolenddate < time()) {
319
            return get_string('canntenrollate', 'enrol_self', userdate($instance->enrolenddate));
320
        }
321
 
322
        if (!$instance->customint6) {
323
            // New enrols not allowed.
324
            return get_string('canntenrol', 'enrol_self');
325
        }
326
 
327
        if ($DB->record_exists('user_enrolments', array('userid' => $USER->id, 'enrolid' => $instance->id))) {
328
            return get_string('canntenrol', 'enrol_self');
329
        }
330
 
331
        if ($instance->customint3 > 0) {
332
            // Max enrol limit specified.
333
            $count = $DB->count_records('user_enrolments', array('enrolid' => $instance->id));
334
            if ($count >= $instance->customint3) {
335
                // Bad luck, no more self enrolments here.
336
                return get_string('maxenrolledreached', 'enrol_self');
337
            }
338
        }
339
 
340
        if ($instance->customint5) {
341
            require_once("$CFG->dirroot/cohort/lib.php");
342
            if (!cohort_is_member($instance->customint5, $USER->id)) {
343
                $cohort = $DB->get_record('cohort', array('id' => $instance->customint5));
344
                if (!$cohort) {
345
                    return null;
346
                }
347
                $a = format_string($cohort->name, true, array('context' => context::instance_by_id($cohort->contextid)));
348
                return markdown_to_html(get_string('cohortnonmemberinfo', 'enrol_self', $a));
349
            }
350
        }
351
 
352
        return true;
353
    }
354
 
355
    /**
356
     * Return information for enrolment instance containing list of parameters required
357
     * for enrolment, name of enrolment plugin etc.
358
     *
359
     * @param stdClass $instance enrolment instance
360
     * @return stdClass instance info.
361
     */
362
    public function get_enrol_info(stdClass $instance) {
363
 
364
        $instanceinfo = new stdClass();
365
        $instanceinfo->id = $instance->id;
366
        $instanceinfo->courseid = $instance->courseid;
367
        $instanceinfo->type = $this->get_name();
368
        $instanceinfo->name = $this->get_instance_name($instance);
369
        $instanceinfo->status = $this->can_self_enrol($instance);
370
 
371
        if ($instance->password) {
372
            $instanceinfo->requiredparam = new stdClass();
373
            $instanceinfo->requiredparam->enrolpassword = get_string('password', 'enrol_self');
374
        }
375
 
376
        // If enrolment is possible and password is required then return ws function name to get more information.
377
        if ((true === $instanceinfo->status) && $instance->password) {
378
            $instanceinfo->wsfunction = 'enrol_self_get_instance_info';
379
        }
380
        return $instanceinfo;
381
    }
382
 
383
    /**
384
     * Add new instance of enrol plugin with default settings.
385
     * @param stdClass $course
386
     * @return int id of new instance
387
     */
388
    public function add_default_instance($course) {
389
        $fields = $this->get_instance_defaults();
390
 
391
        if ($this->get_config('requirepassword')) {
392
            $fields['password'] = generate_password(20);
393
        }
394
 
395
        return $this->add_instance($course, $fields);
396
    }
397
 
398
    /**
399
     * Returns defaults for new instances.
400
     * @return array
401
     */
402
    public function get_instance_defaults() {
403
        $expirynotify = $this->get_config('expirynotify');
404
        if ($expirynotify == 2) {
405
            $expirynotify = 1;
406
            $notifyall = 1;
407
        } else {
408
            $notifyall = 0;
409
        }
410
 
411
        $fields = array();
412
        $fields['status']          = $this->get_config('status');
413
        $fields['roleid']          = $this->get_config('roleid');
414
        $fields['enrolperiod']     = $this->get_config('enrolperiod');
415
        $fields['expirynotify']    = $expirynotify;
416
        $fields['notifyall']       = $notifyall;
417
        $fields['expirythreshold'] = $this->get_config('expirythreshold');
418
        $fields['customint1']      = $this->get_config('groupkey');
419
        $fields['customint2']      = $this->get_config('longtimenosee');
420
        $fields['customint3']      = $this->get_config('maxenrolled');
421
        $fields['customint4']      = $this->get_config('sendcoursewelcomemessage');
422
        $fields['customint5']      = 0;
423
        $fields['customint6']      = $this->get_config('newenrols');
424
 
425
        return $fields;
426
    }
427
 
428
    /**
429
     * Send welcome email to specified user.
430
     *
431
     * @param stdClass $instance
432
     * @param stdClass $user user record
433
     * @return void
434
     * @deprecated since Moodle 4.4
435
     * @see \enrol_plugin::send_course_welcome_message_to_user()
436
     * @todo MDL-81185 Final deprecation in Moodle 4.8.
437
     */
438
    #[\core\attribute\deprecated('enrol_plugin::send_course_welcome_message_to_user', since: '4.4', mdl: 'MDL-4188')]
439
    protected function email_welcome_message($instance, $user) {
1441 ariadna 440
        \core\deprecation::emit_deprecation(__FUNCTION__);
1 efrain 441
        $this->send_course_welcome_message_to_user(
442
            instance: $instance,
443
            userid: $user->id,
444
            sendoption: $instance->customint4,
445
            message: $instance->customtext1,
446
        );
447
    }
448
 
449
    /**
450
     * Sync all meta course links.
451
     *
452
     * @param progress_trace $trace
453
     * @param int $courseid one course, empty mean all
454
     * @return int 0 means ok, 1 means error, 2 means plugin disabled
455
     */
456
    public function sync(progress_trace $trace, $courseid = null) {
457
        global $DB;
458
 
459
        if (!enrol_is_enabled('self')) {
460
            $trace->finished();
461
            return 2;
462
        }
463
 
464
        // Unfortunately this may take a long time, execution can be interrupted safely here.
465
        core_php_time_limit::raise();
466
        raise_memory_limit(MEMORY_HUGE);
467
 
468
        $trace->output('Verifying self-enrolments...');
469
 
470
        $params = array('now'=>time(), 'useractive'=>ENROL_USER_ACTIVE, 'courselevel'=>CONTEXT_COURSE);
471
        $coursesql = "";
472
        if ($courseid) {
473
            $coursesql = "AND e.courseid = :courseid";
474
            $params['courseid'] = $courseid;
475
        }
476
 
477
        // Note: the logic of self enrolment guarantees that user logged in at least once (=== u.lastaccess set)
478
        //       and that user accessed course at least once too (=== user_lastaccess record exists).
479
 
480
        // First deal with users that did not log in for a really long time - they do not have user_lastaccess records.
481
        $sql = "SELECT e.*, ue.userid
482
                  FROM {user_enrolments} ue
483
                  JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'self' AND e.customint2 > 0)
484
                  JOIN {user} u ON u.id = ue.userid
485
                 WHERE :now - u.lastaccess > e.customint2
486
                       $coursesql";
487
        $rs = $DB->get_recordset_sql($sql, $params);
488
        foreach ($rs as $instance) {
489
            $userid = $instance->userid;
490
            unset($instance->userid);
491
            $this->unenrol_user($instance, $userid);
492
            $days = $instance->customint2 / DAYSECS;
493
            $trace->output("unenrolling user $userid from course $instance->courseid " .
494
                "as they did not log in for at least $days days", 1);
495
        }
496
        $rs->close();
497
 
498
        // Now unenrol from course user did not visit for a long time.
499
        $sql = "SELECT e.*, ue.userid
500
                  FROM {user_enrolments} ue
501
                  JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'self' AND e.customint2 > 0)
502
                  JOIN {user_lastaccess} ul ON (ul.userid = ue.userid AND ul.courseid = e.courseid)
503
                 WHERE :now - ul.timeaccess > e.customint2
504
                       $coursesql";
505
        $rs = $DB->get_recordset_sql($sql, $params);
506
        foreach ($rs as $instance) {
507
            $userid = $instance->userid;
508
            unset($instance->userid);
509
            $this->unenrol_user($instance, $userid);
510
            $days = $instance->customint2 / DAYSECS;
511
            $trace->output("unenrolling user $userid from course $instance->courseid " .
512
                "as they did not access the course for at least $days days", 1);
513
        }
514
        $rs->close();
515
 
516
        $trace->output('...user self-enrolment updates finished.');
517
        $trace->finished();
518
 
519
        $this->process_expirations($trace, $courseid);
520
 
521
        return 0;
522
    }
523
 
524
    /**
525
     * Notify users about enrolment expiration.
526
     *
527
     * Users may be notified by the expiration time of enrollment or unenrollment due to inactivity. The latter is checked in
528
     * the last condition of the where clause:
529
     * days of inactivity + number of days in advance to send the notification > days of inactivity allowed before unenrollment
530
     *
531
     * @param int $timenow Current time.
532
     * @param string $name Name of this enrol plugin.
533
     * @param progress_trace $trace (accepts bool for backwards compatibility only)
534
     * @return void
535
     */
536
    protected function fetch_users_and_notify_expiry(int $timenow, string $name, progress_trace $trace): void {
537
        global $DB, $CFG;
538
 
539
        $sql = "SELECT ue.*, e.expirynotify, e.notifyall, e.expirythreshold, e.courseid, c.fullname, e.customint2,
540
                       COALESCE(ul.timeaccess, 0) AS timeaccess, ue.timestart
541
                  FROM {user_enrolments} ue
542
                  JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = :name AND e.expirynotify > 0  AND e.status = :enabled)
543
                  JOIN {course} c ON (c.id = e.courseid)
544
                  JOIN {user} u ON (u.id = ue.userid AND u.deleted = 0 AND u.suspended = 0)
545
                  LEFT JOIN {user_lastaccess} ul ON (ul.userid = u.id AND ul.courseid = c.id)
546
                 WHERE ue.status = :active
547
                       AND ((ue.timeend > 0 AND ue.timeend > :now1 AND ue.timeend < (e.expirythreshold + :now2))
548
                            OR (e.customint2 > 0 AND (:now3 - COALESCE(ul.timeaccess, 0) + e.expirythreshold > e.customint2)))
549
              ORDER BY ue.enrolid ASC, u.lastname ASC, u.firstname ASC, u.id ASC";
550
        $params = [
551
            'name' => $name,
552
            'enabled' => ENROL_INSTANCE_ENABLED,
553
            'active' => ENROL_USER_ACTIVE,
554
            'now1' => $timenow,
555
            'now2' => $timenow,
556
            'now3' => $timenow,
557
        ];
558
 
559
        $rs = $DB->get_recordset_sql($sql, $params);
560
 
561
        $lastenrollid = 0;
562
        $users = [];
563
 
564
        foreach ($rs as $ue) {
565
            $expirycond = ($ue->timeend > 0) && ($ue->timeend > $timenow) && ($ue->timeend < ($ue->expirythreshold + $timenow));
566
            $inactivitycond = ($ue->customint2 > 0) && (($timenow - $ue->timeaccess + $ue->expirythreshold) > $ue->customint2);
567
 
568
            $user = $DB->get_record('user', ['id' => $ue->userid]);
569
 
570
            if ($expirycond) {
571
                if ($lastenrollid && $lastenrollid != $ue->enrolid) {
572
                    $this->notify_expiry_enroller($lastenrollid, $users, $trace);
573
                    $users = [];
574
                }
575
                $lastenrollid = $ue->enrolid;
576
 
577
                $enroller = $this->get_enroller($ue->enrolid);
578
                $context = context_course::instance($ue->courseid);
579
 
580
                $users[] = [
581
                    'fullname' => fullname($user, has_capability('moodle/site:viewfullnames', $context, $enroller)),
582
                    'timeend' => $ue->timeend,
583
                ];
584
            }
585
            // Notifications to inactive users only if inactive time limit is set.
586
            if ($inactivitycond && $ue->notifyall) {
587
                $ue->message = 'expiryinactivemessageenrolledbody';
588
                $lastaccess = $ue->timeaccess;
589
                if (!$lastaccess) {
590
                    $lastaccess = $ue->timestart;
591
                }
592
                $ue->inactivetime = floor(($timenow - $lastaccess) / DAYSECS);
593
                $this->notify_expiry_enrolled($user, $ue, $trace);
594
            }
595
 
596
            if ($expirycond) {
597
                if (!$ue->notifyall) {
598
                    continue;
599
                }
600
 
601
                if ($ue->timeend - $ue->expirythreshold + 86400 < $timenow) {
602
                    // Notify enrolled users only once at the start of the threshold.
603
                    $trace->output("user $ue->userid was already notified that enrolment in course $ue->courseid expires on " .
604
                        userdate($ue->timeend, '', $CFG->timezone), 1);
605
                    continue;
606
                }
607
 
608
                $this->notify_expiry_enrolled($user, $ue, $trace);
609
            }
610
        }
611
        $rs->close();
612
 
613
        if ($lastenrollid && $users) {
614
            $this->notify_expiry_enroller($lastenrollid, $users, $trace);
615
        }
616
    }
617
 
618
    /**
619
     * Returns the user who is responsible for self enrolments in given instance.
620
     *
621
     * Usually it is the first editing teacher - the person with "highest authority"
622
     * as defined by sort_by_roleassignment_authority() having 'enrol/self:manage'
623
     * capability.
624
     *
625
     * @param int $instanceid enrolment instance id
626
     * @return stdClass user record
627
     */
628
    protected function get_enroller($instanceid) {
629
        global $DB;
630
 
631
        if ($this->lasternollerinstanceid == $instanceid and $this->lasternoller) {
632
            return $this->lasternoller;
633
        }
634
 
635
        $instance = $DB->get_record('enrol', array('id'=>$instanceid, 'enrol'=>$this->get_name()), '*', MUST_EXIST);
636
        $context = context_course::instance($instance->courseid);
637
 
638
        if ($users = get_enrolled_users($context, 'enrol/self:manage')) {
639
            $users = sort_by_roleassignment_authority($users, $context);
640
            $this->lasternoller = reset($users);
641
            unset($users);
642
        } else {
643
            $this->lasternoller = parent::get_enroller($instanceid);
644
        }
645
 
646
        $this->lasternollerinstanceid = $instanceid;
647
 
648
        return $this->lasternoller;
649
    }
650
 
651
    protected function get_expiry_message_body(stdClass $user, stdClass $ue, string $name,
652
            stdClass $enroller, context $context): string {
653
        $a = new stdClass();
654
        $a->course = format_string($ue->fullname, true, ['context' => $context]);
655
        $a->user = fullname($user, true);
656
        // If the enrolment duration is disabled, replace timeend with other data.
657
        if ($ue->timeend == 0) {
658
            $timenow = time();
659
            $lastaccess = $ue->timeaccess > 0 ? $ue->timeaccess : $ue->timestart;
660
            $ue->timeend = $timenow + ($ue->customint2 - ($timenow - $lastaccess));
661
        }
662
        $a->timeend  = userdate($ue->timeend, '', $user->timezone);
663
        $a->enroller = fullname($enroller, has_capability('moodle/site:viewfullnames', $context, $user));
664
        if (isset($ue->inactivetime)) {
665
            $a->inactivetime = $ue->inactivetime;
666
        }
667
        $a->url = new moodle_url('/course/view.php', ['id' => $ue->courseid]);
668
        return get_string($ue->message ?? 'expirymessageenrolledbody', 'enrol_' . $name, $a);
669
    }
670
 
671
    /**
672
     * Restore instance and map settings.
673
     *
674
     * @param restore_enrolments_structure_step $step
675
     * @param stdClass $data
676
     * @param stdClass $course
677
     * @param int $oldid
678
     */
679
    public function restore_instance(restore_enrolments_structure_step $step, stdClass $data, $course, $oldid) {
680
        global $DB;
681
        if ($step->get_task()->get_target() == backup::TARGET_NEW_COURSE) {
682
            $merge = false;
683
        } else {
684
            $merge = array(
685
                'courseid'   => $data->courseid,
686
                'enrol'      => $this->get_name(),
687
                'status'     => $data->status,
688
                'roleid'     => $data->roleid,
689
            );
690
        }
691
        if ($merge and $instances = $DB->get_records('enrol', $merge, 'id')) {
692
            $instance = reset($instances);
693
            $instanceid = $instance->id;
694
        } else {
695
            if (!empty($data->customint5)) {
696
                if ($step->get_task()->is_samesite()) {
697
                    // Keep cohort restriction unchanged - we are on the same site.
698
                } else {
699
                    // Use some id that can not exist in order to prevent self enrolment,
700
                    // because we do not know what cohort it is in this site.
701
                    $data->customint5 = -1;
702
                }
703
            }
704
            $instanceid = $this->add_instance($course, (array)$data);
705
        }
706
        $step->set_mapping('enrol', $oldid, $instanceid);
707
    }
708
 
709
    /**
710
     * Restore user enrolment.
711
     *
712
     * @param restore_enrolments_structure_step $step
713
     * @param stdClass $data
714
     * @param stdClass $instance
715
     * @param int $oldinstancestatus
716
     * @param int $userid
717
     */
718
    public function restore_user_enrolment(restore_enrolments_structure_step $step, $data, $instance, $userid, $oldinstancestatus) {
719
        $this->enrol_user($instance, $userid, null, $data->timestart, $data->timeend, $data->status);
720
    }
721
 
722
    /**
723
     * Restore role assignment.
724
     *
725
     * @param stdClass $instance
726
     * @param int $roleid
727
     * @param int $userid
728
     * @param int $contextid
729
     */
730
    public function restore_role_assignment($instance, $roleid, $userid, $contextid) {
731
        // This is necessary only because we may migrate other types to this instance,
732
        // we do not use component in manual or self enrol.
733
        role_assign($roleid, $userid, $contextid, '', 0);
734
    }
735
 
736
    /**
737
     * Is it possible to delete enrol instance via standard UI?
738
     *
739
     * @param stdClass $instance
740
     * @return bool
741
     */
742
    public function can_delete_instance($instance) {
743
        $context = context_course::instance($instance->courseid);
744
        return has_capability('enrol/self:config', $context);
745
    }
746
 
747
    /**
748
     * Is it possible to hide/show enrol instance via standard UI?
749
     *
750
     * @param stdClass $instance
751
     * @return bool
752
     */
753
    public function can_hide_show_instance($instance) {
754
        $context = context_course::instance($instance->courseid);
755
 
756
        if (!has_capability('enrol/self:config', $context)) {
757
            return false;
758
        }
759
 
760
        // If the instance is currently disabled, before it can be enabled,
761
        // we must check whether the password meets the password policies.
762
        if ($instance->status == ENROL_INSTANCE_DISABLED) {
763
            if ($this->get_config('requirepassword')) {
764
                if (empty($instance->password)) {
765
                    return false;
766
                }
767
            }
768
            // Only check the password if it is set.
769
            if (!empty($instance->password) && $this->get_config('usepasswordpolicy')) {
770
                if (!check_password_policy($instance->password, $errmsg)) {
771
                    return false;
772
                }
773
            }
774
        }
775
 
776
        return true;
777
    }
778
 
779
    /**
780
     * Return an array of valid options for the status.
781
     *
782
     * @return array
783
     */
784
    protected function get_status_options() {
785
        $options = array(ENROL_INSTANCE_ENABLED  => get_string('yes'),
786
                         ENROL_INSTANCE_DISABLED => get_string('no'));
787
        return $options;
788
    }
789
 
790
    /**
791
     * Return an array of valid options for the newenrols property.
792
     *
793
     * @return array
794
     */
795
    protected function get_newenrols_options() {
796
        $options = array(1 => get_string('yes'), 0 => get_string('no'));
797
        return $options;
798
    }
799
 
800
    /**
801
     * Return an array of valid options for the groupkey property.
802
     *
803
     * @return array
804
     */
805
    protected function get_groupkey_options() {
806
        $options = array(1 => get_string('yes'), 0 => get_string('no'));
807
        return $options;
808
    }
809
 
810
    /**
811
     * Return an array of valid options for the expirynotify property.
812
     *
813
     * @return array
814
     */
815
    protected function get_expirynotify_options() {
816
        $options = array(0 => get_string('no'),
817
                         1 => get_string('expirynotifyenroller', 'enrol_self'),
818
                         2 => get_string('expirynotifyall', 'enrol_self'));
819
        return $options;
820
    }
821
 
822
    /**
823
     * Return an array of valid options for the longtimenosee property.
824
     *
825
     * @return array
826
     */
827
    protected function get_longtimenosee_options() {
828
        $options = array(0 => get_string('never'),
829
                         1800 * 3600 * 24 => get_string('numdays', '', 1800),
830
                         1000 * 3600 * 24 => get_string('numdays', '', 1000),
831
                         365 * 3600 * 24 => get_string('numdays', '', 365),
832
                         180 * 3600 * 24 => get_string('numdays', '', 180),
833
                         150 * 3600 * 24 => get_string('numdays', '', 150),
834
                         120 * 3600 * 24 => get_string('numdays', '', 120),
835
                         90 * 3600 * 24 => get_string('numdays', '', 90),
836
                         60 * 3600 * 24 => get_string('numdays', '', 60),
837
                         30 * 3600 * 24 => get_string('numdays', '', 30),
838
                         21 * 3600 * 24 => get_string('numdays', '', 21),
839
                         14 * 3600 * 24 => get_string('numdays', '', 14),
840
                         7 * 3600 * 24 => get_string('numdays', '', 7));
841
        return $options;
842
    }
843
 
844
    /**
845
     * The self enrollment plugin has several bulk operations that can be performed.
846
     * @param course_enrolment_manager $manager
847
     * @return array
848
     */
849
    public function get_bulk_operations(course_enrolment_manager $manager) {
850
        global $CFG;
851
        require_once($CFG->dirroot.'/enrol/self/locallib.php');
852
        $context = $manager->get_context();
853
        $bulkoperations = array();
854
        if (has_capability("enrol/self:manage", $context)) {
855
            $bulkoperations['editselectedusers'] = new enrol_self_editselectedusers_operation($manager, $this);
856
        }
857
        if (has_capability("enrol/self:unenrol", $context)) {
858
            $bulkoperations['deleteselectedusers'] = new enrol_self_deleteselectedusers_operation($manager, $this);
859
        }
860
        return $bulkoperations;
861
    }
862
 
863
    /**
864
     * Add elements to the edit instance form.
865
     *
866
     * @param stdClass $instance
867
     * @param MoodleQuickForm $mform
868
     * @param context $context
869
     * @return bool
870
     */
871
    public function edit_instance_form($instance, MoodleQuickForm $mform, $context) {
872
        global $CFG, $DB;
873
 
1441 ariadna 874
        // Main fields.
875
        if (has_capability('enrol/self:config', $context)) {
876
            // Merge these two settings to one value for the single selection element.
877
            if ($instance->notifyall && $instance->expirynotify) {
878
                $instance->expirynotify = 2;
879
            }
880
            unset($instance->notifyall);
1 efrain 881
 
1441 ariadna 882
            $nameattribs = ['size' => '20', 'maxlength' => '255'];
883
            $mform->addElement('text', 'name', get_string('custominstancename', 'enrol'), $nameattribs);
884
            $mform->setType('name', PARAM_TEXT);
885
            $mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'server');
1 efrain 886
 
1441 ariadna 887
            $options = $this->get_status_options();
888
            $mform->addElement('select', 'status', get_string('status', 'enrol_self'), $options);
889
            $mform->addHelpButton('status', 'status', 'enrol_self');
1 efrain 890
 
1441 ariadna 891
            $options = $this->get_newenrols_options();
892
            $mform->addElement('select', 'customint6', get_string('newenrols', 'enrol_self'), $options);
893
            $mform->addHelpButton('customint6', 'newenrols', 'enrol_self');
894
            $mform->disabledIf('customint6', 'status', 'eq', ENROL_INSTANCE_DISABLED);
1 efrain 895
 
1441 ariadna 896
            $passattribs = ['size' => '20', 'maxlength' => '50'];
897
            $mform->addElement('passwordunmask', 'password', get_string('password', 'enrol_self'), $passattribs);
898
            $mform->addHelpButton('password', 'password', 'enrol_self');
899
            if (empty($instance->id) && $this->get_config('requirepassword')) {
900
                $mform->addRule('password', get_string('required'), 'required', null, 'client');
901
            }
902
            $mform->addRule('password', get_string('maximumchars', '', 50), 'maxlength', 50, 'server');
1 efrain 903
 
1441 ariadna 904
            $options = $this->get_groupkey_options();
905
            $mform->addElement('select', 'customint1', get_string('groupkey', 'enrol_self'), $options);
906
            $mform->addHelpButton('customint1', 'groupkey', 'enrol_self');
1 efrain 907
 
1441 ariadna 908
            $roles = $this->extend_assignable_roles($context, $instance->roleid);
909
            $mform->addElement('select', 'roleid', get_string('role', 'enrol_self'), $roles);
1 efrain 910
 
1441 ariadna 911
            $options = ['optional' => true, 'defaultunit' => 86400];
912
            $mform->addElement('duration', 'enrolperiod', get_string('enrolperiod', 'enrol_self'), $options);
913
            $mform->addHelpButton('enrolperiod', 'enrolperiod', 'enrol_self');
1 efrain 914
 
1441 ariadna 915
            $options = $this->get_expirynotify_options();
916
            $mform->addElement('select', 'expirynotify', get_string('expirynotify', 'core_enrol'), $options);
917
            $mform->addHelpButton('expirynotify', 'expirynotify', 'core_enrol');
1 efrain 918
 
1441 ariadna 919
            $options = ['optional' => false, 'defaultunit' => 86400];
920
            $mform->addElement('duration', 'expirythreshold', get_string('expirythreshold', 'core_enrol'), $options);
921
            $mform->addHelpButton('expirythreshold', 'expirythreshold', 'core_enrol');
922
            $mform->disabledIf('expirythreshold', 'expirynotify', 'eq', 0);
1 efrain 923
 
1441 ariadna 924
            $options = ['optional' => true];
925
            $mform->addElement('date_time_selector', 'enrolstartdate', get_string('enrolstartdate', 'enrol_self'), $options);
926
            $mform->setDefault('enrolstartdate', 0);
927
            $mform->addHelpButton('enrolstartdate', 'enrolstartdate', 'enrol_self');
1 efrain 928
 
1441 ariadna 929
            $options = ['optional' => true];
930
            $mform->addElement('date_time_selector', 'enrolenddate', get_string('enrolenddate', 'enrol_self'), $options);
931
            $mform->setDefault('enrolenddate', 0);
932
            $mform->addHelpButton('enrolenddate', 'enrolenddate', 'enrol_self');
1 efrain 933
 
1441 ariadna 934
            $options = $this->get_longtimenosee_options();
935
            $mform->addElement('select', 'customint2', get_string('longtimenosee', 'enrol_self'), $options);
936
            $mform->addHelpButton('customint2', 'longtimenosee', 'enrol_self');
1 efrain 937
 
1441 ariadna 938
            $mform->addElement('text', 'customint3', get_string('maxenrolled', 'enrol_self'));
939
            $mform->addHelpButton('customint3', 'maxenrolled', 'enrol_self');
940
            $mform->setType('customint3', PARAM_INT);
1 efrain 941
 
1441 ariadna 942
            require_once($CFG->dirroot.'/cohort/lib.php');
1 efrain 943
 
1441 ariadna 944
            $cohorts = [0 => get_string('no')];
945
            $allcohorts = cohort_get_available_cohorts($context, 0, 0, 0);
946
            if ($instance->customint5 && !isset($allcohorts[$instance->customint5])) {
947
                $c = $DB->get_record('cohort',
948
                                    ['id' => $instance->customint5],
949
                                    'id, name, idnumber, contextid, visible',
950
                                    IGNORE_MISSING);
951
                if ($c) {
952
                    // Current cohort was not found because current user can not see it. Still keep it.
953
                    $allcohorts[$instance->customint5] = $c;
954
                }
1 efrain 955
            }
1441 ariadna 956
            foreach ($allcohorts as $c) {
957
                $cohorts[$c->id] = format_string($c->name, true, ['context' => context::instance_by_id($c->contextid)]);
958
                if ($c->idnumber) {
959
                    $cohorts[$c->id] .= ' ['.s($c->idnumber).']';
960
                }
1 efrain 961
            }
1441 ariadna 962
            if ($instance->customint5 && !isset($allcohorts[$instance->customint5])) {
963
                // Somebody deleted a cohort, better keep the wrong value so that random ppl can not enrol.
964
                $cohorts[$instance->customint5] = get_string('unknowncohort', 'cohort', $instance->customint5);
965
            }
966
            if (count($cohorts) > 1) {
967
                $mform->addElement('select', 'customint5', get_string('cohortonly', 'enrol_self'), $cohorts);
968
                $mform->addHelpButton('customint5', 'cohortonly', 'enrol_self');
969
            } else {
970
                $mform->addElement('hidden', 'customint5');
971
                $mform->setType('customint5', PARAM_INT);
972
                $mform->setConstant('customint5', 0);
973
            }
1 efrain 974
        }
975
 
1441 ariadna 976
        // Course welcome message.
977
        if (has_any_capability(['enrol/self:config', 'moodle/course:editcoursewelcomemessage'], $context)) {
978
            $mform->addElement('select', 'customint4', get_string('sendcoursewelcomemessage', 'enrol_self'),
979
                    enrol_send_welcome_email_options());
980
            $mform->addHelpButton('customint4', 'sendcoursewelcomemessage', 'enrol_self');
1 efrain 981
 
1441 ariadna 982
            $options = [
983
                'cols' => '60',
984
                'rows' => '8',
985
            ];
986
            $mform->addElement('textarea', 'customtext1', get_string('customwelcomemessage', 'core_enrol'), $options);
987
            $mform->setDefault('customtext1', get_string('customwelcomemessageplaceholder', 'core_enrol'));
988
            $mform->hideIf(
989
                elementname: 'customtext1',
990
                dependenton: 'customint4',
991
                condition: 'eq',
992
                value: ENROL_DO_NOT_SEND_EMAIL,
993
            );
1 efrain 994
 
1441 ariadna 995
            // Static form elements cannot be hidden by hideIf() so we need to add a dummy group.
996
            // See: https://tracker.moodle.org/browse/MDL-66251.
997
            $group[] = $mform->createElement(
998
                'static',
999
                'customwelcomemessage_extra_help',
1000
                null,
1001
                get_string(
1002
                    identifier: 'customwelcomemessage_help',
1003
                    component: 'core_enrol',
1004
                ),
1005
            );
1006
            $mform->addGroup($group, 'group_customwelcomemessage_extra_help', '', ' ', false);
1007
            $mform->hideIf(
1008
                elementname: 'group_customwelcomemessage_extra_help',
1009
                dependenton: 'customint4',
1010
                condition: 'eq',
1011
                value: ENROL_DO_NOT_SEND_EMAIL,
1012
            );
1013
        }
1 efrain 1014
 
1441 ariadna 1015
        // Enrolment changes warning.
1016
        if (has_capability('enrol/self:config', $context) && enrol_accessing_via_instance($instance)) {
1 efrain 1017
            $warntext = get_string('instanceeditselfwarningtext', 'core_enrol');
1018
            $mform->addElement('static', 'selfwarn', get_string('instanceeditselfwarning', 'core_enrol'), $warntext);
1019
        }
1020
    }
1021
 
1022
    /**
1023
     * We are a good plugin and don't invent our own UI/validation code path.
1024
     *
1025
     * @return boolean
1026
     */
1027
    public function use_standard_editing_ui() {
1028
        return true;
1029
    }
1030
 
1031
    /**
1032
     * Perform custom validation of the data used to edit the instance.
1033
     *
1034
     * @param array $data array of ("fieldname"=>value) of submitted data
1035
     * @param array $files array of uploaded files "element_name"=>tmp_file_path
1036
     * @param object $instance The instance loaded from the DB
1037
     * @param context $context The context of the instance we are editing
1038
     * @return array of "element_name"=>"error_description" if there are errors,
1039
     *         or an empty array if everything is OK.
1040
     * @return void
1041
     */
1042
    public function edit_instance_validation($data, $files, $instance, $context) {
1043
        global $CFG;
1044
        require_once("{$CFG->dirroot}/enrol/self/locallib.php");
1045
 
1046
        $errors = array();
1047
 
1048
        $checkpassword = false;
1049
 
1050
        if ($instance->id) {
1051
            // Check the password if we are enabling the plugin again.
1052
            if (($instance->status == ENROL_INSTANCE_DISABLED) && ($data['status'] == ENROL_INSTANCE_ENABLED)) {
1053
                $checkpassword = true;
1054
            }
1055
 
1056
            // Check the password if the instance is enabled and the password has changed.
1057
            if (($data['status'] == ENROL_INSTANCE_ENABLED) && ($instance->password !== $data['password'])) {
1058
                $checkpassword = true;
1059
            }
1060
 
1061
            // Check the password if we are enabling group enrolment keys.
1062
            if (!$instance->customint1 && $data['customint1']) {
1063
                $checkpassword = true;
1064
            }
1065
        } else {
1066
            $checkpassword = true;
1067
        }
1068
 
1069
        if ($checkpassword) {
1070
            $require = $this->get_config('requirepassword');
1071
            $policy = $this->get_config('usepasswordpolicy');
1072
            if ($require and trim($data['password']) === '') {
1073
                $errors['password'] = get_string('required');
1074
            } else if (!empty($data['password'])) {
1075
                if ($policy) {
1076
                    $errmsg = '';
1077
                    if (!check_password_policy($data['password'], $errmsg)) {
1078
                        $errors['password'] = $errmsg;
1079
                    }
1080
                }
1081
                if ($data['customint1'] && enrol_self_check_group_enrolment_key($instance->courseid, $data['password'])) {
1082
                    $errors['password'] = get_string('passwordmatchesgroupkey', 'enrol_self');
1083
                }
1084
            }
1085
        }
1086
 
1087
        if ($data['status'] == ENROL_INSTANCE_ENABLED) {
1088
            if (!empty($data['enrolenddate']) and $data['enrolenddate'] < $data['enrolstartdate']) {
1089
                $errors['enrolenddate'] = get_string('enrolenddaterror', 'enrol_self');
1090
            }
1091
        }
1092
 
11 efrain 1093
        // If expirynotify is selected, then ensure the threshold is at least one day.
1094
        if (isset($data['expirynotify']) && $data['expirynotify'] > 0 && $data['expirythreshold'] < DAYSECS) {
1 efrain 1095
            $errors['expirythreshold'] = get_string('errorthresholdlow', 'core_enrol');
1096
        }
1097
 
1098
        // Now these ones are checked by quickforms, but we may be called by the upload enrolments tool, or a webservive.
1099
        if (array_key_exists('name', $data) && core_text::strlen($data['name']) > 255) {
1100
            $errors['name'] = get_string('err_maxlength', 'form', 255);
1101
        }
1102
        $validstatus = array_keys($this->get_status_options());
1103
        $validnewenrols = array_keys($this->get_newenrols_options());
1104
        if (core_text::strlen($data['password']) > 50) {
1105
            $errors['name'] = get_string('err_maxlength', 'form', 50);
1106
        }
1107
        $validgroupkey = array_keys($this->get_groupkey_options());
1108
        $context = context_course::instance($instance->courseid);
1109
        $validroles = array_keys($this->extend_assignable_roles($context, $instance->roleid));
1110
        $validexpirynotify = array_keys($this->get_expirynotify_options());
1111
        $validlongtimenosee = array_keys($this->get_longtimenosee_options());
1112
        $tovalidate = array(
1113
            'enrolstartdate' => PARAM_INT,
1114
            'enrolenddate' => PARAM_INT,
1115
            'name' => PARAM_TEXT,
1116
            'customint1' => $validgroupkey,
1117
            'customint2' => $validlongtimenosee,
1118
            'customint3' => PARAM_INT,
1119
            'customint4' => PARAM_INT,
1120
            'customint5' => PARAM_INT,
1121
            'customint6' => $validnewenrols,
1122
            'status' => $validstatus,
1123
            'enrolperiod' => PARAM_INT,
1124
            'expirynotify' => $validexpirynotify,
1125
            'roleid' => $validroles
1126
        );
1127
        if (array_key_exists('expirynotify', $data) && $data['expirynotify'] != 0) {
1128
            $tovalidate['expirythreshold'] = PARAM_INT;
1129
        }
1130
        $typeerrors = $this->validate_param_types($data, $tovalidate);
1131
        $errors = array_merge($errors, $typeerrors);
1132
 
1133
        return $errors;
1134
    }
1135
 
1136
    /**
1137
     * Add new instance of enrol plugin.
1138
     * @param object $course
1139
     * @param array $fields instance fields
1140
     * @return int id of new instance, null if can not be created
1141
     */
1441 ariadna 1142
    public function add_instance($course, ?array $fields = null) {
1 efrain 1143
        // In the form we are representing 2 db columns with one field.
1144
        if (!empty($fields) && !empty($fields['expirynotify'])) {
1145
            if ($fields['expirynotify'] == 2) {
1146
                $fields['expirynotify'] = 1;
1147
                $fields['notifyall'] = 1;
1148
            } else {
1149
                $fields['notifyall'] = 0;
1150
            }
1151
        }
1152
 
1153
        return parent::add_instance($course, $fields);
1154
    }
1155
 
1156
    /**
1157
     * Update instance of enrol plugin.
1158
     * @param stdClass $instance
1159
     * @param stdClass $data modified instance fields
1160
     * @return boolean
1161
     */
1162
    public function update_instance($instance, $data) {
1163
        // In the form we are representing 2 db columns with one field.
1164
        if ($data->expirynotify == 2) {
1165
            $data->expirynotify = 1;
1166
            $data->notifyall = 1;
1167
        } else {
1168
            $data->notifyall = 0;
1169
        }
1170
        // Keep previous/default value of disabled expirythreshold option.
1171
        if (!$data->expirynotify) {
1172
            $data->expirythreshold = $instance->expirythreshold;
1173
        }
1174
        // Add previous value of newenrols if disabled.
1175
        if (!isset($data->customint6)) {
1176
            $data->customint6 = $instance->customint6;
1177
        }
1178
 
1179
        return parent::update_instance($instance, $data);
1180
    }
1181
 
1182
    /**
1183
     * Gets a list of roles that this user can assign for the course as the default for self-enrolment.
1184
     *
1185
     * @param context $context the context.
1186
     * @param integer $defaultrole the id of the role that is set as the default for self-enrolment
1187
     * @return array index is the role id, value is the role name
1188
     */
1189
    public function extend_assignable_roles($context, $defaultrole) {
1190
        global $DB;
1191
 
1192
        $roles = get_assignable_roles($context, ROLENAME_BOTH);
1193
        if (!isset($roles[$defaultrole])) {
1194
            if ($role = $DB->get_record('role', array('id' => $defaultrole))) {
1195
                $roles[$defaultrole] = role_get_name($role, $context, ROLENAME_BOTH);
1196
            }
1197
        }
1198
        return $roles;
1199
    }
1200
 
1201
    /**
1202
     * @deprecated since Moodle 4.4
1203
     */
1441 ariadna 1204
    #[\core\attribute\deprecated('enrol_plugin::get_welcome_message_contact', since: '4.4', mdl: 'MDL-4188', final: true)]
1205
    public function get_welcome_email_contact() {
1206
        \core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
1 efrain 1207
    }
1208
 
1209
    /**
1210
     * Check if enrolment plugin is supported in csv course upload.
1211
     *
1212
     * @return bool
1213
     */
1214
    public function is_csv_upload_supported(): bool {
1215
        return true;
1216
    }
1217
 
1218
    /**
1219
     * Finds matching instances for a given course.
1220
     *
1221
     * @param array $enrolmentdata enrolment data.
1222
     * @param int $courseid Course ID.
1223
     * @return stdClass|null Matching instance
1224
     */
1225
    public function find_instance(array $enrolmentdata, int $courseid): ?stdClass {
1226
 
1227
        $instances = enrol_get_instances($courseid, false);
1228
        $instance = null;
1229
        foreach ($instances as $i) {
1230
            if ($i->enrol == 'self') {
1231
                // This is bad - we can not really distinguish between self instances. So grab first available.
1232
                $instance = $i;
1233
                break;
1234
            }
1235
        }
1236
        return $instance;
1237
    }
1238
 
1239
    /**
1240
     * Fill custom fields data for a given enrolment plugin.
1241
     *
1242
     * @param array $enrolmentdata enrolment data.
1243
     * @param int $courseid Course ID.
1244
     * @return array Updated enrolment data with custom fields info.
1245
     */
1246
    public function fill_enrol_custom_fields(array $enrolmentdata, int $courseid): array {
1247
        return $enrolmentdata + ['password' => ''];
1248
    }
1249
 
1250
    /**
1251
     * Updates enrol plugin instance with provided data.
1252
     * @param int $courseid Course ID.
1253
     * @param array $enrolmentdata enrolment data.
1254
     * @param stdClass $instance Instance to update.
1255
     *
1256
     * @return stdClass updated instance
1257
     */
1258
    public function update_enrol_plugin_data(int $courseid, array $enrolmentdata, stdClass $instance): stdClass {
1259
        if (!empty($enrolmentdata['password'])) {
1260
            $instance->password = $enrolmentdata['password'];
1261
        }
1262
        return parent::update_enrol_plugin_data($courseid, $enrolmentdata, $instance);
1263
    }
1264
 
1265
    /**
1266
     * Check if data is valid for a given enrolment plugin
1267
     *
1268
     * @param array $enrolmentdata enrolment data to validate.
1269
     * @param int|null $courseid Course ID.
1270
     * @return array Errors
1271
     */
1272
    public function validate_enrol_plugin_data(array $enrolmentdata, ?int $courseid = null): array {
1273
        global $CFG, $DB;
1274
 
1275
        require_once($CFG->dirroot . "/enrol/self/locallib.php");
1276
 
1277
        // If password is omitted or empty in csv it will be generated automatically if it is a required policy.
1278
 
1279
        $errors = parent::validate_enrol_plugin_data($enrolmentdata, $courseid);
1280
        $policy = $this->get_config('usepasswordpolicy');
1281
        if (!empty($enrolmentdata['password'])) {
1282
            if ($policy) {
1283
                $errarray = get_password_policy_errors($enrolmentdata['password']);
1284
                foreach ($errarray as $i => $err) {
1285
                    $errors['enrol_self' . $i] = $err;
1286
                }
1287
            }
1288
 
1289
            if ($courseid) {
1290
                // This is bad - no way to identify which instance it is.
1291
                // So if any instance in course uses group key we should error.
1292
                $usegroupenrolmentkeys =
1293
                    $DB->count_records('enrol', ['courseid' => $courseid, 'enrol' => 'self', 'customint1' => 1]);
1294
                if ($usegroupenrolmentkeys && enrol_self_check_group_enrolment_key($courseid, $enrolmentdata['password'])) {
1295
                    $errors['errorpasswordmatchesgroupkey'] =
1296
                        new lang_string('passwordmatchesgroupkey', 'enrol_self', $enrolmentdata['password']);
1297
                }
1298
            }
1299
        }
1300
        return $errors;
1301
    }
1302
}
1303
 
1304
/**
1305
 * Get icon mapping for font-awesome.
1306
 */
1307
function enrol_self_get_fontawesome_icon_map() {
1308
    return [
1309
        'enrol_self:withkey' => 'fa-key',
1441 ariadna 1310
        'enrol_self:withoutkey' => 'fa-solid fa-right-to-bracket',
1 efrain 1311
    ];
1312
}