• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ewallah / moodle-enrol_coursecompleted / 10771360676

02 Jul 2024 02:39PM UTC coverage: 99.102% (-0.9%) from 100.0%
10771360676

push

github

rdebleu
version update

331 of 334 relevant lines covered (99.1%)

4.15 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.6
/classes/plugin.php
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
 * Enrol coursecompleted plugin
19
 *
20
 * @package   enrol_coursecompleted
21
 * @copyright eWallah (www.eWallah.net)
22
 * @author    Renaat Debleu <info@eWallah.net>
23
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25

26
/**
27
 * Enrol coursecompleted plugin
28
 *
29
 * @package   enrol_coursecompleted
30
 * @copyright eWallah (www.eWallah.net)
31
 * @author    Renaat Debleu <info@eWallah.net>
32
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
33
 */
34
class enrol_coursecompleted_plugin extends enrol_plugin {
35

36
    /**
37
     * Returns localised name of enrol instance
38
     *
39
     * @param object $instance (null is accepted too)
40
     * @return string
41
     */
42
    public function get_instance_name($instance) {
43
        $tmp = is_null($instance) ? 'unknown' : $instance->customint1;
7✔
44
        if (
45
            $tmp !== 'unknown' &&
7✔
46
            $context = context_course::instance($tmp, IGNORE_MISSING)
7✔
47
        ) {
48
            $formatter = \core\di::get(\core\formatting::class);
7✔
49
            $name = $formatter->format_string(
7✔
50
                get_course($tmp)->shortname,
7✔
51
                context: $context,
7✔
52
                filter: false
7✔
53
            );
7✔
54
            return get_string('aftercourse', 'enrol_coursecompleted', $name);
7✔
55
        }
56
        return get_string('coursedeleted', '', $tmp);
×
57
    }
58

59
    /**
60
     * Returns optional enrolment information icons.
61
     *
62
     * @param array $instances all enrol instances of this type in one course
63
     * @return array of pix_icon
64
     */
65
    public function get_info_icons(array $instances) {
66
        $arr = [];
2✔
67
        $formatter = \core\di::get(\core\formatting::class);
2✔
68
        foreach ($instances as $instance) {
2✔
69
            if (
70
                $this->is_active($instance) &&
2✔
71
                $context = context_course::instance($instance->customint1, IGNORE_MISSING)
2✔
72
            ) {
73
                $name = $formatter->format_string(get_course($instance->customint1)->fullname, context: $context);
2✔
74
                $arr[] = new pix_icon('icon', get_string('aftercourse', 'enrol_coursecompleted', $name), 'enrol_coursecompleted');
2✔
75
            }
76
        }
77
        return $arr;
2✔
78
    }
79

80
    /**
81
     * Returns optional enrolment instance description text.
82
     *
83
     * @param object $instance
84
     * @return string short html text
85
     */
86
    public function get_description_text($instance) {
87
        $id = $instance->customint1;
×
88
        return "Enrolment by completion of course with id $id";
×
89
    }
90

91
    /**
92
     * Add information for people who want to enrol.
93
     *
94
     * @param stdClass $instance
95
     * @return string html text, usually a form in a text box
96
     */
97
    public function enrol_page_hook(stdClass $instance) {
98
        global $OUTPUT;
99
        if (!$this->is_active($instance)) {
1✔
100
            return '';
1✔
101
        }
102

103
        $data = [];
1✔
104
        $formatter = \core\di::get(\core\formatting::class);
1✔
105
        if ($this->get_config('svglearnpath')) {
1✔
106
            $items = $this->build_course_path($instance);
1✔
107
            $i = 1;
1✔
108
            foreach ($items as $item) {
1✔
109
                $name = $formatter->format_string(get_course($item)->fullname, context: context_course::instance($item));
1✔
110
                $data[] =
1✔
111
                    [
1✔
112
                        'first' => ($i == 1),
1✔
113
                        'course' => ($item == $instance->courseid),
1✔
114
                        'title' => $name,
1✔
115
                        'href' => new moodle_url('/course/view.php', ['id' => $item]),
1✔
116
                        'seqnumber' => $i,
1✔
117
                    ];
1✔
118
                $i++;
1✔
119
            }
120
        }
121
        $hasdata = count($data) >= 2;
1✔
122
        $name = $formatter->format_string(
1✔
123
            get_course($instance->customint1)->fullname,
1✔
124
            context: context_course::instance($instance->customint1)
1✔
125
        );
1✔
126
        $rdata =
1✔
127
            [
1✔
128
                'coursetitle' => $name,
1✔
129
                'courseurl' => new moodle_url('/course/view.php', ['id' => $instance->customint1]),
1✔
130
                'hasdata' => $hasdata,
1✔
131
                'items' => $data,
1✔
132
            ];
1✔
133
        $str = $OUTPUT->render_from_template('enrol_coursecompleted/learnpath', $rdata);
1✔
134
        return $OUTPUT->box($str);
1✔
135
    }
136

137
    /**
138
     * Gets an array of the user enrolment actions
139
     *
140
     * @param \course_enrolment_manager $manager
141
     * @param stdClass $ue A user enrolment object
142
     * @return array An array of user_enrolment_actions
143
     */
144
    public function get_user_enrolment_actions(\course_enrolment_manager $manager, $ue) {
145
        $actions = parent::get_user_enrolment_actions($manager, $ue);
1✔
146
        $id = $ue->enrolmentinstance->customint1;
1✔
147
        if (context_course::instance($id, IGNORE_MISSING)) {
1✔
148
            $actions[] = new user_enrolment_action(
1✔
149
                new pix_icon('a/search', ''),
1✔
150
                get_string('pluginname', 'report_completion'),
1✔
151
                new moodle_url('/report/completion/index.php', ['course' => $id]),
1✔
152
                ['class' => 'originlink', 'rel' => $ue->id]
1✔
153
            );
1✔
154
        }
155
        return $actions;
1✔
156
    }
157

158
    /**
159
     * Returns edit icons for the page with list of instances
160
     * @param stdClass $instance
161
     * @return array
162
     */
163
    public function get_action_icons(stdClass $instance) {
164
        global $OUTPUT;
165
        if ($instance->enrol !== 'coursecompleted') {
3✔
166
            throw new coding_exception('invalid enrol instance!');
1✔
167
        }
168
        $context = context_course::instance($instance->courseid);
2✔
169
        $icons = [];
2✔
170
        if (has_capability('enrol/coursecompleted:enrolpast', $context)) {
2✔
171
            $managelink = new moodle_url('/enrol/coursecompleted/manage.php', ['enrolid' => $instance->id]);
1✔
172
            $icon = new pix_icon('t/enrolusers', get_string('enrolusers', 'enrol_manual'), 'core', ['class' => 'iconsmall']);
1✔
173
            $icons[] = $OUTPUT->action_icon($managelink, $icon);
1✔
174
        }
175
        return array_merge(parent::get_action_icons($instance), $icons);
2✔
176
    }
177

178
    /**
179
     * Restore instance and map settings.
180
     *
181
     * @param \restore_enrolments_structure_step $step
182
     * @param stdClass $data
183
     * @param stdClass $course
184
     * @param int $oldid
185
     */
186
    public function restore_instance(\restore_enrolments_structure_step $step, stdClass $data, $course, $oldid) {
187
        global $DB;
188
        if ($step->get_task()->get_target() == backup::TARGET_NEW_COURSE) {
1✔
189
            $merge = false;
1✔
190
        } else {
191
            $merge = [
1✔
192
                'courseid' => $course->id,
1✔
193
                'enrol' => 'coursecompleted',
1✔
194
                'roleid' => $data->roleid,
1✔
195
                'customint1' => $data->customint1,
1✔
196
            ];
1✔
197
        }
198
        if ($merge && $instances = $DB->get_records('enrol', $merge, 'id')) {
1✔
199
            $instance = reset($instances);
1✔
200
            $instanceid = $instance->id;
1✔
201
        } else {
202
            $instanceid = $this->add_instance($course, (array)$data);
1✔
203
        }
204
        $step->set_mapping('enrol', $oldid, $instanceid);
1✔
205
    }
206

207
    /**
208
     * Enrol user into course via enrol instance.
209
     *
210
     * @param stdClass $instance
211
     * @param int $userid
212
     * @param int $roleid optional role id
213
     * @param int $timestart 0 means unknown
214
     * @param int $timeend 0 means forever
215
     * @param int $status default to ENROL_USER_ACTIVE for new enrolments, no change by default in updates
216
     * @param bool $recovergrades restore grade history
217
     * @return void
218
     */
219
    public function enrol_user(
220
        stdClass $instance,
221
        $userid,
222
        $roleid = null,
223
        $timestart = 0,
224
        $timeend = 0,
225
        $status = null,
226
        $recovergrades = null
227
    ) {
228
        global $DB;
229
        if ($this->is_active($instance)) {
22✔
230
            // We ignore the role, timestart, timeend and status parameters and fall back on the instance settings.
231
            $roleid = $instance->roleid ?? $this->get_config('roleid');
19✔
232
            if (
233
                $DB->record_exists('role', ['id' => $roleid]) &&
19✔
234
                context_course::instance($instance->customint1, IGNORE_MISSING) &&
19✔
235
                context_course::instance($instance->courseid, IGNORE_MISSING)
19✔
236
            ) {
237
                $timestart = 0;
18✔
238
                $timeend = 0;
18✔
239
                if (isset($instance->customint4) && $instance->customint4 > 0) {
18✔
240
                    $timestart = $instance->customint4;
2✔
241
                }
242
                if (isset($instance->enrolperiod) && $instance->enrolperiod > 0) {
18✔
243
                    $timeend = max(time(), $timestart) + $instance->enrolperiod;
2✔
244
                }
245
                parent::enrol_user($instance, $userid, $roleid, $timestart, $timeend, $status, $recovergrades);
18✔
246
            } else {
247
                debugging('Role does not exist', DEBUG_DEVELOPER);
1✔
248
            }
249
        }
250
    }
251

252
    /**
253
     * Restore user enrolment.
254
     *
255
     * @param \restore_enrolments_structure_step $step
256
     * @param stdClass $data
257
     * @param stdClass $instance
258
     * @param int $userid
259
     * @param int $oldstatus
260
     */
261
    public function restore_user_enrolment(\restore_enrolments_structure_step $step, $data, $instance, $userid, $oldstatus) {
262
        if ($step->get_task()->get_target() == backup::TARGET_NEW_COURSE) {
1✔
263
            $this->enrol_user($instance, $userid);
1✔
264
        }
265
    }
266

267
    /**
268
     * Is it possible to add enrol instance via standard UI?
269
     *
270
     * @param int $courseid id of the course to add the instance to
271
     * @return boolean
272
     */
273
    public function can_add_instance($courseid) {
274
        return has_capability('enrol/coursecompleted:manage', context_course::instance($courseid));
2✔
275
    }
276

277
    /**
278
     * Is it possible to delete enrol instance via standard UI?
279
     *
280
     * @param object $instance
281
     * @return bool
282
     */
283
    public function can_delete_instance($instance) {
284
        return has_capability('enrol/coursecompleted:manage', context_course::instance($instance->courseid));
2✔
285
    }
286

287
    /**
288
     * Is it possible to hide/show enrol instance via standard UI?
289
     *
290
     * @param stdClass $instance
291
     * @return bool
292
     */
293
    public function can_hide_show_instance($instance): bool {
294
        return has_capability('enrol/coursecompleted:manage', context_course::instance($instance->courseid));
2✔
295
    }
296

297
    /**
298
     * Does this plugin allow manual unenrolment of all users?
299
     *
300
     * @param stdClass $instance course enrol instance
301
     * @return bool - true means user with 'enrol/xxx:unenrol' may unenrol others freely
302
     */
303
    public function allow_unenrol(stdClass $instance): bool {
304
        return has_capability('enrol/coursecompleted:manage', context_course::instance($instance->courseid));
3✔
305
    }
306

307
    /**
308
     * Does this plugin allow manual changes in user_enrolments table?
309
     *
310
     * @param stdClass $instance course enrol instance
311
     * @return bool - true means it is possible to change enrol period and status in user_enrolments table
312
     */
313
    public function allow_manage(stdClass $instance): bool {
314
        return has_capability('enrol/coursecompleted:manage', context_course::instance($instance->courseid));
3✔
315
    }
316

317
    /**
318
     * Does this plugin shows enrol me?
319
     *
320
     * @param stdClass $instance course enrol instance
321
     * @return bool - true means it is possible to enrol.
322
     */
323
    public function show_enrolme_link(stdClass $instance): bool {
324
        return ($instance->status == ENROL_INSTANCE_ENABLED);
1✔
325
    }
326

327
    /**
328
     * Execute synchronisation.
329
     * @param progress_trace $trace
330
     * @return int exit code, 0 means ok
331
     */
332
    public function sync(progress_trace $trace): int {
333
        $this->process_expirations($trace);
1✔
334
        return 0;
1✔
335
    }
336

337
    /**
338
     * We are a good plugin and don't invent our own UI/validation code path.
339
     *
340
     * @return bool
341
     */
342
    public function use_standard_editing_ui(): bool {
343
        return true;
2✔
344
    }
345

346
    /**
347
     * Add elements to the edit instance form.
348
     *
349
     * @param stdClass $instance
350
     * @param \MoodleQuickForm $mform
351
     * @param context $context
352
     * @return bool
353
     */
354
    public function edit_instance_form($instance, \MoodleQuickForm $mform, $context) {
355
        $options = [ENROL_INSTANCE_ENABLED => get_string('yes'), ENROL_INSTANCE_DISABLED => get_string('no')];
2✔
356
        $mform->addElement('select', 'status', get_string('enabled', 'admin'), $options);
2✔
357
        $mform->setDefault('status', $this->get_config('status'));
2✔
358

359
        $role = $this->get_config('roleid');
2✔
360
        $start = time();
2✔
361
        if ($instance) {
2✔
362
            if (isset($instance->roleid)) {
2✔
363
                $role = $instance->roleid;
2✔
364
            }
365
            if (isset($instance->customint1)) {
2✔
366
                $start = get_course($instance->customint1)->startdate;
2✔
367
            }
368
        }
369
        $roles = get_default_enrol_roles($context, $role);
2✔
370
        $mform->addElement('select', 'roleid', get_string('assignrole', 'enrol_fee'), $roles);
2✔
371
        $mform->setDefault('roleid', $this->get_config('roleid'));
2✔
372

373
        $arr = ['optional' => true, 'defaulttime' => $start];
2✔
374
        $mform->addElement('date_time_selector', 'customint4', get_string('enroldate', 'enrol_coursecompleted'), $arr);
2✔
375
        $mform->addHelpButton('customint4', 'enroldate', 'enrol_coursecompleted');
2✔
376

377
        $arr = ['optional' => true, 'defaultunit' => 86400];
2✔
378
        $mform->addElement('duration', 'enrolperiod', get_string('enrolperiod', 'enrol_coursecompleted'), $arr);
2✔
379
        $mform->setDefault('enrolperiod', $this->get_config('enrolperiod'));
2✔
380
        $mform->addHelpButton('enrolperiod', 'enrolperiod', 'enrol_coursecompleted');
2✔
381

382
        $conditions = ['onlywithcompletion' => true, 'multiple' => false, 'includefrontpage' => false];
2✔
383
        $mform->addElement('course', 'customint1', get_string('course'), $conditions);
2✔
384
        $mform->addRule('customint1', get_string('required'), 'required', null, 'client');
2✔
385
        $mform->addHelpButton('customint1', 'compcourse', 'enrol_coursecompleted');
2✔
386

387
        $mform->addElement('advcheckbox', 'customint3', get_string('groups'), get_string('group', 'enrol_coursecompleted'));
2✔
388
        $mform->addHelpButton('customint3', 'group', 'enrol_coursecompleted');
2✔
389
        $mform->setDefault('customint3', $this->get_config('keepgroup'));
2✔
390

391
        $options = self::email_options();
2✔
392
        $mform->addElement('select', 'customint2', get_string('categoryemail', 'admin'), $options);
2✔
393

394
        $mform->addHelpButton('customint2', 'welcome', 'enrol_coursecompleted');
2✔
395
        $mform->setDefault('customint2', $this->get_config('welcome'));
2✔
396

397
        $arr = ['cols' => '60', 'rows' => '8'];
2✔
398
        $mform->addElement('textarea', 'customtext1', get_string('customwelcome', 'enrol_coursecompleted'), $arr);
2✔
399
        $mform->addHelpButton('customtext1', 'customwelcome', 'enrol_coursecompleted');
2✔
400
        $mform->disabledIf('customtext1', 'customint2', 'notchecked');
2✔
401

402
        $arr = ['optional' => true, 'defaulttime' => $start];
2✔
403
        $mform->addElement('date_time_selector', 'enrolstartdate', get_string('enrolstartdate', 'enrol_coursecompleted'), $arr);
2✔
404
        $mform->addHelpButton('enrolstartdate', 'enrolstartdate', 'enrol_coursecompleted');
2✔
405

406
        $duration = intval(get_config('moodlecourse', 'courseduration')) ?? YEARSECS;
2✔
407
        $arr['defaulttime'] = $start + $duration;
2✔
408
        $mform->addElement('date_time_selector', 'enrolenddate', get_string('enrolenddate', 'enrol_coursecompleted'), $arr);
2✔
409
        $mform->addHelpButton('enrolenddate', 'enrolenddate', 'enrol_coursecompleted');
2✔
410
    }
411

412
    /**
413
     * Get email options.
414
     * @return array of options
415
     */
416
    public static function email_options(): array {
417
        $options = enrol_send_welcome_email_options();
4✔
418
        unset($options[ENROL_SEND_EMAIL_FROM_KEY_HOLDER]);
4✔
419
        return $options;
4✔
420
    }
421

422
    /**
423
     * Add new instance of enrol plugin.
424
     * @param object $course
425
     * @param array|null $fields
426
     * @return int id of new instance, null if can not be created
427
     */
428
    public function add_instance($course, ?array $fields = null): int {
429
        if ($fields) {
38✔
430
            if (!isset($fields['customint2'])) {
38✔
431
                $fields['customint2'] = $this->get_config('welcome', 1);
37✔
432
            }
433
            if (!isset($fields['customint3'])) {
38✔
434
                $fields['customint3'] = $this->get_config('keepgroup', 1);
38✔
435
            }
436
        }
437
        return parent::add_instance($course, $fields);
38✔
438
    }
439

440
    /**
441
     * Update instance of enrol plugin.
442
     *
443
     * @param stdClass $instance
444
     * @param stdClass $data modified instance fields
445
     * @return boolean
446
     */
447
    public function update_instance($instance, $data) {
448
        $update = parent::update_instance($instance, $data);
1✔
449
        $hook = new \core_enrol\hook\after_enrol_instance_status_updated(enrolinstance: $instance, newstatus: $data->status);
1✔
450
        \core\di::get(\core\hook\manager::class)->dispatch($hook);
1✔
451
        return $update;
1✔
452
    }
453

454
    /**
455
     * Perform custom validation of the data used to edit the instance.
456
     *
457
     * @param array $data array of ("fieldname"=>value) of submitted data
458
     * @param array $files array of uploaded files "element_name"=>tmp_file_path
459
     * @param object $instance The instance loaded from the DB
460
     * @param context $context The context of the instance we are editing
461
     * @return array of "element_name"=>"error_description" if there are errors,
462
     */
463
    public function edit_instance_validation($data, $files, $instance, $context): array {
464
        $errors = [];
1✔
465
        if (!empty($data['enrolenddate'])) {
1✔
466
            // Minimum duration of a course is one hour.
467
            if ($data['enrolenddate'] < $data['enrolstartdate'] + 3600) {
1✔
468
                $errors['enrolenddate'] = get_string('enrolenddaterror', 'enrol_fee');
1✔
469
            }
470
        }
471

472
        if (
473
            empty($data['customint1']) ||
1✔
474
            !context_course::instance($data['customint1'], IGNORE_MISSING)
1✔
475
        ) {
476
            $errors['customint1'] = get_string('error_nonexistingcourse', 'tool_generator');
1✔
477
        }
478
        return $errors;
1✔
479
    }
480

481
    /**
482
     * Build (possible) coursepath
483
     *
484
     * @param stdClass $instance
485
     * @return array $items
486
     */
487
    private function build_course_path(stdClass $instance): array {
488
        $parents = $this->search_parents($instance->customint1);
5✔
489
        $children = $this->search_children($instance->courseid);
5✔
490
        return array_unique(array_merge($parents, $children));
5✔
491
    }
492

493
    /**
494
     * Search parents
495
     *
496
     * @param int $id
497
     * @param int $level
498
     * @return array items
499
     */
500
    private function search_parents($id, $level = 1): array {
501
        global $DB;
502
        $arr = [$id];
5✔
503
        if ($level < 5) {
5✔
504
            $level++;
5✔
505
            $params = ['enrol' => 'coursecompleted', 'courseid' => $id];
5✔
506
            if ($parent = $DB->get_field('enrol', 'customint1', $params, IGNORE_MULTIPLE)) {
5✔
507
                $arr = array_merge($this->search_parents($parent, $level), $arr);
2✔
508
            }
509
        }
510
        return $arr;
5✔
511
    }
512

513
    /**
514
     * Search children
515
     *
516
     * @param int $id
517
     * @param int $level
518
     * @return array items
519
     */
520
    private function search_children($id, $level = 1): array {
521
        global $DB;
522
        $arr = [$id];
5✔
523
        if ($level < 5) {
5✔
524
            $level++;
5✔
525
            $params = ['enrol' => 'coursecompleted', 'customint1' => $id];
5✔
526
            if ($child = $DB->get_field('enrol', 'courseid', $params, IGNORE_MULTIPLE)) {
5✔
527
                $arr = array_merge($arr, $this->search_children($child, $level));
3✔
528
            }
529
        }
530
        return $arr;
5✔
531
    }
532

533
    /**
534
     * Is this instance active?
535
     *
536
     * @param stdClass $instance
537
     * @return bool
538
     */
539
    private function is_active($instance): bool {
540
        $time = time();
23✔
541
        $start = is_null($instance->enrolstartdate) ? 0 : $instance->enrolstartdate;
23✔
542
        if ($start > $time) {
23✔
543
            return false;
2✔
544
        }
545
        $end = is_null($instance->enrolenddate) ? 0 : $instance->enrolenddate;
22✔
546
        if ($end != 0 && $end < $time) {
22✔
547
            // Past enrolment.
548
            return false;
2✔
549
        }
550
        return true;
20✔
551
    }
552

553
    /**
554
     * Returns true if the plugin has one or more bulk operations that can be performed on
555
     * user enrolments.
556
     *
557
     * @param \course_enrolment_manager $manager
558
     * @return bool
559
     */
560
    public function has_bulk_operations(\course_enrolment_manager $manager): bool {
561
        $instances = $manager->get_enrolment_instances();
1✔
562
        $return = false;
1✔
563
        foreach ($instances as $instance) {
1✔
564
            if ($instance->enrol === 'coursecompleted') {
1✔
565
                $return = true;
1✔
566
            }
567
        }
568
        return $return;
1✔
569
    }
570

571
    /**
572
     * The enrol plugin has bulk operations that can be performed.
573
     * @param \course_enrolment_manager $manager
574
     * @return array
575
     */
576
    public function get_bulk_operations(\course_enrolment_manager $manager): array {
577
        $context = $manager->get_context();
1✔
578
        $bulkoperations = [];
1✔
579
        if ($this->has_bulk_operations($manager)) {
1✔
580
            if (has_capability("enrol/coursecompleted:manage", $context)) {
1✔
581
                $bulkoperations['editselectedusers'] = new \enrol_coursecompleted\bulkedit($manager, $this);
1✔
582
            }
583
            if (has_capability("enrol/coursecompleted:unenrol", $context)) {
1✔
584
                $bulkoperations['deleteselectedusers'] = new \enrol_coursecompleted\bulkdelete($manager, $this);
1✔
585
            }
586
        }
587
        return $bulkoperations;
1✔
588
    }
589

590
    /**
591
     * Get all candidates for an enrolment.
592
     * @param int $courseid
593
     * @return array
594
     */
595
    public static function get_candidates(int $courseid): array {
596
        global $DB;
597
        $condition = 'course = ? AND timecompleted > 0';
3✔
598
        $return = $DB->get_fieldset_select('course_completions', 'userid', $condition, [$courseid]);
3✔
599
        return $return ?? [];
3✔
600
    }
601
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc