MediaWiki 1.41.2
SpecialRedirect.php
Go to the documentation of this file.
1<?php
24namespace MediaWiki\Specials;
25
26use HTMLForm;
33use RepoGroup;
34
43
51 protected $mType;
52
60 protected $mValue;
61
62 private RepoGroup $repoGroup;
63 private UserFactory $userFactory;
64
69 public function __construct(
70 RepoGroup $repoGroup,
71 UserFactory $userFactory
72 ) {
73 parent::__construct( 'Redirect' );
74 $this->mType = null;
75 $this->mValue = null;
76
77 $this->repoGroup = $repoGroup;
78 $this->userFactory = $userFactory;
79 }
80
85 public function setParameter( $subpage ) {
86 // parse $subpage to pull out the parts
87 $parts = $subpage !== null ? explode( '/', $subpage, 2 ) : [];
88 $this->mType = $parts[0] ?? null;
89 $this->mValue = $parts[1] ?? null;
90 }
91
97 public function dispatchUser() {
98 if ( !ctype_digit( $this->mValue ) ) {
99 return Status::newFatal( 'redirect-not-numeric' );
100 }
101 $user = $this->userFactory->newFromId( (int)$this->mValue );
102 $user->load(); // Make sure the id is validated by loading the user
103 if ( $user->isAnon() ) {
104 return Status::newFatal( 'redirect-not-exists' );
105 }
106 if ( $user->isHidden() && !$this->getAuthority()->isAllowed( 'hideuser' ) ) {
107 throw new PermissionsError( null, [ 'badaccess-group0' ] );
108 }
109
110 return Status::newGood( [
111 $user->getUserPage()->getFullURL( '', false, PROTO_CURRENT ), 302
112 ] );
113 }
114
120 public function dispatchFile() {
121 try {
122 $title = Title::newFromTextThrow( $this->mValue, NS_FILE );
123 if ( $title && !$title->inNamespace( NS_FILE ) ) {
124 // If the given value contains a namespace enforce file namespace
125 $title = Title::newFromTextThrow( Title::makeName( NS_FILE, $this->mValue ) );
126 }
127 } catch ( MalformedTitleException $e ) {
128 return Status::newFatal( $e->getMessageObject() );
129 }
130 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable False positive
131 $file = $this->repoGroup->findFile( $title );
132
133 if ( !$file || !$file->exists() ) {
134 return Status::newFatal( 'redirect-not-exists' );
135 }
136 // Default behavior: Use the direct link to the file.
137 $url = $file->getUrl();
138 $request = $this->getRequest();
139 $width = $request->getInt( 'width', -1 );
140 $height = $request->getInt( 'height', -1 );
141
142 // If a width is requested...
143 if ( $width != -1 ) {
144 $mto = $file->transform( [ 'width' => $width, 'height' => $height ] );
145 // ... and we can
146 if ( $mto && !$mto->isError() ) {
147 // ... change the URL to point to a thumbnail.
148 // Note: This url is more temporary as can change
149 // if file is reuploaded and has different aspect ratio.
150 $url = [ $mto->getUrl(), $height === -1 ? 301 : 302 ];
151 }
152 }
153
154 return Status::newGood( $url );
155 }
156
163 public function dispatchRevision() {
164 $oldid = $this->mValue;
165 if ( !ctype_digit( $oldid ) ) {
166 return Status::newFatal( 'redirect-not-numeric' );
167 }
168 $oldid = (int)$oldid;
169 if ( $oldid === 0 ) {
170 return Status::newFatal( 'redirect-not-exists' );
171 }
172
173 return Status::newGood( wfAppendQuery( wfScript( 'index' ), [
174 'oldid' => $oldid
175 ] ) );
176 }
177
183 public function dispatchPage() {
184 $curid = $this->mValue;
185 if ( !ctype_digit( $curid ) ) {
186 return Status::newFatal( 'redirect-not-numeric' );
187 }
188 $curid = (int)$curid;
189 if ( $curid === 0 ) {
190 return Status::newFatal( 'redirect-not-exists' );
191 }
192
193 return Status::newGood( wfAppendQuery( wfScript( 'index' ), [
194 'curid' => $curid
195 ] ) );
196 }
197
205 public function dispatchLog() {
206 $logid = $this->mValue;
207 if ( !ctype_digit( $logid ) ) {
208 return Status::newFatal( 'redirect-not-numeric' );
209 }
210 $logid = (int)$logid;
211 if ( $logid === 0 ) {
212 return Status::newFatal( 'redirect-not-exists' );
213 }
214 $query = [ 'title' => 'Special:Log', 'logid' => $logid ];
215 return Status::newGood( wfAppendQuery( wfScript( 'index' ), $query ) );
216 }
217
226 private function dispatch() {
227 // the various namespaces supported by Special:Redirect
228 switch ( $this->mType ) {
229 case 'user':
230 $status = $this->dispatchUser();
231 break;
232 case 'file':
233 $status = $this->dispatchFile();
234 break;
235 case 'revision':
236 $status = $this->dispatchRevision();
237 break;
238 case 'page':
239 $status = $this->dispatchPage();
240 break;
241 case 'logid':
242 $status = $this->dispatchLog();
243 break;
244 default:
245 $status = null;
246 break;
247 }
248 if ( $status && $status->isGood() ) {
249 // These urls can sometimes be linked from prominent places,
250 // so varnish cache.
251 $value = $status->getValue();
252 if ( is_array( $value ) ) {
253 [ $url, $code ] = $value;
254 } else {
255 $url = $value;
256 $code = 301;
257 }
258 if ( $code === 301 ) {
259 $this->getOutput()->setCdnMaxage( 60 * 60 );
260 } else {
261 $this->getOutput()->setCdnMaxage( 10 );
262 }
263 $this->getOutput()->redirect( $url, $code );
264
265 return true;
266 }
267 if ( $this->mValue !== null ) {
268 $this->getOutput()->setStatusCode( 404 );
269
270 // @phan-suppress-next-line PhanTypeMismatchReturnNullable Null of $status seems unreachable
271 return $status;
272 }
273
274 return false;
275 }
276
277 protected function getFormFields() {
278 $ns = [
279 // subpage => message
280 'user' => 'redirect-user',
281 'page' => 'redirect-page',
282 'revision' => 'redirect-revision',
283 'file' => 'redirect-file',
284 'logid' => 'redirect-logid',
285 ];
286 $a = [];
287 $a['type'] = [
288 'type' => 'select',
289 'label-message' => 'redirect-lookup',
290 'options' => [],
291 'default' => current( array_keys( $ns ) ),
292 ];
293 foreach ( $ns as $n => $m ) {
294 $m = $this->msg( $m )->text();
295 $a['type']['options'][$m] = $n;
296 }
297 $a['value'] = [
298 'type' => 'text',
299 'label-message' => 'redirect-value'
300 ];
301 // set the defaults according to the parsed subpage path
302 if ( $this->mType ) {
303 $a['type']['default'] = $this->mType;
304 }
305 if ( $this->mValue ) {
306 $a['value']['default'] = $this->mValue;
307 }
308
309 return $a;
310 }
311
312 public function onSubmit( array $data ) {
313 if ( !empty( $data['type'] ) && !empty( $data['value'] ) ) {
314 $this->setParameter( $data['type'] . '/' . $data['value'] );
315 }
316
317 /* if this returns false, will show the form */
318 return $this->dispatch();
319 }
320
321 public function onSuccess() {
322 /* do nothing, we redirect in $this->dispatch if successful. */
323 }
324
325 protected function alterForm( HTMLForm $form ) {
326 // tweak label on submit button
327 $form->setSubmitTextMsg( 'redirect-submit' );
328 }
329
330 protected function getDisplayFormat() {
331 return 'ooui';
332 }
333
339 protected function getSubpagesForPrefixSearch() {
340 return [
341 'file',
342 'page',
343 'revision',
344 'user',
345 'logid',
346 ];
347 }
348
352 public function requiresPost() {
353 return false;
354 }
355
356 protected function getGroupName() {
357 return 'redirects';
358 }
359}
360
365class_alias( SpecialRedirect::class, 'SpecialRedirect' );
const NS_FILE
Definition Defines.php:70
const PROTO_CURRENT
Definition Defines.php:196
wfAppendQuery( $url, $query)
Append a query string to an existing URL, which may or may not already have query string parameters a...
wfScript( $script='index')
Get the URL path to a MediaWiki entry point.
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition HTMLForm.php:158
setSubmitTextMsg( $msg)
Set the text for the submit button to a message.
Special page which uses an HTMLForm to handle processing.
getRequest()
Get the WebRequest being used for this instance.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getOutput()
Get the OutputPage being used for this instance.
A special page that redirects to: the user for a numeric user id, the file for a given filename,...
dispatchPage()
Handle Special:Redirect/page/xxx (by redirecting to index.php?curid=xxx)
getSubpagesForPrefixSearch()
Return an array of subpages that this special page will accept.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
string null $mType
The type of the redirect (user/file/revision)
__construct(RepoGroup $repoGroup, UserFactory $userFactory)
dispatchRevision()
Handle Special:Redirect/revision/xxx (by redirecting to index.php?oldid=xxx)
onSubmit(array $data)
Process the form on submission.
dispatchLog()
Handle Special:Redirect/logid/xxx (by redirecting to index.php?title=Special:Log&logid=xxx)
setParameter( $subpage)
Set $mType and $mValue based on parsed value of $subpage.
onSuccess()
Do something exciting on successful processing of the form, most likely to show a confirmation messag...
string null $mValue
The identifier/value for the redirect (which id, which file)
getFormFields()
Get an HTMLForm descriptor array.
alterForm(HTMLForm $form)
Play with the HTMLForm if you need to more substantially.
dispatchUser()
Handle Special:Redirect/user/xxxx (by redirecting to User:YYYY)
getDisplayFormat()
Get display format for the form.
dispatchFile()
Handle Special:Redirect/file/xxxx.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:58
MalformedTitleException is thrown when a TitleParser is unable to parse a title string.
Represents a title within MediaWiki.
Definition Title.php:76
Creates User objects.
Show an error when a user tries to do something they do not have the necessary permissions for.
Prioritized list of file repositories.
Definition RepoGroup.php:30
This program is free software; you can redistribute it and/or modify it under the terms of the GNU Ge...
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition router.php:42