MediaWiki
master
|
00001 <?php 00029 class DoubleRedirectJob extends Job { 00030 var $reason, $redirTitle; 00031 00035 static $user; 00036 00043 public static function fixRedirects( $reason, $redirTitle, $destTitle = false ) { 00044 # Need to use the master to get the redirect table updated in the same transaction 00045 $dbw = wfGetDB( DB_MASTER ); 00046 $res = $dbw->select( 00047 array( 'redirect', 'page' ), 00048 array( 'page_namespace', 'page_title' ), 00049 array( 00050 'page_id = rd_from', 00051 'rd_namespace' => $redirTitle->getNamespace(), 00052 'rd_title' => $redirTitle->getDBkey() 00053 ), __METHOD__ ); 00054 if ( !$res->numRows() ) { 00055 return; 00056 } 00057 $jobs = array(); 00058 foreach ( $res as $row ) { 00059 $title = Title::makeTitle( $row->page_namespace, $row->page_title ); 00060 if ( !$title ) { 00061 continue; 00062 } 00063 00064 $jobs[] = new self( $title, array( 00065 'reason' => $reason, 00066 'redirTitle' => $redirTitle->getPrefixedDBkey() ) ); 00067 # Avoid excessive memory usage 00068 if ( count( $jobs ) > 10000 ) { 00069 Job::batchInsert( $jobs ); 00070 $jobs = array(); 00071 } 00072 } 00073 Job::batchInsert( $jobs ); 00074 } 00075 00076 function __construct( $title, $params = false, $id = 0 ) { 00077 parent::__construct( 'fixDoubleRedirect', $title, $params, $id ); 00078 $this->reason = $params['reason']; 00079 $this->redirTitle = Title::newFromText( $params['redirTitle'] ); 00080 } 00081 00085 function run() { 00086 if ( !$this->redirTitle ) { 00087 $this->setLastError( 'Invalid title' ); 00088 return false; 00089 } 00090 00091 $targetRev = Revision::newFromTitle( $this->title, false, Revision::READ_LATEST ); 00092 if ( !$targetRev ) { 00093 wfDebug( __METHOD__.": target redirect already deleted, ignoring\n" ); 00094 return true; 00095 } 00096 $content = $targetRev->getContent(); 00097 $currentDest = $content->getRedirectTarget(); 00098 if ( !$currentDest || !$currentDest->equals( $this->redirTitle ) ) { 00099 wfDebug( __METHOD__.": Redirect has changed since the job was queued\n" ); 00100 return true; 00101 } 00102 00103 # Check for a suppression tag (used e.g. in periodically archived discussions) 00104 $mw = MagicWord::get( 'staticredirect' ); 00105 if ( $content->matchMagicWord( $mw ) ) { 00106 wfDebug( __METHOD__.": skipping: suppressed with __STATICREDIRECT__\n" ); 00107 return true; 00108 } 00109 00110 # Find the current final destination 00111 $newTitle = self::getFinalDestination( $this->redirTitle ); 00112 if ( !$newTitle ) { 00113 wfDebug( __METHOD__.": skipping: single redirect, circular redirect or invalid redirect destination\n" ); 00114 return true; 00115 } 00116 if ( $newTitle->equals( $this->redirTitle ) ) { 00117 # The redirect is already right, no need to change it 00118 # This can happen if the page was moved back (say after vandalism) 00119 wfDebug( __METHOD__.": skipping, already good\n" ); 00120 } 00121 00122 # Preserve fragment (bug 14904) 00123 $newTitle = Title::makeTitle( $newTitle->getNamespace(), $newTitle->getDBkey(), 00124 $currentDest->getFragment(), $newTitle->getInterwiki() ); 00125 00126 # Fix the text 00127 $newContent = $content->updateRedirect( $newTitle ); 00128 00129 if ( $newContent->equals( $content ) ) { 00130 $this->setLastError( 'Content unchanged???' ); 00131 return false; 00132 } 00133 00134 # Save it 00135 global $wgUser; 00136 $oldUser = $wgUser; 00137 $wgUser = $this->getUser(); 00138 $article = WikiPage::factory( $this->title ); 00139 $reason = wfMessage( 'double-redirect-fixed-' . $this->reason, 00140 $this->redirTitle->getPrefixedText(), $newTitle->getPrefixedText() 00141 )->inContentLanguage()->text(); 00142 $article->doEditContent( $newContent, $reason, EDIT_UPDATE | EDIT_SUPPRESS_RC, false, $this->getUser() ); 00143 $wgUser = $oldUser; 00144 00145 return true; 00146 } 00147 00155 public static function getFinalDestination( $title ) { 00156 $dbw = wfGetDB( DB_MASTER ); 00157 00158 $seenTitles = array(); # Circular redirect check 00159 $dest = false; 00160 00161 while ( true ) { 00162 $titleText = $title->getPrefixedDBkey(); 00163 if ( isset( $seenTitles[$titleText] ) ) { 00164 wfDebug( __METHOD__, "Circular redirect detected, aborting\n" ); 00165 return false; 00166 } 00167 $seenTitles[$titleText] = true; 00168 00169 if ( $title->getInterwiki() ) { 00170 // If the target is interwiki, we have to break early (bug 40352). 00171 // Otherwise it will look up a row in the local page table 00172 // with the namespace/page of the interwiki target which can cause 00173 // unexpected results (e.g. X -> foo:Bar -> Bar -> .. ) 00174 break; 00175 } 00176 00177 $row = $dbw->selectRow( 00178 array( 'redirect', 'page' ), 00179 array( 'rd_namespace', 'rd_title', 'rd_interwiki' ), 00180 array( 00181 'rd_from=page_id', 00182 'page_namespace' => $title->getNamespace(), 00183 'page_title' => $title->getDBkey() 00184 ), __METHOD__ ); 00185 if ( !$row ) { 00186 # No redirect from here, chain terminates 00187 break; 00188 } else { 00189 $dest = $title = Title::makeTitle( $row->rd_namespace, $row->rd_title, '', $row->rd_interwiki ); 00190 } 00191 } 00192 return $dest; 00193 } 00194 00199 function getUser() { 00200 if ( !self::$user ) { 00201 self::$user = User::newFromName( wfMessage( 'double-redirect-fixer' )->inContentLanguage()->text(), false ); 00202 # FIXME: newFromName could return false on a badly configured wiki. 00203 if ( !self::$user->isLoggedIn() ) { 00204 self::$user->addToDatabase(); 00205 } 00206 } 00207 return self::$user; 00208 } 00209 } 00210