Repository navigation
Conversation
…e to an email address.
When an account is created with an email address as the username (user_login), defaulting display_name, nickname, or user_nicename to that username exposes the user's email address publicly in post bylines, feeds, comment markup, REST API responses, and author archive URLs.
This changeset updates user handling:
* In wp_insert_user(), when $user_login is an email address and no explicit names are supplied, display_name and nickname fall back to a neutral string ('User') instead of the email address.
* In wp_insert_user(), user_nicename is generated as an unexposed random slug (user-{random}) rather than slugifying the email address.
* When updating an existing user with an email username and no user_nicename is passed, the existing user_nicename is preserved.
* In user-edit.php and user-profile.js, display_username is excluded from the "Display name publicly as" options when the username is an email address.
* Adds dedicated unit tests in tests/phpunit/tests/user/wpInsertUser.php covering new user creation, complex email addresses, explicit name overrides, existing user updates, and filter interactions.
Props therssoftware.
Fixes #66446.
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. Props Bot commandsYou can trigger Props Bot actions by commenting on this PR:
|
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
|
Thanks for the patch! I tested it locally on single-site and multisite, and the account-default changes passed. I left you feedback on Trac suggesting the profile dropdown changes should not go into this initial fix: https://core.trac.wordpress.org/ticket/66446 |
Restores user-edit.php and user-profile.js so the "Display name publicly as" dropdown remains unchanged, focusing this changeset strictly on account defaults for newly registered users in wp_insert_user(). Props therssoftware, dpknauss. See #66446.
|
Some review observations: The patch covers all three parts of the proposal on Trac #66446: no email fallback for A few notes: On the neutral default
Smaller notes
Test gaps
|
…lose test gaps.
Updates wp_insert_user() to use bin2hex( random_bytes( 4 ) ) for generating
an unexposed, unique random suffix for email logins ('User {suffix}' and
'user-{suffix}'). Scopes nicename preservation on update strictly to email logins.
Aligns user-edit.php and user-profile.js via data-is-email attribute.
Adds tests for downstream privacy, multisite, legacy account updates,
and profile options.
Props therssoftware, dpknauss.
See #66446.
|
Thanks for the thorough and insightful review, @dknauss! I've pushed updates addressing all of your observations:
|
There was a problem hiding this comment.
Thanks for the update. I tested 53264a2 on disposable single-site and multisite installs using PHP 8.4 and SQLite. New accounts created through wp_create_user() and wpmu_create_user() got matching anonymous display-name, nickname, and slug suffixes. Public REST names, REST slugs, and author URLs reflected the anonymous defaults. wp_update_user() preserved an explicitly saved legacy email display name.
I found one update regression in both installs. Two calls to wp_insert_user() with the same existing ID and login, changing only the description and leaving the public name fields out, generated two different display names and nicknames. The author slug stayed fixed. An integration that calls wp_insert_user() directly can therefore change someone’s public byline every time it syncs their profile. Could we preserve the stored display name and nickname when those keys are omitted on update and add coverage for that case? The current legacy test exercises wp_update_user(), which merges stored fields before calling wp_insert_user().
One test gap remains on the profile screen. The new test builds its own array by repeating the condition from user-edit.php. It would still pass if the dropdown or its JS blur handler offered the email login. Could the test exercise the actual profile dropdown, both on initial render and after editing the nickname?
AI assistance: Yes. OpenAI Codex ran the PHP fixtures, inspected the patch, and drafted this review with me.
…le display options. - In wp_insert_user(), preserve $old_user_data->display_name and $old_user_data->nickname on update when those fields are omitted from $userdata, preventing successive sync updates from regenerating random display names. - Introduce wp_get_user_profile_display_name_options() in wp-admin/includes/user.php to encapsulate public display options logic and exclude email logins. - Update wp-admin/user-edit.php to use wp_get_user_profile_display_name_options(). - Add unit tests covering wp_insert_user() update preservation with omitted name fields. - Update profile screen tests to exercise wp_get_user_profile_display_name_options() across initial creation and nickname updates. Props therssoftware, dpknauss. See #66446.
|
@dknauss Thanks for the detailed review and catching that update regression! I have addressed both items in commit 28fbdd3:
Both single-site ( |
dknauss
left a comment
There was a problem hiding this comment.
Thanks for turning this around so quickly. I tested a5fc071 on PHP 8.4 with MySQL 8.4. Tests_User_WpInsertUser passes on single site (16 tests, 89 assertions) and multisite (17 tests, 93 assertions), and the full user group passes on both. PHPCS reports no new warnings in the changed files. Both of my earlier points are addressed: repeated wp_insert_user() calls no longer regenerate the public name, and the dropdown options now come from one function that the test calls directly.
A few things before this is ready for a committer:
-
The update preservation now applies to every user, not only email logins. On trunk, a direct
wp_insert_user()update that omitsdisplay_nameornicknameresets both touser_login. With this patch, every account keeps its stored values instead. For a standard user with the loginprobestd, display name "Chosen Name", and nickname "ChosenNick", a direct update that only setsdescriptionresets both names toprobestdon trunk and keeps them with this patch. I think that's the better behaviour, but it's a change to a long-standing default outside #66446's scope, and the@sincenote only mentions email logins. Could we either scope the preservation to email-login accounts, which is all the ticket needs, or keep it broad and call it out on the ticket and in the@sincenote so a committer can decide? -
The JavaScript path is still untested. The new test covers the PHP options, but the
blurhandler inuser-profile.jsbuilds its own list, and that's where an edited nickname changes the dropdown. There's no QUnit coverage foruser-profile.jsyet, so this would be a new test file. One that setsdata-is-emailon#user_login, blurs#nickname, and checks that no option equals the login would close that gap. If that's more than this PR should take on, a note on the ticket describing how it was tested by hand would help. -
New public API during beta. Trunk is at 7.2-beta5, so
@since 7.2.0on the new function, and on thewp_insert_user()changelog, probably needs to become the release this actually lands in. A new public function inwp-admin/includes/user.phpis also an API commitment. Marking it private (an underscore prefix or@access private) would keep the testability without that commitment.
AI assistance: Claude ran the tests and the trunk comparison, and drafted this review with me.
… coverage. - In wp_insert_user(), scope stored display_name and nickname preservation strictly to email-login accounts (is_email( $user_login )), keeping trunk's existing behavior intact for standard non-email accounts. - Mark profile display options helper as private by renaming to _wp_get_user_profile_display_name_options() with @access private in wp-admin/includes/user.php, avoiding public API commitment during beta. - Add QUnit test coverage in tests/qunit/wp-admin/js/user-profile.js testing nickname blur behavior with data-is-email on #user_login. - Add unit test coverage in tests/phpunit/tests/user/wpInsertUser.php verifying that standard accounts reset omitted name fields on update while email logins preserve them. Props therssoftware, dpknauss. See #66446.
|
Thanks for the thorough review and catch, @dknauss! All three points are addressed in the latest commit:
|
dknauss
left a comment
There was a problem hiding this comment.
Thank you, @robelsust. I tested 88e7d3c on PHP 8.4 with MySQL 8.4. The full user group passes on single site (1,490 tests) and multisite (1,553 tests), and Tests_User_WpInsertUser passes on both (16 and 17 tests). PHPCS reports the same warnings on the changed files as trunk, and nothing new.
All three points are addressed:
- Update preservation is now limited to email logins. Both the
nicknameanddisplay_namebranches checkis_email( $user_login ), the same guard the existinguser_nicenamelogic uses. The newprobestdassertions confirm that ordinary accounts reset touser_loginas they do on trunk. - The helper is private.
_wp_get_user_profile_display_name_options()is marked@access private, so there's no new public API. - The JavaScript path is tested. CI's QUnit job reports 718 assertions on this head, against 713 on trunk, so the five new assertions run and pass. The manual steps on the ticket cover the rest.
Two small things to note; neither is a blocker:
- The second QUnit test removes
data-is-emailfrom the shared fixture, and the first test'sCustomNickoption is still in the select. The tests pass in order. A fixture inside#qunit-fixture, which QUnit resets after each test, would keep them independent. @since 7.2.0on the helper and in thewp_insert_user()changelog may need to change to the release this lands in. That's for the committer to decide, and this looks ready for a committer to me!
AI assistance: Claude ran the tests, compared the results with trunk, and I drafted this review with the results and draft details.
Trac ticket: #66446
What
Prevents WordPress from automatically using an email address as a user's public
display_name,nickname, oruser_nicenamewhen accounts are created with an email address as their username (user_login).Why
Many sites and plugins (membership plugins, WooCommerce, SSO integrations, self-registration) register users whose
user_loginis formatted as an email address. Currently:display_name: Falls back to$user_login(john@example.com) if first/last names are absent. This gets exposed in post bylines viaget_the_author(), RSS/Atom feeds (<dc:creator>), oEmbed data, and the public REST API (/wp/v2/users).nickname: Defaults to$user_login(john@example.com).user_nicename: Generatessanitize_title( $user_login )(johnexample-com), exposing the user's email domain and local-part in author archive URLs (/author/johnexample-com/), sitemaps, and comment CSS classes (comment-author-{nicename}).user-edit.php&user-profile.js: Unconditionally offersuser_loginin the "Display name publicly as" select dropdown.Because WordPress accounts allow authenticating with email addresses (since 4.5), exposing the email address as a public display name and URL slug presents both an account enumeration surface and a privacy violation (GDPR Art. 25(2) - data protection by default).
How
wp_insert_user()insrc/wp-includes/user.php:display_name&nickname: When$user_loginis an email address (is_email( $user_login )) and no explicit names are provided, default to a neutral string (__( 'User' )) instead of the email address. Explicit first/last names or explicitdisplay_nameinputs still take precedence.user_nicename: When$user_loginis an email address and no explicit nicename is passed, generate an unexposed random slug ('user-' . strtolower( wp_generate_password( 8, false, false ) )) rather than slugifying the email domain into the URL.$update = true) where no newuser_nicenameis provided in$userdata, preserve$old_user_data->user_nicenameso existing accounts and author links are never altered.@since 7.2.0changelog and@typedescriptions for$display_name,$nickname, and$user_nicename.src/wp-admin/user-edit.php:$public_display['display_username']from the "Display name publicly as" dropdown whenis_email( $profile_user->user_login )is true.src/js/_enqueues/admin/user-profile.js:blur.user_profileevent handler, avoid re-addingdisplay_usernameif the username contains an@/ email address.Dedicated Unit Tests (
tests/phpunit/tests/user/wpInsertUser.php):wp_create_user(), complex email addresses, and filter overrides (pre_user_display_name,pre_user_nickname,pre_user_nicename).Testing Instructions
Automated Tests
Run PHPUnit tests across single-site and multisite:
Expected output:
OK (11 tests, 42 assertions)on both single-site and multisite.0 errors, 0 warningson PHPCS.Manual Testing
wp_create_user( 'member@example.com', 'password', 'member@example.com' )).member@example.comand Email =member@example.comwithout providing First Name or Last Name.User, notmember@example.com./author/user-{random}/, not/author/memberexample-com/.regularuser):display_name,nickname, anduser_nicenametoregularuseras before.