You must use the filter to alter the $data
when the user is updated, you can not act with the action you would usually hook into a user update.
Also you must port several of the security and sanity measures WordPress enacts in its code into your new function.
What you can do:
- Hook into the
wp_pre_insert_user_data
filter. Make sure you do that only under correct permissions: add_filter( 'wp_pre_insert_user_data', 'prefix_change_user_login', 10, 4 );
- Now craft a function
prefix_change_user_login
where you grab the $data
, the user_login
from the $_POST
and pass it through a number of validation and sanitisation (see https://github.com/WordPress/wordpress-develop/blob/bb27ffce6c3b10738008f9a054782945a0744960/src/wp-includes/user.php#L2076-L2498 for more details)
- Further of course you will have to allow actual edit of the user login field in the WP Admin Edit User screen, or, pass that value somehow else to your code.
An example of such function:
/**
* Update user login name
*
* @since x.x.x
* @param array $data {
* Values and keys for the user.
*
* @type string $user_login The user's login. Only included if $update == false
* @type string $user_pass The user's password.
* @type string $user_email The user's email.
* @type string $user_url The user's url.
* @type string $user_nicename The user's nice name. Defaults to a URL-safe version of user's login
* @type string $display_name The user's display name.
* @type string $user_registered MySQL timestamp describing the moment when the user registered. Defaults to
* the current UTC timestamp.
* }
* @param bool $update Whether the user is being updated rather than created.
* @param int|null $user_id ID of the user to be updated, or NULL if the user is being created.
* @param array $userdata The raw array of data passed to wp_insert_user().
*/
function prefix_change_user_login( $data, $update, $user_id, $userdata ){
// DO NOT FORGET TO ADD PROPER NONCE AND REFERRER VALIDATION HERE!!!!
$sanitized_user_login = sanitize_user( wp_unslash( $_POST['user_login'] ), true );// In our case we get the new user login from the POSTed form value in WP Admin. If this is different in your use case, adapt!!
/**
* Filters a username after it has been sanitized.
*
* This filter is called before the user is created or updated.
*
* @since 2.0.3
*
* @param string $sanitized_user_login Username after it has been sanitized.
*/
$pre_user_login = apply_filters( 'pre_user_login', $sanitized_user_login );
$illegal_logins = (array) apply_filters( 'illegal_user_logins', array() );
// Remove any non-printable chars from the login string to see if we have ended up with an empty username.
$user_login = trim( $pre_user_login );
if ( empty( $user_login )
|| mb_strlen( $user_login ) > 60
|| username_exists( $user_login )
|| in_array( strtolower( $user_login ), array_map( 'strtolower', $illegal_logins ), true )
) {
return $data;
}
$data['user_login'] = $user_login;
$user_nicename = sanitize_title( mb_substr( $user_login, 0, 50 ) );// Who knows what the WP Devs had smoked when they decided to make the nicename 10 characters less long than the login name?
global $wpdb;
$user_nicename_check = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->users WHERE user_nicename = %s AND user_login != %s LIMIT 1", $user_nicename, $user_login ) );
if ( $user_nicename_check ) {
$suffix = 2;
while ( $user_nicename_check ) {
// user_nicename allows 50 chars. Subtract one for a hyphen, plus the length of the suffix.
$base_length = 49 - mb_strlen( $suffix );
$alt_user_nicename = mb_substr( $user_nicename, 0, $base_length ) . "-$suffix";
$user_nicename_check = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->users WHERE user_nicename = %s AND user_login != %s LIMIT 1", $alt_user_nicename, $user_login ) );
$suffix++;
}
$user_nicename = $alt_user_nicename;
}
$data['user_nicename'] = $user_nicename;
return $data;
}
Now this will allow you to update the user login name, and it will also update the user nicename (used in archives for example)
This of course still requires you to "enable" editing on the WP Admin area, but that shouldn't be too hard
Note that if any error happens in above code (username exists already, YadaYada) NO error is shown, instead, the user just updates and fallsback to what was prior.
Note that while this code is tested, it is up to you what you do with it, and there are REASONS why wp does not allow to update the user login