Convert all array() syntax to []
[lhc/web/wiklou.git] / includes / specials / SpecialBotPasswords.php
1 <?php
2 /**
3 * Implements Special:BotPasswords
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @ingroup SpecialPage
22 */
23
24 /**
25 * Let users manage bot passwords
26 *
27 * @ingroup SpecialPage
28 */
29 class SpecialBotPasswords extends FormSpecialPage {
30
31 /** @var int Central user ID */
32 private $userId = 0;
33
34 /** @var BotPassword|null Bot password being edited, if any */
35 private $botPassword = null;
36
37 /** @var string Operation being performed: create, update, delete */
38 private $operation = null;
39
40 /** @var string New password set, for communication between onSubmit() and onSuccess() */
41 private $password = null;
42
43 public function __construct() {
44 parent::__construct( 'BotPasswords', 'editmyprivateinfo' );
45 }
46
47 /**
48 * @return bool
49 */
50 public function isListed() {
51 return $this->getConfig()->get( 'EnableBotPasswords' );
52 }
53
54 /**
55 * Main execution point
56 * @param string|null $par
57 */
58 function execute( $par ) {
59 $this->getOutput()->disallowUserJs();
60 $this->requireLogin();
61
62 $par = trim( $par );
63 if ( strlen( $par ) === 0 ) {
64 $par = null;
65 } elseif ( strlen( $par ) > BotPassword::APPID_MAXLENGTH ) {
66 throw new ErrorPageError( 'botpasswords', 'botpasswords-bad-appid',
67 [ htmlspecialchars( $par ) ] );
68 }
69
70 parent::execute( $par );
71 }
72
73 protected function checkExecutePermissions( User $user ) {
74 parent::checkExecutePermissions( $user );
75
76 if ( !$this->getConfig()->get( 'EnableBotPasswords' ) ) {
77 throw new ErrorPageError( 'botpasswords', 'botpasswords-disabled' );
78 }
79
80 $this->userId = CentralIdLookup::factory()->centralIdFromLocalUser( $this->getUser() );
81 if ( !$this->userId ) {
82 throw new ErrorPageError( 'botpasswords', 'botpasswords-no-central-id' );
83 }
84 }
85
86 protected function getFormFields() {
87 $that = $this;
88 $user = $this->getUser();
89 $request = $this->getRequest();
90
91 $fields = [];
92
93 if ( $this->par !== null ) {
94 $this->botPassword = BotPassword::newFromCentralId( $this->userId, $this->par );
95 if ( !$this->botPassword ) {
96 $this->botPassword = BotPassword::newUnsaved( [
97 'centralId' => $this->userId,
98 'appId' => $this->par,
99 ] );
100 }
101
102 $sep = BotPassword::getSeparator();
103 $fields[] = [
104 'type' => 'info',
105 'label-message' => 'username',
106 'default' => $this->getUser()->getName() . $sep . $this->par
107 ];
108
109 if ( $this->botPassword->isSaved() ) {
110 $fields['resetPassword'] = [
111 'type' => 'check',
112 'label-message' => 'botpasswords-label-resetpassword',
113 ];
114 }
115
116 $lang = $this->getLanguage();
117 $showGrants = MWGrants::getValidGrants();
118 $fields['grants'] = [
119 'type' => 'checkmatrix',
120 'label-message' => 'botpasswords-label-grants',
121 'help-message' => 'botpasswords-help-grants',
122 'columns' => [
123 $this->msg( 'botpasswords-label-grants-column' )->escaped() => 'grant'
124 ],
125 'rows' => array_combine(
126 array_map( 'MWGrants::getGrantsLink', $showGrants ),
127 $showGrants
128 ),
129 'default' => array_map(
130 function( $g ) {
131 return "grant-$g";
132 },
133 $this->botPassword->getGrants()
134 ),
135 'tooltips' => array_combine(
136 array_map( 'MWGrants::getGrantsLink', $showGrants ),
137 array_map(
138 function( $rights ) use ( $lang ) {
139 return $lang->semicolonList( array_map( 'User::getRightDescription', $rights ) );
140 },
141 array_intersect_key( MWGrants::getRightsByGrant(), array_flip( $showGrants ) )
142 )
143 ),
144 'force-options-on' => array_map(
145 function( $g ) {
146 return "grant-$g";
147 },
148 MWGrants::getHiddenGrants()
149 ),
150 ];
151
152 $fields['restrictions'] = [
153 'type' => 'textarea',
154 'label-message' => 'botpasswords-label-restrictions',
155 'required' => true,
156 'default' => $this->botPassword->getRestrictions()->toJson( true ),
157 'rows' => 5,
158 'validation-callback' => function ( $v ) {
159 try {
160 MWRestrictions::newFromJson( $v );
161 return true;
162 } catch ( InvalidArgumentException $ex ) {
163 return $ex->getMessage();
164 }
165 },
166 ];
167
168 } else {
169 $dbr = BotPassword::getDB( DB_SLAVE );
170 $res = $dbr->select(
171 'bot_passwords',
172 [ 'bp_app_id' ],
173 [ 'bp_user' => $this->userId ],
174 __METHOD__
175 );
176 foreach ( $res as $row ) {
177 $fields[] = [
178 'section' => 'existing',
179 'type' => 'info',
180 'raw' => true,
181 'default' => Linker::link(
182 $this->getPageTitle( $row->bp_app_id ),
183 htmlspecialchars( $row->bp_app_id ),
184 [],
185 [],
186 [ 'known' ]
187 ),
188 ];
189 }
190
191 $fields['appId'] = [
192 'section' => 'createnew',
193 'type' => 'textwithbutton',
194 'label-message' => 'botpasswords-label-appid',
195 'buttondefault' => $this->msg( 'botpasswords-label-create' )->text(),
196 'required' => true,
197 'size' => BotPassword::APPID_MAXLENGTH,
198 'maxlength' => BotPassword::APPID_MAXLENGTH,
199 'validation-callback' => function ( $v ) {
200 $v = trim( $v );
201 return $v !== '' && strlen( $v ) <= BotPassword::APPID_MAXLENGTH;
202 },
203 ];
204
205 $fields[] = [
206 'type' => 'hidden',
207 'default' => 'new',
208 'name' => 'op',
209 ];
210 }
211
212 return $fields;
213 }
214
215 protected function alterForm( HTMLForm $form ) {
216 $form->setId( 'mw-botpasswords-form' );
217 $form->setTableId( 'mw-botpasswords-table' );
218 $form->addPreText( $this->msg( 'botpasswords-summary' )->parseAsBlock() );
219 $form->suppressDefaultSubmit();
220
221 if ( $this->par !== null ) {
222 if ( $this->botPassword->isSaved() ) {
223 $form->setWrapperLegendMsg( 'botpasswords-editexisting' );
224 $form->addButton( [
225 'name' => 'op',
226 'value' => 'update',
227 'label-message' => 'botpasswords-label-update',
228 'flags' => [ 'primary', 'progressive' ],
229 ] );
230 $form->addButton( [
231 'name' => 'op',
232 'value' => 'delete',
233 'label-message' => 'botpasswords-label-delete',
234 'flags' => [ 'destructive' ],
235 ] );
236 } else {
237 $form->setWrapperLegendMsg( 'botpasswords-createnew' );
238 $form->addButton( [
239 'name' => 'op',
240 'value' => 'create',
241 'label-message' => 'botpasswords-label-create',
242 'flags' => [ 'primary', 'constructive' ],
243 ] );
244 }
245
246 $form->addButton( [
247 'name' => 'op',
248 'value' => 'cancel',
249 'label-message' => 'botpasswords-label-cancel'
250 ] );
251 }
252 }
253
254 public function onSubmit( array $data ) {
255 $op = $this->getRequest()->getVal( 'op', '' );
256
257 switch ( $op ) {
258 case 'new':
259 $this->getOutput()->redirect( $this->getPageTitle( $data['appId'] )->getFullURL() );
260 return false;
261
262 case 'create':
263 $this->operation = 'insert';
264 return $this->save( $data );
265
266 case 'update':
267 $this->operation = 'update';
268 return $this->save( $data );
269
270 case 'delete':
271 $this->operation = 'delete';
272 $bp = BotPassword::newFromCentralId( $this->userId, $this->par );
273 if ( $bp ) {
274 $bp->delete();
275 }
276 return Status::newGood();
277
278 case 'cancel':
279 $this->getOutput()->redirect( $this->getPageTitle()->getFullURL() );
280 return false;
281 }
282
283 return false;
284 }
285
286 private function save( array $data ) {
287 $bp = BotPassword::newUnsaved( [
288 'centralId' => $this->userId,
289 'appId' => $this->par,
290 'restrictions' => MWRestrictions::newFromJson( $data['restrictions'] ),
291 'grants' => array_merge(
292 MWGrants::getHiddenGrants(),
293 preg_replace( '/^grant-/', '', $data['grants'] )
294 )
295 ] );
296
297 if ( $this->operation === 'insert' || !empty( $data['resetPassword'] ) ) {
298 $this->password = PasswordFactory::generateRandomPasswordString(
299 max( 32, $this->getConfig()->get( 'MinimalPasswordLength' ) )
300 );
301 $passwordFactory = new PasswordFactory();
302 $passwordFactory->init( RequestContext::getMain()->getConfig() );
303 $password = $passwordFactory->newFromPlaintext( $this->password );
304 } else {
305 $password = null;
306 }
307
308 if ( $bp->save( $this->operation, $password ) ) {
309 return Status::newGood();
310 } else {
311 // Messages: botpasswords-insert-failed, botpasswords-update-failed
312 return Status::newFatal( "botpasswords-{$this->operation}-failed", $this->par );
313 }
314 }
315
316 public function onSuccess() {
317 $out = $this->getOutput();
318
319 switch ( $this->operation ) {
320 case 'insert':
321 $out->setPageTitle( $this->msg( 'botpasswords-created-title' )->text() );
322 $out->addWikiMsg( 'botpasswords-created-body', $this->par );
323 break;
324
325 case 'update':
326 $out->setPageTitle( $this->msg( 'botpasswords-updated-title' )->text() );
327 $out->addWikiMsg( 'botpasswords-updated-body', $this->par );
328 break;
329
330 case 'delete':
331 $out->setPageTitle( $this->msg( 'botpasswords-deleted-title' )->text() );
332 $out->addWikiMsg( 'botpasswords-deleted-body', $this->par );
333 $this->password = null;
334 break;
335 }
336
337 if ( $this->password !== null ) {
338 $sep = BotPassword::getSeparator();
339 $out->addWikiMsg(
340 'botpasswords-newpassword',
341 htmlspecialchars( $this->getUser()->getName() . $sep . $this->par ),
342 htmlspecialchars( $this->password )
343 );
344 $this->password = null;
345 }
346
347 $out->addReturnTo( $this->getPageTitle() );
348 }
349
350 protected function getGroupName() {
351 return 'users';
352 }
353
354 protected function getDisplayFormat() {
355 return 'ooui';
356 }
357 }