43use Psr\Log\LoggerInterface;
79 private LoggerInterface $log;
88 parent::__construct( $mainModule, $moduleName );
89 $this->jobQueueGroup = $jobQueueGroup;
93 $this->watchlistMaxDuration =
95 $this->watchlistManager = $watchlistManager;
96 $this->userOptionsLookup = $userOptionsLookup;
97 $this->log = LoggerFactory::getInstance(
'upload' );
102 if ( !UploadBase::isEnabled() ) {
111 $this->mParams[
'async'] = ( $this->mParams[
'async'] &&
115 if ( !$this->mParams[
'filekey'] && $this->mParams[
'sessionkey'] ) {
116 $this->mParams[
'filekey'] = $this->mParams[
'sessionkey'];
119 if ( !$this->mParams[
'checkstatus'] ) {
127 } elseif ( !isset( $this->mUpload ) ) {
128 $this->
dieDebug( __METHOD__,
'No upload module set' );
140 if ( $this->mParams[
'async'] && $this->mParams[
'url'] ) {
141 $status = $this->mUpload->canFetchFile();
143 $status = $this->mUpload->fetchFile();
146 if ( !$status->isGood() ) {
147 $this->log->info(
"Unable to fetch file {filename} for {user} because {status}",
149 'user' => $this->
getUser()->getName(),
150 'status' => (
string)$status,
151 'filename' => $this->mParams[
'filename'] ??
'-',
163 if ( !$this->mParams[
'stash'] ) {
164 $permErrors = $this->mUpload->verifyTitlePermissions( $user );
165 if ( $permErrors !==
true ) {
166 $this->dieRecoverableError( $permErrors,
'filename' );
172 $result = $this->getContextResult();
181 if ( $result[
'result'] ===
'Success' ) {
187 $this->mUpload->cleanupTempFile();
196 $services->getJobQueueGroup(),
197 $services->getWatchlistManager(),
198 $services->getUserOptionsLookup()
219 $result = $this->getResult();
228 array_fill_keys( $imParam,
true ),
236 array_fill_keys( $imParam,
true ),
248 private function getContextResult() {
249 $warnings = $this->getApiWarnings();
250 if ( $warnings && !$this->mParams[
'ignorewarnings'] ) {
252 return $this->getWarningsResult( $warnings );
253 } elseif ( $this->mParams[
'chunk'] ) {
255 return $this->getChunkResult( $warnings );
256 } elseif ( $this->mParams[
'stash'] ) {
258 return $this->getStashResult( $warnings );
263 return $this->performUpload( $warnings );
271 private function getStashResult( $warnings ) {
273 $result[
'result'] =
'Success';
274 if ( $warnings && count( $warnings ) > 0 ) {
275 $result[
'warnings'] = $warnings;
279 $this->performStash(
'critical', $result );
289 private function getWarningsResult( $warnings ) {
291 $result[
'result'] =
'Warning';
292 $result[
'warnings'] = $warnings;
295 $this->performStash(
'optional', $result );
307 $configured = $config->
get( MainConfigNames::MinUploadChunkSize );
312 ini_get(
'post_max_size' ),
321 UploadBase::getMaxUploadSize(
'file' ),
322 UploadBase::getMaxPhpUploadSize(),
332 private function getChunkResult( $warnings ) {
335 if ( $warnings && count( $warnings ) > 0 ) {
336 $result[
'warnings'] = $warnings;
339 $chunkUpload = $this->getMain()->getUpload(
'chunk' );
340 $chunkPath = $chunkUpload->getTempName();
341 $chunkSize = $chunkUpload->getSize();
342 $totalSoFar = $this->mParams[
'offset'] + $chunkSize;
343 $minChunkSize = self::getMinUploadChunkSize( $this->getConfig() );
346 if ( $totalSoFar > $this->mParams[
'filesize'] ) {
347 $this->dieWithError(
'apierror-invalid-chunk' );
351 if ( $totalSoFar != $this->mParams[
'filesize'] && $chunkSize < $minChunkSize ) {
352 $this->dieWithError( [
'apierror-chunk-too-small', Message::numParam( $minChunkSize ) ] );
355 if ( $this->mParams[
'offset'] == 0 ) {
356 $this->log->debug(
"Started first chunk of chunked upload of {filename} for {user}",
359 'filename' => $this->mParams[
'filename'] ??
'-',
360 'filesize' => $this->mParams[
'filesize'],
361 'chunkSize' => $chunkSize
364 $filekey = $this->performStash(
'critical' );
366 $filekey = $this->mParams[
'filekey'];
372 $this->log->info(
"Stash failed due to no session for {user}",
375 'filename' => $this->mParams[
'filename'] ??
'-',
376 'filekey' => $this->mParams[
'filekey'] ??
'-',
377 'filesize' => $this->mParams[
'filesize'],
378 'chunkSize' => $chunkSize
381 $this->dieWithError(
'apierror-stashfailed-nosession',
'stashfailed' );
382 } elseif ( $progress[
'result'] !==
'Continue' || $progress[
'stage'] !==
'uploading' ) {
383 $this->dieWithError(
'apierror-stashfailed-complete',
'stashfailed' );
386 $status = $this->mUpload->addChunk(
387 $chunkPath, $chunkSize, $this->mParams[
'offset'] );
388 if ( !$status->isGood() ) {
390 'offset' => $this->mUpload->getOffset(),
392 $this->log->info(
"Chunked upload stash failure {status} for {user}",
394 'status' => (
string)$status,
396 'filename' => $this->mParams[
'filename'] ??
'-',
397 'filekey' => $this->mParams[
'filekey'] ??
'-',
398 'filesize' => $this->mParams[
'filesize'],
399 'chunkSize' => $chunkSize,
400 'offset' => $this->mUpload->getOffset()
403 $this->dieStatusWithCode( $status,
'stashfailed', $extradata );
405 $this->log->debug(
"Got chunk for {filename} with offset {offset} for {user}",
408 'filename' => $this->mParams[
'filename'] ??
'-',
409 'filekey' => $this->mParams[
'filekey'] ??
'-',
410 'filesize' => $this->mParams[
'filesize'],
411 'chunkSize' => $chunkSize,
412 'offset' => $this->mUpload->getOffset()
419 if ( $totalSoFar == $this->mParams[
'filesize'] ) {
420 if ( $this->mParams[
'async'] ) {
424 [
'result' =>
'Poll',
425 'stage' =>
'queued',
'status' => Status::newGood() ]
432 'filename' => $this->mParams[
'filename'],
433 'filekey' => $filekey,
434 'filesize' => $this->mParams[
'filesize'],
435 'session' => $this->
getContext()->exportSession()
437 $this->log->info(
"Received final chunk of {filename} for {user}, queuing assemble job",
440 'filename' => $this->mParams[
'filename'] ??
'-',
441 'filekey' => $this->mParams[
'filekey'] ??
'-',
442 'filesize' => $this->mParams[
'filesize'],
443 'chunkSize' => $chunkSize,
446 $result[
'result'] =
'Poll';
447 $result[
'stage'] =
'queued';
449 $this->log->info(
"Received final chunk of {filename} for {user}, assembling immediately",
452 'filename' => $this->mParams[
'filename'] ??
'-',
453 'filekey' => $this->mParams[
'filekey'] ??
'-',
454 'filesize' => $this->mParams[
'filesize'],
455 'chunkSize' => $chunkSize,
459 $status = $this->mUpload->concatenateChunks();
460 if ( !$status->isGood() ) {
464 [
'result' =>
'Failure',
'stage' =>
'assembling',
'status' => $status ]
466 $this->log->info(
"Non jobqueue assembly of {filename} failed because {status}",
469 'filename' => $this->mParams[
'filename'] ??
'-',
470 'filekey' => $this->mParams[
'filekey'] ??
'-',
471 'filesize' => $this->mParams[
'filesize'],
472 'chunkSize' => $chunkSize,
473 'status' => (
string)$status
476 $this->dieStatusWithCode( $status,
'stashfailed' );
480 $warnings = $this->getApiWarnings();
482 $result[
'warnings'] = $warnings;
488 $this->mUpload->stash->removeFile( $filekey );
489 $filekey = $this->mUpload->getStashFile()->getFileKey();
491 $result[
'result'] =
'Success';
498 'result' =>
'Continue',
499 'stage' =>
'uploading',
500 'offset' => $totalSoFar,
501 'status' => Status::newGood(),
504 $result[
'result'] =
'Continue';
505 $result[
'offset'] = $totalSoFar;
508 $result[
'filekey'] = $filekey;
525 private function performStash( $failureMode, &$data =
null ) {
526 $isPartial = (bool)$this->mParams[
'chunk'];
528 $status = $this->mUpload->tryStashFile( $this->
getUser(), $isPartial );
530 if ( $status->isGood() && !$status->getValue() ) {
532 $status->fatal(
new ApiMessage(
'apierror-stashinvalidfile',
'stashfailed' ) );
534 }
catch ( Exception $e ) {
535 $debugMessage =
'Stashing temporary file failed: ' . get_class( $e ) .
' ' . $e->getMessage();
536 $this->log->info( $debugMessage,
539 'filename' => $this->mParams[
'filename'] ??
'-',
540 'filekey' => $this->mParams[
'filekey'] ??
'-'
544 $status = Status::newFatal( $this->getErrorFormatter()->getMessageFromException(
545 $e, [
'wrap' =>
new ApiMessage(
'apierror-stashexception',
'stashfailed' ) ]
549 if ( $status->isGood() ) {
550 $stashFile = $status->getValue();
551 $data[
'filekey'] = $stashFile->getFileKey();
553 $data[
'sessionkey'] = $data[
'filekey'];
554 return $data[
'filekey'];
557 if ( $status->getMessage()->getKey() ===
'uploadstash-exception' ) {
560 [ $exceptionType, $message ] = $status->getMessage()->getParams();
561 $debugMessage =
'Stashing temporary file failed: ' . $exceptionType .
' ' . $message;
562 $this->log->info( $debugMessage,
565 'filename' => $this->mParams[
'filename'] ??
'-',
566 'filekey' => $this->mParams[
'filekey'] ??
'-'
571 $this->log->info(
"Stash upload failure {status}",
573 'status' => (
string)$status,
575 'filename' => $this->mParams[
'filename'] ??
'-',
576 'filekey' => $this->mParams[
'filekey'] ??
'-'
580 if ( $failureMode !==
'optional' ) {
581 $this->dieStatus( $status );
583 $data[
'stasherrors'] = $this->getErrorFormatter()->arrayFromStatus( $status );
598 private function dieRecoverableError( $errors, $parameter =
null ) {
599 $this->performStash(
'optional', $data );
602 $data[
'invalidparameter'] = $parameter;
606 foreach ( $errors as $error ) {
607 $msg = ApiMessage::create( $error );
608 $msg->setApiData( $msg->getApiData() + $data );
611 $this->dieStatus( $sv );
625 $sv = StatusValue::newGood();
626 foreach ( $status->getMessages() as $error ) {
627 $msg = ApiMessage::create( $error, $overrideCode );
628 if ( $moreExtraData ) {
629 $msg->setApiData( $msg->getApiData() + $moreExtraData );
633 $this->dieStatus( $sv );
645 if ( !$this->mParams[
'chunk'] ) {
646 $this->requireOnlyOneParameter( $this->mParams,
647 'filekey',
'file',
'url' );
651 if ( $this->mParams[
'checkstatus'] && ( $this->mParams[
'filekey'] || $this->mParams[
'url'] ) ) {
652 $statusKey = $this->mParams[
'filekey'] ?: UploadFromUrl::getCacheKey( $this->mParams );
653 $progress = UploadBase::getSessionStatus( $this->
getUser(), $statusKey );
655 $this->log->info(
"Cannot check upload status due to missing upload session for {user}",
657 'user' => $this->
getUser()->getName(),
658 'filename' => $this->mParams[
'filename'] ??
'-',
659 'filekey' => $this->mParams[
'filekey'] ??
'-'
662 $this->dieWithError(
'apierror-upload-missingresult',
'missingresult' );
663 } elseif ( !$progress[
'status']->isGood() ) {
664 $this->dieStatusWithCode( $progress[
'status'],
'stashfailed' );
666 if ( isset( $progress[
'status']->value[
'verification'] ) ) {
667 $this->checkVerification( $progress[
'status']->value[
'verification'] );
669 if ( isset( $progress[
'status']->value[
'warnings'] ) ) {
670 $warnings = $this->transformWarnings( $progress[
'status']->value[
'warnings'] );
672 $progress[
'warnings'] = $warnings;
675 unset( $progress[
'status'] );
677 if ( isset( $progress[
'imageinfo'] ) ) {
678 $imageinfo = $progress[
'imageinfo'];
679 unset( $progress[
'imageinfo'] );
682 $this->getResult()->addValue(
null, $this->getModuleName(), $progress );
686 $this->getResult()->addValue( $this->getModuleName(),
'imageinfo', $imageinfo );
693 if ( $this->mParams[
'filename'] ===
null ) {
694 $this->dieWithError( [
'apierror-missingparam',
'filename' ] );
697 if ( $this->mParams[
'chunk'] ) {
700 if ( isset( $this->mParams[
'filekey'] ) ) {
701 if ( $this->mParams[
'offset'] === 0 ) {
702 $this->dieWithError(
'apierror-upload-filekeynotallowed',
'filekeynotallowed' );
706 $this->mUpload->continueChunks(
707 $this->mParams[
'filename'],
708 $this->mParams[
'filekey'],
712 if ( $this->mParams[
'offset'] !== 0 ) {
713 $this->dieWithError(
'apierror-upload-filekeyneeded',
'filekeyneeded' );
717 $this->mUpload->initialize(
718 $this->mParams[
'filename'],
722 } elseif ( isset( $this->mParams[
'filekey'] ) ) {
724 if ( !UploadFromStash::isValidKey( $this->mParams[
'filekey'] ) ) {
725 $this->dieWithError(
'apierror-invalid-file-key' );
731 $this->mUpload->initialize(
732 $this->mParams[
'filekey'], $this->mParams[
'filename'], !$this->mParams[
'async']
734 } elseif ( isset( $this->mParams[
'file'] ) ) {
738 if ( $this->mParams[
'async'] ) {
739 $this->dieWithError(
'apierror-cannot-async-upload-file' );
743 $this->mUpload->initialize(
744 $this->mParams[
'filename'],
747 } elseif ( isset( $this->mParams[
'url'] ) ) {
749 if ( !UploadFromUrl::isEnabled() ) {
750 $this->dieWithError(
'copyuploaddisabled' );
753 if ( !UploadFromUrl::isAllowedHost( $this->mParams[
'url'] ) ) {
754 $this->dieWithError(
'apierror-copyuploadbaddomain' );
757 if ( !UploadFromUrl::isAllowedUrl( $this->mParams[
'url'] ) ) {
758 $this->dieWithError(
'apierror-copyuploadbadurl' );
762 $this->mUpload->
initialize( $this->mParams[
'filename'],
763 $this->mParams[
'url'] );
776 $permission = $this->mUpload->isAllowed( $user );
778 if ( $permission !==
true ) {
779 if ( !$user->isNamed() ) {
780 $this->dieWithError( [
'apierror-mustbeloggedin', $this->msg(
'action-upload' ) ] );
783 $this->dieStatus( User::newFatalPermissionDeniedStatus( $permission ) );
787 if ( $user->isBlockedFromUpload() ) {
789 $this->dieBlocked( $user->getBlock() );
797 if ( $this->mParams[
'chunk'] ) {
798 $maxSize = UploadBase::getMaxUploadSize();
799 if ( $this->mParams[
'filesize'] > $maxSize ) {
800 $this->dieWithError(
'file-too-large' );
802 if ( !$this->mUpload->getTitle() ) {
803 $this->dieWithError(
'illegal-filename' );
807 $verification = $this->mUpload->validateName();
808 if ( $verification ===
true ) {
811 } elseif ( $this->mParams[
'async'] && ( $this->mParams[
'filekey'] || $this->mParams[
'url'] ) ) {
815 $verification = $this->mUpload->validateName();
816 if ( $verification ===
true ) {
820 wfDebug( __METHOD__ .
" about to verify" );
822 $verification = $this->mUpload->verifyUpload();
824 if ( $verification[
'status'] === UploadBase::OK ) {
827 $this->log->info(
"File verification of {filename} failed for {user} because {result}",
829 'user' => $this->
getUser()->getName(),
830 'resultCode' => $verification[
'status'],
831 'result' => $this->mUpload->getVerificationErrorCode( $verification[
'status'] ),
832 'filename' => $this->mParams[
'filename'] ??
'-',
833 'details' => $verification[
'details'] ??
''
839 $this->checkVerification( $verification );
848 switch ( $verification[
'status'] ) {
850 case UploadBase::MIN_LENGTH_PARTNAME:
851 $this->dieRecoverableError( [
'filename-tooshort' ],
'filename' );
853 case UploadBase::ILLEGAL_FILENAME:
854 $this->dieRecoverableError(
855 [ ApiMessage::create(
856 'illegal-filename',
null, [
'filename' => $verification[
'filtered'] ]
860 case UploadBase::FILENAME_TOO_LONG:
861 $this->dieRecoverableError( [
'filename-toolong' ],
'filename' );
863 case UploadBase::FILETYPE_MISSING:
864 $this->dieRecoverableError( [
'filetype-missing' ],
'filename' );
866 case UploadBase::WINDOWS_NONASCII_FILENAME:
867 $this->dieRecoverableError( [
'windows-nonascii-filename' ],
'filename' );
870 case UploadBase::EMPTY_FILE:
871 $this->dieWithError(
'empty-file' );
873 case UploadBase::FILE_TOO_LARGE:
874 $this->dieWithError(
'file-too-large' );
877 case UploadBase::FILETYPE_BADTYPE:
879 'filetype' => $verification[
'finalExt'],
880 'allowed' => array_values( array_unique(
881 $this->getConfig()->
get( MainConfigNames::FileExtensions ) ) )
884 array_unique( $this->getConfig()->
get( MainConfigNames::FileExtensions ) );
886 'filetype-banned-type',
888 Message::listParam( $extensions,
'comma' ),
889 count( $extensions ),
892 ApiResult::setIndexedTagName( $extradata[
'allowed'],
'ext' );
894 if ( isset( $verification[
'blacklistedExt'] ) ) {
895 $msg[1] = Message::listParam( $verification[
'blacklistedExt'],
'comma' );
896 $msg[4] = count( $verification[
'blacklistedExt'] );
897 $extradata[
'blacklisted'] = array_values( $verification[
'blacklistedExt'] );
898 ApiResult::setIndexedTagName( $extradata[
'blacklisted'],
'ext' );
900 $msg[1] = $verification[
'finalExt'];
904 $this->dieWithError( $msg,
'filetype-banned', $extradata );
907 case UploadBase::VERIFICATION_ERROR:
908 $msg = ApiMessage::create( $verification[
'details'],
'verification-error' );
910 $details = [ $msg->getKey(), ...$msg->getParams() ];
912 $details = $verification[
'details'];
914 ApiResult::setIndexedTagName( $details,
'detail' );
915 $msg->setApiData( $msg->getApiData() + [
'details' => $details ] );
916 $this->dieWithError( $msg );
919 case UploadBase::HOOK_ABORTED:
920 $msg = $verification[
'error'] ===
'' ?
'hookaborted' : $verification[
'error'];
921 $this->dieWithError( $msg,
'hookaborted', [
'details' => $verification[
'error'] ] );
924 $this->dieWithError(
'apierror-unknownerror-nocode',
'unknown-error',
925 [
'details' => [
'code' => $verification[
'status'] ] ] );
937 $warnings = UploadBase::makeWarningsSerializable(
938 $this->mUpload->checkWarnings( $this->getUser() )
941 return $this->transformWarnings( $warnings );
947 ApiResult::setIndexedTagName( $warnings,
'warning' );
949 if ( isset( $warnings[
'duplicate'] ) ) {
950 $dupes = array_column( $warnings[
'duplicate'],
'fileName' );
951 ApiResult::setIndexedTagName( $dupes,
'duplicate' );
952 $warnings[
'duplicate'] = $dupes;
955 if ( isset( $warnings[
'exists'] ) ) {
956 $warning = $warnings[
'exists'];
957 unset( $warnings[
'exists'] );
958 $localFile = $warning[
'normalizedFile'] ?? $warning[
'file'];
959 $warnings[$warning[
'warning']] = $localFile[
'fileName'];
962 if ( isset( $warnings[
'no-change'] ) ) {
963 $file = $warnings[
'no-change'];
964 unset( $warnings[
'no-change'] );
966 $warnings[
'nochange'] = [
967 'timestamp' =>
wfTimestamp( TS_ISO_8601, $file[
'timestamp'] )
971 if ( isset( $warnings[
'duplicate-version'] ) ) {
973 foreach ( $warnings[
'duplicate-version'] as $dupe ) {
975 'timestamp' =>
wfTimestamp( TS_ISO_8601, $dupe[
'timestamp'] )
978 unset( $warnings[
'duplicate-version'] );
980 ApiResult::setIndexedTagName( $dupes,
'ver' );
981 $warnings[
'duplicateversions'] = $dupes;
984 if ( $this->mParams[
'async'] && $this->mParams[
'url'] ) {
985 unset( $warnings[
'empty-file'] );
999 $this->log->info(
"Upload stashing of {filename} failed for {user} because {error}",
1001 'user' => $this->
getUser()->getName(),
1002 'error' => get_class( $e ),
1003 'filename' => $this->mParams[
'filename'] ??
'-',
1004 'filekey' => $this->mParams[
'filekey'] ??
'-'
1008 switch ( get_class( $e ) ) {
1009 case UploadStashFileNotFoundException::class:
1010 $wrap =
'apierror-stashedfilenotfound';
1012 case UploadStashBadPathException::class:
1013 $wrap =
'apierror-stashpathinvalid';
1015 case UploadStashFileException::class:
1016 $wrap =
'apierror-stashfilestorage';
1018 case UploadStashZeroLengthFileException::class:
1019 $wrap =
'apierror-stashzerolength';
1021 case UploadStashNotLoggedInException::class:
1022 return StatusValue::newFatal( ApiMessage::create(
1023 [
'apierror-mustbeloggedin', $this->msg(
'action-upload' ) ],
'stashnotloggedin'
1025 case UploadStashWrongOwnerException::class:
1026 $wrap =
'apierror-stashwrongowner';
1028 case UploadStashNoSuchKeyException::class:
1029 $wrap =
'apierror-stashnosuchfilekey';
1032 $wrap = [
'uploadstash-exception', get_class( $e ) ];
1035 return StatusValue::newFatal(
1036 $this->getErrorFormatter()->getMessageFromException( $e, [
'wrap' => $wrap ] )
1049 $this->mParams[
'text'] ??= $this->mParams[
'comment'];
1052 $file = $this->mUpload->getLocalFile();
1054 $title = $file->getTitle();
1062 $this->mParams[
'watchlist'], $title, $user,
'watchdefault'
1065 if ( !$watch && $this->mParams[
'watchlist'] ==
'preferences' && !$file->exists() ) {
1074 if ( $this->mParams[
'watch'] ) {
1078 if ( $this->mParams[
'tags'] ) {
1080 if ( !$status->isOK() ) {
1081 $this->dieStatus( $status );
1087 if ( $this->mParams[
'async'] ) {
1089 if ( $this->mParams[
'filekey'] ) {
1092 'filename' => $this->mParams[
'filename'],
1093 'filekey' => $this->mParams[
'filekey'],
1094 'comment' => $this->mParams[
'comment'],
1095 'tags' => $this->mParams[
'tags'] ?? [],
1096 'text' => $this->mParams[
'text'],
1098 'watchlistexpiry' => $watchlistExpiry,
1099 'session' => $this->
getContext()->exportSession(),
1100 'ignorewarnings' => $this->mParams[
'ignorewarnings']
1103 } elseif ( $this->mParams[
'url'] ) {
1106 'filename' => $this->mParams[
'filename'],
1107 'url' => $this->mParams[
'url'],
1108 'comment' => $this->mParams[
'comment'],
1109 'tags' => $this->mParams[
'tags'] ?? [],
1110 'text' => $this->mParams[
'text'],
1112 'watchlistexpiry' => $watchlistExpiry,
1113 'session' => $this->
getContext()->exportSession(),
1114 'ignorewarnings' => $this->mParams[
'ignorewarnings']
1118 $this->dieWithError(
'apierror-no-async-support',
'publishfailed' );
1124 $cacheKey =
$job->getCacheKey();
1127 $progress = UploadBase::getSessionStatus( $this->
getUser(), $cacheKey );
1128 if ( $progress && $progress[
'result'] ===
'Poll' ) {
1129 $this->dieWithError(
'apierror-upload-inprogress',
'publishfailed' );
1131 UploadBase::setSessionStatus(
1134 [
'result' =>
'Poll',
'stage' =>
'queued',
'status' => Status::newGood() ]
1137 $this->jobQueueGroup->push(
$job );
1138 $this->log->info(
"Sending publish job of {filename} for {user}",
1140 'user' => $this->
getUser()->getName(),
1141 'filename' => $this->mParams[
'filename'] ??
'-'
1144 $result[
'result'] =
'Poll';
1145 $result[
'stage'] =
'queued';
1148 $status = $this->mUpload->performUpload(
1149 $this->mParams[
'comment'],
1150 $this->mParams[
'text'],
1153 $this->mParams[
'tags'] ?? [],
1157 if ( !$status->isGood() ) {
1158 $this->log->info(
"Non-async API upload publish failed for {user} because {status}",
1160 'user' => $this->
getUser()->getName(),
1161 'filename' => $this->mParams[
'filename'] ??
'-',
1162 'filekey' => $this->mParams[
'filekey'] ??
'-',
1163 'status' => (
string)$status
1166 $this->dieRecoverableError( $status->getMessages() );
1168 $result[
'result'] =
'Success';
1171 $result[
'filename'] = $file->getName();
1172 if ( $warnings && count( $warnings ) > 0 ) {
1173 $result[
'warnings'] = $warnings;
1190 ParamValidator::PARAM_TYPE =>
'string',
1193 ParamValidator::PARAM_DEFAULT =>
''
1196 ParamValidator::PARAM_TYPE =>
'tags',
1197 ParamValidator::PARAM_ISMULTI =>
true,
1200 ParamValidator::PARAM_TYPE =>
'text',
1203 ParamValidator::PARAM_DEFAULT =>
false,
1204 ParamValidator::PARAM_DEPRECATED =>
true,
1217 'ignorewarnings' =>
false,
1219 ParamValidator::PARAM_TYPE =>
'upload',
1224 ParamValidator::PARAM_DEPRECATED =>
true,
1229 ParamValidator::PARAM_TYPE =>
'integer',
1230 IntegerDef::PARAM_MIN => 0,
1231 IntegerDef::PARAM_MAX => UploadBase::getMaxUploadSize(),
1234 ParamValidator::PARAM_TYPE =>
'integer',
1235 IntegerDef::PARAM_MIN => 0,
1238 ParamValidator::PARAM_TYPE =>
'upload',
1242 'checkstatus' =>
false,
1254 'action=upload&filename=Wiki.png' .
1255 '&url=http%3A//upload.wikimedia.org/wikipedia/en/b/bc/Wiki.png&token=123ABC'
1256 =>
'apihelp-upload-example-url',
1257 'action=upload&filename=Wiki.png&filekey=filekey&ignorewarnings=1&token=123ABC'
1258 =>
'apihelp-upload-example-filekey',
1263 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Upload';
1268class_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.
array $params
The job parameters.
getUpload()
Getter for the upload.
Assemble the segments of a chunked upload.
Handle enqueueing of background jobs.
get( $type)
Get the job queue object for a given queue type.
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 WatchlistExpiryMaxDuration
Name constant for the WatchlistExpiryMaxDuration setting, for use with Config::get()
Upload a file from the upload stash into the local file repo.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
static newGood( $value=null)
Factory function for good results.
UploadBase and subclasses are the backend of MediaWiki's file uploads.
static setSessionStatus(UserIdentity $user, $statusKey, $value)
Set the current status of a chunked upload (used for polling).
getLocalFile()
Return the local file and initializes if necessary.
static getSessionStatus(UserIdentity $user, $statusKey)
Get the current status of a chunked upload (used for polling).
Implements uploading from chunks.
Implements regular file uploads.
Implements uploading from previously stored file.
Upload a file by URL, via the jobqueue.
Implements uploading from a HTTP resource.
initialize( $name, $url)
Entry point for API upload.
trait ApiWatchlistTrait
An ApiWatchlistTrait adds class properties and convenience methods for APIs that allow you to watch a...
if(count( $args)< 1) $job