Rev 1 | Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?php// This file is part of Moodle - http://moodle.org///// Moodle is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// Moodle is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with Moodle. If not, see <http://www.gnu.org/licenses/>./*** User external PHPunit tests** @package core_user* @category external* @copyright 2012 Jerome Mouneyrac* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later* @since Moodle 2.4*/namespace core_user;use core_external\external_api;use core_files_external;use core_user_external;use externallib_advanced_testcase;defined('MOODLE_INTERNAL') || die();global $CFG;require_once($CFG->dirroot . '/webservice/tests/helpers.php');require_once($CFG->dirroot . '/user/externallib.php');require_once($CFG->dirroot . '/files/externallib.php');/*** Tests for the user external functions.** @package core_user* @covers \core_user_external*/final class externallib_test extends externallib_advanced_testcase {/*** Test get_users*/public function test_get_users(): void {global $USER, $CFG;$this->resetAfterTest(true);$course = self::getDataGenerator()->create_course();$user1 = array('username' => 'usernametest1','idnumber' => 'idnumbertest1','firstname' => 'First Name User Test 1','lastname' => 'Last Name User Test 1','email' => 'usertest1@example.com','address' => '2 Test Street Perth 6000 WA','phone1' => '01010101010','phone2' => '02020203','department' => 'Department of user 1','institution' => 'Institution of user 1','description' => 'This is a description for user 1','descriptionformat' => FORMAT_MOODLE,'city' => 'Perth','country' => 'AU');$user1 = self::getDataGenerator()->create_user($user1);set_config('usetags', 1);require_once($CFG->dirroot . '/user/editlib.php');$user1->interests = array('Cinema', 'Tennis', 'Dance', 'Guitar', 'Cooking');useredit_update_interests($user1, $user1->interests);$user2 = self::getDataGenerator()->create_user(array('username' => 'usernametest2', 'idnumber' => 'idnumbertest2'));$generatedusers = array();$generatedusers[$user1->id] = $user1;$generatedusers[$user2->id] = $user2;$context = \context_course::instance($course->id);$roleid = $this->assignUserCapability('moodle/user:viewdetails', $context->id);// Enrol the users in the course.$this->getDataGenerator()->enrol_user($user1->id, $course->id, $roleid);$this->getDataGenerator()->enrol_user($user2->id, $course->id, $roleid);$this->getDataGenerator()->enrol_user($USER->id, $course->id, $roleid);// call as admin and receive all possible fields.$this->setAdminUser();$searchparams = array(array('key' => 'invalidkey', 'value' => 'invalidkey'),array('key' => 'email', 'value' => $user1->email),array('key' => 'firstname', 'value' => $user1->firstname));// Call the external function.$result = core_user_external::get_users($searchparams);// We need to execute the return values cleaning process to simulate the web service server$result = external_api::clean_returnvalue(core_user_external::get_users_returns(), $result);// Check we retrieve the good total number of enrolled users + no error on capability.$expectedreturnedusers = 1;$returnedusers = $result['users'];$this->assertEquals($expectedreturnedusers, count($returnedusers));foreach($returnedusers as $returneduser) {$generateduser = ($returneduser['id'] == $USER->id) ?$USER : $generatedusers[$returneduser['id']];$this->assertEquals($generateduser->username, $returneduser['username']);if (!empty($generateduser->idnumber)) {$this->assertEquals($generateduser->idnumber, $returneduser['idnumber']);}$this->assertEquals($generateduser->firstname, $returneduser['firstname']);$this->assertEquals($generateduser->lastname, $returneduser['lastname']);if ($generateduser->email != $USER->email) { // Don't check the tmp modified $USER email.$this->assertEquals($generateduser->email, $returneduser['email']);}if (!empty($generateduser->address)) {$this->assertEquals($generateduser->address, $returneduser['address']);}if (!empty($generateduser->phone1)) {$this->assertEquals($generateduser->phone1, $returneduser['phone1']);}if (!empty($generateduser->phone2)) {$this->assertEquals($generateduser->phone2, $returneduser['phone2']);}if (!empty($generateduser->department)) {$this->assertEquals($generateduser->department, $returneduser['department']);}if (!empty($generateduser->institution)) {$this->assertEquals($generateduser->institution, $returneduser['institution']);}if (!empty($generateduser->description)) {$this->assertEquals($generateduser->description, $returneduser['description']);}if (!empty($generateduser->descriptionformat)) {$this->assertEquals(FORMAT_HTML, $returneduser['descriptionformat']);}if (!empty($generateduser->city)) {$this->assertEquals($generateduser->city, $returneduser['city']);}if (!empty($generateduser->country)) {$this->assertEquals($generateduser->country, $returneduser['country']);}if (!empty($CFG->usetags) and !empty($generateduser->interests)) {$this->assertEquals(implode(', ', $generateduser->interests), $returneduser['interests']);}}// Test the invalid key warning.$warnings = $result['warnings'];$this->assertEquals(count($warnings), 1);$warning = array_pop($warnings);$this->assertEquals($warning['item'], 'invalidkey');$this->assertEquals($warning['warningcode'], 'invalidfieldparameter');// Test sending twice the same search field.try {$searchparams = array(array('key' => 'firstname', 'value' => 'Canard'),array('key' => 'email', 'value' => $user1->email),array('key' => 'firstname', 'value' => $user1->firstname));// Call the external function.$result = core_user_external::get_users($searchparams);$this->fail('Expecting \'keyalreadyset\' moodle_exception to be thrown.');} catch (\moodle_exception $e) {$this->assertEquals('keyalreadyset', $e->errorcode);} catch (\Exception $e) {$this->fail('Expecting \'keyalreadyset\' moodle_exception to be thrown.');}}/*** Test get_users_by_field*/public function test_get_users_by_field(): void {global $USER, $CFG;$this->resetAfterTest(true);$generator = self::getDataGenerator();// Create complex user profile field supporting multi-lang.filter_set_global_state('multilang', TEXTFILTER_ON);$name = '<span lang="en" class="multilang">Employment status</span>'.'<span lang="es" class="multilang">Estado de Empleo</span>';$statuses = 'UE\nSE\n<span lang="en" class="multilang">Other</span><span lang="es" class="multilang">Otro</span>';$generator->create_custom_profile_field(['datatype' => 'menu','shortname' => 'employmentstatus','name' => $name,'param1' => $statuses]);$course = $generator->create_course();$user1 = array('username' => 'usernametest1','idnumber' => 'idnumbertest1','firstname' => 'First Name User Test 1','lastname' => 'Last Name User Test 1','email' => 'usertest1@example.com','address' => '2 Test Street Perth 6000 WA','phone1' => '01010101010','phone2' => '02020203','department' => 'Department of user 1','institution' => 'Institution of user 1','description' => 'This is a description for user 1','descriptionformat' => FORMAT_MOODLE,'city' => 'Perth','country' => 'AU','profile_field_jobposition' => 'Manager','profile_field_employmentstatus' => explode('\n', $statuses)[2],);$user1 = $generator->create_user($user1);if (!empty($CFG->usetags)) {require_once($CFG->dirroot . '/user/editlib.php');$user1->interests = array('Cinema', 'Tennis', 'Dance', 'Guitar', 'Cooking');useredit_update_interests($user1, $user1->interests);}$user2 = $generator->create_user(array('username' => 'usernametest2', 'idnumber' => 'idnumbertest2'));$generatedusers = array();$generatedusers[$user1->id] = $user1;$generatedusers[$user2->id] = $user2;$context = \context_course::instance($course->id);$roleid = $this->assignUserCapability('moodle/user:viewdetails', $context->id);// Enrol the users in the course.$generator->enrol_user($user1->id, $course->id, $roleid, 'manual');$generator->enrol_user($user2->id, $course->id, $roleid, 'manual');$generator->enrol_user($USER->id, $course->id, $roleid, 'manual');// call as admin and receive all possible fields.$this->setAdminUser();$fieldstosearch = array('id', 'idnumber', 'username', 'email');foreach ($fieldstosearch as $fieldtosearch) {// Call the external function.$returnedusers = core_user_external::get_users_by_field($fieldtosearch,array($USER->{$fieldtosearch}, $user1->{$fieldtosearch}, $user2->{$fieldtosearch}));$returnedusers = external_api::clean_returnvalue(core_user_external::get_users_by_field_returns(), $returnedusers);// Expected result differ following the searched field// Admin user in the PHPunit framework doesn't have an idnumber.if ($fieldtosearch == 'idnumber') {$expectedreturnedusers = 2;} else {$expectedreturnedusers = 3;}// Check we retrieve the good total number of enrolled users + no error on capability.$this->assertEquals($expectedreturnedusers, count($returnedusers));foreach($returnedusers as $returneduser) {$generateduser = ($returneduser['id'] == $USER->id) ?$USER : $generatedusers[$returneduser['id']];$this->assertEquals($generateduser->username, $returneduser['username']);if (!empty($generateduser->idnumber)) {$this->assertEquals($generateduser->idnumber, $returneduser['idnumber']);}$this->assertEquals($generateduser->firstname, $returneduser['firstname']);$this->assertEquals($generateduser->lastname, $returneduser['lastname']);if ($generateduser->email != $USER->email) { //don't check the tmp modified $USER email$this->assertEquals($generateduser->email, $returneduser['email']);}if (!empty($generateduser->address)) {$this->assertEquals($generateduser->address, $returneduser['address']);}if (!empty($generateduser->phone1)) {$this->assertEquals($generateduser->phone1, $returneduser['phone1']);}if (!empty($generateduser->phone2)) {$this->assertEquals($generateduser->phone2, $returneduser['phone2']);}if (!empty($generateduser->department)) {$this->assertEquals($generateduser->department, $returneduser['department']);}if (!empty($generateduser->institution)) {$this->assertEquals($generateduser->institution, $returneduser['institution']);}if (!empty($generateduser->description)) {$this->assertEquals($generateduser->description, $returneduser['description']);}if (!empty($generateduser->descriptionformat) and isset($returneduser['descriptionformat'])) {$this->assertEquals($generateduser->descriptionformat, $returneduser['descriptionformat']);}if (!empty($generateduser->city)) {$this->assertEquals($generateduser->city, $returneduser['city']);}if (!empty($generateduser->country)) {$this->assertEquals($generateduser->country, $returneduser['country']);}if (!empty($CFG->usetags) and !empty($generateduser->interests)) {$this->assertEquals(implode(', ', $generateduser->interests), $returneduser['interests']);}// Default language and no theme were used for the user.$this->assertEquals($CFG->lang, $returneduser['lang']);$this->assertEquals($generateduser->trackforums, $returneduser['trackforums']);$this->assertEmpty($returneduser['theme']);if ($returneduser['id'] == $user1->id) {$this->assertCount(1, $returneduser['customfields']);$dbvalue = explode('\n', $statuses)[2];$this->assertEquals($dbvalue, $returneduser['customfields'][0]['value']);$this->assertEquals('Employment status', $returneduser['customfields'][0]['name']);$this->assertEquals('Other', $returneduser['customfields'][0]['displayvalue']);}}}// Test that no result are returned for search by username if we are not admin$this->setGuestUser();// Call the external function.$returnedusers = core_user_external::get_users_by_field('username',array($USER->username, $user1->username, $user2->username));$returnedusers = external_api::clean_returnvalue(core_user_external::get_users_by_field_returns(), $returnedusers);// Only the own $USER username should be returned$this->assertEquals(1, count($returnedusers));// And finally test as one of the enrolled users.$this->setUser($user1);// Call the external function.$returnedusers = core_user_external::get_users_by_field('username',array($USER->username, $user1->username, $user2->username));$returnedusers = external_api::clean_returnvalue(core_user_external::get_users_by_field_returns(), $returnedusers);// Only the own $USER username should be returned still.$this->assertEquals(1, count($returnedusers));}public function get_course_user_profiles_setup($capability) {global $USER, $CFG;$this->resetAfterTest(true);$return = new \stdClass();$generator = self::getDataGenerator();// Create complex user profile field supporting multi-lang.filter_set_global_state('multilang', TEXTFILTER_ON);$name = '<span lang="en" class="multilang">Employment status</span>' .'<span lang="es" class="multilang">Estado de Empleo</span>';$statuses = 'UE\nSE\n<span lang="en" class="multilang">Other</span><span lang="es" class="multilang">Otro</span>';$generator->create_custom_profile_field(['datatype' => 'menu','shortname' => 'employmentstatus','name' => $name,'param1' => $statuses,]);// Create the course and fetch its context.$return->course = self::getDataGenerator()->create_course();$return->user1 = array('username' => 'usernametest1','idnumber' => 'idnumbertest1','firstname' => 'First Name User Test 1','lastname' => 'Last Name User Test 1','email' => 'usertest1@example.com','address' => '2 Test Street Perth 6000 WA','phone1' => '01010101010','phone2' => '02020203','department' => 'Department of user 1','institution' => 'Institution of user 1','description' => 'This is a description for user 1','descriptionformat' => FORMAT_MOODLE,'city' => 'Perth','country' => 'AU','profile_field_employmentstatus' => explode('\n', $statuses)[2],);$return->user1 = self::getDataGenerator()->create_user($return->user1);if (!empty($CFG->usetags)) {require_once($CFG->dirroot . '/user/editlib.php');$return->user1->interests = array('Cinema', 'Tennis', 'Dance', 'Guitar', 'Cooking');useredit_update_interests($return->user1, $return->user1->interests);}$return->user2 = self::getDataGenerator()->create_user();$context = \context_course::instance($return->course->id);$return->roleid = $this->assignUserCapability($capability, $context->id);// Enrol the users in the course.$this->getDataGenerator()->enrol_user($return->user1->id, $return->course->id, $return->roleid, 'manual');$this->getDataGenerator()->enrol_user($return->user2->id, $return->course->id, $return->roleid, 'manual');$this->getDataGenerator()->enrol_user($USER->id, $return->course->id, $return->roleid, 'manual');$group1 = $this->getDataGenerator()->create_group(['courseid' => $return->course->id, 'name' => 'G1']);$group2 = $this->getDataGenerator()->create_group(['courseid' => $return->course->id, 'name' => 'G2']);groups_add_member($group1->id, $return->user1->id);groups_add_member($group2->id, $return->user2->id);return $return;}/*** Test get_course_user_profiles*/public function test_get_course_user_profiles(): void {global $USER, $CFG;$this->resetAfterTest(true);$data = $this->get_course_user_profiles_setup('moodle/user:viewdetails');// Call the external function.$enrolledusers = core_user_external::get_course_user_profiles(array(array('userid' => $USER->id, 'courseid' => $data->course->id)));// We need to execute the return values cleaning process to simulate the web service server.$enrolledusers = external_api::clean_returnvalue(core_user_external::get_course_user_profiles_returns(), $enrolledusers);// Check we retrieve the good total number of enrolled users + no error on capability.$this->assertEquals(1, count($enrolledusers));}public function test_get_user_course_profile_as_admin(): void {global $USER, $CFG;global $USER, $CFG;$this->resetAfterTest(true);$data = $this->get_course_user_profiles_setup('moodle/user:viewdetails');// Do the same call as admin to receive all possible fields.$this->setAdminUser();$USER->email = "admin@example.com";// Call the external function.$enrolledusers = core_user_external::get_course_user_profiles(array(array('userid' => $data->user1->id, 'courseid' => $data->course->id)));// We need to execute the return values cleaning process to simulate the web service server.$enrolledusers = external_api::clean_returnvalue(core_user_external::get_course_user_profiles_returns(), $enrolledusers);// Check we get the requested user and that is in a group.$this->assertCount(1, $enrolledusers);$this->assertCount(1, $enrolledusers[0]['groups']);foreach($enrolledusers as $enrolleduser) {if ($enrolleduser['username'] == $data->user1->username) {$this->assertEquals($data->user1->idnumber, $enrolleduser['idnumber']);$this->assertEquals($data->user1->firstname, $enrolleduser['firstname']);$this->assertEquals($data->user1->lastname, $enrolleduser['lastname']);$this->assertEquals($data->user1->email, $enrolleduser['email']);$this->assertEquals($data->user1->address, $enrolleduser['address']);$this->assertEquals($data->user1->phone1, $enrolleduser['phone1']);$this->assertEquals($data->user1->phone2, $enrolleduser['phone2']);$this->assertEquals($data->user1->department, $enrolleduser['department']);$this->assertEquals($data->user1->institution, $enrolleduser['institution']);$this->assertEquals($data->user1->description, $enrolleduser['description']);$this->assertEquals(FORMAT_HTML, $enrolleduser['descriptionformat']);$this->assertEquals($data->user1->city, $enrolleduser['city']);$this->assertEquals($data->user1->country, $enrolleduser['country']);// Default language was used for the user.$this->assertEquals($CFG->lang, $enrolleduser['lang']);$this->assertEquals('Employment status', $enrolleduser['customfields'][0]['name']);$this->assertEquals('Other', $enrolleduser['customfields'][0]['displayvalue']);if (!empty($CFG->usetags)) {$this->assertEquals(implode(', ', $data->user1->interests), $enrolleduser['interests']);}}}}/*** Test create_users*/public function test_create_users(): void {global $DB;$this->resetAfterTest(true);$user1 = array('username' => 'usernametest1','password' => 'Moodle2012!','idnumber' => 'idnumbertest1','firstname' => 'First Name User Test 1','lastname' => 'Last Name User Test 1','middlename' => 'Middle Name User Test 1','lastnamephonetic' => '最後のお名前のテスト一号','firstnamephonetic' => 'お名前のテスト一号','alternatename' => 'Alternate Name User Test 1','email' => 'usertest1@example.com','description' => 'This is a description for user 1','city' => 'Perth','country' => 'AU','preferences' => [['type' => 'htmleditor','value' => 'atto'], ['type' => 'invalidpreference','value' => 'abcd']],'department' => 'College of Science','institution' => 'National Institute of Physics','phone1' => '01 2345 6789','maildisplay' => 1,'interests' => 'badminton, basketball, cooking, ');// User with an authentication method done externally.$user2 = array('username' => 'usernametest2','firstname' => 'First Name User Test 2','lastname' => 'Last Name User Test 2','email' => 'usertest2@example.com','auth' => 'oauth2');$context = \context_system::instance();$roleid = $this->assignUserCapability('moodle/user:create', $context->id);$this->assignUserCapability('moodle/user:editprofile', $context->id, $roleid);// Call the external function.$createdusers = core_user_external::create_users(array($user1, $user2));// We need to execute the return values cleaning process to simulate the web service server.$createdusers = external_api::clean_returnvalue(core_user_external::create_users_returns(), $createdusers);// Check we retrieve the good total number of created users + no error on capability.$this->assertCount(2, $createdusers);foreach($createdusers as $createduser) {$dbuser = $DB->get_record('user', array('id' => $createduser['id']));if ($createduser['username'] === $user1['username']) {$usertotest = $user1;$this->assertEquals('atto', get_user_preferences('htmleditor', null, $dbuser));$this->assertEquals(null, get_user_preferences('invalidpreference', null, $dbuser));// Confirm user interests have been saved.$interests = \core_tag_tag::get_item_tags_array('core', 'user', $createduser['id'],\core_tag_tag::BOTH_STANDARD_AND_NOT, 0, false);// There should be 3 user interests.$this->assertCount(3, $interests);} else if ($createduser['username'] === $user2['username']) {$usertotest = $user2;}foreach ($dbuser as $property => $value) {if ($property === 'password') {if ($usertotest === $user2) {// External auth mechanisms don't store password in the user table.$this->assertEquals(AUTH_PASSWORD_NOT_CACHED, $value);} else {// Skip hashed passwords.continue;}}// Confirm that the values match.if (isset($usertotest[$property])) {$this->assertEquals($usertotest[$property], $value);}}}// Call without required capability$this->unassignUserCapability('moodle/user:create', $context->id, $roleid);$this->expectException('required_capability_exception');core_user_external::create_users(array($user1));}/*** Test create_users with password and createpassword parameter not set.*/public function test_create_users_empty_password(): void {$this->resetAfterTest();$this->setAdminUser();$user = ['username' => 'usernametest1','firstname' => 'First Name User Test 1','lastname' => 'Last Name User Test 1','email' => 'usertest1@example.com',];// This should throw an exception because either password or createpassword param must be passed for auth_manual.$this->expectException(\invalid_parameter_exception::class);core_user_external::create_users([$user]);}/*** Data provider for \core_user_externallib_testcase::test_create_users_with_same_emails().*/public function create_users_provider_with_same_emails() {return ['Same emails allowed, same case' => [1, false],'Same emails allowed, different case' => [1, true],'Same emails disallowed, same case' => [0, false],'Same emails disallowed, different case' => [0, true],];}/*** Test for \core_user_external::create_users() when user using the same email addresses are being created.** @dataProvider create_users_provider_with_same_emails* @param int $sameemailallowed The value to set for $CFG->allowaccountssameemail.* @param boolean $differentcase Whether to user a different case for the other user.*/public function test_create_users_with_same_emails($sameemailallowed, $differentcase): void {global $DB;$this->resetAfterTest();$this->setAdminUser();// Allow multiple users with the same email address.set_config('allowaccountssameemail', $sameemailallowed);$users = [['username' => 's1','firstname' => 'Johnny','lastname' => 'Bravo','email' => 's1@example.com','password' => 'Passw0rd!'],['username' => 's2','firstname' => 'John','lastname' => 'Doe','email' => $differentcase ? 'S1@EXAMPLE.COM' : 's1@example.com','password' => 'Passw0rd!'],];if (!$sameemailallowed) {// This should throw an exception when $CFG->allowaccountssameemail is empty.$this->expectException(\invalid_parameter_exception::class);}// Create our users.core_user_external::create_users($users);// Confirm that the users have been created.list($insql, $params) = $DB->get_in_or_equal(['s1', 's2']);$this->assertEquals(2, $DB->count_records_select('user', 'username ' . $insql, $params));}/*** Test create_users with invalid parameters** @dataProvider data_create_users_invalid_parameter* @param array $data User data to attempt to register.* @param string $expectmessage Expected exception message.*/public function test_create_users_invalid_parameter(array $data, $expectmessage): void {global $USER, $CFG, $DB;$this->resetAfterTest(true);$this->assignUserCapability('moodle/user:create', SYSCONTEXTID);$this->expectException('invalid_parameter_exception');$this->expectExceptionMessage($expectmessage);core_user_external::create_users(array($data));}/*** Data provider for {@see self::test_create_users_invalid_parameter()}.** @return array*/public function data_create_users_invalid_parameter() {return ['blank_username' => ['data' => ['username' => '','firstname' => 'Foo','lastname' => 'Bar','email' => 'foobar@example.com','createpassword' => 1,],'expectmessage' => 'The field username cannot be blank',],'blank_firtname' => ['data' => ['username' => 'foobar','firstname' => "\t \n",'lastname' => 'Bar','email' => 'foobar@example.com','createpassword' => 1,],'expectmessage' => 'The field firstname cannot be blank',],'blank_lastname' => ['data' => ['username' => 'foobar','firstname' => '0','lastname' => ' ','email' => 'foobar@example.com','createpassword' => 1,],'expectmessage' => 'The field lastname cannot be blank',],'invalid_email' => ['data' => ['username' => 'foobar','firstname' => 'Foo','lastname' => 'Bar','email' => '@foobar','createpassword' => 1,],'expectmessage' => 'Email address is invalid',],'missing_password' => ['data' => ['username' => 'foobar','firstname' => 'Foo','lastname' => 'Bar','email' => 'foobar@example.com',],'expectmessage' => 'Invalid password: you must provide a password, or set createpassword',],];}/*** Test delete_users*/public function test_delete_users(): void {global $USER, $CFG, $DB;$this->resetAfterTest(true);$user1 = self::getDataGenerator()->create_user();$user2 = self::getDataGenerator()->create_user();// Check the users were correctly created.$this->assertEquals(2, $DB->count_records_select('user', 'deleted = 0 AND (id = :userid1 OR id = :userid2)',array('userid1' => $user1->id, 'userid2' => $user2->id)));$context = \context_system::instance();$roleid = $this->assignUserCapability('moodle/user:delete', $context->id);// Call the external function.core_user_external::delete_users(array($user1->id, $user2->id));// Check we retrieve no users + no error on capability.$this->assertEquals(0, $DB->count_records_select('user', 'deleted = 0 AND (id = :userid1 OR id = :userid2)',array('userid1' => $user1->id, 'userid2' => $user2->id)));// Call without required capability.$this->unassignUserCapability('moodle/user:delete', $context->id, $roleid);$this->expectException('required_capability_exception');core_user_external::delete_users(array($user1->id, $user2->id));}/*** Test update_users*/public function test_update_users(): void {global $USER, $CFG, $DB;$this->resetAfterTest(true);$this->preventResetByRollback();$wsuser = self::getDataGenerator()->create_user();self::setUser($wsuser);$context = \context_user::instance($USER->id);$contextid = $context->id;$filename = "reddot.png";$filecontent = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38". "GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";// Call the files api to create a file.$draftfile = core_files_external::upload($contextid, 'user', 'draft', 0, '/',$filename, $filecontent, null, null);$draftfile = external_api::clean_returnvalue(core_files_external::upload_returns(), $draftfile);$draftid = $draftfile['itemid'];$user1 = self::getDataGenerator()->create_user();$user1 = array('id' => $user1->id,'username' => 'usernametest1','password' => 'Moodle2012!','idnumber' => 'idnumbertest1','firstname' => 'First Name User Test 1','lastname' => 'Last Name User Test 1','middlename' => 'Middle Name User Test 1','lastnamephonetic' => '最後のお名前のテスト一号','firstnamephonetic' => 'お名前のテスト一号','alternatename' => 'Alternate Name User Test 1','email' => 'usertest1@example.com','description' => 'This is a description for user 1','city' => 'Perth','userpicture' => $draftid,'country' => 'AU','preferences' => [['type' => 'htmleditor','value' => 'atto'], ['type' => 'invialidpreference','value' => 'abcd']],'department' => 'College of Science','institution' => 'National Institute of Physics','phone1' => '01 2345 6789','maildisplay' => 1,'interests' => 'badminton, basketball, cooking, ');$context = \context_system::instance();$roleid = $this->assignUserCapability('moodle/user:update', $context->id);$this->assignUserCapability('moodle/user:editprofile', $context->id, $roleid);// Check we can't update deleted users, guest users, site admin.$user2 = $user3 = $user4 = $user1;$user2['id'] = $CFG->siteguest;$siteadmins = explode(',', $CFG->siteadmins);$user3['id'] = array_shift($siteadmins);$userdeleted = self::getDataGenerator()->create_user();$user4['id'] = $userdeleted->id;user_delete_user($userdeleted);$user5 = self::getDataGenerator()->create_user();$user5 = array('id' => $user5->id, 'email' => $user5->email);// Call the external function.$returnvalue = core_user_external::update_users(array($user1, $user2, $user3, $user4));$returnvalue = external_api::clean_returnvalue(core_user_external::update_users_returns(), $returnvalue);// Check warnings.$this->assertEquals($user2['id'], $returnvalue['warnings'][0]['itemid']); // Guest user.$this->assertEquals('usernotupdatedguest', $returnvalue['warnings'][0]['warningcode']);$this->assertEquals($user3['id'], $returnvalue['warnings'][1]['itemid']); // Admin user.$this->assertEquals('usernotupdatedadmin', $returnvalue['warnings'][1]['warningcode']);$this->assertEquals($user4['id'], $returnvalue['warnings'][2]['itemid']); // Deleted user.$this->assertEquals('usernotupdateddeleted', $returnvalue['warnings'][2]['warningcode']);$dbuser2 = $DB->get_record('user', array('id' => $user2['id']));$this->assertNotEquals($dbuser2->username, $user2['username']);$dbuser3 = $DB->get_record('user', array('id' => $user3['id']));$this->assertNotEquals($dbuser3->username, $user3['username']);$dbuser4 = $DB->get_record('user', array('id' => $user4['id']));$this->assertNotEquals($dbuser4->username, $user4['username']);$dbuser = $DB->get_record('user', array('id' => $user1['id']));$this->assertEquals($dbuser->username, $user1['username']);$this->assertEquals($dbuser->idnumber, $user1['idnumber']);$this->assertEquals($dbuser->firstname, $user1['firstname']);$this->assertEquals($dbuser->lastname, $user1['lastname']);$this->assertEquals($dbuser->email, $user1['email']);$this->assertEquals($dbuser->description, $user1['description']);$this->assertEquals($dbuser->city, $user1['city']);$this->assertEquals($dbuser->country, $user1['country']);$this->assertNotEquals(0, $dbuser->picture, 'Picture must be set to the new icon itemid for this user');$this->assertEquals($dbuser->department, $user1['department']);$this->assertEquals($dbuser->institution, $user1['institution']);$this->assertEquals($dbuser->phone1, $user1['phone1']);$this->assertEquals($dbuser->maildisplay, $user1['maildisplay']);$this->assertEquals('atto', get_user_preferences('htmleditor', null, $dbuser));$this->assertEquals(null, get_user_preferences('invalidpreference', null, $dbuser));// Confirm user interests have been saved.$interests = \core_tag_tag::get_item_tags_array('core', 'user', $user1['id'], \core_tag_tag::BOTH_STANDARD_AND_NOT, 0, false);// There should be 3 user interests.$this->assertCount(3, $interests);// Confirm no picture change when parameter is not supplied.unset($user1['userpicture']);core_user_external::update_users(array($user1));$dbusernopic = $DB->get_record('user', array('id' => $user1['id']));$this->assertEquals($dbuser->picture, $dbusernopic->picture, 'Picture not change without the parameter.');// Confirm delete of picture deletes the picture from the user record.$user1['userpicture'] = 0;core_user_external::update_users(array($user1));$dbuserdelpic = $DB->get_record('user', array('id' => $user1['id']));$this->assertEquals(0, $dbuserdelpic->picture, 'Picture must be deleted when sent as 0.');// Updating user with an invalid email.$user5['email'] = 'bogus';$returnvalue = core_user_external::update_users(array($user5));$returnvalue = external_api::clean_returnvalue(core_user_external::update_users_returns(), $returnvalue);$this->assertEquals('useremailinvalid', $returnvalue['warnings'][0]['warningcode']);$this->assertStringContainsString('Invalid email address',$returnvalue['warnings'][0]['message']);// Updating user with a duplicate email.$user5['email'] = $user1['email'];$returnvalue = core_user_external::update_users(array($user1, $user5));$returnvalue = external_api::clean_returnvalue(core_user_external::update_users_returns(), $returnvalue);$this->assertEquals('useremailduplicate', $returnvalue['warnings'][0]['warningcode']);$this->assertStringContainsString('Duplicate email address',$returnvalue['warnings'][0]['message']);// Updating a user that does not exist.$user5['id'] = -1;$returnvalue = core_user_external::update_users(array($user5));$returnvalue = external_api::clean_returnvalue(core_user_external::update_users_returns(), $returnvalue);$this->assertEquals('invaliduserid', $returnvalue['warnings'][0]['warningcode']);$this->assertStringContainsString('Invalid user ID',$returnvalue['warnings'][0]['message']);// Updating a remote user.$user1['mnethostid'] = 5;user_update_user($user1); // Update user not using webservice.unset($user1['mnethostid']); // The mnet host ID field is not in the allowed field list for the webservice.$returnvalue = core_user_external::update_users(array($user1));$returnvalue = external_api::clean_returnvalue(core_user_external::update_users_returns(), $returnvalue);$this->assertEquals('usernotupdatedremote', $returnvalue['warnings'][0]['warningcode']);$this->assertStringContainsString('User is a remote user',$returnvalue['warnings'][0]['message']);// Call without required capability.$this->unassignUserCapability('moodle/user:update', $context->id, $roleid);$this->expectException('required_capability_exception');core_user_external::update_users(array($user1));}/*** Data provider for testing \core_user_external::update_users() for users with same emails** @return array*/public function users_with_same_emails() {return ['Same emails not allowed: Update name using exactly the same email' => [0, 'John', 's1@example.com', 'Johnny', 's1@example.com', false, true],'Same emails not allowed: Update using someone else\'s email' => [0, 'John', 's1@example.com', 'Johnny', 's2@example.com', true, false],'Same emails allowed: Update using someone else\'s email' => [1, 'John', 's1@example.com', 'Johnny', 's2@example.com', true, true],'Same emails not allowed: Update using same email but with different case' => [0, 'John', 's1@example.com', 'Johnny', 'S1@EXAMPLE.COM', false, true],'Same emails not allowed: Update using another user\'s email similar to user but with different case' => [0, 'John', 's1@example.com', 'Johnny', 'S1@EXAMPLE.COM', true, false],'Same emails allowed: Update using another user\'s email similar to user but with different case' => [1, 'John', 's1@example.com', 'Johnny', 'S1@EXAMPLE.COM', true, true],];}/*** Test update_users using similar emails with varying cases.** @dataProvider users_with_same_emails* @param boolean $allowsameemail The value to set for $CFG->allowaccountssameemail.* @param string $currentname The user's current name.* @param string $currentemail The user's current email.* @param string $newname The user's new name.* @param string $newemail The user's new email.* @param boolean $withanotheruser Whether to create another user that has the same email as the target user's new email.* @param boolean $successexpected Whether we expect that the target user's email/name will be updated.*/public function test_update_users_emails_with_different_cases($allowsameemail, $currentname, $currentemail,$newname, $newemail, $withanotheruser, $successexpected): void {global $DB;$this->resetAfterTest();$this->setAdminUser();// Set the value for $CFG->allowaccountssameemail.set_config('allowaccountssameemail', $allowsameemail);$generator = self::getDataGenerator();// Create the user that we wish to update.$usertoupdate = $generator->create_user(['email' => $currentemail, 'firstname' => $currentname]);if ($withanotheruser) {// Create another user that has the same email as the new email that we'd like to update for our target user.$generator->create_user(['email' => $newemail]);}// Build the user update parameters.$updateparams = ['id' => $usertoupdate->id,'email' => $newemail,'firstname' => $newname];// Let's try to update the user's information.core_user_external::update_users([$updateparams]);// Fetch the updated user record.$userrecord = $DB->get_record('user', ['id' => $usertoupdate->id], 'id, email, firstname');// If we expect the update to succeed, then the email/name would have been changed.if ($successexpected) {$expectedemail = $newemail;$expectedname = $newname;} else {$expectedemail = $currentemail;$expectedname = $currentname;}// Confirm that our expectations are met.$this->assertEquals($expectedemail, $userrecord->email);$this->assertEquals($expectedname, $userrecord->firstname);}/*** Test add_user_private_files*/public function test_add_user_private_files(): void {global $USER, $CFG, $DB;$this->resetAfterTest(true);$context = \context_system::instance();$roleid = $this->assignUserCapability('moodle/user:manageownfiles', $context->id);$context = \context_user::instance($USER->id);$contextid = $context->id;$component = "user";$filearea = "draft";$itemid = 0;$filepath = "/";$filename = "Simple.txt";$filecontent = base64_encode("Let us create a nice simple file");$contextlevel = null;$instanceid = null;$browser = get_file_browser();// Call the files api to create a file.$draftfile = core_files_external::upload($contextid, $component, $filearea, $itemid, $filepath,$filename, $filecontent, $contextlevel, $instanceid);$draftfile = external_api::clean_returnvalue(core_files_external::upload_returns(), $draftfile);$draftid = $draftfile['itemid'];// Make sure the file was created.$file = $browser->get_file_info($context, $component, $filearea, $draftid, $filepath, $filename);$this->assertNotEmpty($file);// Make sure the file does not exist in the user private files.$file = $browser->get_file_info($context, $component, 'private', 0, $filepath, $filename);$this->assertEmpty($file);// Call the external function.core_user_external::add_user_private_files($draftid);// Make sure the file was added to the user private files.$file = $browser->get_file_info($context, $component, 'private', 0, $filepath, $filename);$this->assertNotEmpty($file);}/*** Test add_user_private_files quota*/public function test_add_user_private_files_quota(): void {global $USER, $CFG, $DB;$this->resetAfterTest(true);$context = \context_system::instance();$roleid = $this->assignUserCapability('moodle/user:manageownfiles', $context->id);$context = \context_user::instance($USER->id);$contextid = $context->id;$component = "user";$filearea = "draft";$itemid = 0;$filepath = "/";$filename = "Simple.txt";$filecontent = base64_encode("Let us create a nice simple file");$contextlevel = null;$instanceid = null;$browser = get_file_browser();// Call the files api to create a file.$draftfile = core_files_external::upload($contextid, $component, $filearea, $itemid, $filepath,$filename, $filecontent, $contextlevel, $instanceid);$draftfile = external_api::clean_returnvalue(core_files_external::upload_returns(), $draftfile);$draftid = $draftfile['itemid'];// Call the external function to add the file to private files.core_user_external::add_user_private_files($draftid);// Force the quota so we are sure it won't be space to add the new file.$fileareainfo = file_get_file_area_info($contextid, 'user', 'private');$CFG->userquota = $fileareainfo['filesize_without_references'] + 1;// Generate a new draftitemid for the same testfile.$draftfile = core_files_external::upload($contextid, $component, $filearea, $itemid, $filepath,$filename, $filecontent, $contextlevel, $instanceid);$draftid = $draftfile['itemid'];$this->expectException('moodle_exception');$this->expectExceptionMessage(get_string('maxareabytes', 'error'));// Call the external function to include the new file.core_user_external::add_user_private_files($draftid);}/*** Test add user device*/public function test_add_user_device(): void {global $USER, $CFG, $DB;$this->resetAfterTest(true);$device = array('appid' => 'com.moodle.moodlemobile','name' => 'occam','model' => 'Nexus 4','platform' => 'Android','version' => '4.2.2','pushid' => 'apushdkasdfj4835','uuid' => 'asdnfl348qlksfaasef859','publickey' => null,);// Call the external function.core_user_external::add_user_device($device['appid'], $device['name'], $device['model'], $device['platform'],$device['version'], $device['pushid'], $device['uuid']);$created = $DB->get_record('user_devices', array('pushid' => $device['pushid']));$created = (array) $created;$this->assertEquals($device, array_intersect_key((array)$created, $device));// Test reuse the same pushid value.$warnings = core_user_external::add_user_device($device['appid'], $device['name'], $device['model'], $device['platform'],$device['version'], $device['pushid'], $device['uuid']);// We need to execute the return values cleaning process to simulate the web service server.$warnings = external_api::clean_returnvalue(core_user_external::add_user_device_returns(), $warnings);$this->assertCount(1, $warnings);// Test update an existing device.$device['pushid'] = 'different than before';$device['publickey'] = 'MFsxCzAJBgNVBAYTAkZSMRMwEQYDVQQ';$warnings = core_user_external::add_user_device($device['appid'], $device['name'], $device['model'], $device['platform'],$device['version'], $device['pushid'], $device['uuid'], $device['publickey']);$warnings = external_api::clean_returnvalue(core_user_external::add_user_device_returns(), $warnings);$this->assertEquals(1, $DB->count_records('user_devices'));$updated = $DB->get_record('user_devices', array('pushid' => $device['pushid']));$this->assertEquals($device, array_intersect_key((array)$updated, $device));// Test creating a new device just changing the uuid.$device['uuid'] = 'newuidforthesameuser';$device['pushid'] = 'new different than before';$warnings = core_user_external::add_user_device($device['appid'], $device['name'], $device['model'], $device['platform'],$device['version'], $device['pushid'], $device['uuid']);$warnings = external_api::clean_returnvalue(core_user_external::add_user_device_returns(), $warnings);$this->assertEquals(2, $DB->count_records('user_devices'));}/*** Test remove user device*/public function test_remove_user_device(): void {global $USER, $CFG, $DB;$this->resetAfterTest(true);$device = array('appid' => 'com.moodle.moodlemobile','name' => 'occam','model' => 'Nexus 4','platform' => 'Android','version' => '4.2.2','pushid' => 'apushdkasdfj4835','uuid' => 'ABCDE3723ksdfhasfaasef859');// A device with the same properties except the appid and pushid.$device2 = $device;$device2['pushid'] = "0987654321";$device2['appid'] = "other.app.com";$this->setAdminUser();// Create a user device using the external API function.core_user_external::add_user_device($device['appid'], $device['name'], $device['model'], $device['platform'],$device['version'], $device['pushid'], $device['uuid']);// Create the same device but for a different app.core_user_external::add_user_device($device2['appid'], $device2['name'], $device2['model'], $device2['platform'],$device2['version'], $device2['pushid'], $device2['uuid']);// Try to remove a device that does not exist.$result = core_user_external::remove_user_device('1234567890');$result = external_api::clean_returnvalue(core_user_external::remove_user_device_returns(), $result);$this->assertFalse($result['removed']);$this->assertCount(1, $result['warnings']);// Try to remove a device that does not exist for an existing app.$result = core_user_external::remove_user_device('1234567890', $device['appid']);$result = external_api::clean_returnvalue(core_user_external::remove_user_device_returns(), $result);$this->assertFalse($result['removed']);$this->assertCount(1, $result['warnings']);// Remove an existing device for an existing app. This will remove one of the two devices.$result = core_user_external::remove_user_device($device['uuid'], $device['appid']);$result = external_api::clean_returnvalue(core_user_external::remove_user_device_returns(), $result);$this->assertTrue($result['removed']);// Remove all the devices. This must remove the remaining device.$result = core_user_external::remove_user_device($device['uuid']);$result = external_api::clean_returnvalue(core_user_external::remove_user_device_returns(), $result);$this->assertTrue($result['removed']);}/*** Test get_user_preferences*/public function test_get_user_preferences(): void {$this->resetAfterTest(true);$user = self::getDataGenerator()->create_user();set_user_preference('calendar_maxevents', 1, $user);set_user_preference('some_random_text', 'text', $user);$this->setUser($user);$result = core_user_external::get_user_preferences();$result = external_api::clean_returnvalue(core_user_external::get_user_preferences_returns(), $result);$this->assertCount(0, $result['warnings']);// Expect 3, _lastloaded is always returned.$this->assertCount(3, $result['preferences']);foreach ($result['preferences'] as $pref) {if ($pref['name'] === '_lastloaded') {continue;}// Check we receive the expected preferences.$this->assertEquals(get_user_preferences($pref['name']), $pref['value']);}// Retrieve just one preference.$result = core_user_external::get_user_preferences('some_random_text');$result = external_api::clean_returnvalue(core_user_external::get_user_preferences_returns(), $result);$this->assertCount(0, $result['warnings']);$this->assertCount(1, $result['preferences']);$this->assertEquals('text', $result['preferences'][0]['value']);// Retrieve non-existent preference.$result = core_user_external::get_user_preferences('non_existent');$result = external_api::clean_returnvalue(core_user_external::get_user_preferences_returns(), $result);$this->assertCount(0, $result['warnings']);$this->assertCount(1, $result['preferences']);$this->assertEquals(null, $result['preferences'][0]['value']);// Check that as admin we can retrieve all the preferences for any user.$this->setAdminUser();$result = core_user_external::get_user_preferences('', $user->id);$result = external_api::clean_returnvalue(core_user_external::get_user_preferences_returns(), $result);$this->assertCount(0, $result['warnings']);$this->assertCount(3, $result['preferences']);foreach ($result['preferences'] as $pref) {if ($pref['name'] === '_lastloaded') {continue;}// Check we receive the expected preferences.$this->assertEquals(get_user_preferences($pref['name'], null, $user), $pref['value']);}// Check that as a non admin user we cannot retrieve other users preferences.$anotheruser = self::getDataGenerator()->create_user();$this->setUser($anotheruser);$this->expectException('required_capability_exception');$result = core_user_external::get_user_preferences('', $user->id);}/*** Test update_picture*/public function test_update_picture(): void {global $DB, $USER;$this->resetAfterTest(true);$user = self::getDataGenerator()->create_user();self::setUser($user);$context = \context_user::instance($USER->id);$contextid = $context->id;$filename = "reddot.png";$filecontent = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38". "GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";// Call the files api to create a file.$draftfile = core_files_external::upload($contextid, 'user', 'draft', 0, '/', $filename, $filecontent, null, null);$draftid = $draftfile['itemid'];// Change user profile image.$result = core_user_external::update_picture($draftid);$result = external_api::clean_returnvalue(core_user_external::update_picture_returns(), $result);$picture = $DB->get_field('user', 'picture', array('id' => $user->id));// The new revision is in the url for the user.$this->assertStringContainsString($picture, $result['profileimageurl']);// Check expected URL for serving the image.$this->assertStringContainsString("/$contextid/user/icon", $result['profileimageurl']);// Delete image.$result = core_user_external::update_picture(0, true);$result = external_api::clean_returnvalue(core_user_external::update_picture_returns(), $result);$picture = $DB->get_field('user', 'picture', array('id' => $user->id));// No picture.$this->assertEquals(0, $picture);// Add again the user profile image (as admin).$this->setAdminUser();$context = \context_user::instance($USER->id);$admincontextid = $context->id;$draftfile = core_files_external::upload($admincontextid, 'user', 'draft', 0, '/', $filename, $filecontent, null, null);$draftid = $draftfile['itemid'];$result = core_user_external::update_picture($draftid, false, $user->id);$result = external_api::clean_returnvalue(core_user_external::update_picture_returns(), $result);// The new revision is in the url for the user.$picture = $DB->get_field('user', 'picture', array('id' => $user->id));$this->assertStringContainsString($picture, $result['profileimageurl']);$this->assertStringContainsString("/$contextid/user/icon", $result['profileimageurl']);}/*** Test update_picture disabled*/public function test_update_picture_disabled(): void {global $CFG;$this->resetAfterTest(true);$CFG->disableuserimages = true;$this->setAdminUser();$this->expectException('moodle_exception');core_user_external::update_picture(0);}/*** Test set_user_preferences*/public function test_set_user_preferences_save(): void {global $DB;$this->resetAfterTest(true);$user1 = self::getDataGenerator()->create_user();$user2 = self::getDataGenerator()->create_user();// Save users preferences.$this->setAdminUser();$preferences = array(array('name' => 'htmleditor','value' => 'atto','userid' => $user1->id,),array('name' => 'htmleditor','value' => 'tiny','userid' => $user2->id,));$result = core_user_external::set_user_preferences($preferences);$result = external_api::clean_returnvalue(core_user_external::set_user_preferences_returns(), $result);$this->assertCount(0, $result['warnings']);$this->assertCount(2, $result['saved']);// Get preference from DB to avoid cache.$this->assertEquals('atto', $DB->get_field('user_preferences', 'value',array('userid' => $user1->id, 'name' => 'htmleditor')));$this->assertEquals('tiny', $DB->get_field('user_preferences', 'value',array('userid' => $user2->id, 'name' => 'htmleditor')));}/*** Test set_user_preferences*/public function test_set_user_preferences_save_invalid_pref(): void {global $DB;$this->resetAfterTest(true);$user1 = self::getDataGenerator()->create_user();// Save users preferences.$this->setAdminUser();$preferences = array(array('name' => 'some_random_pref','value' => 'abc','userid' => $user1->id,),);$result = core_user_external::set_user_preferences($preferences);$result = external_api::clean_returnvalue(core_user_external::set_user_preferences_returns(), $result);$this->assertCount(1, $result['warnings']);$this->assertCount(0, $result['saved']);$this->assertEquals('nopermission', $result['warnings'][0]['warningcode']);// Nothing was written to DB.$this->assertEmpty($DB->count_records('user_preferences', array('name' => 'some_random_pref')));}/*** Test set_user_preferences for an invalid user*/public function test_set_user_preferences_invalid_user(): void {$this->resetAfterTest(true);$this->setAdminUser();$preferences = array(array('name' => 'calendar_maxevents','value' => 4,'userid' => -2));$result = core_user_external::set_user_preferences($preferences);$result = external_api::clean_returnvalue(core_user_external::set_user_preferences_returns(), $result);$this->assertCount(1, $result['warnings']);$this->assertCount(0, $result['saved']);$this->assertEquals('invaliduser', $result['warnings'][0]['warningcode']);$this->assertEquals(-2, $result['warnings'][0]['itemid']);}/*** Test set_user_preferences using an invalid preference*/public function test_set_user_preferences_invalid_preference(): void {global $USER, $DB;$this->resetAfterTest(true);// Create a very long value.$this->setAdminUser();$preferences = array(array('name' => 'calendar_maxevents','value' => str_repeat('a', 1334),'userid' => $USER->id));$result = core_user_external::set_user_preferences($preferences);$result = external_api::clean_returnvalue(core_user_external::set_user_preferences_returns(), $result);$this->assertCount(0, $result['warnings']);$this->assertCount(1, $result['saved']);// Cleaned valud of the preference was saved.$this->assertEquals(1, $DB->get_field('user_preferences', 'value',array('userid' => $USER->id, 'name' => 'calendar_maxevents')));}/*** Test set_user_preferences for other user not being admin*/public function test_set_user_preferences_capability(): void {$this->resetAfterTest(true);$user1 = self::getDataGenerator()->create_user();$user2 = self::getDataGenerator()->create_user();$this->setUser($user1);$preferences = array(array('name' => 'calendar_maxevents','value' => 4,'userid' => $user2->id));$result = core_user_external::set_user_preferences($preferences);$this->assertCount(1, $result['warnings']);$this->assertCount(0, $result['saved']);$this->assertEquals('nopermission', $result['warnings'][0]['warningcode']);$this->assertEquals($user2->id, $result['warnings'][0]['itemid']);}/*** Test update_user_preferences unsetting an existing preference.*/public function test_update_user_preferences_unset(): void {global $DB;$this->resetAfterTest(true);$user = self::getDataGenerator()->create_user();// Save users preferences.$this->setAdminUser();$preferences = array(array('name' => 'htmleditor','value' => 'atto','userid' => $user->id,));$result = core_user_external::set_user_preferences($preferences);$result = external_api::clean_returnvalue(core_user_external::set_user_preferences_returns(), $result);$this->assertCount(0, $result['warnings']);$this->assertCount(1, $result['saved']);// Get preference from DB to avoid cache.$this->assertEquals('atto', $DB->get_field('user_preferences', 'value',array('userid' => $user->id, 'name' => 'htmleditor')));// Now, unset.$result = core_user_external::update_user_preferences($user->id, null, array(array('type' => 'htmleditor')));$this->assertEquals(0, $DB->count_records('user_preferences', array('userid' => $user->id, 'name' => 'htmleditor')));}/*** Test agree_site_policy*/public function test_agree_site_policy(): void {global $CFG, $DB, $USER;$this->resetAfterTest(true);$user = self::getDataGenerator()->create_user();$this->setUser($user);// Site policy not set.$result = core_user_external::agree_site_policy();$result = external_api::clean_returnvalue(core_user_external::agree_site_policy_returns(), $result);$this->assertFalse($result['status']);$this->assertCount(1, $result['warnings']);$this->assertEquals('nositepolicy', $result['warnings'][0]['warningcode']);// Set a policy issue.$CFG->sitepolicy = 'https://moodle.org';$this->assertEquals(0, $USER->policyagreed);$result = core_user_external::agree_site_policy();$result = external_api::clean_returnvalue(core_user_external::agree_site_policy_returns(), $result);$this->assertTrue($result['status']);$this->assertCount(0, $result['warnings']);$this->assertEquals(1, $USER->policyagreed);$this->assertEquals(1, $DB->get_field('user', 'policyagreed', array('id' => $USER->id)));// Try again, we should get a warning.$result = core_user_external::agree_site_policy();$result = external_api::clean_returnvalue(core_user_external::agree_site_policy_returns(), $result);$this->assertFalse($result['status']);$this->assertCount(1, $result['warnings']);$this->assertEquals('alreadyagreed', $result['warnings'][0]['warningcode']);// Set something to make require_login throws an exception.$otheruser = self::getDataGenerator()->create_user();$this->setUser($otheruser);$DB->set_field('user', 'lastname', '', array('id' => $USER->id));$USER->lastname = '';try {$result = core_user_external::agree_site_policy();$this->fail('Expecting \'usernotfullysetup\' moodle_exception to be thrown');} catch (\moodle_exception $e) {$this->assertEquals('usernotfullysetup', $e->errorcode);} catch (\Exception $e) {$this->fail('Expecting \'usernotfullysetup\' moodle_exception to be thrown.');}}/*** Test get_private_files_info*/public function test_get_private_files_info(): void {$this->resetAfterTest(true);$user = self::getDataGenerator()->create_user();$this->setUser($user);$usercontext = \context_user::instance($user->id);$filerecord = array('contextid' => $usercontext->id,'component' => 'user','filearea' => 'private','itemid' => 0,'filepath' => '/','filename' => 'thefile',);$fs = get_file_storage();$file = $fs->create_file_from_string($filerecord, 'abc');// Get my private files information.$result = core_user_external::get_private_files_info();$result = external_api::clean_returnvalue(core_user_external::get_private_files_info_returns(), $result);$this->assertEquals(1, $result['filecount']);$this->assertEquals($file->get_filesize(), $result['filesize']);$this->assertEquals(0, $result['foldercount']);$this->assertEquals($file->get_filesize(), $result['filesizewithoutreferences']);// As admin, get user information.$this->setAdminUser();$result = core_user_external::get_private_files_info($user->id);$result = external_api::clean_returnvalue(core_user_external::get_private_files_info_returns(), $result);$this->assertEquals(1, $result['filecount']);$this->assertEquals($file->get_filesize(), $result['filesize']);$this->assertEquals(0, $result['foldercount']);$this->assertEquals($file->get_filesize(), $result['filesizewithoutreferences']);}/*** Test get_private_files_info missing permissions.*/public function test_get_private_files_info_missing_permissions(): void {$this->resetAfterTest(true);$user1 = self::getDataGenerator()->create_user();$user2 = self::getDataGenerator()->create_user();$this->setUser($user1);$this->expectException('required_capability_exception');// Try to retrieve other user private files info.core_user_external::get_private_files_info($user2->id);}/*** Test the functionality of the {@see \core_user\external\search_identity} class.*/public function test_external_search_identity(): void {global $CFG;$this->resetAfterTest(true);$this->setAdminUser();$user1 = self::getDataGenerator()->create_user(['firstname' => 'Firstone','lastname' => 'Lastone','username' => 'usernameone','idnumber' => 'idnumberone','email' => 'userone@example.com','phone1' => 'tel1','phone2' => 'tel2','department' => 'Department Foo','institution' => 'Institution Foo','city' => 'City One','country' => 'AU',]);$user2 = self::getDataGenerator()->create_user(['firstname' => 'Firsttwo','lastname' => 'Lasttwo','username' => 'usernametwo','idnumber' => 'idnumbertwo','email' => 'usertwo@example.com','phone1' => 'tel1','phone2' => 'tel2','department' => 'Department Foo','institution' => 'Institution Foo','city' => 'City One','country' => 'AU',]);$user3 = self::getDataGenerator()->create_user(['firstname' => 'Firstthree','lastname' => 'Lastthree','username' => 'usernamethree','idnumber' => 'idnumberthree','email' => 'userthree@example.com','phone1' => 'tel1','phone2' => 'tel2','department' => 'Department Foo','institution' => 'Institution Foo','city' => 'City One','country' => 'AU',]);$CFG->showuseridentity = 'email,idnumber,city';$CFG->maxusersperpage = 3;$result = \core_user\external\search_identity::execute('Lastt');$result = external_api::clean_returnvalue(\core_user\external\search_identity::execute_returns(), $result);$this->assertEquals(2, count($result['list']));$this->assertEquals(3, $result['maxusersperpage']);$this->assertEquals(false, $result['overflow']);foreach ($result['list'] as $user) {$this->assertEquals(3, count($user['extrafields']));$this->assertEquals('email', $user['extrafields'][0]['name']);$this->assertEquals('idnumber', $user['extrafields'][1]['name']);$this->assertEquals('city', $user['extrafields'][2]['name']);}$CFG->showuseridentity = 'username';$CFG->maxusersperpage = 2;$result = \core_user\external\search_identity::execute('Firstt');$result = external_api::clean_returnvalue(\core_user\external\search_identity::execute_returns(), $result);$this->assertEquals(2, count($result['list']));$this->assertEquals(2, $result['maxusersperpage']);$this->assertEquals(false, $result['overflow']);foreach ($result['list'] as $user) {$this->assertEquals(1, count($user['extrafields']));$this->assertEquals('username', $user['extrafields'][0]['name']);}$CFG->showuseridentity = 'email';$CFG->maxusersperpage = 2;$result = \core_user\external\search_identity::execute('City One');$result = external_api::clean_returnvalue(\core_user\external\search_identity::execute_returns(), $result);$this->assertEquals(0, count($result['list']));$this->assertEquals(2, $result['maxusersperpage']);$this->assertEquals(false, $result['overflow']);$CFG->showuseridentity = 'city';$CFG->maxusersperpage = 2;foreach ($result['list'] as $user) {$this->assertEquals(1, count($user['extrafields']));$this->assertEquals('username', $user['extrafields'][0]['name']);}$result = \core_user\external\search_identity::execute('City One');$result = external_api::clean_returnvalue(\core_user\external\search_identity::execute_returns(), $result);$this->assertEquals(2, count($result['list']));$this->assertEquals(2, $result['maxusersperpage']);$this->assertEquals(true, $result['overflow']);}/*** Test functionality of the {@see \core_user\external\search_identity} class with alternativefullnameformat defined.*/public function test_external_search_identity_with_alternativefullnameformat(): void {global $CFG;$this->resetAfterTest(true);$this->setAdminUser();$user1 = self::getDataGenerator()->create_user(['lastname' => '小柳','lastnamephonetic' => 'Koyanagi','firstname' => '秋','firstnamephonetic' => 'Aki','email' => 'koyanagiaki@example.com','country' => 'JP',]);$CFG->showuseridentity = 'email';$CFG->maxusersperpage = 3;$CFG->alternativefullnameformat ='<ruby>lastname firstname <rp>(</rp><rt>lastnamephonetic firstnamephonetic</rt><rp>)</rp></ruby>';$result = \core_user\external\search_identity::execute('Ak');$result = external_api::clean_returnvalue(\core_user\external\search_identity::execute_returns(), $result);$this->assertEquals(1, count($result['list']));$this->assertEquals(3, $result['maxusersperpage']);$this->assertEquals(false, $result['overflow']);foreach ($result['list'] as $user) {$this->assertEquals(1, count($user['extrafields']));$this->assertEquals('email', $user['extrafields'][0]['name']);}}/*** Test verifying that update_user_preferences prevents changes to the default homepage for other users.*/public function test_update_user_preferences_homepage_permission_callback(): void {global $DB;$this->resetAfterTest();$user = self::getDataGenerator()->create_user();$this->setUser($user);$adminuser = get_admin();// Allow user selection of the default homepage via preferences.set_config('defaulthomepage', HOMEPAGE_USER);// Try to save another user's home page preference which uses the permissioncallback.$preferences = [['name' => 'user_home_page_preference','value' => '3','userid' => $adminuser->id,]];$result = core_user_external::set_user_preferences($preferences);$result = external_api::clean_returnvalue(core_user_external::set_user_preferences_returns(), $result);$this->assertCount(1, $result['warnings']);$this->assertCount(0, $result['saved']);// Verify no change to the preference, checking from DB to avoid cache.$this->assertEquals(null, $DB->get_field('user_preferences', 'value',['userid' => $adminuser->id, 'name' => 'user_home_page_preference']));}}