42 $request = $this->
getMain()->getRequest();
44 $this->mParams[
'async'] = ( $this->mParams[
'async'] &&
45 $this->
getConfig()->get(
'EnableAsyncUploads' ) );
47 $this->mParams[
'file'] = $request->getFileName(
'file' );
48 $this->mParams[
'chunk'] = $request->getFileName(
'chunk' );
51 if ( !$this->mParams[
'filekey'] && $this->mParams[
'sessionkey'] ) {
52 $this->mParams[
'filekey'] = $this->mParams[
'sessionkey'];
59 } elseif ( !isset( $this->mUpload ) ) {
60 $this->
dieDebug( __METHOD__,
'No upload module set' );
71 $status = $this->mUpload->fetchFile();
72 if ( !$status->isGood() ) {
77 if ( $this->mParams[
'chunk'] ) {
79 if ( $this->mParams[
'filesize'] > $maxSize ) {
82 if ( !$this->mUpload->getTitle() ) {
85 } elseif ( $this->mParams[
'async'] && $this->mParams[
'filekey'] ) {
88 wfDebug( __METHOD__ .
" about to verify\n" );
95 if ( !$this->mParams[
'stash'] ) {
96 $permErrors = $this->mUpload->verifyTitlePermissions( $user );
97 if ( $permErrors !==
true ) {
112 if ( $result[
'result'] ===
'Success' ) {
113 $imageinfo = $this->mUpload->getImageInfo( $this->
getResult() );
118 $this->mUpload->cleanupTempFile();
127 if ( $warnings && !$this->mParams[
'ignorewarnings'] ) {
130 } elseif ( $this->mParams[
'chunk'] ) {
133 } elseif ( $this->mParams[
'stash'] ) {
156 $result[
'result'] =
'Success';
157 if ( $warnings && count( $warnings ) > 0 ) {
158 $result[
'warnings'] = $warnings;
174 $result[
'result'] =
'Warning';
175 $result[
'warnings'] = $warnings;
191 if ( $warnings && count( $warnings ) > 0 ) {
192 $result[
'warnings'] = $warnings;
195 $request = $this->
getMain()->getRequest();
196 $chunkPath = $request->getFileTempname(
'chunk' );
197 $chunkSize = $request->getUpload(
'chunk' )->getSize();
198 $totalSoFar = $this->mParams[
'offset'] + $chunkSize;
199 $minChunkSize = $this->
getConfig()->get(
'MinUploadChunkSize' );
202 if ( $totalSoFar > $this->mParams[
'filesize'] ) {
207 if ( $totalSoFar != $this->mParams[
'filesize'] && $chunkSize < $minChunkSize ) {
211 if ( $this->mParams[
'offset'] == 0 ) {
214 $filekey = $this->mParams[
'filekey'];
220 $this->
dieWithError(
'apierror-stashfailed-nosession',
'stashfailed' );
221 } elseif ( $progress[
'result'] !==
'Continue' || $progress[
'stage'] !==
'uploading' ) {
222 $this->
dieWithError(
'apierror-stashfailed-complete',
'stashfailed' );
225 $status = $this->mUpload->addChunk(
226 $chunkPath, $chunkSize, $this->mParams[
'offset'] );
227 if ( !$status->isGood() ) {
229 'offset' => $this->mUpload->getOffset(),
237 if ( $totalSoFar == $this->mParams[
'filesize'] ) {
238 if ( $this->mParams[
'async'] ) {
242 [
'result' =>
'Poll',
243 'stage' =>
'queued',
'status' => Status::newGood() ]
246 Title::makeTitle(
NS_FILE, $filekey ),
248 'filename' => $this->mParams[
'filename'],
249 'filekey' => $filekey,
253 $result[
'result'] =
'Poll';
254 $result[
'stage'] =
'queued';
256 $status = $this->mUpload->concatenateChunks();
257 if ( !$status->isGood() ) {
261 [
'result' =>
'Failure',
'stage' =>
'assembling',
'status' => $status ]
269 $result[
'warnings'] = $warnings;
275 $this->mUpload->stash->removeFile( $filekey );
276 $filekey = $this->mUpload->getStashFile()->getFileKey();
278 $result[
'result'] =
'Success';
285 'result' =>
'Continue',
286 'stage' =>
'uploading',
287 'offset' => $totalSoFar,
288 'status' => Status::newGood(),
291 $result[
'result'] =
'Continue';
292 $result[
'offset'] = $totalSoFar;
295 $result[
'filekey'] = $filekey;
313 $isPartial = (bool)$this->mParams[
'chunk'];
315 $status = $this->mUpload->tryStashFile( $this->
getUser(), $isPartial );
317 if ( $status->isGood() && !$status->getValue() ) {
319 $status->fatal(
new ApiMessage(
'apierror-stashinvalidfile',
'stashfailed' ) );
321 }
catch ( Exception $e ) {
322 $debugMessage =
'Stashing temporary file failed: ' . get_class( $e ) .
' ' . $e->getMessage();
323 wfDebug( __METHOD__ .
' ' . $debugMessage .
"\n" );
325 $e, [
'wrap' =>
new ApiMessage(
'apierror-stashexception',
'stashfailed' ) ]
329 if ( $status->isGood() ) {
330 $stashFile = $status->getValue();
331 $data[
'filekey'] = $stashFile->getFileKey();
333 $data[
'sessionkey'] = $data[
'filekey'];
334 return $data[
'filekey'];
337 if ( $status->getMessage()->getKey() ===
'uploadstash-exception' ) {
340 list( $exceptionType, $message ) = $status->getMessage()->getParams();
341 $debugMessage =
'Stashing temporary file failed: ' . $exceptionType .
' ' . $message;
342 wfDebug( __METHOD__ .
' ' . $debugMessage .
"\n" );
346 if ( $failureMode !==
'optional' ) {
367 $data[
'invalidparameter'] = $parameter;
370 $sv = StatusValue::newGood();
371 foreach ( $errors as $error ) {
373 $msg->setApiData( $msg->getApiData() + $data );
389 $sv = StatusValue::newGood();
390 foreach ( $status->getErrors() as $error ) {
392 if ( $moreExtraData ) {
393 $msg->setApiData( $msg->getApiData() + $moreExtraData );
408 $request = $this->
getMain()->getRequest();
411 if ( !$this->mParams[
'chunk'] ) {
413 'filekey',
'file',
'url' );
417 if ( $this->mParams[
'filekey'] && $this->mParams[
'checkstatus'] ) {
420 $this->
dieWithError(
'apierror-upload-missingresult',
'missingresult' );
421 } elseif ( !$progress[
'status']->isGood() ) {
424 if ( isset( $progress[
'status']->value[
'verification'] ) ) {
427 if ( isset( $progress[
'status']->value[
'warnings'] ) ) {
430 $progress[
'warnings'] = $warnings;
433 unset( $progress[
'status'] );
435 if ( isset( $progress[
'imageinfo'] ) ) {
436 $imageinfo = $progress[
'imageinfo'];
437 unset( $progress[
'imageinfo'] );
451 if ( is_null( $this->mParams[
'filename'] ) ) {
452 $this->
dieWithError( [
'apierror-missingparam',
'filename' ] );
455 if ( $this->mParams[
'chunk'] ) {
458 if ( isset( $this->mParams[
'filekey'] ) ) {
459 if ( $this->mParams[
'offset'] === 0 ) {
460 $this->
dieWithError(
'apierror-upload-filekeynotallowed',
'filekeynotallowed' );
464 $this->mUpload->continueChunks(
465 $this->mParams[
'filename'],
466 $this->mParams[
'filekey'],
467 $request->getUpload(
'chunk' )
470 if ( $this->mParams[
'offset'] !== 0 ) {
471 $this->
dieWithError(
'apierror-upload-filekeyneeded',
'filekeyneeded' );
475 $this->mUpload->initialize(
476 $this->mParams[
'filename'],
477 $request->getUpload(
'chunk' )
480 } elseif ( isset( $this->mParams[
'filekey'] ) ) {
489 $this->mUpload->initialize(
490 $this->mParams[
'filekey'], $this->mParams[
'filename'], !$this->mParams[
'async']
492 } elseif ( isset( $this->mParams[
'file'] ) ) {
496 if ( $this->mParams[
'async'] ) {
497 $this->
dieWithError(
'apierror-cannot-async-upload-file' );
501 $this->mUpload->initialize(
502 $this->mParams[
'filename'],
503 $request->getUpload(
'file' )
505 } elseif ( isset( $this->mParams[
'url'] ) ) {
520 $this->mUpload->
initialize( $this->mParams[
'filename'],
521 $this->mParams[
'url'] );
534 $permission = $this->mUpload->isAllowed( $user );
536 if ( $permission !==
true ) {
537 if ( !$user->isLoggedIn() ) {
538 $this->
dieWithError( [
'apierror-mustbeloggedin', $this->
msg(
'action-upload' ) ] );
541 $this->
dieStatus( User::newFatalPermissionDeniedStatus( $permission ) );
545 if ( $user->isBlockedFromUpload() ) {
550 if ( $user->isBlockedGlobally() ) {
559 $verification = $this->mUpload->verifyUpload();
572 switch ( $verification[
'status'] ) {
580 'illegal-filename',
null, [
'filename' => $verification[
'filtered'] ]
604 'filetype' => $verification[
'finalExt'],
605 'allowed' => array_values( array_unique( $this->
getConfig()->
get(
'FileExtensions' ) ) )
607 $extensions = array_unique( $this->
getConfig()->
get(
'FileExtensions' ) );
609 'filetype-banned-type',
612 count( $extensions ),
615 ApiResult::setIndexedTagName( $extradata[
'allowed'],
'ext' );
617 if ( isset( $verification[
'blacklistedExt'] ) ) {
619 $msg[4] = count( $verification[
'blacklistedExt'] );
620 $extradata[
'blacklisted'] = array_values( $verification[
'blacklistedExt'] );
621 ApiResult::setIndexedTagName( $extradata[
'blacklisted'],
'ext' );
623 $msg[1] = $verification[
'finalExt'];
627 $this->
dieWithError( $msg,
'filetype-banned', $extradata );
633 $details = array_merge( [ $msg->getKey() ], $msg->getParams() );
635 $details = $verification[
'details'];
637 ApiResult::setIndexedTagName( $details,
'detail' );
638 $msg->setApiData( $msg->getApiData() + [
'details' => $details ] );
644 $msg = $verification[
'error'] ===
'' ?
'hookaborted' : $verification[
'error'];
645 $this->
dieWithError( $msg,
'hookaborted', [
'details' => $verification[
'error'] ] );
648 $this->
dieWithError(
'apierror-unknownerror-nocode',
'unknown-error',
649 [
'details' => [
'code' => $verification[
'status'] ] ] );
670 ApiResult::setIndexedTagName( $warnings,
'warning' );
672 if ( isset( $warnings[
'duplicate'] ) ) {
674 foreach ( $warnings[
'duplicate'] as $dupe ) {
675 $dupes[] = $dupe[
'fileName'];
677 ApiResult::setIndexedTagName( $dupes,
'duplicate' );
678 $warnings[
'duplicate'] = $dupes;
681 if ( isset( $warnings[
'exists'] ) ) {
682 $warning = $warnings[
'exists'];
683 unset( $warnings[
'exists'] );
684 $localFile = $warning[
'normalizedFile'] ?? $warning[
'file'];
685 $warnings[$warning[
'warning']] = $localFile[
'fileName'];
688 if ( isset( $warnings[
'no-change'] ) ) {
689 $file = $warnings[
'no-change'];
690 unset( $warnings[
'no-change'] );
692 $warnings[
'nochange'] = [
697 if ( isset( $warnings[
'duplicate-version'] ) ) {
699 foreach ( $warnings[
'duplicate-version'] as $dupe ) {
701 'timestamp' =>
wfTimestamp( TS_ISO_8601, $dupe[
'timestamp'] )
704 unset( $warnings[
'duplicate-version'] );
706 ApiResult::setIndexedTagName( $dupes,
'ver' );
707 $warnings[
'duplicateversions'] = $dupes;
721 switch ( get_class( $e ) ) {
722 case UploadStashFileNotFoundException::class:
723 $wrap =
'apierror-stashedfilenotfound';
725 case UploadStashBadPathException::class:
726 $wrap =
'apierror-stashpathinvalid';
728 case UploadStashFileException::class:
729 $wrap =
'apierror-stashfilestorage';
731 case UploadStashZeroLengthFileException::class:
732 $wrap =
'apierror-stashzerolength';
734 case UploadStashNotLoggedInException::class:
736 [
'apierror-mustbeloggedin', $this->
msg(
'action-upload' ) ],
'stashnotloggedin'
738 case UploadStashWrongOwnerException::class:
739 $wrap =
'apierror-stashwrongowner';
741 case UploadStashNoSuchKeyException::class:
742 $wrap =
'apierror-stashnosuchfilekey';
745 $wrap = [
'uploadstash-exception', get_class( $e ) ];
748 return StatusValue::newFatal(
762 if ( is_null( $this->mParams[
'text'] ) ) {
763 $this->mParams[
'text'] = $this->mParams[
'comment'];
767 $file = $this->mUpload->getLocalFile();
775 $this->mParams[
'watchlist'],
$file->getTitle(),
'watchdefault'
778 if ( !$watch && $this->mParams[
'watchlist'] ==
'preferences' && !
$file->exists() ) {
786 if ( $this->mParams[
'watch'] ) {
790 if ( $this->mParams[
'tags'] ) {
792 if ( !$status->isOK() ) {
799 if ( $this->mParams[
'async'] ) {
801 if ( $progress && $progress[
'result'] ===
'Poll' ) {
802 $this->
dieWithError(
'apierror-upload-inprogress',
'publishfailed' );
806 $this->mParams[
'filekey'],
807 [
'result' =>
'Poll',
'stage' =>
'queued',
'status' => Status::newGood() ]
810 Title::makeTitle(
NS_FILE, $this->mParams[
'filename'] ),
812 'filename' => $this->mParams[
'filename'],
813 'filekey' => $this->mParams[
'filekey'],
814 'comment' => $this->mParams[
'comment'],
815 'tags' => $this->mParams[
'tags'],
816 'text' => $this->mParams[
'text'],
821 $result[
'result'] =
'Poll';
822 $result[
'stage'] =
'queued';
825 $status = $this->mUpload->performUpload( $this->mParams[
'comment'],
826 $this->mParams[
'text'], $watch, $this->
getUser(), $this->mParams[
'tags'] );
828 if ( !$status->isGood() ) {
831 $result[
'result'] =
'Success';
834 $result[
'filename'] =
$file->getName();
835 if ( $warnings && count( $warnings ) > 0 ) {
836 $result[
'warnings'] = $warnings;
877 'ignorewarnings' =>
false,
902 'checkstatus' =>
false,
914 'action=upload&filename=Wiki.png' .
915 '&url=http%3A//upload.wikimedia.org/wikipedia/en/b/bc/Wiki.png&token=123ABC'
916 =>
'apihelp-upload-example-url',
917 'action=upload&filename=Wiki.png&filekey=filekey&ignorewarnings=1&token=123ABC'
918 =>
'apihelp-upload-example-filekey',
923 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Upload';
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
This abstract class implements many basic API functions, and is the base of all API classes.
const PARAM_DEPRECATED
(boolean) Is the parameter deprecated (will show a warning)?
const PARAM_MAX
(integer) Max value allowed for the parameter, for PARAM_TYPE 'integer' and 'limit'.
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
static dieDebug( $method, $message)
Internal code errors should be reported with this method.
getMain()
Get the main module.
const PARAM_TYPE
(string|string[]) Either an array of allowed value strings, or a string type as described below.
getErrorFormatter()
Get the error formatter.
const PARAM_DFLT
(null|boolean|integer|string) Default value of the parameter.
const PARAM_MIN
(integer) Lowest value allowed for the parameter, for PARAM_TYPE 'integer' and 'limit'.
dieBlocked(AbstractBlock $block)
Throw an ApiUsageException, which will (if uncaught) call the main module's error handler and die wit...
getResult()
Get the result object.
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
getWatchlistValue( $watchlist, $titleObj, $userOption=null)
Return true if we're to watch the page, false if not, null if no change.
getModuleName()
Get the name of the module being executed by this instance.
dieStatus(StatusValue $status)
Throw an ApiUsageException based on the Status object.
requireOnlyOneParameter( $params, $required)
Die if none or more than one of a certain set of parameters is set and not false.
const PARAM_ISMULTI
(boolean) Accept multiple pipe-separated values for this parameter (e.g.
Extension of Message implementing IApiMessage.
static create( $msg, $code=null, array $data=null)
Create an IApiMessage for the message.
performStash( $failureMode, &$data=null)
Stash the file and add the file key, or error information if it fails, to the data.
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
checkPermissions( $user)
Checks that the user has permissions to perform this upload.
dieRecoverableError( $errors, $parameter=null)
Throw an error that the user can recover from by providing a better value for $parameter.
verifyUpload()
Performs file verification, dies on error.
UploadBase UploadFromChunks $mUpload
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
transformWarnings( $warnings)
handleStashException( $e)
Handles a stash exception, giving a useful error to the user.
getHelpUrls()
Return links to more detailed help pages about the module.
dieStatusWithCode( $status, $overrideCode, $moreExtraData=null)
Like dieStatus(), but always uses $overrideCode for the error code, unless the code comes from IApiMe...
isWriteMode()
Indicates whether this module requires write mode.
getWarningsResult( $warnings)
Get Warnings Result.
getContextResult()
Get an upload result based on upload context.
getChunkResult( $warnings)
Get the result of a chunk upload.
getExamplesMessages()
Returns usage examples for this module.
selectUploadModule()
Select an upload module and set it to mUpload.
getStashResult( $warnings)
Get Stash Result, throws an exception if the file could not be stashed.
needsToken()
Returns the token type this module requires in order to execute.
performUpload( $warnings)
Perform the actual upload.
checkVerification(array $verification)
Performs file verification, dies on error.
mustBePosted()
Indicates whether this module must be called with a POST request.
getApiWarnings()
Check warnings.
Assemble the segments of a chunked upload.
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
exportSession()
Export the resolved user IP, HTTP headers, user ID, and session ID.
getContext()
Get the base IContextSource object.
static listParam(array $list, $type='text')
Upload a file from the upload stash into the local file repo.
UploadBase and subclasses are the backend of MediaWiki's file uploads.
static makeWarningsSerializable( $warnings)
Convert the warnings array returned by checkWarnings() to something that can be serialized.
static getSessionStatus(User $user, $statusKey)
Get the current status of a chunked upload (used for polling)
static isEnabled()
Returns true if uploads are enabled.
const WINDOWS_NONASCII_FILENAME
static getMaxUploadSize( $forType=null)
Get the MediaWiki maximum uploaded file size for given type of upload, based on $wgMaxUploadSize.
static isThrottled( $user)
Returns true if the user has surpassed the upload rate limit, false otherwise.
const MIN_LENGTH_PARTNAME
static setSessionStatus(User $user, $statusKey, $value)
Set the current status of a chunked upload (used for polling)
Implements uploading from chunks.
Implements regular file uploads.
Implements uploading from previously stored file.
Implements uploading from a HTTP resource.
initialize( $name, $url)
Entry point for API upload.
static isAllowedHost( $url)
Checks whether the URL is for an allowed host The domains in the whitelist can include wildcard chara...
static isAllowedUrl( $url)
Checks whether the URL is not allowed.
static isEnabled()
Checks if the upload from URL feature is enabled.
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.