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