Skip to content

Commit 8452f4d

Browse files
committed
issue #45: add user enrolment condition
1 parent 075c03e commit 8452f4d

File tree

4 files changed

+942
-2
lines changed

4 files changed

+942
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,342 @@
1+
<?php
2+
// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.
16+
17+
namespace tool_dynamic_cohorts\local\tool_dynamic_cohorts\condition;
18+
19+
use tool_dynamic_cohorts\condition_base;
20+
use tool_dynamic_cohorts\condition_sql;
21+
use context_course;
22+
23+
/**
24+
* Condition based on user's enrolment.
25+
*
26+
* @package tool_dynamic_cohorts
27+
* @copyright 2024 Catalyst IT
28+
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
29+
*/
30+
class user_enrolment extends condition_base {
31+
32+
/**
33+
* Operator for not enrolled users.
34+
*/
35+
public const OPERATOR_NOT_ENROLLED = 0;
36+
37+
/**
38+
* Operator for enrolled users.
39+
*/
40+
public const OPERATOR_ENROLLED = 1;
41+
42+
/**
43+
* Condition name.
44+
*
45+
* @return string
46+
*/
47+
public function get_name(): string {
48+
return get_string('condition:user_enrolment', 'tool_dynamic_cohorts');
49+
}
50+
51+
/**
52+
* Gets a list of all roles.
53+
*
54+
* @return array
55+
*/
56+
protected function get_roles(): array {
57+
$roles = [
58+
0 => get_string('any'),
59+
];
60+
foreach (role_get_names() as $role) {
61+
if ($role->archetype === 'guest') {
62+
continue;
63+
}
64+
$roles[$role->id] = $role->localname;
65+
}
66+
67+
return $roles;
68+
}
69+
70+
/**
71+
* Returns a list of enrolment methods.
72+
*
73+
* @return array
74+
*/
75+
protected function get_enrolment_methods(): array {
76+
$enrolmentmethods = ['' => get_string('any')];
77+
foreach (enrol_get_plugins(false) as $method) {
78+
$name = $method->get_name();
79+
$enrolmentmethods[$name] = get_string('pluginname', 'enrol_' . $name);
80+
}
81+
82+
return $enrolmentmethods;
83+
}
84+
85+
/**
86+
* Gets a list of operators.
87+
*
88+
* @return array A list of operators.
89+
*/
90+
protected function get_operators(): array {
91+
return [
92+
self::OPERATOR_ENROLLED => get_string('enrolled', 'tool_dynamic_cohorts'),
93+
self::OPERATOR_NOT_ENROLLED => get_string('notenrolled', 'tool_dynamic_cohorts'),
94+
];
95+
}
96+
97+
/**
98+
* Add config form elements.
99+
*
100+
* @param \MoodleQuickForm $mform
101+
*/
102+
public function config_form_add(\MoodleQuickForm $mform): void {
103+
// Operator.
104+
$mform->addElement(
105+
'select',
106+
'operator',
107+
get_string('operator', 'tool_dynamic_cohorts'),
108+
$this->get_operators()
109+
);
110+
111+
// Course.
112+
$mform->addElement('course', 'courseid', get_string('course'));
113+
$mform->setType('courseid', PARAM_INT);
114+
$mform->hideIf('courseid', 'contextlevel', 'in', [CONTEXT_SYSTEM, CONTEXT_COURSECAT]);
115+
116+
// Enrolment method.
117+
$mform->addElement(
118+
'select',
119+
'enrolmethod',
120+
get_string('enrolmethod', 'tool_dynamic_cohorts'),
121+
$this->get_enrolment_methods()
122+
);
123+
$mform->setType('enrolmethod', PARAM_COMPONENT);
124+
125+
// Role.
126+
$mform->addElement('select', 'roleid', get_string('role'), $this->get_roles());
127+
$mform->setType('roleid', PARAM_INT);
128+
}
129+
130+
/**
131+
* Validate config form elements.
132+
*
133+
* @param array $data Data to validate.
134+
* @return array
135+
*/
136+
public function config_form_validate(array $data): array {
137+
$errors = [];
138+
139+
if (empty($data['courseid'])) {
140+
$errors['courseid'] = get_string('required');
141+
}
142+
143+
return $errors;
144+
}
145+
146+
/**
147+
* Gets operator.
148+
*
149+
* @return int
150+
*/
151+
protected function get_operator_value(): int {
152+
return $this->get_config_data()['operator'] ?? self::OPERATOR_ENROLLED;
153+
}
154+
155+
/**
156+
* Gets configured role ID.
157+
*
158+
* @return int
159+
*/
160+
protected function get_roleid_value(): int {
161+
return $this->get_config_data()['roleid'] ?? 0;
162+
}
163+
164+
/**
165+
* Gets configured enrolment method.
166+
*
167+
* @return string
168+
*/
169+
protected function get_enrolment_method_value(): string {
170+
return $this->get_config_data()['enrolmethod'] ?? '';
171+
}
172+
173+
/**
174+
* Gets configured course ID.
175+
*
176+
* @return int
177+
*/
178+
protected function get_courseid_value(): int {
179+
return $this->get_config_data()['courseid'] ?? 0;
180+
}
181+
182+
183+
/**
184+
* Human-readable description of the configured condition.
185+
*
186+
* @return string
187+
*/
188+
public function get_config_description(): string {
189+
global $DB;
190+
191+
$coursename = $DB->get_field('course', 'fullname', ['id' => $this->get_courseid_value()]);
192+
$coursename = format_string($coursename, true, ['context' => \context_system::instance(), 'escape' => false]);
193+
194+
return get_string('condition:user_enrolment_description', 'tool_dynamic_cohorts', (object) [
195+
'operator' => strtolower($this->get_operators()[$this->get_operator_value()]),
196+
'role' => $this->get_roles()[$this->get_roleid_value()],
197+
'coursename' => $coursename,
198+
'courseid' => $this->get_courseid_value(),
199+
'enrolmethod' => $this->get_enrolment_methods()[$this->get_enrolment_method_value()],
200+
]);
201+
}
202+
203+
/**
204+
* Human-readable description of the broken condition.
205+
*
206+
* @return string
207+
*/
208+
public function get_broken_description(): string {
209+
global $DB;
210+
211+
// Missing role.
212+
if (!$DB->get_record('role', ['id' => $this->get_roleid_value()])) {
213+
return get_string('missingrole', 'tool_dynamic_cohorts');
214+
}
215+
216+
// Missing course.
217+
if (!$DB->get_record('course', ['id' => $this->get_courseid_value()])) {
218+
return get_string('missingcourse', 'tool_dynamic_cohorts');
219+
}
220+
221+
// Missing enrolment method.
222+
if (!empty($this->get_enrolment_method_value())
223+
&& !array_key_exists($this->get_enrolment_method_value(), $this->get_enrolment_methods())) {
224+
return get_string('missingenrolmentmethod', 'tool_dynamic_cohorts', $this->get_enrolment_method_value());
225+
}
226+
227+
return parent::get_broken_description();
228+
}
229+
230+
/**
231+
* Gets SQL data for building SQL.
232+
*
233+
* @return condition_sql
234+
*/
235+
public function get_sql(): condition_sql {
236+
$sql = new condition_sql('', '1=0', []);
237+
238+
if (!$this->is_broken()) {
239+
$join = '';
240+
$params = [];
241+
242+
// Enrolment tables.
243+
$uetable = condition_sql::generate_table_alias();
244+
$enroltable = condition_sql::generate_table_alias();
245+
246+
// Course parameter.
247+
$courseidparam = condition_sql::generate_param_alias();
248+
$params[$courseidparam] = $this->get_courseid_value();
249+
250+
// In case we are filtering by enrolment method.
251+
$enrolmethodwhere = '';
252+
$enrolmethod = $this->get_enrolment_method_value();
253+
if (!empty($enrolmethod)) {
254+
$enrolmethodparam = condition_sql::generate_param_alias();
255+
$params[$enrolmethodparam] = $enrolmethod;
256+
$enrolmethodwhere = "AND $enroltable.enrol = :{$enrolmethodparam}";
257+
}
258+
259+
// In case we are filtering by role.
260+
$rolesql = '';
261+
$rolewhere = '';
262+
$roleid = $this->get_roleid_value();
263+
if (!empty($roleid)) {
264+
$outertable = condition_sql::generate_table_alias();
265+
$ratable = condition_sql::generate_table_alias();
266+
$roleidparam = condition_sql::generate_param_alias();
267+
$params[$roleidparam] = $roleid;
268+
269+
$context = context_course::instance($this->get_courseid_value());
270+
$contexidtparam = condition_sql::generate_param_alias();
271+
$params[$contexidtparam] = $context->id;
272+
273+
$rolesql = "LEFT JOIN (SELECT $ratable.userid
274+
FROM {role_assignments} $ratable
275+
WHERE $ratable.roleid = :{$roleidparam}
276+
AND $ratable.contextid = :{$contexidtparam}
277+
) {$outertable} ON u.id = {$outertable}.userid ";
278+
279+
$rolewhere = "AND {$outertable}.userid is NOT NULL";
280+
}
281+
282+
$operator = $this->get_operator_value() == self::OPERATOR_ENROLLED ? 'EXISTS' : 'NOT EXISTS';
283+
284+
$where = "{$operator} (SELECT 1 FROM {user_enrolments} {$uetable}
285+
JOIN {enrol} {$enroltable} ON ({$enroltable}.id = {$uetable}.enrolid AND {$enroltable}.status = 0)
286+
$rolesql
287+
WHERE {$uetable}.userid = u.id
288+
AND $enroltable.courseid = :{$courseidparam}
289+
AND {$uetable}.status = 0
290+
$enrolmethodwhere
291+
$rolewhere)";
292+
293+
$sql = new condition_sql($join, $where, $params);
294+
}
295+
296+
return $sql;
297+
}
298+
299+
/**
300+
* Is condition broken.
301+
*
302+
* @return bool
303+
*/
304+
public function is_broken(): bool {
305+
global $DB;
306+
307+
if ($this->get_config_data()) {
308+
// Check role exists.
309+
if (!empty($this->get_roleid_value()) && !$DB->get_record('role', ['id' => $this->get_roleid_value()])) {
310+
return true;
311+
}
312+
313+
// Check course exists.
314+
if (!$DB->get_record('course', ['id' => $this->get_courseid_value()])) {
315+
return true;
316+
}
317+
318+
// Enrolment method is not valid.
319+
if (!empty($this->get_enrolment_method_value())
320+
&& !array_key_exists($this->get_enrolment_method_value(), $this->get_enrolment_methods())) {
321+
return true;
322+
}
323+
}
324+
325+
return false;
326+
}
327+
328+
/**
329+
* Gets a list of event classes the condition will be triggered on.
330+
*
331+
* @return string[]
332+
*/
333+
public function get_events(): array {
334+
return [
335+
'core\event\role_assigned',
336+
'core\event\role_unassigned',
337+
'core\event\user_enrolment_created',
338+
'core\event\user_enrolment_updated',
339+
'core\event\user_enrolment_deleted',
340+
];
341+
}
342+
}

