Problem/Motivation

The same way we have getPassword() and getEmail() which return the raw values, I would expect getUsername() to follow the same logic, and not return a formatted user name.

Simpletest for example is under the assumption that getUsername() return the value used to log in a user (the username) when it in fact returns the formatter user name (aka real name). See #1929420-22: $account->getDisplayName() should be used when outputing username in RDF module for an example where this fails.

Moreover, the user edit form uses getUsername() as default value for the username field, and getUsername() can potentially be altered (e.g. realname module)

Proposed resolution

Align getUsername() with getPassword() and getEmail() and return the raw username value (used to login for example). Use getDisplayName() to get the formatted user name.

Remaining tasks

change record

User interface changes

none

API changes

getUsername() returns the raw/db username. getDisplayName() returns the formatted user name.

Beta phase evaluation

Reference: https://www.drupal.org/core/beta-changes
Issue category Bug because the API is not consistent with other get*() methods.
Issue priority Critical because if a module implements hook_user_format_name() and changes the display output of the user name the account edit form causes data loss and could break login
Unfrozen changes Unfrozen because it a bug.
Disruption Disruptive for core/contributed and custom modules/themes because it will require widespread changes.
CommentFileSizeAuthor
#236 2112679-236.patch45.4 KBdawehner
#236 interdiff.txt1.76 KBdawehner
#232 getusername_should-2112679-232.patch589 bytesnlisgo
#232 interdiff-2112679-230-232.txt589 bytesnlisgo
#230 interdiff.txt5.61 KBdawehner
#230 2112679-230.patch45.4 KBdawehner
#226 2112679-226.patch45.37 KBstefan.r
#226 interdiff-223-226.txt948 bytesstefan.r
#224 2112679-223.patch45.36 KBstefan.r
#224 interdiff-221-223.txt1.75 KBstefan.r
#221 interdiff-213-221.txt2.71 KBstefan.r
#221 2112679-221.patch44.68 KBstefan.r
#213 getusername_should-2112679-213-interdiff.txt8.7 KBberdir
#213 getusername_should-2112679-213.patch44.45 KBberdir
#210 getusername_should-2112679-210-interdiff.txt6.53 KBberdir
#210 getusername_should-2112679-210.patch44.43 KBberdir
#207 getusername_should-2112679-206-interdiff.txt3.25 KBberdir
#207 getusername_should-2112679-206.patch43.41 KBberdir
#200 getusername_should-2112679-200-interdiff.txt3.22 KBberdir
#200 getusername_should-2112679-200.patch43.26 KBberdir
#199 iberify-module.diff.txt803 bytesznerol
#195 getusername_should-2112679-194-interdiff.txt23.2 KBberdir
#194 getusername_should-2112679-194.patch41.88 KBberdir
#190 getusername_should-2112679-190-interdiff.txt2.32 KBberdir
#190 getusername_should-2112679-190.patch41.07 KBberdir
#186 getusername_should-2112679-186.patch42.07 KBznerol
#186 interdiff.txt551 bytesznerol
#180 interdiff.txt34.1 KBsubhojit777
#180 getusername_should-2112679-180.patch41.99 KBsubhojit777
#175 getusername_should-2112679-174.patch41.99 KBnlisgo
#175 interdiff-2112679-163-174.txt1.13 KBnlisgo
#172 getusername_should-2112679-172.patch1.04 MBnlisgo
#172 interdiff-2112679-163-172.txt1.13 KBnlisgo
#163 interdiff-2112679-161-163.txt1.72 KBsubhojit777
#163 getusername_should-2112679-163.patch42 KBsubhojit777
#160 getusername_should-2112679-160.patch41.81 KBnlisgo
#160 interdiff-2112679-158-160.txt1.03 KBnlisgo
#158 user-displayname-2112679-158.patch41.81 KBberdir
#157 user-displayname-2112679-155.patch41.79 KBborisson_
#157 interdiff.txt6.76 KBborisson_
#156 user-displayname-2112679-155-interdiff.txt1.83 KBberdir
#156 user-displayname-2112679-155.patch41.79 KBberdir
#153 2112679-153-interdiff.txt1.77 KBlokapujya
#153 2112679-153.patch39.81 KBlokapujya
#151 interdiff.txt714 bytesjeroent
#151 getusername_should-2112679-151.patch39.44 KBjeroent
#149 getusername_should-2112679-149.patch39.44 KBdustinleblanc
#146 getusername_should-2112679-143.patch39.24 KBdustinleblanc
#142 getusername_should-2112679-142.patch39.79 KBs_leu
#139 getusername_should-2112679-139.patch40.89 KBsubhojit777
#135 user_getusername_-2112679-135.patch40.88 KBamitgoyal
#122 user_getusername_-2112679-122.patch41.97 KBnlisgo
#122 interdiff-2112679-118-122.txt1.06 KBnlisgo
#119 user_getusername_-2112679-118.patch40.91 KBnlisgo
#115 user_getusername_-2112679-115.patch41.4 KBm1r1k
#115 interdiff.txt4.66 KBm1r1k
#112 interdiff.txt9.56 KBm1r1k
#112 user_getusername_-2112679-112.patch42.74 KBm1r1k
#110 interdiff.txt9.56 KBm1r1k
#110 user_getusername_-2112679-110.patch90.55 KBm1r1k
#108 user-get-username-2122679-108.patch38.34 KBm1r1k
#105 interdiff-2112679-102-105.txt0 byteskerby70
#105 user_getusername_-2112679-105.patch99.39 KBkerby70
#102 user_getusername_-2112679-102.patch95.88 KBkerby70
#100 2112679-100.patch96.33 KBpiyuesh23
#98 2112679-98.patch96.87 KBpiyuesh23
#96 2112679-96.patch45.02 KBpiyuesh23
#94 2112679-94.patch44 KBpiyuesh23
#85 interdiff-85.txt14.97 KBkrlucas
#85 2112679-85.patch45.53 KBkrlucas
#78 interdiff-2112679-77.txt63.51 KBdamiankloip
#78 2112679-77.patch102.07 KBdamiankloip
#68 interdiff-2112679-68.txt16.84 KBdamiankloip
#68 2112679-68.patch45.58 KBdamiankloip
#65 2112679-65-interdiff.txt1.78 KBberdir
#65 2112679-65.patch42.32 KBberdir
#63 interdiff-2112679-63.txt1.16 KBdamiankloip
#63 2112679-63.patch40.54 KBdamiankloip
#60 2112679-58.patch40.4 KBberdir
#58 2112679-58.patch40.62 KBberdir
#57 2112679-57.patch42.92 KBberdir
#55 interdiff-2112679-51-53.txt847 byteslokapujya
#53 interdiff.txt1.45 KBlokapujya
#53 2112679-53.patch42.69 KBlokapujya
#51 interdiff.txt1.45 KBlokapujya
#51 2112679-51.patch42.71 KBlokapujya
#48 interdiff-2112679-48.txt3.1 KBdamiankloip
#48 2112679-48.patch42.74 KBdamiankloip
#46 interdiff.txt752 byteslokapujya
#46 2112679-46.patch40.25 KBlokapujya
#44 interdiff-2112679-44.txt2.32 KBdamiankloip
#44 2112679-44.patch40.37 KBdamiankloip
#43 interdiff-2112679-43.txt748 bytesdamiankloip
#43 2112679-43.patch38.53 KBdamiankloip
#40 2112679-40.patch38.5 KBlokapujya
#34 interdiff-34-29.txt2.51 KBkrlucas
#34 user_getusername-2112679-34.patch38.82 KBkrlucas
#29 2112679-get_username-29.patch38.85 KBkrlucas
#29 interdiff-2112679-24-29.txt2.77 KBkrlucas
#24 interdiff-2112679-13-24.txt16.18 KBkrlucas
#24 2112679-get_username-24.patch35.9 KBkrlucas
#18 interdiff-2112679-13-18.txt16.27 KBkrlucas
#18 2112679-get_username-18.patch35.55 KBkrlucas
#13 2112679-get_username-13.patch18.25 KBberdir
#10 2112679-get_username-10.interdiff.txt11.97 KBblueminds
#10 2112679-get_username-10.patch18.59 KBblueminds
#8 2112679-get_username-8.interdiff.txt710 bytesblueminds
#8 2112679-get_username-8.patch7.68 KBblueminds
#7 2112679-get_username-7.interdiff.txt4.13 KBblueminds
#7 2112679-get_username-7.patch7.16 KBblueminds
#5 2112679-4-getUsername.patch3.03 KBkrlucas
#1 2112679_1_getUsername.patch1.11 KBscor

Comments

scor’s picture

Status: Active » Needs review
StatusFileSize
new1.11 KB
berdir’s picture

Not sure. getUsername() *is* the replacement for user_format_name().

Most use cases in core use it like that. If we change it, then we need to provide an alternative. Or, provide a new method to get the raw name, where appropriate.

