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

ewallah / moodle-enrol_coursecompleted / 16692172639

02 Aug 2025 09:22AM UTC coverage: 99.425% (-0.6%) from 100.0%
16692172639

push

github

rdebleu
tests

346 of 348 relevant lines covered (99.43%)

60.93 hits per line

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

99.07
/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
declare(strict_types=1);
27

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

375
        $arr = ['optional' => true, 'defaulttime' => $start];
16✔
376
        $mform->addElement('date_time_selector', 'customint4', get_string('enroldate', $plugin), $arr);
16✔
377
        $mform->addHelpButton('customint4', 'enroldate', $plugin);
16✔
378

379
        $arr = ['optional' => true, 'defaultunit' => 86400];
16✔
380
        $mform->addElement('duration', 'enrolperiod', get_string('enrolperiod', $plugin), $arr);
16✔
381
        $mform->setDefault('enrolperiod', $this->get_config('enrolperiod'));
16✔
382
        $mform->addHelpButton('enrolperiod', 'enrolperiod', $plugin);
16✔
383

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

389
        $mform->addElement('advcheckbox', 'customint5', get_string('unenrol', 'enrol'), get_string('tryunenrol', $plugin));
16✔
390
        $mform->addHelpButton('customint5', 'tryunenrol', $plugin);
16✔
391
        $mform->setDefault('customint5', $this->get_config('tryunenrol'));
16✔
392

393
        $mform->addElement('advcheckbox', 'customint3', get_string('groups'), get_string('group', $plugin));
16✔
394
        $mform->addHelpButton('customint3', 'group', $plugin);
16✔
395
        $mform->setDefault('customint3', $this->get_config('keepgroup'));
16✔
396

397
        $options = self::email_options();
16✔
398
        $mform->addElement('select', 'customint2', get_string('categoryemail', 'admin'), $options);
16✔
399

400
        $mform->addHelpButton('customint2', 'welcome', $plugin);
16✔
401
        $mform->setDefault('customint2', $this->get_config('welcome'));
16✔
402

403
        $arr = ['cols' => '60', 'rows' => '8'];
16✔
404
        $mform->addElement('textarea', 'customtext1', get_string('customwelcome', $plugin), $arr);
16✔
405
        $mform->addHelpButton('customtext1', 'customwelcome', $plugin);
16✔
406
        $mform->disabledIf('customtext1', 'customint2', 'eq', 0);
16✔
407

408
        $arr = ['optional' => true, 'defaulttime' => $start];
16✔
409
        $mform->addElement('date_time_selector', 'enrolstartdate', get_string('enrolstartdate', $plugin), $arr);
16✔
410
        $mform->addHelpButton('enrolstartdate', 'enrolstartdate', $plugin);
16✔
411

412
        $arr['defaulttime'] = $start + get_config('moodlecourse', 'courseduration');
16✔
413
        $mform->addElement('date_time_selector', 'enrolenddate', get_string('enrolenddate', $plugin), $arr);
16✔
414
        $mform->addHelpButton('enrolenddate', 'enrolenddate', 'enrol_coursecompleted');
16✔
415
    }
416

417
    /**
418
     * Get email options.
419
     * @return array of options
420
     */
421
    public static function email_options(): array {
422
        $options = enrol_send_welcome_email_options();
32✔
423
        unset($options[ENROL_SEND_EMAIL_FROM_KEY_HOLDER]);
32✔
424
        return $options;
32✔
425
    }
426

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

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

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

477
        if (
478
            empty($data['customint1']) ||
8✔
479
            !context_course::instance($data['customint1'], IGNORE_MISSING)
8✔
480
        ) {
481
            $errors['customint1'] = get_string('error_nonexistingcourse', 'tool_generator');
8✔
482
        }
483
        return $errors;
8✔
484
    }
485

486
    /**
487
     * Build (possible) coursepath
488
     *
489
     * @param stdClass $instance
490
     * @return array $items
491
     */
492
    private function build_course_path(stdClass $instance): array {
493
        $parents = $this->search_parents($instance->customint1);
56✔
494
        $children = $this->search_children($instance->courseid);
56✔
495
        return array_unique(array_merge($parents, $children));
56✔
496
    }
497

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

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

538
    /**
539
     * Is this instance active?
540
     *
541
     * @param stdClass $instance
542
     * @return bool
543
     */
544
    private function is_active($instance): bool {
545
        $time = time();
281✔
546
        $start = is_null($instance->enrolstartdate) ? 0 : $instance->enrolstartdate;
281✔
547
        if ($start > $time) {
281✔
548
            return false;
16✔
549
        }
550
        $end = is_null($instance->enrolenddate) ? 0 : $instance->enrolenddate;
273✔
551
        if ($end != 0 && $end < $time) {
273✔
552
            // Past enrolment.
553
            return false;
16✔
554
        }
555
        return true;
257✔
556
    }
557

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

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

595
    /**
596
     * Get all candidates for an enrolment.
597
     * @param int $courseid
598
     * @return array
599
     */
600
    public static function get_candidates(int $courseid): array {
601
        global $DB;
602
        $condition = 'course = ? AND timecompleted > 0';
24✔
603
        $return = $DB->get_fieldset_select('course_completions', 'userid', $condition, [$courseid]);
24✔
604
        return $return ?? [];
24✔
605
    }
606
}
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