MediaWiki  master
SpecialUpload.php
Go to the documentation of this file.
1 <?php
25 namespace MediaWiki\Specials;
26 
27 use BitmapHandler;
28 use ChangeTags;
29 use ErrorPageError;
30 use HTMLForm;
32 use LocalFile;
33 use LocalRepo;
34 use LogEventsList;
49 use RepoGroup;
50 use UnexpectedValueException;
51 use UploadBase;
52 use UploadForm;
53 use UploadFromStash;
55 
62 class SpecialUpload extends SpecialPage {
63 
64  private LocalRepo $localRepo;
65  private UserOptionsLookup $userOptionsLookup;
66  private NamespaceInfo $nsInfo;
67  private WatchlistManager $watchlistManager;
68 
75  public function __construct(
76  RepoGroup $repoGroup = null,
77  UserOptionsLookup $userOptionsLookup = null,
78  NamespaceInfo $nsInfo = null,
79  WatchlistManager $watchlistManager = null
80  ) {
81  parent::__construct( 'Upload', 'upload' );
82  // This class is extended and therefor fallback to global state - T265300
83  $services = MediaWikiServices::getInstance();
84  $repoGroup ??= $services->getRepoGroup();
85  $this->localRepo = $repoGroup->getLocalRepo();
86  $this->userOptionsLookup = $userOptionsLookup ?? $services->getUserOptionsLookup();
87  $this->nsInfo = $nsInfo ?? $services->getNamespaceInfo();
88  $this->watchlistManager = $watchlistManager ?? $services->getWatchlistManager();
89  }
90 
91  public function doesWrites() {
92  return true;
93  }
94 
98  public $mRequest;
99  public $mSourceType;
100 
102  public $mUpload;
103 
105  public $mLocalFile;
107 
112  public $mComment;
113  public $mLicense;
114 
118  public $mWatchthis;
121 
125 
128 
131  public $mTokenOk;
132 
134  public $mUploadSuccessful = false;
135 
140 
144  protected function loadRequest() {
145  $this->mRequest = $request = $this->getRequest();
146  $this->mSourceType = $request->getVal( 'wpSourceType', 'file' );
147  $this->mUpload = UploadBase::createFromRequest( $request );
148  $this->mUploadClicked = $request->wasPosted()
149  && ( $request->getCheck( 'wpUpload' )
150  || $request->getCheck( 'wpUploadIgnoreWarning' ) );
151 
152  // Guess the desired name from the filename if not provided
153  $this->mDesiredDestName = $request->getText( 'wpDestFile' );
154  if ( !$this->mDesiredDestName && $request->getFileName( 'wpUploadFile' ) !== null ) {
155  $this->mDesiredDestName = $request->getFileName( 'wpUploadFile' );
156  }
157  $this->mLicense = $request->getText( 'wpLicense' );
158 
159  $this->mDestWarningAck = $request->getText( 'wpDestFileWarningAck' );
160  $this->mIgnoreWarning = $request->getCheck( 'wpIgnoreWarning' )
161  || $request->getCheck( 'wpUploadIgnoreWarning' );
162  $this->mWatchthis = $request->getBool( 'wpWatchthis' ) && $this->getUser()->isRegistered();
163  $this->mCopyrightStatus = $request->getText( 'wpUploadCopyStatus' );
164  $this->mCopyrightSource = $request->getText( 'wpUploadSource' );
165 
166  $this->mForReUpload = $request->getBool( 'wpForReUpload' ); // updating a file
167 
168  $commentDefault = '';
169  $commentMsg = $this->msg( 'upload-default-description' )->inContentLanguage();
170  if ( !$this->mForReUpload && !$commentMsg->isDisabled() ) {
171  $commentDefault = $commentMsg->plain();
172  }
173  $this->mComment = $request->getText( 'wpUploadDescription', $commentDefault );
174 
175  $this->mCancelUpload = $request->getCheck( 'wpCancelUpload' )
176  || $request->getCheck( 'wpReUpload' ); // b/w compat
177 
178  // If it was posted check for the token (no remote POST'ing with user credentials)
179  $token = $request->getVal( 'wpEditToken' );
180  $this->mTokenOk = $this->getUser()->matchEditToken( $token );
181 
182  $this->uploadFormTextTop = '';
183  $this->uploadFormTextAfterSummary = '';
184  }
185 
194  public function userCanExecute( User $user ) {
195  return UploadBase::isEnabled() && parent::userCanExecute( $user );
196  }
197 
201  public function execute( $par ) {
202  $this->useTransactionalTimeLimit();
203 
204  $this->setHeaders();
205  $this->outputHeader();
206 
207  # Check uploading enabled
208  if ( !UploadBase::isEnabled() ) {
209  throw new ErrorPageError( 'uploaddisabled', 'uploaddisabledtext' );
210  }
211 
212  $this->addHelpLink( 'Help:Managing files' );
213 
214  # Check permissions
215  $user = $this->getUser();
216  $permissionRequired = UploadBase::isAllowed( $user );
217  if ( $permissionRequired !== true ) {
218  throw new PermissionsError( $permissionRequired );
219  }
220 
221  # Check blocks
222  if ( $user->isBlockedFromUpload() ) {
223  throw new UserBlockedError(
224  // @phan-suppress-next-line PhanTypeMismatchArgumentNullable Block is checked and not null
225  $user->getBlock(),
226  $user,
227  $this->getLanguage(),
228  $this->getRequest()->getIP()
229  );
230  }
231 
232  # Check whether we actually want to allow changing stuff
233  $this->checkReadOnly();
234 
235  $this->loadRequest();
236 
237  # Unsave the temporary file in case this was a cancelled upload
238  if ( $this->mCancelUpload && !$this->unsaveUploadedFile() ) {
239  # Something went wrong, so unsaveUploadedFile showed a warning
240  return;
241  }
242 
243  # Process upload or show a form
244  if (
245  $this->mTokenOk && !$this->mCancelUpload &&
246  ( $this->mUpload && $this->mUploadClicked )
247  ) {
248  $this->processUpload();
249  } else {
250  # Backwards compatibility hook
251  if ( !$this->getHookRunner()->onUploadForm_initial( $this ) ) {
252  wfDebug( "Hook 'UploadForm:initial' broke output of the upload form" );
253 
254  return;
255  }
256  $this->showUploadForm( $this->getUploadForm() );
257  }
258 
259  # Cleanup
260  if ( $this->mUpload ) {
261  $this->mUpload->cleanupTempFile();
262  }
263  }
264 
270  protected function showUploadForm( $form ) {
271  if ( $form instanceof HTMLForm ) {
272  $form->show();
273  } else {
274  $this->getOutput()->addHTML( $form );
275  }
276  }
277 
286  protected function getUploadForm( $message = '', $sessionKey = '', $hideIgnoreWarning = false ) {
287  # Initialize form
288  $form = new UploadForm(
289  [
290  'watch' => $this->getWatchCheck(),
291  'forreupload' => $this->mForReUpload,
292  'sessionkey' => $sessionKey,
293  'hideignorewarning' => $hideIgnoreWarning,
294  'destwarningack' => (bool)$this->mDestWarningAck,
295 
296  'description' => $this->mComment,
297  'texttop' => $this->uploadFormTextTop,
298  'textaftersummary' => $this->uploadFormTextAfterSummary,
299  'destfile' => $this->mDesiredDestName,
300  ],
301  $this->getContext(),
302  $this->getLinkRenderer(),
303  $this->localRepo,
304  $this->getContentLanguage(),
305  $this->nsInfo,
306  $this->getHookContainer()
307  );
308  $form->setTitle( $this->getPageTitle() ); // Remove subpage
309 
310  # Check the token, but only if necessary
311  if (
312  !$this->mTokenOk && !$this->mCancelUpload &&
313  ( $this->mUpload && $this->mUploadClicked )
314  ) {
315  $form->addPreText( $this->msg( 'session_fail_preview' )->parse() );
316  }
317 
318  # Give a notice if the user is uploading a file that has been deleted or moved
319  # Note that this is independent from the message 'filewasdeleted'
320  $desiredTitleObj = Title::makeTitleSafe( NS_FILE, $this->mDesiredDestName );
321  $delNotice = ''; // empty by default
322  if ( $desiredTitleObj instanceof Title && !$desiredTitleObj->exists() ) {
323  LogEventsList::showLogExtract( $delNotice, [ 'delete', 'move' ],
324  $desiredTitleObj,
325  '', [ 'lim' => 10,
326  'conds' => [ 'log_action != ' . $this->localRepo->getReplicaDB()->addQuotes( 'revision' ) ],
327  'showIfEmpty' => false,
328  'msgKey' => [ 'upload-recreate-warning' ] ]
329  );
330  }
331  $form->addPreText( $delNotice );
332 
333  # Add text to form
334  $form->addPreText( '<div id="uploadtext">' .
335  $this->msg( 'uploadtext', [ $this->mDesiredDestName ] )->parseAsBlock() .
336  '</div>' );
337  # Add upload error message
338  $form->addPreText( $message );
339 
340  # Add footer to form
341  $uploadFooter = $this->msg( 'uploadfooter' );
342  if ( !$uploadFooter->isDisabled() ) {
343  $form->addPostText( '<div id="mw-upload-footer-message">'
344  . $uploadFooter->parseAsBlock() . "</div>\n" );
345  }
346 
347  return $form;
348  }
349 
361  protected function showRecoverableUploadError( $message ) {
362  $stashStatus = $this->mUpload->tryStashFile( $this->getUser() );
363  if ( $stashStatus->isGood() ) {
364  $sessionKey = $stashStatus->getValue()->getFileKey();
365  $uploadWarning = 'upload-tryagain';
366  } else {
367  $sessionKey = null;
368  $uploadWarning = 'upload-tryagain-nostash';
369  }
370  $message = '<h2>' . $this->msg( 'uploaderror' )->escaped() . '</h2>' .
371  Html::errorBox( $message );
372 
373  $form = $this->getUploadForm( $message, $sessionKey );
374  $form->setSubmitText( $this->msg( $uploadWarning )->escaped() );
375  $this->showUploadForm( $form );
376  }
377 
386  protected function showUploadWarning( $warnings ) {
387  # If there are no warnings, or warnings we can ignore, return early.
388  # mDestWarningAck is set when some javascript has shown the warning
389  # to the user. mForReUpload is set when the user clicks the "upload a
390  # new version" link.
391  if ( !$warnings || ( count( $warnings ) == 1
392  && isset( $warnings['exists'] )
393  && ( $this->mDestWarningAck || $this->mForReUpload ) )
394  ) {
395  return false;
396  }
397 
398  $stashStatus = $this->mUpload->tryStashFile( $this->getUser() );
399  if ( $stashStatus->isGood() ) {
400  $sessionKey = $stashStatus->getValue()->getFileKey();
401  $uploadWarning = 'uploadwarning-text';
402  } else {
403  $sessionKey = null;
404  $uploadWarning = 'uploadwarning-text-nostash';
405  }
406 
407  // Add styles for the warning, reused from the live preview
408  $this->getOutput()->addModuleStyles( 'mediawiki.special' );
409 
410  $linkRenderer = $this->getLinkRenderer();
411  $warningHtml = '<h2>' . $this->msg( 'uploadwarning' )->escaped() . "</h2>\n"
412  . '<div class="mw-destfile-warning"><ul>';
413  foreach ( $warnings as $warning => $args ) {
414  if ( $warning == 'badfilename' ) {
415  $this->mDesiredDestName = Title::makeTitle( NS_FILE, $args )->getText();
416  }
417  if ( $warning == 'exists' ) {
418  $msg = "\t<li>" . self::getExistsWarning( $args ) . "</li>\n";
419  } elseif ( $warning == 'no-change' ) {
420  $file = $args;
421  $filename = $file->getTitle()->getPrefixedText();
422  $msg = "\t<li>" . $this->msg( 'fileexists-no-change', $filename )->parse() . "</li>\n";
423  } elseif ( $warning == 'duplicate-version' ) {
424  $file = $args[0];
425  $count = count( $args );
426  $filename = $file->getTitle()->getPrefixedText();
427  $message = $this->msg( 'fileexists-duplicate-version' )
428  ->params( $filename )
429  ->numParams( $count );
430  $msg = "\t<li>" . $message->parse() . "</li>\n";
431  } elseif ( $warning == 'was-deleted' ) {
432  # If the file existed before and was deleted, warn the user of this
433  $ltitle = SpecialPage::getTitleFor( 'Log' );
434  $llink = $linkRenderer->makeKnownLink(
435  $ltitle,
436  $this->msg( 'deletionlog' )->text(),
437  [],
438  [
439  'type' => 'delete',
440  'page' => Title::makeTitle( NS_FILE, $args )->getPrefixedText(),
441  ]
442  );
443  $msg = "\t<li>" . $this->msg( 'filewasdeleted' )->rawParams( $llink )->parse() . "</li>\n";
444  } elseif ( $warning == 'duplicate' ) {
445  $msg = $this->getDupeWarning( $args );
446  } elseif ( $warning == 'duplicate-archive' ) {
447  if ( $args === '' ) {
448  $msg = "\t<li>" . $this->msg( 'file-deleted-duplicate-notitle' )->parse()
449  . "</li>\n";
450  } else {
451  $msg = "\t<li>" . $this->msg( 'file-deleted-duplicate',
452  Title::makeTitle( NS_FILE, $args )->getPrefixedText() )->parse()
453  . "</li>\n";
454  }
455  } else {
456  if ( $args === true ) {
457  $args = [];
458  } elseif ( !is_array( $args ) ) {
459  $args = [ $args ];
460  }
461  $msg = "\t<li>" . $this->msg( $warning, $args )->parse() . "</li>\n";
462  }
463  $warningHtml .= $msg;
464  }
465  $warningHtml .= "</ul></div>\n";
466  $warningHtml .= $this->msg( $uploadWarning )->parseAsBlock();
467 
468  $form = $this->getUploadForm( $warningHtml, $sessionKey, /* $hideIgnoreWarning */ true );
469  $form->setSubmitTextMsg( 'upload-tryagain' );
470  $form->addButton( [
471  'name' => 'wpUploadIgnoreWarning',
472  'value' => $this->msg( 'ignorewarning' )->text()
473  ] );
474  $form->addButton( [
475  'name' => 'wpCancelUpload',
476  'value' => $this->msg( 'reuploaddesc' )->text()
477  ] );
478 
479  $this->showUploadForm( $form );
480 
481  # Indicate that we showed a form
482  return true;
483  }
484 
490  protected function showUploadError( $message ) {
491  $message = '<h2>' . $this->msg( 'uploadwarning' )->escaped() . '</h2>' .
492  Html::errorBox( $message );
493  $this->showUploadForm( $this->getUploadForm( $message ) );
494  }
495 
500  protected function processUpload() {
501  // Fetch the file if required
502  $status = $this->mUpload->fetchFile();
503  if ( !$status->isOK() ) {
504  $this->showUploadError( $this->getOutput()->parseAsInterface(
505  $status->getWikiText( false, false, $this->getLanguage() )
506  ) );
507 
508  return;
509  }
510  if ( !$this->getHookRunner()->onUploadForm_BeforeProcessing( $this ) ) {
511  wfDebug( "Hook 'UploadForm:BeforeProcessing' broke processing the file." );
512  // This code path is deprecated. If you want to break upload processing
513  // do so by hooking into the appropriate hooks in UploadBase::verifyUpload
514  // and UploadBase::verifyFile.
515  // If you use this hook to break uploading, the user will be returned
516  // an empty form with no error message whatsoever.
517  return;
518  }
519 
520  // Upload verification
521  $details = $this->mUpload->verifyUpload();
522  if ( $details['status'] != UploadBase::OK ) {
523  $this->processVerificationError( $details );
524 
525  return;
526  }
527 
528  // Verify permissions for this title
529  $user = $this->getUser();
530  $permErrors = $this->mUpload->verifyTitlePermissions( $user );
531  if ( $permErrors !== true ) {
532  $code = array_shift( $permErrors[0] );
533  $this->showRecoverableUploadError( $this->msg( $code, $permErrors[0] )->parse() );
534 
535  return;
536  }
537 
538  $this->mLocalFile = $this->mUpload->getLocalFile();
539 
540  // Check warnings if necessary
541  if ( !$this->mIgnoreWarning ) {
542  $warnings = $this->mUpload->checkWarnings( $user );
543  if ( $this->showUploadWarning( $warnings ) ) {
544  return;
545  }
546  }
547 
548  // This is as late as we can throttle, after expected issues have been handled
549  if ( UploadBase::isThrottled( $user ) ) {
551  $this->msg( 'actionthrottledtext' )->escaped()
552  );
553  return;
554  }
555 
556  // Get the page text if this is not a reupload
557  if ( !$this->mForReUpload ) {
558  $pageText = self::getInitialPageText( $this->mComment, $this->mLicense,
559  $this->mCopyrightStatus, $this->mCopyrightSource, $this->getConfig() );
560  } else {
561  $pageText = false;
562  }
563 
564  $changeTags = $this->getRequest()->getVal( 'wpChangeTags' );
565  if ( $changeTags === null || $changeTags === '' ) {
566  $changeTags = [];
567  } else {
568  $changeTags = array_filter( array_map( 'trim', explode( ',', $changeTags ) ) );
569  }
570 
571  if ( $changeTags ) {
572  $changeTagsStatus = ChangeTags::canAddTagsAccompanyingChange(
573  $changeTags, $user );
574  if ( !$changeTagsStatus->isOK() ) {
575  $this->showUploadError( $this->getOutput()->parseAsInterface(
576  $changeTagsStatus->getWikiText( false, false, $this->getLanguage() )
577  ) );
578 
579  return;
580  }
581  }
582 
583  $status = $this->mUpload->performUpload(
584  $this->mComment,
585  $pageText,
586  $this->mWatchthis,
587  $user,
588  $changeTags
589  );
590 
591  if ( !$status->isGood() ) {
593  $this->getOutput()->parseAsInterface(
594  $status->getWikiText( false, false, $this->getLanguage() )
595  )
596  );
597 
598  return;
599  }
600 
601  // Success, redirect to description page
602  $this->mUploadSuccessful = true;
603  $this->getHookRunner()->onSpecialUploadComplete( $this );
604  $this->getOutput()->redirect( $this->mLocalFile->getTitle()->getFullURL() );
605  }
606 
616  public static function getInitialPageText( $comment = '', $license = '',
617  $copyStatus = '', $source = '', Config $config = null
618  ) {
619  if ( $config === null ) {
620  wfDebug( __METHOD__ . ' called without a Config instance passed to it' );
621  $config = MediaWikiServices::getInstance()->getMainConfig();
622  }
623 
624  $msg = [];
625  $forceUIMsgAsContentMsg = (array)$config->get( MainConfigNames::ForceUIMsgAsContentMsg );
626  /* These messages are transcluded into the actual text of the description page.
627  * Thus, forcing them as content messages makes the upload to produce an int: template
628  * instead of hardcoding it there in the uploader language.
629  */
630  foreach ( [ 'license-header', 'filedesc', 'filestatus', 'filesource' ] as $msgName ) {
631  if ( in_array( $msgName, $forceUIMsgAsContentMsg ) ) {
632  $msg[$msgName] = "{{int:$msgName}}";
633  } else {
634  $msg[$msgName] = wfMessage( $msgName )->inContentLanguage()->text();
635  }
636  }
637 
638  $licenseText = '';
639  if ( $license !== '' ) {
640  $licenseText = '== ' . $msg['license-header'] . " ==\n{{" . $license . "}}\n";
641  }
642 
643  $pageText = $comment . "\n";
644  $headerText = '== ' . $msg['filedesc'] . ' ==';
645  if ( $comment !== '' && !str_contains( $comment, $headerText ) ) {
646  // prepend header to page text unless it's already there (or there is no content)
647  $pageText = $headerText . "\n" . $pageText;
648  }
649 
650  if ( $config->get( MainConfigNames::UseCopyrightUpload ) ) {
651  $pageText .= '== ' . $msg['filestatus'] . " ==\n" . $copyStatus . "\n";
652  $pageText .= $licenseText;
653  $pageText .= '== ' . $msg['filesource'] . " ==\n" . $source;
654  } else {
655  $pageText .= $licenseText;
656  }
657 
658  // allow extensions to modify the content
660  ->onUploadForm_getInitialPageText( $pageText, $msg, $config );
661 
662  return $pageText;
663  }
664 
677  protected function getWatchCheck() {
678  $user = $this->getUser();
679  if ( $this->userOptionsLookup->getBoolOption( $user, 'watchdefault' ) ) {
680  // Watch all edits!
681  return true;
682  }
683 
684  $desiredTitleObj = Title::makeTitleSafe( NS_FILE, $this->mDesiredDestName );
685  if ( $desiredTitleObj instanceof Title &&
686  $this->watchlistManager->isWatched( $user, $desiredTitleObj ) ) {
687  // Already watched, don't change that
688  return true;
689  }
690 
691  $local = $this->localRepo->newFile( $this->mDesiredDestName );
692  if ( $local && $local->exists() ) {
693  // We're uploading a new version of an existing file.
694  // No creation, so don't watch it if we're not already.
695  return false;
696  } else {
697  // New page should get watched if that's our option.
698  return $this->userOptionsLookup->getBoolOption( $user, 'watchcreations' ) ||
699  $this->userOptionsLookup->getBoolOption( $user, 'watchuploads' );
700  }
701  }
702 
708  protected function processVerificationError( $details ) {
709  switch ( $details['status'] ) {
712  $this->showRecoverableUploadError( $this->msg( 'minlength1' )->escaped() );
713  break;
715  $this->showRecoverableUploadError( $this->msg( 'illegalfilename',
716  $details['filtered'] )->parse() );
717  break;
719  $this->showRecoverableUploadError( $this->msg( 'filename-toolong' )->escaped() );
720  break;
722  $this->showRecoverableUploadError( $this->msg( 'filetype-missing' )->parse() );
723  break;
725  $this->showRecoverableUploadError( $this->msg( 'windows-nonascii-filename' )->parse() );
726  break;
727 
730  $this->showUploadError( $this->msg( 'emptyfile' )->escaped() );
731  break;
733  $this->showUploadError( $this->msg( 'largefileserver' )->escaped() );
734  break;
736  $msg = $this->msg( 'filetype-banned-type' );
737  if ( isset( $details['blacklistedExt'] ) ) {
738  $msg->params( $this->getLanguage()->commaList( $details['blacklistedExt'] ) );
739  } else {
740  $msg->params( $details['finalExt'] );
741  }
742  $extensions =
743  array_unique( $this->getConfig()->get( MainConfigNames::FileExtensions ) );
744  $msg->params( $this->getLanguage()->commaList( $extensions ),
745  count( $extensions ) );
746 
747  // Add PLURAL support for the first parameter. This results
748  // in a bit unlogical parameter sequence, but does not break
749  // old translations
750  if ( isset( $details['blacklistedExt'] ) ) {
751  $msg->params( count( $details['blacklistedExt'] ) );
752  } else {
753  $msg->params( 1 );
754  }
755 
756  $this->showUploadError( $msg->parse() );
757  break;
759  unset( $details['status'] );
760  $code = array_shift( $details['details'] );
761  $this->showUploadError( $this->msg( $code, $details['details'] )->parse() );
762  break;
764  if ( is_array( $details['error'] ) ) { # allow hooks to return error details in an array
765  $args = $details['error'];
766  $error = array_shift( $args );
767  } else {
768  $error = $details['error'];
769  $args = null;
770  }
771 
772  $this->showUploadError( $this->msg( $error, $args )->parse() );
773  break;
774  default:
775  throw new UnexpectedValueException( __METHOD__ . ": Unknown value `{$details['status']}`" );
776  }
777  }
778 
784  protected function unsaveUploadedFile() {
785  if ( !( $this->mUpload instanceof UploadFromStash ) ) {
786  return true;
787  }
788  $success = $this->mUpload->unsaveUploadedFile();
789  if ( !$success ) {
790  $this->getOutput()->showFatalError(
791  $this->msg( 'filedeleteerror' )
792  ->params( $this->mUpload->getTempPath() )
793  ->escaped()
794  );
795 
796  return false;
797  } else {
798  return true;
799  }
800  }
801 
811  public static function getExistsWarning( $exists ) {
812  if ( !$exists ) {
813  return '';
814  }
815 
816  $file = $exists['file'];
817  $filename = $file->getTitle()->getPrefixedText();
818  $warnMsg = null;
819 
820  if ( $exists['warning'] == 'exists' ) {
821  // Exact match
822  $warnMsg = wfMessage( 'fileexists', $filename );
823  } elseif ( $exists['warning'] == 'page-exists' ) {
824  // Page exists but file does not
825  $warnMsg = wfMessage( 'filepageexists', $filename );
826  } elseif ( $exists['warning'] == 'exists-normalized' ) {
827  $warnMsg = wfMessage( 'fileexists-extension', $filename,
828  $exists['normalizedFile']->getTitle()->getPrefixedText() );
829  } elseif ( $exists['warning'] == 'thumb' ) {
830  // Swapped argument order compared with other messages for backwards compatibility
831  $warnMsg = wfMessage( 'fileexists-thumbnail-yes',
832  $exists['thumbFile']->getTitle()->getPrefixedText(), $filename );
833  } elseif ( $exists['warning'] == 'thumb-name' ) {
834  // Image w/o '180px-' does not exists, but we do not like these filenames
835  $name = $file->getName();
836  $badPart = substr( $name, 0, strpos( $name, '-' ) + 1 );
837  $warnMsg = wfMessage( 'file-thumbnail-no', $badPart );
838  } elseif ( $exists['warning'] == 'bad-prefix' ) {
839  $warnMsg = wfMessage( 'filename-bad-prefix', $exists['prefix'] );
840  }
841 
842  return $warnMsg ? $warnMsg->page( $file->getTitle() )->parse() : '';
843  }
844 
850  public function getDupeWarning( $dupes ) {
851  if ( !$dupes ) {
852  return '';
853  }
854 
855  $gallery = ImageGalleryBase::factory( false, $this->getContext() );
856  $gallery->setShowBytes( false );
857  $gallery->setShowDimensions( false );
858  foreach ( $dupes as $file ) {
859  $gallery->add( $file->getTitle() );
860  }
861 
862  return '<li>' .
863  $this->msg( 'file-exists-duplicate' )->numParams( count( $dupes ) )->parse() .
864  $gallery->toHTML() . "</li>\n";
865  }
866 
867  protected function getGroupName() {
868  return 'media';
869  }
870 
879  public static function rotationEnabled() {
880  $bitmapHandler = new BitmapHandler();
881  return $bitmapHandler->autoRotateEnabled();
882  }
883 }
884 
889 class_alias( SpecialUpload::class, 'SpecialUpload' );
const NS_FILE
Definition: Defines.php:70
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
$success
Generic handler for bitmap images.
static canAddTagsAccompanyingChange(array $tags, Authority $performer=null, $checkBlock=true)
Is it OK to allow the user to apply all the specified tags at the same time as they edit/make the cha...
Definition: ChangeTags.php:396
An error page which can definitely be safely rendered using the OutputPage.
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition: HTMLForm.php:158
Image gallery.
static factory( $mode=false, IContextSource $context=null)
Get a new image gallery.
Local file in the wiki's own database.
Definition: LocalFile.php:64
Local repository that stores files in the local filesystem and registers them in the wiki's own datab...
Definition: LocalRepo.php:45
static showLogExtract(&$out, $types=[], $page='', $user='', $param=[])
Show log extract.
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Definition: HookRunner.php:569
This class is a collection of static functions that serve two purposes:
Definition: Html.php:57
static errorBox( $html, $heading='', $className='')
Return an error box.
Definition: Html.php:822
A class containing constants representing the names of configuration variables.
const ForceUIMsgAsContentMsg
Name constant for the ForceUIMsgAsContentMsg setting, for use with Config::get()
const UseCopyrightUpload
Name constant for the UseCopyrightUpload setting, for use with Config::get()
const FileExtensions
Name constant for the FileExtensions setting, for use with Config::get()
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
WebRequest clone which takes values from a provided array.
Definition: FauxRequest.php:42
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
Definition: WebRequest.php:50
Parent class for all special pages.
Definition: SpecialPage.php:65
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
getUser()
Shortcut to get the User executing this instance.
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
getPageTitle( $subpage=false)
Get a self-referential title object.
checkReadOnly()
If the wiki is currently in readonly mode, throws a ReadOnlyError.
getConfig()
Shortcut to get main config object.
getContext()
Gets the context this SpecialPage is executed in.
getRequest()
Get the WebRequest being used for this instance.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getOutput()
Get the OutputPage being used for this instance.
getContentLanguage()
Shortcut to get content language.
getLanguage()
Shortcut to get user's language.
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages Per default the message key is the canonical name o...
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
Form for handling uploads and special page.
bool $mForReUpload
The user followed an "overwrite this file" link.
doesWrites()
Indicates whether this special page may perform database writes.
bool $mCancelUpload
The user clicked "Cancel and return to upload form" button.
getWatchCheck()
See if we should check the 'watch this page' checkbox on the form based on the user's preferences and...
bool $mUploadSuccessful
Subclasses can use this to determine whether a file was uploaded.
static rotationEnabled()
Should we rotate images in the preview on Special:Upload.
unsaveUploadedFile()
Remove a temporarily kept file stashed by saveTempUploadedFile().
string $mDesiredDestName
User input variables from the "description" section.
__construct(RepoGroup $repoGroup=null, UserOptionsLookup $userOptionsLookup=null, NamespaceInfo $nsInfo=null, WatchlistManager $watchlistManager=null)
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
string $uploadFormTextTop
Raw html injection point for hooks not using HTMLForm.
static getInitialPageText( $comment='', $license='', $copyStatus='', $source='', Config $config=null)
Get the initial image page text based on a comment and optional file status information.
showUploadForm( $form)
Show the main upload form.
$mIgnoreWarning
User input variables from the root section.
showUploadError( $message)
Show the upload form with error message, but do not stash the file.
processVerificationError( $details)
Provides output to the user for a result of UploadBase::verifyUpload.
string $uploadFormTextAfterSummary
Raw html injection point for hooks not using HTMLForm.
static getExistsWarning( $exists)
Functions for formatting warnings.
showUploadWarning( $warnings)
Stashes the upload, shows the main form, but adds a "continue anyway button".
userCanExecute(User $user)
This page can be shown if uploading is enabled.
getDupeWarning( $dupes)
Construct a warning and a gallery from an array of duplicate files.
getUploadForm( $message='', $sessionKey='', $hideIgnoreWarning=false)
Get an UploadForm instance with title and text properly set.
loadRequest()
Initialize instance variables from request and create an Upload handler.
showRecoverableUploadError( $message)
Stashes the upload and shows the main upload form.
WebRequest FauxRequest $mRequest
Misc variables.
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
Represents a title within MediaWiki.
Definition: Title.php:76
exists( $flags=0)
Check if page exists.
Definition: Title.php:3235
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:650
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:624
Provides access to user options.
internal since 1.36
Definition: User.php:98
getTitle()
Get the Title object that we'll be acting on, as specified in the WebRequest.
Definition: MediaWiki.php:186
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
UploadBase and subclasses are the backend of MediaWiki's file uploads.
Definition: UploadBase.php:55
const EMPTY_FILE
Definition: UploadBase.php:113
static createFromRequest(&$request, $type=null)
Create a form of UploadBase depending on wpSourceType and initializes it.
Definition: UploadBase.php:197
const FILETYPE_MISSING
Definition: UploadBase.php:117
static isEnabled()
Returns true if uploads are enabled.
Definition: UploadBase.php:153
const HOOK_ABORTED
Definition: UploadBase.php:120
const VERIFICATION_ERROR
Definition: UploadBase.php:119
static isAllowed(Authority $performer)
Returns true if the user can use this upload module or else a string identifying the missing permissi...
Definition: UploadBase.php:167
const WINDOWS_NONASCII_FILENAME
Definition: UploadBase.php:122
const FILETYPE_BADTYPE
Definition: UploadBase.php:118
const FILE_TOO_LARGE
Definition: UploadBase.php:121
static isThrottled( $user)
Returns true if the user has surpassed the upload rate limit, false otherwise.
Definition: UploadBase.php:183
const ILLEGAL_FILENAME
Definition: UploadBase.php:115
const MIN_LENGTH_PARTNAME
Definition: UploadBase.php:114
const FILENAME_TOO_LONG
Definition: UploadBase.php:123
Sub class of HTMLForm that provides the form section of SpecialUpload.
Definition: UploadForm.php:35
Implements uploading from previously stored file.
Show an error when the user tries to do something whilst blocked.
Interface for configuration instances.
Definition: Config.php:32
$source
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition: router.php:42