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