MediaWiki
REL1_24
|
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 }