getUsername() is the same as label() (user specific vs. entity generic, like getTitle() and label() on nodes), which *always* returned the formatted username. This is the one to use in most cases, so I think it should be the default.

scor’s picture

getUsername() *is* the replacement for user_format_name().

That might be the way you coded it, but all I'm saying is that from a DX perspective, the above is not intuitive, at least not for someone like me who didn't participate in the design of getUsername().

IMO something like getFormattedName() or getName() is more intuitive for a replacement of user_format_name(). getUsername() gives no hint that what you get back might not be the username, but something that a module might have made up. If you are looking to align it with node's getTitle(), then calling it getName() makes sense:

$user->getName();
scor’s picture

+++ b/core/modules/user/user.module
@@ -163,7 +163,9 @@ function user_uri($user) {
 function user_label($entity_type, $entity) {
...
+  $name = $entity->getUsername();
+  \Drupal::moduleHandler()->alter('user_format_name', $name, $this);
+  return $name;
 }
 

user_label() was removed, so this code should be moved to user_format_name() now. user_format_name() should not return $entity->getUsername() directly, but instead pass it throughthe module handler alter function.

krlucas’s picture

Issue summary: View changes
StatusFileSize
new3.03 KB

@scor and I talked to @moshe weitzman and he agreed that getUsername should return the user's unique log in name and that another method should return the display name. Most of core and contrib should call getDisplayName but getting access to the value needed for log in seems legit.

This patch adds a getDisplayName method to the AccountInterface and updates the method to UserSession class and User entity class.

Status: Needs review » Needs work

The last submitted patch, 5: 2112679-4-getUsername.patch, failed testing.

blueminds’s picture

Status: Needs work » Needs review
StatusFileSize
new7.16 KB
new4.13 KB

Fixed failed tests, added test coverage.

blueminds’s picture

forgot to update comment user name

berdir’s picture

Status: Needs review » Needs work
+++ b/core/lib/Drupal/Core/Session/AccountInterface.php
@@ -112,7 +112,18 @@ public function getPreferredLangcode($default = NULL);
+   *   An unsanitized string with the unique username. The code receiving
+   *   this result must ensure that \Drupal\Component\Utility\String::checkPlain()
+   *   is called on it before it is
+   *   printed to the page.

Weird wrapping, the last part shouldn't be on a separate line.

Patch looks good, but I think we need to review the current usage of getUsername() and change to getDisplayName() wherever we want to display the formatted username.

Some that I think need to be updated:

- comment_preview():

    if (!empty($account) && $account->isAuthenticated()) {
      $comment->setOwner($account);
      $comment->setAuthorName(check_plain($account->getUsername()));
    }
    elseif (empty($author_name)) {
      $comment->setAuthorName(\Drupal::config('user.settings')->get('anonymous'));
    }

Can be simplified a lot with getDisplayName(). That said, comment author name is actually a bit tricky, see also preSave(). Not sure what to do there, as it is used as the form value in some cases... But not using the display name would mean that the alter wouldn't work?

- ContactController::contactPersonalPage()
- template_preprocess_forums()
- Rss::render()
- Not sure why rdf_preprocess_user() in the referenced issue did not want to use the display name?
- shortcut_set_switch_submit()
- A few in user.module I think and user.pages.inc I think
- RegisterForm
- UserAutocomplete::getMatches()
- UserSearch

blueminds’s picture

Status: Needs work » Needs review
StatusFileSize
new18.59 KB
new11.97 KB

I addressed following:
- ContactController::contactPersonalPage()
- template_preprocess_forums()
- Rss::render() (only node)
- Not sure why rdf_preprocess_user() in the referenced issue did not want to use the display name?
- shortcut_set_switch_submit()
- A few in user.module I think and user.pages.inc I think
- UserAutocomplete::getMatches()
- UserSearch

lets see what test bot says.

Comments are more tricky. I think for the author field we should already store the displayName value. However there are two places where the author value is used to load the user - comment_preview() and CommentFormController::submit(). I suggest to rework these two places to load the user entity differently and then the preSave() can directly set as the author the displayName.

berdir’s picture

Something else that I noticed is the user:name token. We probably need a token there for the raw username and one for the formatted, if you look at things like the registration confirmation mails, they use user:name twice, when addressing the user, it should use the formatted version and when it's used for the login information, it should be the actual user name.

berdir’s picture

StatusFileSize
new18.25 KB

psr-4 re-roll.

Status: Needs review » Needs work

The last submitted patch, 13: 2112679-get_username-13.patch, failed testing.

berdir’s picture

Looks like a random fail:

Drupal\system\Tests\Cache\ApcuBackendUnitTest
Created time is correct. Other GenericCacheBackendUnitTestBase.php 172 Drupal\system\Tests\Cache\GenericCacheBackendUnitTestBase->testSetGet()

berdir’s picture

Status: Needs work » Needs review

13: 2112679-get_username-13.patch queued for re-testing.

krlucas’s picture

Assigned: Unassigned » krlucas

I'll work on updating token support and the comment module related issues.

krlucas’s picture

StatusFileSize
new35.55 KB
new16.27 KB

Updated the user token support and references to the [user:name] token that I could find.

Status: Needs review » Needs work

The last submitted patch, 18: 2112679-get_username-18.patch, failed testing.

scor’s picture

+++ b/core/modules/user/src/Tests/UserTokenReplaceTest.php
@@ -81,9 +83,11 @@ function testUserTokenReplacement() {
-    $tests['[user:name]'] = check_plain(user_format_name($account));
+    $tests['[user:username]'] = check_plain($account->getUsername());
+    $tests['[user:displayname]'] = check_plain($account->getDisplayName());

We should probably keep the [user:name] to maintain backward compatibility with sites upgrading from D7, but what value should it get?

krlucas’s picture

Assigned: krlucas » Unassigned

[user:name] should probably get $user->getUsername() since D7 doesn't use user_format_name() when it does the replacements for e-mails, filepaths.

Also, [comment:author:name] and [node:author:name] need to be updated. This is probably the trickiness @blueminds was referring to in #10 as the comment auth values are stored in the comment table, not pulled from an associated user. Further, some places (according to #10) might use the stored name in the comment table to retrieve the user object. If we started storing the altered username those things would break.

berdir’s picture

Actually, if you look at d7 user_tokens(), it *does* use format_username(), as does 8.x right now.

Dave Reid might be able to help here, he maintains realname.module, one of the main reasons for that hook to even exist and he maintains the token module and I think I've seen him mentioning this problem before I think.

scor’s picture

That's easy then, [user:name] can just map to $account->getDisplayName(), and there is no need to introduce [user:displayname]. We can also have [user:username] be getUserName().

@berdir: are you in Austin?

krlucas’s picture

StatusFileSize
new35.9 KB
new16.18 KB

Ok. New patch adds the [user:username] token. [user:name] is mapped to getDisplayName. I also found some other instances of user_format_name().

Still need to figure the node:author:name token and the comment->author (including the token).

blueminds’s picture

Status: Needs work » Needs review

just to have the testbot trigger

blueminds’s picture

+++ b/core/modules/user/user.tokens.inc
@@ -25,7 +25,11 @@ function user_token_info() {
+    'name' => t("Display Name"),

As far as I remember, to distinguish between processed and non-processed value the token module uses "raw" in the token name/description. Shouldn't this pattern be used here as well to clearly communicate if the username value is processed or comes directly from the database?

krlucas’s picture

Assigned: Unassigned » krlucas
Status: Needs review » Needs work

@blueminds good point. I'll check token module and re-roll the patch to keep it consistent.

krlucas’s picture

Token module itself doesn't define a "raw" token. Realname does, but it also has a different name for the "display name". I'm going to leave as is for now in case anyone else has an opinion on the token naming.

Diving into the Comment author related stuff mentioned in #24.

krlucas’s picture

StatusFileSize
new2.77 KB
new38.85 KB

The comment stuff was weird, as @blueminds mentioned the form submit and preview were using the author name to load the user and set the comment owner.

Instead I followed the advice of the "todo" and set the uid for the comment in the form constructor. Then the parent form submit builds the comment entity and sets the owner properly.

Patch attached.

krlucas’s picture

Assigned: krlucas » Unassigned
Status: Needs work » Needs review

Unassigning and setting to needs review.

blueminds’s picture

Status: Needs review » Needs work

Thanks, looks good!

A few comments:

  1. +++ b/core/lib/Drupal/Core/Session/UserSession.php
    @@ -217,6 +217,14 @@ function getPreferredAdminLangcode($default = NULL) {
       public function getUsername() {
    +    $name = $this->name ?: '';
    +    return $name;
    +  }
    

    This can be on one line, no need to create a new variable here. Same below in User::getUsername()

  2. +++ b/core/modules/user/user.tokens.inc
    @@ -25,7 +25,11 @@ function user_token_info() {
       $user['name'] = array(
    -    'name' => t("Name"),
    +    'name' => t("Display Name"),
    +    'description' => t("The display name of the user account."),
    +  );
    +  $user['username'] = array(
    +    'name' => t("Login Name"),
         'description' => t("The login name of the user account."),
       );
    

    I still think this is not self explanatory enough. If you look into template_preprocess_username() there we have a variable 'name_raw'. The token however uses just 'name' and introduces 'username'. Moreover we have the API method getDisplayName(). So we have four namings and just two things. I suggest either the token to use name_raw for the DB value or let's introduce 'display_name'. I prefer the later as we already have getDisplayName(), so making it uniform makes sense (also in the templates). To justify: if you look at 'name' and 'username' none of them really says which one comes formatted.

blueminds’s picture

+++ b/core/modules/user/user.module
@@ -638,7 +638,7 @@ function template_preprocess_username(&$variables) {
+  $name = $variables['name_raw'] = $account->getDisplayName();

Also this seems to be not right, as the raw value is expected to be as *is* from DB. getDisplayName() will get already processed value.

krlucas’s picture

@blueminds I know what you mean. However, see @scor's comment in #20. He makes a good point that people moving from D7 to D8 will need to update all their [user:name] tokens. Perhaps we leave [user:name] as "deprecated" and map it and a new token [user:displayname] to getDisplayName().

RE: #32 it's definitely not intuitive but that's the functionality in D7: https://api.drupal.org/api/drupal/includes%21theme.inc/function/template...

Per the comments "name_raw" in template_preprocess_username() is still the formatted username--but unshortened and unsanitized. Do we want to change the meaning of "name_raw" in that context?

I'll roll a patch with fixes for 1. in #31.

krlucas’s picture

Status: Needs work » Needs review
StatusFileSize
new38.82 KB
new2.51 KB

Here's the patch re-rolled to now reduce the size of the getUsername methods per first part of #31.

blueminds’s picture

Yup, I see. Other than that, I think it can be RTBC. Me coded part of it, so someone else please :)

berdir’s picture

Status: Needs review » Needs work

The views user name field doesn't work correctly yet I think, create a list of users as table with fields, the display formatted user name (which is checked by default) has no effect.

scor’s picture

Issue tags: +RDF code sprint
lokapujya’s picture

lokapujya’s picture

Status: Needs work » Needs review
StatusFileSize
new38.5 KB

Reroll, will test.

lokapujya’s picture

Status: Needs review » Needs work

Tested: The "Use formatted username" in views doesn't change anything.

How do you get a formatted name that is different form the raw name?

I created a user_format_name_alter() and that changes the name (Is that what a formatted name is?), but the views setting doesn't affect the altered name; It always uses the altered name. Tried username_alter() but that didn't change the name at all.

The last submitted patch, 34: user_getusername-2112679-34.patch, failed testing.

damiankloip’s picture

StatusFileSize
new38.53 KB
new748 bytes

I guess you still had the link to user option checked too? This will then use theme_username regardless, which means you will always get the display name if you check to use a link. I will open a separate issue for that.

damiankloip’s picture

Status: Needs work » Needs review
StatusFileSize
new40.37 KB
new2.32 KB

Actually, might as well roll it in here. It's dependent on this anyway.

@lokapujya, these changes should work as expected. If you could test again, that would be great. This is a quick patch - I don't have much time. Should work though :)

Status: Needs review » Needs work

The last submitted patch, 44: 2112679-44.patch, failed testing.

lokapujya’s picture

Status: Needs work » Needs review
StatusFileSize
new40.25 KB
new752 bytes

#44 fixes the problem mentioned in #41, however it seems like renderLink() needs an authenticated user. I'm not sure how to fix it, but here is some code that is passing tests for me.

damiankloip’s picture

That's a revert of what I changed in #44. It works but it is not right IMO - It's ugly and hacky. That's an artefact of D7 views.

We now have the entity attached to a row. Hence the call to getEntity() instead. So we need to get it working using that.

damiankloip’s picture

StatusFileSize
new42.74 KB
new3.1 KB

Thinking about it, what exactly is not working for you with the patch in #44?

getEntity() will return an entity which is what we want - just without the hackery. The tests just need adapting to reflect doing this properly IMO.

Status: Needs review » Needs work

The last submitted patch, 48: 2112679-48.patch, failed testing.

lokapujya’s picture

what exactly is not working for you with the patch in #44?

Nothing, I didn't mean so suggest that the code should be reverted; Just showing the exact part of the change that broke tests as they existed.

The remaining test failure is passing on my local environment.
EDIT: Well it was, until I did a git clean, and reapplied.

lokapujya’s picture

Status: Needs work » Needs review
StatusFileSize
new42.71 KB
new1.45 KB

Changed test to use the views result from the new user. Removed an unused variable.

Edit: Left in a debug that is not highlighted in the interdiff.

damiankloip’s picture

OK. Looks pretty good to me. I didn't actually test the patch, just converted the test. It was using the same Jacky approach that the code was before :) do you want to remove that debug? We are prod looking pretty good then I think.

lokapujya’s picture

StatusFileSize
new42.69 KB
new1.45 KB
damiankloip’s picture

Looks like the actual patch is correct (with debug() removed) but just the wrong interdiff attached?

Also, if possible, could you use the name of the issue-comment in the interdiff too? d.o will hate you for calling another file interdiff.txt :)

lokapujya’s picture

StatusFileSize
new847 bytes

interdiff do-over. :)

damiankloip’s picture

Yep :) looks good.

berdir’s picture

StatusFileSize
new42.92 KB

Re-roll.

berdir’s picture

Priority: Normal » Major
StatusFileSize
new40.62 KB

Another re-roll. Setting to a major bug because if you save the user edit form with a module that alters usernames, you end up with a different username and crazy bugs like that...

Status: Needs review » Needs work

The last submitted patch, 58: 2112679-58.patch, failed testing.

berdir’s picture

Status: Needs work » Needs review
StatusFileSize
new40.4 KB

Ups, had some junk in that patch file.

Status: Needs review » Needs work

The last submitted patch, 60: 2112679-58.patch, failed testing.

undertext’s picture

Agrh. Just started this issue https://www.drupal.org/node/2311219 and then saw this topic.
Maybe we should only add getDisplayName() and change logic of getUserName() in this issue, and do not replace user_format_name occurrences, or just close that issue ?
Anyway with the latest path 'label_callback' from core/modules/user/src/Entity/User model is till using user_format_name() function.
Need to deal with it.

damiankloip’s picture

Status: Needs work » Needs review
StatusFileSize
new40.54 KB
new1.16 KB

Let's see how this gets on. Amended the deprecated message to make it correct too.

I think we should not tackle that in this issue, as the callback will need to pass the callable from the annotation which we currently cannot do.

Status: Needs review » Needs work

The last submitted patch, 63: 2112679-63.patch, failed testing.

berdir’s picture

Status: Needs work » Needs review
StatusFileSize
new42.32 KB
new1.78 KB

I lost the test module along the way. Also added an explicit sort on that test view, the order of users was not reliable, not sure how that is related to this issue, though.

damiankloip’s picture

Ah ha! I assumed they were still in there :)

dawehner’s picture

  1. +++ b/core/lib/Drupal/Core/Session/AccountInterface.php
    @@ -114,7 +114,17 @@ public function getPreferredLangcode($default = NULL);
    +   *   An unsanitized string with the unique username. The code receiving this
    +   *   result must ensure that \Drupal\Component\Utility\String::checkPlain()
    +   *   is called on it before it is printed to the page.
    
    @@ -123,13 +133,13 @@ public function getPreferredAdminLangcode($default = NULL);
    -   *   An unsanitized string with the username to display. The code receiving
    +   * @return string
    +   *   An unsanitized string with the user name to display. The code receiving
        *   this result must ensure that \Drupal\Component\Utility\String::checkPlain()
        *   is called on it before it is
        *   printed to the page.
    

    It would be great if we could mention that the best way would be to use this just in templates.

  2. +++ b/core/modules/action/src/Plugin/Action/EmailAction.php
    @@ -135,7 +135,7 @@ public function buildConfigurationForm(array $form, array &$form_state) {
    +      '#description' => t('The message that should be sent. You may include placeholders like [node:title], [user:name], [user:username] and [comment:body] to represent data that will be different each time message is sent. Not all placeholders will be available in all contexts.'),
    

    I wonder whether we could avoid confusing if there would be user:displayname and user:username instead.

  3. +++ b/core/modules/comment/comment.module
    @@ -759,22 +759,6 @@ function comment_preview(CommentInterface $comment, array &$form_state) {
         // Attach the user and time information.
    

    I guess we can drop the user bit of the documentation.

  4. +++ b/core/modules/comment/src/CommentForm.php
    @@ -212,6 +212,12 @@ public function form(array $form, array &$form_state) {
     
    +    // Set the uid on new comments to the current user.
    +    $form['uid'] = array(
    +      '#type' => 'value',
    +      '#value' => $comment->id() ? $comment->getOwnerId() : $this->currentUser->id(),
    +    );
    +
    
    @@ -308,13 +314,7 @@ public function submit(array $form, array &$form_state) {
    -    // If the comment was posted by a registered user, assign the author's ID.
    -    // @todo Too fragile. Should be prepared and stored in comment_form()
    -    // already.
         $author_name = $comment->getAuthorName();
    -    if (!$comment->is_anonymous && !empty($author_name) && ($account = user_load_by_name($author_name))) {
    -      $comment->setOwner($account);
    -    }
    

    nice!

  5. +++ b/core/modules/user/src/Plugin/views/field/Name.php
    @@ -79,26 +69,29 @@ public function buildOptionsForm(&$form, &$form_state) {
       protected function renderLink($data, ResultRow $values) {
    -    $account = entity_create('user');
    -    $account->uid = $this->getValue($values, 'uid');
    -    $account->name = $this->getValue($values);
    -    if (!empty($this->options['link_to_user']) || !empty($this->options['overwrite_anonymous'])) {
    -      if (!empty($this->options['overwrite_anonymous']) && !$account->id()) {
    -        // This is an anonymous user, and we're overriting the text.
    -        return String::checkPlain($this->options['anonymous_text']);
    -      }
    -      elseif (!empty($this->options['link_to_user'])) {
    -        $account->name = $this->getValue($values);
    +    $account = $this->getEntity($values);
    +
    +    if (!empty($this->options['overwrite_anonymous']) && !$account->id()) {
    +      // This is an anonymous user, and we're overwriting the text.
    +      return String::checkPlain($this->options['anonymous_text']);
    +    }
    +
    +    if (!empty($this->options['link_to_user'])) {
    +      if (!empty($this->options['format_username'])) {
             $username = array(
               '#theme' => 'username',
               '#account' => $account,
             );
             return drupal_render($username);
           }
    +      else {
    +        return parent::renderLink($data, $values);
    +      }
         }
    

    looks perfect! Much simpler logic actually at the end of the day.

  6. +++ b/core/modules/user/src/Tests/UserTokenReplaceTest.php
    @@ -66,7 +67,8 @@ function testUserTokenReplacement() {
    +    $tests['[current-user:name]'] = String::checkPlain($global_account->getDisplayName());
    

    Same as with just user:

  7. +++ b/core/modules/user/src/Tests/Views/HandlerFieldUserNameTest.php
    @@ -25,29 +25,27 @@ class HandlerFieldUserNameTest extends UserTestBase {
       public static $testViews = array('test_views_handler_field_user_name');
    

    quick note: this view does not have a sort handler, at least we should add it on a followup due to given potential random failures.

  8. +++ b/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_views_handler_field_user_name.yml
    @@ -42,6 +42,13 @@ display:
             type: fields
    +      sorts:
    +        uid:
    +          id: uid
    +          table: users
    +          field: uid
    +          plugin_id: standard
    +          provider: views
    

    Nice, someone read my mind!

damiankloip’s picture

StatusFileSize
new45.58 KB
new16.84 KB

Here we go. Made those changes and discussed on irc - Let's go back to user:name like we had before and user:display-name for the display name?

berdir’s picture

+++ b/core/modules/user/config/install/user.mail.yml
@@ -1,28 +1,28 @@
-  body: "[user:name],\n\nYour account on [site:name] has been canceled.\n\n--  [site:name] team"
-  subject: 'Account details for [user:name] at [site:name] (canceled)'
+  body: "[user:display-name],\n\nYour account on [site:name] has been canceled.\n\n--  [site:name] team"
+  subject: 'Account details for [user:display-name] at [site:name] (canceled)'

Tokens are fine with me.

Is this something that the migration of those settings should care about? Possibly in a follow-up?

damiankloip’s picture

Yes, that's a good point indeed. And yes, I think that should be a follow up. This patch is big enough for this "simple" change :)

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

It would be great to fill a change notice or rather update an existing one, given how otherwise the mass of change notices would grow.

chx’s picture

Status: Reviewed & tested by the community » Needs review
Issue tags: +Needs issue summary update

I am very confused. One source of the confusion is the issue summary suggesting ->label() but the patch adding a new ->getDisplayName() . That, I guess is a simple change to the issue summary.

Then getUsername is return $this->name ?: ''; when just return $this->name should be fine. Isn't that so? The default is '' after all.

But the biggest confusion is the issue itself. So getUsername doesn't return the raw username? In what circumstances? That's not in the IS either. I mean, yeah, anonymous and that alter probably doesn't belong there but my problem is that getDisplayName is not the result of user_format_name() as one would expect but ... what? The name of method is decidedly odd and the distinction between getUsername and getDisplayName is not clear to me -- one wonders whether that's a case "listen to the code, it's trying to tell you something" or "chx is dumb and the doxygen is not helping". Is really the distinction only in anonymous + alter? If so then why are we adding a new method instead of moving those into user_format_name() ? I just don't get what and why happens here.

Tentatively setting to CNR.

berdir’s picture

I only understand half of what you said @chx, but here's how it is:

Current situation:
- getUsername() == label() == user_format_name(), which is the possibly altered username. Most common use case is realname module.

This is a problem because getUsername() is used as if it *would* be the raw username, for example it is the default value for the username field when editing a user.

So this issue adds a getDisplayName(), which is the same as label() and user_format_name() (which is deprecated, that is why we are adding a new method). getUsername() now returns the actual username, unchanged.

chx’s picture

Remind me then why we are not using label() ?

But if we go with getDisplayName, the summaries are not helpful: "Returns the unique username of this account." vs "Returns the display name of this account." I think what you wanted to say is "Returns the username as it is stored" vs "Returns the user name as it should be displayed" but then again the second is not right because it's not how it should be displayed because it needs to be checkPlain'd first :/ see this is a case of "listen to the code, it's trying to tell you something" after all -- why don't we checkPlain the return value of getDisplayName, hrm?

scor’s picture

Title: $user->getUsername() should return the username, not the formatted user name » $user->getUsername() should return the username and the formatted user name needs a new method
Issue summary: View changes

chx has a point regarding the label() method. Shouldn't we ensure that all entities have such method for consistency so one can always call ->label() on any entity?

berdir’s picture

what method exactly? All entities have label() and label on users is user_format_name()/getDisplayName().

label() is however currently not check plain'ed, so we can't check_plain() getDisplayName(). Yes, maybe it would make sense, but that's a completely different discussion and would result in a number of other problems, because sometimes, you need the label unescaped and it is very common to use the label in @something placeholders, so those would have to be updated to use !. So I disagree that we should do this here.

Update the issue summary and the docblocks, sure, if anyone has ideas on how to improve that...

chx’s picture

Amend the doxygen of EntityInterface::label() to clearly state

  1. It's not escaped
  2. It is not necessarily any property of the entity. AFAICS ContentEntityBase::label supports label callback on entities -- I think it's only for users that it is defined, user_format_name apparently.

once that's done, you can use label() freely for the formatted username. Next, why getUserName() ? Why not getName() since it returns $user->name->value ? What else would it be if not the name of the user? Node has getTitle and not getNodeTitle.

damiankloip’s picture

StatusFileSize
new102.07 KB
new63.51 KB

Here's the getUsername > getName change. That makes sense. Let's talk about chx's other point some more, as we ideally do not want to be keeping user_format_name. I would like to see us being able to support callbacks on the entity instance. So core could ship with a call to getDisplayName(), other people could then override easily if they wanted to by specifying their own label callback. thoughts?

Status: Needs review » Needs work

The last submitted patch, 78: 2112679-77.patch, failed testing.

damiankloip’s picture

Actually, sorry, I am going mad.

getUsername() is the current name already. Let's not change that in this issue chx.

chx’s picture

Can be a followup, for sure. Also, we can simply override User::label once we documented label as described in #77. We can even remove the "label callback" capability because that's a really weird thing in an OOP world -- just override label(), done.

mgifford’s picture

krlucas queued 78: 2112679-77.patch for re-testing.

The last submitted patch, 78: 2112679-77.patch, failed testing.

krlucas’s picture

Status: Needs work » Needs review
StatusFileSize
new45.53 KB
new14.97 KB

Re-rolled the patch from #68 (patch in 78 changed getUsername to getName which we're postponing for follow-up).

krlucas’s picture

I honestly don't really remember why we went with getDisplayName instead of just overriding label. If we do decide to use label() instead, can we get rid of all that get/set/hasLabelCallback business from entities in general?

lokapujya’s picture

" If we do decide to use label() instead, can we get rid of all that get/set/hasLabelCallback business from entities in general?"

Are we doing that here or in a followup? Would be nice to move this along.

mgifford queued 85: 2112679-85.patch for re-testing.

Status: Needs review » Needs work

The last submitted patch, 85: 2112679-85.patch, failed testing.

jeroent’s picture

Issue tags: +Needs reroll
nlisgo’s picture

This issue is affected by this: #2434697: Remove UserAutocompleteController

The UserAutocompleteController is now gone. We are instead to use entity_autocomplete.

nlisgo’s picture

piyuesh23’s picture

Assigned: Unassigned » piyuesh23
Issue tags: +#drupalgoa2015
piyuesh23’s picture

Assigned: piyuesh23 » Unassigned
Status: Needs work » Needs review
StatusFileSize
new44 KB

Patch re-rolled. Attaching the re-rolled patch below.

Status: Needs review » Needs work

The last submitted patch, 94: 2112679-94.patch, failed testing.

piyuesh23’s picture

Status: Needs work » Needs review
StatusFileSize
new45.02 KB

Missed out the test files. Attaching another patch with test files attached as well.

Status: Needs review » Needs work

The last submitted patch, 96: 2112679-96.patch, failed testing.

piyuesh23’s picture

Status: Needs work » Needs review
StatusFileSize
new96.87 KB

Re-rolled patch. Uploading the updated patch.

Status: Needs review » Needs work

The last submitted patch, 98: 2112679-98.patch, failed testing.

piyuesh23’s picture

Status: Needs work » Needs review
StatusFileSize
new96.33 KB

Removed duplicate functions & uploading the fixed patch.

Status: Needs review » Needs work

The last submitted patch, 100: 2112679-100.patch, failed testing.

kerby70’s picture

Issue tags: -Needs reroll
StatusFileSize
new95.88 KB

Quick reroll attached fixing patch failure after change in 8.0.x-dev.

jeroent’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 102: user_getusername_-2112679-102.patch, failed testing.

kerby70’s picture

Status: Needs work » Needs review
StatusFileSize
new99.39 KB
new0 bytes

Continued patch changes.

Status: Needs review » Needs work

The last submitted patch, 105: user_getusername_-2112679-105.patch, failed testing.

m1r1k’s picture

Assigned: Unassigned » m1r1k
m1r1k’s picture

Assigned: m1r1k » Unassigned
Status: Needs work » Needs review
StatusFileSize
new38.34 KB

Reroll and bunch of fixes.

Status: Needs review » Needs work

The last submitted patch, 108: user-get-username-2122679-108.patch, failed testing.

m1r1k’s picture

Status: Needs work » Needs review
StatusFileSize
new90.55 KB
new9.56 KB

Return lost changes and fix test failures

Status: Needs review » Needs work

The last submitted patch, 110: user_getusername_-2112679-110.patch, failed testing.

m1r1k’s picture

Status: Needs work » Needs review
StatusFileSize
new42.74 KB
new9.56 KB

Oops, wrong one.

Status: Needs review » Needs work

The last submitted patch, 112: user_getusername_-2112679-112.patch, failed testing.

m1r1k’s picture

Assigned: Unassigned » m1r1k
Related issues: +#2454145: Replace user_name handler with Field API formatter

Has to be rerolled after this one

m1r1k’s picture

Assigned: m1r1k » Unassigned
Status: Needs work » Needs review
StatusFileSize
new4.66 KB
new41.4 KB

Rerolled

m1r1k’s picture

Issue tags: +drupaldevdays
mile23’s picture

Status: Needs review » Needs work
Issue tags: +Needs reroll

Not sure about the rest of it, but core/modules/user/src/Entities/User.php says this in the entity definition annotation:

 *   label_callback = "user_format_name",

Note that the entity annotation docs don't tell you what to do for label_callback: https://www.drupal.org/node/2207559

This issue is a blocker for #2311219: Fix hook_user_format_name_alter() documentation and stop referring to user_format_name() which means we can't finish deprecating user_* functions until this is decided.

nlisgo’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll

This is a re-roll as getting rid of a change to hook_rebuild as list_themes() is no longer available since #2151469: Clean-up usage of deprecated list_themes() and _system_rebuild_theme_data() in favor of theme_handler service

nlisgo’s picture

StatusFileSize
new40.91 KB

oh, here's the patch :)

nlisgo’s picture

All other lines in this patch seem related to this issue.

Status: Needs review » Needs work

The last submitted patch, 119: user_getusername_-2112679-118.patch, failed testing.

nlisgo’s picture

Status: Needs work » Needs review
StatusFileSize
new1.06 KB
new41.97 KB

Status: Needs review » Needs work

The last submitted patch, 122: user_getusername_-2112679-122.patch, failed testing.

Status: Needs work » Needs review
mile23’s picture

Title: $user->getUsername() should return the username and the formatted user name needs a new method » getUsername() should return the username getDisplayName() for the formatted user name
Issue summary: View changes
Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs issue summary update +Needs change record

Thanks for sticking with it, @nlisgo.

Updated the issue summary, pretty sure this will need a change record.

Hopefully some of the folks from early in the comment thread can weigh in as well.

berdir’s picture

Status: Reviewed & tested by the community » Needs work

Didn't verify that all places have been updated but the patch looks good to me.

I've been using an old version of the patch here for almost a year now, some places in Drupal are really broken in HEAD when you use hook_user_name_alter(), for example the user edit form that displays the altered username instead of the raw one as default value.

Yes, this needs needs a change record, or maybe some change record updates (if that, then that should be outlined in the issue summary) and a beta evaluation, explaining why the disruption (and is quite a disruption, I've had conflicts on my old patch on almost every update I did) is worth it and needed. Those things need to be done before it can be RTBC'd, so setting back to needs work for that.

mile23’s picture

This is a bit of a blocker for #2311219: Fix hook_user_format_name_alter() documentation and stop referring to user_format_name(), which is removing a @deprecated function.

It's possible that we could deprecate user_format_name() in some other way, and if that's the case then maybe we could work on that instead of this disruptive way.

mile23’s picture

lokapujya’s picture

Issue summary: View changes

Added beta evaluation. Instead of new CR, can we update: #2017231?

mile23’s picture

Edit: never mind.

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 122: user_getusername_-2112679-122.patch, failed testing.

berdir’s picture

Issue tags: +Needs reroll
subhojit777’s picture

Assigned: Unassigned » subhojit777
amitgoyal’s picture

Status: Needs work » Needs review
StatusFileSize
new40.88 KB

Re-roll of #122.

amitgoyal’s picture

Issue tags: -Needs reroll

Removing 'needs reroll' tag.

Status: Needs review » Needs work

The last submitted patch, 135: user_getusername_-2112679-135.patch, failed testing.

subhojit777’s picture

I started working on this issue while I was at work and was unable to continue it. Looking into it now.

subhojit777’s picture

Status: Needs work » Needs review
StatusFileSize
new40.89 KB
subhojit777’s picture

Assigned: subhojit777 » Unassigned
andypost’s picture

Status: Needs review » Needs work
+++ b/core/modules/file/src/Tests/FileFieldPathTest.php
@@ -46,7 +46,7 @@ function testUploadPath() {
-    $this->updateFileField($field_name, $type_name, array('file_directory' => '[current-user:uid]/[current-user:name]'));
+    $this->updateFileField($field_name, $type_name, array('file_directory' => '[current-user:uid]/[current-user:display-name]'));

that's looks wrong because defaults for user uploads are not affected in patch otherwise you need to provide upgrade path and change migrations, so please remove this hunk

s_leu’s picture

Status: Needs work » Needs review
StatusFileSize
new39.79 KB

Here's a re-roll that reverts the hunk in FileFieldPathTest.php as suggested in #141

Status: Needs review » Needs work

The last submitted patch, 142: getusername_should-2112679-142.patch, failed testing.

The last submitted patch, 139: getusername_should-2112679-139.patch, failed testing.

dustinleblanc’s picture

StatusFileSize
new39.24 KB

Re-rolling again, previous patch didnt apply

dustinleblanc’s picture

Status: Needs work » Needs review

Changing status

Status: Needs review » Needs work

The last submitted patch, 146: getusername_should-2112679-143.patch, failed testing.

dustinleblanc’s picture

Status: Needs work » Needs review
StatusFileSize
new39.44 KB

I missed some git conficts; had to clean up some tests that had changed in HEAD since original patch

Status: Needs review » Needs work

The last submitted patch, 149: getusername_should-2112679-149.patch, failed testing.

jeroent’s picture

Status: Needs work » Needs review
StatusFileSize
new39.44 KB
new714 bytes

Fixed the last failing test.

Status: Needs review » Needs work

The last submitted patch, 151: getusername_should-2112679-151.patch, failed testing.

lokapujya’s picture

Status: Needs work » Needs review
StatusFileSize
new39.81 KB
new1.77 KB

Seems like the test is not looking at the right result. It should look at the new user, not anonymous.

Note: In a previous patch we used the hardcoded 3rd result. Somewhere along the line, the 3 changed to 0. My patch picks the result from the userID which is hopefully less fragile.

Berdir queued 153: 2112679-153.patch for re-testing.

Status: Needs review » Needs work

The last submitted patch, 153: 2112679-153.patch, failed testing.

berdir’s picture

Status: Needs work » Needs review
StatusFileSize
new41.79 KB
new1.83 KB

Reroll, fixed some tests.

borisson_’s picture

StatusFileSize
new6.76 KB
new41.79 KB

I changed a couple of instances where we already had a changed hunk but still used the old style array syntax to use the new array syntax, I also changed one comment that was wider than 80 columns.

berdir’s picture

StatusFileSize
new41.81 KB

Another reroll.

Status: Needs review » Needs work

The last submitted patch, 158: user-displayname-2112679-158.patch, failed testing.

nlisgo’s picture

Status: Needs work » Needs review
StatusFileSize
new1.03 KB
new41.81 KB
daffie’s picture

Status: Needs review » Needs work

I did a quick review and I have two remarks:

  1. +++ b/core/modules/user/src/Tests/UserTokenReplaceTest.php
    @@ -65,12 +66,15 @@ function testUserTokenReplacement() {
    +    $tests['[current-user:name]'] = Html::escape($global_account->getDisplayName());
    

    This line does not look good to me.

  2. +++ b/core/modules/user/user.tokens.inc
    @@ -30,7 +30,11 @@ function user_token_info() {
    +  $user['username'] = array(
    

    I thought we were using name and display-name and not name and username.

@nlisgo: Thanks for the typo fix.

subhojit777’s picture

Assigned: Unassigned » subhojit777
diff --git a/core/core.api.php b/core/core.api.php
index 85fc654..5f9d22a 100755

I guess its an unrelated change.

subhojit777’s picture

Assigned: subhojit777 » Unassigned
Status: Needs work » Needs review
StatusFileSize
new42 KB
new1.72 KB

Items suggested in #161 are covered.

Status: Needs review » Needs work

The last submitted patch, 163: getusername_should-2112679-163.patch, failed testing.

subhojit777’s picture

Assigned: Unassigned » subhojit777
subhojit777’s picture

Status: Needs work » Needs review

Seems like a random failure. Tests passed in my local.

subhojit777’s picture

Issue tags: -Needs beta evaluation

Added beta evauation as per #129. Are we going to edit 2017231 for change record?

daffie’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs change record, -Needs issue summary update
catch’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/lib/Drupal/Core/Session/AccountInterface.php
    @@ -124,6 +124,17 @@ public function getPreferredAdminLangcode($fallback_to_default = TRUE);
    +   *   \Drupal\Component\Utility\String::checkPlain() is called on it before it
    

    This needs updating to Html::escape() and is not quite right with Twig autoescape.

  2. +++ b/core/lib/Drupal/Core/Session/UserSession.php
    @@ -160,6 +160,13 @@ function getPreferredAdminLangcode($fallback_to_default = TRUE) {
    +    return $this->name ?: '';
    

    When is this the case?

+++ b/core/modules/action/src/Plugin/Action/EmailAction.php
@@ -185,7 +185,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
+      '#description' => t('The message that should be sent. You may include placeholders like [node:title], [user:display-name], [user:name] and [comment:body] to represent data that will be different each time message is sent. Not all placeholders will be available in all contexts.'),

Having both name and display name here is confusing.

berdir’s picture

2. Yeah, looks like that doesn't make sense, we already initialize the property.

Confusing how? That's *exactly* where we need those two different tokens, see the updated default mails. You want to use the display name when addressing the user, but when telling him how to log in, you want to use the actual username.

I understand that this is quite an API change but it's also a serious bug when altering the username, since it will use the display name in the user edit form, just to name one example. I've been using a patch from this issue for more than I year but never found time to push it through. We can try to find a way to make the change smaller if needed, but in a way, the change is good since it forces you to think about which one you want to use. A possible alternative would be to keep user:name as it is and introduce a new token for the actual user name and stop using getUsername() where you want to actual username (yes, sounds weird).

nlisgo’s picture

Status: Needs work » Needs review
StatusFileSize
new1.13 KB
new1.04 MB

Addressing points 1 and 2 in #170.

Status: Needs review » Needs work

The last submitted patch, 172: getusername_should-2112679-172.patch, failed testing.

The last submitted patch, 172: getusername_should-2112679-172.patch, failed testing.

nlisgo’s picture

Status: Needs work » Needs review
StatusFileSize
new1.13 KB
new41.99 KB

Ignore the bad patch in #172.

nlisgo’s picture

subhojit777’s picture

Assigned: subhojit777 » Unassigned
catch’s picture

@berdir #171

I think it would be less confusing if we had getLoginName() or getAccountName() vs. getDisplayName(). We could possbly deprecate name() and add the extra method?

subhojit777’s picture

subhojit777’s picture

StatusFileSize
new41.99 KB
new34.1 KB

Patch using getAccountName().

Status: Needs review » Needs work

The last submitted patch, 180: getusername_should-2112679-180.patch, failed testing.

scor’s picture

I agree with catch's point in #178, but the patch #180 does not implement that. The patch should keep getDisplayName(), but replace name() with getLoginName() or getAccountName() to be more explicit than something generic and ambiguous like "name".

EDIT: @catch: do you not like getUsername() which we already have? or maybe I'm misunderstanding your point.

The last submitted patch, 180: getusername_should-2112679-180.patch, failed testing.

catch’s picture

Yes scor's interpretation is what I meant. Either getLoginName() or getAccountName() would be much less ambiguous compared to name().

alexpott’s picture

We've never had a test implementation of hook_user_format_name_alter(). Through work on #2559971: Make SafeMarkup::format() return a safe string object to remove reliance on a static, unpredictable safe list though which I found #2571909: CommentForm selects using the user formatted name it is clear this is a critical issue. Any implementation of the hook will break Drupal and if a user saves their user form we will have data loss.

znerol’s picture

Status: Needs work » Needs review
StatusFileSize
new551 bytes
new42.07 KB
alexpott’s picture

Status: Needs review » Needs work
+++ b/core/modules/user/tests/modules/user_name_test/user_name_test.info.yml
@@ -0,0 +1,7 @@
+name: 'User name tests'
+type: module
+description: 'Support module for user name testing.'
+package: Testing
+version: VERSION
+core: 8.x
+hidden: true
diff --git a/core/modules/user/tests/modules/user_name_test/user_name_test.module b/core/modules/user/tests/modules/user_name_test/user_name_test.module

diff --git a/core/modules/user/tests/modules/user_name_test/user_name_test.module b/core/modules/user/tests/modules/user_name_test/user_name_test.module
new file mode 100644

new file mode 100644
index 0000000..bd1cbb2

index 0000000..bd1cbb2
--- /dev/null

--- /dev/null
+++ b/core/modules/user/tests/modules/user_name_test/user_name_test.module

+++ b/core/modules/user/tests/modules/user_name_test/user_name_test.module
+++ b/core/modules/user/tests/modules/user_name_test/user_name_test.module
@@ -0,0 +1,13 @@

@@ -0,0 +1,13 @@
+<?php
+
+/**
+ * @file
+ * User name tests bootstrap file.
+ */
+
+/**
+ * Implements hook_user_format_name_alter().
+ */
+function user_name_test_user_format_name_alter(&$name, $account) {
+  $name .= \Drupal::state()->get('user_name_test_altered_name');
+}

This is now all duplicate code - I added a test recently that adds a hook implementation - user_hooks_test_user_format_name_alter() let's use it here.

berdir’s picture

Assigned: Unassigned » berdir

Working on this.

The last submitted patch, 186: getusername_should-2112679-186.patch, failed testing.

berdir’s picture

Assigned: berdir » Unassigned
Status: Needs work » Needs review
StatusFileSize
new41.07 KB
new2.32 KB

Updated to use the existing test module.

Want to discuss the method names tomorrow with @catch.

Status: Needs review » Needs work

The last submitted patch, 190: getusername_should-2112679-190.patch, failed testing.

The last submitted patch, 186: getusername_should-2112679-186.patch, failed testing.

The last submitted patch, 190: getusername_should-2112679-190.patch, failed testing.

berdir’s picture

Status: Needs work » Needs review
StatusFileSize
new41.88 KB

Going back DisplayName since that's not the method we want to rename.

We have 130 calls to getUsername(). I'm not sure if and how much of that we should really rename here. Maybe we should just add getAccoutName(), deprecate getUsername() for D9 and document that you should use getAccountName() or getDisplayName() ?

berdir’s picture

Here's the interdiff.

dawehner’s picture

+++ b/core/lib/Drupal/Core/Session/AccountInterface.php
@@ -121,6 +121,17 @@ public function getPreferredAdminLangcode($fallback_to_default = TRUE);
+   *   An unsanitized string with the user name to display. The code receiving
+   *   this result must ensure that
+   *   \Drupal\Component\Utility\Html::escape() is called on it before it
+   *   is printed to the page or used in a template.

Should we document that autoescaping makes that not needed most of the time?

alexpott’s picture

+++ b/core/modules/user/user.module
@@ -1408,7 +1408,7 @@ function user_toolbar() {
-  \Drupal::logger('user')->notice('Session closed for %name.', array('%name' => $user->getAccountName()));
+  \Drupal::logger('user')->notice('Session closed for %name.', array('%name' => $user->getDisplayName()));

I think this is wrong... the D7 version of this code was:

watchdog('user', 'Session closed for %name.', array('%name' => $user->name));
dawehner’s picture

Working on the change record now.

znerol’s picture

StatusFileSize
new803 bytes

Attached is a little module which should help with manual testing.

berdir’s picture

Did what I suggested in #194, did not address the reviews yet, want to discuss the @return documentation with @alexpott.

damiankloip’s picture

+++ b/core/modules/user/src/Entity/User.php
@@ -362,7 +362,14 @@ public function isAnonymous() {
+    return $this->get('name')->value ?: '';

Won't we get a string casted value anyway here, from typed data?

mile23’s picture

Status: Needs review » Needs work

If the only use-case for getDisplayName() is to return 'Anonymous' when there's no logged-in user, then we don't need getDisplayName().

Thus change getUsername() to AccountInterface::getName() which reflects that its the name for the account. It then returns either the name or 'Anonymous' if there's no logged-in user.

We already have AccountInterface::isAnonymous() to check whether we should rely on the account name for anything.

If we had a machine name plus human-readable feature for User, it would make sense to have two name methods. But we don't.

Contrib or site-builders can attach a field to give a display name.

Just my two cents. Don't want to trip up a critical.

Reviewing the patch in #194

+++ b/core/modules/user/src/Entity/User.php
@@ -362,7 +362,14 @@ public function isAnonymous() {
+  public function getDisplayName() {
+    $name = $this->getUsername() ?: \Drupal::config('user.settings')->get('anonymous');

Use $this->isAnonymous().

alexpott’s picture

@Mile23 we have hook_user_format_name_alter() see the text module posted by @znerol in #199. If a module implements this hook they can cause data loss on the account edit form because when you edit your account form it will use the altered name.

alexpott’s picture

Issue summary: View changes
mile23’s picture

hook_user_format_name_alter()

It's like they say: If you see a gun in the first act of the play, someone will die before the third.

Sorry. Insomnia. I hope you're all enjoying Spain.

znerol’s picture

I think we should remove hook_user_format_name_alter(). I understand that this will impact a couple of contrib modules. However, I argue that all of them could implement their use cases with different means. Even more so because they tend to be incompatible with each other due to their potentially conflicting uses of that alter hook.

Another reason for ditching that hook is that this is virtually incompatible with autocomplete. I.e., if you implement the hook, then the modified part of the name will not be searched for on entity reference autocompletes.

As far as I can see (grep) this would affect the following D7 contrib projects:

berdir’s picture

Status: Needs work » Needs review
StatusFileSize
new43.41 KB
new3.25 KB

Discussed the @return documentation with alexpott, this should be better.

Also updated the tokens as discussed. We deprecate name, just like getUsername() in favor of either account-name or display-name.

Status: Needs review » Needs work

The last submitted patch, 207: getusername_should-2112679-206.patch, failed testing.

dawehner’s picture

+++ b/core/modules/user/user.tokens.inc
@@ -30,13 +30,17 @@ function user_token_info() {
   $user['name'] = array(
-    'name' => t("Display Name"),
-    'description' => t("The display name of the user account."),
+    'name' => t("Deprecated: User Name"),
+    'description' => t("Deprecated: Use account-name or display-name instead."),
   );
   $user['account-name'] = array(
     'name' => t("Account Name"),
     'description' => t("The login name of the user account."),
   );
+  $user['display-name'] = array(
+    'name' => t("Display Name"),
+    'description' => t("The display name of the user account."),
+  );
   $user['mail'] = array(
     'name' => t("Email"),
     'description' => t("The email address of the user account."),

@@ -97,14 +101,14 @@ function user_tokens($type, $tokens, array $data, array $options, BubbleableMeta
         case 'account-name':
-          $display_name = $account->getDisplayName();
+          $display_name = $account->getAccountName();

We should add some test coverage for that additional token

berdir’s picture

Status: Needs work » Needs review
StatusFileSize
new44.43 KB
new6.53 KB

Update and extend UserTokenReplaceTest

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

#2112679-206: getUsername() should return the username getDisplayName() for the formatted user name made a compelling argument why we need the feature in the first place.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/modules/action/src/Plugin/Action/EmailAction.php
    @@ -185,7 +185,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
    +      '#description' => t('The message that should be sent. You may include placeholders like [node:title], [user:account-name], [user:name] and [comment:body] to represent data that will be different each time message is sent. Not all placeholders will be available in all contexts.'),
    
    +++ b/core/modules/action/src/Plugin/Action/MessageAction.php
    @@ -78,7 +78,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
    +      '#description' => t('The message to be displayed to the current user. You may include placeholders like [node:title], [user:account-name], [user:name] and [comment:body] to represent data that will be different each time message is sent. Not all placeholders will be available in all contexts.'),
    
    +++ b/core/modules/user/config/install/user.mail.yml
    @@ -1,28 +1,28 @@
    +  body: "[user:account-name],\n\nA site administrator at [site:name] has created an account for you. You may now log in by clicking this link or copying and pasting it to your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:name]\npassword: Your password\n\n--  [site:name] team"
    ...
    +  body: "[user:account-name],\n\nThank you for registering at [site:name]. You may now log in by clicking this link or copying and pasting it to your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:name]\npassword: Your password\n\n--  [site:name] team"
    ...
    +  body: "[user:account-name],\n\nYour account at [site:name] has been activated.\n\nYou may now log in by clicking this link or copying and pasting it into your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:name]\npassword: Your password\n\n--  [site:name] team"
    ...
    +  body: "[user:name],\n\nYour account on [site:account-name] has been blocked.\n\n--  [site:name] team"
    
    +++ b/core/modules/user/src/AccountSettingsForm.php
    @@ -201,7 +201,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    +    $email_token_help = $this->t('Available variables are: [site:name], [site:url], [user:display-name], [user:name], [user:mail], [site:login-url], [site:url-brief], [user:edit-url], [user:one-time-login-url], [user:cancel-url].');
    

    Let's remove all usages of [user:name] in core.

  2. +++ b/core/modules/user/src/Tests/UserTokenReplaceTest.php
    @@ -57,7 +62,9 @@ function testUserTokenReplacement() {
    +    $tests['[user:account-name]'] = Html::escape($account->getAccountName());
    

    No need to call Html::escape() on the new account-name - it is not possible to put an escaped character in a user name anyway - plus we have autoescape.

  3. +++ b/core/modules/user/user.module
    @@ -738,8 +739,8 @@ function _user_cancel($edit, $account, $method) {
    +      drupal_set_message(t('%name has been disabled.', array('%name' => $account->getDisplayName())));
    +      $logger->notice('Blocked user: %name %email.', array('%name' => $account->getDisplayName(), '%email' => '<' . $account->getEmail() . '>'));
    
    @@ -749,8 +750,8 @@ function _user_cancel($edit, $account, $method) {
    +      drupal_set_message(t('%name has been deleted.', array('%name' => $account->getDisplayName())));
    +      $logger->notice('Deleted user: %name %email.', array('%name' => $account->getDisplayName(), '%email' => '<' . $account->getEmail() . '>'));
    

    In Drupal 7 this was the account name and not the display name which I think makes sense.

berdir’s picture

Status: Needs work » Needs review
StatusFileSize
new44.45 KB
new8.7 KB

1. Makes sense, also the tokens were actually wrong.
2. As discussed, I added/kept it to be consistent with the token implementation which uses it too.
3. As discussed, I used the display name for the dsm() and account name for the log.

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

Good catch!

berdir’s picture

The last submitted patch, 207: getusername_should-2112679-206.patch, failed testing.

mile23’s picture

Just a nit. Could be changed on commit.

+++ b/core/lib/Drupal/Core/Session/AccountInterface.php
@@ -115,10 +135,11 @@ public function getPreferredAdminLangcode($fallback_to_default = TRUE);
+   * @return string|\Drupal\Component\Utility\SafeStringInterface
+   *   Either a string that will be autoescaped or a SafeString object that is
+   *   already escaped. Both are safe to be printed.

'Either is safe to be printed.'

catch’s picture

+++ b/core/lib/Drupal/Core/Session/AccountInterface.php
@@ -108,6 +108,26 @@ public function getPreferredAdminLangcode($fallback_to_default = TRUE);
+   *   \Drupal\user\RoleInterface::getDisplayName8) instead.

getDisplayName8 - fixable on commit.

So in general I agree with znerol's point in #206 - I'd much rather see things like this handled by formatters a 'name' user entity view mode or similar.'

However we do not use formatters on the user entity when we render user names - like 'submitted by, menu links etc., so that will require a lot of things put into place so that we're not retrieving the username from the account object directly in those places. Additionally either @alexpott or @Berdir pointed out that it's not possible to have two formatters for the same field on the same view mode - which we'll need if we want to show both user pictures and user names in different places on node/comment.

Should definitely aim for that, but this feels like a good fix for the comment autocomplete data loss that doesn't prevent us doing a broader fix when the other bits are in place. Would be good to have a follow-up for submitted by though, I'd open it but not sure if there's an existing issue out there.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 213: getusername_should-2112679-213.patch, failed testing.

stefan.r’s picture

Status: Needs work » Reviewed & tested by the community
StatusFileSize
new44.68 KB
new2.71 KB

Only tiny nits in docs, so leaving RTBC per #214

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/lib/Drupal/Core/Session/AccountInterface.php
@@ -108,17 +108,39 @@ public function getPreferredAdminLangcode($fallback_to_default = TRUE);
+  /**
+   * Returns the unaltered name of this account.
+   *
+   * @return
+   *   An unsanitized plain-text string with the username to display.
+   */
+  public function getAccountName();

Saying that is returns a username to display is confusing... primarily you use this when you are not displaying the username. In fact we should document that this should only be used to display the name to admins and the user themselves and only in the context of the name used to login.

stefan.r’s picture

Assigned: Unassigned » stefan.r

I'll update the docs

stefan.r’s picture

Assigned: stefan.r » Unassigned
Status: Needs work » Needs review
StatusFileSize
new1.75 KB
new45.36 KB

@alexpott it's not just confusing, it seems wrong... but that's what the docs used to say. I guess we should update getUsername() as well then (even if it's deprecated). Updating the patch accordingly.

dawehner’s picture

Morning nitpick:

+++ b/core/lib/Drupal/Core/Session/AccountInterface.php
@@ -106,22 +106,30 @@
    * @return
...
    * @return

Let's use @return string

stefan.r’s picture

StatusFileSize
new948 bytes
new45.37 KB
andypost’s picture

  1. +++ b/core/lib/Drupal/Core/Session/AccountInterface.php
    @@ -106,19 +106,49 @@ public function getPreferredLangcode($fallback_to_default = TRUE);
    +   * @deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0.
    ...
    +  public function getUsername();
    ...
    +  public function getAccountName();
    
    +++ b/core/modules/user/src/Entity/User.php
    @@ -362,7 +362,21 @@ public function isAnonymous() {
    +  public function getDisplayName() {
    +    $name = $this->getUsername() ?: \Drupal::config('user.settings')->get('anonymous');
    

    Why that calls deprecated method?

  2. +++ b/core/modules/user/src/Entity/User.php
    @@ -362,7 +362,21 @@ public function isAnonymous() {
         \Drupal::moduleHandler()->alter('user_format_name', $name, $this);
         return $name;
    

    I think better to add here a protected property to call alter only once, mean cache generated value

catch’s picture

Status: Needs review » Needs work
  1. +++ b/core/lib/Drupal/Core/Session/AccountInterface.php
    @@ -106,19 +106,49 @@ public function getPreferredLangcode($fallback_to_default = TRUE);
    +   *   \Drupal\user\RoleInterface::getDisplayName() instead.
    

    Should be AccountInterface::getDisplayName() no?

  2. +++ b/core/modules/action/src/Plugin/Action/EmailAction.php
    @@ -185,7 +185,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
    +      '#description' => t('The message that should be sent. You may include placeholders like [node:title], [user:account-name], [user:name] and [comment:body] to represent data that will be different each time message is sent. Not all placeholders will be available in all contexts.'),
    

    Shouldn't this be user:account-name and user:display-name now?

  3. +++ b/core/modules/action/src/Plugin/Action/MessageAction.php
    @@ -78,7 +78,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
    +      '#description' => t('The message to be displayed to the current user. You may include placeholders like [node:title], [user:account-name], [user:name] and [comment:body] to represent data that will be different each time message is sent. Not all placeholders will be available in all contexts.'),
    

    Same question.

  4. +++ b/core/modules/user/config/install/user.mail.yml
    @@ -1,28 +1,28 @@
    +  subject: 'Replacement login information for [user:display-name] at [site:name]'
    

    Hmm I had to read through the whole string to see if we really wanted display-name here, but we do use account-name later so they seem like good changes.

  5. +++ b/core/modules/user/src/AccountSettingsForm.php
    @@ -201,7 +201,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    +    $email_token_help = $this->t('Available variables are: [site:name], [site:url], [user:display-name], [user:name], [user:mail], [site:login-url], [site:url-brief], [user:edit-url], [user:one-time-login-url], [user:cancel-url].');
    

    user:account-name again - still using user:name

  6. +++ b/core/modules/user/user.module
    @@ -738,8 +739,8 @@ function _user_cancel($edit, $account, $method) {
    +      drupal_set_message(t('%name has been disabled.', array('%name' => $account->getDisplayName())));
    

    Shouldn't this be getAccountName()?

dawehner’s picture

Working on addressing the feedback.

dawehner’s picture

Status: Needs work » Needs review
StatusFileSize
new45.4 KB
new5.61 KB

Hmm I had to read through the whole string to see if we really wanted display-name here, but we do use account-name later so they seem like good changes.

Yeah, I think it is the right change, just imagine that users don't want to reveal their account login username.

Shouldn't this be getAccountName()?

Yeah I kinda agree that this is the thing, that should happen, but yeah people don't care that much, berdir isn't sure at least.

nlisgo’s picture

+++ b/core/modules/user/src/Entity/User.php
@@ -411,7 +425,10 @@ public static function getAnonymousUser() {
+      static::$anonymousUser = new $class([
+        'uid' => [LanguageInterface::LANGCODE_DEFAULT => 0],
+        'name' => [LanguageInterface::LANGCODE_DEFAULT => '']
+      ], $entity_type->id());
     }

Needs a comma at the end of the last value in the array.

nlisgo’s picture

StatusFileSize
new589 bytes
new589 bytes

Added comma referred to in #231.

Status: Needs review » Needs work

The last submitted patch, 232: getusername_should-2112679-232.patch, failed testing.

The last submitted patch, 232: getusername_should-2112679-232.patch, failed testing.

nlisgo’s picture

I totally screwed up that patch. Let me try again :(

dawehner’s picture

Status: Needs work » Needs review
StatusFileSize
new1.76 KB
new45.4 KB

We discussed that we actually want the display name. Imagine /admin/people and a user administrator. A user is removed and accidentally you see a different user is removed,
as the user name is displayed.

@nlisgo I just include your changes into the patch as well.

nlisgo’s picture

@dawehner thanks. I tried to load it in but the wifi failed me. I can't blame the wifi for my patch though.

berdir’s picture

Status: Needs review » Reviewed & tested by the community

My own patch was already RTBC, so I think I can RTBC the changes since then, looks all good to me.

stefan.r’s picture

+1 looks great!

webchick’s picture

Status: Reviewed & tested by the community » Fixed

Walked through this patch with @dawehner at my side at DrupalCon. :)

  1. +++ b/core/lib/Drupal/Core/Session/AccountInterface.php
    @@ -106,19 +106,49 @@ public function getPreferredLangcode($fallback_to_default = TRUE);
    +  /**
    +   * Returns the unaltered login name of this account.
    +   *
    +   * @return string
    +   *   An unsanitized plain-text string with the name of this account that is
    +   *   used to log in. Only display this name to admins and to the user who owns
    +   *   this account, and only in the context of the name used to login. For
    +   *   any other display purposes, use
    +   *   \Drupal\Core\Session\AccountInterface::getDisplayName() instead.
    +   */
    +  public function getAccountName();
    

    The one thing I raised my eyebrows about is that this doesn't sound like a dangerous method, but it is a dangerous method. Like we could call it "getRawAccountName()" or something.

    OTOH, after looking more into the old implementation of getUsername(), it also didn't escape anything, so I think this is fine.

  2. +++ b/core/modules/user/user.module
    @@ -738,8 +739,8 @@ function _user_cancel($edit, $account, $method) {
    -      drupal_set_message(t('%name has been disabled.', array('%name' => $account->getUsername())));
    -      $logger->notice('Blocked user: %name %email.', array('%name' => $account->getUsername(), '%email' => '<' . $account->getEmail() . '>'));
    +      drupal_set_message(t('%name has been disabled.', array('%name' => $account->getDisplayName())));
    +      $logger->notice('Blocked user: %name %email.', array('%name' => $account->getAccountName(), '%email' => '<' . $account->getEmail() . '>'));
    

    I had a question about hunks like this... before we were using ->getUsername() in both, but here we swap logger's version for $account->getAccountName().

    When asking dawehner about this, he said that this is because in admin UIs, when deleting a user, you want to see the name of the person you just deleted (which would have been their display name), but in logs you'd want to use the username in case you need to query for the person.

Other than that, this is pretty straight-forward. Introduces new methods, deprecates the old one, switches it everywhere.

It looks like @alexpott's most recent feedback was taken care of, soooooo....

Committed and pushed to 8.0.x. One critical down! :D Thanks!

  • webchick committed 1d1fe19 on 8.0.x
    Issue #2112679 by Berdir, damiankloip, nlisgo, krlucas, lokapujya, m1r1k...
mile23’s picture

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.