53 $this->oldTitle->getUserPermissionsErrors(
'move',
$user ),
54 $this->oldTitle->getUserPermissionsErrors(
'edit',
$user ),
55 $this->newTitle->getUserPermissionsErrors(
'move-target',
$user ),
56 $this->newTitle->getUserPermissionsErrors(
'edit',
$user )
61 foreach ( $errors
as $error ) {
68 $status->fatal(
'spamprotectiontext' );
71 $tp = $this->newTitle->getTitleProtection();
72 if ( $tp !==
false && !
$user->isAllowed( $tp[
'permission'] ) ) {
73 $status->fatal(
'cantmove-titleprotected' );
77 [ $this->oldTitle, $this->newTitle,
$user, $reason,
$status ]
94 if ( $this->oldTitle->equals( $this->newTitle ) ) {
97 if ( !$this->oldTitle->isMovable() ) {
98 $status->fatal(
'immobile-source-namespace', $this->oldTitle->getNsText() );
100 if ( $this->newTitle->isExternal() ) {
101 $status->fatal(
'immobile-target-namespace-iw' );
103 if ( !$this->newTitle->isMovable() ) {
104 $status->fatal(
'immobile-target-namespace', $this->newTitle->getNsText() );
107 $oldid = $this->oldTitle->getArticleID();
109 if ( $this->newTitle->getDBkey() ===
'' ) {
110 $status->fatal(
'articleexists' );
113 ( $this->oldTitle->getDBkey() ==
'' ) ||
115 ( $this->newTitle->getDBkey() ==
'' )
117 $status->fatal(
'badarticleerror' );
120 # The move is allowed only if (1) the target doesn't exist, or
121 # (2) the target is a redirect to the source, and has no history
122 # (so we can undo bad moves right after they're done).
124 $status->fatal(
'articleexists' );
129 $this->oldTitle->getContentModel() !== $this->newTitle->getContentModel() ) {
140 'content-not-allowed-here',
142 $this->newTitle->getPrefixedText(),
148 if ( $this->oldTitle->inNamespace(
NS_FILE ) ) {
152 if ( $this->newTitle->inNamespace(
NS_FILE ) && !$this->oldTitle->inNamespace(
NS_FILE ) ) {
153 $status->fatal(
'nonfile-cannot-move-to-file' );
157 Hooks::run(
'MovePageIsValidMove', [ $this->oldTitle, $this->newTitle,
$status ] );
170 $file->load( File::READ_LATEST );
171 if (
$file->exists() ) {
173 $status->fatal(
'imageinvalidfilename' );
176 $status->fatal(
'imagetypemismatch' );
180 if ( !$this->newTitle->inNamespace(
NS_FILE ) ) {
181 $status->fatal(
'imagenocrossnamespace' );
195 # Is it an existing file?
196 if ( $this->newTitle->inNamespace(
NS_FILE ) ) {
198 $file->load( File::READ_LATEST );
199 if (
$file->exists() ) {
200 wfDebug( __METHOD__ .
": file exists\n" );
204 # Is it a redirect with no history?
205 if ( !$this->newTitle->isSingleRevRedirect() ) {
206 wfDebug( __METHOD__ .
": not a one-rev redirect\n" );
209 # Get the article text
211 if ( !is_object(
$rev ) ) {
215 # Does the redirect point to the source?
216 # Or is it a broken self-redirect, usually caused by namespace collisions?
220 if ( $redirTitle->getPrefixedDBkey() !== $this->oldTitle->getPrefixedDBkey() &&
221 $redirTitle->getPrefixedDBkey() !== $this->newTitle->getPrefixedDBkey() ) {
222 wfDebug( __METHOD__ .
": redirect points to other page\n" );
228 # Fail safe (not a redirect after all. strange.)
229 wfDebug( __METHOD__ .
": failsafe: database says " . $this->newTitle->getPrefixedDBkey() .
230 " is a redirect, but it doesn't contain a valid redirect.\n" );
254 $dbw->startAtomic( __METHOD__, IDatabase::ATOMIC_CANCELABLE );
256 Hooks::run(
'TitleMoveStarting', [ $this->oldTitle, $this->newTitle,
$user ] );
259 $protected = $this->oldTitle->isProtected();
262 $nullRevision = $this->
moveToInternal( $user, $this->newTitle, $reason, $createRedirect,
269 $prefixes = $dbw->select(
271 [
'cl_sortkey_prefix',
'cl_to' ],
272 [
'cl_from' => $pageid ],
276 foreach ( $prefixes
as $prefixRow ) {
277 $prefix = $prefixRow->cl_sortkey_prefix;
278 $catTo = $prefixRow->cl_to;
279 $dbw->update(
'categorylinks',
282 $this->newTitle->getCategorySortkey( $prefix ) ),
285 'cl_timestamp=cl_timestamp' ],
287 'cl_from' => $pageid,
293 $redirid = $this->oldTitle->getArticleID();
296 # Protect the redirect title as the title used to be...
299 [
'pr_type',
'pr_level',
'pr_cascade',
'pr_user',
'pr_expiry' ],
300 [
'pr_page' => $pageid ],
305 foreach (
$res as $row ) {
307 'pr_page' => $redirid,
308 'pr_type' => $row->pr_type,
309 'pr_level' => $row->pr_level,
310 'pr_cascade' => $row->pr_cascade,
311 'pr_user' => $row->pr_user,
312 'pr_expiry' => $row->pr_expiry
315 $dbw->insert(
'page_restrictions', $rowsInsert, __METHOD__, [
'IGNORE' ] );
320 $this->oldTitle->getPrefixedText(),
321 $this->newTitle->getPrefixedText()
322 )->inContentLanguage()->text();
324 $comment .=
wfMessage(
'colon-separator' )->inContentLanguage()->text() . $reason;
328 $insertedPrIds = $dbw->select(
331 [
'pr_page' => $redirid ],
334 $logRelationsValues = [];
335 foreach ( $insertedPrIds
as $prid ) {
336 $logRelationsValues[] = $prid->pr_id;
341 $logEntry->setTarget( $this->newTitle );
342 $logEntry->setComment( $comment );
343 $logEntry->setPerformer(
$user );
344 $logEntry->setParameters( [
345 '4::oldtitle' => $this->oldTitle->getPrefixedText(),
347 $logEntry->setRelations( [
'pr_id' => $logRelationsValues ] );
348 $logEntry->setTags( $changeTags );
349 $logId = $logEntry->insert();
350 $logEntry->publish( $logId );
354 if ( $this->oldTitle->getNamespace() != $this->newTitle->getNamespace() ) {
355 $dbw->update(
'pagelinks',
356 [
'pl_from_namespace' => $this->newTitle->getNamespace() ],
357 [
'pl_from' => $pageid ],
360 $dbw->update(
'templatelinks',
361 [
'tl_from_namespace' => $this->newTitle->getNamespace() ],
362 [
'tl_from' => $pageid ],
365 $dbw->update(
'imagelinks',
366 [
'il_from_namespace' => $this->newTitle->getNamespace() ],
367 [
'il_from' => $pageid ],
373 $oldtitle = $this->oldTitle->getDBkey();
374 $newtitle = $this->newTitle->getDBkey();
377 if ( $oldsnamespace != $newsnamespace || $oldtitle != $newtitle ) {
378 $store = MediaWikiServices::getInstance()->getWatchedItemStore();
379 $store->duplicateAllAssociatedEntries( $this->oldTitle, $this->newTitle );
384 if ( $this->oldTitle->getNamespace() ==
NS_FILE ) {
387 $dbw->cancelAtomic( __METHOD__ );
393 'TitleMoveCompleting',
394 [ $this->oldTitle, $this->newTitle,
395 $user, $pageid, $redirid, $reason, $nullRevision ]
398 $dbw->endAtomic( __METHOD__ );
441 $file->load( File::READ_LATEST );
442 if (
$file->exists() ) {
468 array $changeTags = []
470 if ( $nt->exists() ) {
471 $moveOverRedirect =
true;
472 $logType =
'move_redir';
474 $moveOverRedirect =
false;
478 if ( $moveOverRedirect ) {
480 'delete_and_move_reason',
481 $this->oldTitle->getPrefixedText()
482 )->inContentLanguage()->text();
485 $status = $newpage->doDeleteArticleReal(
497 throw new MWException(
'Failed to delete page-move revision: '
498 .
$status->getWikiText(
false,
false,
'en' ) );
501 $nt->resetArticleID(
false );
504 if ( $createRedirect ) {
505 if ( $this->oldTitle->getNamespace() ==
NS_CATEGORY
506 && !
wfMessage(
'category-move-redirect-override' )->inContentLanguage()->isDisabled()
509 wfMessage(
'category-move-redirect-override' )
510 ->params( $nt->getPrefixedText() )->inContentLanguage()->plain() );
513 $redirectContent = $contentHandler->makeRedirectContent( $nt,
514 wfMessage(
'move-redirect-text' )->inContentLanguage()->
plain() );
519 $redirectContent =
null;
524 $contentModel = $this->oldTitle->getContentModel();
526 $defaultContentModelChanging = ( $oldDefault !== $newDefault
527 && $oldDefault === $contentModel );
530 $oldid = $this->oldTitle->getArticleID();
534 $logEntry->setPerformer(
$user );
535 $logEntry->setTarget( $logTitle );
536 $logEntry->setComment( $reason );
537 $logEntry->setParameters( [
538 '4::target' => $nt->getPrefixedText(),
539 '5::noredir' => $redirectContent ?
'0' :
'1',
544 $comment = $formatter->getPlainActionText();
546 $comment .=
wfMessage(
'colon-separator' )->inContentLanguage()->text() . $reason;
552 $oldcountable = $oldpage->isCountable();
556 # Change the name of the target page:
557 $dbw->update(
'page',
559 'page_namespace' => $nt->getNamespace(),
560 'page_title' => $nt->getDBkey(),
562 [
'page_id' => $oldid ],
566 # Save a null revision in the page's history notifying of the move
568 if ( !is_object( $nullRevision ) ) {
569 throw new MWException(
'Failed to create null revision while moving page ID '
570 . $oldid .
' to ' . $nt->getPrefixedDBkey() );
573 $nullRevId = $nullRevision->insertOn( $dbw );
574 $logEntry->setAssociatedRevId( $nullRevId );
581 $user->incEditCount();
583 if ( !$redirectContent ) {
588 $this->oldTitle->resetArticleID( 0 );
589 $nt->resetArticleID( $oldid );
592 $newpage->updateRevisionOn( $dbw, $nullRevision );
595 [ $newpage, $nullRevision, $nullRevision->getParentId(),
$user ] );
597 $newpage->doEditUpdates( $nullRevision,
$user,
598 [
'changed' =>
false,
'moved' =>
true,
'oldcountable' => $oldcountable ] );
601 if ( $defaultContentModelChanging ) {
604 [
'rev_content_model' => $contentModel ],
605 [
'rev_page' => $nt->getArticleID(),
'rev_content_model IS NULL' ],
612 # Recreate the redirect, this time in the other direction.
613 if ( $redirectContent ) {
616 $newid = $redirectArticle->insertOn( $dbw );
618 $this->oldTitle->resetArticleID( $newid );
620 'title' => $this->oldTitle,
622 'user_text' =>
$user->getName(),
623 'user' =>
$user->getId(),
624 'comment' => $comment,
625 'content' => $redirectContent ] );
626 $redirectRevId = $redirectRevision->
insertOn( $dbw );
627 $redirectArticle->updateRevisionOn( $dbw, $redirectRevision, 0 );
630 [ $redirectArticle, $redirectRevision,
false,
$user ] );
632 $redirectArticle->doEditUpdates( $redirectRevision,
$user, [
'created' =>
true ] );
635 $redirectTags = $changeTags;
637 $redirectTags[] =
'mw-new-redirect';
644 $logid = $logEntry->insert();
646 $logEntry->setTags( $changeTags );
647 $logEntry->publish( $logid );
649 return $nullRevision;