00001 <?php 00002 00008 class DoubleRedirectJob extends Job { 00009 var $reason, $redirTitle, $destTitleText; 00010 static $user; 00011 00018 public static function fixRedirects( $reason, $redirTitle, $destTitle = false ) { 00019 # Need to use the master to get the redirect table updated in the same transaction 00020 $dbw = wfGetDB( DB_MASTER ); 00021 $res = $dbw->select( 00022 array( 'redirect', 'page' ), 00023 array( 'page_namespace', 'page_title' ), 00024 array( 00025 'page_id = rd_from', 00026 'rd_namespace' => $redirTitle->getNamespace(), 00027 'rd_title' => $redirTitle->getDBkey() 00028 ), __METHOD__ ); 00029 if ( !$res->numRows() ) { 00030 return; 00031 } 00032 $jobs = array(); 00033 foreach ( $res as $row ) { 00034 $title = Title::makeTitle( $row->page_namespace, $row->page_title ); 00035 if ( !$title ) { 00036 continue; 00037 } 00038 00039 $jobs[] = new self( $title, array( 00040 'reason' => $reason, 00041 'redirTitle' => $redirTitle->getPrefixedDBkey() ) ); 00042 # Avoid excessive memory usage 00043 if ( count( $jobs ) > 10000 ) { 00044 Job::batchInsert( $jobs ); 00045 $jobs = array(); 00046 } 00047 } 00048 Job::batchInsert( $jobs ); 00049 } 00050 function __construct( $title, $params = false, $id = 0 ) { 00051 parent::__construct( 'fixDoubleRedirect', $title, $params, $id ); 00052 $this->reason = $params['reason']; 00053 $this->redirTitle = Title::newFromText( $params['redirTitle'] ); 00054 $this->destTitleText = !empty( $params['destTitle'] ) ? $params['destTitle'] : ''; 00055 } 00056 00057 function run() { 00058 if ( !$this->redirTitle ) { 00059 $this->setLastError( 'Invalid title' ); 00060 return false; 00061 } 00062 00063 $targetRev = Revision::newFromTitle( $this->title ); 00064 if ( !$targetRev ) { 00065 wfDebug( __METHOD__.": target redirect already deleted, ignoring\n" ); 00066 return true; 00067 } 00068 $text = $targetRev->getText(); 00069 $currentDest = Title::newFromRedirect( $text ); 00070 if ( !$currentDest || !$currentDest->equals( $this->redirTitle ) ) { 00071 wfDebug( __METHOD__.": Redirect has changed since the job was queued\n" ); 00072 return true; 00073 } 00074 00075 # Check for a suppression tag (used e.g. in periodically archived discussions) 00076 $mw = MagicWord::get( 'staticredirect' ); 00077 if ( $mw->match( $text ) ) { 00078 wfDebug( __METHOD__.": skipping: suppressed with __STATICREDIRECT__\n" ); 00079 return true; 00080 } 00081 00082 # Find the current final destination 00083 $newTitle = self::getFinalDestination( $this->redirTitle ); 00084 if ( !$newTitle ) { 00085 wfDebug( __METHOD__.": skipping: single redirect, circular redirect or invalid redirect destination\n" ); 00086 return true; 00087 } 00088 if ( $newTitle->equals( $this->redirTitle ) ) { 00089 # The redirect is already right, no need to change it 00090 # This can happen if the page was moved back (say after vandalism) 00091 wfDebug( __METHOD__.": skipping, already good\n" ); 00092 } 00093 00094 # Preserve fragment (bug 14904) 00095 $newTitle = Title::makeTitle( $newTitle->getNamespace(), $newTitle->getDBkey(), 00096 $currentDest->getFragment() ); 00097 00098 # Fix the text 00099 # Remember that redirect pages can have categories, templates, etc., 00100 # so the regex has to be fairly general 00101 $newText = preg_replace( '/ \[ \[ [^\]]* \] \] /x', 00102 '[[' . $newTitle->getFullText() . ']]', 00103 $text, 1 ); 00104 00105 if ( $newText === $text ) { 00106 $this->setLastError( 'Text unchanged???' ); 00107 return false; 00108 } 00109 00110 # Save it 00111 global $wgUser; 00112 $oldUser = $wgUser; 00113 $wgUser = $this->getUser(); 00114 $article = new Article( $this->title ); 00115 $reason = wfMsgForContent( 'double-redirect-fixed-' . $this->reason, 00116 $this->redirTitle->getPrefixedText(), $newTitle->getPrefixedText() ); 00117 $article->doEdit( $newText, $reason, EDIT_UPDATE | EDIT_SUPPRESS_RC ); 00118 $wgUser = $oldUser; 00119 00120 return true; 00121 } 00122 00127 public static function getFinalDestination( $title ) { 00128 $dbw = wfGetDB( DB_MASTER ); 00129 00130 $seenTitles = array(); # Circular redirect check 00131 $dest = false; 00132 00133 while ( true ) { 00134 $titleText = $title->getPrefixedDBkey(); 00135 if ( isset( $seenTitles[$titleText] ) ) { 00136 wfDebug( __METHOD__, "Circular redirect detected, aborting\n" ); 00137 return false; 00138 } 00139 $seenTitles[$titleText] = true; 00140 00141 $row = $dbw->selectRow( 00142 array( 'redirect', 'page' ), 00143 array( 'rd_namespace', 'rd_title' ), 00144 array( 00145 'rd_from=page_id', 00146 'page_namespace' => $title->getNamespace(), 00147 'page_title' => $title->getDBkey() 00148 ), __METHOD__ ); 00149 if ( !$row ) { 00150 # No redirect from here, chain terminates 00151 break; 00152 } else { 00153 $dest = $title = Title::makeTitle( $row->rd_namespace, $row->rd_title ); 00154 } 00155 } 00156 return $dest; 00157 } 00158 00162 function getUser() { 00163 if ( !self::$user ) { 00164 self::$user = User::newFromName( wfMsgForContent( 'double-redirect-fixer' ), false ); 00165 if ( !self::$user->isLoggedIn() ) { 00166 self::$user->addToDatabase(); 00167 } 00168 } 00169 return self::$user; 00170 } 00171 } 00172