49use Psr\Log\LoggerInterface;
54use Wikimedia\Timestamp\TimestampFormat as TS;
71 private LoggerInterface $log;
82 parent::__construct( $mainModule, $moduleName );
87 $this->watchlistMaxDuration =
89 $this->watchlistManager = $watchlistManager;
90 $this->watchedItemStore = $watchedItemStore;
91 $this->userOptionsLookup = $userOptionsLookup;
92 $this->log = LoggerFactory::getInstance(
'upload' );
97 if ( !UploadBase::isEnabled() ) {
107 $this->mParams[
'async'] = $this->mParams[
'async'] &&
112 if ( !$this->mParams[
'filekey'] && $this->mParams[
'sessionkey'] ) {
113 $this->mParams[
'filekey'] = $this->mParams[
'sessionkey'];
116 if ( !$this->mParams[
'checkstatus'] ) {
125 } elseif ( !$this->mUpload ) {
139 if ( $this->mParams[
'async'] && $this->mParams[
'url'] ) {
140 $status = $this->mUpload->canFetchFile();
142 $status = $this->mUpload->fetchFile();
145 if ( !$status->isGood() ) {
146 $this->log->info(
"Unable to fetch file {filename} for {user} because {status}",
148 'user' => $this->
getUser()->getName(),
149 'status' => (
string)$status,
150 'filename' => $this->mParams[
'filename'] ??
'-',
162 if ( !$this->mParams[
'stash'] ) {
163 $status = $this->mUpload->authorizeUpload( $user );
164 if ( !$status->isGood() ) {
165 $this->dieRecoverableError( $status->getMessages(),
'filename' );
171 $result = $this->getContextResult();
180 if ( $result[
'result'] ===
'Success' ) {
186 $this->mUpload->cleanupTempFile();
199 $services->getJobQueueGroup(),
200 $services->getWatchlistManager(),
201 $services->getWatchedItemStore(),
202 $services->getUserOptionsLookup(),
203 $services->getRepoGroup(),
220 $stashFile = $upload->getStashFile();
222 $info = $this->getUploadImageInfoInternal( $stashFile,
true );
225 $info = $this->getUploadImageInfoInternal( $localFile,
false );
231 private function getUploadImageInfoInternal( File $file,
bool $stashedImageInfos ): array {
232 $result = $this->getResult();
235 if ( $stashedImageInfos ) {
236 $imParam = ApiQueryStashImageInfo::getPropertyNames();
237 $info = ApiQueryStashImageInfo::getInfo(
239 array_fill_keys( $imParam,
true ),
243 $imParam = ApiQueryImageInfo::getPropertyNames( [
'uploadwarning' ] );
244 $info = ApiQueryImageInfo::getInfo(
246 array_fill_keys( $imParam,
true ),
258 private function getContextResult() {
259 $warnings = $this->getApiWarnings();
260 if ( $warnings && !$this->mParams[
'ignorewarnings'] ) {
262 return $this->getWarningsResult( $warnings );
263 } elseif ( $this->mParams[
'chunk'] ) {
265 return $this->getChunkResult( $warnings );
266 } elseif ( $this->mParams[
'stash'] ) {
268 return $this->getStashResult( $warnings );
273 return $this->performUpload( $warnings );
281 private function getStashResult( $warnings ) {
282 $result = [
'result' =>
'Success' ];
283 if ( $warnings && count( $warnings ) > 0 ) {
284 $result[
'warnings'] = $warnings;
288 $this->performStash(
'critical', $result );
298 private function getWarningsResult( $warnings ) {
300 'result' =>
'Warning',
301 'warnings' => $warnings,
306 $this->performStash(
'optional', $result );
318 $configured = $config->
get( MainConfigNames::MinUploadChunkSize );
323 ini_get(
'post_max_size' ),
332 UploadBase::getMaxUploadSize(
'file' ),
333 UploadBase::getMaxPhpUploadSize(),
343 private function getChunkResult( $warnings ) {
346 if ( $warnings && count( $warnings ) > 0 ) {
347 $result[
'warnings'] = $warnings;
350 $chunkUpload = $this->getMain()->getUpload(
'chunk' );
351 $chunkPath = $chunkUpload->getTempName();
352 $chunkSize = $chunkUpload->getSize();
353 $totalSoFar = $this->mParams[
'offset'] + $chunkSize;
354 $minChunkSize = self::getMinUploadChunkSize( $this->getConfig() );
357 if ( $totalSoFar > $this->mParams[
'filesize'] ) {
358 $this->dieWithError(
'apierror-invalid-chunk' );
362 if ( $totalSoFar != $this->mParams[
'filesize'] && $chunkSize < $minChunkSize ) {
363 $this->dieWithError( [
'apierror-chunk-too-small', Message::numParam( $minChunkSize ) ] );
366 if ( $this->mParams[
'offset'] == 0 ) {
367 $this->log->debug(
"Started first chunk of chunked upload of {filename} for {user}",
370 'filename' => $this->mParams[
'filename'] ??
'-',
371 'filesize' => $this->mParams[
'filesize'],
372 'chunkSize' => $chunkSize
375 $filekey = $this->performStash(
'critical' );
377 $filekey = $this->mParams[
'filekey'];
380 $progress = UploadBase::getSessionStatus( $this->
getUser(), $filekey );
383 $this->log->info(
"Stash failed due to no session for {user}",
386 'filename' => $this->mParams[
'filename'] ??
'-',
387 'filekey' => $this->mParams[
'filekey'] ??
'-',
388 'filesize' => $this->mParams[
'filesize'],
389 'chunkSize' => $chunkSize
392 $this->dieWithError(
'apierror-stashfailed-nosession',
'stashfailed' );
393 } elseif ( $progress[
'result'] !==
'Continue' || $progress[
'stage'] !==
'uploading' ) {
394 $this->dieWithError(
'apierror-stashfailed-complete',
'stashfailed' );
397 $status = $this->mUpload->addChunk(
398 $chunkPath, $chunkSize, $this->mParams[
'offset'] );
399 if ( !$status->isGood() ) {
401 'offset' => $this->mUpload->getOffset(),
403 $this->log->info(
"Chunked upload stash failure {status} for {user}",
405 'status' => (
string)$status,
407 'filename' => $this->mParams[
'filename'] ??
'-',
408 'filekey' => $this->mParams[
'filekey'] ??
'-',
409 'filesize' => $this->mParams[
'filesize'],
410 'chunkSize' => $chunkSize,
411 'offset' => $this->mUpload->getOffset()
414 $this->dieStatusWithCode( $status,
'stashfailed', $extradata );
416 $this->log->debug(
"Got chunk for {filename} with offset {offset} for {user}",
419 'filename' => $this->mParams[
'filename'] ??
'-',
420 'filekey' => $this->mParams[
'filekey'] ??
'-',
421 'filesize' => $this->mParams[
'filesize'],
422 'chunkSize' => $chunkSize,
423 'offset' => $this->mUpload->getOffset()
430 if ( $totalSoFar == $this->mParams[
'filesize'] ) {
431 if ( $this->mParams[
'async'] ) {
432 UploadBase::setSessionStatus(
435 [
'result' =>
'Poll',
436 'stage' =>
'queued',
'status' => Status::newGood() ]
442 $this->jobQueueGroup->lazyPush(
new AssembleUploadChunksJob( [
443 'filename' => $this->mParams[
'filename'],
444 'filekey' => $filekey,
445 'filesize' => $this->mParams[
'filesize'],
446 'session' => $this->
getContext()->exportSession()
448 $this->log->info(
"Received final chunk of {filename} for {user}, queuing assemble job",
451 'filename' => $this->mParams[
'filename'] ??
'-',
452 'filekey' => $this->mParams[
'filekey'] ??
'-',
453 'filesize' => $this->mParams[
'filesize'],
454 'chunkSize' => $chunkSize,
457 $result[
'result'] =
'Poll';
458 $result[
'stage'] =
'queued';
460 $this->log->info(
"Received final chunk of {filename} for {user}, assembling immediately",
463 'filename' => $this->mParams[
'filename'] ??
'-',
464 'filekey' => $this->mParams[
'filekey'] ??
'-',
465 'filesize' => $this->mParams[
'filesize'],
466 'chunkSize' => $chunkSize,
470 $status = $this->mUpload->concatenateChunks();
471 if ( !$status->isGood() ) {
472 UploadBase::setSessionStatus(
475 [
'result' =>
'Failure',
'stage' =>
'assembling',
'status' => $status ]
477 $this->log->info(
"Non jobqueue assembly of {filename} failed because {status}",
480 'filename' => $this->mParams[
'filename'] ??
'-',
481 'filekey' => $this->mParams[
'filekey'] ??
'-',
482 'filesize' => $this->mParams[
'filesize'],
483 'chunkSize' => $chunkSize,
484 'status' => (
string)$status
487 $this->dieStatusWithCode( $status,
'stashfailed' );
491 $warnings = $this->getApiWarnings();
493 $result[
'warnings'] = $warnings;
498 UploadBase::setSessionStatus( $this->
getUser(), $filekey,
false );
499 $this->mUpload->stash->removeFile( $filekey );
500 $filekey = $this->mUpload->getStashFile()->getFileKey();
502 $result[
'result'] =
'Success';
505 UploadBase::setSessionStatus(
509 'result' =>
'Continue',
510 'stage' =>
'uploading',
511 'offset' => $totalSoFar,
512 'status' => Status::newGood(),
515 $result[
'result'] =
'Continue';
516 $result[
'offset'] = $totalSoFar;
519 $result[
'filekey'] = $filekey;
536 private function performStash( $failureMode, &$data = [] ) {
537 if ( $failureMode ===
'optional' && $this->mUpload->skipStashFileAttempt() ) {
541 $isPartial = (bool)$this->mParams[
'chunk'];
543 $status = $this->mUpload->tryStashFile( $this->
getUser(), $isPartial );
545 if ( $status->isGood() && !$status->getValue() ) {
547 $status->fatal(
new ApiMessage(
'apierror-stashinvalidfile',
'stashfailed' ) );
549 }
catch ( Exception $e ) {
550 $debugMessage =
'Stashing temporary file failed: ' . get_class( $e ) .
' ' . $e->getMessage();
551 $this->log->info( $debugMessage,
554 'filename' => $this->mParams[
'filename'] ??
'-',
555 'filekey' => $this->mParams[
'filekey'] ??
'-'
559 $status = Status::newFatal( $this->getErrorFormatter()->getMessageFromException(
560 $e, [
'wrap' =>
new ApiMessage(
'apierror-stashexception',
'stashfailed' ) ]
564 if ( $status->isGood() ) {
565 $stashFile = $status->getValue();
566 $data[
'filekey'] = $stashFile->getFileKey();
568 $data[
'sessionkey'] = $data[
'filekey'];
569 return $data[
'filekey'];
572 if ( $status->getMessage()->getKey() ===
'uploadstash-exception' ) {
575 [ $exceptionType, $message ] = $status->getMessage()->getParams();
576 $debugMessage =
'Stashing temporary file failed: ' . $exceptionType .
' ' . $message;
577 $this->log->info( $debugMessage,
580 'filename' => $this->mParams[
'filename'] ??
'-',
581 'filekey' => $this->mParams[
'filekey'] ??
'-'
586 $this->log->info(
"Stash upload failure {status}",
588 'status' => (
string)$status,
590 'filename' => $this->mParams[
'filename'] ??
'-',
591 'filekey' => $this->mParams[
'filekey'] ??
'-'
595 if ( $failureMode !==
'optional' ) {
596 $this->dieStatus( $status );
598 $data[
'stasherrors'] = $this->getErrorFormatter()->arrayFromStatus( $status );
612 private function dieRecoverableError( $errors, $parameter =
null ): never {
614 $this->performStash(
'optional', $data );
617 $data[
'invalidparameter'] = $parameter;
621 foreach ( $errors as $error ) {
622 $msg = ApiMessage::create( $error );
623 $msg->setApiData( $msg->getApiData() + $data );
626 $this->dieStatus( $sv );
641 foreach ( $status->getMessages() as $error ) {
642 $msg = ApiMessage::create( $error, $overrideCode );
643 if ( $moreExtraData ) {
644 $msg->setApiData( $msg->getApiData() + $moreExtraData );
648 $this->dieStatus( $sv );
660 if ( !$this->mParams[
'chunk'] ) {
661 $this->requireOnlyOneParameter( $this->mParams,
662 'filekey',
'file',
'url' );
666 if ( $this->mParams[
'checkstatus'] &&
667 ( $this->mParams[
'filekey'] || ( $this->mParams[
'url'] && $this->mParams[
'filename'] ) )
669 $statusKey = $this->mParams[
'filekey'] ?: UploadFromUrl::getCacheKey( $this->mParams );
670 $progress = UploadBase::getSessionStatus( $this->
getUser(), $statusKey );
672 $this->log->info(
"Cannot check upload status due to missing upload session for {user}",
674 'user' => $this->
getUser()->getName(),
675 'url' => $this->mParams[
'url'] ??
'-',
676 'filename' => $this->mParams[
'filename'] ??
'-',
677 'filekey' => $this->mParams[
'filekey'] ??
'-'
680 $this->dieWithError(
'apierror-upload-missingresult',
'missingresult' );
681 } elseif ( !$progress[
'status']->isGood() ) {
682 $this->dieStatusWithCode( $progress[
'status'],
'stashfailed' );
684 if ( isset( $progress[
'status']->value[
'verification'] ) ) {
685 $this->checkVerification( $progress[
'status']->value[
'verification'] );
687 if ( isset( $progress[
'status']->value[
'warnings'] ) ) {
688 $warnings = $this->transformWarnings( $progress[
'status']->value[
'warnings'] );
690 $progress[
'warnings'] = $warnings;
694 unset( $progress[
'status'] );
696 if ( $progress[
'result'] ===
'Success' ) {
697 if ( isset( $progress[
'filekey'] ) ) {
699 $file = $this->localRepo->getUploadStash()->getFile( $progress[
'filekey'] );
701 $imageinfo = $this->getUploadImageInfoInternal( $file,
true );
703 } elseif ( isset( $progress[
'filename'] ) && isset( $progress[
'timestamp'] ) ) {
705 $file = $this->localRepo->findFile(
706 $progress[
'filename'],
707 [
'time' => $progress[
'timestamp'],
'latest' =>
true ]
710 $imageinfo = $this->getUploadImageInfoInternal( $file,
false );
712 } elseif ( isset( $progress[
'imageinfo'] ) ) {
714 $imageinfo = $progress[
'imageinfo'];
716 unset( $progress[
'imageinfo'] );
719 $this->getResult()->addValue(
null, $this->getModuleName(), $progress );
723 $this->getResult()->addValue( $this->getModuleName(),
'imageinfo', $imageinfo );
730 if ( $this->mParams[
'filename'] ===
null ) {
731 $this->dieWithError( [
'apierror-missingparam',
'filename' ] );
734 if ( $this->mParams[
'chunk'] ) {
737 if ( isset( $this->mParams[
'filekey'] ) ) {
738 if ( $this->mParams[
'offset'] === 0 ) {
739 $this->dieWithError(
'apierror-upload-filekeynotallowed',
'filekeynotallowed' );
743 $this->mUpload->continueChunks(
744 $this->mParams[
'filename'],
745 $this->mParams[
'filekey'],
746 $this->getMain()->getUpload(
'chunk' )
749 if ( $this->mParams[
'offset'] !== 0 ) {
750 $this->dieWithError(
'apierror-upload-filekeyneeded',
'filekeyneeded' );
754 $this->mUpload->initialize(
755 $this->mParams[
'filename'],
756 $this->getMain()->getUpload(
'chunk' )
759 } elseif ( isset( $this->mParams[
'filekey'] ) ) {
761 if ( !UploadFromStash::isValidKey( $this->mParams[
'filekey'] ) ) {
762 $this->dieWithError(
'apierror-invalid-file-key' );
768 $this->mUpload->initialize(
769 $this->mParams[
'filekey'], $this->mParams[
'filename'], !$this->mParams[
'async']
771 } elseif ( isset( $this->mParams[
'file'] ) ) {
775 if ( $this->mParams[
'async'] ) {
776 $this->dieWithError(
'apierror-cannot-async-upload-file' );
780 $this->mUpload->initialize(
781 $this->mParams[
'filename'],
782 $this->getMain()->getUpload(
'file' )
784 } elseif ( isset( $this->mParams[
'url'] ) ) {
786 if ( !UploadFromUrl::isEnabled() ) {
787 $this->dieWithError(
'copyuploaddisabled' );
790 if ( !UploadFromUrl::isAllowedHost( $this->mParams[
'url'] ) ) {
791 $this->dieWithError(
'apierror-copyuploadbaddomain' );
794 if ( !UploadFromUrl::isAllowedUrl( $this->mParams[
'url'] ) ) {
795 $this->dieWithError(
'apierror-copyuploadbadurl' );
801 $this->mUpload->
initialize( $this->mParams[
'filename'],
802 $this->mParams[
'url'], !$this->mParams[
'async'] );
815 $permission = $this->mUpload->isAllowed( $user );
817 if ( $permission !==
true ) {
818 if ( !$user->isNamed() ) {
819 $this->dieWithError( [
'apierror-mustbeloggedin', $this->msg(
'action-upload' ) ] );
822 $this->dieStatus( User::newFatalPermissionDeniedStatus( $permission ) );
826 if ( $user->isBlockedFromUpload() ) {
828 $this->dieBlocked( $user->getBlock() );
836 if ( $this->mParams[
'chunk'] ) {
837 $maxSize = UploadBase::getMaxUploadSize(
'file' );
838 if ( $this->mParams[
'filesize'] > $maxSize ) {
839 $this->dieWithError(
'file-too-large' );
841 if ( !$this->mUpload->getTitle() ) {
842 $this->dieWithError(
'illegal-filename' );
846 $verification = $this->mUpload->validateName();
847 if ( $verification ===
true ) {
850 } elseif ( $this->mParams[
'async'] && ( $this->mParams[
'filekey'] || $this->mParams[
'url'] ) ) {
854 $verification = $this->mUpload->validateName();
855 if ( $verification ===
true ) {
859 wfDebug( __METHOD__ .
" about to verify" );
861 $verification = $this->mUpload->verifyUpload();
863 if ( $verification[
'status'] === UploadBase::OK ) {
866 $this->log->info(
"File verification of {filename} failed for {user} because {result}",
868 'user' => $this->
getUser()->getName(),
869 'resultCode' => $verification[
'status'],
870 'result' => $this->mUpload->getVerificationErrorCode( $verification[
'status'] ),
871 'filename' => $this->mParams[
'filename'] ??
'-',
872 'details' => $verification[
'details'] ??
''
878 $this->checkVerification( $verification );
887 $status = $this->mUpload->convertVerifyErrorToStatus( $verification );
888 if ( $status->isRecoverableError() ) {
889 $this->dieRecoverableError( [ $status->asApiMessage() ], $status->getInvalidParameter() );
892 $this->dieWithError( $status->asApiMessage() );
904 $warnings = UploadBase::makeWarningsSerializable(
905 $this->mUpload->checkWarnings( $this->getUser() )
908 return $this->transformWarnings( $warnings );
914 ApiResult::setIndexedTagName( $warnings,
'warning' );
916 if ( isset( $warnings[
'duplicate'] ) ) {
917 $dupes = array_column( $warnings[
'duplicate'],
'fileName' );
918 ApiResult::setIndexedTagName( $dupes,
'duplicate' );
919 $warnings[
'duplicate'] = $dupes;
922 if ( isset( $warnings[
'exists'] ) ) {
923 $warning = $warnings[
'exists'];
924 unset( $warnings[
'exists'] );
925 $localFile = $warning[
'normalizedFile'] ?? $warning[
'file'];
926 $warnings[$warning[
'warning']] = $localFile[
'fileName'];
929 if ( isset( $warnings[
'no-change'] ) ) {
930 $file = $warnings[
'no-change'];
931 unset( $warnings[
'no-change'] );
933 $warnings[
'nochange'] = [
934 'timestamp' =>
wfTimestamp( TS::ISO_8601, $file[
'timestamp'] )
938 if ( isset( $warnings[
'duplicate-version'] ) ) {
940 foreach ( $warnings[
'duplicate-version'] as $dupe ) {
942 'timestamp' =>
wfTimestamp( TS::ISO_8601, $dupe[
'timestamp'] )
945 unset( $warnings[
'duplicate-version'] );
947 ApiResult::setIndexedTagName( $dupes,
'ver' );
948 $warnings[
'duplicateversions'] = $dupes;
962 $this->log->info(
"Upload stashing of {filename} failed for {user} because {error}",
964 'user' => $this->
getUser()->getName(),
965 'error' => get_class( $e ),
966 'filename' => $this->mParams[
'filename'] ??
'-',
967 'filekey' => $this->mParams[
'filekey'] ??
'-'
971 switch ( get_class( $e ) ) {
972 case UploadStashFileNotFoundException::class:
973 $wrap =
'apierror-stashedfilenotfound';
975 case UploadStashBadPathException::class:
976 $wrap =
'apierror-stashpathinvalid';
978 case UploadStashFileException::class:
979 $wrap =
'apierror-stashfilestorage';
981 case UploadStashZeroLengthFileException::class:
982 $wrap =
'apierror-stashzerolength';
984 case UploadStashNotLoggedInException::class:
985 return StatusValue::newFatal( ApiMessage::create(
986 [
'apierror-mustbeloggedin', $this->msg(
'action-upload' ) ],
'stashnotloggedin'
988 case UploadStashWrongOwnerException::class:
989 $wrap =
'apierror-stashwrongowner';
991 case UploadStashNoSuchKeyException::class:
992 $wrap =
'apierror-stashnosuchfilekey';
995 $wrap = [
'uploadstash-exception', get_class( $e ) ];
998 return StatusValue::newFatal(
999 $this->getErrorFormatter()->getMessageFromException( $e, [
'wrap' => $wrap ] )
1012 $this->mParams[
'text'] ??= $this->mParams[
'comment'];
1015 $file = $this->mUpload->getLocalFile();
1017 $title = $file->getTitle();
1025 $this->mParams[
'watchlist'], $title, $user,
'watchdefault'
1028 if ( !$watch && $this->mParams[
'watchlist'] ==
'preferences' && !$file->exists() ) {
1037 if ( $this->mParams[
'watch'] ) {
1041 if ( $this->mParams[
'tags'] ) {
1042 $status = ChangeTags::canAddTagsAccompanyingChange( $this->mParams[
'tags'], $this->
getAuthority() );
1043 if ( !$status->isOK() ) {
1044 $this->dieStatus( $status );
1050 if ( $this->mParams[
'async'] ) {
1052 if ( $this->mParams[
'filekey'] ) {
1055 'filename' => $this->mParams[
'filename'],
1056 'filekey' => $this->mParams[
'filekey'],
1057 'comment' => $this->mParams[
'comment'],
1058 'tags' => $this->mParams[
'tags'] ?? [],
1059 'text' => $this->mParams[
'text'],
1061 'watchlistexpiry' => $watchlistExpiry,
1062 'session' => $this->
getContext()->exportSession(),
1063 'ignorewarnings' => $this->mParams[
'ignorewarnings']
1066 } elseif ( $this->mParams[
'url'] ) {
1069 'filename' => $this->mParams[
'filename'],
1070 'url' => $this->mParams[
'url'],
1071 'comment' => $this->mParams[
'comment'],
1072 'tags' => $this->mParams[
'tags'] ?? [],
1073 'text' => $this->mParams[
'text'],
1075 'watchlistexpiry' => $watchlistExpiry,
1076 'session' => $this->
getContext()->exportSession(),
1077 'ignorewarnings' => $this->mParams[
'ignorewarnings']
1081 $this->dieWithError(
'apierror-no-async-support',
'publishfailed' );
1087 $cacheKey =
$job->getCacheKey();
1090 $progress = UploadBase::getSessionStatus( $this->
getUser(), $cacheKey );
1091 if ( $progress && $progress[
'result'] ===
'Poll' ) {
1092 $this->dieWithError(
'apierror-upload-inprogress',
'publishfailed' );
1094 UploadBase::setSessionStatus(
1097 [
'result' =>
'Poll',
'stage' =>
'queued',
'status' => Status::newGood() ]
1100 $this->jobQueueGroup->push(
$job );
1101 $this->log->info(
"Sending publish job of {filename} for {user}",
1103 'user' => $this->
getUser()->getName(),
1104 'filename' => $this->mParams[
'filename'] ??
'-'
1107 $result[
'result'] =
'Poll';
1108 $result[
'stage'] =
'queued';
1110 $status = $this->mUpload->performUpload(
1111 $this->mParams[
'comment'],
1112 $this->mParams[
'text'],
1115 $this->mParams[
'tags'] ?? [],
1119 if ( !$status->isGood() ) {
1120 $this->log->info(
"Non-async API upload publish failed for {user} because {status}",
1122 'user' => $this->
getUser()->getName(),
1123 'filename' => $this->mParams[
'filename'] ??
'-',
1124 'filekey' => $this->mParams[
'filekey'] ??
'-',
1125 'status' => (
string)$status
1128 $this->dieRecoverableError( $status->getMessages() );
1130 $result[
'result'] =
'Success';
1133 $result[
'filename'] = $file->getName();
1134 if ( $warnings && count( $warnings ) > 0 ) {
1135 $result[
'warnings'] = $warnings;
1155 ParamValidator::PARAM_TYPE =>
'string',
1158 ParamValidator::PARAM_DEFAULT =>
''
1161 ParamValidator::PARAM_TYPE =>
'tags',
1162 ParamValidator::PARAM_ISMULTI =>
true,
1165 ParamValidator::PARAM_TYPE =>
'text',
1168 ParamValidator::PARAM_DEFAULT =>
false,
1169 ParamValidator::PARAM_DEPRECATED =>
true,
1182 'ignorewarnings' =>
false,
1184 ParamValidator::PARAM_TYPE =>
'upload',
1189 ParamValidator::PARAM_DEPRECATED =>
true,
1194 ParamValidator::PARAM_TYPE =>
'integer',
1195 IntegerDef::PARAM_MIN => 0,
1196 IntegerDef::PARAM_MAX => UploadBase::getMaxUploadSize(
'file' ),
1199 ParamValidator::PARAM_TYPE =>
'integer',
1200 IntegerDef::PARAM_MIN => 0,
1203 ParamValidator::PARAM_TYPE =>
'upload',
1207 'checkstatus' =>
false,
1221 'action=upload&filename=Wiki.png' .
1222 '&url=http%3A//upload.wikimedia.org/wikipedia/en/b/bc/Wiki.png&token=123ABC'
1223 =>
'apihelp-upload-example-url',
1224 'action=upload&filename=Wiki.png&filekey=filekey&ignorewarnings=1&token=123ABC'
1225 =>
'apihelp-upload-example-filekey',
1231 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Upload';
1236class_alias( ApiUpload::class,
'ApiUpload' );
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfShorthandToInteger(?string $string='', int $default=-1)
Converts shorthand byte notation to integer form.
wfTimestamp( $outputtype=TS::UNIX, $ts=0)
Get a timestamp string in one of various formats.
if(!defined('MW_SETUP_CALLBACK'))
This is the main API class, used for both external and internal processing.
A class containing constants representing the names of configuration variables.
const EnableAsyncUploads
Name constant for the EnableAsyncUploads setting, for use with Config::get()
const WatchlistExpiry
Name constant for the WatchlistExpiry setting, for use with Config::get()
const EnableAsyncUploadsByURL
Name constant for the EnableAsyncUploadsByURL setting, for use with Config::get()
const WatchlistExpiryMaxDuration
Name constant for the WatchlistExpiryMaxDuration setting, for use with Config::get()
Generic operation result class Has warning/error list, boolean status and arbitrary value.
static newGood( $value=null)
Factory function for good results.
trait ApiWatchlistTrait
An ApiWatchlistTrait adds class properties and convenience methods for APIs that allow you to watch a...
if(count( $args)< 1) $job