MediaWiki master
SpecialRedirect.php
Go to the documentation of this file.
1<?php
21namespace MediaWiki\Specials;
22
30use RepoGroup;
31
45
53 protected $mType;
54
62 protected $mValue;
63
64 private RepoGroup $repoGroup;
65 private UserFactory $userFactory;
66
67 public function __construct(
68 RepoGroup $repoGroup,
69 UserFactory $userFactory
70 ) {
71 parent::__construct( 'Redirect' );
72 $this->mType = null;
73 $this->mValue = null;
74
75 $this->repoGroup = $repoGroup;
76 $this->userFactory = $userFactory;
77 }
78
83 public function setParameter( $subpage ) {
84 // parse $subpage to pull out the parts
85 $parts = $subpage !== null ? explode( '/', $subpage, 2 ) : [];
86 $this->mType = $parts[0] ?? null;
87 $this->mValue = $parts[1] ?? null;
88 }
89
95 public function dispatchUser() {
96 if ( !ctype_digit( $this->mValue ) ) {
97 return Status::newFatal( 'redirect-not-numeric' );
98 }
99 $user = $this->userFactory->newFromId( (int)$this->mValue );
100 $user->load(); // Make sure the id is validated by loading the user
101 if ( $user->isAnon() ) {
102 return Status::newFatal( 'redirect-not-exists' );
103 }
104 if ( $user->isHidden() && !$this->getAuthority()->isAllowed( 'hideuser' ) ) {
105 throw new PermissionsError( null, [ 'badaccess-group0' ] );
106 }
107
108 return Status::newGood( [
109 $user->getUserPage()->getFullURL( '', false, PROTO_CURRENT ), 302
110 ] );
111 }
112
118 public function dispatchFile() {
119 try {
120 $title = Title::newFromTextThrow( $this->mValue, NS_FILE );
121 if ( $title && !$title->inNamespace( NS_FILE ) ) {
122 // If the given value contains a namespace enforce file namespace
123 $title = Title::newFromTextThrow( Title::makeName( NS_FILE, $this->mValue ) );
124 }
125 } catch ( MalformedTitleException $e ) {
126 return Status::newFatal( $e->getMessageObject() );
127 }
128 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable False positive
129 $file = $this->repoGroup->findFile( $title );
130
131 if ( !$file || !$file->exists() ) {
132 return Status::newFatal( 'redirect-not-exists' );
133 }
134 // Default behavior: Use the direct link to the file.
135 $url = $file->getUrl();
136 $request = $this->getRequest();
137 $width = $request->getInt( 'width', -1 );
138 $height = $request->getInt( 'height', -1 );
139
140 // If a width is requested...
141 if ( $width != -1 ) {
142 $mto = $file->transform( [ 'width' => $width, 'height' => $height ] );
143 // ... and we can
144 if ( $mto && !$mto->isError() ) {
145 // ... change the URL to point to a thumbnail.
146 // Note: This url is more temporary as can change
147 // if file is reuploaded and has different aspect ratio.
148 $url = [ $mto->getUrl(), $height === -1 ? 301 : 302 ];
149 }
150 }
151
152 return Status::newGood( $url );
153 }
154
161 public function dispatchRevision() {
162 $oldid = $this->mValue;
163 if ( !ctype_digit( $oldid ) ) {
164 return Status::newFatal( 'redirect-not-numeric' );
165 }
166 $oldid = (int)$oldid;
167 if ( $oldid === 0 ) {
168 return Status::newFatal( 'redirect-not-exists' );
169 }
170
171 return Status::newGood( wfAppendQuery( wfScript( 'index' ), [
172 'oldid' => $oldid
173 ] ) );
174 }
175
181 public function dispatchPage() {
182 $curid = $this->mValue;
183 if ( !ctype_digit( $curid ) ) {
184 return Status::newFatal( 'redirect-not-numeric' );
185 }
186 $curid = (int)$curid;
187 if ( $curid === 0 ) {
188 return Status::newFatal( 'redirect-not-exists' );
189 }
190
191 return Status::newGood( wfAppendQuery( wfScript( 'index' ), [
192 'curid' => $curid
193 ] ) );
194 }
195
203 public function dispatchLog() {
204 $logid = $this->mValue;
205 if ( !ctype_digit( $logid ) ) {
206 return Status::newFatal( 'redirect-not-numeric' );
207 }
208 $logid = (int)$logid;
209 if ( $logid === 0 ) {
210 return Status::newFatal( 'redirect-not-exists' );
211 }
212 $query = [ 'title' => 'Special:Log', 'logid' => $logid ];
213 return Status::newGood( wfAppendQuery( wfScript( 'index' ), $query ) );
214 }
215
224 private function dispatch() {
225 // the various namespaces supported by Special:Redirect
226 switch ( $this->mType ) {
227 case 'user':
228 $status = $this->dispatchUser();
229 break;
230 case 'file':
231 $status = $this->dispatchFile();
232 break;
233 case 'revision':
234 $status = $this->dispatchRevision();
235 break;
236 case 'page':
237 $status = $this->dispatchPage();
238 break;
239 case 'logid':
240 $status = $this->dispatchLog();
241 break;
242 default:
243 $status = null;
244 break;
245 }
246 if ( $status && $status->isGood() ) {
247 // These urls can sometimes be linked from prominent places,
248 // so varnish cache.
249 $value = $status->getValue();
250 if ( is_array( $value ) ) {
251 [ $url, $code ] = $value;
252 } else {
253 $url = $value;
254 $code = 301;
255 }
256 if ( $code === 301 ) {
257 $this->getOutput()->setCdnMaxage( 60 * 60 );
258 } else {
259 $this->getOutput()->setCdnMaxage( 10 );
260 }
261 $this->getOutput()->redirect( $url, $code );
262
263 return true;
264 }
265 if ( $this->mValue !== null ) {
266 $this->getOutput()->setStatusCode( 404 );
267
268 // @phan-suppress-next-line PhanTypeMismatchReturnNullable Null of $status seems unreachable
269 return $status;
270 }
271
272 return false;
273 }
274
275 protected function getFormFields() {
276 return [
277 'type' => [
278 'type' => 'select',
279 'label-message' => 'redirect-lookup',
280 'options-messages' => [
281 'redirect-user' => 'user',
282 'redirect-page' => 'page',
283 'redirect-revision' => 'revision',
284 'redirect-file' => 'file',
285 'redirect-logid' => 'logid',
286 ],
287 'default' => $this->mType,
288 ],
289 'value' => [
290 'type' => 'text',
291 'label-message' => 'redirect-value',
292 'default' => $this->mValue,
293 'required' => true,
294 ],
295 ];
296 }
297
298 public function onSubmit( array $data ) {
299 if ( !empty( $data['type'] ) && !empty( $data['value'] ) ) {
300 $this->setParameter( $data['type'] . '/' . $data['value'] );
301 }
302
303 /* if this returns false, will show the form */
304 return $this->dispatch();
305 }
306
307 public function onSuccess() {
308 /* do nothing, we redirect in $this->dispatch if successful. */
309 }
310
311 protected function alterForm( HTMLForm $form ) {
312 // tweak label on submit button
313 $form->setSubmitTextMsg( 'redirect-submit' );
314 }
315
316 protected function getDisplayFormat() {
317 return 'ooui';
318 }
319
325 protected function getSubpagesForPrefixSearch() {
326 return [
327 'file',
328 'page',
329 'revision',
330 'user',
331 'logid',
332 ];
333 }
334
338 public function requiresPost() {
339 return false;
340 }
341
342 protected function getGroupName() {
343 return 'redirects';
344 }
345}
346
351class_alias( SpecialRedirect::class, 'SpecialRedirect' );
const NS_FILE
Definition Defines.php:71
const PROTO_CURRENT
Definition Defines.php:236
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:210
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.
Redirect dispatcher for user IDs, thumbnails, and various permalinks.
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
Create 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:32