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 defined('MOODLE_INTERNAL') ||
die();
22 require_once($CFG->dirroot
.'/user/lib.php');
25 * Unit tests for user lib api.
29 * @copyright 2013 Rajesh Taneja <rajesh@moodle.com>
30 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
32 class userlib_test
extends \advanced_testcase
{
34 * Test user_get_user_details_courses
36 public function test_user_get_user_details_courses() {
39 $this->resetAfterTest();
41 // Create user and modify user profile.
42 $user1 = $this->getDataGenerator()->create_user();
43 $user2 = $this->getDataGenerator()->create_user();
44 $user3 = $this->getDataGenerator()->create_user();
46 $course1 = $this->getDataGenerator()->create_course();
47 $coursecontext = \context_course
::instance($course1->id
);
48 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
49 $this->getDataGenerator()->enrol_user($user1->id
, $course1->id
);
50 $this->getDataGenerator()->enrol_user($user2->id
, $course1->id
);
51 role_assign($teacherrole->id
, $user1->id
, $coursecontext->id
);
52 role_assign($teacherrole->id
, $user2->id
, $coursecontext->id
);
54 accesslib_clear_all_caches_for_unit_testing();
56 // Get user2 details as a user with super system capabilities.
57 $result = user_get_user_details_courses($user2);
58 $this->assertEquals($user2->id
, $result['id']);
59 $this->assertEquals(fullname($user2), $result['fullname']);
60 $this->assertEquals($course1->id
, $result['enrolledcourses'][0]['id']);
62 $this->setUser($user1);
63 // Get user2 details as a user who can only see this user in a course.
64 $result = user_get_user_details_courses($user2);
65 $this->assertEquals($user2->id
, $result['id']);
66 $this->assertEquals(fullname($user2), $result['fullname']);
67 $this->assertEquals($course1->id
, $result['enrolledcourses'][0]['id']);
69 // Get user2 details as a user who doesn't share any course with user2.
70 $this->setUser($user3);
71 $result = user_get_user_details_courses($user2);
72 $this->assertNull($result);
76 * Verify return when course groupmode set to 'no groups'.
78 public function test_user_get_user_details_courses_groupmode_nogroups() {
79 $this->resetAfterTest();
81 // Enrol 2 users into a course with groupmode set to 'no groups'.
82 // Profiles should be visible.
83 $user1 = $this->getDataGenerator()->create_user();
84 $user2 = $this->getDataGenerator()->create_user();
85 $course = $this->getDataGenerator()->create_course((object) ['groupmode' => 0]);
86 $this->getDataGenerator()->enrol_user($user1->id
, $course->id
);
87 $this->getDataGenerator()->enrol_user($user2->id
, $course->id
);
89 $this->setUser($user1);
90 $userdetails = user_get_user_details_courses($user2);
91 $this->assertIsArray($userdetails);
92 $this->assertEquals($user2->id
, $userdetails['id']);
96 * Verify return when course groupmode set to 'separate groups'.
98 public function test_user_get_user_details_courses_groupmode_separate() {
99 $this->resetAfterTest();
101 // Enrol 2 users into a course with groupmode set to 'separate groups'.
102 // The users are not in any groups, so profiles should be hidden (same as if they were in separate groups).
103 $user1 = $this->getDataGenerator()->create_user();
104 $user2 = $this->getDataGenerator()->create_user();
105 $course = $this->getDataGenerator()->create_course((object) ['groupmode' => 1]);
106 $this->getDataGenerator()->enrol_user($user1->id
, $course->id
);
107 $this->getDataGenerator()->enrol_user($user2->id
, $course->id
);
109 $this->setUser($user1);
110 $this->assertNull(user_get_user_details_courses($user2));
114 * Verify return when course groupmode set to 'visible groups'.
116 public function test_user_get_user_details_courses_groupmode_visible() {
117 $this->resetAfterTest();
119 // Enrol 2 users into a course with groupmode set to 'visible groups'.
120 // The users are not in any groups, and profiles should be visible because of the groupmode.
121 $user1 = $this->getDataGenerator()->create_user();
122 $user2 = $this->getDataGenerator()->create_user();
123 $course = $this->getDataGenerator()->create_course((object) ['groupmode' => 2]);
124 $this->getDataGenerator()->enrol_user($user1->id
, $course->id
);
125 $this->getDataGenerator()->enrol_user($user2->id
, $course->id
);
127 $this->setUser($user1);
128 $userdetails = user_get_user_details_courses($user2);
129 $this->assertIsArray($userdetails);
130 $this->assertEquals($user2->id
, $userdetails['id']);
134 * Tests that the user fields returned by the method can be limited.
136 * @covers ::user_get_user_details_courses
138 public function test_user_get_user_details_courses_limit_return() {
139 $this->resetAfterTest();
142 $user1 = $this->getDataGenerator()->create_user();
143 $user2 = $this->getDataGenerator()->create_user();
144 $course = $this->getDataGenerator()->create_course();
145 $this->getDataGenerator()->enrol_user($user1->id
, $course->id
);
146 $this->getDataGenerator()->enrol_user($user2->id
, $course->id
);
148 // Calculate the minimum fields that can be returned.
149 $namefields = \core_user\fields
::for_name()->get_required_fields();
150 $fields = array_intersect($namefields, user_get_default_fields());
152 $minimaluser = (object) [
154 'deleted' => $user2->deleted
,
157 foreach ($namefields as $field) {
158 $minimaluser->$field = $user2->$field;
161 $this->setUser($user1);
162 $fulldetails = user_get_user_details_courses($user2);
163 $limiteddetails = user_get_user_details_courses($minimaluser, $fields);
164 $this->assertIsArray($fulldetails);
165 $this->assertIsArray($limiteddetails);
166 $this->assertEquals($user2->id
, $fulldetails['id']);
167 $this->assertEquals($user2->id
, $limiteddetails['id']);
169 // Test that less data was returned when using a filter.
170 $fullcount = count($fulldetails);
171 $limitedcount = count($limiteddetails);
172 $this->assertLessThan($fullcount, $limitedcount);
173 $this->assertNotEquals($fulldetails, $limiteddetails);
177 * Test user_update_user.
179 public function test_user_update_user() {
182 $this->resetAfterTest();
184 // Create user and modify user profile.
185 $user = $this->getDataGenerator()->create_user();
186 $user->firstname
= 'Test';
187 $user->password
= 'M00dLe@T';
189 // Update user and capture event.
190 $sink = $this->redirectEvents();
191 user_update_user($user);
192 $events = $sink->get_events();
194 $event = array_pop($events);
196 // Test updated value.
197 $dbuser = $DB->get_record('user', array('id' => $user->id
));
198 $this->assertSame($user->firstname
, $dbuser->firstname
);
199 $this->assertNotSame('M00dLe@T', $dbuser->password
);
202 $this->assertInstanceOf('\core\event\user_updated', $event);
203 $this->assertSame($user->id
, $event->objectid
);
204 $this->assertEquals(\context_user
::instance($user->id
), $event->get_context());
206 // Update user with no password update.
207 $password = $user->password
= hash_internal_user_password('M00dLe@T');
208 user_update_user($user, false);
209 $dbuser = $DB->get_record('user', array('id' => $user->id
));
210 $this->assertSame($password, $dbuser->password
);
212 // Verify event is not triggred by user_update_user when needed.
213 $sink = $this->redirectEvents();
214 user_update_user($user, false, false);
215 $events = $sink->get_events();
217 $this->assertCount(0, $events);
219 // With password, there should be 1 event.
220 $sink = $this->redirectEvents();
221 user_update_user($user, true, false);
222 $events = $sink->get_events();
224 $this->assertCount(1, $events);
225 $event = array_pop($events);
226 $this->assertInstanceOf('\core\event\user_password_updated', $event);
228 // Test user data validation.
229 $user->username
= 'johndoe123';
230 $user->auth
= 'shibolth';
231 $user->country
= 'WW';
233 $user->theme
= 'somewrongthemename';
234 $user->timezone
= '30.5';
235 $debugmessages = $this->getDebuggingMessages();
236 user_update_user($user, true, false);
237 $this->assertDebuggingCalledCount(5, $debugmessages);
239 // Now, with valid user data.
240 $user->username
= 'johndoe321';
241 $user->auth
= 'shibboleth';
242 $user->country
= 'AU';
244 $user->theme
= 'classic';
245 $user->timezone
= 'Australia/Perth';
246 user_update_user($user, true, false);
247 $this->assertDebuggingNotCalled();
253 public function test_create_users() {
256 $this->resetAfterTest();
259 'username' => 'usernametest1',
260 'password' => 'Moodle2012!',
261 'idnumber' => 'idnumbertest1',
262 'firstname' => 'First Name User Test 1',
263 'lastname' => 'Last Name User Test 1',
264 'middlename' => 'Middle Name User Test 1',
265 'lastnamephonetic' => '最後のお名前のテスト一号',
266 'firstnamephonetic' => 'お名前のテスト一号',
267 'alternatename' => 'Alternate Name User Test 1',
268 'email' => 'usertest1@example.com',
269 'description' => 'This is a description for user 1',
274 // Create user and capture event.
275 $sink = $this->redirectEvents();
276 $user['id'] = user_create_user($user);
277 $events = $sink->get_events();
279 $event = array_pop($events);
281 // Test user info in DB.
282 $dbuser = $DB->get_record('user', array('id' => $user['id']));
283 $this->assertEquals($dbuser->username
, $user['username']);
284 $this->assertEquals($dbuser->idnumber
, $user['idnumber']);
285 $this->assertEquals($dbuser->firstname
, $user['firstname']);
286 $this->assertEquals($dbuser->lastname
, $user['lastname']);
287 $this->assertEquals($dbuser->email
, $user['email']);
288 $this->assertEquals($dbuser->description
, $user['description']);
289 $this->assertEquals($dbuser->city
, $user['city']);
290 $this->assertEquals($dbuser->country
, $user['country']);
293 $this->assertInstanceOf('\core\event\user_created', $event);
294 $this->assertEquals($user['id'], $event->objectid
);
295 $this->assertEquals(\context_user
::instance($user['id']), $event->get_context());
297 // Verify event is not triggred by user_create_user when needed.
298 $user = array('username' => 'usernametest2'); // Create another user.
299 $sink = $this->redirectEvents();
300 user_create_user($user, true, false);
301 $events = $sink->get_events();
303 $this->assertCount(0, $events);
305 // Test user data validation, first some invalid data.
306 $user['username'] = 'johndoe123';
307 $user['auth'] = 'shibolth';
308 $user['country'] = 'WW';
309 $user['lang'] = 'xy';
310 $user['theme'] = 'somewrongthemename';
311 $user['timezone'] = '-30.5';
312 $debugmessages = $this->getDebuggingMessages();
313 $user['id'] = user_create_user($user, true, false);
314 $this->assertDebuggingCalledCount(5, $debugmessages);
315 $dbuser = $DB->get_record('user', array('id' => $user['id']));
316 $this->assertEquals($dbuser->country
, 0);
317 $this->assertEquals($dbuser->lang
, 'en');
318 $this->assertEquals($dbuser->timezone
, '');
320 // Now, with valid user data.
321 $user['username'] = 'johndoe321';
322 $user['auth'] = 'shibboleth';
323 $user['country'] = 'AU';
324 $user['lang'] = 'en';
325 $user['theme'] = 'classic';
326 $user['timezone'] = 'Australia/Perth';
327 user_create_user($user, true, false);
328 $this->assertDebuggingNotCalled();
332 * Test that creating users populates default values
334 * @covers ::user_create_user
336 public function test_user_create_user_default_values(): void
{
339 $this->resetAfterTest();
341 // Update default values for city/country (both initially empty).
342 set_config('defaultcity', 'Nadi');
343 set_config('country', 'FJ');
345 $userid = user_create_user((object) [
346 'username' => 'newuser',
349 $user = \core_user
::get_user($userid);
350 $this->assertEquals($CFG->calendartype
, $user->calendartype
);
351 $this->assertEquals($CFG->defaultpreference_maildisplay
, $user->maildisplay
);
352 $this->assertEquals($CFG->defaultpreference_mailformat
, $user->mailformat
);
353 $this->assertEquals($CFG->defaultpreference_maildigest
, $user->maildigest
);
354 $this->assertEquals($CFG->defaultpreference_autosubscribe
, $user->autosubscribe
);
355 $this->assertEquals($CFG->defaultpreference_trackforums
, $user->trackforums
);
356 $this->assertEquals($CFG->lang
, $user->lang
);
357 $this->assertEquals($CFG->defaultcity
, $user->city
);
358 $this->assertEquals($CFG->country
, $user->country
);
362 * Test that {@link user_create_user()} throws exception when invalid username is provided.
364 * @dataProvider data_create_user_invalid_username
365 * @param string $username Invalid username
366 * @param string $expectmessage Expected exception message
368 public function test_create_user_invalid_username($username, $expectmessage) {
371 $this->resetAfterTest();
372 $CFG->extendedusernamechars
= false;
375 'username' => $username,
378 $this->expectException('moodle_exception');
379 $this->expectExceptionMessage($expectmessage);
381 user_create_user($user);
385 * Data provider for {@link self::test_create_user_invalid_username()}.
389 public function data_create_user_invalid_username() {
393 'The username cannot be blank',
395 'only_whitespace' => [
397 'The username cannot be blank',
401 'The username must be in lower case',
403 'extended_chars' => [
405 'The given username contains invalid characters',
411 * Test function user_count_login_failures().
413 public function test_user_count_login_failures() {
414 $this->resetAfterTest();
415 $user = $this->getDataGenerator()->create_user();
416 $this->assertEquals(0, get_user_preferences('login_failed_count_since_success', 0, $user));
417 for ($i = 0; $i < 10; $i++
) {
418 login_attempt_failed($user);
420 $this->assertEquals(10, get_user_preferences('login_failed_count_since_success', 0, $user));
421 $count = user_count_login_failures($user); // Reset count.
422 $this->assertEquals(10, $count);
423 $this->assertEquals(0, get_user_preferences('login_failed_count_since_success', 0, $user));
425 for ($i = 0; $i < 10; $i++
) {
426 login_attempt_failed($user);
428 $this->assertEquals(10, get_user_preferences('login_failed_count_since_success', 0, $user));
429 $count = user_count_login_failures($user, false); // Do not reset count.
430 $this->assertEquals(10, $count);
431 $this->assertEquals(10, get_user_preferences('login_failed_count_since_success', 0, $user));
435 * Test function user_add_password_history().
437 public function test_user_add_password_history() {
440 $this->resetAfterTest();
442 $user1 = $this->getDataGenerator()->create_user();
443 $user2 = $this->getDataGenerator()->create_user();
444 $user3 = $this->getDataGenerator()->create_user();
445 $DB->delete_records('user_password_history', array());
447 set_config('passwordreuselimit', 0);
449 user_add_password_history($user1->id
, 'pokus');
450 $this->assertEquals(0, $DB->count_records('user_password_history'));
452 // Test adding and discarding of old.
454 set_config('passwordreuselimit', 3);
456 user_add_password_history($user1->id
, 'pokus');
457 $this->assertEquals(1, $DB->count_records('user_password_history'));
458 $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user1->id
)));
460 user_add_password_history($user1->id
, 'pokus2');
461 user_add_password_history($user1->id
, 'pokus3');
462 user_add_password_history($user1->id
, 'pokus4');
463 $this->assertEquals(3, $DB->count_records('user_password_history'));
464 $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user1->id
)));
466 user_add_password_history($user2->id
, 'pokus1');
467 $this->assertEquals(4, $DB->count_records('user_password_history'));
468 $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user1->id
)));
469 $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user2->id
)));
471 user_add_password_history($user2->id
, 'pokus2');
472 user_add_password_history($user2->id
, 'pokus3');
473 $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user2->id
)));
475 $ids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id
), 'timecreated ASC, id ASC'));
476 user_add_password_history($user2->id
, 'pokus4');
477 $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user2->id
)));
478 $newids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id
), 'timecreated ASC, id ASC'));
480 $removed = array_shift($ids);
481 $added = array_pop($newids);
482 $this->assertSame($ids, $newids);
483 $this->assertGreaterThan($removed, $added);
485 // Test disabling prevents changes.
487 set_config('passwordreuselimit', 0);
489 $this->assertEquals(6, $DB->count_records('user_password_history'));
491 $ids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id
), 'timecreated ASC, id ASC'));
492 user_add_password_history($user2->id
, 'pokus5');
493 user_add_password_history($user3->id
, 'pokus1');
494 $newids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id
), 'timecreated ASC, id ASC'));
495 $this->assertSame($ids, $newids);
496 $this->assertEquals(6, $DB->count_records('user_password_history'));
498 set_config('passwordreuselimit', -1);
500 $ids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id
), 'timecreated ASC, id ASC'));
501 user_add_password_history($user2->id
, 'pokus6');
502 user_add_password_history($user3->id
, 'pokus6');
503 $newids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id
), 'timecreated ASC, id ASC'));
504 $this->assertSame($ids, $newids);
505 $this->assertEquals(6, $DB->count_records('user_password_history'));
509 * Test function user_add_password_history().
511 public function test_user_is_previously_used_password() {
514 $this->resetAfterTest();
516 $user1 = $this->getDataGenerator()->create_user();
517 $user2 = $this->getDataGenerator()->create_user();
518 $DB->delete_records('user_password_history', array());
520 set_config('passwordreuselimit', 0);
522 user_add_password_history($user1->id
, 'pokus');
523 $this->assertFalse(user_is_previously_used_password($user1->id
, 'pokus'));
525 set_config('passwordreuselimit', 3);
527 user_add_password_history($user2->id
, 'pokus1');
528 user_add_password_history($user2->id
, 'pokus2');
530 user_add_password_history($user1->id
, 'pokus1');
531 $this->assertTrue(user_is_previously_used_password($user1->id
, 'pokus1'));
532 $this->assertFalse(user_is_previously_used_password($user1->id
, 'pokus2'));
533 $this->assertFalse(user_is_previously_used_password($user1->id
, 'pokus3'));
534 $this->assertFalse(user_is_previously_used_password($user1->id
, 'pokus4'));
536 user_add_password_history($user1->id
, 'pokus2');
537 $this->assertTrue(user_is_previously_used_password($user1->id
, 'pokus1'));
538 $this->assertTrue(user_is_previously_used_password($user1->id
, 'pokus2'));
539 $this->assertFalse(user_is_previously_used_password($user1->id
, 'pokus3'));
540 $this->assertFalse(user_is_previously_used_password($user1->id
, 'pokus4'));
542 user_add_password_history($user1->id
, 'pokus3');
543 $this->assertTrue(user_is_previously_used_password($user1->id
, 'pokus1'));
544 $this->assertTrue(user_is_previously_used_password($user1->id
, 'pokus2'));
545 $this->assertTrue(user_is_previously_used_password($user1->id
, 'pokus3'));
546 $this->assertFalse(user_is_previously_used_password($user1->id
, 'pokus4'));
548 user_add_password_history($user1->id
, 'pokus4');
549 $this->assertFalse(user_is_previously_used_password($user1->id
, 'pokus1'));
550 $this->assertTrue(user_is_previously_used_password($user1->id
, 'pokus2'));
551 $this->assertTrue(user_is_previously_used_password($user1->id
, 'pokus3'));
552 $this->assertTrue(user_is_previously_used_password($user1->id
, 'pokus4'));
554 set_config('passwordreuselimit', 2);
556 $this->assertFalse(user_is_previously_used_password($user1->id
, 'pokus1'));
557 $this->assertFalse(user_is_previously_used_password($user1->id
, 'pokus2'));
558 $this->assertTrue(user_is_previously_used_password($user1->id
, 'pokus3'));
559 $this->assertTrue(user_is_previously_used_password($user1->id
, 'pokus4'));
561 set_config('passwordreuselimit', 3);
563 $this->assertFalse(user_is_previously_used_password($user1->id
, 'pokus1'));
564 $this->assertFalse(user_is_previously_used_password($user1->id
, 'pokus2'));
565 $this->assertTrue(user_is_previously_used_password($user1->id
, 'pokus3'));
566 $this->assertTrue(user_is_previously_used_password($user1->id
, 'pokus4'));
568 set_config('passwordreuselimit', 0);
570 $this->assertFalse(user_is_previously_used_password($user1->id
, 'pokus1'));
571 $this->assertFalse(user_is_previously_used_password($user1->id
, 'pokus2'));
572 $this->assertFalse(user_is_previously_used_password($user1->id
, 'pokus3'));
573 $this->assertFalse(user_is_previously_used_password($user1->id
, 'pokus4'));
577 * Test that password history is deleted together with user.
579 public function test_delete_of_hashes_on_user_delete() {
582 $this->resetAfterTest();
584 $user1 = $this->getDataGenerator()->create_user();
585 $user2 = $this->getDataGenerator()->create_user();
586 $DB->delete_records('user_password_history', array());
588 set_config('passwordreuselimit', 3);
590 user_add_password_history($user1->id
, 'pokus');
591 user_add_password_history($user2->id
, 'pokus1');
592 user_add_password_history($user2->id
, 'pokus2');
594 $this->assertEquals(3, $DB->count_records('user_password_history'));
595 $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user1->id
)));
596 $this->assertEquals(2, $DB->count_records('user_password_history', array('userid' => $user2->id
)));
599 $this->assertEquals(1, $DB->count_records('user_password_history'));
600 $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user1->id
)));
601 $this->assertEquals(0, $DB->count_records('user_password_history', array('userid' => $user2->id
)));
605 * Test user_list_view function
607 public function test_user_list_view() {
609 $this->resetAfterTest();
611 // Course without sections.
612 $course = $this->getDataGenerator()->create_course();
613 $context = \context_course
::instance($course->id
);
615 $this->setAdminUser();
617 // Redirect events to the sink, so we can recover them later.
618 $sink = $this->redirectEvents();
620 user_list_view($course, $context);
621 $events = $sink->get_events();
622 $this->assertCount(1, $events);
623 $event = reset($events);
625 // Check the event details are correct.
626 $this->assertInstanceOf('\core\event\user_list_viewed', $event);
627 $this->assertEquals($context, $event->get_context());
628 $this->assertEquals($course->shortname
, $event->other
['courseshortname']);
629 $this->assertEquals($course->fullname
, $event->other
['coursefullname']);
634 * Test setting the user menu avatar size.
636 public function test_user_menu_custom_avatar_size() {
638 $this->resetAfterTest(true);
643 $user = $this->getDataGenerator()->create_user();
644 $this->setUser($user);
645 $opts = user_get_user_navigation_info($user, $PAGE, array('avatarsize' => $testsize));
646 $avatarhtml = $opts->metadata
['useravatar'];
649 preg_match('/size-100/', $avatarhtml, $matches);
650 $this->assertCount(1, $matches);
654 * Test user_can_view_profile
656 public function test_user_can_view_profile() {
659 $this->resetAfterTest();
661 // Create five users.
662 $user1 = $this->getDataGenerator()->create_user();
663 $user2 = $this->getDataGenerator()->create_user();
664 $user3 = $this->getDataGenerator()->create_user();
665 $user4 = $this->getDataGenerator()->create_user();
666 $user5 = $this->getDataGenerator()->create_user();
667 $user6 = $this->getDataGenerator()->create_user(array('deleted' => 1));
668 $user7 = $this->getDataGenerator()->create_user();
669 $user8 = $this->getDataGenerator()->create_user();
670 $user8->id
= 0; // Visitor.
672 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
673 // Add the course creator role to the course contact and assign a user to that role.
674 $CFG->coursecontact
= '2';
675 $coursecreatorrole = $DB->get_record('role', array('shortname' => 'coursecreator'));
676 $this->getDataGenerator()->role_assign($coursecreatorrole->id
, $user7->id
);
678 // Create two courses.
679 $course1 = $this->getDataGenerator()->create_course();
680 $course2 = $this->getDataGenerator()->create_course();
681 $coursecontext = \context_course
::instance($course2->id
);
682 // Prepare another course with separate groups and groupmodeforce set to true.
683 $record = new \
stdClass();
684 $record->groupmode
= 1;
685 $record->groupmodeforce
= 1;
686 $course3 = $this->getDataGenerator()->create_course($record);
687 // Enrol users 1 and 2 in first course.
688 $this->getDataGenerator()->enrol_user($user1->id
, $course1->id
);
689 $this->getDataGenerator()->enrol_user($user2->id
, $course1->id
);
690 // Enrol users 2 and 3 in second course.
691 $this->getDataGenerator()->enrol_user($user2->id
, $course2->id
);
692 $this->getDataGenerator()->enrol_user($user3->id
, $course2->id
);
693 // Enrol users 1, 4, and 5 into course 3.
694 $this->getDataGenerator()->enrol_user($user1->id
, $course3->id
);
695 $this->getDataGenerator()->enrol_user($user4->id
, $course3->id
);
696 $this->getDataGenerator()->enrol_user($user5->id
, $course3->id
);
698 // User 3 should not be able to see user 1, either by passing their own course (course 2) or user 1's course (course 1).
699 $this->setUser($user3);
700 $this->assertFalse(user_can_view_profile($user1, $course2));
701 $this->assertFalse(user_can_view_profile($user1, $course1));
703 // Remove capability moodle/user:viewdetails in course 2.
704 assign_capability('moodle/user:viewdetails', CAP_PROHIBIT
, $studentrole->id
, $coursecontext);
705 // Set current user to user 1.
706 $this->setUser($user1);
707 // User 1 can see User 1's profile.
708 $this->assertTrue(user_can_view_profile($user1));
710 $tempcfg = $CFG->forceloginforprofiles
;
711 $CFG->forceloginforprofiles
= 0;
712 // Not forced to log in to view profiles, should be able to see all profiles besides user 6.
713 $users = array($user1, $user2, $user3, $user4, $user5, $user7);
714 foreach ($users as $user) {
715 $this->assertTrue(user_can_view_profile($user));
718 $CFG->forceloginforprofiles
= $tempcfg;
720 // User 1 can not see user 6 as they have been deleted.
721 $this->assertFalse(user_can_view_profile($user6));
722 // User 1 can see User 7 as they are a course contact.
723 $this->assertTrue(user_can_view_profile($user7));
724 // User 1 is in a course with user 2 and has the right capability - return true.
725 $this->assertTrue(user_can_view_profile($user2));
726 // User 1 is not in a course with user 3 - return false.
727 $this->assertFalse(user_can_view_profile($user3));
729 // Set current user to user 2.
730 $this->setUser($user2);
731 // User 2 is in a course with user 3 but does not have the right capability - return false.
732 $this->assertFalse(user_can_view_profile($user3));
734 // Set user 1 in one group and users 4 and 5 in another group.
735 $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course3->id
));
736 $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course3->id
));
737 groups_add_member($group1->id
, $user1->id
);
738 groups_add_member($group2->id
, $user4->id
);
739 groups_add_member($group2->id
, $user5->id
);
740 $this->setUser($user1);
741 // Check that user 1 can not see user 4.
742 $this->assertFalse(user_can_view_profile($user4));
743 // Check that user 5 can see user 4.
744 $this->setUser($user5);
745 $this->assertTrue(user_can_view_profile($user4));
747 // Test the user:viewalldetails cap check using the course creator role which, by default, can't see student profiles.
748 $this->setUser($user7);
749 $this->assertFalse(user_can_view_profile($user4));
750 assign_capability('moodle/user:viewalldetails', CAP_ALLOW
, $coursecreatorrole->id
, \context_system
::instance()->id
, true);
751 reload_all_capabilities();
752 $this->assertTrue(user_can_view_profile($user4));
753 unassign_capability('moodle/user:viewalldetails', $coursecreatorrole->id
, $coursecontext->id
);
754 reload_all_capabilities();
756 $CFG->coursecontact
= null;
758 // Visitor (Not a guest user, userid=0).
759 $CFG->forceloginforprofiles
= 1;
760 $this->setUser($user8);
761 $this->assertFalse(user_can_view_profile($user1));
763 // Let us test with guest user.
764 $this->setGuestUser();
765 $CFG->forceloginforprofiles
= 1;
766 foreach ($users as $user) {
767 $this->assertFalse(user_can_view_profile($user));
770 // Even with cap, still guests should not be allowed in.
771 $guestrole = $DB->get_records_menu('role', array('shortname' => 'guest'), 'id', 'archetype, id');
772 assign_capability('moodle/user:viewdetails', CAP_ALLOW
, $guestrole['guest'], \context_system
::instance()->id
, true);
773 reload_all_capabilities();
774 foreach ($users as $user) {
775 $this->assertFalse(user_can_view_profile($user));
778 $CFG->forceloginforprofiles
= 0;
779 foreach ($users as $user) {
780 $this->assertTrue(user_can_view_profile($user));
783 // Let us test with Visitor user.
784 $this->setUser($user8);
785 $CFG->forceloginforprofiles
= 1;
786 foreach ($users as $user) {
787 $this->assertFalse(user_can_view_profile($user));
790 $CFG->forceloginforprofiles
= 0;
791 foreach ($users as $user) {
792 $this->assertTrue(user_can_view_profile($user));
795 // Testing non-shared courses where capabilities are met, using system role overrides.
796 $CFG->forceloginforprofiles
= $tempcfg;
797 $course4 = $this->getDataGenerator()->create_course();
798 $this->getDataGenerator()->enrol_user($user1->id
, $course4->id
);
800 // Assign a manager role at the system context.
801 $managerrole = $DB->get_record('role', array('shortname' => 'manager'));
802 $user9 = $this->getDataGenerator()->create_user();
803 $this->getDataGenerator()->role_assign($managerrole->id
, $user9->id
);
805 // Make sure viewalldetails and viewdetails are overridden to 'prevent' (i.e. can be overridden at a lower context).
806 $systemcontext = \context_system
::instance();
807 assign_capability('moodle/user:viewdetails', CAP_PREVENT
, $managerrole->id
, $systemcontext, true);
808 assign_capability('moodle/user:viewalldetails', CAP_PREVENT
, $managerrole->id
, $systemcontext, true);
810 // And override these to 'Allow' in a specific course.
811 $course4context = \context_course
::instance($course4->id
);
812 assign_capability('moodle/user:viewalldetails', CAP_ALLOW
, $managerrole->id
, $course4context, true);
813 assign_capability('moodle/user:viewdetails', CAP_ALLOW
, $managerrole->id
, $course4context, true);
815 // The manager now shouldn't have viewdetails in the system or user context.
816 $this->setUser($user9);
817 $user1context = \context_user
::instance($user1->id
);
818 $this->assertFalse(has_capability('moodle/user:viewdetails', $systemcontext));
819 $this->assertFalse(has_capability('moodle/user:viewdetails', $user1context));
821 // Confirm that user_can_view_profile() returns true for $user1 when called without $course param. It should find $course1.
822 $this->assertTrue(user_can_view_profile($user1));
824 // Confirm this also works when restricting scope to just that course.
825 $this->assertTrue(user_can_view_profile($user1, $course4));
829 * Test user_get_user_details
831 public function test_user_get_user_details() {
834 $this->resetAfterTest();
836 // Create user and modify user profile.
837 $teacher = $this->getDataGenerator()->create_user();
838 $student = $this->getDataGenerator()->create_user();
839 $studentfullname = fullname($student);
841 $course1 = $this->getDataGenerator()->create_course();
842 $coursecontext = \context_course
::instance($course1->id
);
843 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
844 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
845 $this->getDataGenerator()->enrol_user($teacher->id
, $course1->id
);
846 $this->getDataGenerator()->enrol_user($student->id
, $course1->id
);
847 role_assign($teacherrole->id
, $teacher->id
, $coursecontext->id
);
848 role_assign($studentrole->id
, $student->id
, $coursecontext->id
);
850 accesslib_clear_all_caches_for_unit_testing();
852 // Get student details as a user with super system capabilities.
853 $result = user_get_user_details($student, $course1);
854 $this->assertEquals($student->id
, $result['id']);
855 $this->assertEquals($studentfullname, $result['fullname']);
856 $this->assertEquals($course1->id
, $result['enrolledcourses'][0]['id']);
858 $this->setUser($teacher);
859 // Get student details as a user who can only see this user in a course.
860 $result = user_get_user_details($student, $course1);
861 $this->assertEquals($student->id
, $result['id']);
862 $this->assertEquals($studentfullname, $result['fullname']);
863 $this->assertEquals($course1->id
, $result['enrolledcourses'][0]['id']);
865 // Get student details with required fields.
866 $result = user_get_user_details($student, $course1, array('id', 'fullname'));
867 $this->assertCount(2, $result);
868 $this->assertEquals($student->id
, $result['id']);
869 $this->assertEquals($studentfullname, $result['fullname']);
871 // Get exception for invalid required fields.
872 $this->expectException('moodle_exception');
873 $result = user_get_user_details($student, $course1, array('wrongrequiredfield'));
877 * Regression test for MDL-57840.
879 * Ensure the fields "auth, confirmed, idnumber, lang, theme, timezone and mailformat" are present when
880 * calling user_get_user_details() function.
882 public function test_user_get_user_details_missing_fields() {
885 $this->resetAfterTest(true);
886 $this->setAdminUser(); // We need capabilities to view the data.
887 $user = self
::getDataGenerator()->create_user([
890 'idnumber' => 'someidnumber',
892 'theme' => $CFG->theme
,
895 'trackforums' => '1',
898 // Fields that should get by default.
899 $got = user_get_user_details($user);
900 self
::assertSame('email', $got['auth']);
901 self
::assertSame('0', $got['confirmed']);
902 self
::assertSame('someidnumber', $got['idnumber']);
903 self
::assertSame('en', $got['lang']);
904 self
::assertSame($CFG->theme
, $got['theme']);
905 self
::assertSame('5', $got['timezone']);
906 self
::assertSame('0', $got['mailformat']);
907 self
::assertSame('1', $got['trackforums']);
911 * Test user_get_user_details_permissions.
912 * @covers ::user_get_user_details
914 public function test_user_get_user_details_permissions() {
917 $this->resetAfterTest();
919 // Create user and modify user profile.
920 $teacher = $this->getDataGenerator()->create_user();
921 $student1 = $this->getDataGenerator()->create_user(['idnumber' => 'user1id', 'city' => 'Barcelona', 'address' => 'BCN 1B']);
922 $student2 = $this->getDataGenerator()->create_user();
923 $student1fullname = fullname($student1);
925 $course = $this->getDataGenerator()->create_course();
926 $coursecontext = \context_course
::instance($course->id
);
927 $this->getDataGenerator()->enrol_user($teacher->id
, $course->id
);
928 $this->getDataGenerator()->enrol_user($student1->id
, $course->id
);
929 $this->getDataGenerator()->enrol_user($student2->id
, $course->id
);
930 $this->getDataGenerator()->role_assign('teacher', $teacher->id
, $coursecontext->id
);
931 $this->getDataGenerator()->role_assign('student', $student1->id
, $coursecontext->id
);
932 $this->getDataGenerator()->role_assign('student', $student2->id
, $coursecontext->id
);
934 accesslib_clear_all_caches_for_unit_testing();
936 // Get student details as a user with super system capabilities.
937 $result = user_get_user_details($student1, $course);
938 $this->assertEquals($student1->id
, $result['id']);
939 $this->assertEquals($student1fullname, $result['fullname']);
940 $this->assertEquals($course->id
, $result['enrolledcourses'][0]['id']);
942 $this->setUser($student2);
944 // Get student details with required fields.
945 $result = user_get_user_details($student1, $course, array('id', 'fullname', 'timezone', 'city', 'address', 'idnumber'));
946 $this->assertCount(4, $result); // Ensure address (never returned), idnumber (identity field) are not returned here.
947 $this->assertEquals($student1->id
, $result['id']);
948 $this->assertEquals($student1fullname, $result['fullname']);
949 $this->assertEquals($student1->timezone
, $result['timezone']);
950 $this->assertEquals($student1->city
, $result['city']);
952 // Set new identity fields and hidden fields and try to retrieve them without permission.
953 $CFG->showuseridentity
= $CFG->showuseridentity
. ',idnumber';
954 $CFG->hiddenuserfields
= 'city';
955 $result = user_get_user_details($student1, $course, array('id', 'fullname', 'timezone', 'city', 'address', 'idnumber'));
956 $this->assertCount(3, $result); // Ensure address, city and idnumber are not returned here.
957 $this->assertEquals($student1->id
, $result['id']);
958 $this->assertEquals($student1fullname, $result['fullname']);
959 $this->assertEquals($student1->timezone
, $result['timezone']);
961 // Now, teacher should have permission to see the idnumber and city fields.
962 $this->setUser($teacher);
963 $result = user_get_user_details($student1, $course, array('id', 'fullname', 'timezone', 'city', 'address', 'idnumber'));
964 $this->assertCount(5, $result); // Ensure address is not returned here.
965 $this->assertEquals($student1->id
, $result['id']);
966 $this->assertEquals($student1fullname, $result['fullname']);
967 $this->assertEquals($student1->timezone
, $result['timezone']);
968 $this->assertEquals($student1->idnumber
, $result['idnumber']);
969 $this->assertEquals($student1->city
, $result['city']);
971 // And admins can see anything.
972 $this->setAdminUser();
973 $result = user_get_user_details($student1, $course, array('id', 'fullname', 'timezone', 'city', 'address', 'idnumber'));
974 $this->assertCount(6, $result);
975 $this->assertEquals($student1->id
, $result['id']);
976 $this->assertEquals($student1fullname, $result['fullname']);
977 $this->assertEquals($student1->timezone
, $result['timezone']);
978 $this->assertEquals($student1->idnumber
, $result['idnumber']);
979 $this->assertEquals($student1->city
, $result['city']);
980 $this->assertEquals($student1->address
, $result['address']);
984 * Test user_get_user_details_groups.
985 * @covers ::user_get_user_details
987 public function test_user_get_user_details_groups() {
988 $this->resetAfterTest();
990 // Create user and modify user profile.
991 $teacher = $this->getDataGenerator()->create_user();
992 $student1 = $this->getDataGenerator()->create_user(['idnumber' => 'user1id', 'city' => 'Barcelona', 'address' => 'BCN 1B']);
993 $student2 = $this->getDataGenerator()->create_user();
995 $course = $this->getDataGenerator()->create_course();
996 $coursecontext = \context_course
::instance($course->id
);
997 $this->getDataGenerator()->enrol_user($teacher->id
, $course->id
);
998 $this->getDataGenerator()->enrol_user($student1->id
, $course->id
);
999 $this->getDataGenerator()->enrol_user($student2->id
, $course->id
);
1000 $this->getDataGenerator()->role_assign('teacher', $teacher->id
, $coursecontext->id
);
1001 $this->getDataGenerator()->role_assign('student', $student1->id
, $coursecontext->id
);
1002 $this->getDataGenerator()->role_assign('student', $student2->id
, $coursecontext->id
);
1004 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id
, 'name' => 'G1']);
1005 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id
, 'name' => 'G2']);
1007 // Each student in one group but teacher in two.
1008 groups_add_member($group1->id
, $student1->id
);
1009 groups_add_member($group1->id
, $teacher->id
);
1010 groups_add_member($group2->id
, $student2->id
);
1011 groups_add_member($group2->id
, $teacher->id
);
1013 accesslib_clear_all_caches_for_unit_testing();
1015 // A student can see other users groups when separate groups are not forced.
1016 $this->setUser($student2);
1018 // Get student details with groups.
1019 $result = user_get_user_details($student1, $course, array('id', 'fullname', 'groups'));
1020 $this->assertCount(3, $result);
1021 $this->assertEquals($group1->id
, $result['groups'][0]['id']);
1023 // Teacher is in two different groups.
1024 $result = user_get_user_details($teacher, $course, array('id', 'fullname', 'groups'));
1026 // Order by group id.
1027 usort($result['groups'], function($a, $b) {
1028 return $a['id'] - $b['id'];
1031 $this->assertCount(3, $result);
1032 $this->assertCount(2, $result['groups']);
1033 $this->assertEquals($group1->id
, $result['groups'][0]['id']);
1034 $this->assertEquals($group2->id
, $result['groups'][1]['id']);
1036 // Change to separate groups.
1037 $course->groupmode
= SEPARATEGROUPS
;
1038 $course->groupmodeforce
= true;
1039 update_course($course);
1041 // Teacher is in two groups but I can only see the one shared with me.
1042 $result = user_get_user_details($teacher, $course, array('id', 'fullname', 'groups'));
1044 $this->assertCount(3, $result);
1045 $this->assertCount(1, $result['groups']);
1046 $this->assertEquals($group2->id
, $result['groups'][0]['id']);