MediaWiki master
SpecialRedirect.php
Go to the documentation of this file.
1<?php
7namespace MediaWiki\Specials;
8
17
31
39 protected $mType;
40
48 protected $mValue;
49
50 public function __construct(
51 private readonly RepoGroup $repoGroup,
52 private readonly UserFactory $userFactory
53 ) {
54 parent::__construct( 'Redirect' );
55 $this->mType = null;
56 $this->mValue = null;
57 }
58
63 public function setParameter( $subpage ) {
64 // parse $subpage to pull out the parts
65 $parts = $subpage !== null ? explode( '/', $subpage, 2 ) : [];
66 $this->mType = $parts[0] ?? null;
67 $this->mValue = $parts[1] ?? null;
68 }
69
75 public function dispatchUser() {
76 if ( !ctype_digit( $this->mValue ) ) {
77 return Status::newFatal( 'redirect-not-numeric' );
78 }
79 $user = $this->userFactory->newFromId( (int)$this->mValue );
80 $user->load(); // Make sure the id is validated by loading the user
81 if ( $user->isAnon() ) {
82 return Status::newFatal( 'redirect-not-exists' );
83 }
84 if ( $user->isHidden() && !$this->getAuthority()->isAllowed( 'hideuser' ) ) {
85 throw new PermissionsError( null, [ 'badaccess-group0' ] );
86 }
87
88 return Status::newGood( [
89 $user->getUserPage()->getFullURL( '', false, PROTO_CURRENT ), 302
90 ] );
91 }
92
98 public function dispatchFile() {
99 try {
100 $title = Title::newFromTextThrow( $this->mValue, NS_FILE );
101 if ( $title && !$title->inNamespace( NS_FILE ) ) {
102 // If the given value contains a namespace enforce file namespace
103 $title = Title::newFromTextThrow( Title::makeName( NS_FILE, $this->mValue ) );
104 }
105 } catch ( MalformedTitleException $e ) {
106 return Status::newFatal( $e->getMessageObject() );
107 }
108 $file = $this->repoGroup->findFile( $title );
109
110 if ( !$file || !$file->exists() ) {
111 return Status::newFatal( 'redirect-not-exists' );
112 }
113 // Default behavior: Use the direct link to the file.
114 $url = $file->getUrl();
115 $request = $this->getRequest();
116 $width = $request->getInt( 'width', -1 );
117 $height = $request->getInt( 'height', -1 );
118
119 // If a width is requested...
120 if ( $width != -1 ) {
121 $mto = $file->transform( [ 'width' => $width, 'height' => $height ] );
122 // ... and we can
123 if ( $mto && !$mto->isError() ) {
124 // ... change the URL to point to a thumbnail.
125 // Note: This url is more temporary as can change
126 // if file is reuploaded and has different aspect ratio.
127 $url = [ $mto->getUrl(), $height === -1 ? 301 : 302 ];
128 }
129 }
130
131 return Status::newGood( $url );
132 }
133
140 public function dispatchRevision() {
141 $oldid = $this->mValue;
142 if ( !ctype_digit( $oldid ) ) {
143 return Status::newFatal( 'redirect-not-numeric' );
144 }
145 $oldid = (int)$oldid;
146 if ( $oldid === 0 ) {
147 return Status::newFatal( 'redirect-not-exists' );
148 }
149
150 return Status::newGood( wfAppendQuery( wfScript( 'index' ), [
151 'oldid' => $oldid
152 ] ) );
153 }
154
160 public function dispatchPage() {
161 $curid = $this->mValue;
162 if ( !ctype_digit( $curid ) ) {
163 return Status::newFatal( 'redirect-not-numeric' );
164 }
165 $curid = (int)$curid;
166 if ( $curid === 0 ) {
167 return Status::newFatal( 'redirect-not-exists' );
168 }
169
170 return Status::newGood( wfAppendQuery( wfScript( 'index' ), [
171 'curid' => $curid
172 ] ) );
173 }
174
182 public function dispatchLog() {
183 $logid = $this->mValue;
184 if ( !ctype_digit( $logid ) ) {
185 return Status::newFatal( 'redirect-not-numeric' );
186 }
187 $logid = (int)$logid;
188 if ( $logid === 0 ) {
189 return Status::newFatal( 'redirect-not-exists' );
190 }
191 $query = [ 'title' => 'Special:Log', 'logid' => $logid ];
192 return Status::newGood( wfAppendQuery( wfScript( 'index' ), $query ) );
193 }
194
203 private function dispatch() {
204 // the various namespaces supported by Special:Redirect
205 $status = match ( $this->mType ) {
206 'user' => $this->dispatchUser(),
207 'file' => $this->dispatchFile(),
208 'revision' => $this->dispatchRevision(),
209 'page' => $this->dispatchPage(),
210 'logid' => $this->dispatchLog(),
211 default => null
212 };
213 if ( $status && $status->isGood() ) {
214 // These urls can sometimes be linked from prominent places,
215 // so varnish cache.
216 $value = $status->getValue();
217 if ( is_array( $value ) ) {
218 [ $url, $code ] = $value;
219 } else {
220 $url = $value;
221 $code = 301;
222 }
223 if ( $code === 301 ) {
224 $this->getOutput()->setCdnMaxage( 60 * 60 );
225 } else {
226 $this->getOutput()->setCdnMaxage( 10 );
227 }
228 $this->getOutput()->redirect( $url, $code );
229
230 return true;
231 }
232 if ( $this->mValue !== null ) {
233 $this->getOutput()->setStatusCode( 404 );
234
235 // @phan-suppress-next-line PhanTypeMismatchReturnNullable Null of $status seems unreachable
236 return $status;
237 }
238
239 return false;
240 }
241
243 protected function getFormFields() {
244 return [
245 'type' => [
246 'type' => 'select',
247 'label-message' => 'redirect-lookup',
248 'options-messages' => [
249 'redirect-user' => 'user',
250 'redirect-page' => 'page',
251 'redirect-revision' => 'revision',
252 'redirect-file' => 'file',
253 'redirect-logid' => 'logid',
254 ],
255 'default' => $this->mType,
256 ],
257 'value' => [
258 'type' => 'text',
259 'label-message' => 'redirect-value',
260 'default' => $this->mValue,
261 'required' => true,
262 ],
263 ];
264 }
265
267 public function onSubmit( array $data ) {
268 if ( !empty( $data['type'] ) && !empty( $data['value'] ) ) {
269 $this->setParameter( $data['type'] . '/' . $data['value'] );
270 }
271
272 /* if this returns false, will show the form */
273 return $this->dispatch();
274 }
275
276 public function onSuccess() {
277 /* do nothing, we redirect in $this->dispatch if successful. */
278 }
279
280 protected function alterForm( HTMLForm $form ) {
281 // tweak label on submit button
282 $form->setSubmitTextMsg( 'redirect-submit' );
283 }
284
286 protected function getDisplayFormat() {
287 return 'ooui';
288 }
289
295 protected function getSubpagesForPrefixSearch() {
296 return [
297 'file',
298 'page',
299 'revision',
300 'user',
301 'logid',
302 ];
303 }
304
308 public function requiresPost() {
309 return false;
310 }
311
313 protected function getGroupName() {
314 return 'redirects';
315 }
316}
317
322class_alias( SpecialRedirect::class, 'SpecialRedirect' );
const NS_FILE
Definition Defines.php:57
const PROTO_CURRENT
Definition Defines.php:222
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.
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
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition HTMLForm.php:195
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)
dispatchRevision()
Handle Special:Redirect/revision/xxx (by redirecting to index.php?oldid=xxx)
onSubmit(array $data)
Process the form on submission.bool|string|array|Status As documented for HTMLForm::trySubmit.
dispatchLog()
Handle Special:Redirect/logid/xxx (by redirecting to index.php?title=Special:Log&logid=xxx)
__construct(private readonly RepoGroup $repoGroup, private readonly UserFactory $userFactory)
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.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.See HTMLForm documentation for available values.1....
dispatchFile()
Handle Special:Redirect/file/xxxx.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:44
MalformedTitleException is thrown when a TitleParser is unable to parse a title string.
Represents a title within MediaWiki.
Definition Title.php:69
Create User objects.