MediaWiki master
SpecialRedirect.php
Go to the documentation of this file.
1<?php
24namespace MediaWiki\Specials;
25
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 return [
279 'type' => [
280 'type' => 'select',
281 'label-message' => 'redirect-lookup',
282 'options-messages' => [
283 'redirect-user' => 'user',
284 'redirect-page' => 'page',
285 'redirect-revision' => 'revision',
286 'redirect-file' => 'file',
287 'redirect-logid' => 'logid',
288 ],
289 'default' => $this->mType,
290 ],
291 'value' => [
292 'type' => 'text',
293 'label-message' => 'redirect-value',
294 'default' => $this->mValue,
295 'required' => true,
296 ],
297 ];
298 }
299
300 public function onSubmit( array $data ) {
301 if ( !empty( $data['type'] ) && !empty( $data['value'] ) ) {
302 $this->setParameter( $data['type'] . '/' . $data['value'] );
303 }
304
305 /* if this returns false, will show the form */
306 return $this->dispatch();
307 }
308
309 public function onSuccess() {
310 /* do nothing, we redirect in $this->dispatch if successful. */
311 }
312
313 protected function alterForm( HTMLForm $form ) {
314 // tweak label on submit button
315 $form->setSubmitTextMsg( 'redirect-submit' );
316 }
317
318 protected function getDisplayFormat() {
319 return 'ooui';
320 }
321
327 protected function getSubpagesForPrefixSearch() {
328 return [
329 'file',
330 'page',
331 'revision',
332 'user',
333 'logid',
334 ];
335 }
336
340 public function requiresPost() {
341 return false;
342 }
343
344 protected function getGroupName() {
345 return 'redirects';
346 }
347}
348
353class_alias( SpecialRedirect::class, 'SpecialRedirect' );
const NS_FILE
Definition Defines.php:70
const PROTO_CURRENT
Definition Defines.php:207
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:206
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.
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:54
MalformedTitleException is thrown when a TitleParser is unable to parse a title string.
Represents a title within MediaWiki.
Definition Title.php:78
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...