MDL-63842 core_user: Remove unnecessary DB fetch
[moodle.git] / user / tests / userlib_test.php
CommitLineData
bb78e249
RT
1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Unit tests for user/lib.php.
19 *
20 * @package core_user
21 * @category phpunit
22 * @copyright 2013 Rajesh Taneja <rajesh@moodle.com>
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
26defined('MOODLE_INTERNAL') || die();
27
28global $CFG;
29require_once($CFG->dirroot.'/user/lib.php');
30
31/**
32 * Unit tests for user lib api.
33 *
34 * @package core_user
35 * @category phpunit
36 * @copyright 2013 Rajesh Taneja <rajesh@moodle.com>
37 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38 */
39class core_userliblib_testcase extends advanced_testcase {
d26eae4a
DW
40 /**
41 * Test user_get_user_details_courses
42 */
43 public function test_user_get_user_details_courses() {
44 global $DB;
45
46 $this->resetAfterTest();
47
48 // Create user and modify user profile.
49 $user1 = $this->getDataGenerator()->create_user();
50 $user2 = $this->getDataGenerator()->create_user();
51
52 $course1 = $this->getDataGenerator()->create_course();
53 $coursecontext = context_course::instance($course1->id);
54 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
55 $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
56 $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
57 role_assign($teacherrole->id, $user1->id, $coursecontext->id);
58 role_assign($teacherrole->id, $user2->id, $coursecontext->id);
59
60 accesslib_clear_all_caches_for_unit_testing();
61
62 // Get user2 details as a user with super system capabilities.
63 $result = user_get_user_details_courses($user2);
64 $this->assertEquals($user2->id, $result['id']);
65 $this->assertEquals(fullname($user2), $result['fullname']);
66 $this->assertEquals($course1->id, $result['enrolledcourses'][0]['id']);
67
68 $this->setUser($user1);
69 // Get user2 details as a user who can only see this user in a course.
70 $result = user_get_user_details_courses($user2);
71 $this->assertEquals($user2->id, $result['id']);
72 $this->assertEquals(fullname($user2), $result['fullname']);
73 $this->assertEquals($course1->id, $result['enrolledcourses'][0]['id']);
74
75 }
76
bb78e249
RT
77 /**
78 * Test user_update_user.
79 */
80 public function test_user_update_user() {
81 global $DB;
82
83 $this->resetAfterTest();
84
85 // Create user and modify user profile.
86 $user = $this->getDataGenerator()->create_user();
87 $user->firstname = 'Test';
88 $user->password = 'M00dLe@T';
89
90 // Update user and capture event.
91 $sink = $this->redirectEvents();
92 user_update_user($user);
93 $events = $sink->get_events();
94 $sink->close();
95 $event = array_pop($events);
96
97 // Test updated value.
98 $dbuser = $DB->get_record('user', array('id' => $user->id));
99 $this->assertSame($user->firstname, $dbuser->firstname);
100 $this->assertNotSame('M00dLe@T', $dbuser->password);
101
102 // Test event.
103 $this->assertInstanceOf('\core\event\user_updated', $event);
104 $this->assertSame($user->id, $event->objectid);
105 $this->assertSame('user_updated', $event->get_legacy_eventname());
106 $this->assertEventLegacyData($dbuser, $event);
107 $this->assertEquals(context_user::instance($user->id), $event->get_context());
108 $expectedlogdata = array(SITEID, 'user', 'update', 'view.php?id='.$user->id, '');
109 $this->assertEventLegacyLogData($expectedlogdata, $event);
110
111 // Update user with no password update.
112 $password = $user->password = hash_internal_user_password('M00dLe@T');
113 user_update_user($user, false);
114 $dbuser = $DB->get_record('user', array('id' => $user->id));
115 $this->assertSame($password, $dbuser->password);
2b55cb1b
RT
116
117 // Verify event is not triggred by user_update_user when needed.
118 $sink = $this->redirectEvents();
119 user_update_user($user, false, false);
120 $events = $sink->get_events();
121 $sink->close();
122 $this->assertCount(0, $events);
123
124 // With password, there should be 1 event.
125 $sink = $this->redirectEvents();
126 user_update_user($user, true, false);
127 $events = $sink->get_events();
128 $sink->close();
129 $this->assertCount(1, $events);
130 $event = array_pop($events);
131 $this->assertInstanceOf('\core\event\user_password_updated', $event);
7a067206
SL
132
133 // Test user data validation.
134 $user->username = 'johndoe123';
135 $user->auth = 'shibolth';
136 $user->country = 'WW';
137 $user->lang = 'xy';
138 $user->theme = 'somewrongthemename';
16825c4e 139 $user->timezone = '30.5';
7a067206
SL
140 $user->url = 'wwww.somewrong@#$url.com.aus';
141 $debugmessages = $this->getDebuggingMessages();
142 user_update_user($user, true, false);
143 $this->assertDebuggingCalledCount(6, $debugmessages);
144
145 // Now, with valid user data.
146 $user->username = 'johndoe321';
147 $user->auth = 'shibboleth';
148 $user->country = 'AU';
149 $user->lang = 'en';
150 $user->theme = 'clean';
151 $user->timezone = 'Australia/Perth';
152 $user->url = 'www.moodle.org';
153 user_update_user($user, true, false);
154 $this->assertDebuggingNotCalled();
bb78e249
RT
155 }
156
157 /**
158 * Test create_users.
159 */
160 public function test_create_users() {
161 global $DB;
162
163 $this->resetAfterTest();
164
165 $user = array(
166 'username' => 'usernametest1',
167 'password' => 'Moodle2012!',
168 'idnumber' => 'idnumbertest1',
169 'firstname' => 'First Name User Test 1',
170 'lastname' => 'Last Name User Test 1',
171 'middlename' => 'Middle Name User Test 1',
172 'lastnamephonetic' => '最後のお名前のテスト一号',
173 'firstnamephonetic' => 'お名前のテスト一号',
174 'alternatename' => 'Alternate Name User Test 1',
0fe86bbd 175 'email' => 'usertest1@example.com',
bb78e249
RT
176 'description' => 'This is a description for user 1',
177 'city' => 'Perth',
7a067206 178 'country' => 'AU'
bb78e249
RT
179 );
180
181 // Create user and capture event.
182 $sink = $this->redirectEvents();
183 $user['id'] = user_create_user($user);
184 $events = $sink->get_events();
185 $sink->close();
186 $event = array_pop($events);
187
188 // Test user info in DB.
189 $dbuser = $DB->get_record('user', array('id' => $user['id']));
190 $this->assertEquals($dbuser->username, $user['username']);
191 $this->assertEquals($dbuser->idnumber, $user['idnumber']);
192 $this->assertEquals($dbuser->firstname, $user['firstname']);
193 $this->assertEquals($dbuser->lastname, $user['lastname']);
194 $this->assertEquals($dbuser->email, $user['email']);
195 $this->assertEquals($dbuser->description, $user['description']);
196 $this->assertEquals($dbuser->city, $user['city']);
197 $this->assertEquals($dbuser->country, $user['country']);
198
199 // Test event.
200 $this->assertInstanceOf('\core\event\user_created', $event);
201 $this->assertEquals($user['id'], $event->objectid);
202 $this->assertEquals('user_created', $event->get_legacy_eventname());
203 $this->assertEquals(context_user::instance($user['id']), $event->get_context());
204 $this->assertEventLegacyData($dbuser, $event);
205 $expectedlogdata = array(SITEID, 'user', 'add', '/view.php?id='.$event->objectid, fullname($dbuser));
206 $this->assertEventLegacyLogData($expectedlogdata, $event);
2b55cb1b
RT
207
208 // Verify event is not triggred by user_create_user when needed.
209 $user = array('username' => 'usernametest2'); // Create another user.
210 $sink = $this->redirectEvents();
211 user_create_user($user, true, false);
212 $events = $sink->get_events();
213 $sink->close();
214 $this->assertCount(0, $events);
7a067206
SL
215
216 // Test user data validation, first some invalid data.
217 $user['username'] = 'johndoe123';
218 $user['auth'] = 'shibolth';
219 $user['country'] = 'WW';
220 $user['lang'] = 'xy';
221 $user['theme'] = 'somewrongthemename';
16825c4e 222 $user['timezone'] = '-30.5';
7a067206
SL
223 $user['url'] = 'wwww.somewrong@#$url.com.aus';
224 $debugmessages = $this->getDebuggingMessages();
225 $user['id'] = user_create_user($user, true, false);
226 $this->assertDebuggingCalledCount(6, $debugmessages);
227 $dbuser = $DB->get_record('user', array('id' => $user['id']));
228 $this->assertEquals($dbuser->country, 0);
229 $this->assertEquals($dbuser->lang, 'en');
16825c4e 230 $this->assertEquals($dbuser->timezone, '');
7a067206
SL
231
232 // Now, with valid user data.
233 $user['username'] = 'johndoe321';
234 $user['auth'] = 'shibboleth';
235 $user['country'] = 'AU';
236 $user['lang'] = 'en';
237 $user['theme'] = 'clean';
238 $user['timezone'] = 'Australia/Perth';
239 $user['url'] = 'www.moodle.org';
240 user_create_user($user, true, false);
241 $this->assertDebuggingNotCalled();
bb78e249 242 }
52dc1de7 243
784883e1
DM
244 /**
245 * Test that {@link user_create_user()} throws exception when invalid username is provided.
246 *
247 * @dataProvider data_create_user_invalid_username
248 * @param string $username Invalid username
249 * @param string $expectmessage Expected exception message
250 */
251 public function test_create_user_invalid_username($username, $expectmessage) {
252 global $CFG;
253
254 $this->resetAfterTest();
255 $CFG->extendedusernamechars = false;
256
257 $user = [
258 'username' => $username,
259 ];
260
261 $this->expectException('moodle_exception');
262 $this->expectExceptionMessage($expectmessage);
263
264 user_create_user($user);
265 }
266
267 /**
268 * Data provider for {@link self::test_create_user_invalid_username()}.
269 *
270 * @return array
271 */
272 public function data_create_user_invalid_username() {
273 return [
274 'empty_string' => [
275 '',
276 'The username cannot be blank',
277 ],
278 'only_whitespace' => [
279 "\t\t \t\n ",
280 'The username cannot be blank',
281 ],
282 'lower_case' => [
283 'Mudrd8mz',
284 'The username must be in lower case',
285 ],
286 'extended_chars' => [
287 'dmudrák',
288 'The given username contains invalid characters',
289 ],
290 ];
291 }
292
52dc1de7
AA
293 /**
294 * Test function user_count_login_failures().
295 */
296 public function test_user_count_login_failures() {
297 $this->resetAfterTest();
298 $user = $this->getDataGenerator()->create_user();
299 $this->assertEquals(0, get_user_preferences('login_failed_count_since_success', 0, $user));
300 for ($i = 0; $i < 10; $i++) {
301 login_attempt_failed($user);
302 }
303 $this->assertEquals(10, get_user_preferences('login_failed_count_since_success', 0, $user));
304 $count = user_count_login_failures($user); // Reset count.
305 $this->assertEquals(10, $count);
306 $this->assertEquals(0, get_user_preferences('login_failed_count_since_success', 0, $user));
307
308 for ($i = 0; $i < 10; $i++) {
309 login_attempt_failed($user);
310 }
311 $this->assertEquals(10, get_user_preferences('login_failed_count_since_success', 0, $user));
312 $count = user_count_login_failures($user, false); // Do not reset count.
313 $this->assertEquals(10, $count);
314 $this->assertEquals(10, get_user_preferences('login_failed_count_since_success', 0, $user));
315 }
1d658535
PS
316
317 /**
318 * Test function user_add_password_history().
319 */
320 public function test_user_add_password_history() {
321 global $DB;
322
323 $this->resetAfterTest();
324
325 $user1 = $this->getDataGenerator()->create_user();
326 $user2 = $this->getDataGenerator()->create_user();
327 $user3 = $this->getDataGenerator()->create_user();
328 $DB->delete_records('user_password_history', array());
329
330 set_config('passwordreuselimit', 0);
331
332 user_add_password_history($user1->id, 'pokus');
333 $this->assertEquals(0, $DB->count_records('user_password_history'));
334
335 // Test adding and discarding of old.
336
337 set_config('passwordreuselimit', 3);
338
339 user_add_password_history($user1->id, 'pokus');
340 $this->assertEquals(1, $DB->count_records('user_password_history'));
341 $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user1->id)));
342
343 user_add_password_history($user1->id, 'pokus2');
344 user_add_password_history($user1->id, 'pokus3');
345 user_add_password_history($user1->id, 'pokus4');
346 $this->assertEquals(3, $DB->count_records('user_password_history'));
347 $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user1->id)));
348
349 user_add_password_history($user2->id, 'pokus1');
350 $this->assertEquals(4, $DB->count_records('user_password_history'));
351 $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user1->id)));
352 $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user2->id)));
353
354 user_add_password_history($user2->id, 'pokus2');
355 user_add_password_history($user2->id, 'pokus3');
356 $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user2->id)));
357
358 $ids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
359 user_add_password_history($user2->id, 'pokus4');
360 $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user2->id)));
361 $newids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
362
363 $removed = array_shift($ids);
364 $added = array_pop($newids);
365 $this->assertSame($ids, $newids);
366 $this->assertGreaterThan($removed, $added);
367
368 // Test disabling prevents changes.
369
370 set_config('passwordreuselimit', 0);
371
372 $this->assertEquals(6, $DB->count_records('user_password_history'));
373
374 $ids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
375 user_add_password_history($user2->id, 'pokus5');
376 user_add_password_history($user3->id, 'pokus1');
377 $newids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
378 $this->assertSame($ids, $newids);
379 $this->assertEquals(6, $DB->count_records('user_password_history'));
380
381 set_config('passwordreuselimit', -1);
382
383 $ids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
384 user_add_password_history($user2->id, 'pokus6');
385 user_add_password_history($user3->id, 'pokus6');
386 $newids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
387 $this->assertSame($ids, $newids);
388 $this->assertEquals(6, $DB->count_records('user_password_history'));
389 }
390
391 /**
392 * Test function user_add_password_history().
393 */
394 public function test_user_is_previously_used_password() {
395 global $DB;
396
397 $this->resetAfterTest();
398
399 $user1 = $this->getDataGenerator()->create_user();
400 $user2 = $this->getDataGenerator()->create_user();
401 $DB->delete_records('user_password_history', array());
402
403 set_config('passwordreuselimit', 0);
404
405 user_add_password_history($user1->id, 'pokus');
406 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus'));
407
408 set_config('passwordreuselimit', 3);
409
410 user_add_password_history($user2->id, 'pokus1');
411 user_add_password_history($user2->id, 'pokus2');
412
413 user_add_password_history($user1->id, 'pokus1');
414 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus1'));
415 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus2'));
416 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus3'));
417 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus4'));
418
419 user_add_password_history($user1->id, 'pokus2');
420 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus1'));
421 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus2'));
422 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus3'));
423 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus4'));
424
425 user_add_password_history($user1->id, 'pokus3');
426 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus1'));
427 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus2'));
428 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus3'));
429 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus4'));
430
431 user_add_password_history($user1->id, 'pokus4');
432 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus1'));
433 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus2'));
434 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus3'));
435 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus4'));
436
437 set_config('passwordreuselimit', 2);
438
439 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus1'));
440 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus2'));
441 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus3'));
442 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus4'));
443
444 set_config('passwordreuselimit', 3);
445
446 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus1'));
447 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus2'));
448 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus3'));
449 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus4'));
450
451 set_config('passwordreuselimit', 0);
452
453 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus1'));
454 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus2'));
455 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus3'));
456 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus4'));
457 }
458
459 /**
460 * Test that password history is deleted together with user.
461 */
462 public function test_delete_of_hashes_on_user_delete() {
463 global $DB;
464
465 $this->resetAfterTest();
466
467 $user1 = $this->getDataGenerator()->create_user();
468 $user2 = $this->getDataGenerator()->create_user();
469 $DB->delete_records('user_password_history', array());
470
471 set_config('passwordreuselimit', 3);
472
473 user_add_password_history($user1->id, 'pokus');
474 user_add_password_history($user2->id, 'pokus1');
475 user_add_password_history($user2->id, 'pokus2');
476
477 $this->assertEquals(3, $DB->count_records('user_password_history'));
478 $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user1->id)));
479 $this->assertEquals(2, $DB->count_records('user_password_history', array('userid' => $user2->id)));
480
481 delete_user($user2);
482 $this->assertEquals(1, $DB->count_records('user_password_history'));
483 $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user1->id)));
484 $this->assertEquals(0, $DB->count_records('user_password_history', array('userid' => $user2->id)));
485 }
826d572d
JL
486
487 /**
488 * Test user_list_view function
489 */
490 public function test_user_list_view() {
491
492 $this->resetAfterTest();
493
494 // Course without sections.
495 $course = $this->getDataGenerator()->create_course();
496 $context = context_course::instance($course->id);
497
498 $this->setAdminUser();
499
500 // Redirect events to the sink, so we can recover them later.
501 $sink = $this->redirectEvents();
502
503 user_list_view($course, $context);
504 $events = $sink->get_events();
505 $this->assertCount(1, $events);
506 $event = reset($events);
507
508 // Check the event details are correct.
509 $this->assertInstanceOf('\core\event\user_list_viewed', $event);
510 $this->assertEquals($context, $event->get_context());
511 $this->assertEquals($course->shortname, $event->other['courseshortname']);
512 $this->assertEquals($course->fullname, $event->other['coursefullname']);
513
514 }
515
9dcd5035
DB
516 /**
517 * Test setting the user menu avatar size.
518 */
519 public function test_user_menu_custom_avatar_size() {
520 global $PAGE;
521 $this->resetAfterTest(true);
522
523 $testsize = 100;
524
22b8c5b8 525 $PAGE->set_url('/');
9dcd5035
DB
526 $user = $this->getDataGenerator()->create_user();
527 $opts = user_get_user_navigation_info($user, $PAGE, array('avatarsize' => $testsize));
528 $avatarhtml = $opts->metadata['useravatar'];
529
530 $matches = [];
531 preg_match('/(?:.*width=")(\d*)(?:" height=")(\d*)(?:".*\/>)/', $avatarhtml, $matches);
532 $this->assertCount(3, $matches);
533
534 $this->assertEquals(intval($matches[1]), $testsize);
535 $this->assertEquals(intval($matches[2]), $testsize);
536 }
537
82e4d438
AG
538 /**
539 * Test user_can_view_profile
540 */
541 public function test_user_can_view_profile() {
542 global $DB, $CFG;
543
544 $this->resetAfterTest();
545
546 // Create five users.
547 $user1 = $this->getDataGenerator()->create_user();
548 $user2 = $this->getDataGenerator()->create_user();
549 $user3 = $this->getDataGenerator()->create_user();
550 $user4 = $this->getDataGenerator()->create_user();
551 $user5 = $this->getDataGenerator()->create_user();
552 $user6 = $this->getDataGenerator()->create_user(array('deleted' => 1));
553 $user7 = $this->getDataGenerator()->create_user();
3e40dc8c
AA
554 $user8 = $this->getDataGenerator()->create_user();
555 $user8->id = 0; // Visitor.
82e4d438
AG
556
557 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
558 // Add the course creator role to the course contact and assign a user to that role.
559 $CFG->coursecontact = '2';
560 $coursecreatorrole = $DB->get_record('role', array('shortname' => 'coursecreator'));
561 $this->getDataGenerator()->role_assign($coursecreatorrole->id, $user7->id);
562
563 // Create two courses.
564 $course1 = $this->getDataGenerator()->create_course();
565 $course2 = $this->getDataGenerator()->create_course();
566 $coursecontext = context_course::instance($course2->id);
567 // Prepare another course with separate groups and groupmodeforce set to true.
568 $record = new stdClass();
569 $record->groupmode = 1;
570 $record->groupmodeforce = 1;
571 $course3 = $this->getDataGenerator()->create_course($record);
572 // Enrol users 1 and 2 in first course.
573 $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
574 $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
575 // Enrol users 2 and 3 in second course.
576 $this->getDataGenerator()->enrol_user($user2->id, $course2->id);
577 $this->getDataGenerator()->enrol_user($user3->id, $course2->id);
578 // Enrol users 1, 4, and 5 into course 3.
579 $this->getDataGenerator()->enrol_user($user1->id, $course3->id);
580 $this->getDataGenerator()->enrol_user($user4->id, $course3->id);
581 $this->getDataGenerator()->enrol_user($user5->id, $course3->id);
582
4266786b
JD
583 // 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).
584 $this->setUser($user3);
585 $this->assertFalse(user_can_view_profile($user1, $course2));
586 $this->assertFalse(user_can_view_profile($user1, $course1));
587
82e4d438
AG
588 // Remove capability moodle/user:viewdetails in course 2.
589 assign_capability('moodle/user:viewdetails', CAP_PROHIBIT, $studentrole->id, $coursecontext);
82e4d438
AG
590 // Set current user to user 1.
591 $this->setUser($user1);
592 // User 1 can see User 1's profile.
593 $this->assertTrue(user_can_view_profile($user1));
594
595 $tempcfg = $CFG->forceloginforprofiles;
596 $CFG->forceloginforprofiles = 0;
597 // Not forced to log in to view profiles, should be able to see all profiles besides user 6.
598 $users = array($user1, $user2, $user3, $user4, $user5, $user7);
599 foreach ($users as $user) {
600 $this->assertTrue(user_can_view_profile($user));
601 }
602 // Restore setting.
603 $CFG->forceloginforprofiles = $tempcfg;
604
605 // User 1 can not see user 6 as they have been deleted.
606 $this->assertFalse(user_can_view_profile($user6));
607 // User 1 can see User 7 as they are a course contact.
608 $this->assertTrue(user_can_view_profile($user7));
609 // User 1 is in a course with user 2 and has the right capability - return true.
610 $this->assertTrue(user_can_view_profile($user2));
611 // User 1 is not in a course with user 3 - return false.
612 $this->assertFalse(user_can_view_profile($user3));
613
614 // Set current user to user 2.
615 $this->setUser($user2);
616 // User 2 is in a course with user 3 but does not have the right capability - return false.
617 $this->assertFalse(user_can_view_profile($user3));
618
619 // Set user 1 in one group and users 4 and 5 in another group.
620 $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course3->id));
621 $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course3->id));
622 groups_add_member($group1->id, $user1->id);
623 groups_add_member($group2->id, $user4->id);
624 groups_add_member($group2->id, $user5->id);
625 $this->setUser($user1);
626 // Check that user 1 can not see user 4.
627 $this->assertFalse(user_can_view_profile($user4));
628 // Check that user 5 can see user 4.
629 $this->setUser($user5);
630 $this->assertTrue(user_can_view_profile($user4));
631
4d068a67
JD
632 // Test the user:viewalldetails cap check using the course creator role which, by default, can't see student profiles.
633 $this->setUser($user7);
634 $this->assertFalse(user_can_view_profile($user4));
635 assign_capability('moodle/user:viewalldetails', CAP_ALLOW, $coursecreatorrole->id, context_system::instance()->id, true);
636 reload_all_capabilities();
637 $this->assertTrue(user_can_view_profile($user4));
638 unassign_capability('moodle/user:viewalldetails', $coursecreatorrole->id, $coursecontext->id);
639 reload_all_capabilities();
640
82e4d438 641 $CFG->coursecontact = null;
3e40dc8c
AA
642
643 // Visitor (Not a guest user, userid=0).
644 $CFG->forceloginforprofiles = 1;
645 $this->setUser($user8);
d621396f 646 $this->assertFalse(user_can_view_profile($user1));
3e40dc8c 647
58d85af2
AA
648 // Let us test with guest user.
649 $this->setGuestUser();
650 $CFG->forceloginforprofiles = 1;
651 foreach ($users as $user) {
652 $this->assertFalse(user_can_view_profile($user));
653 }
654
655 // Even with cap, still guests should not be allowed in.
d621396f
TL
656 $guestrole = $DB->get_records_menu('role', array('shortname' => 'guest'), 'id', 'archetype, id');
657 assign_capability('moodle/user:viewdetails', CAP_ALLOW, $guestrole['guest'], context_system::instance()->id, true);
3e40dc8c 658 reload_all_capabilities();
58d85af2
AA
659 foreach ($users as $user) {
660 $this->assertFalse(user_can_view_profile($user));
661 }
662
663 $CFG->forceloginforprofiles = 0;
664 foreach ($users as $user) {
665 $this->assertTrue(user_can_view_profile($user));
666 }
667
668 // Let us test with Visitor user.
669 $this->setUser($user8);
3e40dc8c 670 $CFG->forceloginforprofiles = 1;
58d85af2
AA
671 foreach ($users as $user) {
672 $this->assertFalse(user_can_view_profile($user));
673 }
674
3e40dc8c 675 $CFG->forceloginforprofiles = 0;
58d85af2
AA
676 foreach ($users as $user) {
677 $this->assertTrue(user_can_view_profile($user));
678 }
067accce
JD
679
680 // Testing non-shared courses where capabilities are met, using system role overrides.
681 $CFG->forceloginforprofiles = $tempcfg;
682 $course4 = $this->getDataGenerator()->create_course();
683 $this->getDataGenerator()->enrol_user($user1->id, $course4->id);
684
685 // Assign a manager role at the system context.
686 $managerrole = $DB->get_record('role', array('shortname' => 'manager'));
687 $user9 = $this->getDataGenerator()->create_user();
688 $this->getDataGenerator()->role_assign($managerrole->id, $user9->id);
689
690 // Make sure viewalldetails and viewdetails are overridden to 'prevent' (i.e. can be overridden at a lower context).
691 $systemcontext = context_system::instance();
692 assign_capability('moodle/user:viewdetails', CAP_PREVENT, $managerrole->id, $systemcontext, true);
693 assign_capability('moodle/user:viewalldetails', CAP_PREVENT, $managerrole->id, $systemcontext, true);
067accce
JD
694
695 // And override these to 'Allow' in a specific course.
696 $course4context = context_course::instance($course4->id);
697 assign_capability('moodle/user:viewalldetails', CAP_ALLOW, $managerrole->id, $course4context, true);
698 assign_capability('moodle/user:viewdetails', CAP_ALLOW, $managerrole->id, $course4context, true);
067accce
JD
699
700 // The manager now shouldn't have viewdetails in the system or user context.
701 $this->setUser($user9);
702 $user1context = context_user::instance($user1->id);
703 $this->assertFalse(has_capability('moodle/user:viewdetails', $systemcontext));
704 $this->assertFalse(has_capability('moodle/user:viewdetails', $user1context));
705
706 // Confirm that user_can_view_profile() returns true for $user1 when called without $course param. It should find $course1.
707 $this->assertTrue(user_can_view_profile($user1));
708
709 // Confirm this also works when restricting scope to just that course.
710 $this->assertTrue(user_can_view_profile($user1, $course4));
82e4d438 711 }
6399ecd7
JPG
712
713 /**
714 * Test user_get_user_details
715 */
716 public function test_user_get_user_details() {
717 global $DB;
718
719 $this->resetAfterTest();
720
721 // Create user and modify user profile.
722 $teacher = $this->getDataGenerator()->create_user();
723 $student = $this->getDataGenerator()->create_user();
724 $studentfullname = fullname($student);
725
726 $course1 = $this->getDataGenerator()->create_course();
727 $coursecontext = context_course::instance($course1->id);
728 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
729 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
730 $this->getDataGenerator()->enrol_user($teacher->id, $course1->id);
731 $this->getDataGenerator()->enrol_user($student->id, $course1->id);
732 role_assign($teacherrole->id, $teacher->id, $coursecontext->id);
733 role_assign($studentrole->id, $student->id, $coursecontext->id);
734
735 accesslib_clear_all_caches_for_unit_testing();
736
737 // Get student details as a user with super system capabilities.
738 $result = user_get_user_details($student, $course1);
739 $this->assertEquals($student->id, $result['id']);
740 $this->assertEquals($studentfullname, $result['fullname']);
741 $this->assertEquals($course1->id, $result['enrolledcourses'][0]['id']);
742
743 $this->setUser($teacher);
744 // Get student details as a user who can only see this user in a course.
745 $result = user_get_user_details($student, $course1);
746 $this->assertEquals($student->id, $result['id']);
747 $this->assertEquals($studentfullname, $result['fullname']);
748 $this->assertEquals($course1->id, $result['enrolledcourses'][0]['id']);
749
750 // Get student details with required fields.
751 $result = user_get_user_details($student, $course1, array('id', 'fullname'));
752 $this->assertCount(2, $result);
753 $this->assertEquals($student->id, $result['id']);
754 $this->assertEquals($studentfullname, $result['fullname']);
755
756 // Get exception for invalid required fields.
757 $this->expectException('moodle_exception');
758 $result = user_get_user_details($student, $course1, array('wrongrequiredfield'));
759 }
b80caca1
DTR
760
761 /**
762 * Regression test for MDL-57840.
763 *
764 * Ensure the fields "auth, confirmed, idnumber, lang, theme, timezone and mailformat" are present when
765 * calling user_get_user_details() function.
766 */
767 public function test_user_get_user_details_missing_fields() {
6db24235
JL
768 global $CFG;
769
b80caca1
DTR
770 $this->resetAfterTest(true);
771 $this->setAdminUser(); // We need capabilities to view the data.
772 $user = self::getDataGenerator()->create_user([
773 'auth' => 'auth_something',
774 'confirmed' => '0',
775 'idnumber' => 'someidnumber',
6db24235
JL
776 'lang' => 'en',
777 'theme' => $CFG->theme,
b80caca1
DTR
778 'timezone' => '50',
779 'mailformat' => '0',
780 ]);
781
782 // Fields that should get by default.
783 $got = user_get_user_details($user);
784 self::assertSame('auth_something', $got['auth']);
785 self::assertSame('0', $got['confirmed']);
786 self::assertSame('someidnumber', $got['idnumber']);
6db24235
JL
787 self::assertSame('en', $got['lang']);
788 self::assertSame($CFG->theme, $got['theme']);
b80caca1
DTR
789 self::assertSame('50', $got['timezone']);
790 self::assertSame('0', $got['mailformat']);
791 }
ba6645da
MN
792
793 /**
794 * Test returning the total number of participants.
795 */
796 public function test_user_get_total_participants() {
797 global $DB;
798
799 $this->resetAfterTest();
800
801 // Create a course.
802 $course = self::getDataGenerator()->create_course();
803
804 // Create a teacher.
805 $teacher = self::getDataGenerator()->create_user(['firstname' => 'searchforthis']);
806
807 // Create a bunch of students.
808 $student1 = self::getDataGenerator()->create_user(['firstname' => 'searchforthis']);
809 $student2 = self::getDataGenerator()->create_user(['firstname' => 'searchforthis']);
810 $student3 = self::getDataGenerator()->create_user(['firstname' => 'searchforthis']);
811
812 // Create a group.
813 $group = self::getDataGenerator()->create_group(array('courseid' => $course->id));
814
815 // Enrol the students.
816 self::getDataGenerator()->enrol_user($student1->id, $course->id);
817 self::getDataGenerator()->enrol_user($student2->id, $course->id);
818 self::getDataGenerator()->enrol_user($student3->id, $course->id);
819
820 // Enrol the teacher.
821 $roleids = $DB->get_records_menu('role', null, '', 'shortname, id');
822 self::getDataGenerator()->enrol_user($teacher->id, $course->id, $roleids['editingteacher']);
823
824 // Add the teacher and two of the students to the group.
825 groups_add_member($group->id, $teacher->id);
826 groups_add_member($group->id, $student1->id);
827 groups_add_member($group->id, $student2->id);
828
829 // Set it so the teacher and two of the students have accessed the courses within the last day,
830 // but only one of the students is in the group.
831 $accesssince = time() - DAYSECS;
832 $lastaccess = new stdClass();
833 $lastaccess->userid = $teacher->id;
834 $lastaccess->courseid = $course->id;
835 $lastaccess->timeaccess = time() - DAYSECS;
836 $DB->insert_record('user_lastaccess', $lastaccess);
837
838 $lastaccess->userid = $student1->id;
839 $DB->insert_record('user_lastaccess', $lastaccess);
840
841 $lastaccess->userid = $student3->id;
842 $DB->insert_record('user_lastaccess', $lastaccess);
843
844 // Now, when we perform the following search we should only return 1 user. A student who belongs to
845 // the group and has the name 'searchforthis' and has also accessed the course in the last day.
9651e491
JP
846 $count = user_get_total_participants($course->id, $group->id, $accesssince + 1, $roleids['student'], 0, -1,
847 'searchforthis');
ba6645da
MN
848
849 $this->assertEquals(1, $count);
850 }
851
852 /**
853 * Test returning the number of participants on the front page.
854 */
855 public function test_user_get_total_participants_on_front_page() {
856 $this->resetAfterTest();
857
858 // Set it so that only 3 users have accessed the site within the last day.
859 $accesssince = time() - DAYSECS;
860
861 // Create a bunch of users.
862 $user1 = self::getDataGenerator()->create_user(['firstname' => 'searchforthis', 'lastaccess' => $accesssince]);
863 $user2 = self::getDataGenerator()->create_user(['firstname' => 'searchforthis', 'lastaccess' => $accesssince]);
864 $user3 = self::getDataGenerator()->create_user(['firstname' => 'searchforthis']);
865 $user4 = self::getDataGenerator()->create_user(['firstname' => 'searchforthis', 'lastaccess' => $accesssince]);
866
867 // Create a group.
868 $group = self::getDataGenerator()->create_group(array('courseid' => SITEID));
869
870 // Add 3 of the users to a group.
871 groups_add_member($group->id, $user1->id);
872 groups_add_member($group->id, $user2->id);
873 groups_add_member($group->id, $user3->id);
874
875 // Now, when we perform the following search we should only return 2 users. Users who belong to
876 // the group and have the name 'searchforthis' and have also accessed the site in the last day.
9651e491 877 $count = user_get_total_participants(SITEID, $group->id, $accesssince + 1, 0, 0, -1, 'searchforthis');
ba6645da
MN
878
879 $this->assertEquals(2, $count);
880 }
881
882 /**
883 * Test returning the participants.
884 */
885 public function test_user_get_participants() {
886 global $DB;
887
888 $this->resetAfterTest();
889
890 // Create a course.
891 $course = self::getDataGenerator()->create_course();
892
893 // Create a teacher.
894 $teacher = self::getDataGenerator()->create_user(['firstname' => 'searchforthis']);
895
896 // Create a bunch of students.
897 $student1 = self::getDataGenerator()->create_user(['firstname' => 'searchforthis']);
898 $student2 = self::getDataGenerator()->create_user(['firstname' => 'searchforthis']);
899 $student3 = self::getDataGenerator()->create_user(['firstname' => 'searchforthis']);
900
901 // Create a group.
902 $group = self::getDataGenerator()->create_group(array('courseid' => $course->id));
903
904 // Enrol the students.
905 self::getDataGenerator()->enrol_user($student1->id, $course->id);
906 self::getDataGenerator()->enrol_user($student2->id, $course->id);
907 self::getDataGenerator()->enrol_user($student3->id, $course->id);
908
909 // Enrol the teacher.
910 $roleids = $DB->get_records_menu('role', null, '', 'shortname, id');
911 self::getDataGenerator()->enrol_user($teacher->id, $course->id, $roleids['editingteacher']);
912
913 // Add the teacher and two of the students to the group.
914 groups_add_member($group->id, $teacher->id);
915 groups_add_member($group->id, $student1->id);
916 groups_add_member($group->id, $student2->id);
917
918 // Set it so the teacher and two of the students have accessed the course within the last day, but only one of
919 // the students is in the group.
920 $accesssince = time() - DAYSECS;
921 $lastaccess = new stdClass();
922 $lastaccess->userid = $teacher->id;
923 $lastaccess->courseid = $course->id;
924 $lastaccess->timeaccess = time() - DAYSECS;
925 $DB->insert_record('user_lastaccess', $lastaccess);
926
927 $lastaccess->userid = $student1->id;
928 $DB->insert_record('user_lastaccess', $lastaccess);
929
930 $lastaccess->userid = $student3->id;
931 $DB->insert_record('user_lastaccess', $lastaccess);
932
933 // Now, when we perform the following search we should only return 1 user. A student who belongs to
934 // the group and has the name 'searchforthis' and has also accessed the course in the last day.
9651e491 935 $userset = user_get_participants($course->id, $group->id, $accesssince + 1, $roleids['student'], 0, -1, 'searchforthis');
ba6645da 936
ba6645da 937 $this->assertEquals($student1->id, $userset->current()->id);
78da366b 938 $this->assertEquals(1, iterator_count($userset));
5290d060
SA
939
940 // Search for users without any group.
941 $userset = user_get_participants($course->id, USERSWITHOUTGROUP, 0, $roleids['student'], 0, -1, '');
942
943 $this->assertEquals($student3->id, $userset->current()->id);
944 $this->assertEquals(1, iterator_count($userset));
ba6645da
MN
945 }
946
947 /**
948 * Test returning the participants on the front page.
949 */
950 public function test_user_get_participants_on_front_page() {
951 $this->resetAfterTest();
952
953 // Set it so that only 3 users have accessed the site within the last day.
954 $accesssince = time() - DAYSECS;
955
956 // Create a bunch of users.
957 $user1 = self::getDataGenerator()->create_user(['firstname' => 'searchforthis', 'lastaccess' => $accesssince]);
958 $user2 = self::getDataGenerator()->create_user(['firstname' => 'searchforthis', 'lastaccess' => $accesssince]);
959 $user3 = self::getDataGenerator()->create_user(['firstname' => 'searchforthis']);
960 $user4 = self::getDataGenerator()->create_user(['firstname' => 'searchforthis', 'lastaccess' => $accesssince]);
961
962 // Create a group.
963 $group = self::getDataGenerator()->create_group(array('courseid' => SITEID));
964
965 // Add 3 of the users to a group.
966 groups_add_member($group->id, $user1->id);
967 groups_add_member($group->id, $user2->id);
968 groups_add_member($group->id, $user3->id);
969
970 // Now, when we perform the following search we should only return 2 users. Users who belong to
971 // the group and have the name 'searchforthis' and have also accessed the site in the last day.
9651e491
JP
972 $userset = user_get_participants(SITEID, $group->id, $accesssince + 1, 0, 0, -1, 'searchforthis', '', array(),
973 'ORDER BY id ASC');
ba6645da
MN
974
975 $this->assertEquals($user1->id, $userset->current()->id);
976 $userset->next();
977 $this->assertEquals($user2->id, $userset->current()->id);
978 }
bb78e249 979}