3 * Implements Special:Redirect
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
21 * @ingroup SpecialPage
25 * A special page that redirects to: the user for a numeric user id,
26 * the file for a given filename, or the page for a given revision id.
28 * @ingroup SpecialPage
31 class SpecialRedirect
extends FormSpecialPage
{
34 * The type of the redirect (user/file/revision)
42 * The identifier/value for the redirect (which id, which file)
49 function __construct() {
50 parent
::__construct( 'Redirect' );
56 * Set $mType and $mValue based on parsed value of $subpage.
57 * @param string $subpage
59 function setParameter( $subpage ) {
60 // parse $subpage to pull out the parts
61 $parts = explode( '/', $subpage, 2 );
62 $this->mType
= count( $parts ) > 0 ?
$parts[0] : null;
63 $this->mValue
= count( $parts ) > 1 ?
$parts[1] : null;
67 * Handle Special:Redirect/user/xxxx (by redirecting to User:YYYY)
69 * @return string|null Url to redirect to, or null if $mValue is invalid.
71 function dispatchUser() {
72 if ( !ctype_digit( $this->mValue
) ) {
75 $user = User
::newFromId( (int)$this->mValue
);
76 $username = $user->getName(); // load User as side-effect
77 if ( $user->isAnon() ) {
80 $userpage = Title
::makeTitle( NS_USER
, $username );
82 return $userpage->getFullURL( '', false, PROTO_CURRENT
);
86 * Handle Special:Redirect/file/xxxx
88 * @return string|null Url to redirect to, or null if $mValue is not found.
90 function dispatchFile() {
91 $title = Title
::makeTitleSafe( NS_FILE
, $this->mValue
);
93 if ( !$title instanceof Title
) {
96 $file = wfFindFile( $title );
98 if ( !$file ||
!$file->exists() ) {
101 // Default behavior: Use the direct link to the file.
102 $url = $file->getUrl();
103 $request = $this->getRequest();
104 $width = $request->getInt( 'width', -1 );
105 $height = $request->getInt( 'height', -1 );
107 // If a width is requested...
108 if ( $width != -1 ) {
109 $mto = $file->transform( [ 'width' => $width, 'height' => $height ] );
111 if ( $mto && !$mto->isError() ) {
112 // ... change the URL to point to a thumbnail.
113 $url = $mto->getUrl();
121 * Handle Special:Redirect/revision/xxx
122 * (by redirecting to index.php?oldid=xxx)
124 * @return string|null Url to redirect to, or null if $mValue is invalid.
126 function dispatchRevision() {
127 $oldid = $this->mValue
;
128 if ( !ctype_digit( $oldid ) ) {
131 $oldid = (int)$oldid;
132 if ( $oldid === 0 ) {
136 return wfAppendQuery( wfScript( 'index' ), [
142 * Handle Special:Redirect/page/xxx (by redirecting to index.php?curid=xxx)
144 * @return string|null Url to redirect to, or null if $mValue is invalid.
146 function dispatchPage() {
147 $curid = $this->mValue
;
148 if ( !ctype_digit( $curid ) ) {
151 $curid = (int)$curid;
152 if ( $curid === 0 ) {
156 return wfAppendQuery( wfScript( 'index' ), [
162 * Handle Special:Redirect/logid/xxx
163 * (by redirecting to index.php?title=Special:Log)
166 * @return string|null Url to redirect to, or null if $mValue is invalid.
168 function dispatchLog() {
169 $logid = $this->mValue
;
170 if ( !ctype_digit( $logid ) ) {
173 $logid = (int)$logid;
174 if ( $logid === 0 ) {
185 $dbr = wfGetDB( DB_REPLICA
);
187 // Gets the nested SQL statement which
188 // returns timestamp of the log with the given log ID
189 $inner = $dbr->selectSQLText(
192 [ 'log_id' => $logid ]
195 // Returns all fields mentioned in $logparams of the logs
196 // with the same timestamp as the one returned by the statement above
197 $logsSameTimestamps = $dbr->select(
200 [ "log_timestamp = ($inner)" ]
202 if ( $logsSameTimestamps->numRows() === 0 ) {
206 // Stores the row with the same log ID as the one given
208 foreach ( $logsSameTimestamps as $row ) {
209 if ( (int)$row->log_id
=== $logid ) {
214 array_shift( $logparams );
216 // Stores all the rows with the same values in each column
218 foreach ( $logparams as $cond ) {
220 foreach ( $logsSameTimestamps as $row ) {
221 if ( $row->$cond === $rowMain->$cond ) {
222 $matchedRows[] = $row;
225 if ( count( $matchedRows ) === 1 ) {
228 $logsSameTimestamps = $matchedRows;
230 $query = [ 'title' => 'Special:Log', 'limit' => count( $matchedRows ) ];
232 // A map of database field names from table 'logging' to the values of $logparams
234 'log_timestamp' => 'offset',
235 'log_type' => 'type',
236 'log_user_text' => 'user'
239 foreach ( $logparams as $logKey ) {
240 $query[$keys[$logKey]] = $matchedRows[0]->$logKey;
242 $query['offset'] = $query['offset'] +
1;
245 return wfAppendQuery( wfScript( 'index' ), $url );
249 * Use appropriate dispatch* method to obtain a redirection URL,
250 * and either: redirect, set a 404 error code and error message,
251 * or do nothing (if $mValue wasn't set) allowing the form to be
254 * @return bool True if a redirect was successfully handled.
256 function dispatch() {
257 // the various namespaces supported by Special:Redirect
258 switch ( $this->mType
) {
260 $url = $this->dispatchUser();
263 $url = $this->dispatchFile();
266 $url = $this->dispatchRevision();
269 $url = $this->dispatchPage();
272 $url = $this->dispatchLog();
279 $this->getOutput()->redirect( $url );
283 if ( !is_null( $this->mValue
) ) {
284 $this->getOutput()->setStatusCode( 404 );
285 // Message: redirect-not-exists
286 $msg = $this->getMessagePrefix() . '-not-exists';
288 return Status
::newFatal( $msg );
294 protected function getFormFields() {
295 $mp = $this->getMessagePrefix();
297 // subpage => message
298 // Messages: redirect-user, redirect-page, redirect-revision,
299 // redirect-file, redirect-logid
300 'user' => $mp . '-user',
301 'page' => $mp . '-page',
302 'revision' => $mp . '-revision',
303 'file' => $mp . '-file',
304 'logid' => $mp . '-logid',
309 'label-message' => $mp . '-lookup', // Message: redirect-lookup
311 'default' => current( array_keys( $ns ) ),
313 foreach ( $ns as $n => $m ) {
314 $m = $this->msg( $m )->text();
315 $a['type']['options'][$m] = $n;
319 'label-message' => $mp . '-value' // Message: redirect-value
321 // set the defaults according to the parsed subpage path
322 if ( !empty( $this->mType
) ) {
323 $a['type']['default'] = $this->mType
;
325 if ( !empty( $this->mValue
) ) {
326 $a['value']['default'] = $this->mValue
;
332 public function onSubmit( array $data ) {
333 if ( !empty( $data['type'] ) && !empty( $data['value'] ) ) {
334 $this->setParameter( $data['type'] . '/' . $data['value'] );
337 /* if this returns false, will show the form */
338 return $this->dispatch();
341 public function onSuccess() {
342 /* do nothing, we redirect in $this->dispatch if successful. */
345 protected function alterForm( HTMLForm
$form ) {
346 /* display summary at top of page */
347 $this->outputHeader();
348 // tweak label on submit button
349 // Message: redirect-submit
350 $form->setSubmitTextMsg( $this->getMessagePrefix() . '-submit' );
351 /* submit form every time */
352 $form->setMethod( 'get' );
355 protected function getDisplayFormat() {
360 * Return an array of subpages that this special page will accept.
362 * @return string[] subpages
364 protected function getSubpagesForPrefixSearch() {
377 public function requiresWrite() {
384 public function requiresUnblock() {
388 protected function getGroupName() {