Skip to content

Allow username capitalization change#12438

Open
godkama wants to merge 2 commits intoppy:masterfrom
godkama:capitalization-change
Open

Allow username capitalization change#12438
godkama wants to merge 2 commits intoppy:masterfrom
godkama:capitalization-change

Conversation

@godkama
Copy link

@godkama godkama commented Oct 2, 2025

This is not a big need, but the only way to change username capitalization so far has been only to message support (i think?) or to change username twice (temporary username and then other capitalization). This needs to be checked further since i'm not the best at Laravel but should work!

reopened

@godkama
Copy link
Author

godkama commented Oct 2, 2025

3rd commit just fixes branch being behind- ignore.

@godkama godkama force-pushed the capitalization-change branch from a3a5979 to 086c3ac Compare October 2, 2025 20:15
@godkama godkama force-pushed the capitalization-change branch from 086c3ac to 7830ba2 Compare October 2, 2025 20:26
@godkama
Copy link
Author

godkama commented Oct 2, 2025

/tests/Libraries/ChangeUsernameTests

public function testUsernameWithDifferentCasingIsSame()
    {
        $user = $this->createUser();

        $errors = $user->validateChangeUsername('iAmUser')->all();
        $expected = [osu_trans('model_validation.user.change_username.username_is_same')];

        $this->assertArrayHasKey('username', $errors);
        $this->assertArraySubset($expected, $errors['username'], true);
    }

needs to be checked out, tests fails on there.

@NiceAesth
Copy link
Contributor

Would this not increase the cost of the next namechange? I believe that this is not the case at the moment for namechanges done over email.

@godkama
Copy link
Author

godkama commented Oct 2, 2025

Would this not increase the cost of the next namechange? I believe that this is not the case at the moment for namechanges done over email.

it might, depending whether of not the change is logged into db:

public function usernameChangeHistory()
    {
        return $this->hasMany(UsernameChangeHistory::class);
    }
public function usernameChangeCost()
    {
        $minTier = $this->usernameChangeHistory()->paid()->exists() ? 1 : 0;

        $tier = max(
            $this->usernameChangeHistory()
                ->paid()
                ->where('timestamp', '>', Carbon::now()->subYearsNoOverflow(3))
                ->count(),
            $minTier,
        );

        return match ($tier) {
            0 => 0,
            1 => 8,
            2 => 16,
            3 => 32,
            4 => 64,
            default => 100,
        };
    }

@godkama
Copy link
Author

godkama commented Oct 2, 2025

private function updateUsername(string $newUsername, string $type): UsernameChangeHistory
    {
        $oldUsername = $type === 'revert' ? null : $this->getOriginal('username');
        $this->username_previous = $oldUsername;
        $this->username = $newUsername;

        return DB::transaction(function () use ($newUsername, $oldUsername, $type) {
            Forum\Forum::where('forum_last_poster_id', $this->user_id)->update(['forum_last_poster_name' => $newUsername]);
            // DB::table('phpbb_moderator_cache')->where('user_id', $this->user_id)->update(['username' => $newUsername]);
            Forum\Post::where('poster_id', $this->user_id)->update(['post_username' => $newUsername]);
            Forum\Topic::where('topic_poster', $this->user_id)
                ->update(['topic_first_poster_name' => $newUsername]);
            Forum\Topic::where('topic_last_poster_id', $this->user_id)
                ->update(['topic_last_poster_name' => $newUsername]);

            $history = $this->usernameChangeHistory()->create([
                'username' => $newUsername,
                'username_last' => $oldUsername,
                'timestamp' => Carbon::now(),
                'type' => $type,
            ]);

            if (!$history->exists) {
                throw new ModelNotSavedException('failed saving model');
            }

            $skipValidations = in_array($type, ['inactive', 'revert'], true);
            $this->saveOrExplode(['skipValidations' => $skipValidations]);

            return $history;
        });
    }

i believe this is where we should change it to not add to db. cleanUsername function removes any cases though so a workaround?


if (User::cleanUsername($this->username) === $this->user->username_clean) {
// Block if new username is exactly the same as current (case-sensitive match)
if (strcasecmp($this->username, $this->user->username) === 0 && $this->username === $this->user->username) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder why not just compare them. If It blocks when they are exactly the same, there doesn't seem to be any need to do a case-insensitive comparison?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the cleanUsername strips all possibilities of case comparison

@notbakaneko notbakaneko self-requested a review November 7, 2025 08:32
@peppy peppy moved this from Ready for work to Pending Review in osu! team task tracker Dec 11, 2025
@github-project-automation github-project-automation bot moved this to Ready for work in osu! team task tracker Dec 11, 2025
Copy link
Contributor

@LiquidPL LiquidPL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could also use a message on the frontend explaining that the username change is free because of only being a capitalization change

besides, i suppose this could also use some form of cooldown so that people don't change this constantly, and also as other people pointed out it needs to handle not incrementing the username change count or does it? i'm not sure how exactly it is handled on the support side


if (User::cleanUsername($this->username) === $this->user->username_clean) {
// Block if new username is exactly the same as current (case-sensitive match)
if (strcasecmp($this->username, $this->user->username) === 0 && $this->username === $this->user->username) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't a regular string comparison (aka the second check) be sufficient in this case?

protected $type;

/** @var User */
protected $user;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why was this removed? tbh with modern php it could just be:

Suggested change
protected $user;
protected User $user;

Comment on lines +78 to +81
if ($this->isCapitalizationOnlyChange()) {
return false;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Putting this here will allow restricted users to change the capitalization on their usernames, which shouldn't be the case. isCapitalizationOnlyChange should only bypass the supporter check if anything.

$available = $errors->isEmpty();
$message = $available ? "Username '".e($username)."' is available!" : $errors->toSentence();
$cost = $available ? Auth::user()->usernameChangeCost() : 0;
$isCapitalizationOnly = strcasecmp($username, Auth::user()->username) === 0 && $username !== Auth::user()->username;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this check should probably be put into helpers.php and used both here and in ChangeUsername

Comment on lines +136 to +139
$cost = 0;
if ($available) {
$cost = $isCapitalizationOnly ? 0 : Auth::user()->usernameChangeCost();
}
Copy link
Contributor

@LiquidPL LiquidPL Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could do it this way instead and it'd be easier to read, especially since we don't have to care about the availability checks if the username has the same capitalization

Suggested change
$cost = 0;
if ($available) {
$cost = $isCapitalizationOnly ? 0 : Auth::user()->usernameChangeCost();
}
if ($isCapitalizationOnly) {
$cost = 0;
else {
$cost = $available ? Auth::user()->usernameChangeCost() : 0;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Pending Review

Development

Successfully merging this pull request may close these issues.

5 participants