Merge branch 'MDL-76073-master' of https://github.com/marinaglancy/moodle
[moodle.git] / user / tests / externallib_test.php
blob95812a41baba0e40bd8d130b9e2667ed304ad27d
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.
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/>.
17 /**
18 * User external PHPunit tests
20 * @package core_user
21 * @category external
22 * @copyright 2012 Jerome Mouneyrac
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 * @since Moodle 2.4
27 namespace core_user;
29 use core_files_external;
30 use core_user_external;
31 use externallib_advanced_testcase;
33 defined('MOODLE_INTERNAL') || die();
35 global $CFG;
37 require_once($CFG->dirroot . '/webservice/tests/helpers.php');
38 require_once($CFG->dirroot . '/user/externallib.php');
39 require_once($CFG->dirroot . '/files/externallib.php');
41 class externallib_test extends externallib_advanced_testcase {
43 /**
44 * Test get_users
46 public function test_get_users() {
47 global $USER, $CFG;
49 $this->resetAfterTest(true);
51 $course = self::getDataGenerator()->create_course();
53 $user1 = array(
54 'username' => 'usernametest1',
55 'idnumber' => 'idnumbertest1',
56 'firstname' => 'First Name User Test 1',
57 'lastname' => 'Last Name User Test 1',
58 'email' => 'usertest1@example.com',
59 'address' => '2 Test Street Perth 6000 WA',
60 'phone1' => '01010101010',
61 'phone2' => '02020203',
62 'department' => 'Department of user 1',
63 'institution' => 'Institution of user 1',
64 'description' => 'This is a description for user 1',
65 'descriptionformat' => FORMAT_MOODLE,
66 'city' => 'Perth',
67 'country' => 'AU'
70 $user1 = self::getDataGenerator()->create_user($user1);
71 set_config('usetags', 1);
72 require_once($CFG->dirroot . '/user/editlib.php');
73 $user1->interests = array('Cinema', 'Tennis', 'Dance', 'Guitar', 'Cooking');
74 useredit_update_interests($user1, $user1->interests);
76 $user2 = self::getDataGenerator()->create_user(
77 array('username' => 'usernametest2', 'idnumber' => 'idnumbertest2'));
79 $generatedusers = array();
80 $generatedusers[$user1->id] = $user1;
81 $generatedusers[$user2->id] = $user2;
83 $context = \context_course::instance($course->id);
84 $roleid = $this->assignUserCapability('moodle/user:viewdetails', $context->id);
86 // Enrol the users in the course.
87 $this->getDataGenerator()->enrol_user($user1->id, $course->id, $roleid);
88 $this->getDataGenerator()->enrol_user($user2->id, $course->id, $roleid);
89 $this->getDataGenerator()->enrol_user($USER->id, $course->id, $roleid);
91 // call as admin and receive all possible fields.
92 $this->setAdminUser();
94 $searchparams = array(
95 array('key' => 'invalidkey', 'value' => 'invalidkey'),
96 array('key' => 'email', 'value' => $user1->email),
97 array('key' => 'firstname', 'value' => $user1->firstname));
99 // Call the external function.
100 $result = core_user_external::get_users($searchparams);
102 // We need to execute the return values cleaning process to simulate the web service server
103 $result = \external_api::clean_returnvalue(core_user_external::get_users_returns(), $result);
105 // Check we retrieve the good total number of enrolled users + no error on capability.
106 $expectedreturnedusers = 1;
107 $returnedusers = $result['users'];
108 $this->assertEquals($expectedreturnedusers, count($returnedusers));
110 foreach($returnedusers as $returneduser) {
111 $generateduser = ($returneduser['id'] == $USER->id) ?
112 $USER : $generatedusers[$returneduser['id']];
113 $this->assertEquals($generateduser->username, $returneduser['username']);
114 if (!empty($generateduser->idnumber)) {
115 $this->assertEquals($generateduser->idnumber, $returneduser['idnumber']);
117 $this->assertEquals($generateduser->firstname, $returneduser['firstname']);
118 $this->assertEquals($generateduser->lastname, $returneduser['lastname']);
119 if ($generateduser->email != $USER->email) { // Don't check the tmp modified $USER email.
120 $this->assertEquals($generateduser->email, $returneduser['email']);
122 if (!empty($generateduser->address)) {
123 $this->assertEquals($generateduser->address, $returneduser['address']);
125 if (!empty($generateduser->phone1)) {
126 $this->assertEquals($generateduser->phone1, $returneduser['phone1']);
128 if (!empty($generateduser->phone2)) {
129 $this->assertEquals($generateduser->phone2, $returneduser['phone2']);
131 if (!empty($generateduser->department)) {
132 $this->assertEquals($generateduser->department, $returneduser['department']);
134 if (!empty($generateduser->institution)) {
135 $this->assertEquals($generateduser->institution, $returneduser['institution']);
137 if (!empty($generateduser->description)) {
138 $this->assertEquals($generateduser->description, $returneduser['description']);
140 if (!empty($generateduser->descriptionformat)) {
141 $this->assertEquals(FORMAT_HTML, $returneduser['descriptionformat']);
143 if (!empty($generateduser->city)) {
144 $this->assertEquals($generateduser->city, $returneduser['city']);
146 if (!empty($generateduser->country)) {
147 $this->assertEquals($generateduser->country, $returneduser['country']);
149 if (!empty($CFG->usetags) and !empty($generateduser->interests)) {
150 $this->assertEquals(implode(', ', $generateduser->interests), $returneduser['interests']);
154 // Test the invalid key warning.
155 $warnings = $result['warnings'];
156 $this->assertEquals(count($warnings), 1);
157 $warning = array_pop($warnings);
158 $this->assertEquals($warning['item'], 'invalidkey');
159 $this->assertEquals($warning['warningcode'], 'invalidfieldparameter');
161 // Test sending twice the same search field.
162 try {
163 $searchparams = array(
164 array('key' => 'firstname', 'value' => 'Canard'),
165 array('key' => 'email', 'value' => $user1->email),
166 array('key' => 'firstname', 'value' => $user1->firstname));
168 // Call the external function.
169 $result = core_user_external::get_users($searchparams);
170 $this->fail('Expecting \'keyalreadyset\' moodle_exception to be thrown.');
171 } catch (\moodle_exception $e) {
172 $this->assertEquals('keyalreadyset', $e->errorcode);
173 } catch (\Exception $e) {
174 $this->fail('Expecting \'keyalreadyset\' moodle_exception to be thrown.');
179 * Test get_users_by_field
181 public function test_get_users_by_field() {
182 global $USER, $CFG;
184 $this->resetAfterTest(true);
186 $course = self::getDataGenerator()->create_course();
187 $user1 = array(
188 'username' => 'usernametest1',
189 'idnumber' => 'idnumbertest1',
190 'firstname' => 'First Name User Test 1',
191 'lastname' => 'Last Name User Test 1',
192 'email' => 'usertest1@example.com',
193 'address' => '2 Test Street Perth 6000 WA',
194 'phone1' => '01010101010',
195 'phone2' => '02020203',
196 'department' => 'Department of user 1',
197 'institution' => 'Institution of user 1',
198 'description' => 'This is a description for user 1',
199 'descriptionformat' => FORMAT_MOODLE,
200 'city' => 'Perth',
201 'country' => 'AU',
203 $user1 = self::getDataGenerator()->create_user($user1);
204 if (!empty($CFG->usetags)) {
205 require_once($CFG->dirroot . '/user/editlib.php');
206 $user1->interests = array('Cinema', 'Tennis', 'Dance', 'Guitar', 'Cooking');
207 useredit_update_interests($user1, $user1->interests);
209 $user2 = self::getDataGenerator()->create_user(
210 array('username' => 'usernametest2', 'idnumber' => 'idnumbertest2'));
212 $generatedusers = array();
213 $generatedusers[$user1->id] = $user1;
214 $generatedusers[$user2->id] = $user2;
216 $context = \context_course::instance($course->id);
217 $roleid = $this->assignUserCapability('moodle/user:viewdetails', $context->id);
219 // Enrol the users in the course.
220 $this->getDataGenerator()->enrol_user($user1->id, $course->id, $roleid, 'manual');
221 $this->getDataGenerator()->enrol_user($user2->id, $course->id, $roleid, 'manual');
222 $this->getDataGenerator()->enrol_user($USER->id, $course->id, $roleid, 'manual');
224 // call as admin and receive all possible fields.
225 $this->setAdminUser();
227 $fieldstosearch = array('id', 'idnumber', 'username', 'email');
229 foreach ($fieldstosearch as $fieldtosearch) {
231 // Call the external function.
232 $returnedusers = core_user_external::get_users_by_field($fieldtosearch,
233 array($USER->{$fieldtosearch}, $user1->{$fieldtosearch}, $user2->{$fieldtosearch}));
234 $returnedusers = \external_api::clean_returnvalue(core_user_external::get_users_by_field_returns(), $returnedusers);
236 // Expected result differ following the searched field
237 // Admin user in the PHPunit framework doesn't have an idnumber.
238 if ($fieldtosearch == 'idnumber') {
239 $expectedreturnedusers = 2;
240 } else {
241 $expectedreturnedusers = 3;
244 // Check we retrieve the good total number of enrolled users + no error on capability.
245 $this->assertEquals($expectedreturnedusers, count($returnedusers));
247 foreach($returnedusers as $returneduser) {
248 $generateduser = ($returneduser['id'] == $USER->id) ?
249 $USER : $generatedusers[$returneduser['id']];
250 $this->assertEquals($generateduser->username, $returneduser['username']);
251 if (!empty($generateduser->idnumber)) {
252 $this->assertEquals($generateduser->idnumber, $returneduser['idnumber']);
254 $this->assertEquals($generateduser->firstname, $returneduser['firstname']);
255 $this->assertEquals($generateduser->lastname, $returneduser['lastname']);
256 if ($generateduser->email != $USER->email) { //don't check the tmp modified $USER email
257 $this->assertEquals($generateduser->email, $returneduser['email']);
259 if (!empty($generateduser->address)) {
260 $this->assertEquals($generateduser->address, $returneduser['address']);
262 if (!empty($generateduser->phone1)) {
263 $this->assertEquals($generateduser->phone1, $returneduser['phone1']);
265 if (!empty($generateduser->phone2)) {
266 $this->assertEquals($generateduser->phone2, $returneduser['phone2']);
268 if (!empty($generateduser->department)) {
269 $this->assertEquals($generateduser->department, $returneduser['department']);
271 if (!empty($generateduser->institution)) {
272 $this->assertEquals($generateduser->institution, $returneduser['institution']);
274 if (!empty($generateduser->description)) {
275 $this->assertEquals($generateduser->description, $returneduser['description']);
277 if (!empty($generateduser->descriptionformat) and isset($returneduser['descriptionformat'])) {
278 $this->assertEquals($generateduser->descriptionformat, $returneduser['descriptionformat']);
280 if (!empty($generateduser->city)) {
281 $this->assertEquals($generateduser->city, $returneduser['city']);
283 if (!empty($generateduser->country)) {
284 $this->assertEquals($generateduser->country, $returneduser['country']);
286 if (!empty($CFG->usetags) and !empty($generateduser->interests)) {
287 $this->assertEquals(implode(', ', $generateduser->interests), $returneduser['interests']);
289 // Default language and no theme were used for the user.
290 $this->assertEquals($CFG->lang, $returneduser['lang']);
291 $this->assertEmpty($returneduser['theme']);
295 // Test that no result are returned for search by username if we are not admin
296 $this->setGuestUser();
298 // Call the external function.
299 $returnedusers = core_user_external::get_users_by_field('username',
300 array($USER->username, $user1->username, $user2->username));
301 $returnedusers = \external_api::clean_returnvalue(core_user_external::get_users_by_field_returns(), $returnedusers);
303 // Only the own $USER username should be returned
304 $this->assertEquals(1, count($returnedusers));
306 // And finally test as one of the enrolled users.
307 $this->setUser($user1);
309 // Call the external function.
310 $returnedusers = core_user_external::get_users_by_field('username',
311 array($USER->username, $user1->username, $user2->username));
312 $returnedusers = \external_api::clean_returnvalue(core_user_external::get_users_by_field_returns(), $returnedusers);
314 // Only the own $USER username should be returned still.
315 $this->assertEquals(1, count($returnedusers));
318 public function get_course_user_profiles_setup($capability) {
319 global $USER, $CFG;
321 $this->resetAfterTest(true);
323 $return = new \stdClass();
325 // Create the course and fetch its context.
326 $return->course = self::getDataGenerator()->create_course();
327 $return->user1 = array(
328 'username' => 'usernametest1',
329 'idnumber' => 'idnumbertest1',
330 'firstname' => 'First Name User Test 1',
331 'lastname' => 'Last Name User Test 1',
332 'email' => 'usertest1@example.com',
333 'address' => '2 Test Street Perth 6000 WA',
334 'phone1' => '01010101010',
335 'phone2' => '02020203',
336 'department' => 'Department of user 1',
337 'institution' => 'Institution of user 1',
338 'description' => 'This is a description for user 1',
339 'descriptionformat' => FORMAT_MOODLE,
340 'city' => 'Perth',
341 'country' => 'AU'
343 $return->user1 = self::getDataGenerator()->create_user($return->user1);
344 if (!empty($CFG->usetags)) {
345 require_once($CFG->dirroot . '/user/editlib.php');
346 $return->user1->interests = array('Cinema', 'Tennis', 'Dance', 'Guitar', 'Cooking');
347 useredit_update_interests($return->user1, $return->user1->interests);
349 $return->user2 = self::getDataGenerator()->create_user();
351 $context = \context_course::instance($return->course->id);
352 $return->roleid = $this->assignUserCapability($capability, $context->id);
354 // Enrol the users in the course.
355 $this->getDataGenerator()->enrol_user($return->user1->id, $return->course->id, $return->roleid, 'manual');
356 $this->getDataGenerator()->enrol_user($return->user2->id, $return->course->id, $return->roleid, 'manual');
357 $this->getDataGenerator()->enrol_user($USER->id, $return->course->id, $return->roleid, 'manual');
359 return $return;
363 * Test get_course_user_profiles
365 public function test_get_course_user_profiles() {
366 global $USER, $CFG;
368 $this->resetAfterTest(true);
370 $data = $this->get_course_user_profiles_setup('moodle/user:viewdetails');
372 // Call the external function.
373 $enrolledusers = core_user_external::get_course_user_profiles(array(
374 array('userid' => $USER->id, 'courseid' => $data->course->id)));
376 // We need to execute the return values cleaning process to simulate the web service server.
377 $enrolledusers = \external_api::clean_returnvalue(core_user_external::get_course_user_profiles_returns(), $enrolledusers);
379 // Check we retrieve the good total number of enrolled users + no error on capability.
380 $this->assertEquals(1, count($enrolledusers));
383 public function test_get_user_course_profile_as_admin() {
384 global $USER, $CFG;
386 global $USER, $CFG;
388 $this->resetAfterTest(true);
390 $data = $this->get_course_user_profiles_setup('moodle/user:viewdetails');
392 // Do the same call as admin to receive all possible fields.
393 $this->setAdminUser();
394 $USER->email = "admin@example.com";
396 // Call the external function.
397 $enrolledusers = core_user_external::get_course_user_profiles(array(
398 array('userid' => $data->user1->id, 'courseid' => $data->course->id)));
400 // We need to execute the return values cleaning process to simulate the web service server.
401 $enrolledusers = \external_api::clean_returnvalue(core_user_external::get_course_user_profiles_returns(), $enrolledusers);
403 foreach($enrolledusers as $enrolleduser) {
404 if ($enrolleduser['username'] == $data->user1->username) {
405 $this->assertEquals($data->user1->idnumber, $enrolleduser['idnumber']);
406 $this->assertEquals($data->user1->firstname, $enrolleduser['firstname']);
407 $this->assertEquals($data->user1->lastname, $enrolleduser['lastname']);
408 $this->assertEquals($data->user1->email, $enrolleduser['email']);
409 $this->assertEquals($data->user1->address, $enrolleduser['address']);
410 $this->assertEquals($data->user1->phone1, $enrolleduser['phone1']);
411 $this->assertEquals($data->user1->phone2, $enrolleduser['phone2']);
412 $this->assertEquals($data->user1->department, $enrolleduser['department']);
413 $this->assertEquals($data->user1->institution, $enrolleduser['institution']);
414 $this->assertEquals($data->user1->description, $enrolleduser['description']);
415 $this->assertEquals(FORMAT_HTML, $enrolleduser['descriptionformat']);
416 $this->assertEquals($data->user1->city, $enrolleduser['city']);
417 $this->assertEquals($data->user1->country, $enrolleduser['country']);
418 if (!empty($CFG->usetags)) {
419 $this->assertEquals(implode(', ', $data->user1->interests), $enrolleduser['interests']);
426 * Test create_users
428 public function test_create_users() {
429 global $DB;
431 $this->resetAfterTest(true);
433 $user1 = array(
434 'username' => 'usernametest1',
435 'password' => 'Moodle2012!',
436 'idnumber' => 'idnumbertest1',
437 'firstname' => 'First Name User Test 1',
438 'lastname' => 'Last Name User Test 1',
439 'middlename' => 'Middle Name User Test 1',
440 'lastnamephonetic' => '最後のお名前のテスト一号',
441 'firstnamephonetic' => 'お名前のテスト一号',
442 'alternatename' => 'Alternate Name User Test 1',
443 'email' => 'usertest1@example.com',
444 'description' => 'This is a description for user 1',
445 'city' => 'Perth',
446 'country' => 'AU',
447 'preferences' => [[
448 'type' => 'htmleditor',
449 'value' => 'atto'
450 ], [
451 'type' => 'invalidpreference',
452 'value' => 'abcd'
455 'department' => 'College of Science',
456 'institution' => 'National Institute of Physics',
457 'phone1' => '01 2345 6789',
458 'maildisplay' => 1,
459 'interests' => 'badminton, basketball, cooking, '
462 // User with an authentication method done externally.
463 $user2 = array(
464 'username' => 'usernametest2',
465 'firstname' => 'First Name User Test 2',
466 'lastname' => 'Last Name User Test 2',
467 'email' => 'usertest2@example.com',
468 'auth' => 'oauth2'
471 $context = \context_system::instance();
472 $roleid = $this->assignUserCapability('moodle/user:create', $context->id);
473 $this->assignUserCapability('moodle/user:editprofile', $context->id, $roleid);
475 // Call the external function.
476 $createdusers = core_user_external::create_users(array($user1, $user2));
478 // We need to execute the return values cleaning process to simulate the web service server.
479 $createdusers = \external_api::clean_returnvalue(core_user_external::create_users_returns(), $createdusers);
481 // Check we retrieve the good total number of created users + no error on capability.
482 $this->assertCount(2, $createdusers);
484 foreach($createdusers as $createduser) {
485 $dbuser = $DB->get_record('user', array('id' => $createduser['id']));
487 if ($createduser['username'] === $user1['username']) {
488 $usertotest = $user1;
489 $this->assertEquals('atto', get_user_preferences('htmleditor', null, $dbuser));
490 $this->assertEquals(null, get_user_preferences('invalidpreference', null, $dbuser));
491 // Confirm user interests have been saved.
492 $interests = \core_tag_tag::get_item_tags_array('core', 'user', $createduser['id'],
493 \core_tag_tag::BOTH_STANDARD_AND_NOT, 0, false);
494 // There should be 3 user interests.
495 $this->assertCount(3, $interests);
497 } else if ($createduser['username'] === $user2['username']) {
498 $usertotest = $user2;
501 foreach ($dbuser as $property => $value) {
502 if ($property === 'password') {
503 if ($usertotest === $user2) {
504 // External auth mechanisms don't store password in the user table.
505 $this->assertEquals(AUTH_PASSWORD_NOT_CACHED, $value);
506 } else {
507 // Skip hashed passwords.
508 continue;
511 // Confirm that the values match.
512 if (isset($usertotest[$property])) {
513 $this->assertEquals($usertotest[$property], $value);
518 // Call without required capability
519 $this->unassignUserCapability('moodle/user:create', $context->id, $roleid);
520 $this->expectException('required_capability_exception');
521 core_user_external::create_users(array($user1));
525 * Test create_users with password and createpassword parameter not set.
527 public function test_create_users_empty_password() {
528 $this->resetAfterTest();
529 $this->setAdminUser();
531 $user = [
532 'username' => 'usernametest1',
533 'firstname' => 'First Name User Test 1',
534 'lastname' => 'Last Name User Test 1',
535 'email' => 'usertest1@example.com',
538 // This should throw an exception because either password or createpassword param must be passed for auth_manual.
539 $this->expectException(\invalid_parameter_exception::class);
540 core_user_external::create_users([$user]);
544 * Data provider for \core_user_externallib_testcase::test_create_users_with_same_emails().
546 public function create_users_provider_with_same_emails() {
547 return [
548 'Same emails allowed, same case' => [
549 1, false
551 'Same emails allowed, different case' => [
552 1, true
554 'Same emails disallowed, same case' => [
555 0, false
557 'Same emails disallowed, different case' => [
558 0, true
564 * Test for \core_user_external::create_users() when user using the same email addresses are being created.
566 * @dataProvider create_users_provider_with_same_emails
567 * @param int $sameemailallowed The value to set for $CFG->allowaccountssameemail.
568 * @param boolean $differentcase Whether to user a different case for the other user.
570 public function test_create_users_with_same_emails($sameemailallowed, $differentcase) {
571 global $DB;
573 $this->resetAfterTest();
574 $this->setAdminUser();
576 // Allow multiple users with the same email address.
577 set_config('allowaccountssameemail', $sameemailallowed);
578 $users = [
580 'username' => 's1',
581 'firstname' => 'Johnny',
582 'lastname' => 'Bravo',
583 'email' => 's1@example.com',
584 'password' => 'Passw0rd!'
587 'username' => 's2',
588 'firstname' => 'John',
589 'lastname' => 'Doe',
590 'email' => $differentcase ? 'S1@EXAMPLE.COM' : 's1@example.com',
591 'password' => 'Passw0rd!'
595 if (!$sameemailallowed) {
596 // This should throw an exception when $CFG->allowaccountssameemail is empty.
597 $this->expectException(\invalid_parameter_exception::class);
600 // Create our users.
601 core_user_external::create_users($users);
603 // Confirm that the users have been created.
604 list($insql, $params) = $DB->get_in_or_equal(['s1', 's2']);
605 $this->assertEquals(2, $DB->count_records_select('user', 'username ' . $insql, $params));
609 * Test create_users with invalid parameters
611 * @dataProvider data_create_users_invalid_parameter
612 * @param array $data User data to attempt to register.
613 * @param string $expectmessage Expected exception message.
615 public function test_create_users_invalid_parameter(array $data, $expectmessage) {
616 global $USER, $CFG, $DB;
618 $this->resetAfterTest(true);
619 $this->assignUserCapability('moodle/user:create', SYSCONTEXTID);
621 $this->expectException('invalid_parameter_exception');
622 $this->expectExceptionMessage($expectmessage);
624 core_user_external::create_users(array($data));
628 * Data provider for {@see self::test_create_users_invalid_parameter()}.
630 * @return array
632 public function data_create_users_invalid_parameter() {
633 return [
634 'blank_username' => [
635 'data' => [
636 'username' => '',
637 'firstname' => 'Foo',
638 'lastname' => 'Bar',
639 'email' => 'foobar@example.com',
640 'createpassword' => 1,
642 'expectmessage' => 'The field username cannot be blank',
644 'blank_firtname' => [
645 'data' => [
646 'username' => 'foobar',
647 'firstname' => "\t \n",
648 'lastname' => 'Bar',
649 'email' => 'foobar@example.com',
650 'createpassword' => 1,
652 'expectmessage' => 'The field firstname cannot be blank',
654 'blank_lastname' => [
655 'data' => [
656 'username' => 'foobar',
657 'firstname' => '0',
658 'lastname' => ' ',
659 'email' => 'foobar@example.com',
660 'createpassword' => 1,
662 'expectmessage' => 'The field lastname cannot be blank',
664 'invalid_email' => [
665 'data' => [
666 'username' => 'foobar',
667 'firstname' => 'Foo',
668 'lastname' => 'Bar',
669 'email' => '@foobar',
670 'createpassword' => 1,
672 'expectmessage' => 'Email address is invalid',
674 'missing_password' => [
675 'data' => [
676 'username' => 'foobar',
677 'firstname' => 'Foo',
678 'lastname' => 'Bar',
679 'email' => 'foobar@example.com',
681 'expectmessage' => 'Invalid password: you must provide a password, or set createpassword',
687 * Test delete_users
689 public function test_delete_users() {
690 global $USER, $CFG, $DB;
692 $this->resetAfterTest(true);
694 $user1 = self::getDataGenerator()->create_user();
695 $user2 = self::getDataGenerator()->create_user();
697 // Check the users were correctly created.
698 $this->assertEquals(2, $DB->count_records_select('user', 'deleted = 0 AND (id = :userid1 OR id = :userid2)',
699 array('userid1' => $user1->id, 'userid2' => $user2->id)));
701 $context = \context_system::instance();
702 $roleid = $this->assignUserCapability('moodle/user:delete', $context->id);
704 // Call the external function.
705 core_user_external::delete_users(array($user1->id, $user2->id));
707 // Check we retrieve no users + no error on capability.
708 $this->assertEquals(0, $DB->count_records_select('user', 'deleted = 0 AND (id = :userid1 OR id = :userid2)',
709 array('userid1' => $user1->id, 'userid2' => $user2->id)));
711 // Call without required capability.
712 $this->unassignUserCapability('moodle/user:delete', $context->id, $roleid);
713 $this->expectException('required_capability_exception');
714 core_user_external::delete_users(array($user1->id, $user2->id));
718 * Test update_users
720 public function test_update_users() {
721 global $USER, $CFG, $DB;
723 $this->resetAfterTest(true);
724 $this->preventResetByRollback();
726 $wsuser = self::getDataGenerator()->create_user();
727 self::setUser($wsuser);
729 $context = \context_user::instance($USER->id);
730 $contextid = $context->id;
731 $filename = "reddot.png";
732 $filecontent = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38"
733 . "GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
735 // Call the files api to create a file.
736 $draftfile = core_files_external::upload($contextid, 'user', 'draft', 0, '/',
737 $filename, $filecontent, null, null);
738 $draftfile = \external_api::clean_returnvalue(core_files_external::upload_returns(), $draftfile);
740 $draftid = $draftfile['itemid'];
742 $user1 = self::getDataGenerator()->create_user();
744 $user1 = array(
745 'id' => $user1->id,
746 'username' => 'usernametest1',
747 'password' => 'Moodle2012!',
748 'idnumber' => 'idnumbertest1',
749 'firstname' => 'First Name User Test 1',
750 'lastname' => 'Last Name User Test 1',
751 'middlename' => 'Middle Name User Test 1',
752 'lastnamephonetic' => '最後のお名前のテスト一号',
753 'firstnamephonetic' => 'お名前のテスト一号',
754 'alternatename' => 'Alternate Name User Test 1',
755 'email' => 'usertest1@example.com',
756 'description' => 'This is a description for user 1',
757 'city' => 'Perth',
758 'userpicture' => $draftid,
759 'country' => 'AU',
760 'preferences' => [[
761 'type' => 'htmleditor',
762 'value' => 'atto'
763 ], [
764 'type' => 'invialidpreference',
765 'value' => 'abcd'
768 'department' => 'College of Science',
769 'institution' => 'National Institute of Physics',
770 'phone1' => '01 2345 6789',
771 'maildisplay' => 1,
772 'interests' => 'badminton, basketball, cooking, '
775 $context = \context_system::instance();
776 $roleid = $this->assignUserCapability('moodle/user:update', $context->id);
777 $this->assignUserCapability('moodle/user:editprofile', $context->id, $roleid);
779 // Check we can't update deleted users, guest users, site admin.
780 $user2 = $user3 = $user4 = $user1;
781 $user2['id'] = $CFG->siteguest;
783 $siteadmins = explode(',', $CFG->siteadmins);
784 $user3['id'] = array_shift($siteadmins);
786 $userdeleted = self::getDataGenerator()->create_user();
787 $user4['id'] = $userdeleted->id;
788 user_delete_user($userdeleted);
790 $user5 = self::getDataGenerator()->create_user();
791 $user5 = array('id' => $user5->id, 'email' => $user5->email);
793 // Call the external function.
794 $returnvalue = core_user_external::update_users(array($user1, $user2, $user3, $user4));
795 $returnvalue = \external_api::clean_returnvalue(core_user_external::update_users_returns(), $returnvalue);
797 // Check warnings.
798 $this->assertEquals($user2['id'], $returnvalue['warnings'][0]['itemid']); // Guest user.
799 $this->assertEquals('usernotupdatedguest', $returnvalue['warnings'][0]['warningcode']);
800 $this->assertEquals($user3['id'], $returnvalue['warnings'][1]['itemid']); // Admin user.
801 $this->assertEquals('usernotupdatedadmin', $returnvalue['warnings'][1]['warningcode']);
802 $this->assertEquals($user4['id'], $returnvalue['warnings'][2]['itemid']); // Deleted user.
803 $this->assertEquals('usernotupdateddeleted', $returnvalue['warnings'][2]['warningcode']);
805 $dbuser2 = $DB->get_record('user', array('id' => $user2['id']));
806 $this->assertNotEquals($dbuser2->username, $user2['username']);
807 $dbuser3 = $DB->get_record('user', array('id' => $user3['id']));
808 $this->assertNotEquals($dbuser3->username, $user3['username']);
809 $dbuser4 = $DB->get_record('user', array('id' => $user4['id']));
810 $this->assertNotEquals($dbuser4->username, $user4['username']);
812 $dbuser = $DB->get_record('user', array('id' => $user1['id']));
813 $this->assertEquals($dbuser->username, $user1['username']);
814 $this->assertEquals($dbuser->idnumber, $user1['idnumber']);
815 $this->assertEquals($dbuser->firstname, $user1['firstname']);
816 $this->assertEquals($dbuser->lastname, $user1['lastname']);
817 $this->assertEquals($dbuser->email, $user1['email']);
818 $this->assertEquals($dbuser->description, $user1['description']);
819 $this->assertEquals($dbuser->city, $user1['city']);
820 $this->assertEquals($dbuser->country, $user1['country']);
821 $this->assertNotEquals(0, $dbuser->picture, 'Picture must be set to the new icon itemid for this user');
822 $this->assertEquals($dbuser->department, $user1['department']);
823 $this->assertEquals($dbuser->institution, $user1['institution']);
824 $this->assertEquals($dbuser->phone1, $user1['phone1']);
825 $this->assertEquals($dbuser->maildisplay, $user1['maildisplay']);
826 $this->assertEquals('atto', get_user_preferences('htmleditor', null, $dbuser));
827 $this->assertEquals(null, get_user_preferences('invalidpreference', null, $dbuser));
829 // Confirm user interests have been saved.
830 $interests = \core_tag_tag::get_item_tags_array('core', 'user', $user1['id'], \core_tag_tag::BOTH_STANDARD_AND_NOT, 0, false);
831 // There should be 3 user interests.
832 $this->assertCount(3, $interests);
834 // Confirm no picture change when parameter is not supplied.
835 unset($user1['userpicture']);
836 core_user_external::update_users(array($user1));
837 $dbusernopic = $DB->get_record('user', array('id' => $user1['id']));
838 $this->assertEquals($dbuser->picture, $dbusernopic->picture, 'Picture not change without the parameter.');
840 // Confirm delete of picture deletes the picture from the user record.
841 $user1['userpicture'] = 0;
842 core_user_external::update_users(array($user1));
843 $dbuserdelpic = $DB->get_record('user', array('id' => $user1['id']));
844 $this->assertEquals(0, $dbuserdelpic->picture, 'Picture must be deleted when sent as 0.');
846 // Updating user with an invalid email.
847 $user5['email'] = 'bogus';
848 $returnvalue = core_user_external::update_users(array($user5));
849 $returnvalue = \external_api::clean_returnvalue(core_user_external::update_users_returns(), $returnvalue);
850 $this->assertEquals('useremailinvalid', $returnvalue['warnings'][0]['warningcode']);
851 $this->assertStringContainsString('Invalid email address',
852 $returnvalue['warnings'][0]['message']);
854 // Updating user with a duplicate email.
855 $user5['email'] = $user1['email'];
856 $returnvalue = core_user_external::update_users(array($user1, $user5));
857 $returnvalue = \external_api::clean_returnvalue(core_user_external::update_users_returns(), $returnvalue);
858 $this->assertEquals('useremailduplicate', $returnvalue['warnings'][0]['warningcode']);
859 $this->assertStringContainsString('Duplicate email address',
860 $returnvalue['warnings'][0]['message']);
862 // Updating a user that does not exist.
863 $user5['id'] = -1;
864 $returnvalue = core_user_external::update_users(array($user5));
865 $returnvalue = \external_api::clean_returnvalue(core_user_external::update_users_returns(), $returnvalue);
866 $this->assertEquals('invaliduserid', $returnvalue['warnings'][0]['warningcode']);
867 $this->assertStringContainsString('Invalid user ID',
868 $returnvalue['warnings'][0]['message']);
870 // Updating a remote user.
871 $user1['mnethostid'] = 5;
872 user_update_user($user1); // Update user not using webservice.
873 unset($user1['mnethostid']); // The mnet host ID field is not in the allowed field list for the webservice.
874 $returnvalue = core_user_external::update_users(array($user1));
875 $returnvalue = \external_api::clean_returnvalue(core_user_external::update_users_returns(), $returnvalue);
876 $this->assertEquals('usernotupdatedremote', $returnvalue['warnings'][0]['warningcode']);
877 $this->assertStringContainsString('User is a remote user',
878 $returnvalue['warnings'][0]['message']);
880 // Call without required capability.
881 $this->unassignUserCapability('moodle/user:update', $context->id, $roleid);
882 $this->expectException('required_capability_exception');
883 core_user_external::update_users(array($user1));
887 * Data provider for testing \core_user_external::update_users() for users with same emails
889 * @return array
891 public function users_with_same_emails() {
892 return [
893 'Same emails not allowed: Update name using exactly the same email' => [
894 0, 'John', 's1@example.com', 'Johnny', 's1@example.com', false, true
896 'Same emails not allowed: Update using someone else\'s email' => [
897 0, 'John', 's1@example.com', 'Johnny', 's2@example.com', true, false
899 'Same emails allowed: Update using someone else\'s email' => [
900 1, 'John', 's1@example.com', 'Johnny', 's2@example.com', true, true
902 'Same emails not allowed: Update using same email but with different case' => [
903 0, 'John', 's1@example.com', 'Johnny', 'S1@EXAMPLE.COM', false, true
905 'Same emails not allowed: Update using another user\'s email similar to user but with different case' => [
906 0, 'John', 's1@example.com', 'Johnny', 'S1@EXAMPLE.COM', true, false
908 'Same emails allowed: Update using another user\'s email similar to user but with different case' => [
909 1, 'John', 's1@example.com', 'Johnny', 'S1@EXAMPLE.COM', true, true
915 * Test update_users using similar emails with varying cases.
917 * @dataProvider users_with_same_emails
918 * @param boolean $allowsameemail The value to set for $CFG->allowaccountssameemail.
919 * @param string $currentname The user's current name.
920 * @param string $currentemail The user's current email.
921 * @param string $newname The user's new name.
922 * @param string $newemail The user's new email.
923 * @param boolean $withanotheruser Whether to create another user that has the same email as the target user's new email.
924 * @param boolean $successexpected Whether we expect that the target user's email/name will be updated.
926 public function test_update_users_emails_with_different_cases($allowsameemail, $currentname, $currentemail,
927 $newname, $newemail, $withanotheruser, $successexpected) {
928 global $DB;
930 $this->resetAfterTest();
931 $this->setAdminUser();
933 // Set the value for $CFG->allowaccountssameemail.
934 set_config('allowaccountssameemail', $allowsameemail);
936 $generator = self::getDataGenerator();
938 // Create the user that we wish to update.
939 $usertoupdate = $generator->create_user(['email' => $currentemail, 'firstname' => $currentname]);
941 if ($withanotheruser) {
942 // Create another user that has the same email as the new email that we'd like to update for our target user.
943 $generator->create_user(['email' => $newemail]);
946 // Build the user update parameters.
947 $updateparams = [
948 'id' => $usertoupdate->id,
949 'email' => $newemail,
950 'firstname' => $newname
952 // Let's try to update the user's information.
953 core_user_external::update_users([$updateparams]);
955 // Fetch the updated user record.
956 $userrecord = $DB->get_record('user', ['id' => $usertoupdate->id], 'id, email, firstname');
958 // If we expect the update to succeed, then the email/name would have been changed.
959 if ($successexpected) {
960 $expectedemail = $newemail;
961 $expectedname = $newname;
962 } else {
963 $expectedemail = $currentemail;
964 $expectedname = $currentname;
966 // Confirm that our expectations are met.
967 $this->assertEquals($expectedemail, $userrecord->email);
968 $this->assertEquals($expectedname, $userrecord->firstname);
972 * Test add_user_private_files
974 public function test_add_user_private_files() {
975 global $USER, $CFG, $DB;
977 $this->resetAfterTest(true);
979 $context = \context_system::instance();
980 $roleid = $this->assignUserCapability('moodle/user:manageownfiles', $context->id);
982 $context = \context_user::instance($USER->id);
983 $contextid = $context->id;
984 $component = "user";
985 $filearea = "draft";
986 $itemid = 0;
987 $filepath = "/";
988 $filename = "Simple.txt";
989 $filecontent = base64_encode("Let us create a nice simple file");
990 $contextlevel = null;
991 $instanceid = null;
992 $browser = get_file_browser();
994 // Call the files api to create a file.
995 $draftfile = core_files_external::upload($contextid, $component, $filearea, $itemid, $filepath,
996 $filename, $filecontent, $contextlevel, $instanceid);
997 $draftfile = \external_api::clean_returnvalue(core_files_external::upload_returns(), $draftfile);
999 $draftid = $draftfile['itemid'];
1000 // Make sure the file was created.
1001 $file = $browser->get_file_info($context, $component, $filearea, $draftid, $filepath, $filename);
1002 $this->assertNotEmpty($file);
1004 // Make sure the file does not exist in the user private files.
1005 $file = $browser->get_file_info($context, $component, 'private', 0, $filepath, $filename);
1006 $this->assertEmpty($file);
1008 // Call the external function.
1009 core_user_external::add_user_private_files($draftid);
1011 // Make sure the file was added to the user private files.
1012 $file = $browser->get_file_info($context, $component, 'private', 0, $filepath, $filename);
1013 $this->assertNotEmpty($file);
1017 * Test add user device
1019 public function test_add_user_device() {
1020 global $USER, $CFG, $DB;
1022 $this->resetAfterTest(true);
1024 $device = array(
1025 'appid' => 'com.moodle.moodlemobile',
1026 'name' => 'occam',
1027 'model' => 'Nexus 4',
1028 'platform' => 'Android',
1029 'version' => '4.2.2',
1030 'pushid' => 'apushdkasdfj4835',
1031 'uuid' => 'asdnfl348qlksfaasef859'
1034 // Call the external function.
1035 core_user_external::add_user_device($device['appid'], $device['name'], $device['model'], $device['platform'],
1036 $device['version'], $device['pushid'], $device['uuid']);
1038 $created = $DB->get_record('user_devices', array('pushid' => $device['pushid']));
1039 $created = (array) $created;
1041 $this->assertEquals($device, array_intersect_key((array)$created, $device));
1043 // Test reuse the same pushid value.
1044 $warnings = core_user_external::add_user_device($device['appid'], $device['name'], $device['model'], $device['platform'],
1045 $device['version'], $device['pushid'], $device['uuid']);
1046 // We need to execute the return values cleaning process to simulate the web service server.
1047 $warnings = \external_api::clean_returnvalue(core_user_external::add_user_device_returns(), $warnings);
1048 $this->assertCount(1, $warnings);
1050 // Test update an existing device.
1051 $device['pushid'] = 'different than before';
1052 $warnings = core_user_external::add_user_device($device['appid'], $device['name'], $device['model'], $device['platform'],
1053 $device['version'], $device['pushid'], $device['uuid']);
1054 $warnings = \external_api::clean_returnvalue(core_user_external::add_user_device_returns(), $warnings);
1056 $this->assertEquals(1, $DB->count_records('user_devices'));
1057 $updated = $DB->get_record('user_devices', array('pushid' => $device['pushid']));
1058 $this->assertEquals($device, array_intersect_key((array)$updated, $device));
1060 // Test creating a new device just changing the uuid.
1061 $device['uuid'] = 'newuidforthesameuser';
1062 $device['pushid'] = 'new different than before';
1063 $warnings = core_user_external::add_user_device($device['appid'], $device['name'], $device['model'], $device['platform'],
1064 $device['version'], $device['pushid'], $device['uuid']);
1065 $warnings = \external_api::clean_returnvalue(core_user_external::add_user_device_returns(), $warnings);
1066 $this->assertEquals(2, $DB->count_records('user_devices'));
1070 * Test remove user device
1072 public function test_remove_user_device() {
1073 global $USER, $CFG, $DB;
1075 $this->resetAfterTest(true);
1077 $device = array(
1078 'appid' => 'com.moodle.moodlemobile',
1079 'name' => 'occam',
1080 'model' => 'Nexus 4',
1081 'platform' => 'Android',
1082 'version' => '4.2.2',
1083 'pushid' => 'apushdkasdfj4835',
1084 'uuid' => 'ABCDE3723ksdfhasfaasef859'
1087 // A device with the same properties except the appid and pushid.
1088 $device2 = $device;
1089 $device2['pushid'] = "0987654321";
1090 $device2['appid'] = "other.app.com";
1092 $this->setAdminUser();
1093 // Create a user device using the external API function.
1094 core_user_external::add_user_device($device['appid'], $device['name'], $device['model'], $device['platform'],
1095 $device['version'], $device['pushid'], $device['uuid']);
1097 // Create the same device but for a different app.
1098 core_user_external::add_user_device($device2['appid'], $device2['name'], $device2['model'], $device2['platform'],
1099 $device2['version'], $device2['pushid'], $device2['uuid']);
1101 // Try to remove a device that does not exist.
1102 $result = core_user_external::remove_user_device('1234567890');
1103 $result = \external_api::clean_returnvalue(core_user_external::remove_user_device_returns(), $result);
1104 $this->assertFalse($result['removed']);
1105 $this->assertCount(1, $result['warnings']);
1107 // Try to remove a device that does not exist for an existing app.
1108 $result = core_user_external::remove_user_device('1234567890', $device['appid']);
1109 $result = \external_api::clean_returnvalue(core_user_external::remove_user_device_returns(), $result);
1110 $this->assertFalse($result['removed']);
1111 $this->assertCount(1, $result['warnings']);
1113 // Remove an existing device for an existing app. This will remove one of the two devices.
1114 $result = core_user_external::remove_user_device($device['uuid'], $device['appid']);
1115 $result = \external_api::clean_returnvalue(core_user_external::remove_user_device_returns(), $result);
1116 $this->assertTrue($result['removed']);
1118 // Remove all the devices. This must remove the remaining device.
1119 $result = core_user_external::remove_user_device($device['uuid']);
1120 $result = \external_api::clean_returnvalue(core_user_external::remove_user_device_returns(), $result);
1121 $this->assertTrue($result['removed']);
1125 * Test get_user_preferences
1127 public function test_get_user_preferences() {
1128 $this->resetAfterTest(true);
1130 $user = self::getDataGenerator()->create_user();
1131 set_user_preference('calendar_maxevents', 1, $user);
1132 set_user_preference('some_random_text', 'text', $user);
1134 $this->setUser($user);
1136 $result = core_user_external::get_user_preferences();
1137 $result = \external_api::clean_returnvalue(core_user_external::get_user_preferences_returns(), $result);
1138 $this->assertCount(0, $result['warnings']);
1139 // Expect 3, _lastloaded is always returned.
1140 $this->assertCount(3, $result['preferences']);
1142 foreach ($result['preferences'] as $pref) {
1143 if ($pref['name'] === '_lastloaded') {
1144 continue;
1146 // Check we receive the expected preferences.
1147 $this->assertEquals(get_user_preferences($pref['name']), $pref['value']);
1150 // Retrieve just one preference.
1151 $result = core_user_external::get_user_preferences('some_random_text');
1152 $result = \external_api::clean_returnvalue(core_user_external::get_user_preferences_returns(), $result);
1153 $this->assertCount(0, $result['warnings']);
1154 $this->assertCount(1, $result['preferences']);
1155 $this->assertEquals('text', $result['preferences'][0]['value']);
1157 // Retrieve non-existent preference.
1158 $result = core_user_external::get_user_preferences('non_existent');
1159 $result = \external_api::clean_returnvalue(core_user_external::get_user_preferences_returns(), $result);
1160 $this->assertCount(0, $result['warnings']);
1161 $this->assertCount(1, $result['preferences']);
1162 $this->assertEquals(null, $result['preferences'][0]['value']);
1164 // Check that as admin we can retrieve all the preferences for any user.
1165 $this->setAdminUser();
1166 $result = core_user_external::get_user_preferences('', $user->id);
1167 $result = \external_api::clean_returnvalue(core_user_external::get_user_preferences_returns(), $result);
1168 $this->assertCount(0, $result['warnings']);
1169 $this->assertCount(3, $result['preferences']);
1171 foreach ($result['preferences'] as $pref) {
1172 if ($pref['name'] === '_lastloaded') {
1173 continue;
1175 // Check we receive the expected preferences.
1176 $this->assertEquals(get_user_preferences($pref['name'], null, $user), $pref['value']);
1179 // Check that as a non admin user we cannot retrieve other users preferences.
1180 $anotheruser = self::getDataGenerator()->create_user();
1181 $this->setUser($anotheruser);
1183 $this->expectException('required_capability_exception');
1184 $result = core_user_external::get_user_preferences('', $user->id);
1188 * Test update_picture
1190 public function test_update_picture() {
1191 global $DB, $USER;
1193 $this->resetAfterTest(true);
1195 $user = self::getDataGenerator()->create_user();
1196 self::setUser($user);
1198 $context = \context_user::instance($USER->id);
1199 $contextid = $context->id;
1200 $filename = "reddot.png";
1201 $filecontent = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38"
1202 . "GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
1204 // Call the files api to create a file.
1205 $draftfile = core_files_external::upload($contextid, 'user', 'draft', 0, '/', $filename, $filecontent, null, null);
1206 $draftid = $draftfile['itemid'];
1208 // Change user profile image.
1209 $result = core_user_external::update_picture($draftid);
1210 $result = \external_api::clean_returnvalue(core_user_external::update_picture_returns(), $result);
1211 $picture = $DB->get_field('user', 'picture', array('id' => $user->id));
1212 // The new revision is in the url for the user.
1213 $this->assertStringContainsString($picture, $result['profileimageurl']);
1214 // Check expected URL for serving the image.
1215 $this->assertStringContainsString("/$contextid/user/icon", $result['profileimageurl']);
1217 // Delete image.
1218 $result = core_user_external::update_picture(0, true);
1219 $result = \external_api::clean_returnvalue(core_user_external::update_picture_returns(), $result);
1220 $picture = $DB->get_field('user', 'picture', array('id' => $user->id));
1221 // No picture.
1222 $this->assertEquals(0, $picture);
1224 // Add again the user profile image (as admin).
1225 $this->setAdminUser();
1227 $context = \context_user::instance($USER->id);
1228 $admincontextid = $context->id;
1229 $draftfile = core_files_external::upload($admincontextid, 'user', 'draft', 0, '/', $filename, $filecontent, null, null);
1230 $draftid = $draftfile['itemid'];
1232 $result = core_user_external::update_picture($draftid, false, $user->id);
1233 $result = \external_api::clean_returnvalue(core_user_external::update_picture_returns(), $result);
1234 // The new revision is in the url for the user.
1235 $picture = $DB->get_field('user', 'picture', array('id' => $user->id));
1236 $this->assertStringContainsString($picture, $result['profileimageurl']);
1237 $this->assertStringContainsString("/$contextid/user/icon", $result['profileimageurl']);
1241 * Test update_picture disabled
1243 public function test_update_picture_disabled() {
1244 global $CFG;
1245 $this->resetAfterTest(true);
1246 $CFG->disableuserimages = true;
1248 $this->setAdminUser();
1249 $this->expectException('moodle_exception');
1250 core_user_external::update_picture(0);
1254 * Test set_user_preferences
1256 public function test_set_user_preferences_save() {
1257 global $DB;
1258 $this->resetAfterTest(true);
1260 $user1 = self::getDataGenerator()->create_user();
1261 $user2 = self::getDataGenerator()->create_user();
1263 // Save users preferences.
1264 $this->setAdminUser();
1265 $preferences = array(
1266 array(
1267 'name' => 'htmleditor',
1268 'value' => 'atto',
1269 'userid' => $user1->id,
1271 array(
1272 'name' => 'htmleditor',
1273 'value' => 'tinymce',
1274 'userid' => $user2->id,
1278 $result = core_user_external::set_user_preferences($preferences);
1279 $result = \external_api::clean_returnvalue(core_user_external::set_user_preferences_returns(), $result);
1280 $this->assertCount(0, $result['warnings']);
1281 $this->assertCount(2, $result['saved']);
1283 // Get preference from DB to avoid cache.
1284 $this->assertEquals('atto', $DB->get_field('user_preferences', 'value',
1285 array('userid' => $user1->id, 'name' => 'htmleditor')));
1286 $this->assertEquals('tinymce', $DB->get_field('user_preferences', 'value',
1287 array('userid' => $user2->id, 'name' => 'htmleditor')));
1291 * Test set_user_preferences
1293 public function test_set_user_preferences_save_invalid_pref() {
1294 global $DB;
1295 $this->resetAfterTest(true);
1297 $user1 = self::getDataGenerator()->create_user();
1299 // Save users preferences.
1300 $this->setAdminUser();
1301 $preferences = array(
1302 array(
1303 'name' => 'some_random_pref',
1304 'value' => 'abc',
1305 'userid' => $user1->id,
1309 $result = core_user_external::set_user_preferences($preferences);
1310 $result = \external_api::clean_returnvalue(core_user_external::set_user_preferences_returns(), $result);
1311 $this->assertCount(1, $result['warnings']);
1312 $this->assertCount(0, $result['saved']);
1313 $this->assertEquals('nopermission', $result['warnings'][0]['warningcode']);
1315 // Nothing was written to DB.
1316 $this->assertEmpty($DB->count_records('user_preferences', array('name' => 'some_random_pref')));
1320 * Test set_user_preferences for an invalid user
1322 public function test_set_user_preferences_invalid_user() {
1323 $this->resetAfterTest(true);
1325 $this->setAdminUser();
1326 $preferences = array(
1327 array(
1328 'name' => 'calendar_maxevents',
1329 'value' => 4,
1330 'userid' => -2
1334 $result = core_user_external::set_user_preferences($preferences);
1335 $result = \external_api::clean_returnvalue(core_user_external::set_user_preferences_returns(), $result);
1336 $this->assertCount(1, $result['warnings']);
1337 $this->assertCount(0, $result['saved']);
1338 $this->assertEquals('invaliduser', $result['warnings'][0]['warningcode']);
1339 $this->assertEquals(-2, $result['warnings'][0]['itemid']);
1343 * Test set_user_preferences using an invalid preference
1345 public function test_set_user_preferences_invalid_preference() {
1346 global $USER, $DB;
1348 $this->resetAfterTest(true);
1349 // Create a very long value.
1350 $this->setAdminUser();
1351 $preferences = array(
1352 array(
1353 'name' => 'calendar_maxevents',
1354 'value' => str_repeat('a', 1334),
1355 'userid' => $USER->id
1359 $result = core_user_external::set_user_preferences($preferences);
1360 $result = \external_api::clean_returnvalue(core_user_external::set_user_preferences_returns(), $result);
1361 $this->assertCount(0, $result['warnings']);
1362 $this->assertCount(1, $result['saved']);
1363 // Cleaned valud of the preference was saved.
1364 $this->assertEquals(1, $DB->get_field('user_preferences', 'value',
1365 array('userid' => $USER->id, 'name' => 'calendar_maxevents')));
1369 * Test set_user_preferences for other user not being admin
1371 public function test_set_user_preferences_capability() {
1372 $this->resetAfterTest(true);
1374 $user1 = self::getDataGenerator()->create_user();
1375 $user2 = self::getDataGenerator()->create_user();
1377 $this->setUser($user1);
1378 $preferences = array(
1379 array(
1380 'name' => 'calendar_maxevents',
1381 'value' => 4,
1382 'userid' => $user2->id
1386 $result = core_user_external::set_user_preferences($preferences);
1388 $this->assertCount(1, $result['warnings']);
1389 $this->assertCount(0, $result['saved']);
1390 $this->assertEquals('nopermission', $result['warnings'][0]['warningcode']);
1391 $this->assertEquals($user2->id, $result['warnings'][0]['itemid']);
1395 * Test update_user_preferences unsetting an existing preference.
1397 public function test_update_user_preferences_unset() {
1398 global $DB;
1399 $this->resetAfterTest(true);
1401 $user = self::getDataGenerator()->create_user();
1403 // Save users preferences.
1404 $this->setAdminUser();
1405 $preferences = array(
1406 array(
1407 'name' => 'htmleditor',
1408 'value' => 'atto',
1409 'userid' => $user->id,
1413 $result = core_user_external::set_user_preferences($preferences);
1414 $result = \external_api::clean_returnvalue(core_user_external::set_user_preferences_returns(), $result);
1415 $this->assertCount(0, $result['warnings']);
1416 $this->assertCount(1, $result['saved']);
1418 // Get preference from DB to avoid cache.
1419 $this->assertEquals('atto', $DB->get_field('user_preferences', 'value',
1420 array('userid' => $user->id, 'name' => 'htmleditor')));
1422 // Now, unset.
1423 $result = core_user_external::update_user_preferences($user->id, null, array(array('type' => 'htmleditor')));
1425 $this->assertEquals(0, $DB->count_records('user_preferences', array('userid' => $user->id, 'name' => 'htmleditor')));
1429 * Test agree_site_policy
1431 public function test_agree_site_policy() {
1432 global $CFG, $DB, $USER;
1433 $this->resetAfterTest(true);
1435 $user = self::getDataGenerator()->create_user();
1436 $this->setUser($user);
1438 // Site policy not set.
1439 $result = core_user_external::agree_site_policy();
1440 $result = \external_api::clean_returnvalue(core_user_external::agree_site_policy_returns(), $result);
1441 $this->assertFalse($result['status']);
1442 $this->assertCount(1, $result['warnings']);
1443 $this->assertEquals('nositepolicy', $result['warnings'][0]['warningcode']);
1445 // Set a policy issue.
1446 $CFG->sitepolicy = 'https://moodle.org';
1447 $this->assertEquals(0, $USER->policyagreed);
1449 $result = core_user_external::agree_site_policy();
1450 $result = \external_api::clean_returnvalue(core_user_external::agree_site_policy_returns(), $result);
1451 $this->assertTrue($result['status']);
1452 $this->assertCount(0, $result['warnings']);
1453 $this->assertEquals(1, $USER->policyagreed);
1454 $this->assertEquals(1, $DB->get_field('user', 'policyagreed', array('id' => $USER->id)));
1456 // Try again, we should get a warning.
1457 $result = core_user_external::agree_site_policy();
1458 $result = \external_api::clean_returnvalue(core_user_external::agree_site_policy_returns(), $result);
1459 $this->assertFalse($result['status']);
1460 $this->assertCount(1, $result['warnings']);
1461 $this->assertEquals('alreadyagreed', $result['warnings'][0]['warningcode']);
1463 // Set something to make require_login throws an exception.
1464 $otheruser = self::getDataGenerator()->create_user();
1465 $this->setUser($otheruser);
1467 $DB->set_field('user', 'lastname', '', array('id' => $USER->id));
1468 $USER->lastname = '';
1469 try {
1470 $result = core_user_external::agree_site_policy();
1471 $this->fail('Expecting \'usernotfullysetup\' moodle_exception to be thrown');
1472 } catch (\moodle_exception $e) {
1473 $this->assertEquals('usernotfullysetup', $e->errorcode);
1474 } catch (\Exception $e) {
1475 $this->fail('Expecting \'usernotfullysetup\' moodle_exception to be thrown.');
1480 * Test get_private_files_info
1482 public function test_get_private_files_info() {
1484 $this->resetAfterTest(true);
1485 $user = self::getDataGenerator()->create_user();
1486 $this->setUser($user);
1487 $usercontext = \context_user::instance($user->id);
1489 $filerecord = array(
1490 'contextid' => $usercontext->id,
1491 'component' => 'user',
1492 'filearea' => 'private',
1493 'itemid' => 0,
1494 'filepath' => '/',
1495 'filename' => 'thefile',
1498 $fs = get_file_storage();
1499 $file = $fs->create_file_from_string($filerecord, 'abc');
1501 // Get my private files information.
1502 $result = core_user_external::get_private_files_info();
1503 $result = \external_api::clean_returnvalue(core_user_external::get_private_files_info_returns(), $result);
1504 $this->assertEquals(1, $result['filecount']);
1505 $this->assertEquals($file->get_filesize(), $result['filesize']);
1506 $this->assertEquals(0, $result['foldercount']);
1507 $this->assertEquals($file->get_filesize(), $result['filesizewithoutreferences']);
1509 // As admin, get user information.
1510 $this->setAdminUser();
1511 $result = core_user_external::get_private_files_info($user->id);
1512 $result = \external_api::clean_returnvalue(core_user_external::get_private_files_info_returns(), $result);
1513 $this->assertEquals(1, $result['filecount']);
1514 $this->assertEquals($file->get_filesize(), $result['filesize']);
1515 $this->assertEquals(0, $result['foldercount']);
1516 $this->assertEquals($file->get_filesize(), $result['filesizewithoutreferences']);
1520 * Test get_private_files_info missing permissions.
1522 public function test_get_private_files_info_missing_permissions() {
1524 $this->resetAfterTest(true);
1525 $user1 = self::getDataGenerator()->create_user();
1526 $user2 = self::getDataGenerator()->create_user();
1527 $this->setUser($user1);
1529 $this->expectException('required_capability_exception');
1530 // Try to retrieve other user private files info.
1531 core_user_external::get_private_files_info($user2->id);
1535 * Test the functionality of the {@see \core_user\external\search_identity} class.
1537 public function test_external_search_identity() {
1538 global $CFG;
1540 $this->resetAfterTest(true);
1541 $this->setAdminUser();
1543 $user1 = self::getDataGenerator()->create_user([
1544 'firstname' => 'Firstone',
1545 'lastname' => 'Lastone',
1546 'username' => 'usernameone',
1547 'idnumber' => 'idnumberone',
1548 'email' => 'userone@example.com',
1549 'phone1' => 'tel1',
1550 'phone2' => 'tel2',
1551 'department' => 'Department Foo',
1552 'institution' => 'Institution Foo',
1553 'city' => 'City One',
1554 'country' => 'AU',
1557 $user2 = self::getDataGenerator()->create_user([
1558 'firstname' => 'Firsttwo',
1559 'lastname' => 'Lasttwo',
1560 'username' => 'usernametwo',
1561 'idnumber' => 'idnumbertwo',
1562 'email' => 'usertwo@example.com',
1563 'phone1' => 'tel1',
1564 'phone2' => 'tel2',
1565 'department' => 'Department Foo',
1566 'institution' => 'Institution Foo',
1567 'city' => 'City One',
1568 'country' => 'AU',
1571 $user3 = self::getDataGenerator()->create_user([
1572 'firstname' => 'Firstthree',
1573 'lastname' => 'Lastthree',
1574 'username' => 'usernamethree',
1575 'idnumber' => 'idnumberthree',
1576 'email' => 'userthree@example.com',
1577 'phone1' => 'tel1',
1578 'phone2' => 'tel2',
1579 'department' => 'Department Foo',
1580 'institution' => 'Institution Foo',
1581 'city' => 'City One',
1582 'country' => 'AU',
1585 $CFG->showuseridentity = 'email,idnumber,city';
1586 $CFG->maxusersperpage = 3;
1588 $result = \core_user\external\search_identity::execute('Lastt');
1589 $result = \external_api::clean_returnvalue(\core_user\external\search_identity::execute_returns(), $result);
1591 $this->assertEquals(2, count($result['list']));
1592 $this->assertEquals(3, $result['maxusersperpage']);
1593 $this->assertEquals(false, $result['overflow']);
1595 foreach ($result['list'] as $user) {
1596 $this->assertEquals(3, count($user['extrafields']));
1597 $this->assertEquals('email', $user['extrafields'][0]['name']);
1598 $this->assertEquals('idnumber', $user['extrafields'][1]['name']);
1599 $this->assertEquals('city', $user['extrafields'][2]['name']);
1602 $CFG->showuseridentity = 'username';
1603 $CFG->maxusersperpage = 2;
1605 $result = \core_user\external\search_identity::execute('Firstt');
1606 $result = \external_api::clean_returnvalue(\core_user\external\search_identity::execute_returns(), $result);
1608 $this->assertEquals(2, count($result['list']));
1609 $this->assertEquals(2, $result['maxusersperpage']);
1610 $this->assertEquals(false, $result['overflow']);
1612 foreach ($result['list'] as $user) {
1613 $this->assertEquals(1, count($user['extrafields']));
1614 $this->assertEquals('username', $user['extrafields'][0]['name']);
1617 $CFG->showuseridentity = 'email';
1618 $CFG->maxusersperpage = 2;
1620 $result = \core_user\external\search_identity::execute('City One');
1621 $result = \external_api::clean_returnvalue(\core_user\external\search_identity::execute_returns(), $result);
1623 $this->assertEquals(0, count($result['list']));
1624 $this->assertEquals(2, $result['maxusersperpage']);
1625 $this->assertEquals(false, $result['overflow']);
1627 $CFG->showuseridentity = 'city';
1628 $CFG->maxusersperpage = 2;
1630 foreach ($result['list'] as $user) {
1631 $this->assertEquals(1, count($user['extrafields']));
1632 $this->assertEquals('username', $user['extrafields'][0]['name']);
1635 $result = \core_user\external\search_identity::execute('City One');
1636 $result = \external_api::clean_returnvalue(\core_user\external\search_identity::execute_returns(), $result);
1638 $this->assertEquals(2, count($result['list']));
1639 $this->assertEquals(2, $result['maxusersperpage']);
1640 $this->assertEquals(true, $result['overflow']);
1644 * Test functionality of the {@see \core_user\external\search_identity} class with alternativefullnameformat defined.
1646 public function test_external_search_identity_with_alternativefullnameformat() {
1647 global $CFG;
1649 $this->resetAfterTest(true);
1650 $this->setAdminUser();
1652 $user1 = self::getDataGenerator()->create_user([
1653 'lastname' => '小柳',
1654 'lastnamephonetic' => 'Koyanagi',
1655 'firstname' => '秋',
1656 'firstnamephonetic' => 'Aki',
1657 'email' => 'koyanagiaki@example.com',
1658 'country' => 'JP',
1661 $CFG->showuseridentity = 'email';
1662 $CFG->maxusersperpage = 3;
1663 $CFG->alternativefullnameformat =
1664 '<ruby>lastname firstname <rp>(</rp><rt>lastnamephonetic firstnamephonetic</rt><rp>)</rp></ruby>';
1666 $result = \core_user\external\search_identity::execute('Ak');
1667 $result = \external_api::clean_returnvalue(\core_user\external\search_identity::execute_returns(), $result);
1669 $this->assertEquals(1, count($result['list']));
1670 $this->assertEquals(3, $result['maxusersperpage']);
1671 $this->assertEquals(false, $result['overflow']);
1673 foreach ($result['list'] as $user) {
1674 $this->assertEquals(1, count($user['extrafields']));
1675 $this->assertEquals('email', $user['extrafields'][0]['name']);