| Index: trunk/phase3/includes/DoubleRedirectJob.php |
| — | — | @@ -0,0 +1,155 @@ |
| | 2 | +<?php |
| | 3 | + |
| | 4 | +class DoubleRedirectJob extends Job { |
| | 5 | + var $reason, $redirTitle, $destTitleText; |
| | 6 | + static $user; |
| | 7 | + |
| | 8 | + /** |
| | 9 | + * Insert jobs into the job queue to fix redirects to the given title |
| | 10 | + * @param string $type The reason for the fix, see message double-redirect-fixed-<reason> |
| | 11 | + * @param Title $redirTitle The title which has changed, redirects pointing to this title are fixed |
| | 12 | + */ |
| | 13 | + public static function fixRedirects( $reason, $redirTitle, $destTitle = false ) { |
| | 14 | + # Need to use the master to get the redirect table updated in the same transaction |
| | 15 | + $dbw = wfGetDB( DB_MASTER ); |
| | 16 | + $res = $dbw->select( |
| | 17 | + array( 'redirect', 'page' ), |
| | 18 | + array( 'page_namespace', 'page_title' ), |
| | 19 | + array( |
| | 20 | + 'page_id = rd_from', |
| | 21 | + 'rd_namespace' => $redirTitle->getNamespace(), |
| | 22 | + 'rd_title' => $redirTitle->getDBkey() |
| | 23 | + ), __METHOD__ ); |
| | 24 | + if ( !$res->numRows() ) { |
| | 25 | + return; |
| | 26 | + } |
| | 27 | + $jobs = array(); |
| | 28 | + foreach ( $res as $row ) { |
| | 29 | + $title = Title::makeTitle( $row->page_namespace, $row->page_title ); |
| | 30 | + if ( !$title ) { |
| | 31 | + continue; |
| | 32 | + } |
| | 33 | + |
| | 34 | + $jobs[] = new self( $title, array( |
| | 35 | + 'reason' => $reason, |
| | 36 | + 'redirTitle' => $redirTitle->getPrefixedDBkey() ) ); |
| | 37 | + # Avoid excessive memory usage |
| | 38 | + if ( count( $jobs ) > 10000 ) { |
| | 39 | + Job::batchInsert( $jobs ); |
| | 40 | + $jobs = array(); |
| | 41 | + } |
| | 42 | + } |
| | 43 | + Job::batchInsert( $jobs ); |
| | 44 | + } |
| | 45 | + function __construct( $title, $params = false, $id = 0 ) { |
| | 46 | + parent::__construct( 'fixDoubleRedirect', $title, $params, $id ); |
| | 47 | + $this->reason = $params['reason']; |
| | 48 | + $this->redirTitle = Title::newFromText( $params['redirTitle'] ); |
| | 49 | + $this->destTitleText = !empty( $params['destTitle'] ) ? $params['destTitle'] : ''; |
| | 50 | + } |
| | 51 | + |
| | 52 | + function run() { |
| | 53 | + if ( !$this->redirTitle ) { |
| | 54 | + $this->setLastError( 'Invalid title' ); |
| | 55 | + return false; |
| | 56 | + } |
| | 57 | + |
| | 58 | + $targetRev = Revision::newFromTitle( $this->title ); |
| | 59 | + if ( !$targetRev ) { |
| | 60 | + wfDebug( __METHOD__.": target redirect already deleted, ignoring\n" ); |
| | 61 | + return true; |
| | 62 | + } |
| | 63 | + $text = $targetRev->getText(); |
| | 64 | + $currentDest = Title::newFromRedirect( $text ); |
| | 65 | + if ( !$currentDest || !$currentDest->equals( $this->redirTitle ) ) { |
| | 66 | + wfDebug( __METHOD__.": Redirect has changed since the job was queued\n" ); |
| | 67 | + return true; |
| | 68 | + } |
| | 69 | + |
| | 70 | + # Find the current final destination |
| | 71 | + $newTitle = self::getFinalDestination( $this->redirTitle ); |
| | 72 | + if ( !$newTitle ) { |
| | 73 | + wfDebug( __METHOD__.": skipping: single redirect, circular redirect or invalid redirect destination\n" ); |
| | 74 | + return true; |
| | 75 | + } |
| | 76 | + if ( $newTitle->equals( $this->redirTitle ) ) { |
| | 77 | + # The redirect is already right, no need to change it |
| | 78 | + # This can happen if the page was moved back (say after vandalism) |
| | 79 | + wfDebug( __METHOD__.": skipping, already good\n" ); |
| | 80 | + } |
| | 81 | + |
| | 82 | + # Fix the text |
| | 83 | + # Remember that redirect pages can have categories, templates, etc., |
| | 84 | + # so the regex has to be fairly general |
| | 85 | + $newText = preg_replace( '/ \[ \[ [^\]]* \] \] /x', |
| | 86 | + '[[' . $newTitle->getPrefixedText() . ']]', |
| | 87 | + $text, 1 ); |
| | 88 | + |
| | 89 | + if ( $newText === $text ) { |
| | 90 | + $this->setLastError( 'Text unchanged???' ); |
| | 91 | + return false; |
| | 92 | + } |
| | 93 | + |
| | 94 | + # Save it |
| | 95 | + global $wgUser; |
| | 96 | + $oldUser = $wgUser; |
| | 97 | + $wgUser = $this->getUser(); |
| | 98 | + $article = new Article( $this->title ); |
| | 99 | + $reason = wfMsgForContent( 'double-redirect-fixed-' . $this->reason, |
| | 100 | + $this->redirTitle->getPrefixedText(), $newTitle->getPrefixedText() ); |
| | 101 | + $article->doEdit( $newText, $reason, EDIT_UPDATE | EDIT_SUPPRESS_RC ); |
| | 102 | + $wgUser = $oldUser; |
| | 103 | + |
| | 104 | + return true; |
| | 105 | + } |
| | 106 | + |
| | 107 | + /** |
| | 108 | + * Get the final destination of a redirect |
| | 109 | + * Returns false if the specified title is not a redirect, or if it is a circular redirect |
| | 110 | + */ |
| | 111 | + public static function getFinalDestination( $title ) { |
| | 112 | + $dbw = wfGetDB( DB_MASTER ); |
| | 113 | + |
| | 114 | + $seenTitles = array(); # Circular redirect check |
| | 115 | + $dest = false; |
| | 116 | + |
| | 117 | + while ( true ) { |
| | 118 | + $titleText = $title->getPrefixedDBkey(); |
| | 119 | + if ( isset( $seenTitles[$titleText] ) ) { |
| | 120 | + wfDebug( __METHOD__, "Circular redirect detected, aborting\n" ); |
| | 121 | + return false; |
| | 122 | + } |
| | 123 | + $seenTitles[$titleText] = true; |
| | 124 | + |
| | 125 | + $row = $dbw->selectRow( |
| | 126 | + array( 'redirect', 'page' ), |
| | 127 | + array( 'rd_namespace', 'rd_title' ), |
| | 128 | + array( |
| | 129 | + 'rd_from=page_id', |
| | 130 | + 'page_namespace' => $title->getNamespace(), |
| | 131 | + 'page_title' => $title->getDBkey() |
| | 132 | + ), __METHOD__ ); |
| | 133 | + if ( !$row ) { |
| | 134 | + # No redirect from here, chain terminates |
| | 135 | + break; |
| | 136 | + } else { |
| | 137 | + $dest = $title = Title::makeTitle( $row->rd_namespace, $row->rd_title ); |
| | 138 | + } |
| | 139 | + } |
| | 140 | + return $dest; |
| | 141 | + } |
| | 142 | + |
| | 143 | + /** |
| | 144 | + * Get a user object for doing edits, from a request-lifetime cache |
| | 145 | + */ |
| | 146 | + function getUser() { |
| | 147 | + if ( !self::$user ) { |
| | 148 | + self::$user = User::newFromName( wfMsgForContent( 'double-redirect-fixer' ), false ); |
| | 149 | + if ( !self::$user->isLoggedIn() ) { |
| | 150 | + self::$user->addToDatabase(); |
| | 151 | + } |
| | 152 | + } |
| | 153 | + return self::$user; |
| | 154 | + } |
| | 155 | +} |
| | 156 | + |
| Property changes on: trunk/phase3/includes/DoubleRedirectJob.php |
| ___________________________________________________________________ |
| Name: svn:eol-style |
| 1 | 157 | + native |