From c1f9b1f7b1b77776192048005dcc66dcf3df2bfb Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Sat, 27 Dec 2014 15:41:37 +0100 Subject: Update to MediaWiki 1.24.1 --- includes/api/ApiUpload.php | 186 ++++++++++++++++----------------------------- 1 file changed, 64 insertions(+), 122 deletions(-) (limited to 'includes/api/ApiUpload.php') diff --git a/includes/api/ApiUpload.php b/includes/api/ApiUpload.php index 467eccf8..657181b7 100644 --- a/includes/api/ApiUpload.php +++ b/includes/api/ApiUpload.php @@ -28,17 +28,12 @@ * @ingroup API */ class ApiUpload extends ApiBase { - - /** - * @var UploadBase - */ + /** @var UploadBase|UploadFromChunks */ protected $mUpload = null; protected $mParams; public function execute() { - global $wgEnableAsyncUploads; - // Check whether upload is enabled if ( !UploadBase::isEnabled() ) { $this->dieUsageMsg( 'uploaddisabled' ); @@ -50,13 +45,14 @@ class ApiUpload extends ApiBase { $this->mParams = $this->extractRequestParams(); $request = $this->getMain()->getRequest(); // Check if async mode is actually supported (jobs done in cli mode) - $this->mParams['async'] = ( $this->mParams['async'] && $wgEnableAsyncUploads ); + $this->mParams['async'] = ( $this->mParams['async'] && $this->getConfig()->get( 'EnableAsyncUploads' ) ); // Add the uploaded file to the params array $this->mParams['file'] = $request->getFileName( 'file' ); $this->mParams['chunk'] = $request->getFileName( 'chunk' ); // Copy the session key to the file key, for backward compatibility. if ( !$this->mParams['filekey'] && $this->mParams['sessionkey'] ) { + $this->logFeatureUsage( 'action=upload&sessionkey' ); $this->mParams['filekey'] = $this->mParams['sessionkey']; } @@ -95,7 +91,7 @@ class ApiUpload extends ApiBase { } elseif ( $this->mParams['async'] && $this->mParams['filekey'] ) { // defer verification to background process } else { - wfDebug( __METHOD__ . 'about to verify' ); + wfDebug( __METHOD__ . " about to verify\n" ); $this->verifyUpload(); } @@ -141,6 +137,7 @@ class ApiUpload extends ApiBase { // Stash the file and get stash result return $this->getStashResult( $warnings ); } + // This is the most common case -- a normal upload with no warnings // performUpload will return a formatted properly for the API with status return $this->performUpload( $warnings ); @@ -165,6 +162,7 @@ class ApiUpload extends ApiBase { } catch ( MWException $e ) { $this->dieUsage( $e->getMessage(), 'stashfailed' ); } + return $result; } @@ -185,6 +183,7 @@ class ApiUpload extends ApiBase { } catch ( MWException $e ) { $result['warnings']['stashfailed'] = $e->getMessage(); } + return $result; } @@ -212,11 +211,11 @@ class ApiUpload extends ApiBase { } } else { $filekey = $this->mParams['filekey']; - /** @var $status Status */ $status = $this->mUpload->addChunk( $chunkPath, $chunkSize, $this->mParams['offset'] ); if ( !$status->isGood() ) { $this->dieUsage( $status->getWikiText(), 'stashfailed' ); + return array(); } } @@ -233,7 +232,7 @@ class ApiUpload extends ApiBase { array( 'result' => 'Poll', 'stage' => 'queued', 'status' => Status::newGood() ) ); - $ok = JobQueueGroup::singleton()->push( new AssembleUploadChunksJob( + JobQueueGroup::singleton()->push( new AssembleUploadChunksJob( Title::makeTitle( NS_FILE, $filekey ), array( 'filename' => $this->mParams['filename'], @@ -241,17 +240,13 @@ class ApiUpload extends ApiBase { 'session' => $this->getContext()->exportSession() ) ) ); - if ( $ok ) { - $result['result'] = 'Poll'; - } else { - UploadBase::setSessionStatus( $filekey, false ); - $this->dieUsage( - "Failed to start AssembleUploadChunks.php", 'stashfailed' ); - } + $result['result'] = 'Poll'; + $result['stage'] = 'queued'; } else { $status = $this->mUpload->concatenateChunks(); if ( !$status->isGood() ) { $this->dieUsage( $status->getWikiText(), 'stashfailed' ); + return array(); } @@ -265,6 +260,7 @@ class ApiUpload extends ApiBase { } $result['filekey'] = $filekey; $result['offset'] = $this->mParams['offset'] + $chunkSize; + return $result; } @@ -272,7 +268,7 @@ class ApiUpload extends ApiBase { * Stash the file and return the file key * Also re-raises exceptions with slightly more informative message strings (useful for API) * @throws MWException - * @return String file key + * @return string File key */ private function performStash() { try { @@ -287,6 +283,7 @@ class ApiUpload extends ApiBase { wfDebug( __METHOD__ . ' ' . $message . "\n" ); throw new MWException( $message ); } + return $fileKey; } @@ -341,6 +338,7 @@ class ApiUpload extends ApiBase { } unset( $progress['status'] ); // remove Status object $this->getResult()->addValue( null, $this->getModuleName(), $progress ); + return false; } @@ -357,6 +355,7 @@ class ApiUpload extends ApiBase { $sessionData['sessionkey'] = $this->mParams['statuskey']; } $this->getResult()->addValue( null, $this->getModuleName(), $sessionData ); + return false; } @@ -440,7 +439,7 @@ class ApiUpload extends ApiBase { /** * Checks that the user has permissions to perform this upload. * Dies with usage message on inadequate permissions. - * @param $user User The user to check. + * @param User $user The user to check. */ protected function checkPermissions( $user ) { // Check whether the user has the appropriate permissions to upload anyway @@ -449,9 +448,9 @@ class ApiUpload extends ApiBase { if ( $permission !== true ) { if ( !$user->isLoggedIn() ) { $this->dieUsageMsg( array( 'mustbeloggedin', 'upload' ) ); - } else { - $this->dieUsageMsg( 'badaccess-groups' ); } + + $this->dieUsageMsg( 'badaccess-groups' ); } } @@ -469,10 +468,9 @@ class ApiUpload extends ApiBase { /** * Performs file verification, dies on error. + * @param array $verification */ protected function checkVerification( array $verification ) { - global $wgFileExtensions; - // @todo Move them to ApiBase's message map switch ( $verification['status'] ) { // Recoverable errors @@ -481,7 +479,7 @@ class ApiUpload extends ApiBase { break; case UploadBase::ILLEGAL_FILENAME: $this->dieRecoverableError( 'illegal-filename', 'filename', - array( 'filename' => $verification['filtered'] ) ); + array( 'filename' => $verification['filtered'] ) ); break; case UploadBase::FILENAME_TOO_LONG: $this->dieRecoverableError( 'filename-toolong', 'filename' ); @@ -504,7 +502,7 @@ class ApiUpload extends ApiBase { case UploadBase::FILETYPE_BADTYPE: $extradata = array( 'filetype' => $verification['finalExt'], - 'allowed' => array_values( array_unique( $wgFileExtensions ) ) + 'allowed' => array_values( array_unique( $this->getConfig()->get( 'FileExtensions' ) ) ) ); $this->getResult()->setIndexedTagName( $extradata['allowed'], 'ext' ); @@ -521,15 +519,15 @@ class ApiUpload extends ApiBase { case UploadBase::VERIFICATION_ERROR: $this->getResult()->setIndexedTagName( $verification['details'], 'detail' ); $this->dieUsage( 'This file did not pass file verification', 'verification-error', - 0, array( 'details' => $verification['details'] ) ); + 0, array( 'details' => $verification['details'] ) ); break; case UploadBase::HOOK_ABORTED: $this->dieUsage( "The modification you tried to make was aborted by an extension hook", - 'hookaborted', 0, array( 'error' => $verification['error'] ) ); + 'hookaborted', 0, array( 'error' => $verification['error'] ) ); break; default: $this->dieUsage( 'An unknown error occurred', 'unknown-error', - 0, array( 'code' => $verification['status'] ) ); + 0, array( 'code' => $verification['status'] ) ); break; } } @@ -555,6 +553,7 @@ class ApiUpload extends ApiBase { if ( isset( $warnings['duplicate'] ) ) { $dupes = array(); + /** @var File $dupe */ foreach ( $warnings['duplicate'] as $dupe ) { $dupes[] = $dupe->getName(); } @@ -565,10 +564,14 @@ class ApiUpload extends ApiBase { if ( isset( $warnings['exists'] ) ) { $warning = $warnings['exists']; unset( $warnings['exists'] ); - $localFile = isset( $warning['normalizedFile'] ) ? $warning['normalizedFile'] : $warning['file']; + /** @var LocalFile $localFile */ + $localFile = isset( $warning['normalizedFile'] ) + ? $warning['normalizedFile'] + : $warning['file']; $warnings[$warning['warning']] = $localFile->getName(); } } + return $warnings; } @@ -587,10 +590,23 @@ class ApiUpload extends ApiBase { /** @var $file File */ $file = $this->mUpload->getLocalFile(); - $watch = $this->getWatchlistValue( $this->mParams['watchlist'], $file->getTitle() ); + + // For preferences mode, we want to watch if 'watchdefault' is set or + // if the *file* doesn't exist and 'watchcreations' is set. But + // getWatchlistValue()'s automatic handling checks if the *title* + // exists or not, so we need to check both prefs manually. + $watch = $this->getWatchlistValue( + $this->mParams['watchlist'], $file->getTitle(), 'watchdefault' + ); + if ( !$watch && $this->mParams['watchlist'] == 'preferences' && !$file->exists() ) { + $watch = $this->getWatchlistValue( + $this->mParams['watchlist'], $file->getTitle(), 'watchcreations' + ); + } // Deprecated parameters if ( $this->mParams['watch'] ) { + $this->logFeatureUsage( 'action=upload&watch' ); $watch = true; } @@ -604,7 +620,7 @@ class ApiUpload extends ApiBase { $this->mParams['filekey'], array( 'result' => 'Poll', 'stage' => 'queued', 'status' => Status::newGood() ) ); - $ok = JobQueueGroup::singleton()->push( new PublishStashedFileJob( + JobQueueGroup::singleton()->push( new PublishStashedFileJob( Title::makeTitle( NS_FILE, $this->mParams['filename'] ), array( 'filename' => $this->mParams['filename'], @@ -615,13 +631,8 @@ class ApiUpload extends ApiBase { 'session' => $this->getContext()->exportSession() ) ) ); - if ( $ok ) { - $result['result'] = 'Poll'; - } else { - UploadBase::setSessionStatus( $this->mParams['filekey'], false ); - $this->dieUsage( - "Failed to start PublishStashedFile.php", 'publishfailed' ); - } + $result['result'] = 'Poll'; + $result['stage'] = 'queued'; } else { /** @var $status Status */ $status = $this->mUpload->performUpload( $this->mParams['comment'], @@ -637,11 +648,10 @@ class ApiUpload extends ApiBase { 'result' => 'Queued', 'statuskey' => $error[0][1], ); - } else { - $this->getResult()->setIndexedTagName( $error, 'error' ); - - $this->dieUsage( 'An internal error occurred', 'internal-error', 0, $error ); } + + $this->getResult()->setIndexedTagName( $error, 'error' ); + $this->dieUsage( 'An internal error occurred', 'internal-error', 0, $error ); } $result['result'] = 'Success'; } @@ -658,8 +668,7 @@ class ApiUpload extends ApiBase { * Checks if asynchronous copy uploads are enabled and throws an error if they are not. */ protected function checkAsyncDownloadEnabled() { - global $wgAllowAsyncCopyUploads; - if ( !$wgAllowAsyncCopyUploads ) { + if ( !$this->getConfig()->get( 'AllowAsyncCopyUploads' ) ) { $this->dieUsage( 'Asynchronous copy uploads disabled', 'asynccopyuploaddisabled' ); } } @@ -681,10 +690,6 @@ class ApiUpload extends ApiBase { ApiBase::PARAM_DFLT => '' ), 'text' => null, - 'token' => array( - ApiBase::PARAM_TYPE => 'string', - ApiBase::PARAM_REQUIRED => true - ), 'watch' => array( ApiBase::PARAM_DFLT => false, ApiBase::PARAM_DEPRECATED => true, @@ -728,17 +733,19 @@ class ApiUpload extends ApiBase { public function getParamDescription() { $params = array( 'filename' => 'Target filename', - 'token' => 'Edit token. You can get one of these through prop=info', - 'comment' => 'Upload comment. Also used as the initial page text for new files if "text" is not specified', + 'comment' => 'Upload comment. Also used as the initial page text for new ' . + 'files if "text" is not specified', 'text' => 'Initial page text for new files', 'watch' => 'Watch the page', - 'watchlist' => 'Unconditionally add or remove the page from your watchlist, use preferences or do not change watch', + 'watchlist' => 'Unconditionally add or remove the page from your watchlist, ' . + 'use preferences or do not change watch', 'ignorewarnings' => 'Ignore any warnings', 'file' => 'File contents', 'url' => 'URL to fetch the file from', 'filekey' => 'Key that identifies a previous upload that was stashed temporarily.', 'sessionkey' => 'Same as filekey, maintained for backward compatibility.', - 'stash' => 'If set, the server will not add the file to the repository and stash it temporarily.', + 'stash' => 'If set, the server will not add the file to the repository ' . + 'and stash it temporarily.', 'chunk' => 'Chunk contents', 'offset' => 'Offset of chunk in bytes', @@ -752,42 +759,6 @@ class ApiUpload extends ApiBase { ); return $params; - - } - - public function getResultProperties() { - return array( - '' => array( - 'result' => array( - ApiBase::PROP_TYPE => array( - 'Success', - 'Warning', - 'Continue', - 'Queued' - ), - ), - 'filekey' => array( - ApiBase::PROP_TYPE => 'string', - ApiBase::PROP_NULLABLE => true - ), - 'sessionkey' => array( - ApiBase::PROP_TYPE => 'string', - ApiBase::PROP_NULLABLE => true - ), - 'offset' => array( - ApiBase::PROP_TYPE => 'integer', - ApiBase::PROP_NULLABLE => true - ), - 'statuskey' => array( - ApiBase::PROP_TYPE => 'string', - ApiBase::PROP_NULLABLE => true - ), - 'filename' => array( - ApiBase::PROP_TYPE => 'string', - ApiBase::PROP_NULLABLE => true - ) - ) - ); } public function getDescription() { @@ -797,49 +768,20 @@ class ApiUpload extends ApiBase { ' * Have the MediaWiki server fetch a file from a URL, using the "url" parameter', ' * Complete an earlier upload that failed due to warnings, using the "filekey" parameter', 'Note that the HTTP POST must be done as a file upload (i.e. using multipart/form-data) when', - 'sending the "file". Also you must get and send an edit token before doing any upload stuff' - ); - } - - public function getPossibleErrors() { - return array_merge( parent::getPossibleErrors(), - $this->getRequireOnlyOneParameterErrorMessages( array( 'filekey', 'file', 'url', 'statuskey' ) ), - array( - array( 'uploaddisabled' ), - array( 'invalid-file-key' ), - array( 'uploaddisabled' ), - array( 'mustbeloggedin', 'upload' ), - array( 'badaccess-groups' ), - array( 'code' => 'fetchfileerror', 'info' => '' ), - array( 'code' => 'nomodule', 'info' => 'No upload module set' ), - array( 'code' => 'empty-file', 'info' => 'The file you submitted was empty' ), - array( 'code' => 'filetype-missing', 'info' => 'The file is missing an extension' ), - array( 'code' => 'filename-tooshort', 'info' => 'The filename is too short' ), - array( 'code' => 'overwrite', 'info' => 'Overwriting an existing file is not allowed' ), - array( 'code' => 'stashfailed', 'info' => 'Stashing temporary file failed' ), - array( 'code' => 'publishfailed', 'info' => 'Publishing of stashed file failed' ), - array( 'code' => 'internal-error', 'info' => 'An internal error occurred' ), - array( 'code' => 'asynccopyuploaddisabled', 'info' => 'Asynchronous copy uploads disabled' ), - array( 'code' => 'stasherror', 'info' => 'An upload stash error occurred' ), - array( 'fileexists-forbidden' ), - array( 'fileexists-shared-forbidden' ), - ) + 'sending the "file".', ); } public function needsToken() { - return true; - } - - public function getTokenSalt() { - return ''; + return 'csrf'; } public function getExamples() { return array( - 'api.php?action=upload&filename=Wiki.png&url=http%3A//upload.wikimedia.org/wikipedia/en/b/bc/Wiki.png' + 'api.php?action=upload&filename=Wiki.png' . + '&url=http%3A//upload.wikimedia.org/wikipedia/en/b/bc/Wiki.png&token=123ABC' => 'Upload from a URL', - 'api.php?action=upload&filename=Wiki.png&filekey=filekey&ignorewarnings=1' + 'api.php?action=upload&filename=Wiki.png&filekey=filekey&ignorewarnings=1&token=123ABC' => 'Complete an upload that failed due to warnings', ); } -- cgit v1.2.2