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

ewallah / moodle-availability_coursecompleted / 26175215469

20 May 2026 04:15PM UTC coverage: 97.222% (-2.8%) from 100.0%
26175215469

push

github

rdebleu
MDL Shield

3 of 5 new or added lines in 1 file covered. (60.0%)

70 of 72 relevant lines covered (97.22%)

6.83 hits per line

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

96.23
/classes/condition.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
 * Condition main class.
19
 *
20
 * @package   availability_coursecompleted
21
 * @copyright iplusacademy (www.iplusacademy.org)
22
 * @author    Renaat Debleu <info@eWallah.net>
23
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25

26
namespace availability_coursecompleted;
27

28
use completion_info;
29
use core_availability\info;
30
use coding_exception;
31
use moodle_url;
32
use stdClass;
33

34
/**
35
 * Condition main class.
36
 *
37
 * @package   availability_coursecompleted
38
 * @copyright iplusacademy (www.iplusacademy.org)
39
 * @author    Renaat Debleu <info@eWallah.net>
40
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
41
 */
42
class condition extends \core_availability\condition {
43
    /** @var bool completed Is course completed or not */
44
    protected bool $completed;
45

46
    /** @var int courseid 0 => Current course, 2 => course 2 ... */
47
    protected int $courseid = 0;
48

49
    /**
50
     * Constructor.
51
     *
52
     * @param stdClass $structure Data structure from JSON decode
53
     */
54
    public function __construct($structure) {
55
        $this->completed = property_exists($structure, 'id') && $structure->id;
40✔
56
        $this->courseid = property_exists($structure, 'courseid') ? $structure->courseid : 0;
40✔
57
    }
58

59
    /**
60
     * Saves tree data back to a structure object.
61
     *
62
     * @return stdClass Structure object (ready to be made into JSON format)
63
     */
64
    public function save() {
65
        return (object)['type' => 'coursecompleted', 'id' => $this->completed, 'courseid' => $this->courseid];
4✔
66
    }
67

68
    /**
69
     * Returns a JSON object which corresponds to a condition of this type.
70
     *
71
     * @param bool $completed Completed or not, default false
72
     * @param int $courseid Course id, default 0
73
     * @return stdClass Object representing condition
74
     */
75
    public static function get_json(bool $completed = false, int $courseid = 0) {
76
        return (object)['type' => 'coursecompleted', 'id' => $completed, 'courseid' => $courseid];
4✔
77
    }
78

79
    /**
80
     * Determines whether an item is currently available according to this availability condition.
81
     *
82
     * @param bool $not Set true if we are inverting the condition
83
     * @param info $info Item we're checking
84
     * @param bool $grabthelot (works only for current user)
85
     * @param int $userid User ID to check availability for
86
     * @return bool True if available
87
     */
88
    public function is_available($not, info $info, $grabthelot, $userid) {
89
        $cache = \cache::make('core', 'coursecompletion');
12✔
90
        try {
91
            $course = $this->courseid === 0 ? $info->get_course() : get_course($this->courseid);
12✔
92
        } catch (\exception) {
4✔
93
            // Deleted courses alwas return false.
94
            return false;
4✔
95
        }
96

97
        $values = $cache->get("{$userid}_{$course->id}");
12✔
98
        if ($values && $value = current($values)) {
12✔
99
            $allow = (bool)$value->timecompleted;
8✔
100
        } else {
101
            $completioninfo = new \completion_info($course);
12✔
102
            $allow = $completioninfo->is_course_complete($userid);
12✔
103
            unset($completioninfo);
12✔
104
        }
105

106
        if (!$this->completed) {
12✔
107
            $allow = !$allow;
8✔
108
        }
109

110
        if ($not) {
12✔
111
            return !$allow;
4✔
112
        }
113

114
        return $allow;
12✔
115
    }
116

117
    /**
118
     * Obtains a string describing this restriction (whether or not it actually applies).
119
     *
120
     * @param bool $full Set true if this is the 'full information' view
121
     * @param bool $not Set true if we are inverting the condition
122
     * @param info $info Item we're checking
123
     * @return string Information string (for admin) about all restrictions on this item
124
     */
125
    public function get_description($full, $not, info $info) {
126
        global $CFG;
127
        $allow = $this->completed;
8✔
128
        if ($not) {
8✔
129
            $allow = !$allow;
8✔
130
        }
131

132
        if ($this->courseid === 0) {
8✔
133
            return get_string($allow ? 'getdescription' : 'getdescriptionnot', 'availability_coursecompleted');
8✔
134
        }
135

136
        try {
137
            $course = get_course($this->courseid);
4✔
NEW
138
        } catch (\dml_missing_record_exception) {
×
NEW
139
            return get_string('missing', 'availability_coursecompleted');
×
140
        }
141
        $rawname = empty($CFG->navshowfullcoursenames) ? $course->shortname : $course->fullname;
4✔
142
        $name = self::description_format_string($rawname);
4✔
143
        $url = \html_writer::link(new moodle_url('/course/view.php', ['id' => $this->courseid]), $name);
4✔
144
        return get_string($allow ? 'getotherdescription' : 'getotherdescriptionnot', 'availability_coursecompleted', $url);
4✔
145
    }
146

147
    /**
148
     * Obtains a representation of the options of this condition as a string for debugging.
149
     *
150
     * @return string Text representation of parameters
151
     */
152
    protected function get_debug_string() {
153
        if ($this->courseid === 0) {
8✔
154
            return get_string($this->completed ? 'true' : 'false', 'mod_quiz');
8✔
155
        }
156

157
        $name = format_string(get_course($this->courseid)->shortname);
4✔
158
        return get_string($this->completed ? 'true' : 'false', 'mod_quiz') . ' ' . $name;
4✔
159
    }
160

161
    /**
162
     * Checks whether this condition applies to user lists.
163
     *
164
     * @return bool True if this condition applies to user lists
165
     */
166
    public function is_applied_to_user_lists() {
167
        // Course completions are assumed to be 'permanent'.
168
        return true;
4✔
169
    }
170

171
    /**
172
     * Tests against a user list. Users who cannot access the activity due to
173
     * availability restrictions will be removed from the list.
174
     *
175
     * @param array $users Array of userid => object
176
     * @param bool $not If tree's parent indicates it's being checked negatively
177
     * @param info $info Info about current context
178
     * @param capability_checker $checker Capability checker
179
     * @return array Filtered version of input array
180
     */
181
    public function filter_user_list(
182
        array $users,
183
        $not,
184
        \core_availability\info $info,
185
        \core_availability\capability_checker $checker
186
    ) {
187

188
        global $DB;
189
        $result = [];
4✔
190
        // If the array is not empty.
191
        if ($users !== []) {
4✔
192
            $courseid = $this->courseid === 0 ? $info->get_course()->id : $this->courseid;
4✔
193
            $cond = $this->completed ? 'NOT' : '';
4✔
194
            $sql = "SELECT DISTINCT userid
4✔
195
                      FROM {course_completions}
196
                      WHERE timecompleted IS {$cond} NULL AND course = ?";
4✔
197
            $compusers = $DB->get_records_sql($sql, [$courseid]);
4✔
198

199
            // List users who have access to the completion report.
200
            $adusers = $checker->get_users_by_capability('report/completion:view');
4✔
201
            // Filter the user list.
202
            foreach ($users as $id => $user) {
4✔
203
                // Always include users with access to completion report.
204
                if (array_key_exists($id, $adusers)) {
4✔
205
                    $result[$id] = $user;
4✔
206
                } else {
207
                    // Other users are included or not based on course completion.
208
                    $allow = array_key_exists($id, $compusers);
4✔
209
                    if ($not) {
4✔
210
                        $allow = !$allow;
4✔
211
                    }
212

213
                    if ($allow) {
4✔
214
                        $result[$id] = $user;
4✔
215
                    }
216
                }
217
            }
218
        }
219

220
        return $result;
4✔
221
    }
222
}
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