* The timeout for copy uploads is set by $wgHTTPTimeout.
*/
$wgAllowCopyUploads = false;
+/**
+ * Allow asynchronous copy uploads.
+ * This feature is experimental.
+ */
+$wgAllowAsyncCopyUploads = false;
/**
* Max size for uploads, in bytes. Applies to all uploads.
if ( !$template
|| $template->getNamespace() !== NS_TEMPLATE
|| !$template->exists() ) {
- $text = "== $subject ==\n\n$text\n\n-- $signature";
+ $text = "\n== $subject ==\n\n$text\n\n-- $signature";
} else {
$text = '{{'. $template->getText()
. " | subject=$subject | body=$text | signature=$signature }}";
$flags = $article->checkFlags( $flags );
if ( $flags & EDIT_UPDATE ) {
- $text .= $article->getContent();
+ $text = $article->getContent() . $text;
}
$dbw = wfGetDB( DB_MASTER );
// First check permission to upload
$this->checkPermissions( $wgUser );
- // Check permission to upload this file
- $permErrors = $this->mUpload->verifyPermissions( $wgUser );
- if ( $permErrors !== true ) {
- // Todo: more specific error message
- $this->dieUsageMsg( array( 'badaccess-groups' ) );
- }
// Fetch the file
$status = $this->mUpload->fetchFile();
// Check if the uploaded file is sane
$this->verifyUpload();
+
+ // Check permission to upload this file
+ $permErrors = $this->mUpload->verifyPermissions( $wgUser );
+ if ( $permErrors !== true ) {
+ // Todo: stash the upload and allow choosing a new name
+ $this->dieUsageMsg( array( 'badaccess-groups' ) );
+ }
// Check warnings if necessary
$warnings = $this->checkForWarnings();
$this->dieUsage( 'The filename is not allowed', 'illegal-filename',
0, array( 'filename' => $verification['filtered'] ) );
break;
- case UploadBase::OVERWRITE_EXISTING_FILE:
- $this->dieUsage( 'Overwriting an existing file is not allowed', 'overwrite' );
- break;
case UploadBase::VERIFICATION_ERROR:
$this->getResult()->setIndexedTagName( $verification['details'], 'detail' );
$this->dieUsage( 'This file did not pass file verification', 'verification-error',
if ( !$status->isGood() ) {
$error = $status->getErrorsArray();
- $this->getResult()->setIndexedTagName( $error, 'error' );
+
+ if ( count( $error ) == 1 && $error[0][0] == 'async' ) {
+ // The upload can not be performed right now, because the user
+ // requested so
+ return array(
+ 'result' => 'Queued',
+ 'sessionkey' => $error[0][1],
+ );
+ } else {
+ $this->getResult()->setIndexedTagName( $error, 'error' );
- $this->dieUsage( 'An internal error occurred', 'internal-error', 0, $error );
+ $this->dieUsage( 'An internal error occurred', 'internal-error', 0, $error );
+ }
}
$file = $this->mUpload->getLocalFile();
'ignorewarnings' => false,
'file' => null,
'url' => null,
- 'asyncdownload' => false,
- 'leavemessage' => false,
+
'sessionkey' => null,
);
+
+ global $wgAllowAsyncCopyUploads;
+ if ( $wgAllowAsyncCopyUploads ) {
+ $params += array(
+ 'asyncdownload' => false,
+ 'leavemessage' => false,
+ );
+ }
return $params;
}
public function getParamDescription() {
- return array(
+ $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',
'ignorewarnings' => 'Ignore any warnings',
'file' => 'File contents',
'url' => 'Url to fetch the file from',
- 'asyncdownload' => 'Make fetching a URL asyncronous',
- 'leavemessage' => 'If asyncdownload is used, leave a message on the user talk page if finished',
'sessionkey' => 'Session key returned by a previous upload that failed due to warnings',
);
+
+ global $wgAllowAsyncCopyUploads;
+ if ( $wgAllowAsyncCopyUploads ) {
+ $params += array(
+ 'asyncdownload' => 'Make fetching a URL asynchronous',
+ 'leavemessage' => 'If asyncdownload is used, leave a message on the user talk page if finished',
+ );
+ }
+
+ return $params;
+
}
public function getDescription() {
* @ingroup JobQueue
*/
class UploadFromUrlJob extends Job {
+ public $upload;
+ protected $user;
public function __construct( $title, $params, $id = 0 ) {
parent::__construct( 'uploadFromUrl', $title, $params, $id );
public function run() {
# Initialize this object and the upload object
- $upload = new UploadFromUrl();
- $upload->initialize(
+ $this->upload = new UploadFromUrl();
+ $this->upload->initialize(
$this->title->getText(),
$this->params['url'],
false
$this->user = User::newFromName( $this->params['userName'] );
# Fetch the file
- $status = $upload->fetchFile();
+ $status = $this->upload->fetchFile();
if ( !$status->isOk() ) {
$this->leaveMessage( $status );
return;
}
+ # Verify upload
+ $result = $this->upload->verifyUpload();
+ if ( $result['status'] != UploadBase::OK ) {
+ $status = $this->upload->convertVerifyErrorToStatus( $result );
+ $this->leaveMessage( $status );
+ return;
+ }
+
# Check warnings
if ( !$this->params['ignoreWarnings'] ) {
- $warnings = $this->checkWarnings();
+ $warnings = $this->upload->checkWarnings();
if ( $warnings ) {
if ( $this->params['leaveMessage'] ) {
$this->user->leaveUserMessage(
wfMsg( 'upload-warning-subj' ),
- wfMsg( 'upload-warning-msg', $this->params['sessionKey'] )
+ wfMsg( 'upload-warning-msg',
+ $this->params['sessionKey'],
+ $this->params['url'] )
);
} else {
$this->storeResultInSession( 'Warning',
'warnings', $warnings );
}
+
+ // FIXME: stash in session
return;
}
}
# Perform the upload
- $status = $upload->performUpload(
+ $status = $this->upload->performUpload(
$this->params['comment'],
$this->params['pageText'],
- $this->params['watch']
+ $this->params['watch'],
+ $this->user
);
$this->leaveMessage( $status );
}
protected function leaveMessage( $status ) {
if ( $this->params['leaveMessage'] ) {
if ( $status->isGood() ) {
- $file = $this->getLocalFile();
-
- $this->user->leaveUserMessage( wfMsg( 'successfulupload' ),
- wfMsg( 'upload-success-msg', $file->getDescriptionUrl() ) );
+ $this->user->leaveUserMessage( wfMsg( 'upload-success-subj' ),
+ wfMsg( 'upload-success-msg',
+ $this->upload->getTitle()->getText(),
+ $this->params['url']
+ ) );
} else {
$this->user->leaveUserMessage( wfMsg( 'upload-failure-subj' ),
- wfMsg( 'upload-failure-msg', $status->getWikiText() ) );
+ wfMsg( 'upload-failure-msg',
+ $status->getWikiText(),
+ $this->params['url']
+ ) );
}
} else {
if ( $status->isOk() ) {
/**
* Store a result in the session data
+ * THIS IS BROKEN. $_SESSION does not exist when using runJobs.php
*
* @param $result string The result (Success|Warning|Failure)
* @param $dataKey string The key of the extra data
*/
class UploadFromUrl extends UploadBase {
protected $mAsync, $mUrl;
+ protected $mIgnoreWarnings = true;
/**
* Checks if the user is allowed to use the upload-by-URL feature. If the
* asynchronous download.
*/
public function initialize( $name, $url, $async = false ) {
- global $wgUser;
+ global $wgAllowAsyncCopyUploads;
$this->mUrl = $url;
- $this->mAsync = $async;
+ $this->mAsync = $wgAllowAsyncCopyUploads ? $async : false;
- $tempPath = $async ? null : $this->makeTemporaryFile();
+ $tempPath = $this->mAsync ? null : $this->makeTemporaryFile();
# File size and removeTempFile will be filled in later
$this->initializePathInfo( $name, $tempPath, 0, false );
}
}
return Status::newGood();
}
+ /**
+ * Create a new temporary file in the URL subdirectory of wfTempDir().
+ *
+ * @return string Path to the file
+ */
protected function makeTemporaryFile() {
return tempnam( wfTempDir(), 'URL' );
}
+ /**
+ * Save the result of a HTTP request to the temporary file
+ *
+ * @param $req HttpRequest
+ * @return Status
+ */
private function saveTempFile( $req ) {
if ( $this->mTempPath === false ) {
return Status::newFatal( 'tmp-create-error' );
return Status::newGood();
}
+ /**
+ * Download the file, save it to the temporary file and update the file
+ * size and set $mRemoveTempFile to true.
+ */
protected function reallyFetchFile() {
$req = HttpRequest::factory( $this->mUrl );
$status = $req->execute();
return $status;
}
+ /**
+ * Wrapper around the parent function in order to defer verifying the
+ * upload until the file really has been fetched.
+ */
+ public function verifyUpload() {
+ if ( $this->mAsync ) {
+ return array( 'status' => self::OK );
+ }
+ return parent::verifyUpload();
+ }
+
+ /**
+ * Wrapper around the parent function in order to defer checking warnings
+ * until the file really has been fetched.
+ */
+ public function checkWarnings() {
+ if ( $this->mAsync ) {
+ $this->mIgnoreWarnings = false;
+ return array();
+ }
+ return parent::checkWarnings();
+ }
+
+ /**
+ * Wrapper around the parent function in order to defer checking protection
+ * until we are sure that the file can actually be uploaded
+ */
+ public function verifyPermissions( $user ) {
+ if ( $this->mAsync ) {
+ return true;
+ }
+ return parent::verifyPermissions( $user );
+ }
+
+ /**
+ * Wrapper around the parent function in order to defer uploading to the
+ * job queue for asynchronous uploads
+ */
public function performUpload( $comment, $pageText, $watch, $user ) {
if ( $this->mAsync ) {
$sessionKey = $this->insertJob( $comment, $pageText, $watch, $user );