See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 402] [Versions 401 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * 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 */ 26 27 namespace core_user; 28 29 use core_files_external; 30 use core_user_external; 31 use externallib_advanced_testcase; 32 use external_api; 33 34 defined('MOODLE_INTERNAL') || die(); 35 36 global $CFG; 37 38 require_once($CFG->dirroot . '/webservice/tests/helpers.php'); 39 require_once($CFG->dirroot . '/user/externallib.php'); 40 require_once($CFG->dirroot . '/files/externallib.php'); 41 42 class externallib_test extends externallib_advanced_testcase { 43 44 /** 45 * Test get_users 46 */ 47 public function test_get_users() { 48 global $USER, $CFG; 49 50 $this->resetAfterTest(true); 51 52 $course = self::getDataGenerator()->create_course(); 53 54 $user1 = array( 55 'username' => 'usernametest1', 56 'idnumber' => 'idnumbertest1', 57 'firstname' => 'First Name User Test 1', 58 'lastname' => 'Last Name User Test 1', 59 'email' => 'usertest1@example.com', 60 'address' => '2 Test Street Perth 6000 WA', 61 'phone1' => '01010101010', 62 'phone2' => '02020203', 63 'department' => 'Department of user 1', 64 'institution' => 'Institution of user 1', 65 'description' => 'This is a description for user 1', 66 'descriptionformat' => FORMAT_MOODLE, 67 'city' => 'Perth', 68 'country' => 'AU' 69 ); 70 71 $user1 = self::getDataGenerator()->create_user($user1); 72 set_config('usetags', 1); 73 require_once($CFG->dirroot . '/user/editlib.php'); 74 $user1->interests = array('Cinema', 'Tennis', 'Dance', 'Guitar', 'Cooking'); 75 useredit_update_interests($user1, $user1->interests); 76 77 $user2 = self::getDataGenerator()->create_user( 78 array('username' => 'usernametest2', 'idnumber' => 'idnumbertest2')); 79 80 $generatedusers = array(); 81 $generatedusers[$user1->id] = $user1; 82 $generatedusers[$user2->id] = $user2; 83 84 $context = \context_course::instance($course->id); 85 $roleid = $this->assignUserCapability('moodle/user:viewdetails', $context->id); 86 87 // Enrol the users in the course. 88 $this->getDataGenerator()->enrol_user($user1->id, $course->id, $roleid); 89 $this->getDataGenerator()->enrol_user($user2->id, $course->id, $roleid); 90 $this->getDataGenerator()->enrol_user($USER->id, $course->id, $roleid); 91 92 // call as admin and receive all possible fields. 93 $this->setAdminUser(); 94 95 $searchparams = array( 96 array('key' => 'invalidkey', 'value' => 'invalidkey'), 97 array('key' => 'email', 'value' => $user1->email), 98 array('key' => 'firstname', 'value' => $user1->firstname)); 99 100 // Call the external function. 101 $result = core_user_external::get_users($searchparams); 102 103 // We need to execute the return values cleaning process to simulate the web service server 104 $result = \external_api::clean_returnvalue(core_user_external::get_users_returns(), $result); 105 106 // Check we retrieve the good total number of enrolled users + no error on capability. 107 $expectedreturnedusers = 1; 108 $returnedusers = $result['users']; 109 $this->assertEquals($expectedreturnedusers, count($returnedusers)); 110 111 foreach($returnedusers as $returneduser) { 112 $generateduser = ($returneduser['id'] == $USER->id) ? 113 $USER : $generatedusers[$returneduser['id']]; 114 $this->assertEquals($generateduser->username, $returneduser['username']); 115 if (!empty($generateduser->idnumber)) { 116 $this->assertEquals($generateduser->idnumber, $returneduser['idnumber']); 117 } 118 $this->assertEquals($generateduser->firstname, $returneduser['firstname']); 119 $this->assertEquals($generateduser->lastname, $returneduser['lastname']); 120 if ($generateduser->email != $USER->email) { // Don't check the tmp modified $USER email. 121 $this->assertEquals($generateduser->email, $returneduser['email']); 122 } 123 if (!empty($generateduser->address)) { 124 $this->assertEquals($generateduser->address, $returneduser['address']); 125 } 126 if (!empty($generateduser->phone1)) { 127 $this->assertEquals($generateduser->phone1, $returneduser['phone1']); 128 } 129 if (!empty($generateduser->phone2)) { 130 $this->assertEquals($generateduser->phone2, $returneduser['phone2']); 131 } 132 if (!empty($generateduser->department)) { 133 $this->assertEquals($generateduser->department, $returneduser['department']); 134 } 135 if (!empty($generateduser->institution)) { 136 $this->assertEquals($generateduser->institution, $returneduser['institution']); 137 } 138 if (!empty($generateduser->description)) { 139 $this->assertEquals($generateduser->description, $returneduser['description']); 140 } 141 if (!empty($generateduser->descriptionformat)) { 142 $this->assertEquals(FORMAT_HTML, $returneduser['descriptionformat']); 143 } 144 if (!empty($generateduser->city)) { 145 $this->assertEquals($generateduser->city, $returneduser['city']); 146 } 147 if (!empty($generateduser->country)) { 148 $this->assertEquals($generateduser->country, $returneduser['country']); 149 } 150 if (!empty($CFG->usetags) and !empty($generateduser->interests)) { 151 $this->assertEquals(implode(', ', $generateduser->interests), $returneduser['interests']); 152 } 153 } 154 155 // Test the invalid key warning. 156 $warnings = $result['warnings']; 157 $this->assertEquals(count($warnings), 1); 158 $warning = array_pop($warnings); 159 $this->assertEquals($warning['item'], 'invalidkey'); 160 $this->assertEquals($warning['warningcode'], 'invalidfieldparameter'); 161 162 // Test sending twice the same search field. 163 try { 164 $searchparams = array( 165 array('key' => 'firstname', 'value' => 'Canard'), 166 array('key' => 'email', 'value' => $user1->email), 167 array('key' => 'firstname', 'value' => $user1->firstname)); 168 169 // Call the external function. 170 $result = core_user_external::get_users($searchparams); 171 $this->fail('Expecting \'keyalreadyset\' moodle_exception to be thrown.'); 172 } catch (\moodle_exception $e) { 173 $this->assertEquals('keyalreadyset', $e->errorcode); 174 } catch (\Exception $e) { 175 $this->fail('Expecting \'keyalreadyset\' moodle_exception to be thrown.'); 176 } 177 } 178 179 /** 180 * Test get_users_by_field 181 */ 182 public function test_get_users_by_field() { 183 global $USER, $CFG; 184 185 $this->resetAfterTest(true); 186 187 $course = self::getDataGenerator()->create_course(); 188 $user1 = array( 189 'username' => 'usernametest1', 190 'idnumber' => 'idnumbertest1', 191 'firstname' => 'First Name User Test 1', 192 'lastname' => 'Last Name User Test 1', 193 'email' => 'usertest1@example.com', 194 'address' => '2 Test Street Perth 6000 WA', 195 'phone1' => '01010101010', 196 'phone2' => '02020203', 197 'department' => 'Department of user 1', 198 'institution' => 'Institution of user 1', 199 'description' => 'This is a description for user 1', 200 'descriptionformat' => FORMAT_MOODLE, 201 'city' => 'Perth', 202 'country' => 'AU', 203 ); 204 $user1 = self::getDataGenerator()->create_user($user1); 205 if (!empty($CFG->usetags)) { 206 require_once($CFG->dirroot . '/user/editlib.php'); 207 $user1->interests = array('Cinema', 'Tennis', 'Dance', 'Guitar', 'Cooking'); 208 useredit_update_interests($user1, $user1->interests); 209 } 210 $user2 = self::getDataGenerator()->create_user( 211 array('username' => 'usernametest2', 'idnumber' => 'idnumbertest2')); 212 213 $generatedusers = array(); 214 $generatedusers[$user1->id] = $user1; 215 $generatedusers[$user2->id] = $user2; 216 217 $context = \context_course::instance($course->id); 218 $roleid = $this->assignUserCapability('moodle/user:viewdetails', $context->id); 219 220 // Enrol the users in the course. 221 $this->getDataGenerator()->enrol_user($user1->id, $course->id, $roleid, 'manual'); 222 $this->getDataGenerator()->enrol_user($user2->id, $course->id, $roleid, 'manual'); 223 $this->getDataGenerator()->enrol_user($USER->id, $course->id, $roleid, 'manual'); 224 225 // call as admin and receive all possible fields. 226 $this->setAdminUser(); 227 228 $fieldstosearch = array('id', 'idnumber', 'username', 'email'); 229 230 foreach ($fieldstosearch as $fieldtosearch) { 231 232 // Call the external function. 233 $returnedusers = core_user_external::get_users_by_field($fieldtosearch, 234 array($USER->{$fieldtosearch}, $user1->{$fieldtosearch}, $user2->{$fieldtosearch})); 235 $returnedusers = \external_api::clean_returnvalue(core_user_external::get_users_by_field_returns(), $returnedusers); 236 237 // Expected result differ following the searched field 238 // Admin user in the PHPunit framework doesn't have an idnumber. 239 if ($fieldtosearch == 'idnumber') { 240 $expectedreturnedusers = 2; 241 } else { 242 $expectedreturnedusers = 3; 243 } 244 245 // Check we retrieve the good total number of enrolled users + no error on capability. 246 $this->assertEquals($expectedreturnedusers, count($returnedusers)); 247 248 foreach($returnedusers as $returneduser) { 249 $generateduser = ($returneduser['id'] == $USER->id) ? 250 $USER : $generatedusers[$returneduser['id']]; 251 $this->assertEquals($generateduser->username, $returneduser['username']); 252 if (!empty($generateduser->idnumber)) { 253 $this->assertEquals($generateduser->idnumber, $returneduser['idnumber']); 254 } 255 $this->assertEquals($generateduser->firstname, $returneduser['firstname']); 256 $this->assertEquals($generateduser->lastname, $returneduser['lastname']); 257 if ($generateduser->email != $USER->email) { //don't check the tmp modified $USER email 258 $this->assertEquals($generateduser->email, $returneduser['email']); 259 } 260 if (!empty($generateduser->address)) { 261 $this->assertEquals($generateduser->address, $returneduser['address']); 262 } 263 if (!empty($generateduser->phone1)) { 264 $this->assertEquals($generateduser->phone1, $returneduser['phone1']); 265 } 266 if (!empty($generateduser->phone2)) { 267 $this->assertEquals($generateduser->phone2, $returneduser['phone2']); 268 } 269 if (!empty($generateduser->department)) { 270 $this->assertEquals($generateduser->department, $returneduser['department']); 271 } 272 if (!empty($generateduser->institution)) { 273 $this->assertEquals($generateduser->institution, $returneduser['institution']); 274 } 275 if (!empty($generateduser->description)) { 276 $this->assertEquals($generateduser->description, $returneduser['description']); 277 } 278 if (!empty($generateduser->descriptionformat) and isset($returneduser['descriptionformat'])) { 279 $this->assertEquals($generateduser->descriptionformat, $returneduser['descriptionformat']); 280 } 281 if (!empty($generateduser->city)) { 282 $this->assertEquals($generateduser->city, $returneduser['city']); 283 } 284 if (!empty($generateduser->country)) { 285 $this->assertEquals($generateduser->country, $returneduser['country']); 286 } 287 if (!empty($CFG->usetags) and !empty($generateduser->interests)) { 288 $this->assertEquals(implode(', ', $generateduser->interests), $returneduser['interests']); 289 } 290 // Default language and no theme were used for the user. 291 $this->assertEquals($CFG->lang, $returneduser['lang']); 292 $this->assertEmpty($returneduser['theme']); 293 } 294 } 295 296 // Test that no result are returned for search by username if we are not admin 297 $this->setGuestUser(); 298 299 // Call the external function. 300 $returnedusers = core_user_external::get_users_by_field('username', 301 array($USER->username, $user1->username, $user2->username)); 302 $returnedusers = \external_api::clean_returnvalue(core_user_external::get_users_by_field_returns(), $returnedusers); 303 304 // Only the own $USER username should be returned 305 $this->assertEquals(1, count($returnedusers)); 306 307 // And finally test as one of the enrolled users. 308 $this->setUser($user1); 309 310 // Call the external function. 311 $returnedusers = core_user_external::get_users_by_field('username', 312 array($USER->username, $user1->username, $user2->username)); 313 $returnedusers = \external_api::clean_returnvalue(core_user_external::get_users_by_field_returns(), $returnedusers); 314 315 // Only the own $USER username should be returned still. 316 $this->assertEquals(1, count($returnedusers)); 317 } 318 319 public function get_course_user_profiles_setup($capability) { 320 global $USER, $CFG; 321 322 $this->resetAfterTest(true); 323 324 $return = new \stdClass(); 325 326 // Create the course and fetch its context. 327 $return->course = self::getDataGenerator()->create_course(); 328 $return->user1 = array( 329 'username' => 'usernametest1', 330 'idnumber' => 'idnumbertest1', 331 'firstname' => 'First Name User Test 1', 332 'lastname' => 'Last Name User Test 1', 333 'email' => 'usertest1@example.com', 334 'address' => '2 Test Street Perth 6000 WA', 335 'phone1' => '01010101010', 336 'phone2' => '02020203', 337 'department' => 'Department of user 1', 338 'institution' => 'Institution of user 1', 339 'description' => 'This is a description for user 1', 340 'descriptionformat' => FORMAT_MOODLE, 341 'city' => 'Perth', 342 'country' => 'AU' 343 ); 344 $return->user1 = self::getDataGenerator()->create_user($return->user1); 345 if (!empty($CFG->usetags)) { 346 require_once($CFG->dirroot . '/user/editlib.php'); 347 $return->user1->interests = array('Cinema', 'Tennis', 'Dance', 'Guitar', 'Cooking'); 348 useredit_update_interests($return->user1, $return->user1->interests); 349 } 350 $return->user2 = self::getDataGenerator()->create_user(); 351 352 $context = \context_course::instance($return->course->id); 353 $return->roleid = $this->assignUserCapability($capability, $context->id); 354 355 // Enrol the users in the course. 356 $this->getDataGenerator()->enrol_user($return->user1->id, $return->course->id, $return->roleid, 'manual'); 357 $this->getDataGenerator()->enrol_user($return->user2->id, $return->course->id, $return->roleid, 'manual'); 358 $this->getDataGenerator()->enrol_user($USER->id, $return->course->id, $return->roleid, 'manual'); 359 360 $group1 = $this->getDataGenerator()->create_group(['courseid' => $return->course->id, 'name' => 'G1']); 361 $group2 = $this->getDataGenerator()->create_group(['courseid' => $return->course->id, 'name' => 'G2']); 362 363 groups_add_member($group1->id, $return->user1->id); 364 groups_add_member($group2->id, $return->user2->id); 365 366 return $return; 367 } 368 369 /** 370 * Test get_course_user_profiles 371 */ 372 public function test_get_course_user_profiles() { 373 global $USER, $CFG; 374 375 $this->resetAfterTest(true); 376 377 $data = $this->get_course_user_profiles_setup('moodle/user:viewdetails'); 378 379 // Call the external function. 380 $enrolledusers = core_user_external::get_course_user_profiles(array( 381 array('userid' => $USER->id, 'courseid' => $data->course->id))); 382 383 // We need to execute the return values cleaning process to simulate the web service server. 384 $enrolledusers = \external_api::clean_returnvalue(core_user_external::get_course_user_profiles_returns(), $enrolledusers); 385 386 // Check we retrieve the good total number of enrolled users + no error on capability. 387 $this->assertEquals(1, count($enrolledusers)); 388 } 389 390 public function test_get_user_course_profile_as_admin() { 391 global $USER, $CFG; 392 393 global $USER, $CFG; 394 395 $this->resetAfterTest(true); 396 397 $data = $this->get_course_user_profiles_setup('moodle/user:viewdetails'); 398 399 // Do the same call as admin to receive all possible fields. 400 $this->setAdminUser(); 401 $USER->email = "admin@example.com"; 402 403 // Call the external function. 404 $enrolledusers = core_user_external::get_course_user_profiles(array( 405 array('userid' => $data->user1->id, 'courseid' => $data->course->id))); 406 407 // We need to execute the return values cleaning process to simulate the web service server. 408 $enrolledusers = \external_api::clean_returnvalue(core_user_external::get_course_user_profiles_returns(), $enrolledusers); 409 // Check we get the requested user and that is in a group. 410 $this->assertCount(1, $enrolledusers); 411 $this->assertCount(1, $enrolledusers[0]['groups']); 412 413 foreach($enrolledusers as $enrolleduser) { 414 if ($enrolleduser['username'] == $data->user1->username) { 415 $this->assertEquals($data->user1->idnumber, $enrolleduser['idnumber']); 416 $this->assertEquals($data->user1->firstname, $enrolleduser['firstname']); 417 $this->assertEquals($data->user1->lastname, $enrolleduser['lastname']); 418 $this->assertEquals($data->user1->email, $enrolleduser['email']); 419 $this->assertEquals($data->user1->address, $enrolleduser['address']); 420 $this->assertEquals($data->user1->phone1, $enrolleduser['phone1']); 421 $this->assertEquals($data->user1->phone2, $enrolleduser['phone2']); 422 $this->assertEquals($data->user1->department, $enrolleduser['department']); 423 $this->assertEquals($data->user1->institution, $enrolleduser['institution']); 424 $this->assertEquals($data->user1->description, $enrolleduser['description']); 425 $this->assertEquals(FORMAT_HTML, $enrolleduser['descriptionformat']); 426 $this->assertEquals($data->user1->city, $enrolleduser['city']); 427 $this->assertEquals($data->user1->country, $enrolleduser['country']); 428 if (!empty($CFG->usetags)) { 429 $this->assertEquals(implode(', ', $data->user1->interests), $enrolleduser['interests']); 430 } 431 } 432 } 433 } 434 435 /** 436 * Test create_users 437 */ 438 public function test_create_users() { 439 global $DB; 440 441 $this->resetAfterTest(true); 442 443 $user1 = array( 444 'username' => 'usernametest1', 445 'password' => 'Moodle2012!', 446 'idnumber' => 'idnumbertest1', 447 'firstname' => 'First Name User Test 1', 448 'lastname' => 'Last Name User Test 1', 449 'middlename' => 'Middle Name User Test 1', 450 'lastnamephonetic' => '最後のお名前のテスト一号', 451 'firstnamephonetic' => 'お名前のテスト一号', 452 'alternatename' => 'Alternate Name User Test 1', 453 'email' => 'usertest1@example.com', 454 'description' => 'This is a description for user 1', 455 'city' => 'Perth', 456 'country' => 'AU', 457 'preferences' => [[ 458 'type' => 'htmleditor', 459 'value' => 'atto' 460 ], [ 461 'type' => 'invalidpreference', 462 'value' => 'abcd' 463 ] 464 ], 465 'department' => 'College of Science', 466 'institution' => 'National Institute of Physics', 467 'phone1' => '01 2345 6789', 468 'maildisplay' => 1, 469 'interests' => 'badminton, basketball, cooking, ' 470 ); 471 472 // User with an authentication method done externally. 473 $user2 = array( 474 'username' => 'usernametest2', 475 'firstname' => 'First Name User Test 2', 476 'lastname' => 'Last Name User Test 2', 477 'email' => 'usertest2@example.com', 478 'auth' => 'oauth2' 479 ); 480 481 $context = \context_system::instance(); 482 $roleid = $this->assignUserCapability('moodle/user:create', $context->id); 483 $this->assignUserCapability('moodle/user:editprofile', $context->id, $roleid); 484 485 // Call the external function. 486 $createdusers = core_user_external::create_users(array($user1, $user2)); 487 488 // We need to execute the return values cleaning process to simulate the web service server. 489 $createdusers = \external_api::clean_returnvalue(core_user_external::create_users_returns(), $createdusers); 490 491 // Check we retrieve the good total number of created users + no error on capability. 492 $this->assertCount(2, $createdusers); 493 494 foreach($createdusers as $createduser) { 495 $dbuser = $DB->get_record('user', array('id' => $createduser['id'])); 496 497 if ($createduser['username'] === $user1['username']) { 498 $usertotest = $user1; 499 $this->assertEquals('atto', get_user_preferences('htmleditor', null, $dbuser)); 500 $this->assertEquals(null, get_user_preferences('invalidpreference', null, $dbuser)); 501 // Confirm user interests have been saved. 502 $interests = \core_tag_tag::get_item_tags_array('core', 'user', $createduser['id'], 503 \core_tag_tag::BOTH_STANDARD_AND_NOT, 0, false); 504 // There should be 3 user interests. 505 $this->assertCount(3, $interests); 506 507 } else if ($createduser['username'] === $user2['username']) { 508 $usertotest = $user2; 509 } 510 511 foreach ($dbuser as $property => $value) { 512 if ($property === 'password') { 513 if ($usertotest === $user2) { 514 // External auth mechanisms don't store password in the user table. 515 $this->assertEquals(AUTH_PASSWORD_NOT_CACHED, $value); 516 } else { 517 // Skip hashed passwords. 518 continue; 519 } 520 } 521 // Confirm that the values match. 522 if (isset($usertotest[$property])) { 523 $this->assertEquals($usertotest[$property], $value); 524 } 525 } 526 } 527 528 // Call without required capability 529 $this->unassignUserCapability('moodle/user:create', $context->id, $roleid); 530 $this->expectException('required_capability_exception'); 531 core_user_external::create_users(array($user1)); 532 } 533 534 /** 535 * Test create_users with password and createpassword parameter not set. 536 */ 537 public function test_create_users_empty_password() { 538 $this->resetAfterTest(); 539 $this->setAdminUser(); 540 541 $user = [ 542 'username' => 'usernametest1', 543 'firstname' => 'First Name User Test 1', 544 'lastname' => 'Last Name User Test 1', 545 'email' => 'usertest1@example.com', 546 ]; 547 548 // This should throw an exception because either password or createpassword param must be passed for auth_manual. 549 $this->expectException(\invalid_parameter_exception::class); 550 core_user_external::create_users([$user]); 551 } 552 553 /** 554 * Data provider for \core_user_externallib_testcase::test_create_users_with_same_emails(). 555 */ 556 public function create_users_provider_with_same_emails() { 557 return [ 558 'Same emails allowed, same case' => [ 559 1, false 560 ], 561 'Same emails allowed, different case' => [ 562 1, true 563 ], 564 'Same emails disallowed, same case' => [ 565 0, false 566 ], 567 'Same emails disallowed, different case' => [ 568 0, true 569 ], 570 ]; 571 } 572 573 /** 574 * Test for \core_user_external::create_users() when user using the same email addresses are being created. 575 * 576 * @dataProvider create_users_provider_with_same_emails 577 * @param int $sameemailallowed The value to set for $CFG->allowaccountssameemail. 578 * @param boolean $differentcase Whether to user a different case for the other user. 579 */ 580 public function test_create_users_with_same_emails($sameemailallowed, $differentcase) { 581 global $DB; 582 583 $this->resetAfterTest(); 584 $this->setAdminUser(); 585 586 // Allow multiple users with the same email address. 587 set_config('allowaccountssameemail', $sameemailallowed); 588 $users = [ 589 [ 590 'username' => 's1', 591 'firstname' => 'Johnny', 592 'lastname' => 'Bravo', 593 'email' => 's1@example.com', 594 'password' => 'Passw0rd!' 595 ], 596 [ 597 'username' => 's2', 598 'firstname' => 'John', 599 'lastname' => 'Doe', 600 'email' => $differentcase ? 'S1@EXAMPLE.COM' : 's1@example.com', 601 'password' => 'Passw0rd!' 602 ], 603 ]; 604 605 if (!$sameemailallowed) { 606 // This should throw an exception when $CFG->allowaccountssameemail is empty. 607 $this->expectException(\invalid_parameter_exception::class); 608 } 609 610 // Create our users. 611 core_user_external::create_users($users); 612 613 // Confirm that the users have been created. 614 list($insql, $params) = $DB->get_in_or_equal(['s1', 's2']); 615 $this->assertEquals(2, $DB->count_records_select('user', 'username ' . $insql, $params)); 616 } 617 618 /** 619 * Test create_users with invalid parameters 620 * 621 * @dataProvider data_create_users_invalid_parameter 622 * @param array $data User data to attempt to register. 623 * @param string $expectmessage Expected exception message. 624 */ 625 public function test_create_users_invalid_parameter(array $data, $expectmessage) { 626 global $USER, $CFG, $DB; 627 628 $this->resetAfterTest(true); 629 $this->assignUserCapability('moodle/user:create', SYSCONTEXTID); 630 631 $this->expectException('invalid_parameter_exception'); 632 $this->expectExceptionMessage($expectmessage); 633 634 core_user_external::create_users(array($data)); 635 } 636 637 /** 638 * Data provider for {@see self::test_create_users_invalid_parameter()}. 639 * 640 * @return array 641 */ 642 public function data_create_users_invalid_parameter() { 643 return [ 644 'blank_username' => [ 645 'data' => [ 646 'username' => '', 647 'firstname' => 'Foo', 648 'lastname' => 'Bar', 649 'email' => 'foobar@example.com', 650 'createpassword' => 1, 651 ], 652 'expectmessage' => 'The field username cannot be blank', 653 ], 654 'blank_firtname' => [ 655 'data' => [ 656 'username' => 'foobar', 657 'firstname' => "\t \n", 658 'lastname' => 'Bar', 659 'email' => 'foobar@example.com', 660 'createpassword' => 1, 661 ], 662 'expectmessage' => 'The field firstname cannot be blank', 663 ], 664 'blank_lastname' => [ 665 'data' => [ 666 'username' => 'foobar', 667 'firstname' => '0', 668 'lastname' => ' ', 669 'email' => 'foobar@example.com', 670 'createpassword' => 1, 671 ], 672 'expectmessage' => 'The field lastname cannot be blank', 673 ], 674 'invalid_email' => [ 675 'data' => [ 676 'username' => 'foobar', 677 'firstname' => 'Foo', 678 'lastname' => 'Bar', 679 'email' => '@foobar', 680 'createpassword' => 1, 681 ], 682 'expectmessage' => 'Email address is invalid', 683 ], 684 'missing_password' => [ 685 'data' => [ 686 'username' => 'foobar', 687 'firstname' => 'Foo', 688 'lastname' => 'Bar', 689 'email' => 'foobar@example.com', 690 ], 691 'expectmessage' => 'Invalid password: you must provide a password, or set createpassword', 692 ], 693 ]; 694 } 695 696 /** 697 * Test delete_users 698 */ 699 public function test_delete_users() { 700 global $USER, $CFG, $DB; 701 702 $this->resetAfterTest(true); 703 704 $user1 = self::getDataGenerator()->create_user(); 705 $user2 = self::getDataGenerator()->create_user(); 706 707 // Check the users were correctly created. 708 $this->assertEquals(2, $DB->count_records_select('user', 'deleted = 0 AND (id = :userid1 OR id = :userid2)', 709 array('userid1' => $user1->id, 'userid2' => $user2->id))); 710 711 $context = \context_system::instance(); 712 $roleid = $this->assignUserCapability('moodle/user:delete', $context->id); 713 714 // Call the external function. 715 core_user_external::delete_users(array($user1->id, $user2->id)); 716 717 // Check we retrieve no users + no error on capability. 718 $this->assertEquals(0, $DB->count_records_select('user', 'deleted = 0 AND (id = :userid1 OR id = :userid2)', 719 array('userid1' => $user1->id, 'userid2' => $user2->id))); 720 721 // Call without required capability. 722 $this->unassignUserCapability('moodle/user:delete', $context->id, $roleid); 723 $this->expectException('required_capability_exception'); 724 core_user_external::delete_users(array($user1->id, $user2->id)); 725 } 726 727 /** 728 * Test update_users 729 */ 730 public function test_update_users() { 731 global $USER, $CFG, $DB; 732 733 $this->resetAfterTest(true); 734 $this->preventResetByRollback(); 735 736 $wsuser = self::getDataGenerator()->create_user(); 737 self::setUser($wsuser); 738 739 $context = \context_user::instance($USER->id); 740 $contextid = $context->id; 741 $filename = "reddot.png"; 742 $filecontent = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38" 743 . "GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; 744 745 // Call the files api to create a file. 746 $draftfile = core_files_external::upload($contextid, 'user', 'draft', 0, '/', 747 $filename, $filecontent, null, null); 748 $draftfile = \external_api::clean_returnvalue(core_files_external::upload_returns(), $draftfile); 749 750 $draftid = $draftfile['itemid']; 751 752 $user1 = self::getDataGenerator()->create_user(); 753 754 $user1 = array( 755 'id' => $user1->id, 756 'username' => 'usernametest1', 757 'password' => 'Moodle2012!', 758 'idnumber' => 'idnumbertest1', 759 'firstname' => 'First Name User Test 1', 760 'lastname' => 'Last Name User Test 1', 761 'middlename' => 'Middle Name User Test 1', 762 'lastnamephonetic' => '最後のお名前のテスト一号', 763 'firstnamephonetic' => 'お名前のテスト一号', 764 'alternatename' => 'Alternate Name User Test 1', 765 'email' => 'usertest1@example.com', 766 'description' => 'This is a description for user 1', 767 'city' => 'Perth', 768 'userpicture' => $draftid, 769 'country' => 'AU', 770 'preferences' => [[ 771 'type' => 'htmleditor', 772 'value' => 'atto' 773 ], [ 774 'type' => 'invialidpreference', 775 'value' => 'abcd' 776 ] 777 ], 778 'department' => 'College of Science', 779 'institution' => 'National Institute of Physics', 780 'phone1' => '01 2345 6789', 781 'maildisplay' => 1, 782 'interests' => 'badminton, basketball, cooking, ' 783 ); 784 785 $context = \context_system::instance(); 786 $roleid = $this->assignUserCapability('moodle/user:update', $context->id); 787 $this->assignUserCapability('moodle/user:editprofile', $context->id, $roleid); 788 789 // Check we can't update deleted users, guest users, site admin. 790 $user2 = $user3 = $user4 = $user1; 791 $user2['id'] = $CFG->siteguest; 792 793 $siteadmins = explode(',', $CFG->siteadmins); 794 $user3['id'] = array_shift($siteadmins); 795 796 $userdeleted = self::getDataGenerator()->create_user(); 797 $user4['id'] = $userdeleted->id; 798 user_delete_user($userdeleted); 799 800 $user5 = self::getDataGenerator()->create_user(); 801 $user5 = array('id' => $user5->id, 'email' => $user5->email); 802 803 // Call the external function. 804 $returnvalue = core_user_external::update_users(array($user1, $user2, $user3, $user4)); 805 $returnvalue = \external_api::clean_returnvalue(core_user_external::update_users_returns(), $returnvalue); 806 807 // Check warnings. 808 $this->assertEquals($user2['id'], $returnvalue['warnings'][0]['itemid']); // Guest user. 809 $this->assertEquals('usernotupdatedguest', $returnvalue['warnings'][0]['warningcode']); 810 $this->assertEquals($user3['id'], $returnvalue['warnings'][1]['itemid']); // Admin user. 811 $this->assertEquals('usernotupdatedadmin', $returnvalue['warnings'][1]['warningcode']); 812 $this->assertEquals($user4['id'], $returnvalue['warnings'][2]['itemid']); // Deleted user. 813 $this->assertEquals('usernotupdateddeleted', $returnvalue['warnings'][2]['warningcode']); 814 815 $dbuser2 = $DB->get_record('user', array('id' => $user2['id'])); 816 $this->assertNotEquals($dbuser2->username, $user2['username']); 817 $dbuser3 = $DB->get_record('user', array('id' => $user3['id'])); 818 $this->assertNotEquals($dbuser3->username, $user3['username']); 819 $dbuser4 = $DB->get_record('user', array('id' => $user4['id'])); 820 $this->assertNotEquals($dbuser4->username, $user4['username']); 821 822 $dbuser = $DB->get_record('user', array('id' => $user1['id'])); 823 $this->assertEquals($dbuser->username, $user1['username']); 824 $this->assertEquals($dbuser->idnumber, $user1['idnumber']); 825 $this->assertEquals($dbuser->firstname, $user1['firstname']); 826 $this->assertEquals($dbuser->lastname, $user1['lastname']); 827 $this->assertEquals($dbuser->email, $user1['email']); 828 $this->assertEquals($dbuser->description, $user1['description']); 829 $this->assertEquals($dbuser->city, $user1['city']); 830 $this->assertEquals($dbuser->country, $user1['country']); 831 $this->assertNotEquals(0, $dbuser->picture, 'Picture must be set to the new icon itemid for this user'); 832 $this->assertEquals($dbuser->department, $user1['department']); 833 $this->assertEquals($dbuser->institution, $user1['institution']); 834 $this->assertEquals($dbuser->phone1, $user1['phone1']); 835 $this->assertEquals($dbuser->maildisplay, $user1['maildisplay']); 836 $this->assertEquals('atto', get_user_preferences('htmleditor', null, $dbuser)); 837 $this->assertEquals(null, get_user_preferences('invalidpreference', null, $dbuser)); 838 839 // Confirm user interests have been saved. 840 $interests = \core_tag_tag::get_item_tags_array('core', 'user', $user1['id'], \core_tag_tag::BOTH_STANDARD_AND_NOT, 0, false); 841 // There should be 3 user interests. 842 $this->assertCount(3, $interests); 843 844 // Confirm no picture change when parameter is not supplied. 845 unset($user1['userpicture']); 846 core_user_external::update_users(array($user1)); 847 $dbusernopic = $DB->get_record('user', array('id' => $user1['id'])); 848 $this->assertEquals($dbuser->picture, $dbusernopic->picture, 'Picture not change without the parameter.'); 849 850 // Confirm delete of picture deletes the picture from the user record. 851 $user1['userpicture'] = 0; 852 core_user_external::update_users(array($user1)); 853 $dbuserdelpic = $DB->get_record('user', array('id' => $user1['id'])); 854 $this->assertEquals(0, $dbuserdelpic->picture, 'Picture must be deleted when sent as 0.'); 855 856 // Updating user with an invalid email. 857 $user5['email'] = 'bogus'; 858 $returnvalue = core_user_external::update_users(array($user5)); 859 $returnvalue = \external_api::clean_returnvalue(core_user_external::update_users_returns(), $returnvalue); 860 $this->assertEquals('useremailinvalid', $returnvalue['warnings'][0]['warningcode']); 861 $this->assertStringContainsString('Invalid email address', 862 $returnvalue['warnings'][0]['message']); 863 864 // Updating user with a duplicate email. 865 $user5['email'] = $user1['email']; 866 $returnvalue = core_user_external::update_users(array($user1, $user5)); 867 $returnvalue = \external_api::clean_returnvalue(core_user_external::update_users_returns(), $returnvalue); 868 $this->assertEquals('useremailduplicate', $returnvalue['warnings'][0]['warningcode']); 869 $this->assertStringContainsString('Duplicate email address', 870 $returnvalue['warnings'][0]['message']); 871 872 // Updating a user that does not exist. 873 $user5['id'] = -1; 874 $returnvalue = core_user_external::update_users(array($user5)); 875 $returnvalue = \external_api::clean_returnvalue(core_user_external::update_users_returns(), $returnvalue); 876 $this->assertEquals('invaliduserid', $returnvalue['warnings'][0]['warningcode']); 877 $this->assertStringContainsString('Invalid user ID', 878 $returnvalue['warnings'][0]['message']); 879 880 // Updating a remote user. 881 $user1['mnethostid'] = 5; 882 user_update_user($user1); // Update user not using webservice. 883 unset($user1['mnethostid']); // The mnet host ID field is not in the allowed field list for the webservice. 884 $returnvalue = core_user_external::update_users(array($user1)); 885 $returnvalue = \external_api::clean_returnvalue(core_user_external::update_users_returns(), $returnvalue); 886 $this->assertEquals('usernotupdatedremote', $returnvalue['warnings'][0]['warningcode']); 887 $this->assertStringContainsString('User is a remote user', 888 $returnvalue['warnings'][0]['message']); 889 890 // Call without required capability. 891 $this->unassignUserCapability('moodle/user:update', $context->id, $roleid); 892 $this->expectException('required_capability_exception'); 893 core_user_external::update_users(array($user1)); 894 } 895 896 /** 897 * Data provider for testing \core_user_external::update_users() for users with same emails 898 * 899 * @return array 900 */ 901 public function users_with_same_emails() { 902 return [ 903 'Same emails not allowed: Update name using exactly the same email' => [ 904 0, 'John', 's1@example.com', 'Johnny', 's1@example.com', false, true 905 ], 906 'Same emails not allowed: Update using someone else\'s email' => [ 907 0, 'John', 's1@example.com', 'Johnny', 's2@example.com', true, false 908 ], 909 'Same emails allowed: Update using someone else\'s email' => [ 910 1, 'John', 's1@example.com', 'Johnny', 's2@example.com', true, true 911 ], 912 'Same emails not allowed: Update using same email but with different case' => [ 913 0, 'John', 's1@example.com', 'Johnny', 'S1@EXAMPLE.COM', false, true 914 ], 915 'Same emails not allowed: Update using another user\'s email similar to user but with different case' => [ 916 0, 'John', 's1@example.com', 'Johnny', 'S1@EXAMPLE.COM', true, false 917 ], 918 'Same emails allowed: Update using another user\'s email similar to user but with different case' => [ 919 1, 'John', 's1@example.com', 'Johnny', 'S1@EXAMPLE.COM', true, true 920 ], 921 ]; 922 } 923 924 /** 925 * Test update_users using similar emails with varying cases. 926 * 927 * @dataProvider users_with_same_emails 928 * @param boolean $allowsameemail The value to set for $CFG->allowaccountssameemail. 929 * @param string $currentname The user's current name. 930 * @param string $currentemail The user's current email. 931 * @param string $newname The user's new name. 932 * @param string $newemail The user's new email. 933 * @param boolean $withanotheruser Whether to create another user that has the same email as the target user's new email. 934 * @param boolean $successexpected Whether we expect that the target user's email/name will be updated. 935 */ 936 public function test_update_users_emails_with_different_cases($allowsameemail, $currentname, $currentemail, 937 $newname, $newemail, $withanotheruser, $successexpected) { 938 global $DB; 939 940 $this->resetAfterTest(); 941 $this->setAdminUser(); 942 943 // Set the value for $CFG->allowaccountssameemail. 944 set_config('allowaccountssameemail', $allowsameemail); 945 946 $generator = self::getDataGenerator(); 947 948 // Create the user that we wish to update. 949 $usertoupdate = $generator->create_user(['email' => $currentemail, 'firstname' => $currentname]); 950 951 if ($withanotheruser) { 952 // Create another user that has the same email as the new email that we'd like to update for our target user. 953 $generator->create_user(['email' => $newemail]); 954 } 955 956 // Build the user update parameters. 957 $updateparams = [ 958 'id' => $usertoupdate->id, 959 'email' => $newemail, 960 'firstname' => $newname 961 ]; 962 // Let's try to update the user's information. 963 core_user_external::update_users([$updateparams]); 964 965 // Fetch the updated user record. 966 $userrecord = $DB->get_record('user', ['id' => $usertoupdate->id], 'id, email, firstname'); 967 968 // If we expect the update to succeed, then the email/name would have been changed. 969 if ($successexpected) { 970 $expectedemail = $newemail; 971 $expectedname = $newname; 972 } else { 973 $expectedemail = $currentemail; 974 $expectedname = $currentname; 975 } 976 // Confirm that our expectations are met. 977 $this->assertEquals($expectedemail, $userrecord->email); 978 $this->assertEquals($expectedname, $userrecord->firstname); 979 } 980 981 /** 982 * Test add_user_private_files 983 */ 984 public function test_add_user_private_files() { 985 global $USER, $CFG, $DB; 986 987 $this->resetAfterTest(true); 988 989 $context = \context_system::instance(); 990 $roleid = $this->assignUserCapability('moodle/user:manageownfiles', $context->id); 991 992 $context = \context_user::instance($USER->id); 993 $contextid = $context->id; 994 $component = "user"; 995 $filearea = "draft"; 996 $itemid = 0; 997 $filepath = "/"; 998 $filename = "Simple.txt"; 999 $filecontent = base64_encode("Let us create a nice simple file"); 1000 $contextlevel = null; 1001 $instanceid = null; 1002 $browser = get_file_browser(); 1003 1004 // Call the files api to create a file. 1005 $draftfile = core_files_external::upload($contextid, $component, $filearea, $itemid, $filepath, 1006 $filename, $filecontent, $contextlevel, $instanceid); 1007 $draftfile = \external_api::clean_returnvalue(core_files_external::upload_returns(), $draftfile); 1008 1009 $draftid = $draftfile['itemid']; 1010 // Make sure the file was created. 1011 $file = $browser->get_file_info($context, $component, $filearea, $draftid, $filepath, $filename); 1012 $this->assertNotEmpty($file); 1013 1014 // Make sure the file does not exist in the user private files. 1015 $file = $browser->get_file_info($context, $component, 'private', 0, $filepath, $filename); 1016 $this->assertEmpty($file); 1017 1018 // Call the external function. 1019 core_user_external::add_user_private_files($draftid); 1020 1021 // Make sure the file was added to the user private files. 1022 $file = $browser->get_file_info($context, $component, 'private', 0, $filepath, $filename); 1023 $this->assertNotEmpty($file); 1024 } 1025 1026 1027 /** 1028 * Test add_user_private_files quota 1029 */ 1030 public function test_add_user_private_files_quota() { 1031 global $USER, $CFG, $DB; 1032 1033 $this->resetAfterTest(true); 1034 1035 $context = \context_system::instance(); 1036 $roleid = $this->assignUserCapability('moodle/user:manageownfiles', $context->id); 1037 1038 $context = \context_user::instance($USER->id); 1039 $contextid = $context->id; 1040 $component = "user"; 1041 $filearea = "draft"; 1042 $itemid = 0; 1043 $filepath = "/"; 1044 $filename = "Simple.txt"; 1045 $filecontent = base64_encode("Let us create a nice simple file"); 1046 $contextlevel = null; 1047 $instanceid = null; 1048 $browser = get_file_browser(); 1049 1050 // Call the files api to create a file. 1051 $draftfile = core_files_external::upload($contextid, $component, $filearea, $itemid, $filepath, 1052 $filename, $filecontent, $contextlevel, $instanceid); 1053 $draftfile = \external_api::clean_returnvalue(core_files_external::upload_returns(), $draftfile); 1054 $draftid = $draftfile['itemid']; 1055 1056 // Call the external function to add the file to private files. 1057 core_user_external::add_user_private_files($draftid); 1058 1059 // Force the quota so we are sure it won't be space to add the new file. 1060 $fileareainfo = file_get_file_area_info($contextid, 'user', 'private'); 1061 $CFG->userquota = $fileareainfo['filesize_without_references'] + 1; 1062 1063 // Generate a new draftitemid for the same testfile. 1064 $draftfile = core_files_external::upload($contextid, $component, $filearea, $itemid, $filepath, 1065 $filename, $filecontent, $contextlevel, $instanceid); 1066 $draftid = $draftfile['itemid']; 1067 1068 $this->expectException('moodle_exception'); 1069 $this->expectExceptionMessage(get_string('maxareabytes', 'error')); 1070 1071 // Call the external function to include the new file. 1072 core_user_external::add_user_private_files($draftid); 1073 } 1074 1075 /** 1076 * Test add user device 1077 */ 1078 public function test_add_user_device() { 1079 global $USER, $CFG, $DB; 1080 1081 $this->resetAfterTest(true); 1082 1083 $device = array( 1084 'appid' => 'com.moodle.moodlemobile', 1085 'name' => 'occam', 1086 'model' => 'Nexus 4', 1087 'platform' => 'Android', 1088 'version' => '4.2.2', 1089 'pushid' => 'apushdkasdfj4835', 1090 'uuid' => 'asdnfl348qlksfaasef859', 1091 'publickey' => null, 1092 ); 1093 1094 // Call the external function. 1095 core_user_external::add_user_device($device['appid'], $device['name'], $device['model'], $device['platform'], 1096 $device['version'], $device['pushid'], $device['uuid']); 1097 1098 $created = $DB->get_record('user_devices', array('pushid' => $device['pushid'])); 1099 $created = (array) $created; 1100 1101 $this->assertEquals($device, array_intersect_key((array)$created, $device)); 1102 1103 // Test reuse the same pushid value. 1104 $warnings = core_user_external::add_user_device($device['appid'], $device['name'], $device['model'], $device['platform'], 1105 $device['version'], $device['pushid'], $device['uuid']); 1106 // We need to execute the return values cleaning process to simulate the web service server. 1107 $warnings = \external_api::clean_returnvalue(core_user_external::add_user_device_returns(), $warnings); 1108 $this->assertCount(1, $warnings); 1109 1110 // Test update an existing device. 1111 $device['pushid'] = 'different than before'; 1112 $device['publickey'] = 'MFsxCzAJBgNVBAYTAkZSMRMwEQYDVQQ'; 1113 $warnings = core_user_external::add_user_device($device['appid'], $device['name'], $device['model'], $device['platform'], 1114 $device['version'], $device['pushid'], $device['uuid'], $device['publickey']); 1115 $warnings = external_api::clean_returnvalue(core_user_external::add_user_device_returns(), $warnings); 1116 1117 $this->assertEquals(1, $DB->count_records('user_devices')); 1118 $updated = $DB->get_record('user_devices', array('pushid' => $device['pushid'])); 1119 $this->assertEquals($device, array_intersect_key((array)$updated, $device)); 1120 1121 // Test creating a new device just changing the uuid. 1122 $device['uuid'] = 'newuidforthesameuser'; 1123 $device['pushid'] = 'new different than before'; 1124 $warnings = core_user_external::add_user_device($device['appid'], $device['name'], $device['model'], $device['platform'], 1125 $device['version'], $device['pushid'], $device['uuid']); 1126 $warnings = \external_api::clean_returnvalue(core_user_external::add_user_device_returns(), $warnings); 1127 $this->assertEquals(2, $DB->count_records('user_devices')); 1128 } 1129 1130 /** 1131 * Test remove user device 1132 */ 1133 public function test_remove_user_device() { 1134 global $USER, $CFG, $DB; 1135 1136 $this->resetAfterTest(true); 1137 1138 $device = array( 1139 'appid' => 'com.moodle.moodlemobile', 1140 'name' => 'occam', 1141 'model' => 'Nexus 4', 1142 'platform' => 'Android', 1143 'version' => '4.2.2', 1144 'pushid' => 'apushdkasdfj4835', 1145 'uuid' => 'ABCDE3723ksdfhasfaasef859' 1146 ); 1147 1148 // A device with the same properties except the appid and pushid. 1149 $device2 = $device; 1150 $device2['pushid'] = "0987654321"; 1151 $device2['appid'] = "other.app.com"; 1152 1153 $this->setAdminUser(); 1154 // Create a user device using the external API function. 1155 core_user_external::add_user_device($device['appid'], $device['name'], $device['model'], $device['platform'], 1156 $device['version'], $device['pushid'], $device['uuid']); 1157 1158 // Create the same device but for a different app. 1159 core_user_external::add_user_device($device2['appid'], $device2['name'], $device2['model'], $device2['platform'], 1160 $device2['version'], $device2['pushid'], $device2['uuid']); 1161 1162 // Try to remove a device that does not exist. 1163 $result = core_user_external::remove_user_device('1234567890'); 1164 $result = \external_api::clean_returnvalue(core_user_external::remove_user_device_returns(), $result); 1165 $this->assertFalse($result['removed']); 1166 $this->assertCount(1, $result['warnings']); 1167 1168 // Try to remove a device that does not exist for an existing app. 1169 $result = core_user_external::remove_user_device('1234567890', $device['appid']); 1170 $result = \external_api::clean_returnvalue(core_user_external::remove_user_device_returns(), $result); 1171 $this->assertFalse($result['removed']); 1172 $this->assertCount(1, $result['warnings']); 1173 1174 // Remove an existing device for an existing app. This will remove one of the two devices. 1175 $result = core_user_external::remove_user_device($device['uuid'], $device['appid']); 1176 $result = \external_api::clean_returnvalue(core_user_external::remove_user_device_returns(), $result); 1177 $this->assertTrue($result['removed']); 1178 1179 // Remove all the devices. This must remove the remaining device. 1180 $result = core_user_external::remove_user_device($device['uuid']); 1181 $result = \external_api::clean_returnvalue(core_user_external::remove_user_device_returns(), $result); 1182 $this->assertTrue($result['removed']); 1183 } 1184 1185 /** 1186 * Test get_user_preferences 1187 */ 1188 public function test_get_user_preferences() { 1189 $this->resetAfterTest(true); 1190 1191 $user = self::getDataGenerator()->create_user(); 1192 set_user_preference('calendar_maxevents', 1, $user); 1193 set_user_preference('some_random_text', 'text', $user); 1194 1195 $this->setUser($user); 1196 1197 $result = core_user_external::get_user_preferences(); 1198 $result = \external_api::clean_returnvalue(core_user_external::get_user_preferences_returns(), $result); 1199 $this->assertCount(0, $result['warnings']); 1200 // Expect 3, _lastloaded is always returned. 1201 $this->assertCount(3, $result['preferences']); 1202 1203 foreach ($result['preferences'] as $pref) { 1204 if ($pref['name'] === '_lastloaded') { 1205 continue; 1206 } 1207 // Check we receive the expected preferences. 1208 $this->assertEquals(get_user_preferences($pref['name']), $pref['value']); 1209 } 1210 1211 // Retrieve just one preference. 1212 $result = core_user_external::get_user_preferences('some_random_text'); 1213 $result = \external_api::clean_returnvalue(core_user_external::get_user_preferences_returns(), $result); 1214 $this->assertCount(0, $result['warnings']); 1215 $this->assertCount(1, $result['preferences']); 1216 $this->assertEquals('text', $result['preferences'][0]['value']); 1217 1218 // Retrieve non-existent preference. 1219 $result = core_user_external::get_user_preferences('non_existent'); 1220 $result = \external_api::clean_returnvalue(core_user_external::get_user_preferences_returns(), $result); 1221 $this->assertCount(0, $result['warnings']); 1222 $this->assertCount(1, $result['preferences']); 1223 $this->assertEquals(null, $result['preferences'][0]['value']); 1224 1225 // Check that as admin we can retrieve all the preferences for any user. 1226 $this->setAdminUser(); 1227 $result = core_user_external::get_user_preferences('', $user->id); 1228 $result = \external_api::clean_returnvalue(core_user_external::get_user_preferences_returns(), $result); 1229 $this->assertCount(0, $result['warnings']); 1230 $this->assertCount(3, $result['preferences']); 1231 1232 foreach ($result['preferences'] as $pref) { 1233 if ($pref['name'] === '_lastloaded') { 1234 continue; 1235 } 1236 // Check we receive the expected preferences. 1237 $this->assertEquals(get_user_preferences($pref['name'], null, $user), $pref['value']); 1238 } 1239 1240 // Check that as a non admin user we cannot retrieve other users preferences. 1241 $anotheruser = self::getDataGenerator()->create_user(); 1242 $this->setUser($anotheruser); 1243 1244 $this->expectException('required_capability_exception'); 1245 $result = core_user_external::get_user_preferences('', $user->id); 1246 } 1247 1248 /** 1249 * Test update_picture 1250 */ 1251 public function test_update_picture() { 1252 global $DB, $USER; 1253 1254 $this->resetAfterTest(true); 1255 1256 $user = self::getDataGenerator()->create_user(); 1257 self::setUser($user); 1258 1259 $context = \context_user::instance($USER->id); 1260 $contextid = $context->id; 1261 $filename = "reddot.png"; 1262 $filecontent = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38" 1263 . "GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; 1264 1265 // Call the files api to create a file. 1266 $draftfile = core_files_external::upload($contextid, 'user', 'draft', 0, '/', $filename, $filecontent, null, null); 1267 $draftid = $draftfile['itemid']; 1268 1269 // Change user profile image. 1270 $result = core_user_external::update_picture($draftid); 1271 $result = \external_api::clean_returnvalue(core_user_external::update_picture_returns(), $result); 1272 $picture = $DB->get_field('user', 'picture', array('id' => $user->id)); 1273 // The new revision is in the url for the user. 1274 $this->assertStringContainsString($picture, $result['profileimageurl']); 1275 // Check expected URL for serving the image. 1276 $this->assertStringContainsString("/$contextid/user/icon", $result['profileimageurl']); 1277 1278 // Delete image. 1279 $result = core_user_external::update_picture(0, true); 1280 $result = \external_api::clean_returnvalue(core_user_external::update_picture_returns(), $result); 1281 $picture = $DB->get_field('user', 'picture', array('id' => $user->id)); 1282 // No picture. 1283 $this->assertEquals(0, $picture); 1284 1285 // Add again the user profile image (as admin). 1286 $this->setAdminUser(); 1287 1288 $context = \context_user::instance($USER->id); 1289 $admincontextid = $context->id; 1290 $draftfile = core_files_external::upload($admincontextid, 'user', 'draft', 0, '/', $filename, $filecontent, null, null); 1291 $draftid = $draftfile['itemid']; 1292 1293 $result = core_user_external::update_picture($draftid, false, $user->id); 1294 $result = \external_api::clean_returnvalue(core_user_external::update_picture_returns(), $result); 1295 // The new revision is in the url for the user. 1296 $picture = $DB->get_field('user', 'picture', array('id' => $user->id)); 1297 $this->assertStringContainsString($picture, $result['profileimageurl']); 1298 $this->assertStringContainsString("/$contextid/user/icon", $result['profileimageurl']); 1299 } 1300 1301 /** 1302 * Test update_picture disabled 1303 */ 1304 public function test_update_picture_disabled() { 1305 global $CFG; 1306 $this->resetAfterTest(true); 1307 $CFG->disableuserimages = true; 1308 1309 $this->setAdminUser(); 1310 $this->expectException('moodle_exception'); 1311 core_user_external::update_picture(0); 1312 } 1313 1314 /** 1315 * Test set_user_preferences 1316 */ 1317 public function test_set_user_preferences_save() { 1318 global $DB; 1319 $this->resetAfterTest(true); 1320 1321 $user1 = self::getDataGenerator()->create_user(); 1322 $user2 = self::getDataGenerator()->create_user(); 1323 1324 // Save users preferences. 1325 $this->setAdminUser(); 1326 $preferences = array( 1327 array( 1328 'name' => 'htmleditor', 1329 'value' => 'atto', 1330 'userid' => $user1->id, 1331 ), 1332 array( 1333 'name' => 'htmleditor', 1334 'value' => 'tinymce', 1335 'userid' => $user2->id, 1336 ) 1337 ); 1338 1339 $result = core_user_external::set_user_preferences($preferences); 1340 $result = \external_api::clean_returnvalue(core_user_external::set_user_preferences_returns(), $result); 1341 $this->assertCount(0, $result['warnings']); 1342 $this->assertCount(2, $result['saved']); 1343 1344 // Get preference from DB to avoid cache. 1345 $this->assertEquals('atto', $DB->get_field('user_preferences', 'value', 1346 array('userid' => $user1->id, 'name' => 'htmleditor'))); 1347 $this->assertEquals('tinymce', $DB->get_field('user_preferences', 'value', 1348 array('userid' => $user2->id, 'name' => 'htmleditor'))); 1349 } 1350 1351 /** 1352 * Test set_user_preferences 1353 */ 1354 public function test_set_user_preferences_save_invalid_pref() { 1355 global $DB; 1356 $this->resetAfterTest(true); 1357 1358 $user1 = self::getDataGenerator()->create_user(); 1359 1360 // Save users preferences. 1361 $this->setAdminUser(); 1362 $preferences = array( 1363 array( 1364 'name' => 'some_random_pref', 1365 'value' => 'abc', 1366 'userid' => $user1->id, 1367 ), 1368 ); 1369 1370 $result = core_user_external::set_user_preferences($preferences); 1371 $result = \external_api::clean_returnvalue(core_user_external::set_user_preferences_returns(), $result); 1372 $this->assertCount(1, $result['warnings']); 1373 $this->assertCount(0, $result['saved']); 1374 $this->assertEquals('nopermission', $result['warnings'][0]['warningcode']); 1375 1376 // Nothing was written to DB. 1377 $this->assertEmpty($DB->count_records('user_preferences', array('name' => 'some_random_pref'))); 1378 } 1379 1380 /** 1381 * Test set_user_preferences for an invalid user 1382 */ 1383 public function test_set_user_preferences_invalid_user() { 1384 $this->resetAfterTest(true); 1385 1386 $this->setAdminUser(); 1387 $preferences = array( 1388 array( 1389 'name' => 'calendar_maxevents', 1390 'value' => 4, 1391 'userid' => -2 1392 ) 1393 ); 1394 1395 $result = core_user_external::set_user_preferences($preferences); 1396 $result = \external_api::clean_returnvalue(core_user_external::set_user_preferences_returns(), $result); 1397 $this->assertCount(1, $result['warnings']); 1398 $this->assertCount(0, $result['saved']); 1399 $this->assertEquals('invaliduser', $result['warnings'][0]['warningcode']); 1400 $this->assertEquals(-2, $result['warnings'][0]['itemid']); 1401 } 1402 1403 /** 1404 * Test set_user_preferences using an invalid preference 1405 */ 1406 public function test_set_user_preferences_invalid_preference() { 1407 global $USER, $DB; 1408 1409 $this->resetAfterTest(true); 1410 // Create a very long value. 1411 $this->setAdminUser(); 1412 $preferences = array( 1413 array( 1414 'name' => 'calendar_maxevents', 1415 'value' => str_repeat('a', 1334), 1416 'userid' => $USER->id 1417 ) 1418 ); 1419 1420 $result = core_user_external::set_user_preferences($preferences); 1421 $result = \external_api::clean_returnvalue(core_user_external::set_user_preferences_returns(), $result); 1422 $this->assertCount(0, $result['warnings']); 1423 $this->assertCount(1, $result['saved']); 1424 // Cleaned valud of the preference was saved. 1425 $this->assertEquals(1, $DB->get_field('user_preferences', 'value', 1426 array('userid' => $USER->id, 'name' => 'calendar_maxevents'))); 1427 } 1428 1429 /** 1430 * Test set_user_preferences for other user not being admin 1431 */ 1432 public function test_set_user_preferences_capability() { 1433 $this->resetAfterTest(true); 1434 1435 $user1 = self::getDataGenerator()->create_user(); 1436 $user2 = self::getDataGenerator()->create_user(); 1437 1438 $this->setUser($user1); 1439 $preferences = array( 1440 array( 1441 'name' => 'calendar_maxevents', 1442 'value' => 4, 1443 'userid' => $user2->id 1444 ) 1445 ); 1446 1447 $result = core_user_external::set_user_preferences($preferences); 1448 1449 $this->assertCount(1, $result['warnings']); 1450 $this->assertCount(0, $result['saved']); 1451 $this->assertEquals('nopermission', $result['warnings'][0]['warningcode']); 1452 $this->assertEquals($user2->id, $result['warnings'][0]['itemid']); 1453 } 1454 1455 /** 1456 * Test update_user_preferences unsetting an existing preference. 1457 */ 1458 public function test_update_user_preferences_unset() { 1459 global $DB; 1460 $this->resetAfterTest(true); 1461 1462 $user = self::getDataGenerator()->create_user(); 1463 1464 // Save users preferences. 1465 $this->setAdminUser(); 1466 $preferences = array( 1467 array( 1468 'name' => 'htmleditor', 1469 'value' => 'atto', 1470 'userid' => $user->id, 1471 ) 1472 ); 1473 1474 $result = core_user_external::set_user_preferences($preferences); 1475 $result = \external_api::clean_returnvalue(core_user_external::set_user_preferences_returns(), $result); 1476 $this->assertCount(0, $result['warnings']); 1477 $this->assertCount(1, $result['saved']); 1478 1479 // Get preference from DB to avoid cache. 1480 $this->assertEquals('atto', $DB->get_field('user_preferences', 'value', 1481 array('userid' => $user->id, 'name' => 'htmleditor'))); 1482 1483 // Now, unset. 1484 $result = core_user_external::update_user_preferences($user->id, null, array(array('type' => 'htmleditor'))); 1485 1486 $this->assertEquals(0, $DB->count_records('user_preferences', array('userid' => $user->id, 'name' => 'htmleditor'))); 1487 } 1488 1489 /** 1490 * Test agree_site_policy 1491 */ 1492 public function test_agree_site_policy() { 1493 global $CFG, $DB, $USER; 1494 $this->resetAfterTest(true); 1495 1496 $user = self::getDataGenerator()->create_user(); 1497 $this->setUser($user); 1498 1499 // Site policy not set. 1500 $result = core_user_external::agree_site_policy(); 1501 $result = \external_api::clean_returnvalue(core_user_external::agree_site_policy_returns(), $result); 1502 $this->assertFalse($result['status']); 1503 $this->assertCount(1, $result['warnings']); 1504 $this->assertEquals('nositepolicy', $result['warnings'][0]['warningcode']); 1505 1506 // Set a policy issue. 1507 $CFG->sitepolicy = 'https://moodle.org'; 1508 $this->assertEquals(0, $USER->policyagreed); 1509 1510 $result = core_user_external::agree_site_policy(); 1511 $result = \external_api::clean_returnvalue(core_user_external::agree_site_policy_returns(), $result); 1512 $this->assertTrue($result['status']); 1513 $this->assertCount(0, $result['warnings']); 1514 $this->assertEquals(1, $USER->policyagreed); 1515 $this->assertEquals(1, $DB->get_field('user', 'policyagreed', array('id' => $USER->id))); 1516 1517 // Try again, we should get a warning. 1518 $result = core_user_external::agree_site_policy(); 1519 $result = \external_api::clean_returnvalue(core_user_external::agree_site_policy_returns(), $result); 1520 $this->assertFalse($result['status']); 1521 $this->assertCount(1, $result['warnings']); 1522 $this->assertEquals('alreadyagreed', $result['warnings'][0]['warningcode']); 1523 1524 // Set something to make require_login throws an exception. 1525 $otheruser = self::getDataGenerator()->create_user(); 1526 $this->setUser($otheruser); 1527 1528 $DB->set_field('user', 'lastname', '', array('id' => $USER->id)); 1529 $USER->lastname = ''; 1530 try { 1531 $result = core_user_external::agree_site_policy(); 1532 $this->fail('Expecting \'usernotfullysetup\' moodle_exception to be thrown'); 1533 } catch (\moodle_exception $e) { 1534 $this->assertEquals('usernotfullysetup', $e->errorcode); 1535 } catch (\Exception $e) { 1536 $this->fail('Expecting \'usernotfullysetup\' moodle_exception to be thrown.'); 1537 } 1538 } 1539 1540 /** 1541 * Test get_private_files_info 1542 */ 1543 public function test_get_private_files_info() { 1544 1545 $this->resetAfterTest(true); 1546 $user = self::getDataGenerator()->create_user(); 1547 $this->setUser($user); 1548 $usercontext = \context_user::instance($user->id); 1549 1550 $filerecord = array( 1551 'contextid' => $usercontext->id, 1552 'component' => 'user', 1553 'filearea' => 'private', 1554 'itemid' => 0, 1555 'filepath' => '/', 1556 'filename' => 'thefile', 1557 ); 1558 1559 $fs = get_file_storage(); 1560 $file = $fs->create_file_from_string($filerecord, 'abc'); 1561 1562 // Get my private files information. 1563 $result = core_user_external::get_private_files_info(); 1564 $result = \external_api::clean_returnvalue(core_user_external::get_private_files_info_returns(), $result); 1565 $this->assertEquals(1, $result['filecount']); 1566 $this->assertEquals($file->get_filesize(), $result['filesize']); 1567 $this->assertEquals(0, $result['foldercount']); 1568 $this->assertEquals($file->get_filesize(), $result['filesizewithoutreferences']); 1569 1570 // As admin, get user information. 1571 $this->setAdminUser(); 1572 $result = core_user_external::get_private_files_info($user->id); 1573 $result = \external_api::clean_returnvalue(core_user_external::get_private_files_info_returns(), $result); 1574 $this->assertEquals(1, $result['filecount']); 1575 $this->assertEquals($file->get_filesize(), $result['filesize']); 1576 $this->assertEquals(0, $result['foldercount']); 1577 $this->assertEquals($file->get_filesize(), $result['filesizewithoutreferences']); 1578 } 1579 1580 /** 1581 * Test get_private_files_info missing permissions. 1582 */ 1583 public function test_get_private_files_info_missing_permissions() { 1584 1585 $this->resetAfterTest(true); 1586 $user1 = self::getDataGenerator()->create_user(); 1587 $user2 = self::getDataGenerator()->create_user(); 1588 $this->setUser($user1); 1589 1590 $this->expectException('required_capability_exception'); 1591 // Try to retrieve other user private files info. 1592 core_user_external::get_private_files_info($user2->id); 1593 } 1594 1595 /** 1596 * Test the functionality of the {@see \core_user\external\search_identity} class. 1597 */ 1598 public function test_external_search_identity() { 1599 global $CFG; 1600 1601 $this->resetAfterTest(true); 1602 $this->setAdminUser(); 1603 1604 $user1 = self::getDataGenerator()->create_user([ 1605 'firstname' => 'Firstone', 1606 'lastname' => 'Lastone', 1607 'username' => 'usernameone', 1608 'idnumber' => 'idnumberone', 1609 'email' => 'userone@example.com', 1610 'phone1' => 'tel1', 1611 'phone2' => 'tel2', 1612 'department' => 'Department Foo', 1613 'institution' => 'Institution Foo', 1614 'city' => 'City One', 1615 'country' => 'AU', 1616 ]); 1617 1618 $user2 = self::getDataGenerator()->create_user([ 1619 'firstname' => 'Firsttwo', 1620 'lastname' => 'Lasttwo', 1621 'username' => 'usernametwo', 1622 'idnumber' => 'idnumbertwo', 1623 'email' => 'usertwo@example.com', 1624 'phone1' => 'tel1', 1625 'phone2' => 'tel2', 1626 'department' => 'Department Foo', 1627 'institution' => 'Institution Foo', 1628 'city' => 'City One', 1629 'country' => 'AU', 1630 ]); 1631 1632 $user3 = self::getDataGenerator()->create_user([ 1633 'firstname' => 'Firstthree', 1634 'lastname' => 'Lastthree', 1635 'username' => 'usernamethree', 1636 'idnumber' => 'idnumberthree', 1637 'email' => 'userthree@example.com', 1638 'phone1' => 'tel1', 1639 'phone2' => 'tel2', 1640 'department' => 'Department Foo', 1641 'institution' => 'Institution Foo', 1642 'city' => 'City One', 1643 'country' => 'AU', 1644 ]); 1645 1646 $CFG->showuseridentity = 'email,idnumber,city'; 1647 $CFG->maxusersperpage = 3; 1648 1649 $result = \core_user\external\search_identity::execute('Lastt'); 1650 $result = \external_api::clean_returnvalue(\core_user\external\search_identity::execute_returns(), $result); 1651 1652 $this->assertEquals(2, count($result['list'])); 1653 $this->assertEquals(3, $result['maxusersperpage']); 1654 $this->assertEquals(false, $result['overflow']); 1655 1656 foreach ($result['list'] as $user) { 1657 $this->assertEquals(3, count($user['extrafields'])); 1658 $this->assertEquals('email', $user['extrafields'][0]['name']); 1659 $this->assertEquals('idnumber', $user['extrafields'][1]['name']); 1660 $this->assertEquals('city', $user['extrafields'][2]['name']); 1661 } 1662 1663 $CFG->showuseridentity = 'username'; 1664 $CFG->maxusersperpage = 2; 1665 1666 $result = \core_user\external\search_identity::execute('Firstt'); 1667 $result = \external_api::clean_returnvalue(\core_user\external\search_identity::execute_returns(), $result); 1668 1669 $this->assertEquals(2, count($result['list'])); 1670 $this->assertEquals(2, $result['maxusersperpage']); 1671 $this->assertEquals(false, $result['overflow']); 1672 1673 foreach ($result['list'] as $user) { 1674 $this->assertEquals(1, count($user['extrafields'])); 1675 $this->assertEquals('username', $user['extrafields'][0]['name']); 1676 } 1677 1678 $CFG->showuseridentity = 'email'; 1679 $CFG->maxusersperpage = 2; 1680 1681 $result = \core_user\external\search_identity::execute('City One'); 1682 $result = \external_api::clean_returnvalue(\core_user\external\search_identity::execute_returns(), $result); 1683 1684 $this->assertEquals(0, count($result['list'])); 1685 $this->assertEquals(2, $result['maxusersperpage']); 1686 $this->assertEquals(false, $result['overflow']); 1687 1688 $CFG->showuseridentity = 'city'; 1689 $CFG->maxusersperpage = 2; 1690 1691 foreach ($result['list'] as $user) { 1692 $this->assertEquals(1, count($user['extrafields'])); 1693 $this->assertEquals('username', $user['extrafields'][0]['name']); 1694 } 1695 1696 $result = \core_user\external\search_identity::execute('City One'); 1697 $result = \external_api::clean_returnvalue(\core_user\external\search_identity::execute_returns(), $result); 1698 1699 $this->assertEquals(2, count($result['list'])); 1700 $this->assertEquals(2, $result['maxusersperpage']); 1701 $this->assertEquals(true, $result['overflow']); 1702 } 1703 1704 /** 1705 * Test functionality of the {@see \core_user\external\search_identity} class with alternativefullnameformat defined. 1706 */ 1707 public function test_external_search_identity_with_alternativefullnameformat() { 1708 global $CFG; 1709 1710 $this->resetAfterTest(true); 1711 $this->setAdminUser(); 1712 1713 $user1 = self::getDataGenerator()->create_user([ 1714 'lastname' => '小柳', 1715 'lastnamephonetic' => 'Koyanagi', 1716 'firstname' => '秋', 1717 'firstnamephonetic' => 'Aki', 1718 'email' => 'koyanagiaki@example.com', 1719 'country' => 'JP', 1720 ]); 1721 1722 $CFG->showuseridentity = 'email'; 1723 $CFG->maxusersperpage = 3; 1724 $CFG->alternativefullnameformat = 1725 '<ruby>lastname firstname <rp>(</rp><rt>lastnamephonetic firstnamephonetic</rt><rp>)</rp></ruby>'; 1726 1727 $result = \core_user\external\search_identity::execute('Ak'); 1728 $result = \external_api::clean_returnvalue(\core_user\external\search_identity::execute_returns(), $result); 1729 1730 $this->assertEquals(1, count($result['list'])); 1731 $this->assertEquals(3, $result['maxusersperpage']); 1732 $this->assertEquals(false, $result['overflow']); 1733 1734 foreach ($result['list'] as $user) { 1735 $this->assertEquals(1, count($user['extrafields'])); 1736 $this->assertEquals('email', $user['extrafields'][0]['name']); 1737 } 1738 } 1739 1740 /** 1741 * Test verifying that update_user_preferences prevents changes to the default homepage for other users. 1742 */ 1743 public function test_update_user_preferences_homepage_permission_callback() { 1744 global $DB; 1745 $this->resetAfterTest(); 1746 1747 $user = self::getDataGenerator()->create_user(); 1748 $this->setUser($user); 1749 $adminuser = get_admin(); 1750 1751 // Allow user selection of the default homepage via preferences. 1752 set_config('defaulthomepage', HOMEPAGE_USER); 1753 1754 // Try to save another user's home page preference which uses the permissioncallback. 1755 $preferences = [ 1756 [ 1757 'name' => 'user_home_page_preference', 1758 'value' => '3', 1759 'userid' => $adminuser->id, 1760 ] 1761 ]; 1762 $result = core_user_external::set_user_preferences($preferences); 1763 $result = \external_api::clean_returnvalue(core_user_external::set_user_preferences_returns(), $result); 1764 $this->assertCount(1, $result['warnings']); 1765 $this->assertCount(0, $result['saved']); 1766 1767 // Verify no change to the preference, checking from DB to avoid cache. 1768 $this->assertEquals(null, $DB->get_field('user_preferences', 'value', 1769 ['userid' => $adminuser->id, 'name' => 'user_home_page_preference'])); 1770 } 1771 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body