@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class PhabricatorUserUsernameTransaction
4 extends PhabricatorUserTransactionType {
5
6 const TRANSACTIONTYPE = 'user.rename';
7
8 public function generateOldValue($object) {
9 return $object->getUsername();
10 }
11
12 public function generateNewValue($object, $value) {
13 return $value;
14 }
15
16 public function applyInternalEffects($object, $value) {
17 $object->setUsername($value);
18 }
19
20 public function applyExternalEffects($object, $value) {
21 $actor = $this->getActor();
22 $user = $object;
23
24 $old_username = $this->getOldValue();
25 $new_username = $this->getNewValue();
26
27 // The SSH key cache currently includes usernames, so dirty it. See T12554
28 // for discussion.
29 PhabricatorAuthSSHKeyQuery::deleteSSHKeyCache();
30
31 id(new PhabricatorPeopleUsernameMailEngine())
32 ->setSender($actor)
33 ->setRecipient($object)
34 ->setOldUsername($old_username)
35 ->setNewUsername($new_username)
36 ->sendMail();
37 }
38
39 public function getTitle() {
40 if (!$this->getViewer()->getIsAdmin()) {
41 return pht(
42 '%s renamed this user.',
43 $this->renderAuthor());
44 }
45 return pht(
46 '%s renamed this user from %s to %s.',
47 $this->renderAuthor(),
48 $this->renderOldValue(),
49 $this->renderNewValue());
50 }
51
52 public function getTitleForFeed() {
53 if (!$this->getViewer()->getIsAdmin()) {
54 return pht(
55 '%s renamed %s.',
56 $this->renderAuthor(),
57 $this->renderObject());
58 }
59 return pht(
60 '%s renamed %s from %s to %s.',
61 $this->renderAuthor(),
62 $this->renderObject(),
63 $this->renderOldValue(),
64 $this->renderNewValue());
65 }
66
67 public function validateTransactions($object, array $xactions) {
68 $actor = $this->getActor();
69 $errors = array();
70
71 foreach ($xactions as $xaction) {
72 $new = $xaction->getNewValue();
73 $old = $xaction->getOldValue();
74
75 if ($old === $new) {
76 continue;
77 }
78
79 if (!$actor->getIsAdmin()) {
80 $errors[] = $this->newInvalidError(
81 pht('You must be an administrator to rename users.'));
82 }
83
84 if (!strlen($new)) {
85 $errors[] = $this->newInvalidError(
86 pht('New username is required.'),
87 $xaction);
88 } else if (!PhabricatorUser::validateUsername($new)) {
89 $errors[] = $this->newInvalidError(
90 PhabricatorUser::describeValidUsername($new),
91 $xaction);
92 } else if ($this->generateOldValue($object) === $new) {
93 $errors[] = $this->newInvalidError(
94 pht('New username cannot be the old username.'),
95 $xaction);
96 }
97
98 $user = id(new PhabricatorPeopleQuery())
99 ->setViewer(PhabricatorUser::getOmnipotentUser())
100 ->withUsernames(array($new))
101 ->executeOne();
102 if ($user) {
103 // See T13446. We may be changing the letter case of a username, which
104 // is a perfectly fine edit.
105 $is_self = ($user->getPHID() === $object->getPHID());
106 if (!$is_self) {
107 $errors[] = $this->newInvalidError(
108 pht(
109 'Another user already has the username "%s".',
110 $new),
111 $xaction);
112 }
113 }
114
115 }
116
117 return $errors;
118 }
119
120 public function getRequiredCapabilities(
121 $object,
122 PhabricatorApplicationTransaction $xaction) {
123
124 // Unlike normal user edits, renames require admin permissions, which
125 // is enforced by validateTransactions().
126
127 return null;
128 }
129
130 public function shouldTryMFA(
131 $object,
132 PhabricatorApplicationTransaction $xaction) {
133 return true;
134 }
135
136}