Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 161 |
|
0.00% |
0 / 17 |
CRAP | |
0.00% |
0 / 1 |
SpecialGlobalRenameRequest | |
0.00% |
0 / 161 |
|
0.00% |
0 / 17 |
1560 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
doesWrites | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
userCanExecute | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
displayRestrictionError | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 35 |
|
0.00% |
0 / 1 |
72 | |||
isGlobalUser | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
alterForm | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getDisplayFormat | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
preHtml | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
getFormFields | |
0.00% |
0 / 67 |
|
0.00% |
0 / 1 |
20 | |||
suggestedUsername | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
20 | |||
validateNewname | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
validateEmail | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
onSubmit | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
20 | |||
onSuccess | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
requiresUnblock | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getGroupName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * @section LICENSE |
4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License as published by |
6 | * the Free Software Foundation; either version 2 of the License, or |
7 | * (at your option) any later version. |
8 | * |
9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | * GNU General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU General Public License along |
15 | * with this program; if not, write to the Free Software Foundation, Inc., |
16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
17 | * http://www.gnu.org/copyleft/gpl.html |
18 | * |
19 | * @file |
20 | */ |
21 | |
22 | namespace MediaWiki\Extension\CentralAuth\Special; |
23 | |
24 | use MediaWiki\Extension\CentralAuth\GlobalRename\GlobalRenameDenylist; |
25 | use MediaWiki\Extension\CentralAuth\GlobalRename\GlobalRenameRequest; |
26 | use MediaWiki\Extension\CentralAuth\GlobalRename\GlobalRenameRequestStore; |
27 | use MediaWiki\Extension\CentralAuth\User\CentralAuthUser; |
28 | use MediaWiki\HTMLForm\HTMLForm; |
29 | use MediaWiki\Message\Message; |
30 | use MediaWiki\Parser\Sanitizer; |
31 | use MediaWiki\SpecialPage\FormSpecialPage; |
32 | use MediaWiki\Status\Status; |
33 | use MediaWiki\User\User; |
34 | use MediaWiki\User\UserNameUtils; |
35 | use MediaWiki\WikiMap\WikiMap; |
36 | use PermissionsError; |
37 | use Wikimedia\Rdbms\IDBAccessObject; |
38 | |
39 | /** |
40 | * Request an account rename. |
41 | * |
42 | * @author Bryan Davis <bd808@wikimedia.org> |
43 | * @copyright © 2014 Bryan Davis and Wikimedia Foundation. |
44 | */ |
45 | class SpecialGlobalRenameRequest extends FormSpecialPage { |
46 | |
47 | private GlobalRenameDenylist $globalRenameDenylist; |
48 | private UserNameUtils $userNameUtils; |
49 | private GlobalRenameRequestStore $globalRenameRequestStore; |
50 | |
51 | public function __construct( |
52 | GlobalRenameDenylist $globalRenameDenylist, |
53 | UserNameUtils $userNameUtils, |
54 | GlobalRenameRequestStore $globalRenameRequestStore |
55 | ) { |
56 | parent::__construct( 'GlobalRenameRequest' ); |
57 | $this->globalRenameDenylist = $globalRenameDenylist; |
58 | $this->userNameUtils = $userNameUtils; |
59 | $this->globalRenameRequestStore = $globalRenameRequestStore; |
60 | } |
61 | |
62 | public function doesWrites() { |
63 | return true; |
64 | } |
65 | |
66 | /** @inheritDoc */ |
67 | public function userCanExecute( User $user ) { |
68 | return $this->globalRenameDenylist->checkUser( $user->getName() ); |
69 | } |
70 | |
71 | public function displayRestrictionError() { |
72 | throw new PermissionsError( null, [ 'centralauth-badaccess-blacklisted' ] ); |
73 | } |
74 | |
75 | /** |
76 | * @param string|null $par Subpage string if one was specified |
77 | */ |
78 | public function execute( $par ) { |
79 | $this->requireNamedUser(); |
80 | $this->addHelpLink( 'Help:Extension:CentralAuth/Global_rename' ); |
81 | |
82 | switch ( $par ) { |
83 | case 'status': |
84 | // Render status page |
85 | $user = $this->getUser(); |
86 | $username = $user->getName(); |
87 | $wiki = $this->isGlobalUser() ? null : WikiMap::getCurrentWikiId(); |
88 | $pending = $this->globalRenameRequestStore->newForUser( |
89 | $username, $wiki |
90 | ); |
91 | if ( !$pending->exists() ) { |
92 | $this->getOutput()->redirect( $this->getPageTitle()->getFullURL(), 303 ); |
93 | return; |
94 | } |
95 | $out = $this->getOutput(); |
96 | $out->setPageTitleMsg( |
97 | $this->msg( 'globalrenamerequest-status-title' ) |
98 | ); |
99 | $out->addWikiMsg( 'globalrenamerequest-status-text', |
100 | $username, $pending->getNewName() |
101 | ); |
102 | break; |
103 | |
104 | case 'available': |
105 | // TODO: ajax name availability check (T72623) |
106 | break; |
107 | |
108 | default: |
109 | // Request form |
110 | $out = $this->getOutput(); |
111 | $user = $this->getUser(); |
112 | $wiki = $this->isGlobalUser() ? null : WikiMap::getCurrentWikiId(); |
113 | |
114 | $pending = $this->globalRenameRequestStore->newForUser( |
115 | $user->getName(), $wiki |
116 | ); |
117 | if ( $pending->exists() ) { |
118 | $out->redirect( |
119 | $this->getPageTitle( 'status' )->getFullURL(), '303' |
120 | ); |
121 | return; |
122 | } |
123 | parent::execute( $par ); |
124 | break; |
125 | } |
126 | } |
127 | |
128 | /** |
129 | * Is the current user a global user? |
130 | * @return bool |
131 | */ |
132 | protected function isGlobalUser() { |
133 | $user = $this->getUser(); |
134 | $causer = CentralAuthUser::getInstance( $user ); |
135 | return $causer->exists() && $causer->isAttached(); |
136 | } |
137 | |
138 | /** |
139 | * @param HTMLForm $form |
140 | */ |
141 | protected function alterForm( HTMLForm $form ) { |
142 | $form->setSubmitTextMsg( 'globalrenamerequest-submit-text' ); |
143 | } |
144 | |
145 | /** |
146 | * @return string |
147 | */ |
148 | protected function getDisplayFormat() { |
149 | return 'ooui'; |
150 | } |
151 | |
152 | /** |
153 | * @return string |
154 | */ |
155 | protected function preHtml() { |
156 | $msg = $this->msg( 'globalrenamerequest-pretext' )->parse(); |
157 | if ( !$this->isGlobalUser() ) { |
158 | $msg = $this->msg( 'globalrenamerequest-forced' )->parse() . $msg; |
159 | } |
160 | return $msg; |
161 | } |
162 | |
163 | /** |
164 | * @return array[] |
165 | */ |
166 | public function getFormFields() { |
167 | $suggestedUsername = $this->suggestedUsername(); |
168 | if ( $suggestedUsername !== false ) { |
169 | $suggestedHelp = [ |
170 | 'globalrenamerequest-newname-help', |
171 | $suggestedUsername, |
172 | ]; |
173 | } else { |
174 | // Help message if we couldn't generate a suggested username |
175 | $suggestedHelp = 'globalrenamerequest-newname-help-basic'; |
176 | } |
177 | $fields = [ |
178 | 'username' => [ |
179 | 'cssclass' => 'mw-globalrenamerequest-field', |
180 | 'csshelpclass' => 'mw-globalrenamerequest-help', |
181 | 'default' => $this->getUser()->getName(), |
182 | 'label-message' => 'globalrenamerequest-username-label', |
183 | 'type' => 'info', |
184 | ], |
185 | 'newname' => [ |
186 | 'cssclass' => 'mw-globalrenamerequest-field', |
187 | 'csshelpclass' => 'mw-globalrenamerequest-help', |
188 | 'default' => $this->getRequest()->getVal( 'newname', $this->par ), |
189 | 'id' => 'mw-renamerequest-newname', |
190 | 'label-message' => 'globalrenamerequest-newname-label', |
191 | 'name' => 'newname', |
192 | 'required' => true, |
193 | 'type' => 'text', |
194 | 'help-message' => $suggestedHelp, |
195 | 'validation-callback' => [ $this, 'validateNewname' ], |
196 | ], |
197 | ]; |
198 | |
199 | $currentEmail = $this->getUser()->getEmail(); |
200 | if ( $currentEmail === '' ) { |
201 | $fields['email'] = [ |
202 | 'cssclass' => 'mw-globalrenamerequest-field', |
203 | 'csshelpclass' => 'mw-globalrenamerequest-help', |
204 | 'default' => $this->getRequest()->getVal( 'email', $this->par ), |
205 | 'id' => 'mw-renamerequest-email', |
206 | 'label-message' => 'globalrenamerequest-email-label', |
207 | 'name' => 'email', |
208 | 'placeholder' => 'username@example.com', |
209 | 'required' => true, |
210 | 'type' => 'email', |
211 | 'help-message' => 'globalrenamerequest-email-why-explain', |
212 | ]; |
213 | $fields['email2'] = [ |
214 | 'cssclass' => 'mw-globalrenamerequest-field', |
215 | 'csshelpclass' => 'mw-globalrenamerequest-help', |
216 | 'default' => $this->getRequest()->getVal( 'email2', $this->par ), |
217 | 'id' => 'mw-renamerequest-email2', |
218 | 'label-message' => 'globalrenamerequest-email2-label', |
219 | 'name' => 'email2', |
220 | 'placeholder' => 'username@example.com', |
221 | 'required' => true, |
222 | 'type' => 'email', |
223 | 'help-message' => 'globalrenamerequest-email2-help', |
224 | 'validation-callback' => [ $this, 'validateEmail' ], |
225 | ]; |
226 | } |
227 | |
228 | if ( $this->isGlobalUser() ) { |
229 | $fields['reason'] = [ |
230 | 'cssclass' => 'mw-globalrenamerequest-field', |
231 | 'default' => $this->getRequest()->getVal( 'reason', $this->par ), |
232 | 'id' => 'mw-renamerequest-reason', |
233 | 'label-message' => 'globalrenamerequest-reason-label', |
234 | 'name' => 'reason', |
235 | 'required' => true, |
236 | 'rows' => 5, |
237 | 'type' => 'textarea', |
238 | ]; |
239 | } |
240 | return $fields; |
241 | } |
242 | |
243 | /** |
244 | * Generate a username that appears to be globally available that an |
245 | * unimaginative user could use if they like. |
246 | * |
247 | * @return string|bool false if can't generate a username |
248 | */ |
249 | protected function suggestedUsername() { |
250 | // Only allow 5 tries (T260865) |
251 | $counter = 0; |
252 | // Whether we found a username that is available to use |
253 | $found = false; |
254 | $rand = ''; |
255 | while ( !$found && $counter < 5 ) { |
256 | $rand = $this->getUser()->getName() . rand( 123, 999 ); |
257 | $found = GlobalRenameRequest::isNameAvailable( $rand, IDBAccessObject::READ_NORMAL )->isOK(); |
258 | $counter++; |
259 | } |
260 | if ( $found ) { |
261 | return $rand; |
262 | } else { |
263 | return false; |
264 | } |
265 | } |
266 | |
267 | /** |
268 | * @param string|array $value The value the field was submitted with |
269 | * @param array $alldata The data collected from the form |
270 | * @param HTMLForm $form |
271 | * @return bool|Message True on success, or String error to display, or |
272 | * false to fail validation without displaying an error. |
273 | */ |
274 | public function validateNewname( $value, $alldata, HTMLForm $form ) { |
275 | if ( $value === null ) { |
276 | // Not submitted yet |
277 | return true; |
278 | } |
279 | $check = GlobalRenameRequest::isNameAvailable( $value ); |
280 | return $check->isGood() ? true : $check->getMessage(); |
281 | } |
282 | |
283 | /** |
284 | * @param string|array $value The value the field was submitted with |
285 | * @param array $alldata The data collected from the form |
286 | * @param HTMLForm $form |
287 | * @return bool|Message True on success, or String error to display, or |
288 | * false to fail validation without displaying an error. |
289 | */ |
290 | public function validateEmail( $value, $alldata, HTMLForm $form ) { |
291 | if ( $alldata['email'] !== $alldata['email2'] ) { |
292 | return $this->msg( 'globalrenamerequest-email-mismatch' ); |
293 | } elseif ( !Sanitizer::validateEmail( $alldata['email'] ) ) { |
294 | return $this->msg( 'globalrenamerequest-email-invalid' ); |
295 | } |
296 | return true; |
297 | } |
298 | |
299 | /** |
300 | * @param array $data |
301 | * @return Status |
302 | */ |
303 | public function onSubmit( array $data ) { |
304 | $wiki = $this->isGlobalUser() ? null : WikiMap::getCurrentWikiId(); |
305 | $reason = $data['reason'] ?? null; |
306 | $safeName = $this->userNameUtils->getCanonical( $data['newname'], UserNameUtils::RIGOR_CREATABLE ); |
307 | |
308 | $request = $this->globalRenameRequestStore->newBlankRequest(); |
309 | $request->setName( $this->getUser()->getName() ); |
310 | $request->setWiki( $wiki ); |
311 | $request->setNewName( $safeName ); |
312 | $request->setReason( $reason ); |
313 | |
314 | if ( $this->globalRenameRequestStore->save( $request ) ) { |
315 | $status = Status::newGood(); |
316 | |
317 | if ( isset( $data['email'] ) ) { |
318 | $user = $this->getUser(); |
319 | $user->setEmail( $data['email'] ); |
320 | $user->saveSettings(); |
321 | $status = $user->sendConfirmationMail( 'set' ); |
322 | } |
323 | } else { |
324 | $status = Status::newFatal( |
325 | $this->msg( 'globalrenamerequest-save-error' ) |
326 | ); |
327 | } |
328 | return $status; |
329 | } |
330 | |
331 | public function onSuccess() { |
332 | $this->getOutput()->redirect( |
333 | $this->getPageTitle( 'status' )->getFullURL(), '303' |
334 | ); |
335 | } |
336 | |
337 | /** |
338 | * Even blocked users should be able to ask for a rename. |
339 | * @return bool |
340 | */ |
341 | public function requiresUnblock() { |
342 | return false; |
343 | } |
344 | |
345 | /** @inheritDoc */ |
346 | protected function getGroupName() { |
347 | return 'login'; |
348 | } |
349 | } |