lang/en/tool_dynamic_cohorts.php

+6
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@
6060
$string['condition:course_not_completed'] = 'Course not completed';
6161
$string['condition:course_not_completed_description'] = 'Users who have not completed course "{$a->course}"';
6262
$string['condition:profile_field_description'] = 'Users with {$a->field} {$a->fieldoperator} {$a->fieldvalue}';
63+
$string['condition:user_enrolment'] = 'User enrolment';
64+
$string['condition:user_enrolment_description'] = 'Users who are {$a->operator} into course "{$a->coursename}" (id {$a->courseid}) with "{$a->role}" role using "{$a->enrolmethod}" enrolment method';
6365
$string['condition:user_last_login'] = 'User last login';
6466
$string['condition:user_created'] = 'User created time';
6567
$string['condition:user_profile'] = 'User standard profile field';
@@ -82,6 +84,8 @@
8284
$string['edit_rule'] = 'Edit rule';
8385
$string['enabled'] = 'Enabled';
8486
$string['enable_confirm'] = 'Are you sure you want to enable rule {$a}?';
87+
$string['enrolmethod'] = 'Enrolment method';
88+
$string['enrolled'] = 'Enrolled';
8589
$string['ever'] = 'Ever';
8690
$string['everloggedin'] = 'Users who have logged in at least once';
8791
$string['event:conditioncreated'] = 'Condition created';
@@ -112,11 +116,13 @@
112116
$string['matchingusers'] = 'Matching users';
113117
$string['missingcourse'] = 'Missing course';
114118
$string['missingcoursecat'] = 'Missing course category';
119+
$string['missingenrolmentmethod'] = 'Missing enrolment method {$a}';
115120
$string['missingrole'] = 'Missing role';
116121
$string['name'] = 'Rule name';
117122
$string['name_help'] = 'A human readable name of this rule.';
118123
$string['never'] = 'Never';
119124
$string['neverloggedin'] = 'Users who have never logged in';
125+
$string['notenrolled'] = 'Not enrolled';
120126
$string['operator'] = 'Operator';
121127
$string['or'] = 'OR';
122128
$string['pleaseselectcohort'] = 'Please select a cohort';

0 commit comments

Comments
 (0)