This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * Implements Special:Movepage |
||
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 | * A special page that allows users to change page titles |
||
26 | * |
||
27 | * @ingroup SpecialPage |
||
28 | */ |
||
29 | class MovePageForm extends UnlistedSpecialPage { |
||
30 | /** @var Title */ |
||
31 | protected $oldTitle = null; |
||
32 | |||
33 | /** @var Title */ |
||
34 | protected $newTitle; |
||
35 | |||
36 | /** @var string Text input */ |
||
37 | protected $reason; |
||
38 | |||
39 | // Checks |
||
40 | |||
41 | /** @var bool */ |
||
42 | protected $moveTalk; |
||
43 | |||
44 | /** @var bool */ |
||
45 | protected $deleteAndMove; |
||
46 | |||
47 | /** @var bool */ |
||
48 | protected $moveSubpages; |
||
49 | |||
50 | /** @var bool */ |
||
51 | protected $fixRedirects; |
||
52 | |||
53 | /** @var bool */ |
||
54 | protected $leaveRedirect; |
||
55 | |||
56 | /** @var bool */ |
||
57 | protected $moveOverShared; |
||
58 | |||
59 | private $watch = false; |
||
60 | |||
61 | public function __construct() { |
||
62 | parent::__construct( 'Movepage' ); |
||
63 | } |
||
64 | |||
65 | public function doesWrites() { |
||
66 | return true; |
||
67 | } |
||
68 | |||
69 | public function execute( $par ) { |
||
70 | $this->useTransactionalTimeLimit(); |
||
71 | |||
72 | $this->checkReadOnly(); |
||
73 | |||
74 | $this->setHeaders(); |
||
75 | $this->outputHeader(); |
||
76 | |||
77 | $request = $this->getRequest(); |
||
78 | $target = !is_null( $par ) ? $par : $request->getVal( 'target' ); |
||
79 | |||
80 | // Yes, the use of getVal() and getText() is wanted, see bug 20365 |
||
81 | |||
82 | $oldTitleText = $request->getVal( 'wpOldTitle', $target ); |
||
83 | $this->oldTitle = Title::newFromText( $oldTitleText ); |
||
84 | |||
85 | if ( !$this->oldTitle ) { |
||
86 | // Either oldTitle wasn't passed, or newFromText returned null |
||
87 | throw new ErrorPageError( 'notargettitle', 'notargettext' ); |
||
88 | } |
||
89 | if ( !$this->oldTitle->exists() ) { |
||
90 | throw new ErrorPageError( 'nopagetitle', 'nopagetext' ); |
||
91 | } |
||
92 | |||
93 | $newTitleTextMain = $request->getText( 'wpNewTitleMain' ); |
||
94 | $newTitleTextNs = $request->getInt( 'wpNewTitleNs', $this->oldTitle->getNamespace() ); |
||
95 | // Backwards compatibility for forms submitting here from other sources |
||
96 | // which is more common than it should be.. |
||
97 | $newTitleText_bc = $request->getText( 'wpNewTitle' ); |
||
98 | $this->newTitle = strlen( $newTitleText_bc ) > 0 |
||
99 | ? Title::newFromText( $newTitleText_bc ) |
||
100 | : Title::makeTitleSafe( $newTitleTextNs, $newTitleTextMain ); |
||
101 | |||
102 | $user = $this->getUser(); |
||
103 | |||
104 | # Check rights |
||
105 | $permErrors = $this->oldTitle->getUserPermissionsErrors( 'move', $user ); |
||
106 | if ( count( $permErrors ) ) { |
||
107 | // Auto-block user's IP if the account was "hard" blocked |
||
108 | DeferredUpdates::addCallableUpdate( function() use ( $user ) { |
||
109 | $user->spreadAnyEditBlock(); |
||
110 | } ); |
||
111 | throw new PermissionsError( 'move', $permErrors ); |
||
112 | } |
||
113 | |||
114 | $def = !$request->wasPosted(); |
||
115 | |||
116 | $this->reason = $request->getText( 'wpReason' ); |
||
117 | $this->moveTalk = $request->getBool( 'wpMovetalk', $def ); |
||
118 | $this->fixRedirects = $request->getBool( 'wpFixRedirects', $def ); |
||
119 | $this->leaveRedirect = $request->getBool( 'wpLeaveRedirect', $def ); |
||
120 | $this->moveSubpages = $request->getBool( 'wpMovesubpages' ); |
||
121 | $this->deleteAndMove = $request->getBool( 'wpDeleteAndMove' ); |
||
122 | $this->moveOverShared = $request->getBool( 'wpMoveOverSharedFile' ); |
||
123 | $this->watch = $request->getCheck( 'wpWatch' ) && $user->isLoggedIn(); |
||
124 | |||
125 | if ( 'submit' == $request->getVal( 'action' ) && $request->wasPosted() |
||
126 | && $user->matchEditToken( $request->getVal( 'wpEditToken' ) ) |
||
127 | ) { |
||
128 | $this->doSubmit(); |
||
129 | } else { |
||
130 | $this->showForm( [] ); |
||
131 | } |
||
132 | } |
||
133 | |||
134 | /** |
||
135 | * Show the form |
||
136 | * |
||
137 | * @param array $err Error messages. Each item is an error message. |
||
138 | * It may either be a string message name or array message name and |
||
139 | * parameters, like the second argument to OutputPage::wrapWikiMsg(). |
||
140 | */ |
||
141 | function showForm( $err ) { |
||
142 | $this->getSkin()->setRelevantTitle( $this->oldTitle ); |
||
143 | |||
144 | $out = $this->getOutput(); |
||
145 | $out->setPageTitle( $this->msg( 'move-page', $this->oldTitle->getPrefixedText() ) ); |
||
146 | $out->addModules( 'mediawiki.special.movePage' ); |
||
147 | $out->addModuleStyles( 'mediawiki.special.movePage.styles' ); |
||
148 | $this->addHelpLink( 'Help:Moving a page' ); |
||
149 | |||
150 | $out->addWikiMsg( $this->getConfig()->get( 'FixDoubleRedirects' ) ? |
||
151 | 'movepagetext' : |
||
152 | 'movepagetext-noredirectfixer' |
||
153 | ); |
||
154 | |||
155 | if ( $this->oldTitle->getNamespace() == NS_USER && !$this->oldTitle->isSubpage() ) { |
||
156 | $out->wrapWikiMsg( |
||
157 | "<div class=\"warningbox mw-moveuserpage-warning\">\n$1\n</div>", |
||
158 | 'moveuserpage-warning' |
||
159 | ); |
||
160 | } elseif ( $this->oldTitle->getNamespace() == NS_CATEGORY ) { |
||
161 | $out->wrapWikiMsg( |
||
162 | "<div class=\"warningbox mw-movecategorypage-warning\">\n$1\n</div>", |
||
163 | 'movecategorypage-warning' |
||
164 | ); |
||
165 | } |
||
166 | |||
167 | $deleteAndMove = false; |
||
168 | $moveOverShared = false; |
||
169 | |||
170 | $newTitle = $this->newTitle; |
||
171 | |||
172 | if ( !$newTitle ) { |
||
173 | # Show the current title as a default |
||
174 | # when the form is first opened. |
||
175 | $newTitle = $this->oldTitle; |
||
176 | } elseif ( !count( $err ) ) { |
||
177 | # If a title was supplied, probably from the move log revert |
||
178 | # link, check for validity. We can then show some diagnostic |
||
179 | # information and save a click. |
||
180 | $newerr = $this->oldTitle->isValidMoveOperation( $newTitle ); |
||
181 | if ( is_array( $newerr ) ) { |
||
182 | $err = $newerr; |
||
183 | } |
||
184 | } |
||
185 | |||
186 | $user = $this->getUser(); |
||
187 | |||
188 | View Code Duplication | if ( count( $err ) == 1 && isset( $err[0][0] ) && $err[0][0] == 'articleexists' |
|
189 | && $newTitle->quickUserCan( 'delete', $user ) |
||
190 | ) { |
||
191 | $out->wrapWikiMsg( |
||
192 | "<div class='warningbox'>\n$1\n</div>\n", |
||
193 | [ 'delete_and_move_text', $newTitle->getPrefixedText() ] |
||
194 | ); |
||
195 | $deleteAndMove = true; |
||
196 | $err = []; |
||
197 | } |
||
198 | |||
199 | View Code Duplication | if ( count( $err ) == 1 && isset( $err[0][0] ) && $err[0][0] == 'file-exists-sharedrepo' |
|
200 | && $user->isAllowed( 'reupload-shared' ) |
||
201 | ) { |
||
202 | $out->wrapWikiMsg( |
||
203 | "<div class='warningbox'>\n$1\n</div>\n", |
||
204 | [ |
||
205 | 'move-over-sharedrepo', |
||
206 | $newTitle->getPrefixedText() |
||
207 | ] |
||
208 | ); |
||
209 | $moveOverShared = true; |
||
210 | $err = []; |
||
211 | } |
||
212 | |||
213 | $oldTalk = $this->oldTitle->getTalkPage(); |
||
214 | $oldTitleSubpages = $this->oldTitle->hasSubpages(); |
||
215 | $oldTitleTalkSubpages = $this->oldTitle->getTalkPage()->hasSubpages(); |
||
216 | |||
217 | $canMoveSubpage = ( $oldTitleSubpages || $oldTitleTalkSubpages ) && |
||
218 | !count( $this->oldTitle->getUserPermissionsErrors( 'move-subpages', $user ) ); |
||
219 | |||
220 | # We also want to be able to move assoc. subpage talk-pages even if base page |
||
221 | # has no associated talk page, so || with $oldTitleTalkSubpages. |
||
222 | $considerTalk = !$this->oldTitle->isTalkPage() && |
||
223 | ( $oldTalk->exists() |
||
224 | || ( $oldTitleTalkSubpages && $canMoveSubpage ) ); |
||
225 | |||
226 | $dbr = wfGetDB( DB_REPLICA ); |
||
227 | if ( $this->getConfig()->get( 'FixDoubleRedirects' ) ) { |
||
228 | $hasRedirects = $dbr->selectField( 'redirect', '1', |
||
229 | [ |
||
230 | 'rd_namespace' => $this->oldTitle->getNamespace(), |
||
231 | 'rd_title' => $this->oldTitle->getDBkey(), |
||
232 | ], __METHOD__ ); |
||
233 | } else { |
||
234 | $hasRedirects = false; |
||
235 | } |
||
236 | |||
237 | if ( count( $err ) ) { |
||
238 | $out->addHTML( "<div class='errorbox'>\n" ); |
||
239 | $action_desc = $this->msg( 'action-move' )->plain(); |
||
240 | $out->addWikiMsg( 'permissionserrorstext-withaction', count( $err ), $action_desc ); |
||
241 | |||
242 | if ( count( $err ) == 1 ) { |
||
243 | $errMsg = $err[0]; |
||
244 | $errMsgName = array_shift( $errMsg ); |
||
245 | |||
246 | if ( $errMsgName == 'hookaborted' ) { |
||
247 | $out->addHTML( "<p>{$errMsg[0]}</p>\n" ); |
||
248 | } else { |
||
249 | $out->addWikiMsgArray( $errMsgName, $errMsg ); |
||
250 | } |
||
251 | } else { |
||
252 | $errStr = []; |
||
253 | |||
254 | foreach ( $err as $errMsg ) { |
||
255 | if ( $errMsg[0] == 'hookaborted' ) { |
||
256 | $errStr[] = $errMsg[1]; |
||
257 | } else { |
||
258 | $errMsgName = array_shift( $errMsg ); |
||
259 | $errStr[] = $this->msg( $errMsgName, $errMsg )->parse(); |
||
260 | } |
||
261 | } |
||
262 | |||
263 | $out->addHTML( '<ul><li>' . implode( "</li>\n<li>", $errStr ) . "</li></ul>\n" ); |
||
264 | } |
||
265 | $out->addHTML( "</div>\n" ); |
||
266 | } |
||
267 | |||
268 | if ( $this->oldTitle->isProtected( 'move' ) ) { |
||
269 | # Is the title semi-protected? |
||
270 | if ( $this->oldTitle->isSemiProtected( 'move' ) ) { |
||
271 | $noticeMsg = 'semiprotectedpagemovewarning'; |
||
272 | $classes[] = 'mw-textarea-sprotected'; |
||
0 ignored issues
–
show
|
|||
273 | } else { |
||
274 | # Then it must be protected based on static groups (regular) |
||
275 | $noticeMsg = 'protectedpagemovewarning'; |
||
276 | $classes[] = 'mw-textarea-protected'; |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
$classes was never initialized. Although not strictly required by PHP, it is generally a good practice to add $classes = array(); before regardless.
Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code. Let’s take a look at an example: foreach ($collection as $item) {
$myArray['foo'] = $item->getFoo();
if ($item->hasBar()) {
$myArray['bar'] = $item->getBar();
}
// do something with $myArray
}
As you can see in this example, the array This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop. ![]() |
|||
277 | } |
||
278 | $out->addHTML( "<div class='mw-warning-with-logexcerpt'>\n" ); |
||
279 | $out->addWikiMsg( $noticeMsg ); |
||
280 | LogEventsList::showLogExtract( |
||
281 | $out, |
||
282 | 'protect', |
||
283 | $this->oldTitle, |
||
284 | '', |
||
285 | [ 'lim' => 1 ] |
||
286 | ); |
||
287 | $out->addHTML( "</div>\n" ); |
||
288 | } |
||
289 | |||
290 | // Byte limit (not string length limit) for wpReason and wpNewTitleMain |
||
291 | // is enforced in the mediawiki.special.movePage module |
||
292 | |||
293 | $immovableNamespaces = []; |
||
294 | foreach ( array_keys( $this->getLanguage()->getNamespaces() ) as $nsId ) { |
||
295 | if ( !MWNamespace::isMovable( $nsId ) ) { |
||
296 | $immovableNamespaces[] = $nsId; |
||
297 | } |
||
298 | } |
||
299 | |||
300 | $handler = ContentHandler::getForTitle( $this->oldTitle ); |
||
301 | |||
302 | $out->enableOOUI(); |
||
303 | $fields = []; |
||
304 | |||
305 | $fields[] = new OOUI\FieldLayout( |
||
306 | new MediaWiki\Widget\ComplexTitleInputWidget( [ |
||
307 | 'id' => 'wpNewTitle', |
||
308 | 'namespace' => [ |
||
309 | 'id' => 'wpNewTitleNs', |
||
310 | 'name' => 'wpNewTitleNs', |
||
311 | 'value' => $newTitle->getNamespace(), |
||
312 | 'exclude' => $immovableNamespaces, |
||
313 | ], |
||
314 | 'title' => [ |
||
315 | 'id' => 'wpNewTitleMain', |
||
316 | 'name' => 'wpNewTitleMain', |
||
317 | 'value' => $newTitle->getText(), |
||
318 | // Inappropriate, since we're expecting the user to input a non-existent page's title |
||
319 | 'suggestions' => false, |
||
320 | ], |
||
321 | 'infusable' => true, |
||
322 | ] ), |
||
323 | [ |
||
324 | 'label' => $this->msg( 'newtitle' )->text(), |
||
325 | 'align' => 'top', |
||
326 | ] |
||
327 | ); |
||
328 | |||
329 | $fields[] = new OOUI\FieldLayout( |
||
330 | new OOUI\TextInputWidget( [ |
||
331 | 'name' => 'wpReason', |
||
332 | 'id' => 'wpReason', |
||
333 | 'maxLength' => 200, |
||
334 | 'infusable' => true, |
||
335 | 'value' => $this->reason, |
||
336 | ] ), |
||
337 | [ |
||
338 | 'label' => $this->msg( 'movereason' )->text(), |
||
339 | 'align' => 'top', |
||
340 | ] |
||
341 | ); |
||
342 | |||
343 | if ( $considerTalk ) { |
||
344 | $fields[] = new OOUI\FieldLayout( |
||
345 | new OOUI\CheckboxInputWidget( [ |
||
346 | 'name' => 'wpMovetalk', |
||
347 | 'id' => 'wpMovetalk', |
||
348 | 'value' => '1', |
||
349 | 'selected' => $this->moveTalk, |
||
350 | ] ), |
||
351 | [ |
||
352 | 'label' => $this->msg( 'movetalk' )->text(), |
||
353 | 'help' => new OOUI\HtmlSnippet( $this->msg( 'movepagetalktext' )->parseAsBlock() ), |
||
354 | 'align' => 'inline', |
||
355 | 'infusable' => true, |
||
356 | 'id' => 'wpMovetalk-field', |
||
357 | ] |
||
358 | ); |
||
359 | } |
||
360 | |||
361 | if ( $user->isAllowed( 'suppressredirect' ) ) { |
||
362 | if ( $handler->supportsRedirects() ) { |
||
363 | $isChecked = $this->leaveRedirect; |
||
364 | $isDisabled = false; |
||
365 | } else { |
||
366 | $isChecked = false; |
||
367 | $isDisabled = true; |
||
368 | } |
||
369 | $fields[] = new OOUI\FieldLayout( |
||
370 | new OOUI\CheckboxInputWidget( [ |
||
371 | 'name' => 'wpLeaveRedirect', |
||
372 | 'id' => 'wpLeaveRedirect', |
||
373 | 'value' => '1', |
||
374 | 'selected' => $isChecked, |
||
375 | 'disabled' => $isDisabled, |
||
376 | ] ), |
||
377 | [ |
||
378 | 'label' => $this->msg( 'move-leave-redirect' )->text(), |
||
379 | 'align' => 'inline', |
||
380 | ] |
||
381 | ); |
||
382 | } |
||
383 | |||
384 | View Code Duplication | if ( $hasRedirects ) { |
|
385 | $fields[] = new OOUI\FieldLayout( |
||
386 | new OOUI\CheckboxInputWidget( [ |
||
387 | 'name' => 'wpFixRedirects', |
||
388 | 'id' => 'wpFixRedirects', |
||
389 | 'value' => '1', |
||
390 | 'selected' => $this->fixRedirects, |
||
391 | ] ), |
||
392 | [ |
||
393 | 'label' => $this->msg( 'fix-double-redirects' )->text(), |
||
394 | 'align' => 'inline', |
||
395 | ] |
||
396 | ); |
||
397 | } |
||
398 | |||
399 | if ( $canMoveSubpage ) { |
||
400 | $maximumMovedPages = $this->getConfig()->get( 'MaximumMovedPages' ); |
||
401 | $fields[] = new OOUI\FieldLayout( |
||
402 | new OOUI\CheckboxInputWidget( [ |
||
403 | 'name' => 'wpMovesubpages', |
||
404 | 'id' => 'wpMovesubpages', |
||
405 | 'value' => '1', |
||
406 | # Don't check the box if we only have talk subpages to |
||
407 | # move and we aren't moving the talk page. |
||
408 | 'selected' => $this->moveSubpages && ( $this->oldTitle->hasSubpages() || $this->moveTalk ), |
||
409 | ] ), |
||
410 | [ |
||
411 | 'label' => new OOUI\HtmlSnippet( |
||
412 | $this->msg( |
||
413 | ( $this->oldTitle->hasSubpages() |
||
414 | ? 'move-subpages' |
||
415 | : 'move-talk-subpages' ) |
||
416 | )->numParams( $maximumMovedPages )->params( $maximumMovedPages )->parse() |
||
417 | ), |
||
418 | 'align' => 'inline', |
||
419 | ] |
||
420 | ); |
||
421 | } |
||
422 | |||
423 | # Don't allow watching if user is not logged in |
||
424 | if ( $user->isLoggedIn() ) { |
||
425 | $watchChecked = $user->isLoggedIn() && ( $this->watch || $user->getBoolOption( 'watchmoves' ) |
||
426 | || $user->isWatched( $this->oldTitle ) ); |
||
427 | $fields[] = new OOUI\FieldLayout( |
||
428 | new OOUI\CheckboxInputWidget( [ |
||
429 | 'name' => 'wpWatch', |
||
430 | 'id' => 'watch', # ew |
||
431 | 'value' => '1', |
||
432 | 'selected' => $watchChecked, |
||
433 | ] ), |
||
434 | [ |
||
435 | 'label' => $this->msg( 'move-watch' )->text(), |
||
436 | 'align' => 'inline', |
||
437 | ] |
||
438 | ); |
||
439 | } |
||
440 | |||
441 | $hiddenFields = ''; |
||
442 | if ( $moveOverShared ) { |
||
443 | $hiddenFields .= Html::hidden( 'wpMoveOverSharedFile', '1' ); |
||
444 | } |
||
445 | |||
446 | View Code Duplication | if ( $deleteAndMove ) { |
|
447 | $fields[] = new OOUI\FieldLayout( |
||
448 | new OOUI\CheckboxInputWidget( [ |
||
449 | 'name' => 'wpDeleteAndMove', |
||
450 | 'id' => 'wpDeleteAndMove', |
||
451 | 'value' => '1', |
||
452 | ] ), |
||
453 | [ |
||
454 | 'label' => $this->msg( 'delete_and_move_confirm' )->text(), |
||
455 | 'align' => 'inline', |
||
456 | ] |
||
457 | ); |
||
458 | } |
||
459 | |||
460 | $fields[] = new OOUI\FieldLayout( |
||
461 | new OOUI\ButtonInputWidget( [ |
||
462 | 'name' => 'wpMove', |
||
463 | 'value' => $this->msg( 'movepagebtn' )->text(), |
||
464 | 'label' => $this->msg( 'movepagebtn' )->text(), |
||
465 | 'flags' => [ 'primary', 'progressive' ], |
||
466 | 'type' => 'submit', |
||
467 | ] ), |
||
468 | [ |
||
469 | 'align' => 'top', |
||
470 | ] |
||
471 | ); |
||
472 | |||
473 | $fieldset = new OOUI\FieldsetLayout( [ |
||
474 | 'label' => $this->msg( 'move-page-legend' )->text(), |
||
475 | 'id' => 'mw-movepage-table', |
||
476 | 'items' => $fields, |
||
477 | ] ); |
||
478 | |||
479 | $form = new OOUI\FormLayout( [ |
||
480 | 'method' => 'post', |
||
481 | 'action' => $this->getPageTitle()->getLocalURL( 'action=submit' ), |
||
482 | 'id' => 'movepage', |
||
483 | ] ); |
||
484 | $form->appendContent( |
||
485 | $fieldset, |
||
486 | new OOUI\HtmlSnippet( |
||
487 | $hiddenFields . |
||
488 | Html::hidden( 'wpOldTitle', $this->oldTitle->getPrefixedText() ) . |
||
489 | Html::hidden( 'wpEditToken', $user->getEditToken() ) |
||
490 | ) |
||
491 | ); |
||
492 | |||
493 | $out->addHTML( |
||
494 | new OOUI\PanelLayout( [ |
||
495 | 'classes' => [ 'movepage-wrapper' ], |
||
496 | 'expanded' => false, |
||
497 | 'padded' => true, |
||
498 | 'framed' => true, |
||
499 | 'content' => $form, |
||
500 | ] ) |
||
501 | ); |
||
502 | |||
503 | $this->showLogFragment( $this->oldTitle ); |
||
504 | $this->showSubpages( $this->oldTitle ); |
||
505 | } |
||
506 | |||
507 | function doSubmit() { |
||
508 | $user = $this->getUser(); |
||
509 | |||
510 | if ( $user->pingLimiter( 'move' ) ) { |
||
511 | throw new ThrottledError; |
||
512 | } |
||
513 | |||
514 | $ot = $this->oldTitle; |
||
515 | $nt = $this->newTitle; |
||
516 | |||
517 | # don't allow moving to pages with # in |
||
518 | if ( !$nt || $nt->hasFragment() ) { |
||
519 | $this->showForm( [ [ 'badtitletext' ] ] ); |
||
520 | |||
521 | return; |
||
522 | } |
||
523 | |||
524 | # Show a warning if the target file exists on a shared repo |
||
525 | if ( $nt->getNamespace() == NS_FILE |
||
526 | && !( $this->moveOverShared && $user->isAllowed( 'reupload-shared' ) ) |
||
527 | && !RepoGroup::singleton()->getLocalRepo()->findFile( $nt ) |
||
528 | && wfFindFile( $nt ) |
||
529 | ) { |
||
530 | $this->showForm( [ [ 'file-exists-sharedrepo' ] ] ); |
||
531 | |||
532 | return; |
||
533 | } |
||
534 | |||
535 | # Delete to make way if requested |
||
536 | if ( $this->deleteAndMove ) { |
||
537 | $permErrors = $nt->getUserPermissionsErrors( 'delete', $user ); |
||
538 | if ( count( $permErrors ) ) { |
||
539 | # Only show the first error |
||
540 | $this->showForm( $permErrors ); |
||
541 | |||
542 | return; |
||
543 | } |
||
544 | |||
545 | $reason = $this->msg( 'delete_and_move_reason', $ot )->inContentLanguage()->text(); |
||
546 | |||
547 | // Delete an associated image if there is |
||
548 | if ( $nt->getNamespace() == NS_FILE ) { |
||
549 | $file = wfLocalFile( $nt ); |
||
550 | $file->load( File::READ_LATEST ); |
||
551 | if ( $file->exists() ) { |
||
552 | $file->delete( $reason, false, $user ); |
||
553 | } |
||
554 | } |
||
555 | |||
556 | $error = ''; // passed by ref |
||
557 | $page = WikiPage::factory( $nt ); |
||
558 | $deleteStatus = $page->doDeleteArticleReal( $reason, false, 0, true, $error, $user ); |
||
559 | if ( !$deleteStatus->isGood() ) { |
||
560 | $this->showForm( $deleteStatus->getErrorsArray() ); |
||
561 | |||
562 | return; |
||
563 | } |
||
564 | } |
||
565 | |||
566 | $handler = ContentHandler::getForTitle( $ot ); |
||
567 | |||
568 | if ( !$handler->supportsRedirects() ) { |
||
569 | $createRedirect = false; |
||
570 | } elseif ( $user->isAllowed( 'suppressredirect' ) ) { |
||
571 | $createRedirect = $this->leaveRedirect; |
||
572 | } else { |
||
573 | $createRedirect = true; |
||
574 | } |
||
575 | |||
576 | # Do the actual move. |
||
577 | $mp = new MovePage( $ot, $nt ); |
||
578 | $valid = $mp->isValidMove(); |
||
579 | if ( !$valid->isOK() ) { |
||
580 | $this->showForm( $valid->getErrorsArray() ); |
||
581 | return; |
||
582 | } |
||
583 | |||
584 | $permStatus = $mp->checkPermissions( $user, $this->reason ); |
||
585 | if ( !$permStatus->isOK() ) { |
||
586 | $this->showForm( $permStatus->getErrorsArray() ); |
||
587 | return; |
||
588 | } |
||
589 | |||
590 | $status = $mp->move( $user, $this->reason, $createRedirect ); |
||
591 | if ( !$status->isOK() ) { |
||
592 | $this->showForm( $status->getErrorsArray() ); |
||
593 | return; |
||
594 | } |
||
595 | |||
596 | if ( $this->getConfig()->get( 'FixDoubleRedirects' ) && $this->fixRedirects ) { |
||
597 | DoubleRedirectJob::fixRedirects( 'move', $ot, $nt ); |
||
598 | } |
||
599 | |||
600 | $out = $this->getOutput(); |
||
601 | $out->setPageTitle( $this->msg( 'pagemovedsub' ) ); |
||
602 | |||
603 | $linkRenderer = $this->getLinkRenderer(); |
||
604 | $oldLink = $linkRenderer->makeLink( |
||
605 | $ot, |
||
606 | null, |
||
607 | [ 'id' => 'movepage-oldlink' ], |
||
608 | [ 'redirect' => 'no' ] |
||
609 | ); |
||
610 | $newLink = $linkRenderer->makeKnownLink( |
||
611 | $nt, |
||
612 | null, |
||
613 | [ 'id' => 'movepage-newlink' ] |
||
614 | ); |
||
615 | $oldText = $ot->getPrefixedText(); |
||
616 | $newText = $nt->getPrefixedText(); |
||
617 | |||
618 | if ( $ot->exists() ) { |
||
619 | // NOTE: we assume that if the old title exists, it's because it was re-created as |
||
620 | // a redirect to the new title. This is not safe, but what we did before was |
||
621 | // even worse: we just determined whether a redirect should have been created, |
||
622 | // and reported that it was created if it should have, without any checks. |
||
623 | // Also note that isRedirect() is unreliable because of bug 37209. |
||
624 | $msgName = 'movepage-moved-redirect'; |
||
625 | } else { |
||
626 | $msgName = 'movepage-moved-noredirect'; |
||
627 | } |
||
628 | |||
629 | $out->addHTML( $this->msg( 'movepage-moved' )->rawParams( $oldLink, |
||
630 | $newLink )->params( $oldText, $newText )->parseAsBlock() ); |
||
631 | $out->addWikiMsg( $msgName ); |
||
632 | |||
633 | Hooks::run( 'SpecialMovepageAfterMove', [ &$this, &$ot, &$nt ] ); |
||
634 | |||
635 | # Now we move extra pages we've been asked to move: subpages and talk |
||
636 | # pages. First, if the old page or the new page is a talk page, we |
||
637 | # can't move any talk pages: cancel that. |
||
638 | if ( $ot->isTalkPage() || $nt->isTalkPage() ) { |
||
639 | $this->moveTalk = false; |
||
640 | } |
||
641 | |||
642 | if ( count( $ot->getUserPermissionsErrors( 'move-subpages', $user ) ) ) { |
||
643 | $this->moveSubpages = false; |
||
644 | } |
||
645 | |||
646 | /** |
||
647 | * Next make a list of id's. This might be marginally less efficient |
||
648 | * than a more direct method, but this is not a highly performance-cri- |
||
649 | * tical code path and readable code is more important here. |
||
650 | * |
||
651 | * If the target namespace doesn't allow subpages, moving with subpages |
||
652 | * would mean that you couldn't move them back in one operation, which |
||
653 | * is bad. |
||
654 | * @todo FIXME: A specific error message should be given in this case. |
||
655 | */ |
||
656 | |||
657 | // @todo FIXME: Use Title::moveSubpages() here |
||
658 | $dbr = wfGetDB( DB_MASTER ); |
||
659 | if ( $this->moveSubpages && ( |
||
660 | MWNamespace::hasSubpages( $nt->getNamespace() ) || ( |
||
661 | $this->moveTalk |
||
662 | && MWNamespace::hasSubpages( $nt->getTalkPage()->getNamespace() ) |
||
663 | ) |
||
664 | ) ) { |
||
665 | $conds = [ |
||
666 | 'page_title' . $dbr->buildLike( $ot->getDBkey() . '/', $dbr->anyString() ) |
||
667 | . ' OR page_title = ' . $dbr->addQuotes( $ot->getDBkey() ) |
||
668 | ]; |
||
669 | $conds['page_namespace'] = []; |
||
670 | if ( MWNamespace::hasSubpages( $nt->getNamespace() ) ) { |
||
671 | $conds['page_namespace'][] = $ot->getNamespace(); |
||
672 | } |
||
673 | if ( $this->moveTalk && |
||
674 | MWNamespace::hasSubpages( $nt->getTalkPage()->getNamespace() ) |
||
675 | ) { |
||
676 | $conds['page_namespace'][] = $ot->getTalkPage()->getNamespace(); |
||
677 | } |
||
678 | } elseif ( $this->moveTalk ) { |
||
679 | $conds = [ |
||
680 | 'page_namespace' => $ot->getTalkPage()->getNamespace(), |
||
681 | 'page_title' => $ot->getDBkey() |
||
682 | ]; |
||
683 | } else { |
||
684 | # Skip the query |
||
685 | $conds = null; |
||
686 | } |
||
687 | |||
688 | $extraPages = []; |
||
689 | if ( !is_null( $conds ) ) { |
||
690 | $extraPages = TitleArray::newFromResult( |
||
691 | $dbr->select( 'page', |
||
692 | [ 'page_id', 'page_namespace', 'page_title' ], |
||
693 | $conds, |
||
694 | __METHOD__ |
||
695 | ) |
||
696 | ); |
||
697 | } |
||
698 | |||
699 | $extraOutput = []; |
||
700 | $count = 1; |
||
701 | foreach ( $extraPages as $oldSubpage ) { |
||
0 ignored issues
–
show
The expression
$extraPages of type object<TitleArrayFromResult>|null|array is not guaranteed to be traversable. How about adding an additional type check?
There are different options of fixing this problem.
![]() |
|||
702 | if ( $ot->equals( $oldSubpage ) || $nt->equals( $oldSubpage ) ) { |
||
703 | # Already did this one. |
||
704 | continue; |
||
705 | } |
||
706 | |||
707 | $newPageName = preg_replace( |
||
708 | '#^' . preg_quote( $ot->getDBkey(), '#' ) . '#', |
||
709 | StringUtils::escapeRegexReplacement( $nt->getDBkey() ), # bug 21234 |
||
710 | $oldSubpage->getDBkey() |
||
711 | ); |
||
712 | |||
713 | if ( $oldSubpage->isSubpage() && ( $ot->isTalkPage() xor $nt->isTalkPage() ) ) { |
||
714 | // Moving a subpage from a subject namespace to a talk namespace or vice-versa |
||
715 | $newNs = $nt->getNamespace(); |
||
716 | } elseif ( $oldSubpage->isTalkPage() ) { |
||
717 | $newNs = $nt->getTalkPage()->getNamespace(); |
||
718 | } else { |
||
719 | $newNs = $nt->getSubjectPage()->getNamespace(); |
||
720 | } |
||
721 | |||
722 | # Bug 14385: we need makeTitleSafe because the new page names may |
||
723 | # be longer than 255 characters. |
||
724 | $newSubpage = Title::makeTitleSafe( $newNs, $newPageName ); |
||
725 | if ( !$newSubpage ) { |
||
726 | $oldLink = $linkRenderer->makeKnownLink( $oldSubpage ); |
||
727 | $extraOutput[] = $this->msg( 'movepage-page-unmoved' )->rawParams( $oldLink ) |
||
728 | ->params( Title::makeName( $newNs, $newPageName ) )->escaped(); |
||
729 | continue; |
||
730 | } |
||
731 | |||
732 | # This was copy-pasted from Renameuser, bleh. |
||
733 | if ( $newSubpage->exists() && !$oldSubpage->isValidMoveTarget( $newSubpage ) ) { |
||
734 | $link = $linkRenderer->makeKnownLink( $newSubpage ); |
||
735 | $extraOutput[] = $this->msg( 'movepage-page-exists' )->rawParams( $link )->escaped(); |
||
736 | } else { |
||
737 | $success = $oldSubpage->moveTo( $newSubpage, true, $this->reason, $createRedirect ); |
||
738 | |||
739 | if ( $success === true ) { |
||
740 | if ( $this->fixRedirects ) { |
||
741 | DoubleRedirectJob::fixRedirects( 'move', $oldSubpage, $newSubpage ); |
||
742 | } |
||
743 | $oldLink = $linkRenderer->makeLink( |
||
744 | $oldSubpage, |
||
745 | null, |
||
746 | [], |
||
747 | [ 'redirect' => 'no' ] |
||
748 | ); |
||
749 | |||
750 | $newLink = $linkRenderer->makeKnownLink( $newSubpage ); |
||
751 | $extraOutput[] = $this->msg( 'movepage-page-moved' ) |
||
752 | ->rawParams( $oldLink, $newLink )->escaped(); |
||
753 | ++$count; |
||
754 | |||
755 | $maximumMovedPages = $this->getConfig()->get( 'MaximumMovedPages' ); |
||
756 | if ( $count >= $maximumMovedPages ) { |
||
757 | $extraOutput[] = $this->msg( 'movepage-max-pages' ) |
||
758 | ->numParams( $maximumMovedPages )->escaped(); |
||
759 | break; |
||
760 | } |
||
761 | } else { |
||
762 | $oldLink = $linkRenderer->makeKnownLink( $oldSubpage ); |
||
763 | $newLink = $linkRenderer->makeLink( $newSubpage ); |
||
764 | $extraOutput[] = $this->msg( 'movepage-page-unmoved' ) |
||
765 | ->rawParams( $oldLink, $newLink )->escaped(); |
||
766 | } |
||
767 | } |
||
768 | } |
||
769 | |||
770 | if ( $extraOutput !== [] ) { |
||
771 | $out->addHTML( "<ul>\n<li>" . implode( "</li>\n<li>", $extraOutput ) . "</li>\n</ul>" ); |
||
772 | } |
||
773 | |||
774 | # Deal with watches (we don't watch subpages) |
||
775 | WatchAction::doWatchOrUnwatch( $this->watch, $ot, $user ); |
||
776 | WatchAction::doWatchOrUnwatch( $this->watch, $nt, $user ); |
||
777 | } |
||
778 | |||
779 | function showLogFragment( $title ) { |
||
780 | $moveLogPage = new LogPage( 'move' ); |
||
781 | $out = $this->getOutput(); |
||
782 | $out->addHTML( Xml::element( 'h2', null, $moveLogPage->getName()->text() ) ); |
||
783 | LogEventsList::showLogExtract( $out, 'move', $title ); |
||
784 | } |
||
785 | |||
786 | /** |
||
787 | * Show subpages of the page being moved. Section is not shown if both current |
||
788 | * namespace does not support subpages and no talk subpages were found. |
||
789 | * |
||
790 | * @param Title $title Page being moved. |
||
791 | */ |
||
792 | function showSubpages( $title ) { |
||
793 | $nsHasSubpages = MWNamespace::hasSubpages( $title->getNamespace() ); |
||
794 | $subpages = $title->getSubpages(); |
||
795 | $count = $subpages instanceof TitleArray ? $subpages->count() : 0; |
||
796 | |||
797 | $titleIsTalk = $title->isTalkPage(); |
||
798 | $subpagesTalk = $title->getTalkPage()->getSubpages(); |
||
799 | $countTalk = $subpagesTalk instanceof TitleArray ? $subpagesTalk->count() : 0; |
||
800 | $totalCount = $count + $countTalk; |
||
801 | |||
802 | if ( !$nsHasSubpages && $countTalk == 0 ) { |
||
803 | return; |
||
804 | } |
||
805 | |||
806 | $this->getOutput()->wrapWikiMsg( |
||
807 | '== $1 ==', |
||
808 | [ 'movesubpage', ( $titleIsTalk ? $count : $totalCount ) ] |
||
809 | ); |
||
810 | |||
811 | if ( $nsHasSubpages ) { |
||
812 | $this->showSubpagesList( $subpages, $count, 'movesubpagetext', true ); |
||
813 | } |
||
814 | |||
815 | if ( !$titleIsTalk && $countTalk > 0 ) { |
||
816 | $this->showSubpagesList( $subpagesTalk, $countTalk, 'movesubpagetalktext' ); |
||
817 | } |
||
818 | } |
||
819 | |||
820 | function showSubpagesList( $subpages, $pagecount, $wikiMsg, $noSubpageMsg = false ) { |
||
821 | $out = $this->getOutput(); |
||
822 | |||
823 | # No subpages. |
||
824 | if ( $pagecount == 0 && $noSubpageMsg ) { |
||
825 | $out->addWikiMsg( 'movenosubpage' ); |
||
826 | return; |
||
827 | } |
||
828 | |||
829 | $out->addWikiMsg( $wikiMsg, $this->getLanguage()->formatNum( $pagecount ) ); |
||
830 | $out->addHTML( "<ul>\n" ); |
||
831 | |||
832 | $linkBatch = new LinkBatch( $subpages ); |
||
833 | $linkBatch->setCaller( __METHOD__ ); |
||
834 | $linkBatch->execute(); |
||
835 | $linkRenderer = $this->getLinkRenderer(); |
||
836 | |||
837 | foreach ( $subpages as $subpage ) { |
||
838 | $link = $linkRenderer->makeLink( $subpage ); |
||
839 | $out->addHTML( "<li>$link</li>\n" ); |
||
840 | } |
||
841 | $out->addHTML( "</ul>\n" ); |
||
842 | } |
||
843 | |||
844 | /** |
||
845 | * Return an array of subpages beginning with $search that this special page will accept. |
||
846 | * |
||
847 | * @param string $search Prefix to search for |
||
848 | * @param int $limit Maximum number of results to return (usually 10) |
||
849 | * @param int $offset Number of results to skip (usually 0) |
||
850 | * @return string[] Matching subpages |
||
851 | */ |
||
852 | public function prefixSearchSubpages( $search, $limit, $offset ) { |
||
853 | return $this->prefixSearchString( $search, $limit, $offset ); |
||
854 | } |
||
855 | |||
856 | protected function getGroupName() { |
||
857 | return 'pagetools'; |
||
858 | } |
||
859 | } |
||
860 |
Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.
Let’s take a look at an example:
As you can see in this example, the array
$myArray
is initialized the first time when the foreach loop is entered. You can also see that the value of thebar
key is only written conditionally; thus, its value might result from a previous iteration.This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.