MediaWiki REL1_35
DoubleRedirectJob.php
Go to the documentation of this file.
1<?php
27
33class DoubleRedirectJob extends Job {
34
38 private $redirTitle;
39
41 private static $user;
42
52 public function __construct( Title $title, array $params ) {
53 parent::__construct( 'fixDoubleRedirect', $title, $params );
54 $this->redirTitle = Title::newFromText( $params['redirTitle'] );
55 }
56
65 public static function fixRedirects( $reason, $redirTitle, $destTitle = false ) {
66 # Need to use the master to get the redirect table updated in the same transaction
67 $dbw = wfGetDB( DB_MASTER );
68 $res = $dbw->select(
69 [ 'redirect', 'page' ],
70 [ 'page_namespace', 'page_title' ],
71 [
72 'page_id = rd_from',
73 'rd_namespace' => $redirTitle->getNamespace(),
74 'rd_title' => $redirTitle->getDBkey()
75 ], __METHOD__ );
76 if ( !$res->numRows() ) {
77 return;
78 }
79 $jobs = [];
80 foreach ( $res as $row ) {
81 $title = Title::makeTitle( $row->page_namespace, $row->page_title );
82 if ( !$title ) {
83 continue;
84 }
85
86 $jobs[] = new self( $title, [
87 'reason' => $reason,
88 'redirTitle' => $redirTitle->getPrefixedDBkey() ] );
89 # Avoid excessive memory usage
90 if ( count( $jobs ) > 10000 ) {
91 JobQueueGroup::singleton()->push( $jobs );
92 $jobs = [];
93 }
94 }
95 JobQueueGroup::singleton()->push( $jobs );
96 }
97
101 public function run() {
102 if ( !$this->redirTitle ) {
103 $this->setLastError( 'Invalid title' );
104
105 return false;
106 }
107
108 $targetRev = MediaWikiServices::getInstance()
109 ->getRevisionLookup()
110 ->getRevisionByTitle( $this->title, 0, RevisionLookup::READ_LATEST );
111 if ( !$targetRev ) {
112 wfDebug( __METHOD__ . ": target redirect already deleted, ignoring" );
113
114 return true;
115 }
116 $content = $targetRev->getContent( SlotRecord::MAIN );
117 $currentDest = $content ? $content->getRedirectTarget() : null;
118 if ( !$currentDest || !$currentDest->equals( $this->redirTitle ) ) {
119 wfDebug( __METHOD__ . ": Redirect has changed since the job was queued" );
120
121 return true;
122 }
123
124 // Check for a suppression tag (used e.g. in periodically archived discussions)
125 $mw = MediaWikiServices::getInstance()->getMagicWordFactory()->get( 'staticredirect' );
126 if ( $content->matchMagicWord( $mw ) ) {
127 wfDebug( __METHOD__ . ": skipping: suppressed with __STATICREDIRECT__" );
128
129 return true;
130 }
131
132 // Find the current final destination
133 $newTitle = self::getFinalDestination( $this->redirTitle );
134 if ( !$newTitle ) {
135 wfDebug( __METHOD__ .
136 ": skipping: single redirect, circular redirect or invalid redirect destination" );
137
138 return true;
139 }
140 if ( $newTitle->equals( $this->redirTitle ) ) {
141 // The redirect is already right, no need to change it
142 // This can happen if the page was moved back (say after vandalism)
143 wfDebug( __METHOD__ . " : skipping, already good" );
144 }
145
146 // Preserve fragment (T16904)
147 $newTitle = Title::makeTitle( $newTitle->getNamespace(), $newTitle->getDBkey(),
148 $currentDest->getFragment(), $newTitle->getInterwiki() );
149
150 // Fix the text
151 $newContent = $content->updateRedirect( $newTitle );
152
153 if ( $newContent->equals( $content ) ) {
154 $this->setLastError( 'Content unchanged???' );
155
156 return false;
157 }
158
159 $user = $this->getUser();
160 if ( !$user ) {
161 $this->setLastError( 'Invalid user' );
162
163 return false;
164 }
165
166 // Save it
167 global $wgUser;
168 $oldUser = $wgUser;
169 $wgUser = $user;
170 $article = WikiPage::factory( $this->title );
171
172 // Messages: double-redirect-fixed-move, double-redirect-fixed-maintenance
173 $reason = wfMessage( 'double-redirect-fixed-' . $this->params['reason'],
174 $this->redirTitle->getPrefixedText(), $newTitle->getPrefixedText()
175 )->inContentLanguage()->text();
177 $article->doEditContent( $newContent, $reason, $flags, false, $user );
178 $wgUser = $oldUser;
179
180 return true;
181 }
182
191 public static function getFinalDestination( $title ) {
192 $dbw = wfGetDB( DB_MASTER );
193
194 // Circular redirect check
195 $seenTitles = [];
196 $dest = false;
197
198 while ( true ) {
199 $titleText = $title->getPrefixedDBkey();
200 if ( isset( $seenTitles[$titleText] ) ) {
201 wfDebug( __METHOD__, "Circular redirect detected, aborting" );
202
203 return false;
204 }
205 $seenTitles[$titleText] = true;
206
207 if ( $title->isExternal() ) {
208 // If the target is interwiki, we have to break early (T42352).
209 // Otherwise it will look up a row in the local page table
210 // with the namespace/page of the interwiki target which can cause
211 // unexpected results (e.g. X -> foo:Bar -> Bar -> .. )
212 break;
213 }
214
215 $row = $dbw->selectRow(
216 [ 'redirect', 'page' ],
217 [ 'rd_namespace', 'rd_title', 'rd_interwiki' ],
218 [
219 'rd_from=page_id',
220 'page_namespace' => $title->getNamespace(),
221 'page_title' => $title->getDBkey()
222 ], __METHOD__ );
223 if ( !$row ) {
224 # No redirect from here, chain terminates
225 break;
226 } else {
227 $dest = $title = Title::makeTitle(
228 $row->rd_namespace,
229 $row->rd_title,
230 '',
231 $row->rd_interwiki
232 );
233 }
234 }
235
236 return $dest;
237 }
238
246 private function getUser() {
247 if ( !self::$user ) {
248 $username = wfMessage( 'double-redirect-fixer' )->inContentLanguage()->text();
249 self::$user = User::newFromName( $username );
250 # User::newFromName() can return false on a badly configured wiki.
251 if ( self::$user && !self::$user->isLoggedIn() ) {
252 self::$user->addToDatabase();
253 }
254 }
255
256 return self::$user;
257 }
258}
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Job to fix double redirects after moving a page.
static getFinalDestination( $title)
Get the final destination of a redirect.
getUser()
Get a user object for doing edits, from a request-lifetime cache False will be returned if the user n...
static fixRedirects( $reason, $redirTitle, $destTitle=false)
Insert jobs into the job queue to fix redirects to the given title.
__construct(Title $title, array $params)
Title $redirTitle
The title which has changed, redirects pointing to this title are fixed.
Class to both describe a background job and handle jobs.
Definition Job.php:32
Title $title
Definition Job.php:43
setLastError( $error)
Definition Job.php:461
array $params
Array of job parameters.
Definition Job.php:37
MediaWikiServices is the service locator for the application scope of MediaWiki.
Value object representing a content slot associated with a page revision.
Represents a title within MediaWiki.
Definition Title.php:42
getNamespace()
Get the namespace index, i.e.
Definition Title.php:1041
getPrefixedDBkey()
Get the prefixed database key form.
Definition Title.php:1847
isExternal()
Is this Title interwiki?
Definition Title.php:931
getDBkey()
Get the main part with underscores.
Definition Title.php:1032
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition User.php:60
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition User.php:541
const EDIT_INTERNAL
Definition Defines.php:149
const EDIT_UPDATE
Definition Defines.php:143
const EDIT_SUPPRESS_RC
Definition Defines.php:145
Service for looking up page revisions.
const DB_MASTER
Definition defines.php:29
$content
Definition router.php:76