MediaWiki  master
SpecialRedirect.php
Go to the documentation of this file.
1 <?php
25 
34 
42  protected $mType;
43 
51  protected $mValue;
52 
54  private $repoGroup;
55 
57  private $userFactory;
58 
63  public function __construct(
64  RepoGroup $repoGroup,
65  UserFactory $userFactory
66  ) {
67  parent::__construct( 'Redirect' );
68  $this->mType = null;
69  $this->mValue = null;
70 
71  $this->repoGroup = $repoGroup;
72  $this->userFactory = $userFactory;
73  }
74 
79  public function setParameter( $subpage ) {
80  // parse $subpage to pull out the parts
81  $parts = $subpage !== null ? explode( '/', $subpage, 2 ) : [];
82  $this->mType = $parts[0] ?? null;
83  $this->mValue = $parts[1] ?? null;
84  }
85 
91  public function dispatchUser() {
92  if ( !ctype_digit( $this->mValue ) ) {
93  // Message: redirect-not-numeric
94  return Status::newFatal( $this->getMessagePrefix() . '-not-numeric' );
95  }
96  $user = $this->userFactory->newFromId( (int)$this->mValue );
97  $user->load(); // Make sure the id is validated by loading the user
98  if ( $user->isAnon() ) {
99  // Message: redirect-not-exists
100  return Status::newFatal( $this->getMessagePrefix() . '-not-exists' );
101  }
102  if ( $user->isHidden() && !$this->getAuthority()->isAllowed( 'hideuser' ) ) {
103  throw new PermissionsError( null, [ 'badaccess-group0' ] );
104  }
105 
106  return Status::newGood( [
107  $user->getUserPage()->getFullURL( '', false, PROTO_CURRENT ), 302
108  ] );
109  }
110 
116  public function dispatchFile() {
117  try {
118  $title = Title::newFromTextThrow( $this->mValue, NS_FILE );
119  if ( $title && !$title->inNamespace( NS_FILE ) ) {
120  // If the given value contains a namespace enforce file namespace
121  $title = Title::newFromTextThrow( Title::makeName( NS_FILE, $this->mValue ) );
122  }
123  } catch ( MalformedTitleException $e ) {
124  return Status::newFatal( $e->getMessageObject() );
125  }
126  // @phan-suppress-next-line PhanTypeMismatchArgumentNullable False positive
127  $file = $this->repoGroup->findFile( $title );
128 
129  if ( !$file || !$file->exists() ) {
130  // Message: redirect-not-exists
131  return Status::newFatal( $this->getMessagePrefix() . '-not-exists' );
132  }
133  // Default behavior: Use the direct link to the file.
134  $url = $file->getUrl();
135  $request = $this->getRequest();
136  $width = $request->getInt( 'width', -1 );
137  $height = $request->getInt( 'height', -1 );
138 
139  // If a width is requested...
140  if ( $width != -1 ) {
141  $mto = $file->transform( [ 'width' => $width, 'height' => $height ] );
142  // ... and we can
143  if ( $mto && !$mto->isError() ) {
144  // ... change the URL to point to a thumbnail.
145  // Note: This url is more temporary as can change
146  // if file is reuploaded and has different aspect ratio.
147  $url = [ $mto->getUrl(), $height === -1 ? 301 : 302 ];
148  }
149  }
150 
151  return Status::newGood( $url );
152  }
153 
160  public function dispatchRevision() {
161  $oldid = $this->mValue;
162  if ( !ctype_digit( $oldid ) ) {
163  // Message: redirect-not-numeric
164  return Status::newFatal( $this->getMessagePrefix() . '-not-numeric' );
165  }
166  $oldid = (int)$oldid;
167  if ( $oldid === 0 ) {
168  // Message: redirect-not-exists
169  return Status::newFatal( $this->getMessagePrefix() . '-not-exists' );
170  }
171 
172  return Status::newGood( wfAppendQuery( wfScript( 'index' ), [
173  'oldid' => $oldid
174  ] ) );
175  }
176 
182  public function dispatchPage() {
183  $curid = $this->mValue;
184  if ( !ctype_digit( $curid ) ) {
185  // Message: redirect-not-numeric
186  return Status::newFatal( $this->getMessagePrefix() . '-not-numeric' );
187  }
188  $curid = (int)$curid;
189  if ( $curid === 0 ) {
190  // Message: redirect-not-exists
191  return Status::newFatal( $this->getMessagePrefix() . '-not-exists' );
192  }
193 
194  return Status::newGood( wfAppendQuery( wfScript( 'index' ), [
195  'curid' => $curid
196  ] ) );
197  }
198 
206  public function dispatchLog() {
207  $logid = $this->mValue;
208  if ( !ctype_digit( $logid ) ) {
209  // Message: redirect-not-numeric
210  return Status::newFatal( $this->getMessagePrefix() . '-not-numeric' );
211  }
212  $logid = (int)$logid;
213  if ( $logid === 0 ) {
214  // Message: redirect-not-exists
215  return Status::newFatal( $this->getMessagePrefix() . '-not-exists' );
216  }
217  $query = [ 'title' => 'Special:Log', 'logid' => $logid ];
218  return Status::newGood( wfAppendQuery( wfScript( 'index' ), $query ) );
219  }
220 
229  private function dispatch() {
230  // the various namespaces supported by Special:Redirect
231  switch ( $this->mType ) {
232  case 'user':
233  $status = $this->dispatchUser();
234  break;
235  case 'file':
236  $status = $this->dispatchFile();
237  break;
238  case 'revision':
239  $status = $this->dispatchRevision();
240  break;
241  case 'page':
242  $status = $this->dispatchPage();
243  break;
244  case 'logid':
245  $status = $this->dispatchLog();
246  break;
247  default:
248  $status = null;
249  break;
250  }
251  if ( $status && $status->isGood() ) {
252  // These urls can sometimes be linked from prominent places,
253  // so varnish cache.
254  $value = $status->getValue();
255  if ( is_array( $value ) ) {
256  [ $url, $code ] = $value;
257  } else {
258  $url = $value;
259  $code = 301;
260  }
261  if ( $code === 301 ) {
262  $this->getOutput()->setCdnMaxage( 60 * 60 );
263  } else {
264  $this->getOutput()->setCdnMaxage( 10 );
265  }
266  $this->getOutput()->redirect( $url, $code );
267 
268  return true;
269  }
270  if ( $this->mValue !== null ) {
271  $this->getOutput()->setStatusCode( 404 );
272 
273  // @phan-suppress-next-line PhanTypeMismatchReturnNullable Null of $status seems unreachable
274  return $status;
275  }
276 
277  return false;
278  }
279 
280  protected function getFormFields() {
281  $mp = $this->getMessagePrefix();
282  $ns = [
283  // subpage => message
284  // Messages: redirect-user, redirect-page, redirect-revision,
285  // redirect-file, redirect-logid
286  'user' => $mp . '-user',
287  'page' => $mp . '-page',
288  'revision' => $mp . '-revision',
289  'file' => $mp . '-file',
290  'logid' => $mp . '-logid',
291  ];
292  $a = [];
293  $a['type'] = [
294  'type' => 'select',
295  'label-message' => $mp . '-lookup', // Message: redirect-lookup
296  'options' => [],
297  'default' => current( array_keys( $ns ) ),
298  ];
299  foreach ( $ns as $n => $m ) {
300  $m = $this->msg( $m )->text();
301  $a['type']['options'][$m] = $n;
302  }
303  $a['value'] = [
304  'type' => 'text',
305  'label-message' => $mp . '-value' // Message: redirect-value
306  ];
307  // set the defaults according to the parsed subpage path
308  if ( !empty( $this->mType ) ) {
309  $a['type']['default'] = $this->mType;
310  }
311  if ( !empty( $this->mValue ) ) {
312  $a['value']['default'] = $this->mValue;
313  }
314 
315  return $a;
316  }
317 
318  public function onSubmit( array $data ) {
319  if ( !empty( $data['type'] ) && !empty( $data['value'] ) ) {
320  $this->setParameter( $data['type'] . '/' . $data['value'] );
321  }
322 
323  /* if this returns false, will show the form */
324  return $this->dispatch();
325  }
326 
327  public function onSuccess() {
328  /* do nothing, we redirect in $this->dispatch if successful. */
329  }
330 
331  protected function alterForm( HTMLForm $form ) {
332  /* display summary at top of page */
333  $this->outputHeader();
334  // tweak label on submit button
335  // Message: redirect-submit
336  $form->setSubmitTextMsg( $this->getMessagePrefix() . '-submit' );
337  /* submit form every time */
338  $form->setMethod( 'get' );
339  }
340 
341  protected function getDisplayFormat() {
342  return 'ooui';
343  }
344 
350  protected function getSubpagesForPrefixSearch() {
351  return [
352  'file',
353  'page',
354  'revision',
355  'user',
356  'logid',
357  ];
358  }
359 
363  public function requiresWrite() {
364  return false;
365  }
366 
370  public function requiresUnblock() {
371  return false;
372  }
373 
374  protected function getGroupName() {
375  return 'redirects';
376  }
377 }
const NS_FILE
Definition: Defines.php:70
const PROTO_CURRENT
Definition: Defines.php:198
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 path to a specified script file, respecting file extensions; this is a wrapper around $wgScri...
Special page which uses an HTMLForm to handle processing.
getMessagePrefix()
Get message prefix for HTMLForm.
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition: HTMLForm.php:150
setMethod( $method='post')
Set the method used to submit the form.
Definition: HTMLForm.php:1870
setSubmitTextMsg( $msg)
Set the text for the submit button to a message.
Definition: HTMLForm.php:1613
MalformedTitleException is thrown when a TitleParser is unable to parse a title string.
Creates User objects.
Definition: UserFactory.php:38
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:29
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages Per default the message key is the canonical name o...
getOutput()
Get the OutputPage being used for this instance.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getRequest()
Get the WebRequest being used for this instance.
A special page that redirects to: the user for a numeric user id, the file for a given filename,...
getSubpagesForPrefixSearch()
Return an array of subpages that this special page will accept.
setParameter( $subpage)
Set $mType and $mValue based on parsed value of $subpage.
onSubmit(array $data)
Process the form on POST submission.
getDisplayFormat()
Get display format for the form.
dispatchLog()
Handle Special:Redirect/logid/xxx (by redirecting to index.php?title=Special:Log&logid=xxx)
getFormFields()
Get an HTMLForm descriptor array.
onSuccess()
Do something exciting on successful processing of the form, most likely to show a confirmation messag...
dispatchUser()
Handle Special:Redirect/user/xxxx (by redirecting to User:YYYY)
string null $mValue
The identifier/value for the redirect (which id, which file)
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
alterForm(HTMLForm $form)
Play with the HTMLForm if you need to more substantially.
dispatchRevision()
Handle Special:Redirect/revision/xxx (by redirecting to index.php?oldid=xxx)
dispatchPage()
Handle Special:Redirect/page/xxx (by redirecting to index.php?curid=xxx)
dispatchFile()
Handle Special:Redirect/file/xxxx.
__construct(RepoGroup $repoGroup, UserFactory $userFactory)
string null $mType
The type of the redirect (user/file/revision)
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:73
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:85
static newFromTextThrow( $text, $defaultNamespace=NS_MAIN)
Like Title::newFromText(), but throws MalformedTitleException when the title is invalid,...
Definition: Title.php:408
static makeName( $ns, $title, $fragment='', $interwiki='', $canonicalNamespace=false)
Make a prefixed DB key from a DB key and a namespace index.
Definition: Title.php:857
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition: router.php:42