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 HTMLForm; |
25 | use IDBAccessObject; |
26 | use MediaWiki\Extension\CentralAuth\GlobalRename\GlobalRenameDenylist; |
27 | use MediaWiki\Extension\CentralAuth\GlobalRename\GlobalRenameRequest; |
28 | use MediaWiki\Extension\CentralAuth\GlobalRename\GlobalRenameRequestStore; |
29 | use MediaWiki\Extension\CentralAuth\User\CentralAuthUser; |
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 Message; |
37 | use PermissionsError; |
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 | /** @var GlobalRenameDenylist */ |
48 | private $globalRenameDenylist; |
49 | |
50 | /** @var UserNameUtils */ |
51 | private $userNameUtils; |
52 | |
53 | /** @var GlobalRenameRequestStore */ |
54 | private $globalRenameRequestStore; |
55 | |
56 | /** |
57 | * @param GlobalRenameDenylist $globalRenameDenylist |
58 | * @param UserNameUtils $userNameUtils |
59 | * @param GlobalRenameRequestStore $globalRenameRequestStore |
60 | */ |
61 | public function __construct( |
62 | GlobalRenameDenylist $globalRenameDenylist, |
63 | UserNameUtils $userNameUtils, |
64 | GlobalRenameRequestStore $globalRenameRequestStore |
65 | ) { |
66 | parent::__construct( 'GlobalRenameRequest' ); |
67 | $this->globalRenameDenylist = $globalRenameDenylist; |
68 | $this->userNameUtils = $userNameUtils; |
69 | $this->globalRenameRequestStore = $globalRenameRequestStore; |
70 | } |
71 | |
72 | public function doesWrites() { |
73 | return true; |
74 | } |
75 | |
76 | /** @inheritDoc */ |
77 | public function userCanExecute( User $user ) { |
78 | return $this->globalRenameDenylist->checkUser( $user->getName() ); |
79 | } |
80 | |
81 | public function displayRestrictionError() { |
82 | throw new PermissionsError( null, [ 'centralauth-badaccess-blacklisted' ] ); |
83 | } |
84 | |
85 | /** |
86 | * @param string|null $par Subpage string if one was specified |
87 | */ |
88 | public function execute( $par ) { |
89 | $this->requireNamedUser(); |
90 | $this->addHelpLink( 'Help:Extension:CentralAuth/Global_rename' ); |
91 | |
92 | switch ( $par ) { |
93 | case 'status': |
94 | // Render status page |
95 | $user = $this->getUser(); |
96 | $username = $user->getName(); |
97 | $wiki = $this->isGlobalUser() ? null : WikiMap::getCurrentWikiId(); |
98 | $pending = $this->globalRenameRequestStore->newForUser( |
99 | $username, $wiki |
100 | ); |
101 | if ( !$pending->exists() ) { |
102 | $this->getOutput()->redirect( $this->getPageTitle()->getFullURL(), 303 ); |
103 | return; |
104 | } |
105 | $out = $this->getOutput(); |
106 | $out->setPageTitleMsg( |
107 | $this->msg( 'globalrenamerequest-status-title' ) |
108 | ); |
109 | $out->addWikiMsg( 'globalrenamerequest-status-text', |
110 | $username, $pending->getNewName() |
111 | ); |
112 | break; |
113 | |
114 | case 'available': |
115 | // TODO: ajax name availability check (T72623) |
116 | break; |
117 | |
118 | default: |
119 | // Request form |
120 | $out = $this->getOutput(); |
121 | $user = $this->getUser(); |
122 | $wiki = $this->isGlobalUser() ? null : WikiMap::getCurrentWikiId(); |
123 | |
124 | $pending = $this->globalRenameRequestStore->newForUser( |
125 | $user->getName(), $wiki |
126 | ); |
127 | if ( $pending->exists() ) { |
128 | $out->redirect( |
129 | $this->getPageTitle( 'status' )->getFullURL(), '303' |
130 | ); |
131 | return; |
132 | } |
133 | parent::execute( $par ); |
134 | break; |
135 | } |
136 | } |
137 | |
138 | /** |
139 | * Is the current user a global user? |
140 | * @return bool |
141 | */ |
142 | protected function isGlobalUser() { |
143 | $user = $this->getUser(); |
144 | $causer = CentralAuthUser::getInstance( $user ); |
145 | return $causer->exists() && $causer->isAttached(); |
146 | } |
147 | |
148 | /** |
149 | * @param HTMLForm $form |
150 | */ |
151 | protected function alterForm( HTMLForm $form ) { |
152 | $form->setSubmitTextMsg( 'globalrenamerequest-submit-text' ); |
153 | } |
154 | |
155 | /** |
156 | * @return string |
157 | */ |
158 | protected function getDisplayFormat() { |
159 | return 'ooui'; |
160 | } |
161 | |
162 | /** |
163 | * @return string |
164 | */ |
165 | protected function preHtml() { |
166 | $msg = $this->msg( 'globalrenamerequest-pretext' )->parse(); |
167 | if ( !$this->isGlobalUser() ) { |
168 | $msg = $this->msg( 'globalrenamerequest-forced' )->parse() . $msg; |
169 | } |
170 | return $msg; |
171 | } |
172 | |
173 | /** |
174 | * @return array[] |
175 | */ |
176 | public function getFormFields() { |
177 | $suggestedUsername = $this->suggestedUsername(); |
178 | if ( $suggestedUsername !== false ) { |
179 | $suggestedHelp = [ |
180 | 'globalrenamerequest-newname-help', |
181 | $suggestedUsername, |
182 | ]; |
183 | } else { |
184 | // Help message if we couldn't generate a suggested username |
185 | $suggestedHelp = 'globalrenamerequest-newname-help-basic'; |
186 | } |
187 | $fields = [ |
188 | 'username' => [ |
189 | 'cssclass' => 'mw-globalrenamerequest-field', |
190 | 'csshelpclass' => 'mw-globalrenamerequest-help', |
191 | 'default' => $this->getUser()->getName(), |
192 | 'label-message' => 'globalrenamerequest-username-label', |
193 | 'type' => 'info', |
194 | ], |
195 | 'newname' => [ |
196 | 'cssclass' => 'mw-globalrenamerequest-field', |
197 | 'csshelpclass' => 'mw-globalrenamerequest-help', |
198 | 'default' => $this->getRequest()->getVal( 'newname', $this->par ), |
199 | 'id' => 'mw-renamerequest-newname', |
200 | 'label-message' => 'globalrenamerequest-newname-label', |
201 | 'name' => 'newname', |
202 | 'required' => true, |
203 | 'type' => 'text', |
204 | 'help-message' => $suggestedHelp, |
205 | 'validation-callback' => [ $this, 'validateNewname' ], |
206 | ], |
207 | ]; |
208 | |
209 | $currentEmail = $this->getUser()->getEmail(); |
210 | if ( $currentEmail === '' ) { |
211 | $fields['email'] = [ |
212 | 'cssclass' => 'mw-globalrenamerequest-field', |
213 | 'csshelpclass' => 'mw-globalrenamerequest-help', |
214 | 'default' => $this->getRequest()->getVal( 'email', $this->par ), |
215 | 'id' => 'mw-renamerequest-email', |
216 | 'label-message' => 'globalrenamerequest-email-label', |
217 | 'name' => 'email', |
218 | 'placeholder' => 'username@example.com', |
219 | 'required' => true, |
220 | 'type' => 'email', |
221 | 'help-message' => 'globalrenamerequest-email-why-explain', |
222 | ]; |
223 | $fields['email2'] = [ |
224 | 'cssclass' => 'mw-globalrenamerequest-field', |
225 | 'csshelpclass' => 'mw-globalrenamerequest-help', |
226 | 'default' => $this->getRequest()->getVal( 'email2', $this->par ), |
227 | 'id' => 'mw-renamerequest-email2', |
228 | 'label-message' => 'globalrenamerequest-email2-label', |
229 | 'name' => 'email2', |
230 | 'placeholder' => 'username@example.com', |
231 | 'required' => true, |
232 | 'type' => 'email', |
233 | 'help-message' => 'globalrenamerequest-email2-help', |
234 | 'validation-callback' => [ $this, 'validateEmail' ], |
235 | ]; |
236 | } |
237 | |
238 | if ( $this->isGlobalUser() ) { |
239 | $fields['reason'] = [ |
240 | 'cssclass' => 'mw-globalrenamerequest-field', |
241 | 'default' => $this->getRequest()->getVal( 'reason', $this->par ), |
242 | 'id' => 'mw-renamerequest-reason', |
243 | 'label-message' => 'globalrenamerequest-reason-label', |
244 | 'name' => 'reason', |
245 | 'required' => true, |
246 | 'rows' => 5, |
247 | 'type' => 'textarea', |
248 | ]; |
249 | } |
250 | return $fields; |
251 | } |
252 | |
253 | /** |
254 | * Generate a username that appears to be globally available that an |
255 | * unimaginative user could use if they like. |
256 | * |
257 | * @return string|bool false if can't generate a username |
258 | */ |
259 | protected function suggestedUsername() { |
260 | // Only allow 5 tries (T260865) |
261 | $counter = 0; |
262 | // Whether we found a username that is available to use |
263 | $found = false; |
264 | $rand = ''; |
265 | while ( !$found && $counter < 5 ) { |
266 | $rand = $this->getUser()->getName() . rand( 123, 999 ); |
267 | $found = GlobalRenameRequest::isNameAvailable( $rand, IDBAccessObject::READ_NORMAL )->isOK(); |
268 | $counter++; |
269 | } |
270 | if ( $found ) { |
271 | return $rand; |
272 | } else { |
273 | return false; |
274 | } |
275 | } |
276 | |
277 | /** |
278 | * @param string|array $value The value the field was submitted with |
279 | * @param array $alldata The data collected from the form |
280 | * @param HTMLForm $form |
281 | * @return bool|Message True on success, or String error to display, or |
282 | * false to fail validation without displaying an error. |
283 | */ |
284 | public function validateNewname( $value, $alldata, HTMLForm $form ) { |
285 | if ( $value === null ) { |
286 | // Not submitted yet |
287 | return true; |
288 | } |
289 | $check = GlobalRenameRequest::isNameAvailable( $value ); |
290 | return $check->isGood() ? true : $check->getMessage(); |
291 | } |
292 | |
293 | /** |
294 | * @param string|array $value The value the field was submitted with |
295 | * @param array $alldata The data collected from the form |
296 | * @param HTMLForm $form |
297 | * @return bool|Message True on success, or String error to display, or |
298 | * false to fail validation without displaying an error. |
299 | */ |
300 | public function validateEmail( $value, $alldata, HTMLForm $form ) { |
301 | if ( $alldata['email'] !== $alldata['email2'] ) { |
302 | return $this->msg( 'globalrenamerequest-email-mismatch' ); |
303 | } elseif ( !Sanitizer::validateEmail( $alldata['email'] ) ) { |
304 | return $this->msg( 'globalrenamerequest-email-invalid' ); |
305 | } |
306 | return true; |
307 | } |
308 | |
309 | /** |
310 | * @param array $data |
311 | * @return Status |
312 | */ |
313 | public function onSubmit( array $data ) { |
314 | $wiki = $this->isGlobalUser() ? null : WikiMap::getCurrentWikiId(); |
315 | $reason = $data['reason'] ?? null; |
316 | $safeName = $this->userNameUtils->getCanonical( $data['newname'], UserNameUtils::RIGOR_CREATABLE ); |
317 | |
318 | $request = $this->globalRenameRequestStore->newBlankRequest(); |
319 | $request->setName( $this->getUser()->getName() ); |
320 | $request->setWiki( $wiki ); |
321 | $request->setNewName( $safeName ); |
322 | $request->setReason( $reason ); |
323 | |
324 | if ( $this->globalRenameRequestStore->save( $request ) ) { |
325 | $status = Status::newGood(); |
326 | |
327 | if ( isset( $data['email'] ) ) { |
328 | $user = $this->getUser(); |
329 | $user->setEmail( $data['email'] ); |
330 | $user->saveSettings(); |
331 | $status = $user->sendConfirmationMail( 'set' ); |
332 | } |
333 | } else { |
334 | $status = Status::newFatal( |
335 | $this->msg( 'globalrenamerequest-save-error' ) |
336 | ); |
337 | } |
338 | return $status; |
339 | } |
340 | |
341 | public function onSuccess() { |
342 | $this->getOutput()->redirect( |
343 | $this->getPageTitle( 'status' )->getFullURL(), '303' |
344 | ); |
345 | } |
346 | |
347 | /** |
348 | * Even blocked users should be able to ask for a rename. |
349 | * @return bool |
350 | */ |
351 | public function requiresUnblock() { |
352 | return false; |
353 | } |
354 | |
355 | /** @inheritDoc */ |
356 | protected function getGroupName() { |
357 | return 'login'; |
358 | } |
359 | } |