MediaWiki  REL1_24
MovePage.php
Go to the documentation of this file.
00001 <?php
00002 
00028 class MovePage {
00029 
00033     protected $oldTitle;
00034 
00038     protected $newTitle;
00039 
00040     public function __construct( Title $oldTitle, Title $newTitle ) {
00041         $this->oldTitle = $oldTitle;
00042         $this->newTitle = $newTitle;
00043     }
00044 
00051     public function move( User $user, $reason, $createRedirect ) {
00052         global $wgCategoryCollation;
00053 
00054         // If it is a file, move it first.
00055         // It is done before all other moving stuff is done because it's hard to revert.
00056         $dbw = wfGetDB( DB_MASTER );
00057         if ( $this->oldTitle->getNamespace() == NS_FILE ) {
00058             $file = wfLocalFile( $this->oldTitle );
00059             if ( $file->exists() ) {
00060                 $status = $file->move( $this->newTitle );
00061                 if ( !$status->isOk() ) {
00062                     return $status;
00063                 }
00064             }
00065             // Clear RepoGroup process cache
00066             RepoGroup::singleton()->clearCache( $this->oldTitle );
00067             RepoGroup::singleton()->clearCache( $this->newTitle ); # clear false negative cache
00068         }
00069 
00070         $dbw->begin( __METHOD__ ); # If $file was a LocalFile, its transaction would have closed our own.
00071         $pageid = $this->oldTitle->getArticleID( Title::GAID_FOR_UPDATE );
00072         $protected = $this->oldTitle->isProtected();
00073 
00074         // Do the actual move
00075         $this->moveToInternal( $user, $this->newTitle, $reason, $createRedirect );
00076 
00077         // Refresh the sortkey for this row.  Be careful to avoid resetting
00078         // cl_timestamp, which may disturb time-based lists on some sites.
00079         // @todo This block should be killed, it's duplicating code
00080         // from LinksUpdate::getCategoryInsertions() and friends.
00081         $prefixes = $dbw->select(
00082             'categorylinks',
00083             array( 'cl_sortkey_prefix', 'cl_to' ),
00084             array( 'cl_from' => $pageid ),
00085             __METHOD__
00086         );
00087         if ( $this->newTitle->getNamespace() == NS_CATEGORY ) {
00088             $type = 'subcat';
00089         } elseif ( $this->newTitle->getNamespace() == NS_FILE ) {
00090             $type = 'file';
00091         } else {
00092             $type = 'page';
00093         }
00094         foreach ( $prefixes as $prefixRow ) {
00095             $prefix = $prefixRow->cl_sortkey_prefix;
00096             $catTo = $prefixRow->cl_to;
00097             $dbw->update( 'categorylinks',
00098                 array(
00099                     'cl_sortkey' => Collation::singleton()->getSortKey(
00100                             $this->newTitle->getCategorySortkey( $prefix ) ),
00101                     'cl_collation' => $wgCategoryCollation,
00102                     'cl_type' => $type,
00103                     'cl_timestamp=cl_timestamp' ),
00104                 array(
00105                     'cl_from' => $pageid,
00106                     'cl_to' => $catTo ),
00107                 __METHOD__
00108             );
00109         }
00110 
00111         $redirid = $this->oldTitle->getArticleID();
00112 
00113         if ( $protected ) {
00114             # Protect the redirect title as the title used to be...
00115             $dbw->insertSelect( 'page_restrictions', 'page_restrictions',
00116                 array(
00117                     'pr_page' => $redirid,
00118                     'pr_type' => 'pr_type',
00119                     'pr_level' => 'pr_level',
00120                     'pr_cascade' => 'pr_cascade',
00121                     'pr_user' => 'pr_user',
00122                     'pr_expiry' => 'pr_expiry'
00123                 ),
00124                 array( 'pr_page' => $pageid ),
00125                 __METHOD__,
00126                 array( 'IGNORE' )
00127             );
00128             # Update the protection log
00129             $log = new LogPage( 'protect' );
00130             $comment = wfMessage(
00131                 'prot_1movedto2',
00132                 $this->oldTitle->getPrefixedText(),
00133                 $this->newTitle->getPrefixedText()
00134             )->inContentLanguage()->text();
00135             if ( $reason ) {
00136                 $comment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $reason;
00137             }
00138             // @todo FIXME: $params?
00139             $logId = $log->addEntry(
00140                 'move_prot',
00141                 $this->newTitle,
00142                 $comment,
00143                 array( $this->oldTitle->getPrefixedText() ),
00144                 $user
00145             );
00146 
00147             // reread inserted pr_ids for log relation
00148             $insertedPrIds = $dbw->select(
00149                 'page_restrictions',
00150                 'pr_id',
00151                 array( 'pr_page' => $redirid ),
00152                 __METHOD__
00153             );
00154             $logRelationsValues = array();
00155             foreach ( $insertedPrIds as $prid ) {
00156                 $logRelationsValues[] = $prid->pr_id;
00157             }
00158             $log->addRelations( 'pr_id', $logRelationsValues, $logId );
00159         }
00160 
00161         // Update *_from_namespace fields as needed
00162         if ( $this->oldTitle->getNamespace() != $this->newTitle->getNamespace() ) {
00163             $dbw->update( 'pagelinks',
00164                 array( 'pl_from_namespace' => $this->newTitle->getNamespace() ),
00165                 array( 'pl_from' => $pageid ),
00166                 __METHOD__
00167             );
00168             $dbw->update( 'templatelinks',
00169                 array( 'tl_from_namespace' => $this->newTitle->getNamespace() ),
00170                 array( 'tl_from' => $pageid ),
00171                 __METHOD__
00172             );
00173             $dbw->update( 'imagelinks',
00174                 array( 'il_from_namespace' => $this->newTitle->getNamespace() ),
00175                 array( 'il_from' => $pageid ),
00176                 __METHOD__
00177             );
00178         }
00179 
00180         # Update watchlists
00181         $oldtitle = $this->oldTitle->getDBkey();
00182         $newtitle = $this->newTitle->getDBkey();
00183         $oldsnamespace = MWNamespace::getSubject( $this->oldTitle->getNamespace() );
00184         $newsnamespace = MWNamespace::getSubject( $this->newTitle->getNamespace() );
00185         if ( $oldsnamespace != $newsnamespace || $oldtitle != $newtitle ) {
00186             WatchedItem::duplicateEntries( $this->oldTitle, $this->newTitle );
00187         }
00188 
00189         $dbw->commit( __METHOD__ );
00190 
00191         wfRunHooks( 'TitleMoveComplete', array( &$this->oldTitle, &$this->newTitle, &$user, $pageid, $redirid, $reason ) );
00192         return Status::newGood();
00193 
00194     }
00195 
00208     private function moveToInternal( User $user, &$nt, $reason = '', $createRedirect = true ) {
00209         global $wgContLang;
00210 
00211         if ( $nt->exists() ) {
00212             $moveOverRedirect = true;
00213             $logType = 'move_redir';
00214         } else {
00215             $moveOverRedirect = false;
00216             $logType = 'move';
00217         }
00218 
00219         if ( $createRedirect ) {
00220             if ( $this->oldTitle->getNamespace() == NS_CATEGORY
00221                 && !wfMessage( 'category-move-redirect-override' )->inContentLanguage()->isDisabled()
00222             ) {
00223                 $redirectContent = new WikitextContent(
00224                     wfMessage( 'category-move-redirect-override' )
00225                         ->params( $nt->getPrefixedText() )->inContentLanguage()->plain() );
00226             } else {
00227                 $contentHandler = ContentHandler::getForTitle( $this->oldTitle );
00228                 $redirectContent = $contentHandler->makeRedirectContent( $nt,
00229                     wfMessage( 'move-redirect-text' )->inContentLanguage()->plain() );
00230             }
00231 
00232             // NOTE: If this page's content model does not support redirects, $redirectContent will be null.
00233         } else {
00234             $redirectContent = null;
00235         }
00236 
00237         // bug 57084: log_page should be the ID of the *moved* page
00238         $oldid = $this->oldTitle->getArticleID();
00239         $logTitle = clone $this->oldTitle;
00240 
00241         $logEntry = new ManualLogEntry( 'move', $logType );
00242         $logEntry->setPerformer( $user );
00243         $logEntry->setTarget( $logTitle );
00244         $logEntry->setComment( $reason );
00245         $logEntry->setParameters( array(
00246             '4::target' => $nt->getPrefixedText(),
00247             '5::noredir' => $redirectContent ? '0': '1',
00248         ) );
00249 
00250         $formatter = LogFormatter::newFromEntry( $logEntry );
00251         $formatter->setContext( RequestContext::newExtraneousContext( $this->oldTitle ) );
00252         $comment = $formatter->getPlainActionText();
00253         if ( $reason ) {
00254             $comment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $reason;
00255         }
00256         # Truncate for whole multibyte characters.
00257         $comment = $wgContLang->truncate( $comment, 255 );
00258 
00259         $dbw = wfGetDB( DB_MASTER );
00260 
00261         $newpage = WikiPage::factory( $nt );
00262 
00263         if ( $moveOverRedirect ) {
00264             $newid = $nt->getArticleID();
00265             $newcontent = $newpage->getContent();
00266 
00267             # Delete the old redirect. We don't save it to history since
00268             # by definition if we've got here it's rather uninteresting.
00269             # We have to remove it so that the next step doesn't trigger
00270             # a conflict on the unique namespace+title index...
00271             $dbw->delete( 'page', array( 'page_id' => $newid ), __METHOD__ );
00272 
00273             $newpage->doDeleteUpdates( $newid, $newcontent );
00274         }
00275 
00276         # Save a null revision in the page's history notifying of the move
00277         $nullRevision = Revision::newNullRevision( $dbw, $oldid, $comment, true, $user );
00278         if ( !is_object( $nullRevision ) ) {
00279             throw new MWException( 'No valid null revision produced in ' . __METHOD__ );
00280         }
00281 
00282         $nullRevision->insertOn( $dbw );
00283 
00284         # Change the name of the target page:
00285         $dbw->update( 'page',
00286             /* SET */ array(
00287                 'page_namespace' => $nt->getNamespace(),
00288                 'page_title' => $nt->getDBkey(),
00289             ),
00290             /* WHERE */ array( 'page_id' => $oldid ),
00291             __METHOD__
00292         );
00293 
00294         // clean up the old title before reset article id - bug 45348
00295         if ( !$redirectContent ) {
00296             WikiPage::onArticleDelete( $this->oldTitle );
00297         }
00298 
00299         $this->oldTitle->resetArticleID( 0 ); // 0 == non existing
00300         $nt->resetArticleID( $oldid );
00301         $newpage->loadPageData( WikiPage::READ_LOCKING ); // bug 46397
00302 
00303         $newpage->updateRevisionOn( $dbw, $nullRevision );
00304 
00305         wfRunHooks( 'NewRevisionFromEditComplete',
00306             array( $newpage, $nullRevision, $nullRevision->getParentId(), $user ) );
00307 
00308         $newpage->doEditUpdates( $nullRevision, $user, array( 'changed' => false ) );
00309 
00310         if ( !$moveOverRedirect ) {
00311             WikiPage::onArticleCreate( $nt );
00312         }
00313 
00314         # Recreate the redirect, this time in the other direction.
00315         if ( $redirectContent ) {
00316             $redirectArticle = WikiPage::factory( $this->oldTitle );
00317             $redirectArticle->loadFromRow( false, WikiPage::READ_LOCKING ); // bug 46397
00318             $newid = $redirectArticle->insertOn( $dbw );
00319             if ( $newid ) { // sanity
00320                 $this->oldTitle->resetArticleID( $newid );
00321                 $redirectRevision = new Revision( array(
00322                     'title' => $this->oldTitle, // for determining the default content model
00323                     'page' => $newid,
00324                     'user_text' => $user->getName(),
00325                     'user' => $user->getId(),
00326                     'comment' => $comment,
00327                     'content' => $redirectContent ) );
00328                 $redirectRevision->insertOn( $dbw );
00329                 $redirectArticle->updateRevisionOn( $dbw, $redirectRevision, 0 );
00330 
00331                 wfRunHooks( 'NewRevisionFromEditComplete',
00332                     array( $redirectArticle, $redirectRevision, false, $user ) );
00333 
00334                 $redirectArticle->doEditUpdates( $redirectRevision, $user, array( 'created' => true ) );
00335             }
00336         }
00337 
00338         # Log the move
00339         $logid = $logEntry->insert();
00340         $logEntry->publish( $logid );
00341     }
00342 
00343 }