43 parent::__construct(
'Movepage' );
46 public function execute( $par ) {
53 $target = !is_null( $par ) ? $par : $request->getVal(
'target' );
57 $oldTitleText = $request->getVal(
'wpOldTitle', $target );
60 if ( is_null( $this->oldTitle ) ) {
63 if ( !$this->oldTitle->exists() ) {
67 $newTitleTextMain = $request->getText(
'wpNewTitleMain' );
68 $newTitleTextNs = $request->getInt(
'wpNewTitleNs', $this->oldTitle->getNamespace() );
71 $newTitleText_bc = $request->getText(
'wpNewTitle' );
72 $this->newTitle = strlen( $newTitleText_bc ) > 0
79 $permErrors = $this->oldTitle->getUserPermissionsErrors(
'move',
$user );
80 if ( count( $permErrors ) ) {
82 $user->spreadAnyEditBlock();
86 $def = !$request->wasPosted();
88 $this->reason = $request->getText(
'wpReason' );
89 $this->moveTalk = $request->getBool(
'wpMovetalk', $def );
90 $this->fixRedirects = $request->getBool(
'wpFixRedirects', $def );
91 $this->leaveRedirect = $request->getBool(
'wpLeaveRedirect', $def );
92 $this->moveSubpages = $request->getBool(
'wpMovesubpages',
false );
93 $this->deleteAndMove = $request->getBool(
'wpDeleteAndMove' ) && $request->getBool(
'wpConfirm' );
94 $this->moveOverShared = $request->getBool(
'wpMoveOverSharedFile',
false );
95 $this->watch = $request->getCheck(
'wpWatch' ) &&
$user->isLoggedIn();
97 if (
'submit' == $request->getVal(
'action' ) && $request->wasPosted()
98 &&
$user->matchEditToken( $request->getVal(
'wpEditToken' ) )
116 $this->
getSkin()->setRelevantTitle( $this->oldTitle );
121 $out->setPageTitle( $this->
msg(
'move-page', $this->oldTitle->getPrefixedText() ) );
122 $out->addModules(
'mediawiki.special.movePage' );
127 # Show the current title as a default
128 # when the form is first opened.
130 } elseif ( !count( $err ) ) {
131 # If a title was supplied, probably from the move log revert
132 # link, check for validity. We can then show some diagnostic
133 # information and save a click.
135 if ( is_array( $newerr ) ) {
142 if ( count( $err ) == 1 && isset( $err[0][0] ) && $err[0][0] ==
'articleexists'
146 $movepagebtn = $this->
msg(
'delete_and_move' )->text();
147 $submitVar =
'wpDeleteAndMove';
151 <td class='mw-input'>" .
153 $this->
msg(
'delete_and_move_confirm' )->
text(),
161 if ( $this->oldTitle->getNamespace() ==
NS_USER && !$this->oldTitle->isSubpage() ) {
163 "<div class=\"error mw-moveuserpage-warning\">\n$1\n</div>",
164 'moveuserpage-warning'
168 $out->addWikiMsg( $wgFixDoubleRedirects ?
170 'movepagetext-noredirectfixer'
172 $movepagebtn = $this->
msg(
'movepagebtn' )->text();
173 $submitVar =
'wpMove';
177 if ( count( $err ) == 1 && isset( $err[0][0] ) && $err[0][0] ==
'file-exists-sharedrepo'
178 &&
$user->isAllowed(
'reupload-shared' )
181 $submitVar =
'wpMoveOverSharedFile';
185 $oldTalk = $this->oldTitle->getTalkPage();
186 $oldTitleSubpages = $this->oldTitle->hasSubpages();
187 $oldTitleTalkSubpages = $this->oldTitle->getTalkPage()->hasSubpages();
189 $canMoveSubpage = ( $oldTitleSubpages || $oldTitleTalkSubpages ) &&
190 !count( $this->oldTitle->getUserPermissionsErrors(
'move-subpages',
$user ) );
192 # We also want to be able to move assoc. subpage talk-pages even if base page
193 # has no associated talk page, so || with $oldTitleTalkSubpages.
194 $considerTalk = !$this->oldTitle->isTalkPage() &&
196 || ( $oldTitleTalkSubpages && $canMoveSubpage ) );
199 if ( $wgFixDoubleRedirects ) {
200 $hasRedirects =
$dbr->selectField(
'redirect',
'1',
202 'rd_namespace' => $this->oldTitle->getNamespace(),
203 'rd_title' => $this->oldTitle->getDBkey(),
206 $hasRedirects =
false;
209 if ( $considerTalk ) {
210 $out->addWikiMsg(
'movepagetalktext' );
213 if ( count( $err ) ) {
214 $out->addHTML(
"<div class='error'>\n" );
215 $action_desc = $this->
msg(
'action-move' )->plain();
216 $out->addWikiMsg(
'permissionserrorstext-withaction', count( $err ), $action_desc );
218 if ( count( $err ) == 1 ) {
220 $errMsgName = array_shift( $errMsg );
222 if ( $errMsgName ==
'hookaborted' ) {
223 $out->addHTML(
"<p>{$errMsg[0]}</p>\n" );
225 $out->addWikiMsgArray( $errMsgName, $errMsg );
230 foreach ( $err
as $errMsg ) {
231 if ( $errMsg[0] ==
'hookaborted' ) {
232 $errStr[] = $errMsg[1];
234 $errMsgName = array_shift( $errMsg );
235 $errStr[] = $this->
msg( $errMsgName, $errMsg )->parse();
239 $out->addHTML(
'<ul><li>' . implode(
"</li>\n<li>", $errStr ) .
"</li></ul>\n" );
241 $out->addHTML(
"</div>\n" );
244 if ( $this->oldTitle->isProtected(
'move' ) ) {
245 # Is the title semi-protected?
246 if ( $this->oldTitle->isSemiProtected(
'move' ) ) {
247 $noticeMsg =
'semiprotectedpagemovewarning';
248 $classes[] =
'mw-textarea-sprotected';
250 # Then it must be protected based on static groups (regular)
251 $noticeMsg =
'protectedpagemovewarning';
252 $classes[] =
'mw-textarea-protected';
254 $out->addHTML(
"<div class='mw-warning-with-logexcerpt'>\n" );
255 $out->addWikiMsg( $noticeMsg );
263 $out->addHTML(
"</div>\n" );
269 $immovableNamespaces =
array();
271 foreach ( array_keys( $this->
getLanguage()->getNamespaces() )
as $nsId ) {
273 $immovableNamespaces[] = $nsId;
284 'action' => $this->
getPageTitle()->getLocalURL(
'action=submit' ),
292 <td class='mw-label'>" .
293 $this->
msg(
'movearticle' )->escaped() .
295 <td class='mw-input'>
296 <strong>{$oldTitleLink}</strong>
300 <td class='mw-label'>" .
303 <td class='mw-input'>" .
306 'selected' => $newTitle->getNamespace(),
307 'exclude' => $immovableNamespaces
309 array(
'name' =>
'wpNewTitleNs',
'id' =>
'wpNewTitleNs' )
317 'id' =>
'wpNewTitleMain',
321 Html::hidden(
'wpOldTitle', $this->oldTitle->getPrefixedText() ) .
325 <td class='mw-label'>" .
326 Xml::label( $this->
msg(
'movereason' )->text(),
'wpReason' ) .
328 <td class='mw-input'>" .
338 if ( $considerTalk ) {
342 <td class='mw-input'>" .
344 $this->
msg(
'movetalk' )->
text(),
354 if (
$user->isAllowed(
'suppressredirect' ) ) {
355 if ( $handler->supportsRedirects() ) {
361 'disabled' =>
'disabled'
367 <td class='mw-input' >" .
369 $this->
msg(
'move-leave-redirect' )->
text(),
380 if ( $hasRedirects ) {
384 <td class='mw-input' >" .
386 $this->
msg(
'fix-double-redirects' )->
text(),
396 if ( $canMoveSubpage ) {
400 <td class=\"mw-input\">" .
403 # Don
't check the box if we only have talk subpages to
404 # move and we aren't moving the talk
page.
405 $this->moveSubpages && ( $this->oldTitle->hasSubpages() ||
$this->moveTalk ),
406 array(
'id' =>
'wpMovesubpages' )
410 ( $this->oldTitle->hasSubpages()
412 :
'move-talk-subpages' )
413 )->numParams( $wgMaximumMovedPages )->params( $wgMaximumMovedPages )->parse()
420 $watchChecked =
$user->isLoggedIn() && ( $this->watch ||
$user->getBoolOption(
'watchmoves' )
421 ||
$user->isWatched( $this->oldTitle ) );
422 # Don't allow watching if user is not logged in
423 if (
$user->isLoggedIn() ) {
427 <td class='mw-input'>" .
429 $this->
msg(
'move-watch' )->
text(),
442 <td class='mw-submit'>" .
458 global $wgMaximumMovedPages, $wgFixDoubleRedirects;
462 if (
$user->pingLimiter(
'move' ) ) {
469 # don't allow moving to pages with # in
470 if ( !$nt || $nt->hasFragment() ) {
476 # Show a warning if the target file exists on a shared repo
477 if ( $nt->getNamespace() ==
NS_FILE
478 && !( $this->moveOverShared &&
$user->isAllowed(
'reupload-shared' ) )
487 # Delete to make way if requested
488 if ( $this->deleteAndMove ) {
489 $permErrors = $nt->getUserPermissionsErrors(
'delete',
$user );
490 if ( count( $permErrors ) ) {
491 # Only show the first error
497 $reason = $this->
msg(
'delete_and_move_reason', $ot )->inContentLanguage()->text();
500 if ( $nt->getNamespace() ==
NS_FILE ) {
502 if (
$file->exists() ) {
510 if ( !$deleteStatus->isGood() ) {
511 $this->
showForm( $deleteStatus->getErrorsArray() );
519 if ( !$handler->supportsRedirects() ) {
520 $createRedirect =
false;
521 } elseif (
$user->isAllowed(
'suppressredirect' ) ) {
524 $createRedirect =
true;
527 # Do the actual move.
528 $error = $ot->moveTo( $nt,
true, $this->reason, $createRedirect );
535 if ( $wgFixDoubleRedirects && $this->fixRedirects ) {
540 $out->setPageTitle( $this->
msg(
'pagemovedsub' ) );
546 array(
'redirect' =>
'no' )
549 $oldText = $ot->getPrefixedText();
550 $newText = $nt->getPrefixedText();
552 if ( $ot->exists() ) {
558 $msgName =
'movepage-moved-redirect';
560 $msgName =
'movepage-moved-noredirect';
563 $out->addHTML( $this->
msg(
'movepage-moved' )->rawParams( $oldLink,
564 $newLink )->params( $oldText, $newText )->parseAsBlock() );
565 $out->addWikiMsg( $msgName );
569 # Now we move extra pages we've been asked to move: subpages and talk
570 # pages. First, if the old page or the new page is a talk page, we
571 # can't move any talk pages: cancel that.
572 if ( $ot->isTalkPage() || $nt->isTalkPage() ) {
573 $this->moveTalk =
false;
576 if ( count( $ot->getUserPermissionsErrors(
'move-subpages',
$user ) ) ) {
577 $this->moveSubpages =
false;
580 # Next make a list of id's. This might be marginally less efficient
581 # than a more direct method, but this is not a highly performance-cri-
582 # tical code path and readable code is more important here.
584 # Note: this query works nicely on MySQL 5, but the optimizer in MySQL
585 # 4 might get confused. If so, consider rewriting as a UNION.
587 # If the target namespace doesn't allow subpages, moving with subpages
588 # would mean that you couldn't move them back in one operation, which
590 # @todo FIXME: A specific error message should be given in this case.
594 if ( $this->moveSubpages && (
601 'page_title' .
$dbr->buildLike( $ot->getDBkey() .
'/',
$dbr->anyString() )
602 .
' OR page_title = ' .
$dbr->addQuotes( $ot->getDBkey() )
604 $conds[
'page_namespace'] =
array();
606 $conds[
'page_namespace'][] = $ot->getNamespace();
608 if ( $this->moveTalk &&
611 $conds[
'page_namespace'][] = $ot->getTalkPage()->getNamespace();
613 } elseif ( $this->moveTalk ) {
615 'page_namespace' => $ot->getTalkPage()->getNamespace(),
616 'page_title' => $ot->getDBkey()
623 $extraPages =
array();
624 if ( !is_null( $conds ) ) {
626 $dbr->select(
'page',
627 array(
'page_id',
'page_namespace',
'page_title' ),
634 $extraOutput =
array();
636 foreach ( $extraPages
as $oldSubpage ) {
637 if ( $ot->equals( $oldSubpage ) || $nt->equals( $oldSubpage ) ) {
638 # Already did this one.
642 $newPageName = preg_replace(
643 '#^' . preg_quote( $ot->getDBkey(),
'#' ) .
'#',
645 $oldSubpage->getDBkey()
648 if ( $oldSubpage->isTalkPage() ) {
649 $newNs = $nt->getTalkPage()->getNamespace();
651 $newNs = $nt->getSubjectPage()->getNamespace();
654 # Bug 14385: we need makeTitleSafe because the new page names may
655 # be longer than 255 characters.
657 if ( !$newSubpage ) {
659 $extraOutput[] = $this->
msg(
'movepage-page-unmoved' )->rawParams( $oldLink )
664 # This was copy-pasted from Renameuser, bleh.
665 if ( $newSubpage->exists() && !$oldSubpage->isValidMoveTarget( $newSubpage ) ) {
667 $extraOutput[] = $this->
msg(
'movepage-page-exists' )->rawParams(
$link )->escaped();
669 $success = $oldSubpage->moveTo( $newSubpage,
true, $this->reason, $createRedirect );
672 if ( $this->fixRedirects ) {
679 array(
'redirect' =>
'no' )
683 $extraOutput[] = $this->
msg(
'movepage-page-moved' )->rawParams( $oldLink, $newLink )->escaped();
686 if (
$count >= $wgMaximumMovedPages ) {
687 $extraOutput[] = $this->
msg(
'movepage-max-pages' )->numParams( $wgMaximumMovedPages )->escaped();
693 $extraOutput[] = $this->
msg(
'movepage-page-unmoved' )->rawParams( $oldLink, $newLink )->escaped();
698 if ( $extraOutput !==
array() ) {
699 $out->addHTML(
"<ul>\n<li>" . implode(
"</li>\n<li>", $extraOutput ) .
"</li>\n</ul>" );
702 # Deal with watches (we don't watch subpages)
708 $moveLogPage =
new LogPage(
'move' );
710 $out->addHTML(
Xml::element(
'h2',
null, $moveLogPage->getName()->text() ) );
719 $subpages =
$title->getSubpages();
727 $out->addWikiMsg(
'movenosubpage' );
733 $out->addHTML(
"<ul>\n" );
735 foreach ( $subpages
as $subpage ) {
737 $out->addHTML(
"<li>$link</li>\n" );
739 $out->addHTML(
"</ul>\n" );