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