MDL-64774 user: Better unit tests for updating users with similar emails
[moodle.git] / user / tests / externallib_test.php
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/>.
17 /**
18  * User external PHPunit tests
19  *
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
25  */
27 defined('MOODLE_INTERNAL') || die();
29 global $CFG;
31 require_once($CFG->dirroot . '/webservice/tests/helpers.php');
32 require_once($CFG->dirroot . '/user/externallib.php');
33 require_once($CFG->dirroot . '/files/externallib.php');
35 class core_user_externallib_testcase extends externallib_advanced_testcase {
37     /**
38      * Test get_users
39      */
40     public function test_get_users() {
41         global $USER, $CFG;
43         $this->resetAfterTest(true);
45         $course = self::getDataGenerator()->create_course();
47         $user1 = array(
48             'username' => 'usernametest1',
49             'idnumber' => 'idnumbertest1',
50             'firstname' => 'First Name User Test 1',
51             'lastname' => 'Last Name User Test 1',
52             'email' => 'usertest1@example.com',
53             'address' => '2 Test Street Perth 6000 WA',
54             'phone1' => '01010101010',
55             'phone2' => '02020203',
56             'icq' => 'testuser1',
57             'skype' => 'testuser1',
58             'yahoo' => 'testuser1',
59             'aim' => 'testuser1',
60             'msn' => 'testuser1',
61             'department' => 'Department of user 1',
62             'institution' => 'Institution of user 1',
63             'description' => 'This is a description for user 1',
64             'descriptionformat' => FORMAT_MOODLE,
65             'city' => 'Perth',
66             'url' => 'http://moodle.org',
67             'country' => 'AU'
68             );
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']);
116             }
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']);
121             }
122             if (!empty($generateduser->address)) {
123                 $this->assertEquals($generateduser->address, $returneduser['address']);
124             }
125             if (!empty($generateduser->phone1)) {
126                 $this->assertEquals($generateduser->phone1, $returneduser['phone1']);
127             }
128             if (!empty($generateduser->phone2)) {
129                 $this->assertEquals($generateduser->phone2, $returneduser['phone2']);
130             }
131             if (!empty($generateduser->icq)) {
132                 $this->assertEquals($generateduser->icq, $returneduser['icq']);
133             }
134             if (!empty($generateduser->skype)) {
135                 $this->assertEquals($generateduser->skype, $returneduser['skype']);
136             }
137             if (!empty($generateduser->yahoo)) {
138                 $this->assertEquals($generateduser->yahoo, $returneduser['yahoo']);
139             }
140             if (!empty($generateduser->aim)) {
141                 $this->assertEquals($generateduser->aim, $returneduser['aim']);
142             }
143             if (!empty($generateduser->msn)) {
144                 $this->assertEquals($generateduser->msn, $returneduser['msn']);
145             }
146             if (!empty($generateduser->department)) {
147                 $this->assertEquals($generateduser->department, $returneduser['department']);
148             }
149             if (!empty($generateduser->institution)) {
150                 $this->assertEquals($generateduser->institution, $returneduser['institution']);
151             }
152             if (!empty($generateduser->description)) {
153                 $this->assertEquals($generateduser->description, $returneduser['description']);
154             }
155             if (!empty($generateduser->descriptionformat)) {
156                 $this->assertEquals(FORMAT_HTML, $returneduser['descriptionformat']);
157             }
158             if (!empty($generateduser->city)) {
159                 $this->assertEquals($generateduser->city, $returneduser['city']);
160             }
161             if (!empty($generateduser->country)) {
162                 $this->assertEquals($generateduser->country, $returneduser['country']);
163             }
164             if (!empty($generateduser->url)) {
165                 $this->assertEquals($generateduser->url, $returneduser['url']);
166             }
167             if (!empty($CFG->usetags) and !empty($generateduser->interests)) {
168                 $this->assertEquals(implode(', ', $generateduser->interests), $returneduser['interests']);
169             }
170         }
172         // Test the invalid key warning.
173         $warnings = $result['warnings'];
174         $this->assertEquals(count($warnings), 1);
175         $warning = array_pop($warnings);
176         $this->assertEquals($warning['item'], 'invalidkey');
177         $this->assertEquals($warning['warningcode'], 'invalidfieldparameter');
179         // Test sending twice the same search field.
180         try {
181             $searchparams = array(
182             array('key' => 'firstname', 'value' => 'Canard'),
183             array('key' => 'email', 'value' => $user1->email),
184             array('key' => 'firstname', 'value' => $user1->firstname));
186             // Call the external function.
187             $result = core_user_external::get_users($searchparams);
188             $this->fail('Expecting \'keyalreadyset\' moodle_exception to be thrown.');
189         } catch (moodle_exception $e) {
190             $this->assertEquals('keyalreadyset', $e->errorcode);
191         } catch (Exception $e) {
192             $this->fail('Expecting \'keyalreadyset\' moodle_exception to be thrown.');
193         }
194     }
196     /**
197      * Test get_users_by_field
198      */
199     public function test_get_users_by_field() {
200         global $USER, $CFG;
202         $this->resetAfterTest(true);
204         $course = self::getDataGenerator()->create_course();
205         $user1 = array(
206             'username' => 'usernametest1',
207             'idnumber' => 'idnumbertest1',
208             'firstname' => 'First Name User Test 1',
209             'lastname' => 'Last Name User Test 1',
210             'email' => 'usertest1@example.com',
211             'address' => '2 Test Street Perth 6000 WA',
212             'phone1' => '01010101010',
213             'phone2' => '02020203',
214             'icq' => 'testuser1',
215             'skype' => 'testuser1',
216             'yahoo' => 'testuser1',
217             'aim' => 'testuser1',
218             'msn' => 'testuser1',
219             'department' => 'Department of user 1',
220             'institution' => 'Institution of user 1',
221             'description' => 'This is a description for user 1',
222             'descriptionformat' => FORMAT_MOODLE,
223             'city' => 'Perth',
224             'url' => 'http://moodle.org',
225             'country' => 'AU',
226             'lang' => 'kkl',
227             'theme' => 'kkt',
228         );
229         $user1 = self::getDataGenerator()->create_user($user1);
230         if (!empty($CFG->usetags)) {
231             require_once($CFG->dirroot . '/user/editlib.php');
232             $user1->interests = array('Cinema', 'Tennis', 'Dance', 'Guitar', 'Cooking');
233             useredit_update_interests($user1, $user1->interests);
234         }
235         $user2 = self::getDataGenerator()->create_user(
236                 array('username' => 'usernametest2', 'idnumber' => 'idnumbertest2'));
238         $generatedusers = array();
239         $generatedusers[$user1->id] = $user1;
240         $generatedusers[$user2->id] = $user2;
242         $context = context_course::instance($course->id);
243         $roleid = $this->assignUserCapability('moodle/user:viewdetails', $context->id);
245         // Enrol the users in the course.
246         $this->getDataGenerator()->enrol_user($user1->id, $course->id, $roleid, 'manual');
247         $this->getDataGenerator()->enrol_user($user2->id, $course->id, $roleid, 'manual');
248         $this->getDataGenerator()->enrol_user($USER->id, $course->id, $roleid, 'manual');
250         // call as admin and receive all possible fields.
251         $this->setAdminUser();
253         $fieldstosearch = array('id', 'idnumber', 'username', 'email');
255         foreach ($fieldstosearch as $fieldtosearch) {
257             // Call the external function.
258             $returnedusers = core_user_external::get_users_by_field($fieldtosearch,
259                         array($USER->{$fieldtosearch}, $user1->{$fieldtosearch}, $user2->{$fieldtosearch}));
260             $returnedusers = external_api::clean_returnvalue(core_user_external::get_users_by_field_returns(), $returnedusers);
262             // Expected result differ following the searched field
263             // Admin user in the PHPunit framework doesn't have an idnumber.
264             if ($fieldtosearch == 'idnumber') {
265                 $expectedreturnedusers = 2;
266             } else {
267                 $expectedreturnedusers = 3;
268             }
270             // Check we retrieve the good total number of enrolled users + no error on capability.
271             $this->assertEquals($expectedreturnedusers, count($returnedusers));
273             foreach($returnedusers as $returneduser) {
274                 $generateduser = ($returneduser['id'] == $USER->id) ?
275                                     $USER : $generatedusers[$returneduser['id']];
276                 $this->assertEquals($generateduser->username, $returneduser['username']);
277                 if (!empty($generateduser->idnumber)) {
278                     $this->assertEquals($generateduser->idnumber, $returneduser['idnumber']);
279                 }
280                 $this->assertEquals($generateduser->firstname, $returneduser['firstname']);
281                 $this->assertEquals($generateduser->lastname, $returneduser['lastname']);
282                 if ($generateduser->email != $USER->email) { //don't check the tmp modified $USER email
283                     $this->assertEquals($generateduser->email, $returneduser['email']);
284                 }
285                 if (!empty($generateduser->address)) {
286                     $this->assertEquals($generateduser->address, $returneduser['address']);
287                 }
288                 if (!empty($generateduser->phone1)) {
289                     $this->assertEquals($generateduser->phone1, $returneduser['phone1']);
290                 }
291                 if (!empty($generateduser->phone2)) {
292                     $this->assertEquals($generateduser->phone2, $returneduser['phone2']);
293                 }
294                 if (!empty($generateduser->icq)) {
295                     $this->assertEquals($generateduser->icq, $returneduser['icq']);
296                 }
297                 if (!empty($generateduser->skype)) {
298                     $this->assertEquals($generateduser->skype, $returneduser['skype']);
299                 }
300                 if (!empty($generateduser->yahoo)) {
301                     $this->assertEquals($generateduser->yahoo, $returneduser['yahoo']);
302                 }
303                 if (!empty($generateduser->aim)) {
304                     $this->assertEquals($generateduser->aim, $returneduser['aim']);
305                 }
306                 if (!empty($generateduser->msn)) {
307                     $this->assertEquals($generateduser->msn, $returneduser['msn']);
308                 }
309                 if (!empty($generateduser->department)) {
310                     $this->assertEquals($generateduser->department, $returneduser['department']);
311                 }
312                 if (!empty($generateduser->institution)) {
313                     $this->assertEquals($generateduser->institution, $returneduser['institution']);
314                 }
315                 if (!empty($generateduser->description)) {
316                     $this->assertEquals($generateduser->description, $returneduser['description']);
317                 }
318                 if (!empty($generateduser->descriptionformat) and isset($returneduser['descriptionformat'])) {
319                     $this->assertEquals($generateduser->descriptionformat, $returneduser['descriptionformat']);
320                 }
321                 if (!empty($generateduser->city)) {
322                     $this->assertEquals($generateduser->city, $returneduser['city']);
323                 }
324                 if (!empty($generateduser->country)) {
325                     $this->assertEquals($generateduser->country, $returneduser['country']);
326                 }
327                 if (!empty($generateduser->url)) {
328                     $this->assertEquals($generateduser->url, $returneduser['url']);
329                 }
330                 if (!empty($CFG->usetags) and !empty($generateduser->interests)) {
331                     $this->assertEquals(implode(', ', $generateduser->interests), $returneduser['interests']);
332                 }
333                 // Check empty since incorrect values were used when creating the user.
334                 if ($returneduser['id'] == $user1->id) {
335                     $this->assertEmpty($returneduser['lang']);
336                     $this->assertEmpty($returneduser['theme']);
337                 }
338             }
339         }
341         // Test that no result are returned for search by username if we are not admin
342         $this->setGuestUser();
344         // Call the external function.
345         $returnedusers = core_user_external::get_users_by_field('username',
346                     array($USER->username, $user1->username, $user2->username));
347         $returnedusers = external_api::clean_returnvalue(core_user_external::get_users_by_field_returns(), $returnedusers);
349         // Only the own $USER username should be returned
350         $this->assertEquals(1, count($returnedusers));
352         // And finally test as one of the enrolled users.
353         $this->setUser($user1);
355         // Call the external function.
356         $returnedusers = core_user_external::get_users_by_field('username',
357             array($USER->username, $user1->username, $user2->username));
358         $returnedusers = external_api::clean_returnvalue(core_user_external::get_users_by_field_returns(), $returnedusers);
360         // Only the own $USER username should be returned still.
361         $this->assertEquals(1, count($returnedusers));
362     }
364     public function get_course_user_profiles_setup($capability) {
365         global $USER, $CFG;
367         $this->resetAfterTest(true);
369         $return = new stdClass();
371         // Create the course and fetch its context.
372         $return->course = self::getDataGenerator()->create_course();
373         $return->user1 = array(
374             'username' => 'usernametest1',
375             'idnumber' => 'idnumbertest1',
376             'firstname' => 'First Name User Test 1',
377             'lastname' => 'Last Name User Test 1',
378             'email' => 'usertest1@example.com',
379             'address' => '2 Test Street Perth 6000 WA',
380             'phone1' => '01010101010',
381             'phone2' => '02020203',
382             'icq' => 'testuser1',
383             'skype' => 'testuser1',
384             'yahoo' => 'testuser1',
385             'aim' => 'testuser1',
386             'msn' => 'testuser1',
387             'department' => 'Department of user 1',
388             'institution' => 'Institution of user 1',
389             'description' => 'This is a description for user 1',
390             'descriptionformat' => FORMAT_MOODLE,
391             'city' => 'Perth',
392             'url' => 'http://moodle.org',
393             'country' => 'AU'
394         );
395         $return->user1 = self::getDataGenerator()->create_user($return->user1);
396         if (!empty($CFG->usetags)) {
397             require_once($CFG->dirroot . '/user/editlib.php');
398             $return->user1->interests = array('Cinema', 'Tennis', 'Dance', 'Guitar', 'Cooking');
399             useredit_update_interests($return->user1, $return->user1->interests);
400         }
401         $return->user2 = self::getDataGenerator()->create_user();
403         $context = context_course::instance($return->course->id);
404         $return->roleid = $this->assignUserCapability($capability, $context->id);
406         // Enrol the users in the course.
407         $this->getDataGenerator()->enrol_user($return->user1->id, $return->course->id, $return->roleid, 'manual');
408         $this->getDataGenerator()->enrol_user($return->user2->id, $return->course->id, $return->roleid, 'manual');
409         $this->getDataGenerator()->enrol_user($USER->id, $return->course->id, $return->roleid, 'manual');
411         return $return;
412     }
414     /**
415      * Test get_course_user_profiles
416      */
417     public function test_get_course_user_profiles() {
418         global $USER, $CFG;
420         $this->resetAfterTest(true);
422         $data = $this->get_course_user_profiles_setup('moodle/user:viewdetails');
424         // Call the external function.
425         $enrolledusers = core_user_external::get_course_user_profiles(array(
426                     array('userid' => $USER->id, 'courseid' => $data->course->id)));
428         // We need to execute the return values cleaning process to simulate the web service server.
429         $enrolledusers = external_api::clean_returnvalue(core_user_external::get_course_user_profiles_returns(), $enrolledusers);
431         // Check we retrieve the good total number of enrolled users + no error on capability.
432         $this->assertEquals(1, count($enrolledusers));
433     }
435     public function test_get_user_course_profile_as_admin() {
436         global $USER, $CFG;
438         global $USER, $CFG;
440         $this->resetAfterTest(true);
442         $data = $this->get_course_user_profiles_setup('moodle/user:viewdetails');
444         // Do the same call as admin to receive all possible fields.
445         $this->setAdminUser();
446         $USER->email = "admin@example.com";
448         // Call the external function.
449         $enrolledusers = core_user_external::get_course_user_profiles(array(
450             array('userid' => $data->user1->id, 'courseid' => $data->course->id)));
452         // We need to execute the return values cleaning process to simulate the web service server.
453         $enrolledusers = external_api::clean_returnvalue(core_user_external::get_course_user_profiles_returns(), $enrolledusers);
455         foreach($enrolledusers as $enrolleduser) {
456             if ($enrolleduser['username'] == $data->user1->username) {
457                 $this->assertEquals($data->user1->idnumber, $enrolleduser['idnumber']);
458                 $this->assertEquals($data->user1->firstname, $enrolleduser['firstname']);
459                 $this->assertEquals($data->user1->lastname, $enrolleduser['lastname']);
460                 $this->assertEquals($data->user1->email, $enrolleduser['email']);
461                 $this->assertEquals($data->user1->address, $enrolleduser['address']);
462                 $this->assertEquals($data->user1->phone1, $enrolleduser['phone1']);
463                 $this->assertEquals($data->user1->phone2, $enrolleduser['phone2']);
464                 $this->assertEquals($data->user1->icq, $enrolleduser['icq']);
465                 $this->assertEquals($data->user1->skype, $enrolleduser['skype']);
466                 $this->assertEquals($data->user1->yahoo, $enrolleduser['yahoo']);
467                 $this->assertEquals($data->user1->aim, $enrolleduser['aim']);
468                 $this->assertEquals($data->user1->msn, $enrolleduser['msn']);
469                 $this->assertEquals($data->user1->department, $enrolleduser['department']);
470                 $this->assertEquals($data->user1->institution, $enrolleduser['institution']);
471                 $this->assertEquals($data->user1->description, $enrolleduser['description']);
472                 $this->assertEquals(FORMAT_HTML, $enrolleduser['descriptionformat']);
473                 $this->assertEquals($data->user1->city, $enrolleduser['city']);
474                 $this->assertEquals($data->user1->country, $enrolleduser['country']);
475                 $this->assertEquals($data->user1->url, $enrolleduser['url']);
476                 if (!empty($CFG->usetags)) {
477                     $this->assertEquals(implode(', ', $data->user1->interests), $enrolleduser['interests']);
478                 }
479             }
480         }
481     }
483     /**
484      * Test create_users
485      */
486     public function test_create_users() {
487         global $DB;
489         $this->resetAfterTest(true);
491         $user1 = array(
492             'username' => 'usernametest1',
493             'password' => 'Moodle2012!',
494             'idnumber' => 'idnumbertest1',
495             'firstname' => 'First Name User Test 1',
496             'lastname' => 'Last Name User Test 1',
497             'middlename' => 'Middle Name User Test 1',
498             'lastnamephonetic' => '最後のお名前のテスト一号',
499             'firstnamephonetic' => 'お名前のテスト一号',
500             'alternatename' => 'Alternate Name User Test 1',
501             'email' => 'usertest1@example.com',
502             'description' => 'This is a description for user 1',
503             'city' => 'Perth',
504             'country' => 'AU',
505             'preferences' => [[
506                     'type' => 'htmleditor',
507                     'value' => 'atto'
508                 ], [
509                     'type' => 'invalidpreference',
510                     'value' => 'abcd'
511                 ]
512             ],
513             'department' => 'College of Science',
514             'institution' => 'National Institute of Physics',
515             'phone1' => '01 2345 6789',
516             'maildisplay' => 1,
517             'interests' => 'badminton, basketball, cooking,  '
518         );
520         // User with an authentication method done externally.
521         $user2 = array(
522             'username' => 'usernametest2',
523             'firstname' => 'First Name User Test 2',
524             'lastname' => 'Last Name User Test 2',
525             'email' => 'usertest2@example.com',
526             'auth' => 'oauth2'
527         );
529         $context = context_system::instance();
530         $roleid = $this->assignUserCapability('moodle/user:create', $context->id);
531         $this->assignUserCapability('moodle/user:editprofile', $context->id, $roleid);
533         // Call the external function.
534         $createdusers = core_user_external::create_users(array($user1, $user2));
536         // We need to execute the return values cleaning process to simulate the web service server.
537         $createdusers = external_api::clean_returnvalue(core_user_external::create_users_returns(), $createdusers);
539         // Check we retrieve the good total number of created users + no error on capability.
540         $this->assertCount(2, $createdusers);
542         foreach($createdusers as $createduser) {
543             $dbuser = $DB->get_record('user', array('id' => $createduser['id']));
545             if ($createduser['username'] === $user1['username']) {
546                 $usertotest = $user1;
547                 $this->assertEquals('atto', get_user_preferences('htmleditor', null, $dbuser));
548                 $this->assertEquals(null, get_user_preferences('invalidpreference', null, $dbuser));
549                 // Confirm user interests have been saved.
550                 $interests = core_tag_tag::get_item_tags_array('core', 'user', $createduser['id'],
551                         core_tag_tag::BOTH_STANDARD_AND_NOT, 0, false);
552                 // There should be 3 user interests.
553                 $this->assertCount(3, $interests);
555             } else if ($createduser['username'] === $user2['username']) {
556                 $usertotest = $user2;
557             }
559             foreach ($dbuser as $property => $value) {
560                 if ($property === 'password') {
561                     if ($usertotest === $user2) {
562                         // External auth mechanisms don't store password in the user table.
563                         $this->assertEquals(AUTH_PASSWORD_NOT_CACHED, $value);
564                     } else {
565                         // Skip hashed passwords.
566                         continue;
567                     }
568                 }
569                 // Confirm that the values match.
570                 if (isset($usertotest[$property])) {
571                     $this->assertEquals($usertotest[$property], $value);
572                 }
573             }
574         }
576         // Call without required capability
577         $this->unassignUserCapability('moodle/user:create', $context->id, $roleid);
578         $this->expectException('required_capability_exception');
579         core_user_external::create_users(array($user1));
580     }
582     /**
583      * Test create_users with password and createpassword parameter not set.
584      */
585     public function test_create_users_empty_password() {
586         $this->resetAfterTest();
587         $this->setAdminUser();
589         $user = [
590             'username' => 'usernametest1',
591             'firstname' => 'First Name User Test 1',
592             'lastname' => 'Last Name User Test 1',
593             'email' => 'usertest1@example.com',
594         ];
596         // This should throw an exception because either password or createpassword param must be passed for auth_manual.
597         $this->expectException(invalid_parameter_exception::class);
598         core_user_external::create_users([$user]);
599     }
601     /**
602      * Data provider for \core_user_externallib_testcase::test_create_users_with_same_emails().
603      */
604     public function create_users_provider_with_same_emails() {
605         return [
606             'Same emails allowed, same case' => [
607                 1, false
608             ],
609             'Same emails allowed, different case' => [
610                 1, true
611             ],
612             'Same emails disallowed, same case' => [
613                 0, false
614             ],
615             'Same emails disallowed, different case' => [
616                 0, true
617             ],
618         ];
619     }
621     /**
622      * Test for \core_user_external::create_users() when user using the same email addresses are being created.
623      *
624      * @dataProvider create_users_provider_with_same_emails
625      * @param int $sameemailallowed The value to set for $CFG->allowaccountssameemail.
626      * @param boolean $differentcase Whether to user a different case for the other user.
627      */
628     public function test_create_users_with_same_emails($sameemailallowed, $differentcase) {
629         global $DB;
631         $this->resetAfterTest();
632         $this->setAdminUser();
634         // Allow multiple users with the same email address.
635         set_config('allowaccountssameemail', $sameemailallowed);
636         $users = [
637             [
638                 'username' => 's1',
639                 'firstname' => 'Johnny',
640                 'lastname' => 'Bravo',
641                 'email' => 's1@example.com',
642                 'password' => 'Passw0rd!'
643             ],
644             [
645                 'username' => 's2',
646                 'firstname' => 'John',
647                 'lastname' => 'Doe',
648                 'email' => $differentcase ? 'S1@EXAMPLE.COM' : 's1@example.com',
649                 'password' => 'Passw0rd!'
650             ],
651         ];
653         if (!$sameemailallowed) {
654             // This should throw an exception when $CFG->allowaccountssameemail is empty.
655             $this->expectException(invalid_parameter_exception::class);
656         }
658         // Create our users.
659         core_user_external::create_users($users);
661         // Confirm that the users have been created.
662         list($insql, $params) = $DB->get_in_or_equal(['s1', 's2']);
663         $this->assertEquals(2, $DB->count_records_select('user', 'username ' . $insql, $params));
664     }
666     /**
667      * Test create_users with invalid parameters
668      *
669      * @dataProvider data_create_users_invalid_parameter
670      * @param array $data User data to attempt to register.
671      * @param string $expectmessage Expected exception message.
672      */
673     public function test_create_users_invalid_parameter(array $data, $expectmessage) {
674         global $USER, $CFG, $DB;
676         $this->resetAfterTest(true);
677         $this->assignUserCapability('moodle/user:create', SYSCONTEXTID);
679         $this->expectException('invalid_parameter_exception');
680         $this->expectExceptionMessage($expectmessage);
682         core_user_external::create_users(array($data));
683     }
685     /**
686      * Data provider for {@link self::test_create_users_invalid_parameter()}.
687      *
688      * @return array
689      */
690     public function data_create_users_invalid_parameter() {
691         return [
692             'blank_username' => [
693                 'data' => [
694                     'username' => '',
695                     'firstname' => 'Foo',
696                     'lastname' => 'Bar',
697                     'email' => 'foobar@example.com',
698                     'createpassword' => 1,
699                 ],
700                 'expectmessage' => 'The field username cannot be blank',
701             ],
702             'blank_firtname' => [
703                 'data' => [
704                     'username' => 'foobar',
705                     'firstname' => "\t \n",
706                     'lastname' => 'Bar',
707                     'email' => 'foobar@example.com',
708                     'createpassword' => 1,
709                 ],
710                 'expectmessage' => 'The field firstname cannot be blank',
711             ],
712             'blank_lastname' => [
713                 'data' => [
714                     'username' => 'foobar',
715                     'firstname' => '0',
716                     'lastname' => '   ',
717                     'email' => 'foobar@example.com',
718                     'createpassword' => 1,
719                 ],
720                 'expectmessage' => 'The field lastname cannot be blank',
721             ],
722             'invalid_email' => [
723                 'data' => [
724                     'username' => 'foobar',
725                     'firstname' => 'Foo',
726                     'lastname' => 'Bar',
727                     'email' => '@foobar',
728                     'createpassword' => 1,
729                 ],
730                 'expectmessage' => 'Email address is invalid',
731             ],
732             'missing_password' => [
733                 'data' => [
734                     'username' => 'foobar',
735                     'firstname' => 'Foo',
736                     'lastname' => 'Bar',
737                     'email' => 'foobar@example.com',
738                 ],
739                 'expectmessage' => 'Invalid password: you must provide a password, or set createpassword',
740             ],
741         ];
742     }
744     /**
745      * Test delete_users
746      */
747     public function test_delete_users() {
748         global $USER, $CFG, $DB;
750         $this->resetAfterTest(true);
752         $user1 = self::getDataGenerator()->create_user();
753         $user2 = self::getDataGenerator()->create_user();
755         // Check the users were correctly created.
756         $this->assertEquals(2, $DB->count_records_select('user', 'deleted = 0 AND (id = :userid1 OR id = :userid2)',
757                 array('userid1' => $user1->id, 'userid2' => $user2->id)));
759         $context = context_system::instance();
760         $roleid = $this->assignUserCapability('moodle/user:delete', $context->id);
762         // Call the external function.
763         core_user_external::delete_users(array($user1->id, $user2->id));
765         // Check we retrieve no users + no error on capability.
766         $this->assertEquals(0, $DB->count_records_select('user', 'deleted = 0 AND (id = :userid1 OR id = :userid2)',
767                 array('userid1' => $user1->id, 'userid2' => $user2->id)));
769         // Call without required capability.
770         $this->unassignUserCapability('moodle/user:delete', $context->id, $roleid);
771         $this->expectException('required_capability_exception');
772         core_user_external::delete_users(array($user1->id, $user2->id));
773     }
775     /**
776      * Test update_users
777      */
778     public function test_update_users() {
779         global $USER, $CFG, $DB;
781         $this->resetAfterTest(true);
783         $wsuser = self::getDataGenerator()->create_user();
784         self::setUser($wsuser);
786         $context = context_user::instance($USER->id);
787         $contextid = $context->id;
788         $filename = "reddot.png";
789         $filecontent = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38"
790             . "GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
792         // Call the files api to create a file.
793         $draftfile = core_files_external::upload($contextid, 'user', 'draft', 0, '/',
794                 $filename, $filecontent, null, null);
795         $draftfile = external_api::clean_returnvalue(core_files_external::upload_returns(), $draftfile);
797         $draftid = $draftfile['itemid'];
799         $user1 = self::getDataGenerator()->create_user();
801         $user1 = array(
802             'id' => $user1->id,
803             'username' => 'usernametest1',
804             'password' => 'Moodle2012!',
805             'idnumber' => 'idnumbertest1',
806             'firstname' => 'First Name User Test 1',
807             'lastname' => 'Last Name User Test 1',
808             'middlename' => 'Middle Name User Test 1',
809             'lastnamephonetic' => '最後のお名前のテスト一号',
810             'firstnamephonetic' => 'お名前のテスト一号',
811             'alternatename' => 'Alternate Name User Test 1',
812             'email' => 'usertest1@example.com',
813             'description' => 'This is a description for user 1',
814             'city' => 'Perth',
815             'userpicture' => $draftid,
816             'country' => 'AU',
817             'preferences' => [[
818                     'type' => 'htmleditor',
819                     'value' => 'atto'
820                 ], [
821                     'type' => 'invialidpreference',
822                     'value' => 'abcd'
823                 ]
824             ],
825             'department' => 'College of Science',
826             'institution' => 'National Institute of Physics',
827             'phone1' => '01 2345 6789',
828             'maildisplay' => 1,
829             'interests' => 'badminton, basketball, cooking,  '
830         );
832         $context = context_system::instance();
833         $roleid = $this->assignUserCapability('moodle/user:update', $context->id);
834         $this->assignUserCapability('moodle/user:editprofile', $context->id, $roleid);
836         // Check we can't update deleted users, guest users, site admin.
837         $user2 = $user3 = $user4 = $user1;
838         $user2['id'] = $CFG->siteguest;
840         $siteadmins = explode(',', $CFG->siteadmins);
841         $user3['id'] = array_shift($siteadmins);
843         $userdeleted = self::getDataGenerator()->create_user();
844         $user4['id'] = $userdeleted->id;
845         user_delete_user($userdeleted);
847         // Call the external function.
848         core_user_external::update_users(array($user1, $user2, $user3, $user4));
850         $dbuser2 = $DB->get_record('user', array('id' => $user2['id']));
851         $this->assertNotEquals($dbuser2->username, $user2['username']);
852         $dbuser3 = $DB->get_record('user', array('id' => $user3['id']));
853         $this->assertNotEquals($dbuser3->username, $user3['username']);
854         $dbuser4 = $DB->get_record('user', array('id' => $user4['id']));
855         $this->assertNotEquals($dbuser4->username, $user4['username']);
857         $dbuser = $DB->get_record('user', array('id' => $user1['id']));
858         $this->assertEquals($dbuser->username, $user1['username']);
859         $this->assertEquals($dbuser->idnumber, $user1['idnumber']);
860         $this->assertEquals($dbuser->firstname, $user1['firstname']);
861         $this->assertEquals($dbuser->lastname, $user1['lastname']);
862         $this->assertEquals($dbuser->email, $user1['email']);
863         $this->assertEquals($dbuser->description, $user1['description']);
864         $this->assertEquals($dbuser->city, $user1['city']);
865         $this->assertEquals($dbuser->country, $user1['country']);
866         $this->assertNotEquals(0, $dbuser->picture, 'Picture must be set to the new icon itemid for this user');
867         $this->assertEquals($dbuser->department, $user1['department']);
868         $this->assertEquals($dbuser->institution, $user1['institution']);
869         $this->assertEquals($dbuser->phone1, $user1['phone1']);
870         $this->assertEquals($dbuser->maildisplay, $user1['maildisplay']);
871         $this->assertEquals('atto', get_user_preferences('htmleditor', null, $dbuser));
872         $this->assertEquals(null, get_user_preferences('invalidpreference', null, $dbuser));
874         // Confirm user interests have been saved.
875         $interests = core_tag_tag::get_item_tags_array('core', 'user', $user1['id'], core_tag_tag::BOTH_STANDARD_AND_NOT, 0, false);
876         // There should be 3 user interests.
877         $this->assertCount(3, $interests);
879         // Confirm no picture change when parameter is not supplied.
880         unset($user1['userpicture']);
881         core_user_external::update_users(array($user1));
882         $dbusernopic = $DB->get_record('user', array('id' => $user1['id']));
883         $this->assertEquals($dbuser->picture, $dbusernopic->picture, 'Picture not change without the parameter.');
885         // Confirm delete of picture deletes the picture from the user record.
886         $user1['userpicture'] = 0;
887         core_user_external::update_users(array($user1));
888         $dbuserdelpic = $DB->get_record('user', array('id' => $user1['id']));
889         $this->assertEquals(0, $dbuserdelpic->picture, 'Picture must be deleted when sent as 0.');
892         // Call without required capability.
893         $this->unassignUserCapability('moodle/user:update', $context->id, $roleid);
894         $this->expectException('required_capability_exception');
895         core_user_external::update_users(array($user1));
896     }
898     /**
899      * Data provider for testing \core_user_external::update_users() for users with same emails
900      *
901      * @return array
902      */
903     public function users_with_same_emails() {
904         return [
905             'Same emails not allowed: Update name using exactly the same email' => [
906                 0, 'John', 's1@example.com', 'Johnny', 's1@example.com', false, true
907             ],
908             'Same emails not allowed: Update using someone else\'s email' => [
909                 0, 'John', 's1@example.com', 'Johnny', 's2@example.com', true, false
910             ],
911             'Same emails allowed: Update using someone else\'s email' => [
912                 1, 'John', 's1@example.com', 'Johnny', 's2@example.com', true, true
913             ],
914             'Same emails not allowed: Update using same email but with different case' => [
915                 0, 'John', 's1@example.com', 'Johnny', 'S1@EXAMPLE.COM', false, true
916             ],
917             'Same emails not allowed: Update using another user\'s email similar to user but with different case' => [
918                 0, 'John', 's1@example.com', 'Johnny', 'S1@EXAMPLE.COM', true, false
919             ],
920             'Same emails allowed: Update using another user\'s email similar to user but with different case' => [
921                 1, 'John', 's1@example.com', 'Johnny', 'S1@EXAMPLE.COM', true, true
922             ],
923         ];
924     }
926     /**
927      * Test update_users using similar emails with varying cases.
928      *
929      * @dataProvider users_with_same_emails
930      * @param boolean $allowsameemail The value to set for $CFG->allowaccountssameemail.
931      * @param string $currentname The user's current name.
932      * @param string $currentemail The user's current email.
933      * @param string $newname The user's new name.
934      * @param string $newemail The user's new email.
935      * @param boolean $withanotheruser Whether to create another user that has the same email as the target user's new email.
936      * @param boolean $successexpected Whether we expect that the target user's email/name will be updated.
937      */
938     public function test_update_users_emails_with_different_cases($allowsameemail, $currentname, $currentemail,
939                                                                   $newname, $newemail, $withanotheruser, $successexpected) {
940         global $DB;
942         $this->resetAfterTest();
943         $this->setAdminUser();
945         // Set the value for $CFG->allowaccountssameemail.
946         set_config('allowaccountssameemail', $allowsameemail);
948         $generator = self::getDataGenerator();
950         // Create the user that we wish to update.
951         $usertoupdate = $generator->create_user(['email' => $currentemail, 'firstname' => $currentname]);
953         if ($withanotheruser) {
954             // Create another user that has the same email as the new email that we'd like to update for our target user.
955             $generator->create_user(['email' => $newemail]);
956         }
958         // Build the user update parameters.
959         $updateparams = [
960             'id' => $usertoupdate->id,
961             'email' => $newemail,
962             'firstname' => $newname
963         ];
964         // Let's try to update the user's information.
965         core_user_external::update_users([$updateparams]);
967         // Fetch the updated user record.
968         $userrecord = $DB->get_record('user', ['id' => $usertoupdate->id], 'id, email, firstname');
970         // If we expect the update to succeed, then the email/name would have been changed.
971         if ($successexpected) {
972             $expectedemail = $newemail;
973             $expectedname = $newname;
974         } else {
975             $expectedemail = $currentemail;
976             $expectedname = $currentname;
977         }
978         // Confirm that our expectations are met.
979         $this->assertEquals($expectedemail, $userrecord->email);
980         $this->assertEquals($expectedname, $userrecord->firstname);
981     }
983     /**
984      * Test add_user_private_files
985      */
986     public function test_add_user_private_files() {
987         global $USER, $CFG, $DB;
989         $this->resetAfterTest(true);
991         $context = context_system::instance();
992         $roleid = $this->assignUserCapability('moodle/user:manageownfiles', $context->id);
994         $context = context_user::instance($USER->id);
995         $contextid = $context->id;
996         $component = "user";
997         $filearea = "draft";
998         $itemid = 0;
999         $filepath = "/";
1000         $filename = "Simple.txt";
1001         $filecontent = base64_encode("Let us create a nice simple file");
1002         $contextlevel = null;
1003         $instanceid = null;
1004         $browser = get_file_browser();
1006         // Call the files api to create a file.
1007         $draftfile = core_files_external::upload($contextid, $component, $filearea, $itemid, $filepath,
1008                                                  $filename, $filecontent, $contextlevel, $instanceid);
1009         $draftfile = external_api::clean_returnvalue(core_files_external::upload_returns(), $draftfile);
1011         $draftid = $draftfile['itemid'];
1012         // Make sure the file was created.
1013         $file = $browser->get_file_info($context, $component, $filearea, $draftid, $filepath, $filename);
1014         $this->assertNotEmpty($file);
1016         // Make sure the file does not exist in the user private files.
1017         $file = $browser->get_file_info($context, $component, 'private', 0, $filepath, $filename);
1018         $this->assertEmpty($file);
1020         // Call the external function.
1021         core_user_external::add_user_private_files($draftid);
1023         // Make sure the file was added to the user private files.
1024         $file = $browser->get_file_info($context, $component, 'private', 0, $filepath, $filename);
1025         $this->assertNotEmpty($file);
1026     }
1028     /**
1029      * Test add user device
1030      */
1031     public function test_add_user_device() {
1032         global $USER, $CFG, $DB;
1034         $this->resetAfterTest(true);
1036         $device = array(
1037                 'appid' => 'com.moodle.moodlemobile',
1038                 'name' => 'occam',
1039                 'model' => 'Nexus 4',
1040                 'platform' => 'Android',
1041                 'version' => '4.2.2',
1042                 'pushid' => 'apushdkasdfj4835',
1043                 'uuid' => 'asdnfl348qlksfaasef859'
1044                 );
1046         // Call the external function.
1047         core_user_external::add_user_device($device['appid'], $device['name'], $device['model'], $device['platform'],
1048                                             $device['version'], $device['pushid'], $device['uuid']);
1050         $created = $DB->get_record('user_devices', array('pushid' => $device['pushid']));
1051         $created = (array) $created;
1053         $this->assertEquals($device, array_intersect_key((array)$created, $device));
1055         // Test reuse the same pushid value.
1056         $warnings = core_user_external::add_user_device($device['appid'], $device['name'], $device['model'], $device['platform'],
1057                                                         $device['version'], $device['pushid'], $device['uuid']);
1058         // We need to execute the return values cleaning process to simulate the web service server.
1059         $warnings = external_api::clean_returnvalue(core_user_external::add_user_device_returns(), $warnings);
1060         $this->assertCount(1, $warnings);
1062         // Test update an existing device.
1063         $device['pushid'] = 'different than before';
1064         $warnings = core_user_external::add_user_device($device['appid'], $device['name'], $device['model'], $device['platform'],
1065                                                         $device['version'], $device['pushid'], $device['uuid']);
1066         $warnings = external_api::clean_returnvalue(core_user_external::add_user_device_returns(), $warnings);
1068         $this->assertEquals(1, $DB->count_records('user_devices'));
1069         $updated = $DB->get_record('user_devices', array('pushid' => $device['pushid']));
1070         $this->assertEquals($device, array_intersect_key((array)$updated, $device));
1072         // Test creating a new device just changing the uuid.
1073         $device['uuid'] = 'newuidforthesameuser';
1074         $device['pushid'] = 'new different than before';
1075         $warnings = core_user_external::add_user_device($device['appid'], $device['name'], $device['model'], $device['platform'],
1076                                                         $device['version'], $device['pushid'], $device['uuid']);
1077         $warnings = external_api::clean_returnvalue(core_user_external::add_user_device_returns(), $warnings);
1078         $this->assertEquals(2, $DB->count_records('user_devices'));
1079     }
1081     /**
1082      * Test remove user device
1083      */
1084     public function test_remove_user_device() {
1085         global $USER, $CFG, $DB;
1087         $this->resetAfterTest(true);
1089         $device = array(
1090                 'appid' => 'com.moodle.moodlemobile',
1091                 'name' => 'occam',
1092                 'model' => 'Nexus 4',
1093                 'platform' => 'Android',
1094                 'version' => '4.2.2',
1095                 'pushid' => 'apushdkasdfj4835',
1096                 'uuid' => 'ABCDE3723ksdfhasfaasef859'
1097                 );
1099         // A device with the same properties except the appid and pushid.
1100         $device2 = $device;
1101         $device2['pushid'] = "0987654321";
1102         $device2['appid'] = "other.app.com";
1104         $this->setAdminUser();
1105         // Create a user device using the external API function.
1106         core_user_external::add_user_device($device['appid'], $device['name'], $device['model'], $device['platform'],
1107                                             $device['version'], $device['pushid'], $device['uuid']);
1109         // Create the same device but for a different app.
1110         core_user_external::add_user_device($device2['appid'], $device2['name'], $device2['model'], $device2['platform'],
1111                                             $device2['version'], $device2['pushid'], $device2['uuid']);
1113         // Try to remove a device that does not exist.
1114         $result = core_user_external::remove_user_device('1234567890');
1115         $result = external_api::clean_returnvalue(core_user_external::remove_user_device_returns(), $result);
1116         $this->assertFalse($result['removed']);
1117         $this->assertCount(1, $result['warnings']);
1119         // Try to remove a device that does not exist for an existing app.
1120         $result = core_user_external::remove_user_device('1234567890', $device['appid']);
1121         $result = external_api::clean_returnvalue(core_user_external::remove_user_device_returns(), $result);
1122         $this->assertFalse($result['removed']);
1123         $this->assertCount(1, $result['warnings']);
1125         // Remove an existing device for an existing app. This will remove one of the two devices.
1126         $result = core_user_external::remove_user_device($device['uuid'], $device['appid']);
1127         $result = external_api::clean_returnvalue(core_user_external::remove_user_device_returns(), $result);
1128         $this->assertTrue($result['removed']);
1130         // Remove all the devices. This must remove the remaining device.
1131         $result = core_user_external::remove_user_device($device['uuid']);
1132         $result = external_api::clean_returnvalue(core_user_external::remove_user_device_returns(), $result);
1133         $this->assertTrue($result['removed']);
1134     }
1136     /**
1137      * Test get_user_preferences
1138      */
1139     public function test_get_user_preferences() {
1140         $this->resetAfterTest(true);
1142         $user = self::getDataGenerator()->create_user();
1143         set_user_preference('calendar_maxevents', 1, $user);
1144         set_user_preference('some_random_text', 'text', $user);
1146         $this->setUser($user);
1148         $result = core_user_external::get_user_preferences();
1149         $result = external_api::clean_returnvalue(core_user_external::get_user_preferences_returns(), $result);
1150         $this->assertCount(0, $result['warnings']);
1151         // Expect 3, _lastloaded is always returned.
1152         $this->assertCount(3, $result['preferences']);
1154         foreach ($result['preferences'] as $pref) {
1155             if ($pref['name'] === '_lastloaded') {
1156                 continue;
1157             }
1158             // Check we receive the expected preferences.
1159             $this->assertEquals(get_user_preferences($pref['name']), $pref['value']);
1160         }
1162         // Retrieve just one preference.
1163         $result = core_user_external::get_user_preferences('some_random_text');
1164         $result = external_api::clean_returnvalue(core_user_external::get_user_preferences_returns(), $result);
1165         $this->assertCount(0, $result['warnings']);
1166         $this->assertCount(1, $result['preferences']);
1167         $this->assertEquals('text', $result['preferences'][0]['value']);
1169         // Retrieve non-existent preference.
1170         $result = core_user_external::get_user_preferences('non_existent');
1171         $result = external_api::clean_returnvalue(core_user_external::get_user_preferences_returns(), $result);
1172         $this->assertCount(0, $result['warnings']);
1173         $this->assertCount(1, $result['preferences']);
1174         $this->assertEquals(null, $result['preferences'][0]['value']);
1176         // Check that as admin we can retrieve all the preferences for any user.
1177         $this->setAdminUser();
1178         $result = core_user_external::get_user_preferences('', $user->id);
1179         $result = external_api::clean_returnvalue(core_user_external::get_user_preferences_returns(), $result);
1180         $this->assertCount(0, $result['warnings']);
1181         $this->assertCount(3, $result['preferences']);
1183         foreach ($result['preferences'] as $pref) {
1184             if ($pref['name'] === '_lastloaded') {
1185                 continue;
1186             }
1187             // Check we receive the expected preferences.
1188             $this->assertEquals(get_user_preferences($pref['name'], null, $user), $pref['value']);
1189         }
1191         // Check that as a non admin user we cannot retrieve other users preferences.
1192         $anotheruser = self::getDataGenerator()->create_user();
1193         $this->setUser($anotheruser);
1195         $this->expectException('required_capability_exception');
1196         $result = core_user_external::get_user_preferences('', $user->id);
1197     }
1199     /**
1200      * Test update_picture
1201      */
1202     public function test_update_picture() {
1203         global $DB, $USER;
1205         $this->resetAfterTest(true);
1207         $user = self::getDataGenerator()->create_user();
1208         self::setUser($user);
1210         $context = context_user::instance($USER->id);
1211         $contextid = $context->id;
1212         $filename = "reddot.png";
1213         $filecontent = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38"
1214             . "GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
1216         // Call the files api to create a file.
1217         $draftfile = core_files_external::upload($contextid, 'user', 'draft', 0, '/', $filename, $filecontent, null, null);
1218         $draftid = $draftfile['itemid'];
1220         // Change user profile image.
1221         $result = core_user_external::update_picture($draftid);
1222         $result = external_api::clean_returnvalue(core_user_external::update_picture_returns(), $result);
1223         $picture = $DB->get_field('user', 'picture', array('id' => $user->id));
1224         // The new revision is in the url for the user.
1225         $this->assertContains($picture, $result['profileimageurl']);
1226         // Check expected URL for serving the image.
1227         $this->assertContains("/$contextid/user/icon", $result['profileimageurl']);
1229         // Delete image.
1230         $result = core_user_external::update_picture(0, true);
1231         $result = external_api::clean_returnvalue(core_user_external::update_picture_returns(), $result);
1232         $picture = $DB->get_field('user', 'picture', array('id' => $user->id));
1233         // No picture.
1234         $this->assertEquals(0, $picture);
1236         // Add again the user profile image (as admin).
1237         $this->setAdminUser();
1239         $context = context_user::instance($USER->id);
1240         $admincontextid = $context->id;
1241         $draftfile = core_files_external::upload($admincontextid, 'user', 'draft', 0, '/', $filename, $filecontent, null, null);
1242         $draftid = $draftfile['itemid'];
1244         $result = core_user_external::update_picture($draftid, false, $user->id);
1245         $result = external_api::clean_returnvalue(core_user_external::update_picture_returns(), $result);
1246         // The new revision is in the url for the user.
1247         $picture = $DB->get_field('user', 'picture', array('id' => $user->id));
1248         $this->assertContains($picture, $result['profileimageurl']);
1249         $this->assertContains("/$contextid/user/icon", $result['profileimageurl']);
1250     }
1252     /**
1253      * Test update_picture disabled
1254      */
1255     public function test_update_picture_disabled() {
1256         global $CFG;
1257         $this->resetAfterTest(true);
1258         $CFG->disableuserimages = true;
1260         $this->setAdminUser();
1261         $this->expectException('moodle_exception');
1262         core_user_external::update_picture(0);
1263     }
1265     /**
1266      * Test set_user_preferences
1267      */
1268     public function test_set_user_preferences_save() {
1269         global $DB;
1270         $this->resetAfterTest(true);
1272         $user1 = self::getDataGenerator()->create_user();
1273         $user2 = self::getDataGenerator()->create_user();
1275         // Save users preferences.
1276         $this->setAdminUser();
1277         $preferences = array(
1278             array(
1279                 'name' => 'htmleditor',
1280                 'value' => 'atto',
1281                 'userid' => $user1->id,
1282             ),
1283             array(
1284                 'name' => 'htmleditor',
1285                 'value' => 'tinymce',
1286                 'userid' => $user2->id,
1287             )
1288         );
1290         $result = core_user_external::set_user_preferences($preferences);
1291         $result = external_api::clean_returnvalue(core_user_external::set_user_preferences_returns(), $result);
1292         $this->assertCount(0, $result['warnings']);
1293         $this->assertCount(2, $result['saved']);
1295         // Get preference from DB to avoid cache.
1296         $this->assertEquals('atto', $DB->get_field('user_preferences', 'value',
1297             array('userid' => $user1->id, 'name' => 'htmleditor')));
1298         $this->assertEquals('tinymce', $DB->get_field('user_preferences', 'value',
1299             array('userid' => $user2->id, 'name' => 'htmleditor')));
1300     }
1302     /**
1303      * Test set_user_preferences
1304      */
1305     public function test_set_user_preferences_save_invalid_pref() {
1306         global $DB;
1307         $this->resetAfterTest(true);
1309         $user1 = self::getDataGenerator()->create_user();
1311         // Save users preferences.
1312         $this->setAdminUser();
1313         $preferences = array(
1314             array(
1315                 'name' => 'some_random_pref',
1316                 'value' => 'abc',
1317                 'userid' => $user1->id,
1318             ),
1319         );
1321         $result = core_user_external::set_user_preferences($preferences);
1322         $result = external_api::clean_returnvalue(core_user_external::set_user_preferences_returns(), $result);
1323         $this->assertCount(1, $result['warnings']);
1324         $this->assertCount(0, $result['saved']);
1325         $this->assertEquals('nopermission', $result['warnings'][0]['warningcode']);
1327         // Nothing was written to DB.
1328         $this->assertEmpty($DB->count_records('user_preferences', array('name' => 'some_random_pref')));
1329     }
1331     /**
1332      * Test set_user_preferences for an invalid user
1333      */
1334     public function test_set_user_preferences_invalid_user() {
1335         $this->resetAfterTest(true);
1337         $this->setAdminUser();
1338         $preferences = array(
1339             array(
1340                 'name' => 'calendar_maxevents',
1341                 'value' => 4,
1342                 'userid' => -2
1343             )
1344         );
1346         $result = core_user_external::set_user_preferences($preferences);
1347         $result = external_api::clean_returnvalue(core_user_external::set_user_preferences_returns(), $result);
1348         $this->assertCount(1, $result['warnings']);
1349         $this->assertCount(0, $result['saved']);
1350         $this->assertEquals('invaliduser', $result['warnings'][0]['warningcode']);
1351         $this->assertEquals(-2, $result['warnings'][0]['itemid']);
1352     }
1354     /**
1355      * Test set_user_preferences using an invalid preference
1356      */
1357     public function test_set_user_preferences_invalid_preference() {
1358         global $USER, $DB;
1360         $this->resetAfterTest(true);
1361         // Create a very long value.
1362         $this->setAdminUser();
1363         $preferences = array(
1364             array(
1365                 'name' => 'calendar_maxevents',
1366                 'value' => str_repeat('a', 1334),
1367                 'userid' => $USER->id
1368             )
1369         );
1371         $result = core_user_external::set_user_preferences($preferences);
1372         $result = external_api::clean_returnvalue(core_user_external::set_user_preferences_returns(), $result);
1373         $this->assertCount(0, $result['warnings']);
1374         $this->assertCount(1, $result['saved']);
1375         // Cleaned valud of the preference was saved.
1376         $this->assertEquals(1, $DB->get_field('user_preferences', 'value',
1377             array('userid' => $USER->id, 'name' => 'calendar_maxevents')));
1378     }
1380     /**
1381      * Test set_user_preferences for other user not being admin
1382      */
1383     public function test_set_user_preferences_capability() {
1384         $this->resetAfterTest(true);
1386         $user1 = self::getDataGenerator()->create_user();
1387         $user2 = self::getDataGenerator()->create_user();
1389         $this->setUser($user1);
1390         $preferences = array(
1391             array(
1392                 'name' => 'calendar_maxevents',
1393                 'value' => 4,
1394                 'userid' => $user2->id
1395             )
1396         );
1398         $result = core_user_external::set_user_preferences($preferences);
1400         $this->assertCount(1, $result['warnings']);
1401         $this->assertCount(0, $result['saved']);
1402         $this->assertEquals('nopermission', $result['warnings'][0]['warningcode']);
1403         $this->assertEquals($user2->id, $result['warnings'][0]['itemid']);
1404     }
1406     /**
1407      * Test update_user_preferences unsetting an existing preference.
1408      */
1409     public function test_update_user_preferences_unset() {
1410         global $DB;
1411         $this->resetAfterTest(true);
1413         $user = self::getDataGenerator()->create_user();
1415         // Save users preferences.
1416         $this->setAdminUser();
1417         $preferences = array(
1418             array(
1419                 'name' => 'htmleditor',
1420                 'value' => 'atto',
1421                 'userid' => $user->id,
1422             )
1423         );
1425         $result = core_user_external::set_user_preferences($preferences);
1426         $result = external_api::clean_returnvalue(core_user_external::set_user_preferences_returns(), $result);
1427         $this->assertCount(0, $result['warnings']);
1428         $this->assertCount(1, $result['saved']);
1430         // Get preference from DB to avoid cache.
1431         $this->assertEquals('atto', $DB->get_field('user_preferences', 'value',
1432             array('userid' => $user->id, 'name' => 'htmleditor')));
1434         // Now, unset.
1435         $result = core_user_external::update_user_preferences($user->id, null, array(array('type' => 'htmleditor')));
1437         $this->assertEquals(0, $DB->count_records('user_preferences', array('userid' => $user->id, 'name' => 'htmleditor')));
1438     }
1440     /**
1441      * Test agree_site_policy
1442      */
1443     public function test_agree_site_policy() {
1444         global $CFG, $DB, $USER;
1445         $this->resetAfterTest(true);
1447         $user = self::getDataGenerator()->create_user();
1448         $this->setUser($user);
1450         // Site policy not set.
1451         $result = core_user_external::agree_site_policy();
1452         $result = external_api::clean_returnvalue(core_user_external::agree_site_policy_returns(), $result);
1453         $this->assertFalse($result['status']);
1454         $this->assertCount(1, $result['warnings']);
1455         $this->assertEquals('nositepolicy', $result['warnings'][0]['warningcode']);
1457         // Set a policy issue.
1458         $CFG->sitepolicy = 'https://moodle.org';
1459         $this->assertEquals(0, $USER->policyagreed);
1461         $result = core_user_external::agree_site_policy();
1462         $result = external_api::clean_returnvalue(core_user_external::agree_site_policy_returns(), $result);
1463         $this->assertTrue($result['status']);
1464         $this->assertCount(0, $result['warnings']);
1465         $this->assertEquals(1, $USER->policyagreed);
1466         $this->assertEquals(1, $DB->get_field('user', 'policyagreed', array('id' => $USER->id)));
1468         // Try again, we should get a warning.
1469         $result = core_user_external::agree_site_policy();
1470         $result = external_api::clean_returnvalue(core_user_external::agree_site_policy_returns(), $result);
1471         $this->assertFalse($result['status']);
1472         $this->assertCount(1, $result['warnings']);
1473         $this->assertEquals('alreadyagreed', $result['warnings'][0]['warningcode']);
1475         // Set something to make require_login throws an exception.
1476         $otheruser = self::getDataGenerator()->create_user();
1477         $this->setUser($otheruser);
1479         $DB->set_field('user', 'lastname', '', array('id' => $USER->id));
1480         $USER->lastname = '';
1481         try {
1482             $result = core_user_external::agree_site_policy();
1483             $this->fail('Expecting \'usernotfullysetup\' moodle_exception to be thrown');
1484         } catch (moodle_exception $e) {
1485             $this->assertEquals('usernotfullysetup', $e->errorcode);
1486         } catch (Exception $e) {
1487             $this->fail('Expecting \'usernotfullysetup\' moodle_exception to be thrown.');
1488         }
1489     }
1491     /**
1492      * Test get_private_files_info
1493      */
1494     public function test_get_private_files_info() {
1496         $this->resetAfterTest(true);
1497         $user = self::getDataGenerator()->create_user();
1498         $this->setUser($user);
1499         $usercontext = context_user::instance($user->id);
1501         $filerecord = array(
1502             'contextid' => $usercontext->id,
1503             'component' => 'user',
1504             'filearea'  => 'private',
1505             'itemid'    => 0,
1506             'filepath'  => '/',
1507             'filename'  => 'thefile',
1508         );
1510         $fs = get_file_storage();
1511         $file = $fs->create_file_from_string($filerecord, 'abc');
1513         // Get my private files information.
1514         $result = core_user_external::get_private_files_info();
1515         $result = external_api::clean_returnvalue(core_user_external::get_private_files_info_returns(), $result);
1516         $this->assertEquals(1, $result['filecount']);
1517         $this->assertEquals($file->get_filesize(), $result['filesize']);
1518         $this->assertEquals(0, $result['foldercount']);
1519         $this->assertEquals($file->get_filesize(), $result['filesizewithoutreferences']);
1521         // As admin, get user information.
1522         $this->setAdminUser();
1523         $result = core_user_external::get_private_files_info($user->id);
1524         $result = external_api::clean_returnvalue(core_user_external::get_private_files_info_returns(), $result);
1525         $this->assertEquals(1, $result['filecount']);
1526         $this->assertEquals($file->get_filesize(), $result['filesize']);
1527         $this->assertEquals(0, $result['foldercount']);
1528         $this->assertEquals($file->get_filesize(), $result['filesizewithoutreferences']);
1529     }
1531     /**
1532      * Test get_private_files_info missing permissions.
1533      */
1534     public function test_get_private_files_info_missing_permissions() {
1536         $this->resetAfterTest(true);
1537         $user1 = self::getDataGenerator()->create_user();
1538         $user2 = self::getDataGenerator()->create_user();
1539         $this->setUser($user1);
1541         $this->expectException('required_capability_exception');
1542         // Try to retrieve other user private files info.
1543         core_user_external::get_private_files_info($user2->id);
1544     }