25 namespace MediaWiki\Specials;
50 use UnexpectedValueException;
81 parent::__construct(
'Upload',
'upload' );
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();
145 $this->mRequest = $request = $this->
getRequest();
146 $this->mSourceType = $request->getVal(
'wpSourceType',
'file' );
148 $this->mUploadClicked = $request->wasPosted()
149 && ( $request->getCheck(
'wpUpload' )
150 || $request->getCheck(
'wpUploadIgnoreWarning' ) );
153 $this->mDesiredDestName = $request->getText(
'wpDestFile' );
154 if ( !$this->mDesiredDestName && $request->getFileName(
'wpUploadFile' ) !== null ) {
155 $this->mDesiredDestName = $request->getFileName(
'wpUploadFile' );
157 $this->mLicense = $request->getText(
'wpLicense' );
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' );
166 $this->mForReUpload = $request->getBool(
'wpForReUpload' );
168 $commentDefault =
'';
169 $commentMsg = $this->
msg(
'upload-default-description' )->inContentLanguage();
170 if ( !$this->mForReUpload && !$commentMsg->isDisabled() ) {
171 $commentDefault = $commentMsg->plain();
173 $this->mComment = $request->getText(
'wpUploadDescription', $commentDefault );
175 $this->mCancelUpload = $request->getCheck(
'wpCancelUpload' )
176 || $request->getCheck(
'wpReUpload' );
179 $token = $request->getVal(
'wpEditToken' );
180 $this->mTokenOk = $this->
getUser()->matchEditToken( $token );
182 $this->uploadFormTextTop =
'';
183 $this->uploadFormTextAfterSummary =
'';
207 # Check uploading enabled
209 throw new ErrorPageError(
'uploaddisabled',
'uploaddisabledtext' );
217 if ( $permissionRequired !==
true ) {
222 if ( $user->isBlockedFromUpload() ) {
227 $this->getLanguage(),
228 $this->getRequest()->getIP()
232 # Check whether we actually want to allow changing stuff
237 # Unsave the temporary file in case this was a cancelled upload
239 # Something went wrong, so unsaveUploadedFile showed a warning
243 # Process upload or show a form
245 $this->mTokenOk && !$this->mCancelUpload &&
246 ( $this->mUpload && $this->mUploadClicked )
250 # Backwards compatibility hook
251 if ( !$this->
getHookRunner()->onUploadForm_initial( $this ) ) {
252 wfDebug(
"Hook 'UploadForm:initial' broke output of the upload form" );
260 if ( $this->mUpload ) {
261 $this->mUpload->cleanupTempFile();
286 protected function getUploadForm( $message =
'', $sessionKey =
'', $hideIgnoreWarning =
false ) {
291 'forreupload' => $this->mForReUpload,
292 'sessionkey' => $sessionKey,
293 'hideignorewarning' => $hideIgnoreWarning,
294 'destwarningack' => (
bool)$this->mDestWarningAck,
296 'description' => $this->mComment,
297 'texttop' => $this->uploadFormTextTop,
298 'textaftersummary' => $this->uploadFormTextAfterSummary,
299 'destfile' => $this->mDesiredDestName,
310 # Check the token, but only if necessary
312 !$this->mTokenOk && !$this->mCancelUpload &&
313 ( $this->mUpload && $this->mUploadClicked )
315 $form->addPreText( $this->
msg(
'session_fail_preview' )->parse() );
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'
322 if ( $desiredTitleObj instanceof
Title && !$desiredTitleObj->
exists() ) {
326 'conds' => [
'log_action != ' . $this->localRepo->getReplicaDB()->addQuotes(
'revision' ) ],
327 'showIfEmpty' =>
false,
328 'msgKey' => [
'upload-recreate-warning' ] ]
331 $form->addPreText( $delNotice );
334 $form->addPreText(
'<div id="uploadtext">' .
335 $this->
msg(
'uploadtext', [ $this->mDesiredDestName ] )->parseAsBlock() .
337 # Add upload error message
338 $form->addPreText( $message );
341 $uploadFooter = $this->
msg(
'uploadfooter' );
342 if ( !$uploadFooter->isDisabled() ) {
343 $form->addPostText(
'<div id="mw-upload-footer-message">'
344 . $uploadFooter->parseAsBlock() .
"</div>\n" );
362 $stashStatus = $this->mUpload->tryStashFile( $this->
getUser() );
363 if ( $stashStatus->isGood() ) {
364 $sessionKey = $stashStatus->getValue()->getFileKey();
365 $uploadWarning =
'upload-tryagain';
368 $uploadWarning =
'upload-tryagain-nostash';
370 $message =
'<h2>' . $this->
msg(
'uploaderror' )->escaped() .
'</h2>' .
374 $form->setSubmitText( $this->
msg( $uploadWarning )->escaped() );
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
391 if ( !$warnings || ( count( $warnings ) == 1
392 && isset( $warnings[
'exists'] )
393 && ( $this->mDestWarningAck || $this->mForReUpload ) )
398 $stashStatus = $this->mUpload->tryStashFile( $this->
getUser() );
399 if ( $stashStatus->isGood() ) {
400 $sessionKey = $stashStatus->getValue()->getFileKey();
401 $uploadWarning =
'uploadwarning-text';
404 $uploadWarning =
'uploadwarning-text-nostash';
408 $this->
getOutput()->addModuleStyles(
'mediawiki.special' );
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' ) {
417 if ( $warning ==
'exists' ) {
419 } elseif ( $warning ==
'no-change' ) {
421 $filename =
$file->getTitle()->getPrefixedText();
422 $msg =
"\t<li>" . $this->
msg(
'fileexists-no-change', $filename )->parse() .
"</li>\n";
423 } elseif ( $warning ==
'duplicate-version' ) {
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
434 $llink = $linkRenderer->makeKnownLink(
436 $this->
msg(
'deletionlog' )->text(),
443 $msg =
"\t<li>" . $this->
msg(
'filewasdeleted' )->rawParams( $llink )->parse() .
"</li>\n";
444 } elseif ( $warning ==
'duplicate' ) {
446 } elseif ( $warning ==
'duplicate-archive' ) {
447 if ( $args ===
'' ) {
448 $msg =
"\t<li>" . $this->
msg(
'file-deleted-duplicate-notitle' )->parse()
451 $msg =
"\t<li>" . $this->
msg(
'file-deleted-duplicate',
456 if ( $args ===
true ) {
458 } elseif ( !is_array( $args ) ) {
461 $msg =
"\t<li>" . $this->
msg( $warning, $args )->parse() .
"</li>\n";
463 $warningHtml .= $msg;
465 $warningHtml .=
"</ul></div>\n";
466 $warningHtml .= $this->
msg( $uploadWarning )->parseAsBlock();
468 $form = $this->
getUploadForm( $warningHtml, $sessionKey,
true );
469 $form->setSubmitTextMsg(
'upload-tryagain' );
471 'name' =>
'wpUploadIgnoreWarning',
472 'value' => $this->
msg(
'ignorewarning' )->text()
475 'name' =>
'wpCancelUpload',
476 'value' => $this->
msg(
'reuploaddesc' )->text()
481 # Indicate that we showed a form
491 $message =
'<h2>' . $this->
msg(
'uploadwarning' )->escaped() .
'</h2>' .
502 $status = $this->mUpload->fetchFile();
503 if ( !$status->isOK() ) {
505 $status->getWikiText(
false,
false, $this->getLanguage() )
510 if ( !$this->
getHookRunner()->onUploadForm_BeforeProcessing( $this ) ) {
511 wfDebug(
"Hook 'UploadForm:BeforeProcessing' broke processing the file." );
521 $details = $this->mUpload->verifyUpload();
530 $permErrors = $this->mUpload->verifyTitlePermissions( $user );
531 if ( $permErrors !==
true ) {
532 $code = array_shift( $permErrors[0] );
538 $this->mLocalFile = $this->mUpload->getLocalFile();
541 if ( !$this->mIgnoreWarning ) {
542 $warnings = $this->mUpload->checkWarnings( $user );
551 $this->
msg(
'actionthrottledtext' )->escaped()
557 if ( !$this->mForReUpload ) {
559 $this->mCopyrightStatus, $this->mCopyrightSource, $this->
getConfig() );
564 $changeTags = $this->
getRequest()->getVal(
'wpChangeTags' );
565 if ( $changeTags ===
null || $changeTags ===
'' ) {
568 $changeTags = array_filter( array_map(
'trim', explode(
',', $changeTags ) ) );
573 $changeTags, $user );
574 if ( !$changeTagsStatus->isOK() ) {
576 $changeTagsStatus->getWikiText(
false,
false, $this->getLanguage() )
583 $status = $this->mUpload->performUpload(
591 if ( !$status->isGood() ) {
594 $status->getWikiText(
false,
false, $this->getLanguage() )
602 $this->mUploadSuccessful =
true;
604 $this->
getOutput()->redirect( $this->mLocalFile->getTitle()->getFullURL() );
619 if ( $config ===
null ) {
620 wfDebug( __METHOD__ .
' called without a Config instance passed to it' );
630 foreach ( [
'license-header',
'filedesc',
'filestatus',
'filesource' ] as $msgName ) {
631 if ( in_array( $msgName, $forceUIMsgAsContentMsg ) ) {
632 $msg[$msgName] =
"{{int:$msgName}}";
634 $msg[$msgName] =
wfMessage( $msgName )->inContentLanguage()->text();
639 if ( $license !==
'' ) {
640 $licenseText =
'== ' . $msg[
'license-header'] .
" ==\n{{" . $license .
"}}\n";
643 $pageText = $comment .
"\n";
644 $headerText =
'== ' . $msg[
'filedesc'] .
' ==';
645 if ( $comment !==
'' && !str_contains( $comment, $headerText ) ) {
647 $pageText = $headerText .
"\n" . $pageText;
651 $pageText .=
'== ' . $msg[
'filestatus'] .
" ==\n" . $copyStatus .
"\n";
652 $pageText .= $licenseText;
653 $pageText .=
'== ' . $msg[
'filesource'] .
" ==\n" .
$source;
655 $pageText .= $licenseText;
660 ->onUploadForm_getInitialPageText( $pageText, $msg, $config );
679 if ( $this->userOptionsLookup->getBoolOption( $user,
'watchdefault' ) ) {
685 if ( $desiredTitleObj instanceof
Title &&
686 $this->watchlistManager->isWatched( $user, $desiredTitleObj ) ) {
691 $local = $this->localRepo->newFile( $this->mDesiredDestName );
692 if ( $local && $local->exists() ) {
698 return $this->userOptionsLookup->getBoolOption( $user,
'watchcreations' ) ||
699 $this->userOptionsLookup->getBoolOption( $user,
'watchuploads' );
709 switch ( $details[
'status'] ) {
716 $details[
'filtered'] )->parse() );
736 $msg = $this->
msg(
'filetype-banned-type' );
737 if ( isset( $details[
'blacklistedExt'] ) ) {
738 $msg->params( $this->
getLanguage()->commaList( $details[
'blacklistedExt'] ) );
740 $msg->params( $details[
'finalExt'] );
744 $msg->params( $this->
getLanguage()->commaList( $extensions ),
745 count( $extensions ) );
750 if ( isset( $details[
'blacklistedExt'] ) ) {
751 $msg->params( count( $details[
'blacklistedExt'] ) );
759 unset( $details[
'status'] );
760 $code = array_shift( $details[
'details'] );
764 if ( is_array( $details[
'error'] ) ) { # allow hooks to
return error details in an array
765 $args = $details[
'error'];
766 $error = array_shift( $args );
768 $error = $details[
'error'];
775 throw new UnexpectedValueException( __METHOD__ .
": Unknown value `{$details['status']}`" );
788 $success = $this->mUpload->unsaveUploadedFile();
791 $this->
msg(
'filedeleteerror' )
792 ->params( $this->mUpload->getTempPath() )
816 $file = $exists[
'file'];
817 $filename =
$file->getTitle()->getPrefixedText();
820 if ( $exists[
'warning'] ==
'exists' ) {
822 $warnMsg =
wfMessage(
'fileexists', $filename );
823 } elseif ( $exists[
'warning'] ==
'page-exists' ) {
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' ) {
831 $warnMsg =
wfMessage(
'fileexists-thumbnail-yes',
832 $exists[
'thumbFile']->
getTitle()->getPrefixedText(), $filename );
833 } elseif ( $exists[
'warning'] ==
'thumb-name' ) {
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'] );
842 return $warnMsg ? $warnMsg->page(
$file->getTitle() )->parse() :
'';
856 $gallery->setShowBytes(
false );
857 $gallery->setShowDimensions(
false );
858 foreach ( $dupes as
$file ) {
859 $gallery->add(
$file->getTitle() );
863 $this->
msg(
'file-exists-duplicate' )->numParams( count( $dupes ) )->parse() .
864 $gallery->toHTML() .
"</li>\n";
881 return $bitmapHandler->autoRotateEnabled();
889 class_alias( SpecialUpload::class,
'SpecialUpload' );
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.
Generic handler for bitmap images.
An error page which can definitely be safely rendered using the OutputPage.
static factory( $mode=false, IContextSource $context=null)
Get a new image gallery.
Local file in the wiki's own database.
Local repository that stores files in the local filesystem and registers them in the wiki's own datab...
static showLogExtract(&$out, $types=[], $page='', $user='', $param=[])
Show log extract.
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()
Parent class for all special pages.
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.
Show an error when a user tries to do something they do not have the necessary permissions for.
Prioritized list of file repositories.
UploadBase and subclasses are the backend of MediaWiki's file uploads.
static createFromRequest(&$request, $type=null)
Create a form of UploadBase depending on wpSourceType and initializes it.
static isEnabled()
Returns true if uploads are enabled.
static isAllowed(Authority $performer)
Returns true if the user can use this upload module or else a string identifying the missing permissi...
const WINDOWS_NONASCII_FILENAME
static isThrottled( $user)
Returns true if the user has surpassed the upload rate limit, false otherwise.
const MIN_LENGTH_PARTNAME
Implements uploading from previously stored file.
Show an error when the user tries to do something whilst blocked.
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.