summaryrefslogtreecommitdiff
path: root/includes/api/ApiUpload.php
diff options
context:
space:
mode:
Diffstat (limited to 'includes/api/ApiUpload.php')
-rw-r--r--includes/api/ApiUpload.php186
1 files changed, 64 insertions, 122 deletions
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',
);
}