2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
19 use core_enrol_external
;
20 use core_external\external_api
;
21 use enrol_user_enrolment_form
;
22 use externallib_advanced_testcase
;
25 defined('MOODLE_INTERNAL') ||
die();
29 require_once($CFG->dirroot
. '/webservice/tests/helpers.php');
30 require_once($CFG->dirroot
. '/enrol/externallib.php');
33 * Enrol external PHPunit tests
37 * @copyright 2012 Jerome Mouneyrac
38 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
41 final class externallib_test
extends externallib_advanced_testcase
{
44 * dataProvider for test_get_enrolled_users_visibility().
46 public static function get_enrolled_users_visibility_provider(): array {
48 'Course without groups, default behavior (not filtering by cap, group, active)' =>
51 'coursegroupmode' => NOGROUPS
,
52 'withcapability' => null,
54 'onlyactive' => false,
55 'allowedcaps' => array(),
57 'results' => array( // Everybody can view everybody.
58 'user0' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
59 'user1' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
60 'user2' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
61 'user31' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
62 'userall' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
66 'Course with visible groups, default behavior (not filtering by cap, group, active)' =>
69 'coursegroupmode' => VISIBLEGROUPS
,
70 'withcapability' => null,
72 'onlyactive' => false,
73 'allowedcaps' => array(),
75 'results' => array( // Everybody can view everybody.
76 'user0' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
77 'user1' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
78 'user2' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
79 'user31' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
80 'userall' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
84 'Course with separate groups, default behavior (not filtering by cap, group, active)' =>
87 'coursegroupmode' => SEPARATEGROUPS
,
88 'withcapability' => null,
90 'onlyactive' => false,
91 'allowedcaps' => array(),
93 'results' => array( // Only users from own groups are visible.
94 'user0' => array('canview' => array()), // Poor guy, cannot see anybody, himself included.
95 'user1' => array('canview' => array('user1', 'userall')),
96 'user2' => array('canview' => array('user2', 'user2su', 'userall')),
97 'user31' => array('canview' => array('user31', 'user32', 'userall')),
98 'userall' => array('canview' => array('user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
102 'Course with separate groups, default behavior (not filtering but having moodle/site:accessallgroups)' =>
105 'coursegroupmode' => VISIBLEGROUPS
,
106 'withcapability' => null,
108 'onlyactive' => false,
109 'allowedcaps' => array('moodle/site:accessallgroups'),
111 'results' => array( // Everybody can view everybody.
112 'user0' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
113 'user1' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
114 'user2' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
115 'user31' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
116 'userall' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
120 'Course with separate groups, filtering onlyactive (missing moodle/course:enrolreview)' =>
123 'coursegroupmode' => SEPARATEGROUPS
,
124 'withcapability' => null,
126 'onlyactive' => true,
127 'allowedcaps' => array(),
129 'results' => array( // returns exception, cannot view anybody without the cap.
130 'user2' => array('exception' => array(
131 'type' => 'required_capability_exception',
132 'message' => 'Review course enrolments')),
133 'userall' => array('exception' => array(
134 'type' => 'required_capability_exception',
135 'message' => 'Review course enrolments')),
139 'Course with separate groups, filtering onlyactive (having moodle/course:enrolreview)' =>
142 'coursegroupmode' => SEPARATEGROUPS
,
143 'withcapability' => null,
145 'onlyactive' => true,
146 'allowedcaps' => array('moodle/course:enrolreview'),
148 'results' => array( // Suspended are not returned.
149 'user2' => array('canview' => array('user2', 'userall')),
150 'user31' => array('canview' => array('user31', 'user32', 'userall')),
151 'userall' => array('canview' => array('user1', 'user2', 'user31', 'user32', 'userall')),
155 'Course with separate groups, filtering by groupid (not having moodle/site:accessallgroups)' =>
158 'coursegroupmode' => SEPARATEGROUPS
,
159 'withcapability' => null,
160 'groupid' => 'group2',
161 'onlyactive' => false,
162 'allowedcaps' => array(),
164 'results' => array( // Only group 2 members and only for members. Exception for non-members.
165 'user0' => array('exception' => array(
166 'type' => 'required_capability_exception',
167 'message' => 'Access all groups')),
168 'user1' => array('exception' => array(
169 'type' => 'required_capability_exception',
170 'message' => 'Access all groups')),
171 'user2' => array('canview' => array('user2', 'user2su', 'userall')),
172 'userall' => array('canview' => array('user2', 'user2su', 'userall')),
176 'Course with separate groups, filtering by groupid (having moodle/site:accessallgroups)' =>
179 'coursegroupmode' => SEPARATEGROUPS
,
180 'withcapability' => null,
181 'groupid' => 'group2',
182 'onlyactive' => false,
183 'allowedcaps' => array('moodle/site:accessallgroups'),
185 'results' => array( // All users with 'moodle/site:accessallgroups' can view group 2
186 'user0' => array('canview' => array('user2', 'user2su', 'userall')),
187 'user1' => array('canview' => array('user2', 'user2su', 'userall')),
188 'user2' => array('canview' => array('user2', 'user2su', 'userall')),
189 'userall' => array('canview' => array('user2', 'user2su', 'userall')),
193 'Course with separate groups, filtering by withcapability (not having moodle/role:review)' =>
196 'coursegroupmode' => SEPARATEGROUPS
,
197 'withcapability' => 'moodle/course:bulkmessaging',
199 'onlyactive' => false,
200 'allowedcaps' => array(),
202 'results' => array( // No user has 'moodle/role:review' so exception.
203 'user0' => array('exception' => array(
204 'type' => 'required_capability_exception',
205 'message' => 'Review permissions for others')),
206 'user1' => array('exception' => array(
207 'type' => 'required_capability_exception',
208 'message' => 'Review permissions for others')),
209 'user2' => array('exception' => array(
210 'type' => 'required_capability_exception',
211 'message' => 'Review permissions for others')),
212 'userall' => array('exception' => array(
213 'type' => 'required_capability_exception',
214 'message' => 'Review permissions for others')),
218 'Course with separate groups, filtering by withcapability (having moodle/role:review)' =>
221 'coursegroupmode' => SEPARATEGROUPS
,
222 'withcapability' => 'moodle/course:bulkmessaging',
224 'onlyactive' => false,
225 'allowedcaps' => array('moodle/role:review'),
227 'results' => array( // No user has withcapability, but all have 'moodle/role:review'. Empties.
228 'user0' => array('canview' => array()),
229 'user1' => array('canview' => array()),
230 'user2' => array('canview' => array()),
231 'userall' => array('canview' => array()),
235 'Course with separate groups, filtering by withcapability (having moodle/role:review & moodle/course:bulkmessaging)' =>
238 'coursegroupmode' => SEPARATEGROUPS
,
239 'withcapability' => 'moodle/course:bulkmessaging',
241 'onlyactive' => false,
242 'allowedcaps' => array('moodle/role:review', 'moodle/course:bulkmessaging'),
244 'results' => array( // Users (previous) have withcapability, and all have 'moodle/role:review'.
245 'user0' => array('canview' => array()),
246 'user1' => array('canview' => array('user1')),
247 'user2' => array('canview' => array('user2')),
248 'userall' => array('canview' => array('user1', 'user2', 'userall')),
255 * Verify get_enrolled_users() returned users are the expected in every situation.
257 * @dataProvider get_enrolled_users_visibility_provider
259 public function test_get_enrolled_users_visibility($settings, $results): void
{
263 $this->resetAfterTest();
265 // Create the course and the users.
266 $course = $this->getDataGenerator()->create_course(array('groupmode' => $settings['coursegroupmode']));
267 $coursecontext = \context_course
::instance($course->id
);
268 $user0 = $this->getDataGenerator()->create_user(array('username' => 'user0')); // A user without group.
269 $user1 = $this->getDataGenerator()->create_user(array('username' => 'user1')); // User for group 1.
270 $user2 = $this->getDataGenerator()->create_user(array('username' => 'user2')); // Two users for group 2.
271 $user2su = $this->getDataGenerator()->create_user(array('username' => 'user2su')); // (one suspended).
272 $user31 = $this->getDataGenerator()->create_user(array('username' => 'user31')); // Two users for group 3.
273 $user32 = $this->getDataGenerator()->create_user(array('username' => 'user32')); // (both enabled).
274 $userall = $this->getDataGenerator()->create_user(array('username' => 'userall')); // A user in all groups.
276 // Create utility array of created users, to produce better assertion messages.
277 $createdusers = array();
278 foreach (array($user0, $user1, $user2, $user2su, $user31, $user32, $userall) as $createduser) {
279 $createdusers[$createduser->id
] = $createduser->username
;
282 // Enrol the users in the course.
283 $this->getDataGenerator()->enrol_user($user0->id
, $course->id
);
284 $this->getDataGenerator()->enrol_user($user1->id
, $course->id
);
285 $this->getDataGenerator()->enrol_user($user2->id
, $course->id
);
286 $this->getDataGenerator()->enrol_user($user2su->id
, $course->id
, null, 'manual', 0, 0, ENROL_USER_SUSPENDED
);
287 $this->getDataGenerator()->enrol_user($user31->id
, $course->id
);
288 $this->getDataGenerator()->enrol_user($user32->id
, $course->id
);
289 $this->getDataGenerator()->enrol_user($userall->id
, $course->id
);
292 $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id
));
293 $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id
));
294 $group3 = $this->getDataGenerator()->create_group(array('courseid' => $course->id
));
296 // Add the users to the groups.
297 $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id
, 'userid' => $user1->id
));
298 $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id
, 'userid' => $user2->id
));
299 $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id
, 'userid' => $user2su->id
));
300 $this->getDataGenerator()->create_group_member(array('groupid' => $group3->id
, 'userid' => $user31->id
));
301 $this->getDataGenerator()->create_group_member(array('groupid' => $group3->id
, 'userid' => $user32->id
));
302 $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id
, 'userid' => $userall->id
));
303 $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id
, 'userid' => $userall->id
));
304 $this->getDataGenerator()->create_group_member(array('groupid' => $group3->id
, 'userid' => $userall->id
));
306 // Create a role to add the allowedcaps. Users will have this role assigned.
307 $roleid = $this->getDataGenerator()->create_role();
308 // Allow the specified capabilities.
309 if (!empty($settings['allowedcaps'])) {
310 foreach ($settings['allowedcaps'] as $capability) {
311 assign_capability($capability, CAP_ALLOW
, $roleid, $coursecontext);
315 // For each of the users, configure everything, perform the call, and assert results.
316 foreach ($results as $user => $expectations) {
317 // Convert canview expectations into a nice array of ids for easier handling.
320 // Analyse the expectations.
321 if (isset($expectations['canview'])) {
322 foreach ($expectations['canview'] as $canviewuser) {
323 $canview[] = $createdusers[$
{$canviewuser}->id
];
325 } else if (isset($expectations['exception'])) {
326 $exception = $expectations['exception'];
327 $this->expectException($exception['type']);
328 $this->expectExceptionMessage($exception['message']);
330 // Failed, only canview and exception are supported.
331 throw new \
coding_exception('Incomplete, only canview and exception are supported');
334 // Switch to the user and assign the role.
335 $this->setUser($
{$user});
336 role_assign($roleid, $USER->id
, $coursecontext);
338 // Convert groupid to proper id.
340 if (isset($settings['groupid'])) {
341 $groupid = $
{$settings['groupid']}->id
;
344 // Call to the function.
346 array('name' => 'withcapability', 'value' => $settings['withcapability']),
347 array('name' => 'groupid', 'value' => $groupid),
348 array('name' => 'onlyactive', 'value' => $settings['onlyactive']),
349 array('name' => 'userfields', 'value' => 'id')
351 $enrolledusers = core_enrol_external
::get_enrolled_users($course->id
, $options);
353 // We need to execute the return values cleaning process to simulate the web service server.
354 $enrolledusers = external_api
::clean_returnvalue(core_enrol_external
::get_enrolled_users_returns(), $enrolledusers);
356 // We are only interested in ids to check visibility.
358 // Verify the user canview the expected users.
359 foreach ($enrolledusers as $enrolleduser) {
360 $viewed[] = $createdusers[$enrolleduser['id']];
362 // Verify viewed matches canview expectation (using canonicalize to ignore ordering).
363 $this->assertEqualsCanonicalizing($canview, $viewed, "Problem checking visible users for '{$createdusers[$USER->id]}'");
368 * Verify get_enrolled_users() returned users according to their status.
370 public function test_get_enrolled_users_active_suspended(): void
{
373 $this->resetAfterTest();
375 // Create the course and the users.
376 $course = $this->getDataGenerator()->create_course();
377 $coursecontext = \context_course
::instance($course->id
);
378 $user0 = $this->getDataGenerator()->create_user(['username' => 'user0active']);
379 $user1 = $this->getDataGenerator()->create_user(['username' => 'user1active']);
380 $user2 = $this->getDataGenerator()->create_user(['username' => 'user2active']);
381 $user2su = $this->getDataGenerator()->create_user(['username' => 'user2suspended']); // Suspended user.
382 $user3 = $this->getDataGenerator()->create_user(['username' => 'user3active']);
383 $user3su = $this->getDataGenerator()->create_user(['username' => 'user3suspended']); // Suspended user.
385 // Enrol the users in the course.
386 $this->getDataGenerator()->enrol_user($user0->id
, $course->id
);
387 $this->getDataGenerator()->enrol_user($user1->id
, $course->id
);
388 $this->getDataGenerator()->enrol_user($user2->id
, $course->id
);
389 $this->getDataGenerator()->enrol_user($user2su->id
, $course->id
, null, 'manual', 0, 0, ENROL_USER_SUSPENDED
);
390 $this->getDataGenerator()->enrol_user($user3->id
, $course->id
);
391 $this->getDataGenerator()->enrol_user($user3su->id
, $course->id
, null, 'manual', 0, 0, ENROL_USER_SUSPENDED
);
393 // Create a role to add the allowedcaps. Users will have this role assigned.
394 $roleid = $this->getDataGenerator()->create_role();
395 // Allow the specified capabilities.
396 assign_capability('moodle/course:enrolreview', CAP_ALLOW
, $roleid, $coursecontext);
397 assign_capability('moodle/user:viewalldetails', CAP_ALLOW
, $roleid, $coursecontext);
399 // Switch to the user and assign the role.
400 $this->setUser($user0);
401 role_assign($roleid, $USER->id
, $coursecontext);
405 ['name' => 'onlysuspended', 'value' => true],
406 ['name' => 'userfields', 'value' => 'id,username']
408 $suspendedusers = core_enrol_external
::get_enrolled_users($course->id
, $options);
410 // We need to execute the return values cleaning process to simulate the web service server.
411 $suspendedusers = external_api
::clean_returnvalue(core_enrol_external
::get_enrolled_users_returns(), $suspendedusers);
412 $this->assertCount(2, $suspendedusers);
414 foreach ($suspendedusers as $suspendeduser) {
415 $this->assertStringContainsString('suspended', $suspendeduser['username']);
420 ['name' => 'onlyactive', 'value' => true],
421 ['name' => 'userfields', 'value' => 'id,username']
423 $activeusers = core_enrol_external
::get_enrolled_users($course->id
, $options);
425 // We need to execute the return values cleaning process to simulate the web service server.
426 $activeusers = external_api
::clean_returnvalue(core_enrol_external
::get_enrolled_users_returns(), $activeusers);
427 $this->assertCount(4, $activeusers);
429 foreach ($activeusers as $activeuser) {
430 $this->assertStringContainsString('active', $activeuser['username']);
433 // All enrolled users.
435 ['name' => 'userfields', 'value' => 'id,username']
437 $allusers = core_enrol_external
::get_enrolled_users($course->id
, $options);
439 // We need to execute the return values cleaning process to simulate the web service server.
440 $allusers = external_api
::clean_returnvalue(core_enrol_external
::get_enrolled_users_returns(), $allusers);
441 $this->assertCount(6, $allusers);
443 // Active and suspended. Test exception is thrown.
445 ['name' => 'onlyactive', 'value' => true],
446 ['name' => 'onlysuspended', 'value' => true],
447 ['name' => 'userfields', 'value' => 'id,username']
449 $this->expectException('coding_exception');
450 $message = 'Coding error detected, it must be fixed by a programmer: Both onlyactive ' .
451 'and onlysuspended are set, this is probably not what you want!';
452 $this->expectExceptionMessage($message);
453 core_enrol_external
::get_enrolled_users($course->id
, $options);
457 * Test get_users_courses
459 public function test_get_users_courses(): void
{
461 require_once($CFG->dirroot
. '/completion/criteria/completion_criteria_self.php');
463 $this->resetAfterTest(true);
464 $CFG->enablecompletion
= 1;
467 $coursedata1 = array(
468 // Adding tags here to check that \core_external\util::format_string works.
469 'fullname' => '<b>Course 1</b>',
470 // Adding tags here to check that \core_external\util::format_string works.
471 'shortname' => '<b>Course 1</b>',
472 'summary' => 'Lightwork Course 1 description',
473 'summaryformat' => FORMAT_MOODLE
,
475 'enablecompletion' => true,
476 'showgrades' => true,
477 'startdate' => $timenow,
478 'enddate' => $timenow + WEEKSECS
,
482 $coursedata2 = array(
483 'lang' => 'kk', // Check invalid language pack.
486 $course1 = self
::getDataGenerator()->create_course($coursedata1);
487 $course2 = self
::getDataGenerator()->create_course($coursedata2);
488 $courses = array($course1, $course2);
489 $contexts = array ($course1->id
=> \context_course
::instance($course1->id
),
490 $course2->id
=> \context_course
::instance($course2->id
));
492 $student = $this->getDataGenerator()->create_user();
493 $otherstudent = $this->getDataGenerator()->create_user();
494 $studentroleid = $DB->get_field('role', 'id', array('shortname' => 'student'));
495 $this->getDataGenerator()->enrol_user($student->id
, $course1->id
, $studentroleid);
496 $this->getDataGenerator()->enrol_user($otherstudent->id
, $course1->id
, $studentroleid);
497 $this->getDataGenerator()->enrol_user($student->id
, $course2->id
, $studentroleid);
499 // Force last access.
502 'userid' => $student->id
,
503 'courseid' => $course1->id
,
504 'timeaccess' => $timenow
506 $DB->insert_record('user_lastaccess', $lastaccess);
508 // Force completion, setting at least one criteria.
509 require_once($CFG->dirroot
.'/completion/criteria/completion_criteria_self.php');
510 $criteriadata = new \
stdClass();
511 $criteriadata->id
= $course1->id
;
513 $criteriadata->criteria_self
= 1;
515 $criterion = new \
completion_criteria_self();
516 $criterion->update_config($criteriadata);
518 $ccompletion = new \
completion_completion(array('course' => $course1->id
, 'userid' => $student->id
));
519 $ccompletion->mark_complete();
521 // Set course hidden and favourited.
522 set_user_preference('block_myoverview_hidden_course_' . $course1->id
, 1, $student);
523 $ufservice = \core_favourites\service_factory
::get_service_for_user_context(\context_user
::instance($student->id
));
524 $ufservice->create_favourite('core_course', 'courses', $course1->id
, \context_system
::instance());
526 $this->setUser($student);
527 // Call the external function.
528 $enrolledincourses = core_enrol_external
::get_users_courses($student->id
, true);
530 // We need to execute the return values cleaning process to simulate the web service server.
531 $enrolledincourses = external_api
::clean_returnvalue(core_enrol_external
::get_users_courses_returns(), $enrolledincourses);
533 // Check we retrieve the good total number of enrolled users.
534 $this->assertEquals(2, count($enrolledincourses));
536 // We need to format summary and summaryformat before to compare them with those values returned by the webservice.
537 [$course1->summary
, $course1->summaryformat
] = \core_external\util
::format_text(
539 $course1->summaryformat
,
540 $contexts[$course1->id
],
546 // Check there are no differences between $course1 properties and course values returned by the webservice
547 // only for those fields listed in the $coursedata1 array.
548 $course1->fullname
= \core_external\util
::format_string($course1->fullname
, $contexts[$course1->id
]->id
);
549 $course1->shortname
= \core_external\util
::format_string($course1->shortname
, $contexts[$course1->id
]->id
);
550 foreach ($enrolledincourses as $courseenrol) {
551 if ($courseenrol['id'] == $course1->id
) {
552 foreach ($coursedata1 as $fieldname => $value) {
553 $this->assertEquals($courseenrol[$fieldname], $course1->$fieldname);
555 // Text extra fields.
556 $this->assertEquals($course1->fullname
, $courseenrol['displayname']);
557 $this->assertEquals([], $courseenrol['overviewfiles']);
558 $this->assertEquals($timenow, $courseenrol['lastaccess']);
559 $this->assertEquals(100.0, $courseenrol['progress']);
560 $this->assertEquals(true, $courseenrol['completed']);
561 $this->assertTrue($courseenrol['completionhascriteria']);
562 $this->assertTrue($courseenrol['completionusertracked']);
563 $this->assertTrue($courseenrol['hidden']);
564 $this->assertTrue($courseenrol['isfavourite']);
565 $this->assertEquals(2, $courseenrol['enrolledusercount']);
566 $this->assertEquals($course1->timemodified
, $courseenrol['timemodified']);
567 $url = "https://www.example.com/moodle/pluginfile.php/{$contexts[$course1->id]->id}/course/generated/course.svg";
568 $this->assertEquals($url, $courseenrol['courseimage']);
570 // Check language pack. Should be empty since an incorrect one was used when creating the course.
571 $this->assertEmpty($courseenrol['lang']);
572 $this->assertEquals($course2->fullname
, $courseenrol['displayname']);
573 $this->assertEquals([], $courseenrol['overviewfiles']);
574 $this->assertEquals(0, $courseenrol['lastaccess']);
575 $this->assertEquals(0, $courseenrol['progress']);
576 $this->assertEquals(false, $courseenrol['completed']);
577 $this->assertFalse($courseenrol['completionhascriteria']);
578 $this->assertFalse($courseenrol['completionusertracked']);
579 $this->assertFalse($courseenrol['hidden']);
580 $this->assertFalse($courseenrol['isfavourite']);
581 $this->assertEquals(1, $courseenrol['enrolledusercount']);
582 $this->assertEquals($course2->timemodified
, $courseenrol['timemodified']);
583 $url = "https://www.example.com/moodle/pluginfile.php/{$contexts[$course2->id]->id}/course/generated/course.svg";
584 $this->assertEquals($url, $courseenrol['courseimage']);
588 // Check that returnusercount works correctly.
589 $enrolledincourses = core_enrol_external
::get_users_courses($student->id
, false);
590 $enrolledincourses = external_api
::clean_returnvalue(core_enrol_external
::get_users_courses_returns(), $enrolledincourses);
591 foreach ($enrolledincourses as $courseenrol) {
592 $this->assertFalse(isset($courseenrol['enrolledusercount']));
595 // Now check that admin users can see all the info.
596 $this->setAdminUser();
598 $enrolledincourses = core_enrol_external
::get_users_courses($student->id
, true);
599 $enrolledincourses = external_api
::clean_returnvalue(core_enrol_external
::get_users_courses_returns(), $enrolledincourses);
600 $this->assertEquals(2, count($enrolledincourses));
601 foreach ($enrolledincourses as $courseenrol) {
602 if ($courseenrol['id'] == $course1->id
) {
603 $this->assertEquals($timenow, $courseenrol['lastaccess']);
604 $this->assertEquals(100.0, $courseenrol['progress']);
605 $this->assertTrue($courseenrol['completionhascriteria']);
606 $this->assertTrue($courseenrol['completionusertracked']);
607 $this->assertFalse($courseenrol['isfavourite']); // This always false.
608 $this->assertFalse($courseenrol['hidden']); // This always false.
610 $this->assertEquals(0, $courseenrol['progress']);
611 $this->assertFalse($courseenrol['completionhascriteria']);
612 $this->assertFalse($courseenrol['completionusertracked']);
613 $this->assertFalse($courseenrol['isfavourite']); // This always false.
614 $this->assertFalse($courseenrol['hidden']); // This always false.
618 // Check other users can't see private info.
619 $this->setUser($otherstudent);
621 $enrolledincourses = core_enrol_external
::get_users_courses($student->id
, true);
622 $enrolledincourses = external_api
::clean_returnvalue(core_enrol_external
::get_users_courses_returns(), $enrolledincourses);
623 $this->assertEquals(1, count($enrolledincourses));
625 $this->assertEquals($timenow, $enrolledincourses[0]['lastaccess']); // I can see this, not hidden.
626 $this->assertEquals(null, $enrolledincourses[0]['progress']); // I can't see this, private.
628 // Change some global profile visibility fields.
629 $CFG->hiddenuserfields
= 'lastaccess';
630 $enrolledincourses = core_enrol_external
::get_users_courses($student->id
, true);
631 $enrolledincourses = external_api
::clean_returnvalue(core_enrol_external
::get_users_courses_returns(), $enrolledincourses);
633 $this->assertEquals(0, $enrolledincourses[0]['lastaccess']); // I can't see this, hidden by global setting.
637 * Test that get_users_courses respects the capability to view participants when viewing courses of other user
639 public function test_get_users_courses_can_view_participants(): void
{
642 $this->resetAfterTest();
644 $course = $this->getDataGenerator()->create_course();
645 $context = \context_course
::instance($course->id
);
647 $user1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
648 $user2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
650 $this->setUser($user1);
652 $courses = core_enrol_external
::clean_returnvalue(
653 core_enrol_external
::get_users_courses_returns(),
654 core_enrol_external
::get_users_courses($user2->id
, false)
657 $this->assertCount(1, $courses);
658 $this->assertEquals($course->id
, reset($courses)['id']);
660 // Prohibit the capability for viewing course participants.
661 $studentrole = $DB->get_field('role', 'id', ['shortname' => 'student']);
662 assign_capability('moodle/course:viewparticipants', CAP_PROHIBIT
, $studentrole, $context->id
);
664 $courses = core_enrol_external
::clean_returnvalue(
665 core_enrol_external
::get_users_courses_returns(),
666 core_enrol_external
::get_users_courses($user2->id
, false)
668 $this->assertEmpty($courses);
672 * Test that get_users_courses respects the capability to view a users profile when viewing courses of other user
674 public function test_get_users_courses_can_view_profile(): void
{
675 $this->resetAfterTest();
677 $course = $this->getDataGenerator()->create_course([
678 'groupmode' => VISIBLEGROUPS
,
681 $user1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
682 $user2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
684 // Create separate groups for each of our students.
685 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
686 groups_add_member($group1, $user1);
687 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
688 groups_add_member($group2, $user2);
690 $this->setUser($user1);
692 $courses = core_enrol_external
::clean_returnvalue(
693 core_enrol_external
::get_users_courses_returns(),
694 core_enrol_external
::get_users_courses($user2->id
, false)
697 $this->assertCount(1, $courses);
698 $this->assertEquals($course->id
, reset($courses)['id']);
700 // Change to separate groups mode, so students can't view information about each other in different groups.
701 $course->groupmode
= SEPARATEGROUPS
;
702 update_course($course);
704 $courses = core_enrol_external
::clean_returnvalue(
705 core_enrol_external
::get_users_courses_returns(),
706 core_enrol_external
::get_users_courses($user2->id
, false)
708 $this->assertEmpty($courses);
712 * Test get_users_courses with mathjax in the name.
714 public function test_get_users_courses_with_mathjax(): void
{
717 $this->resetAfterTest(true);
719 // Enable MathJax filter in content and headings.
720 $this->configure_filters([
721 ['name' => 'mathjaxloader', 'state' => TEXTFILTER_ON
, 'move' => -1, 'applytostrings' => true],
724 // Create a course with MathJax in the name and summary.
726 'fullname' => 'Course 1 $$(a+b)=2$$',
727 'shortname' => 'Course 1 $$(a+b)=2$$',
728 'summary' => 'Lightwork Course 1 description $$(a+b)=2$$',
729 'summaryformat' => FORMAT_HTML
,
732 $course = self
::getDataGenerator()->create_course($coursedata);
733 $context = \context_course
::instance($course->id
);
735 // Enrol a student in the course.
736 $student = $this->getDataGenerator()->create_user();
737 $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student']);
738 $this->getDataGenerator()->enrol_user($student->id
, $course->id
, $studentroleid);
740 $this->setUser($student);
742 // Call the external function.
743 $enrolledincourses = core_enrol_external
::get_users_courses($student->id
, true);
745 // We need to execute the return values cleaning process to simulate the web service server.
746 $enrolledincourses = external_api
::clean_returnvalue(core_enrol_external
::get_users_courses_returns(), $enrolledincourses);
748 // Check that the amount of courses is the right one.
749 $this->assertCount(1, $enrolledincourses);
751 // Filter the values to compare them with the returned ones.
752 $course->fullname
= \core_external\util
::format_string($course->fullname
, $context->id
);
753 $course->shortname
= \core_external\util
::format_string($course->shortname
, $context->id
);
754 [$course->summary
, $course->summaryformat
] = \core_external\util
::format_text(
756 $course->summaryformat
,
763 // Compare the values.
764 $this->assertStringContainsString('<span class="filter_mathjaxloader_equation">', $enrolledincourses[0]['fullname']);
765 $this->assertStringContainsString('<span class="filter_mathjaxloader_equation">', $enrolledincourses[0]['shortname']);
766 $this->assertStringContainsString('<span class="filter_mathjaxloader_equation">', $enrolledincourses[0]['summary']);
767 $this->assertEquals($course->fullname
, $enrolledincourses[0]['fullname']);
768 $this->assertEquals($course->shortname
, $enrolledincourses[0]['shortname']);
769 $this->assertEquals($course->summary
, $enrolledincourses[0]['summary']);
773 * Test get_course_enrolment_methods
775 public function test_get_course_enrolment_methods(): void
{
778 $this->resetAfterTest(true);
780 // Get enrolment plugins.
781 $selfplugin = enrol_get_plugin('self');
782 $this->assertNotEmpty($selfplugin);
783 $manualplugin = enrol_get_plugin('manual');
784 $this->assertNotEmpty($manualplugin);
786 $studentrole = $DB->get_record('role', array('shortname'=>'student'));
787 $this->assertNotEmpty($studentrole);
789 $course1 = self
::getDataGenerator()->create_course();
790 $coursedata = new \
stdClass();
791 $coursedata->visible
= 0;
792 $course2 = self
::getDataGenerator()->create_course($coursedata);
794 // Add enrolment methods for course.
795 $instanceid1 = $selfplugin->add_instance($course1, array('status' => ENROL_INSTANCE_ENABLED
,
796 'name' => 'Test instance 1',
798 'roleid' => $studentrole->id
));
799 $instanceid2 = $selfplugin->add_instance($course1, array('status' => ENROL_INSTANCE_DISABLED
,
800 'name' => 'Test instance 2',
801 'roleid' => $studentrole->id
));
803 $instanceid3 = $manualplugin->add_instance($course1, array('status' => ENROL_INSTANCE_ENABLED
,
804 'name' => 'Test instance 3'));
806 $enrolmentmethods = $DB->get_records('enrol', array('courseid' => $course1->id
, 'status' => ENROL_INSTANCE_ENABLED
));
807 $this->assertCount(2, $enrolmentmethods);
809 $this->setAdminUser();
811 // Check if information is returned.
812 $enrolmentmethods = core_enrol_external
::get_course_enrolment_methods($course1->id
);
813 $enrolmentmethods = external_api
::clean_returnvalue(core_enrol_external
::get_course_enrolment_methods_returns(),
815 // Enrolment information is currently returned by self enrolment plugin, so count == 1.
816 // This should be changed as we implement get_enrol_info() for other enrolment plugins.
817 $this->assertCount(1, $enrolmentmethods);
819 $enrolmentmethod = $enrolmentmethods[0];
820 $this->assertEquals($course1->id
, $enrolmentmethod['courseid']);
821 $this->assertEquals('self', $enrolmentmethod['type']);
822 $this->assertTrue($enrolmentmethod['status']);
823 $this->assertFalse(isset($enrolmentmethod['wsfunction']));
825 $instanceid4 = $selfplugin->add_instance($course2, array('status' => ENROL_INSTANCE_ENABLED
,
826 'name' => 'Test instance 4',
827 'roleid' => $studentrole->id
,
829 'password' => 'test'));
830 $enrolmentmethods = core_enrol_external
::get_course_enrolment_methods($course2->id
);
831 $enrolmentmethods = external_api
::clean_returnvalue(core_enrol_external
::get_course_enrolment_methods_returns(),
833 $this->assertCount(1, $enrolmentmethods);
835 $enrolmentmethod = $enrolmentmethods[0];
836 $this->assertEquals($course2->id
, $enrolmentmethod['courseid']);
837 $this->assertEquals('self', $enrolmentmethod['type']);
838 $this->assertTrue($enrolmentmethod['status']);
839 $this->assertEquals('enrol_self_get_instance_info', $enrolmentmethod['wsfunction']);
841 // Try to retrieve information using a normal user for a hidden course.
842 $user = self
::getDataGenerator()->create_user();
843 $this->setUser($user);
845 core_enrol_external
::get_course_enrolment_methods($course2->id
);
846 } catch (\moodle_exception
$e) {
847 $this->assertEquals('coursehidden', $e->errorcode
);
851 public function get_enrolled_users_setup($capability) {
854 $this->resetAfterTest(true);
856 $return = new \
stdClass();
858 $return->course
= self
::getDataGenerator()->create_course();
859 $return->user1
= self
::getDataGenerator()->create_user();
860 $return->user2
= self
::getDataGenerator()->create_user();
861 $return->user3
= self
::getDataGenerator()->create_user();
862 $this->setUser($return->user3
);
864 // Set the required capabilities by the external function.
865 $return->context
= \context_course
::instance($return->course
->id
);
866 $return->roleid
= $this->assignUserCapability($capability, $return->context
->id
);
867 $this->assignUserCapability('moodle/user:viewdetails', $return->context
->id
, $return->roleid
);
869 // Enrol the users in the course.
870 $this->getDataGenerator()->enrol_user($return->user1
->id
, $return->course
->id
, $return->roleid
, 'manual');
871 $this->getDataGenerator()->enrol_user($return->user2
->id
, $return->course
->id
, $return->roleid
, 'manual');
872 $this->getDataGenerator()->enrol_user($return->user3
->id
, $return->course
->id
, $return->roleid
, 'manual');
878 * Test get_enrolled_users from core_enrol_external without additional
881 public function test_get_enrolled_users_without_parameters(): void
{
882 $capability = 'moodle/course:viewparticipants';
883 $data = $this->get_enrolled_users_setup($capability);
885 // Call the external function.
886 $enrolledusers = core_enrol_external
::get_enrolled_users($data->course
->id
);
888 // We need to execute the return values cleaning process to simulate the web service server.
889 $enrolledusers = external_api
::clean_returnvalue(core_enrol_external
::get_enrolled_users_returns(), $enrolledusers);
891 // Check the result set.
892 $this->assertEquals(3, count($enrolledusers));
893 $this->assertArrayHasKey('email', $enrolledusers[0]);
897 * Test get_enrolled_users from core_enrol_external with some parameters set.
899 public function test_get_enrolled_users_with_parameters(): void
{
900 $capability = 'moodle/course:viewparticipants';
901 $data = $this->get_enrolled_users_setup($capability);
903 // Call the function with some parameters set.
904 $enrolledusers = core_enrol_external
::get_enrolled_users($data->course
->id
, array(
905 array('name' => 'limitfrom', 'value' => 2),
906 array('name' => 'limitnumber', 'value' => 1),
907 array('name' => 'userfields', 'value' => 'id')
910 // We need to execute the return values cleaning process to simulate the web service server.
911 $enrolledusers = external_api
::clean_returnvalue(core_enrol_external
::get_enrolled_users_returns(), $enrolledusers);
913 // Check the result set, we should only get the 3rd result, which is $user3.
914 $this->assertCount(1, $enrolledusers);
915 $this->assertEquals($data->user3
->id
, $enrolledusers[0]['id']);
916 $this->assertArrayHasKey('id', $enrolledusers[0]);
917 $this->assertArrayNotHasKey('email', $enrolledusers[0]);
922 * Test get_enrolled_users last course access.
924 public function test_get_enrolled_users_including_lastcourseaccess(): void
{
926 $capability = 'moodle/course:viewparticipants';
927 $data = $this->get_enrolled_users_setup($capability);
929 // Call the external function.
930 $enrolledusers = core_enrol_external
::get_enrolled_users($data->course
->id
);
931 // We need to execute the return values cleaning process to simulate the web service server.
932 $enrolledusers = external_api
::clean_returnvalue(core_enrol_external
::get_enrolled_users_returns(), $enrolledusers);
934 // Check the result set.
935 $this->assertEquals(3, count($enrolledusers));
936 $this->assertArrayHasKey('email', $enrolledusers[0]);
937 $this->assertEquals(0, $enrolledusers[0]['lastcourseaccess']);
938 $this->assertEquals(0, $enrolledusers[1]['lastcourseaccess']);
939 $this->assertNotEquals(0, $enrolledusers[2]['lastcourseaccess']); // We forced an access to the course via setUser.
941 // Force last access.
944 'userid' => $enrolledusers[0]['id'],
945 'courseid' => $data->course
->id
,
946 'timeaccess' => $timenow
948 $DB->insert_record('user_lastaccess', $lastaccess);
950 $enrolledusers = core_enrol_external
::get_enrolled_users($data->course
->id
);
951 $enrolledusers = external_api
::clean_returnvalue(core_enrol_external
::get_enrolled_users_returns(), $enrolledusers);
953 // Check the result set.
954 $this->assertEquals(3, count($enrolledusers));
955 $this->assertEquals($timenow, $enrolledusers[0]['lastcourseaccess']);
956 $this->assertEquals(0, $enrolledusers[1]['lastcourseaccess']);
957 $this->assertNotEquals(0, $enrolledusers[2]['lastcourseaccess']);
961 * Test get_enrolled_users from core_enrol_external with capability to
962 * viewparticipants removed.
964 public function test_get_enrolled_users_without_capability(): void
{
965 $capability = 'moodle/course:viewparticipants';
966 $data = $this->get_enrolled_users_setup($capability);
968 // Call without required capability.
969 $this->unassignUserCapability($capability, $data->context
->id
, $data->roleid
);
970 $this->expectException(\moodle_exception
::class);
971 $categories = core_enrol_external
::get_enrolled_users($data->course
->id
);
974 public function get_enrolled_users_with_capability_setup($capability) {
977 $this->resetAfterTest(true);
979 $return = new \
stdClass();
981 // Create the course and fetch its context.
982 $return->course
= self
::getDataGenerator()->create_course();
983 $context = \context_course
::instance($return->course
->id
);
985 // Create one teacher, and two students.
986 $return->teacher
= self
::getDataGenerator()->create_user();
987 $return->student1
= self
::getDataGenerator()->create_user();
988 $return->student2
= self
::getDataGenerator()->create_user();
990 // Create a new student role based on the student archetype but with the capability prohibitted.
991 $fakestudentroleid = create_role('Fake student role', 'fakestudent', 'Fake student role', 'student');
992 assign_capability($capability, CAP_PROHIBIT
, $fakestudentroleid, $context->id
);
994 // Enrol all of the users in the course.
995 // * 'teacher' is an editing teacher.
996 // * 'student1' is a standard student.
997 // * 'student2' is a student with the capability prohibitted.
998 $editingteacherroleid = $DB->get_field('role', 'id', array('shortname' => 'editingteacher'));
999 $studentroleid = $DB->get_field('role', 'id', array('shortname' => 'student'));
1000 $this->getDataGenerator()->enrol_user($return->teacher
->id
, $return->course
->id
, $editingteacherroleid);
1001 $this->getDataGenerator()->enrol_user($return->student1
->id
, $return->course
->id
, $studentroleid);
1002 $this->getDataGenerator()->enrol_user($return->student2
->id
, $return->course
->id
, $fakestudentroleid);
1004 // Log in as the teacher.
1005 $this->setUser($return->teacher
);
1008 accesslib_clear_all_caches_for_unit_testing();
1014 * Test get_enrolled_users_with_capability without additional paramaters.
1016 public function test_get_enrolled_users_with_capability_without_parameters(): void
{
1017 $capability = 'moodle/course:viewparticipants';
1018 $data = $this->get_enrolled_users_with_capability_setup($capability);
1020 $result = core_enrol_external
::get_enrolled_users_with_capability(
1022 'coursecapabilities' => array(
1023 'courseid' => $data->course
->id
,
1024 'capabilities' => array(
1032 // We need to execute the return values cleaning process to simulate the web service server.
1033 $result = external_api
::clean_returnvalue(core_enrol_external
::get_enrolled_users_with_capability_returns(), $result);
1035 // Check an array containing the expected user for the course capability is returned.
1036 $expecteduserlist = $result[0];
1037 $this->assertEquals($data->course
->id
, $expecteduserlist['courseid']);
1038 $this->assertEquals($capability, $expecteduserlist['capability']);
1039 $this->assertEquals(2, count($expecteduserlist['users']));
1043 * Test get_enrolled_users_with_capability
1045 public function test_get_enrolled_users_with_capability_with_parameters(): void
{
1046 $capability = 'moodle/course:viewparticipants';
1047 $data = $this->get_enrolled_users_with_capability_setup($capability);
1049 $result = core_enrol_external
::get_enrolled_users_with_capability(
1051 'coursecapabilities' => array(
1052 'courseid' => $data->course
->id
,
1053 'capabilities' => array(
1059 array('name' => 'limitfrom', 'value' => 1),
1060 array('name' => 'limitnumber', 'value' => 1),
1061 array('name' => 'userfields', 'value' => 'id')
1065 // We need to execute the return values cleaning process to simulate the web service server.
1066 $result = external_api
::clean_returnvalue(core_enrol_external
::get_enrolled_users_with_capability_returns(), $result);
1068 // Check an array containing the expected user for the course capability is returned.
1069 $expecteduserlist = $result[0]['users'];
1070 $expecteduser = reset($expecteduserlist);
1071 $this->assertEquals(1, count($expecteduserlist));
1072 $this->assertEquals($data->student1
->id
, $expecteduser['id']);
1076 * Test get_enrolled_users last course access.
1078 public function test_get_enrolled_users_with_capability_including_lastcourseaccess(): void
{
1080 $capability = 'moodle/course:viewparticipants';
1081 $data = $this->get_enrolled_users_with_capability_setup($capability);
1083 $parameters = array(
1084 'coursecapabilities' => array(
1085 'courseid' => $data->course
->id
,
1086 'capabilities' => array(
1092 $result = core_enrol_external
::get_enrolled_users_with_capability($parameters, array());
1093 // We need to execute the return values cleaning process to simulate the web service server.
1094 $result = external_api
::clean_returnvalue(core_enrol_external
::get_enrolled_users_with_capability_returns(), $result);
1096 // Check an array containing the expected user for the course capability is returned.
1097 $expecteduserlist = $result[0];
1098 $this->assertEquals($data->course
->id
, $expecteduserlist['courseid']);
1099 $this->assertEquals($capability, $expecteduserlist['capability']);
1100 $this->assertEquals(2, count($expecteduserlist['users']));
1101 // We forced an access to the course via setUser.
1102 $this->assertNotEquals(0, $expecteduserlist['users'][0]['lastcourseaccess']);
1103 $this->assertEquals(0, $expecteduserlist['users'][1]['lastcourseaccess']);
1105 // Force last access.
1107 $lastaccess = array(
1108 'userid' => $expecteduserlist['users'][1]['id'],
1109 'courseid' => $data->course
->id
,
1110 'timeaccess' => $timenow
1112 $DB->insert_record('user_lastaccess', $lastaccess);
1114 $result = core_enrol_external
::get_enrolled_users_with_capability($parameters, array());
1115 // We need to execute the return values cleaning process to simulate the web service server.
1116 $result = external_api
::clean_returnvalue(core_enrol_external
::get_enrolled_users_with_capability_returns(), $result);
1118 // Check the result set.
1119 $expecteduserlist = $result[0];
1120 $this->assertEquals(2, count($expecteduserlist['users']));
1121 $this->assertNotEquals(0, $expecteduserlist['users'][0]['lastcourseaccess']);
1122 $this->assertEquals($timenow, $expecteduserlist['users'][1]['lastcourseaccess']);
1126 * dataProvider for test_submit_user_enrolment_form().
1128 public static function submit_user_enrolment_form_provider(): array {
1129 $now = new \
DateTime();
1131 $nextmonth = clone($now);
1132 $nextmonth->add(new \
DateInterval('P1M'));
1137 'status' => ENROL_USER_ACTIVE
,
1139 'day' => $now->format('j'),
1140 'month' => $now->format('n'),
1141 'year' => $now->format('Y'),
1142 'hour' => $now->format('G'),
1147 'day' => $now->format('j'),
1148 'month' => $now->format('n'),
1149 'year' => $now->format('Y'),
1150 'hour' => $now->format('G'),
1155 'expectedresult' => false,
1156 'validationerror' => true,
1160 'status' => ENROL_USER_ACTIVE
,
1162 'day' => $now->format('j'),
1163 'month' => $now->format('n'),
1164 'year' => $now->format('Y'),
1165 'hour' => $now->format('G'),
1170 'day' => $nextmonth->format('j'),
1171 'month' => $nextmonth->format('n'),
1172 'year' => $nextmonth->format('Y'),
1173 'hour' => $nextmonth->format('G'),
1178 'expectedresult' => true,
1179 'validationerror' => false
1183 'status' => ENROL_USER_SUSPENDED
,
1185 'expectedresult' => true,
1186 'validationerror' => false
1192 * @param array $customdata The data we are providing to the webservice.
1193 * @param bool $expectedresult The result we are expecting to receive from the webservice.
1194 * @param bool $validationerror The validationerror we are expecting to receive from the webservice.
1195 * @dataProvider submit_user_enrolment_form_provider
1197 public function test_submit_user_enrolment_form($customdata, $expectedresult, $validationerror): void
{
1200 $this->resetAfterTest(true);
1201 $datagen = $this->getDataGenerator();
1203 /** @var \enrol_manual_plugin $manualplugin */
1204 $manualplugin = enrol_get_plugin('manual');
1206 $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST
);
1207 $teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'editingteacher'], MUST_EXIST
);
1208 $course = $datagen->create_course();
1209 $user = $datagen->create_user();
1210 $teacher = $datagen->create_user();
1213 $instances = enrol_get_instances($course->id
, true);
1214 foreach ($instances as $inst) {
1215 if ($inst->enrol
== 'manual') {
1216 $instanceid = (int)$inst->id
;
1220 if (empty($instanceid)) {
1221 $instanceid = $manualplugin->add_default_instance($course);
1222 if (empty($instanceid)) {
1223 $instanceid = $manualplugin->add_instance($course);
1226 $this->assertNotNull($instanceid);
1228 $instance = $DB->get_record('enrol', ['id' => $instanceid], '*', MUST_EXIST
);
1229 $manualplugin->enrol_user($instance, $user->id
, $studentroleid, 0, 0, ENROL_USER_ACTIVE
);
1230 $manualplugin->enrol_user($instance, $teacher->id
, $teacherroleid, 0, 0, ENROL_USER_ACTIVE
);
1231 $ueid = (int) $DB->get_field(
1234 ['enrolid' => $instance->id
, 'userid' => $user->id
],
1238 // Login as teacher.
1239 $teacher->ignoresesskey
= true;
1240 $this->setUser($teacher);
1246 'timestart' => null,
1251 $formdata = array_merge($formdata, $customdata);
1253 require_once("$CFG->dirroot/enrol/editenrolment_form.php");
1254 $formdata = enrol_user_enrolment_form
::mock_generate_submit_keys($formdata);
1256 $querystring = http_build_query($formdata, '', '&');
1258 $result = external_api
::clean_returnvalue(
1259 core_enrol_external
::submit_user_enrolment_form_returns(),
1260 core_enrol_external
::submit_user_enrolment_form($querystring)
1263 $this->assertEqualsCanonicalizing(
1264 ['result' => $expectedresult, 'validationerror' => $validationerror],
1267 if ($result['result']) {
1268 $ue = $DB->get_record('user_enrolments', ['id' => $ueid], '*', MUST_EXIST
);
1269 $this->assertEquals($formdata['status'], $ue->status
);
1274 * Test for core_enrol_external::unenrol_user_enrolment().
1276 public function test_unenerol_user_enrolment(): void
{
1279 $this->resetAfterTest(true);
1280 $datagen = $this->getDataGenerator();
1282 /** @var \enrol_manual_plugin $manualplugin */
1283 $manualplugin = enrol_get_plugin('manual');
1284 $this->assertNotNull($manualplugin);
1286 $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST
);
1287 $teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'editingteacher'], MUST_EXIST
);
1288 $course = $datagen->create_course();
1289 $user = $datagen->create_user();
1290 $teacher = $datagen->create_user();
1293 $instances = enrol_get_instances($course->id
, true);
1294 foreach ($instances as $inst) {
1295 if ($inst->enrol
== 'manual') {
1296 $instanceid = (int)$inst->id
;
1300 if (empty($instanceid)) {
1301 $instanceid = $manualplugin->add_default_instance($course);
1302 if (empty($instanceid)) {
1303 $instanceid = $manualplugin->add_instance($course);
1306 $this->assertNotNull($instanceid);
1308 $instance = $DB->get_record('enrol', ['id' => $instanceid], '*', MUST_EXIST
);
1309 $manualplugin->enrol_user($instance, $user->id
, $studentroleid, 0, 0, ENROL_USER_ACTIVE
);
1310 $manualplugin->enrol_user($instance, $teacher->id
, $teacherroleid, 0, 0, ENROL_USER_ACTIVE
);
1311 $ueid = (int)$DB->get_field(
1314 ['enrolid' => $instance->id
, 'userid' => $user->id
],
1318 // Login as teacher.
1319 $this->setUser($teacher);
1321 // Invalid data by passing invalid ueid.
1322 $data = core_enrol_external
::unenrol_user_enrolment(101010);
1323 $data = external_api
::clean_returnvalue(core_enrol_external
::unenrol_user_enrolment_returns(), $data);
1324 $this->assertFalse($data['result']);
1325 $this->assertNotEmpty($data['errors']);
1328 $data = core_enrol_external
::unenrol_user_enrolment($ueid);
1329 $data = external_api
::clean_returnvalue(core_enrol_external
::unenrol_user_enrolment_returns(), $data);
1330 $this->assertTrue($data['result']);
1331 $this->assertEmpty($data['errors']);
1333 // Check unenrol user enrolment.
1334 $ue = $DB->count_records('user_enrolments', ['id' => $ueid]);
1335 $this->assertEquals(0, $ue);
1339 * Test for core_enrol_external::test_search_users().
1341 public function test_search_users(): void
{
1344 $this->resetAfterTest(true);
1345 $datagen = $this->getDataGenerator();
1347 /** @var \enrol_manual_plugin $manualplugin */
1348 $manualplugin = enrol_get_plugin('manual');
1349 $this->assertNotNull($manualplugin);
1351 $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST
);
1352 $teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'editingteacher'], MUST_EXIST
);
1354 $course1 = $datagen->create_course();
1355 $course2 = $datagen->create_course();
1357 $user1 = $datagen->create_user(['firstname' => 'user 1']);
1358 $user2 = $datagen->create_user(['firstname' => 'user 2']);
1359 $user3 = $datagen->create_user(['firstname' => 'user 3']);
1360 $teacher = $datagen->create_user(['firstname' => 'user 4']);
1363 $instances = enrol_get_instances($course1->id
, true);
1364 foreach ($instances as $inst) {
1365 if ($inst->enrol
== 'manual') {
1366 $instanceid = (int)$inst->id
;
1370 if (empty($instanceid)) {
1371 $instanceid = $manualplugin->add_default_instance($course1);
1372 if (empty($instanceid)) {
1373 $instanceid = $manualplugin->add_instance($course1);
1376 $this->assertNotNull($instanceid);
1378 $instance = $DB->get_record('enrol', ['id' => $instanceid], '*', MUST_EXIST
);
1379 $manualplugin->enrol_user($instance, $user1->id
, $studentroleid, 0, 0, ENROL_USER_ACTIVE
);
1380 $manualplugin->enrol_user($instance, $user2->id
, $studentroleid, 0, 0, ENROL_USER_ACTIVE
);
1381 $manualplugin->enrol_user($instance, $user3->id
, $studentroleid, 0, 0, ENROL_USER_ACTIVE
);
1382 $manualplugin->enrol_user($instance, $teacher->id
, $teacherroleid, 0, 0, ENROL_USER_ACTIVE
);
1384 $this->setUser($teacher);
1386 // Search for users in a course with enrolled users.
1387 $result = core_enrol_external
::search_users($course1->id
, 'user', true, 0, 30);
1388 $this->assertCount(4, $result);
1390 $this->expectException('moodle_exception');
1391 // Search for users in a course without any enrolled users, shouldn't return anything.
1392 $result = core_enrol_external
::search_users($course2->id
, 'user', true, 0, 30);
1393 $this->assertCount(0, $result);
1395 // Search for invalid first name.
1396 $result = core_enrol_external
::search_users($course1->id
, 'yada yada', true, 0, 30);
1397 $this->assertCount(0, $result);
1399 // Test pagination, it should return only 3 users.
1400 $result = core_enrol_external
::search_users($course1->id
, 'user', true, 0, 3);
1401 $this->assertCount(3, $result);
1403 // Test pagination, it should return only 3 users.
1404 $result = core_enrol_external
::search_users($course1->id
, 'user 1', true, 0, 1);
1405 $result = $result[0];
1406 $this->assertEquals($user1->id
, $result['id']);
1407 $this->assertEquals($user1->email
, $result['email']);
1408 $this->assertEquals(fullname($user1), $result['fullname']);
1410 $this->setUser($user1);
1412 // Search for users in a course with enrolled users.
1413 $result = core_enrol_external
::search_users($course1->id
, 'user', true, 0, 30);
1414 $this->assertCount(4, $result);
1416 $this->expectException('moodle_exception');
1417 // Search for users in a course without any enrolled users, shouldn't return anything.
1418 $result = core_enrol_external
::search_users($course2->id
, 'user', true, 0, 30);
1419 $this->assertCount(0, $result);
1421 // Search for invalid first name.
1422 $result = core_enrol_external
::search_users($course1->id
, 'yada yada', true, 0, 30);
1423 $this->assertCount(0, $result);
1427 * Test for core_enrol_external::search_users() when group mode is active.
1428 * @covers ::search_users
1430 public function test_search_users_groupmode(): void
{
1433 $this->resetAfterTest();
1434 $datagen = $this->getDataGenerator();
1436 $studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST
);
1437 $teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'teacher'], MUST_EXIST
);
1439 $course = $datagen->create_course();
1441 $student1 = $datagen->create_and_enrol($course);
1442 $student2 = $datagen->create_and_enrol($course);
1443 $student3 = $datagen->create_and_enrol($course);
1444 $teacher1 = $datagen->create_and_enrol($course, 'teacher');
1445 $teacher2 = $datagen->create_and_enrol($course, 'teacher');
1446 $teacher3 = $datagen->create_and_enrol($course, 'teacher');
1447 $teacher4 = $datagen->create_and_enrol($course, 'editingteacher');
1450 $group1 = $datagen->create_group(['courseid' => $course->id
]);
1451 $group2 = $datagen->create_group(['courseid' => $course->id
]);
1453 // Add the users to the groups.
1454 $datagen->create_group_member(['groupid' => $group1->id
, 'userid' => $student1->id
]);
1455 $datagen->create_group_member(['groupid' => $group2->id
, 'userid' => $student2->id
]);
1456 $datagen->create_group_member(['groupid' => $group2->id
, 'userid' => $student3->id
]);
1457 $datagen->create_group_member(['groupid' => $group1->id
, 'userid' => $teacher1->id
]);
1458 $datagen->create_group_member(['groupid' => $group2->id
, 'userid' => $teacher1->id
]);
1459 $datagen->create_group_member(['groupid' => $group1->id
, 'userid' => $teacher2->id
]);
1461 // Create the forum.
1462 $record = new stdClass();
1463 $record->introformat
= FORMAT_HTML
;
1464 $record->course
= $course->id
;
1465 $forum = self
::getDataGenerator()->create_module('forum', $record, ['groupmode' => SEPARATEGROUPS
]);
1466 $contextid = $DB->get_field('context', 'id', ['instanceid' => $forum->cmid
, 'contextlevel' => CONTEXT_MODULE
]);
1468 $this->setUser($teacher1);
1469 $result = core_enrol_external
::search_users($course->id
, 'user', true, 0, 30, $contextid);
1470 $this->assertCount(5, $result);
1472 $this->setUser($teacher2);
1473 $result = core_enrol_external
::search_users($course->id
, 'user', true, 0, 30, $contextid);
1474 $this->assertCount(3, $result);
1476 $this->setUser($teacher3);
1477 $result = core_enrol_external
::search_users($course->id
, 'user', true, 0, 30, $contextid);
1478 $this->assertCount(0, $result);
1480 $this->setUser($teacher4);
1481 $result = core_enrol_external
::search_users($course->id
, 'user', true, 0, 30, $contextid);
1482 $this->assertCount(7, $result);
1484 // Now change the group mode to no groups.
1485 set_coursemodule_groupmode($forum->cmid
, NOGROUPS
);
1486 $this->setUser($teacher1);
1487 $result = core_enrol_external
::search_users($course->id
, 'user', true, 0, 30, $contextid);
1488 $this->assertCount(7, $result);
1492 * Tests the get_potential_users external function (not too much detail because the back-end
1493 * is covered in another test).
1495 public function test_get_potential_users(): void
{
1496 $this->resetAfterTest();
1498 // Create a couple of custom profile fields, one of which is in user identity.
1499 $generator = $this->getDataGenerator();
1500 $generator->create_custom_profile_field(['datatype' => 'text',
1501 'shortname' => 'researchtopic', 'name' => 'Research topic']);
1502 $generator->create_custom_profile_field(['datatype' => 'text',
1503 'shortname' => 'specialid', 'name' => 'Special id']);
1504 set_config('showuseridentity', 'department,profile_field_specialid');
1507 $course = $generator->create_course();
1509 // Get enrol id for manual enrol plugin.
1510 foreach (enrol_get_instances($course->id
, true) as $instance) {
1511 if ($instance->enrol
=== 'manual') {
1512 $enrolid = $instance->id
;
1516 // Create a couple of test users.
1517 $user1 = $generator->create_user(['firstname' => 'Eigh', 'lastname' => 'User',
1518 'department' => 'Amphibians', 'profile_field_specialid' => 'Q123',
1519 'profile_field_researchtopic' => 'Frogs']);
1520 $user2 = $generator->create_user(['firstname' => 'Anne', 'lastname' => 'Other',
1521 'department' => 'Amphibians', 'profile_field_specialid' => 'Q456',
1522 'profile_field_researchtopic' => 'Toads']);
1524 // Do this as admin user.
1525 $this->setAdminUser();
1527 // Get potential users and extract the 2 we care about.
1528 $result = core_enrol_external
::get_potential_users($course->id
, $enrolid, '', false, 0, 10);
1529 $result1 = $this->extract_user_from_result($result, $user1->id
);
1530 $result2 = $this->extract_user_from_result($result, $user2->id
);
1532 // Check the fields are the expected ones.
1533 $this->assertEquals(['id', 'fullname', 'customfields',
1534 'profileimageurl', 'profileimageurlsmall', 'department'], array_keys($result1));
1535 $this->assertEquals('Eigh User', $result1['fullname']);
1536 $this->assertEquals('Amphibians', $result1['department']);
1538 // Check the custom fields ONLY include the user identity one.
1540 foreach ($result1['customfields'] as $customfield) {
1541 $fieldvalues[$customfield['shortname']] = $customfield['value'];
1543 $this->assertEquals(['specialid'], array_keys($fieldvalues));
1544 $this->AssertEquals('Q123', $fieldvalues['specialid']);
1546 // Just check user 2 is the right user.
1547 $this->assertEquals('Anne Other', $result2['fullname']);
1551 * Utility function to get one user out of the get_potential_users result.
1553 * @param array $result Result array
1554 * @param int $userid User id
1555 * @return array Data for that user
1557 protected function extract_user_from_result(array $result, int $userid): array {
1558 foreach ($result as $item) {
1559 if ($item['id'] == $userid) {
1563 $this->fail('User not in result: ' . $userid);