diff --git a/admin/tool/uploaduser/index.php b/admin/tool/uploaduser/index.php index a45cb47e42e..b50156ffc2f 100644 --- a/admin/tool/uploaduser/index.php +++ b/admin/tool/uploaduser/index.php @@ -669,7 +669,7 @@ if ($formdata = $mform2->is_cancelled()) { if ($doupdate or $existinguser->password !== $oldpw) { // We want only users that were really updated. - user_update_user($existinguser, false); + user_update_user($existinguser, false, false); $upt->track('status', $struserupdated); $usersupdated++; @@ -687,6 +687,9 @@ if ($formdata = $mform2->is_cancelled()) { } } + // Trigger event. + \core\event\user_updated::create_from_userid($existinguser->id)->trigger(); + } else { // no user information changed $upt->track('status', $struseruptodate); @@ -800,7 +803,7 @@ if ($formdata = $mform2->is_cancelled()) { $upt->track('password', '-', 'normal', false); } - $user->id = user_create_user($user, false); + $user->id = user_create_user($user, false, false); $upt->track('username', html_writer::link(new moodle_url('/user/profile.php', array('id'=>$user->id)), s($user->username)), 'normal', false); // pre-process custom profile menu fields data from csv file @@ -815,6 +818,9 @@ if ($formdata = $mform2->is_cancelled()) { set_user_preference('create_password', 1, $user); } + // Trigger event. + \core\event\user_created::create_from_userid($user->id)->trigger(); + $upt->track('status', $struseradded); $upt->track('id', $user->id, 'normal', false); $usersnew++; diff --git a/auth/email/auth.php b/auth/email/auth.php index b071e3252d9..2c36eabcb33 100644 --- a/auth/email/auth.php +++ b/auth/email/auth.php @@ -94,11 +94,14 @@ class auth_plugin_email extends auth_plugin_base { $user->calendartype = $CFG->calendartype; } - $user->id = user_create_user($user, false); + $user->id = user_create_user($user, false, false); // Save any custom profile field information. profile_save_data($user); + // Trigger event. + \core\event\user_created::create_from_userid($user->id)->trigger(); + if (! send_confirmation_email($user)) { print_error('auth_emailnoemail','auth_email'); } diff --git a/auth/ldap/auth.php b/auth/ldap/auth.php index 9004e267c47..cc1374e3dd7 100644 --- a/auth/ldap/auth.php +++ b/auth/ldap/auth.php @@ -551,7 +551,7 @@ class auth_plugin_ldap extends auth_plugin_base { print_error('auth_ldap_create_error', 'auth_ldap'); } - $user->id = user_create_user($user, false); + $user->id = user_create_user($user, false, false); // Save any custom profile field information profile_save_data($user); @@ -564,6 +564,8 @@ class auth_plugin_ldap extends auth_plugin_base { $user = $DB->get_record('user', array('id'=>$user->id)); + \core\event\user_created::create_from_userid($user->id)->trigger(); + if (! send_confirmation_email($user)) { print_error('noemail', 'auth_ldap'); } @@ -1027,7 +1029,7 @@ class auth_plugin_ldap extends auth_plugin_base { } } } - user_update_user($newuser, false); + user_update_user($newuser, false, false); } } else { return false; diff --git a/auth/ldap/tests/plugin_test.php b/auth/ldap/tests/plugin_test.php index 13cdcc083b9..7f0855e0085 100644 --- a/auth/ldap/tests/plugin_test.php +++ b/auth/ldap/tests/plugin_test.php @@ -279,6 +279,143 @@ class auth_ldap_plugin_testcase extends advanced_testcase { $this->assertEventLegacyLogData($expectedlog, $event); } + /** + * Test logging in via LDAP calls a user_loggedin event. + */ + public function test_ldap_user_signup() { + global $CFG, $DB; + + // User to create. + $user = array( + 'username' => 'usersignuptest1', + 'password' => 'Moodle2014!', + 'idnumber' => 'idsignuptest1', + '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' => 'usersignuptest1@email.com', + 'description' => 'This is a description for user 1', + 'city' => 'Perth', + 'country' => 'au', + 'mnethostid' => $CFG->mnet_localhost_id, + 'auth' => 'ldap' + ); + + if (!extension_loaded('ldap')) { + $this->markTestSkipped('LDAP extension is not loaded.'); + } + + $this->resetAfterTest(); + + require_once($CFG->dirroot.'/auth/ldap/auth.php'); + require_once($CFG->libdir.'/ldaplib.php'); + + if (!defined('TEST_AUTH_LDAP_HOST_URL') or !defined('TEST_AUTH_LDAP_BIND_DN') or !defined('TEST_AUTH_LDAP_BIND_PW') or !defined('TEST_AUTH_LDAP_DOMAIN')) { + $this->markTestSkipped('External LDAP test server not configured.'); + } + + // Make sure we can connect the server. + $debuginfo = ''; + if (!$connection = ldap_connect_moodle(TEST_AUTH_LDAP_HOST_URL, 3, 'rfc2307', TEST_AUTH_LDAP_BIND_DN, TEST_AUTH_LDAP_BIND_PW, LDAP_DEREF_NEVER, $debuginfo, false)) { + $this->markTestSkipped('Can not connect to LDAP test server: '.$debuginfo); + } + + $this->enable_plugin(); + + // Create new empty test container. + $topdn = 'dc=moodletest,'.TEST_AUTH_LDAP_DOMAIN; + + $this->recursive_delete($connection, TEST_AUTH_LDAP_DOMAIN, 'dc=moodletest'); + + $o = array(); + $o['objectClass'] = array('dcObject', 'organizationalUnit'); + $o['dc'] = 'moodletest'; + $o['ou'] = 'MOODLETEST'; + if (!ldap_add($connection, 'dc=moodletest,'.TEST_AUTH_LDAP_DOMAIN, $o)) { + $this->markTestSkipped('Can not create test LDAP container.'); + } + + // Create a few users. + $o = array(); + $o['objectClass'] = array('organizationalUnit'); + $o['ou'] = 'users'; + ldap_add($connection, 'ou='.$o['ou'].','.$topdn, $o); + + // Configure the plugin a bit. + set_config('host_url', TEST_AUTH_LDAP_HOST_URL, 'auth/ldap'); + set_config('start_tls', 0, 'auth/ldap'); + set_config('ldap_version', 3, 'auth/ldap'); + set_config('ldapencoding', 'utf-8', 'auth/ldap'); + set_config('pagesize', '2', 'auth/ldap'); + set_config('bind_dn', TEST_AUTH_LDAP_BIND_DN, 'auth/ldap'); + set_config('bind_pw', TEST_AUTH_LDAP_BIND_PW, 'auth/ldap'); + set_config('user_type', 'rfc2307', 'auth/ldap'); + set_config('contexts', 'ou=users,'.$topdn, 'auth/ldap'); + set_config('search_sub', 0, 'auth/ldap'); + set_config('opt_deref', LDAP_DEREF_NEVER, 'auth/ldap'); + set_config('user_attribute', 'cn', 'auth/ldap'); + set_config('memberattribute', 'memberuid', 'auth/ldap'); + set_config('memberattribute_isdn', 0, 'auth/ldap'); + set_config('creators', 'cn=creators,'.$topdn, 'auth/ldap'); + set_config('removeuser', AUTH_REMOVEUSER_KEEP, 'auth/ldap'); + + set_config('field_map_email', 'mail', 'auth/ldap'); + set_config('field_updatelocal_email', 'oncreate', 'auth/ldap'); + set_config('field_updateremote_email', '0', 'auth/ldap'); + set_config('field_lock_email', 'unlocked', 'auth/ldap'); + + set_config('field_map_firstname', 'givenName', 'auth/ldap'); + set_config('field_updatelocal_firstname', 'oncreate', 'auth/ldap'); + set_config('field_updateremote_firstname', '0', 'auth/ldap'); + set_config('field_lock_firstname', 'unlocked', 'auth/ldap'); + + set_config('field_map_lastname', 'sn', 'auth/ldap'); + set_config('field_updatelocal_lastname', 'oncreate', 'auth/ldap'); + set_config('field_updateremote_lastname', '0', 'auth/ldap'); + set_config('field_lock_lastname', 'unlocked', 'auth/ldap'); + set_config('passtype', 'md5', 'auth/ldap'); + set_config('create_context', 'ou=users,'.$topdn, 'auth/ldap'); + + $this->assertEquals(2, $DB->count_records('user')); + $this->assertEquals(0, $DB->count_records('role_assignments')); + + /** @var auth_plugin_ldap $auth */ + $auth = get_auth_plugin('ldap'); + + $sink = $this->redirectEvents(); + $auth->user_signup((object)$user, false); + $this->assertDebuggingCalled('Not sending email due to $CFG->noemailever config setting'); + $events = $sink->get_events(); + $sink->close(); + + // Verify 2 events get generated. + $this->assertCount(2, $events); + + // Get record from db. + $dbuser = $DB->get_record('user', array('username' => $user['username'])); + $user['id'] = $dbuser->id; + + // Last event is user_created. + $event = array_pop($events); + $this->assertInstanceOf('\core\event\user_created', $event); + $this->assertEquals($user['id'], $event->objectid); + $this->assertEquals('user_created', $event->get_legacy_eventname()); + $this->assertEquals(context_user::instance($user['id']), $event->get_context()); + $expectedlogdata = array(SITEID, 'user', 'add', '/view.php?id='.$event->objectid, fullname($dbuser)); + $this->assertEventLegacyLogData($expectedlogdata, $event); + + // First event is user_password_updated. + $event = array_pop($events); + $this->assertInstanceOf('\core\event\user_password_updated', $event); + $this->assertEventContextNotUsed($event); + + // Delete user which we just created. + ldap_delete($connection, 'cn='.$user['username'].',ou=users,'.$topdn); + } + protected function create_ldap_user($connection, $topdn, $i) { $o = array(); $o['objectClass'] = array('inetOrgPerson', 'organizationalPerson', 'person', 'posixAccount'); diff --git a/lib/classes/event/user_created.php b/lib/classes/event/user_created.php index b7161793172..d6aa4391e8a 100644 --- a/lib/classes/event/user_created.php +++ b/lib/classes/event/user_created.php @@ -113,4 +113,24 @@ class user_created extends base { $this->relateduserid = $this->objectid; } } + + /** + * Create instance of event. + * + * @since Moodle 2.6.4, 2.7.1 + * + * @param int $userid id of user + * @return user_created + */ + public static function create_from_userid($userid) { + $data = array( + 'objectid' => $userid, + 'relateduserid' => $userid, + 'context' => \context_user::instance($userid) + ); + + // Create user_created event. + $event = self::create($data); + return $event; + } } diff --git a/lib/classes/event/user_updated.php b/lib/classes/event/user_updated.php index a8ef5c5500e..1e5e27ab623 100644 --- a/lib/classes/event/user_updated.php +++ b/lib/classes/event/user_updated.php @@ -113,4 +113,24 @@ class user_updated extends base { $this->relateduserid = $this->objectid; } } + + /** + * Create instance of event. + * + * @since Moodle 2.6.4, 2.7.1 + * + * @param int $userid id of user + * @return user_updated + */ + public static function create_from_userid($userid) { + $data = array( + 'objectid' => $userid, + 'relateduserid' => $userid, + 'context' => \context_user::instance($userid) + ); + + // Create user_updated event. + $event = self::create($data); + return $event; + } } diff --git a/lib/moodlelib.php b/lib/moodlelib.php index cc480f4db69..018dd908404 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -4029,7 +4029,7 @@ function create_user_record($username, $password, $auth = 'manual') { $newuser->timemodified = $newuser->timecreated; $newuser->mnethostid = $CFG->mnet_localhost_id; - $newuser->id = user_create_user($newuser, false); + $newuser->id = user_create_user($newuser, false, false); // Save user profile data. profile_save_data($newuser); @@ -4041,6 +4041,9 @@ function create_user_record($username, $password, $auth = 'manual') { // Set the password. update_internal_user_password($user, $password); + // Trigger event. + \core\event\user_created::create_from_userid($newuser->id)->trigger(); + return $user; } @@ -4111,10 +4114,13 @@ function update_user_record_by_id($id) { if ($newuser) { $newuser['id'] = $oldinfo->id; $newuser['timemodified'] = time(); - user_update_user((object) $newuser, false); + user_update_user((object) $newuser, false, false); // Save user profile data. profile_save_data((object) $newuser); + + // Trigger event. + \core\event\user_updated::create_from_userid($newuser['id'])->trigger(); } } @@ -4273,7 +4279,8 @@ function delete_user(stdClass $user) { $updateuser->picture = 0; $updateuser->timemodified = time(); - user_update_user($updateuser, false); + // Don't trigger update event, as user is being deleted. + user_update_user($updateuser, false, false); // Now do a final accesslib cleanup - removes all role assignments in user context and context itself. context_helper::delete_instance(CONTEXT_USER, $user->id); @@ -4710,9 +4717,13 @@ function hash_internal_user_password($password, $fasthash = false) { * * @param stdClass $user User object (password property may be updated). * @param string $password Plain text password. + * @param bool $fasthash If true, use a low cost factor when generating the hash + * This is much faster to generate but makes the hash + * less secure. It is used when lots of hashes need to + * be generated quickly. * @return bool Always returns true. */ -function update_internal_user_password($user, $password) { +function update_internal_user_password($user, $password, $fasthash = false) { global $CFG, $DB; require_once($CFG->libdir.'/password_compat/lib/password.php'); @@ -4721,25 +4732,26 @@ function update_internal_user_password($user, $password) { if ($authplugin->prevent_local_passwords()) { $hashedpassword = AUTH_PASSWORD_NOT_CACHED; } else { - $hashedpassword = hash_internal_user_password($password); + $hashedpassword = hash_internal_user_password($password, $fasthash); } // If verification fails then it means the password has changed. - $passwordchanged = !password_verify($password, $user->password); - $algorithmchanged = password_needs_rehash($user->password, PASSWORD_DEFAULT); + if (isset($user->password)) { + // While creating new user, password in unset in $user object, to avoid + // saving it with user_create() + $passwordchanged = !password_verify($password, $user->password); + $algorithmchanged = password_needs_rehash($user->password, PASSWORD_DEFAULT); + } else { + $passwordchanged = true; + } if ($passwordchanged || $algorithmchanged) { $DB->set_field('user', 'password', $hashedpassword, array('id' => $user->id)); $user->password = $hashedpassword; // Trigger event. - $event = \core\event\user_updated::create(array( - 'objectid' => $user->id, - 'relateduserid' => $user->id, - 'context' => context_user::instance($user->id) - )); - $event->add_record_snapshot('user', $user); - $event->trigger(); + $user = $DB->get_record('user', array('id' => $user->id)); + \core\event\user_password_updated::create_from_user($user)->trigger(); } return true; @@ -5969,18 +5981,7 @@ function setnew_password_and_mail($user, $fasthash = false) { $newpassword = generate_password(); - $hashedpassword = hash_internal_user_password($newpassword, $fasthash); - $DB->set_field('user', 'password', $hashedpassword, array('id' => $user->id)); - $user->password = $hashedpassword; - - // Trigger event. - $event = \core\event\user_updated::create(array( - 'objectid' => $user->id, - 'relateduserid' => $user->id, - 'context' => context_user::instance($user->id) - )); - $event->add_record_snapshot('user', $user); - $event->trigger(); + update_internal_user_password($user, $newpassword, $fasthash); $a = new stdClass(); $a->firstname = fullname($user, true); diff --git a/lib/tests/moodlelib_test.php b/lib/tests/moodlelib_test.php index 3830bda92e4..50cd1a37c38 100644 --- a/lib/tests/moodlelib_test.php +++ b/lib/tests/moodlelib_test.php @@ -2247,11 +2247,21 @@ class core_moodlelib_testcase extends advanced_testcase { // Manually set the user's password to the md5 of the string 'password'. $DB->set_field('user', 'password', '5f4dcc3b5aa765d61d8327deb882cf99', array('id' => $user->id)); + $sink = $this->redirectEvents(); // Update the password. update_internal_user_password($user, 'password'); + $events = $sink->get_events(); + $sink->close(); + $event = array_pop($events); // Password should have been updated to a bcrypt hash. $this->assertFalse(password_is_legacy_hash($user->password)); + + // Verify event information. + $this->assertInstanceOf('\core\event\user_password_updated', $event); + $this->assertSame($user->id, $event->relateduserid); + $this->assertEquals(context_user::instance($user->id), $event->get_context()); + $this->assertEventContextNotUsed($event); } public function test_fullname() { @@ -2562,9 +2572,9 @@ class core_moodlelib_testcase extends advanced_testcase { } /** - * Test user_updated event trigger by various apis. + * Test setnew_password_and_mail. */ - public function test_user_updated_event() { + public function test_setnew_password_and_mail() { global $DB, $CFG; $this->resetAfterTest(); @@ -2578,27 +2588,21 @@ class core_moodlelib_testcase extends advanced_testcase { $sink = $this->redirectEvents(); $sink2 = $this->redirectEmails(); // Make sure we are redirecting emails. setnew_password_and_mail($user); - update_internal_user_password($user, 'randompass'); $events = $sink->get_events(); $sink->close(); $sink2->close(); + $event = array_pop($events); // Test updated value. $dbuser = $DB->get_record('user', array('id' => $user->id)); $this->assertSame($user->firstname, $dbuser->firstname); - $this->assertNotSame('M00dLe@T', $dbuser->password); + $this->assertNotEmpty($dbuser->password); // Test event. - foreach ($events as $event) { - $this->assertInstanceOf('\core\event\user_updated', $event); - $this->assertSame($user->id, $event->objectid); - $this->assertSame('user_updated', $event->get_legacy_eventname()); - $this->assertEventLegacyData($user, $event); - $this->assertEquals(context_user::instance($user->id), $event->get_context()); - $expectedlogdata = array(SITEID, 'user', 'update', 'view.php?id='.$user->id, ''); - $this->assertEventLegacyLogData($expectedlogdata, $event); - $this->assertEventContextNotUsed($event); - } + $this->assertInstanceOf('\core\event\user_password_updated', $event); + $this->assertSame($user->id, $event->relateduserid); + $this->assertEquals(context_user::instance($user->id), $event->get_context()); + $this->assertEventContextNotUsed($event); } /** diff --git a/lib/upgrade.txt b/lib/upgrade.txt index f651affd9c2..fcc0104aedc 100644 --- a/lib/upgrade.txt +++ b/lib/upgrade.txt @@ -1,6 +1,15 @@ This files describes API changes in core libraries and APIs, information provided here is intended especially for developers. +=== 2.6.4 / 2.7.1 === +* setnew_password_and_mail() and update_internal_user_password() will trigger + \core\event\user_password_updated. Previously they used to generate + \core\event\user_updated event. +* update_internal_user_password() accepts optional boolean $fasthash for fast + hashing. +* user_update_user() and user_create_user() api's accept optional param + $triggerevent to avoid respective events to be triggred from the api's. + === 2.7 === * PHPUnit cannot be installed via PEAR any more, please use composer package manager instead. diff --git a/login/change_password.php b/login/change_password.php index ba8e8db1d0c..cb433d5c5c4 100644 --- a/login/change_password.php +++ b/login/change_password.php @@ -122,9 +122,6 @@ if ($mform->is_cancelled()) { unset_user_preference('auth_forcepasswordchange', $USER); unset_user_preference('create_password', $USER); - $user = $DB->get_record('user', array('id' => $USER->id), '*', MUST_EXIST); - \core\event\user_password_updated::create_from_user($user)->trigger(); - $strpasswordchanged = get_string('passwordchanged'); $fullname = fullname($USER, true); diff --git a/login/lib.php b/login/lib.php index d0b8f5d2c34..dd357f463bb 100644 --- a/login/lib.php +++ b/login/lib.php @@ -251,9 +251,6 @@ function core_login_process_password_set($token) { } complete_user_login($user); // Triggers the login event. - $user = $DB->get_record('user', array('id' => $user->id), '*', MUST_EXIST); - \core\event\user_password_updated::create_from_user($user, true)->trigger(); - $urltogo = core_login_get_return_url(); unset($SESSION->wantsurl); redirect($urltogo, get_string('passwordset'), 1); diff --git a/user/edit.php b/user/edit.php index c15475a1e0a..e6282c76d5e 100644 --- a/user/edit.php +++ b/user/edit.php @@ -220,7 +220,7 @@ if ($usernew = $userform->get_data()) { } // Update user with new profile data. - user_update_user($usernew, false); + user_update_user($usernew, false, false); // Update preferences. useredit_update_user_preference($usernew); @@ -244,6 +244,9 @@ if ($usernew = $userform->get_data()) { // Save custom profile fields data. profile_save_data($usernew); + // Trigger event. + \core\event\user_updated::create_from_userid($user->id)->trigger(); + // If email was changed and confirmation is required, send confirmation email now to the new address. if ($emailchanged && $CFG->emailchangeconfirmation) { $tempuser = $DB->get_record('user', array('id' => $user->id), '*', MUST_EXIST); diff --git a/user/editadvanced.php b/user/editadvanced.php index 047868e62fb..d363758cf8d 100644 --- a/user/editadvanced.php +++ b/user/editadvanced.php @@ -155,6 +155,7 @@ $userform = new user_editadvanced_form(null, array( $userform->set_data($user); if ($usernew = $userform->get_data()) { + $usercreated = false; if (empty($usernew->auth)) { // User editing self. @@ -166,7 +167,6 @@ if ($usernew = $userform->get_data()) { $usernew->timemodified = time(); $createpassword = false; - $passwordupdated = false; if ($usernew->id == -1) { unset($usernew->id); @@ -185,17 +185,15 @@ if ($usernew = $userform->get_data()) { } else { $usernew->password = AUTH_PASSWORD_NOT_CACHED; } - $usernew->id = user_create_user($usernew, false); + $usernew->id = user_create_user($usernew, false, false); if (!$authplugin->is_internal() and $authplugin->can_change_password() and !empty($usernew->newpassword)) { if (!$authplugin->user_update_password($usernew, $usernew->newpassword)) { // Do not stop here, we need to finish user creation. debugging(get_string('cannotupdatepasswordonextauth', '', '', $usernew->auth), DEBUG_NONE); - } else { - $passwordupdated = true; } } - + $usercreated = true; } else { $usernew = file_postupdate_standard_editor($usernew, 'description', $editoroptions, $usercontext, 'user', 'profile', 0); // Pass a true old $user here. @@ -203,15 +201,13 @@ if ($usernew = $userform->get_data()) { // Auth update failed. print_error('cannotupdateuseronexauth', '', '', $user->auth); } - user_update_user($usernew, false); + user_update_user($usernew, false, false); // Set new password if specified. if (!empty($usernew->newpassword)) { if ($authplugin->can_change_password()) { if (!$authplugin->user_update_password($usernew, $usernew->newpassword)) { print_error('cannotupdatepasswordonextauth', '', '', $usernew->auth); - } else { - $passwordupdated = true; } unset_user_preference('create_password', $usernew); // Prevent cron from generating the password. } @@ -250,16 +246,19 @@ if ($usernew = $userform->get_data()) { // Reload from db. $usernew = $DB->get_record('user', array('id' => $usernew->id)); - if ($passwordupdated) { - \core\event\user_password_updated::create_from_user($usernew)->trigger(); - } - if ($createpassword) { setnew_password_and_mail($usernew); unset_user_preference('create_password', $usernew); set_user_preference('auth_forcepasswordchange', 1, $usernew); } + // Trigger update/create event, after all fields are stored. + if ($usercreated) { + \core\event\user_created::create_from_userid($usernew->id)->trigger(); + } else { + \core\event\user_updated::create_from_userid($usernew->id)->trigger(); + } + if ($user->id == $USER->id) { // Override old $USER session variable. foreach ((array)$usernew as $variable => $value) { diff --git a/user/externallib.php b/user/externallib.php index 506b629c453..24daf4f60bb 100644 --- a/user/externallib.php +++ b/user/externallib.php @@ -186,7 +186,7 @@ class core_user_external extends external_api { // End of user info validation. // Create the user data now! - $user['id'] = user_create_user($user); + $user['id'] = user_create_user($user, true, false); // Custom fields. if (!empty($user['customfields'])) { @@ -198,6 +198,9 @@ class core_user_external extends external_api { profile_save_data((object) $user); } + // Trigger event. + \core\event\user_created::create_from_userid($user['id'])->trigger(); + // Preferences. if (!empty($user['preferences'])) { foreach ($user['preferences'] as $preference) { @@ -400,7 +403,7 @@ class core_user_external extends external_api { $transaction = $DB->start_delegated_transaction(); foreach ($params['users'] as $user) { - user_update_user($user); + user_update_user($user, true, false); // Update user custom fields. if (!empty($user['customfields'])) { @@ -412,6 +415,9 @@ class core_user_external extends external_api { profile_save_data((object) $user); } + // Trigger event. + \core\event\user_updated::create_from_userid($user['id'])->trigger(); + // Preferences. if (!empty($user['preferences'])) { foreach ($user['preferences'] as $preference) { diff --git a/user/lib.php b/user/lib.php index aee893b2b52..1089e05760e 100644 --- a/user/lib.php +++ b/user/lib.php @@ -29,9 +29,11 @@ * @throws moodle_exception * @param stdClass $user user to create * @param bool $updatepassword if true, authentication plugin will update password. + * @param bool $triggerevent set false if user_created event should not be triggred. + * This will not affect user_password_updated event triggering. * @return int id of the newly created user */ -function user_create_user($user, $updatepassword = true) { +function user_create_user($user, $updatepassword = true, $triggerevent = true) { global $CFG, $DB; // Set the timecreate field to the current time. @@ -87,15 +89,10 @@ function user_create_user($user, $updatepassword = true) { $authplugin->user_update_password($newuser, $userpassword); } - // Trigger event. - $event = \core\event\user_created::create( - array( - 'objectid' => $newuserid, - 'relateduserid' => $newuserid, - 'context' => $usercontext - ) - ); - $event->trigger(); + // Trigger event If required. + if ($triggerevent) { + \core\event\user_created::create_from_userid($newuserid)->trigger(); + } return $newuserid; } @@ -106,8 +103,10 @@ function user_create_user($user, $updatepassword = true) { * @throws moodle_exception * @param stdClass $user the user to update * @param bool $updatepassword if true, authentication plugin will update password. + * @param bool $triggerevent set false if user_updated event should not be triggred. + * This will not affect user_password_updated event triggering. */ -function user_update_user($user, $updatepassword = true) { +function user_update_user($user, $updatepassword = true, $triggerevent = true) { global $DB; // Set the timecreate field to the current time. @@ -165,16 +164,10 @@ function user_update_user($user, $updatepassword = true) { } } } - - // Trigger event. - $event = \core\event\user_updated::create( - array( - 'objectid' => $user->id, - 'relateduserid' => $user->id, - 'context' => context_user::instance($user->id) - ) - ); - $event->trigger(); + // Trigger event if required. + if ($triggerevent) { + \core\event\user_updated::create_from_userid($user->id)->trigger(); + } } /** diff --git a/user/tests/userlib_test.php b/user/tests/userlib_test.php index 915473af14e..10eea017259 100644 --- a/user/tests/userlib_test.php +++ b/user/tests/userlib_test.php @@ -76,6 +76,22 @@ class core_userliblib_testcase extends advanced_testcase { user_update_user($user, false); $dbuser = $DB->get_record('user', array('id' => $user->id)); $this->assertSame($password, $dbuser->password); + + // Verify event is not triggred by user_update_user when needed. + $sink = $this->redirectEvents(); + user_update_user($user, false, false); + $events = $sink->get_events(); + $sink->close(); + $this->assertCount(0, $events); + + // With password, there should be 1 event. + $sink = $this->redirectEvents(); + user_update_user($user, true, false); + $events = $sink->get_events(); + $sink->close(); + $this->assertCount(1, $events); + $event = array_pop($events); + $this->assertInstanceOf('\core\event\user_password_updated', $event); } /** @@ -128,6 +144,14 @@ class core_userliblib_testcase extends advanced_testcase { $this->assertEventLegacyData($dbuser, $event); $expectedlogdata = array(SITEID, 'user', 'add', '/view.php?id='.$event->objectid, fullname($dbuser)); $this->assertEventLegacyLogData($expectedlogdata, $event); + + // Verify event is not triggred by user_create_user when needed. + $user = array('username' => 'usernametest2'); // Create another user. + $sink = $this->redirectEvents(); + user_create_user($user, true, false); + $events = $sink->get_events(); + $sink->close(); + $this->assertCount(0, $events); } /**