r70137 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r70136‎ | r70137 | r70138 >
Date:13:53, 29 July 2010
Author:btongminh
Status:resolved (Comments)
Tags:
Comment:
Made asynchronous upload by URL working, partly. Hid it behind $wgAllowAsyncCopyUploads. If there are no errors then everything works expected; the same if there are unrecoverable errors. User intervention to solve warnings is not yet possible, because $_SESSION is not available in runJobs. This also means that async with leavemessage = false is broken.


Other changes:
* Moved verifyPermissions check in ApiUpload down pending r70135 implementation in the API.
* In User::leaveMessage: append message to end of talk page; add a newline before the heading
Modified paths:
  • /trunk/phase3/includes/DefaultSettings.php (modified) (history)
  • /trunk/phase3/includes/User.php (modified) (history)
  • /trunk/phase3/includes/api/ApiUpload.php (modified) (history)
  • /trunk/phase3/includes/job/UploadFromUrlJob.php (modified) (history)
  • /trunk/phase3/includes/upload/UploadFromUrl.php (modified) (history)

Diff [purge]

Index: trunk/phase3/includes/upload/UploadFromUrl.php
@@ -10,6 +10,7 @@
1111 */
1212 class UploadFromUrl extends UploadBase {
1313 protected $mAsync, $mUrl;
 14+ protected $mIgnoreWarnings = true;
1415
1516 /**
1617 * Checks if the user is allowed to use the upload-by-URL feature. If the
@@ -40,12 +41,12 @@
4142 * asynchronous download.
4243 */
4344 public function initialize( $name, $url, $async = false ) {
44 - global $wgUser;
 45+ global $wgAllowAsyncCopyUploads;
4546
4647 $this->mUrl = $url;
47 - $this->mAsync = $async;
 48+ $this->mAsync = $wgAllowAsyncCopyUploads ? $async : false;
4849
49 - $tempPath = $async ? null : $this->makeTemporaryFile();
 50+ $tempPath = $this->mAsync ? null : $this->makeTemporaryFile();
5051 # File size and removeTempFile will be filled in later
5152 $this->initializePathInfo( $name, $tempPath, 0, false );
5253 }
@@ -88,9 +89,20 @@
8990 }
9091 return Status::newGood();
9192 }
 93+ /**
 94+ * Create a new temporary file in the URL subdirectory of wfTempDir().
 95+ *
 96+ * @return string Path to the file
 97+ */
9298 protected function makeTemporaryFile() {
9399 return tempnam( wfTempDir(), 'URL' );
94100 }
 101+ /**
 102+ * Save the result of a HTTP request to the temporary file
 103+ *
 104+ * @param $req HttpRequest
 105+ * @return Status
 106+ */
95107 private function saveTempFile( $req ) {
96108 if ( $this->mTempPath === false ) {
97109 return Status::newFatal( 'tmp-create-error' );
@@ -103,6 +115,10 @@
104116
105117 return Status::newGood();
106118 }
 119+ /**
 120+ * Download the file, save it to the temporary file and update the file
 121+ * size and set $mRemoveTempFile to true.
 122+ */
107123 protected function reallyFetchFile() {
108124 $req = HttpRequest::factory( $this->mUrl );
109125 $status = $req->execute();
@@ -120,6 +136,44 @@
121137 return $status;
122138 }
123139
 140+ /**
 141+ * Wrapper around the parent function in order to defer verifying the
 142+ * upload until the file really has been fetched.
 143+ */
 144+ public function verifyUpload() {
 145+ if ( $this->mAsync ) {
 146+ return array( 'status' => self::OK );
 147+ }
 148+ return parent::verifyUpload();
 149+ }
 150+
 151+ /**
 152+ * Wrapper around the parent function in order to defer checking warnings
 153+ * until the file really has been fetched.
 154+ */
 155+ public function checkWarnings() {
 156+ if ( $this->mAsync ) {
 157+ $this->mIgnoreWarnings = false;
 158+ return array();
 159+ }
 160+ return parent::checkWarnings();
 161+ }
 162+
 163+ /**
 164+ * Wrapper around the parent function in order to defer checking protection
 165+ * until we are sure that the file can actually be uploaded
 166+ */
 167+ public function verifyPermissions( $user ) {
 168+ if ( $this->mAsync ) {
 169+ return true;
 170+ }
 171+ return parent::verifyPermissions( $user );
 172+ }
 173+
 174+ /**
 175+ * Wrapper around the parent function in order to defer uploading to the
 176+ * job queue for asynchronous uploads
 177+ */
124178 public function performUpload( $comment, $pageText, $watch, $user ) {
125179 if ( $this->mAsync ) {
126180 $sessionKey = $this->insertJob( $comment, $pageText, $watch, $user );
Index: trunk/phase3/includes/User.php
@@ -3767,7 +3767,7 @@
37683768 if ( !$template
37693769 || $template->getNamespace() !== NS_TEMPLATE
37703770 || !$template->exists() ) {
3771 - $text = "== $subject ==\n\n$text\n\n-- $signature";
 3771+ $text = "\n== $subject ==\n\n$text\n\n-- $signature";
37723772 } else {
37733773 $text = '{{'. $template->getText()
37743774 . " | subject=$subject | body=$text | signature=$signature }}";
@@ -3812,7 +3812,7 @@
38133813 $flags = $article->checkFlags( $flags );
38143814
38153815 if ( $flags & EDIT_UPDATE ) {
3816 - $text .= $article->getContent();
 3816+ $text = $article->getContent() . $text;
38173817 }
38183818
38193819 $dbw = wfGetDB( DB_MASTER );
Index: trunk/phase3/includes/api/ApiUpload.php
@@ -59,12 +59,6 @@
6060
6161 // First check permission to upload
6262 $this->checkPermissions( $wgUser );
63 - // Check permission to upload this file
64 - $permErrors = $this->mUpload->verifyPermissions( $wgUser );
65 - if ( $permErrors !== true ) {
66 - // Todo: more specific error message
67 - $this->dieUsageMsg( array( 'badaccess-groups' ) );
68 - }
6963
7064 // Fetch the file
7165 $status = $this->mUpload->fetchFile();
@@ -76,6 +70,13 @@
7771
7872 // Check if the uploaded file is sane
7973 $this->verifyUpload();
 74+
 75+ // Check permission to upload this file
 76+ $permErrors = $this->mUpload->verifyPermissions( $wgUser );
 77+ if ( $permErrors !== true ) {
 78+ // Todo: stash the upload and allow choosing a new name
 79+ $this->dieUsageMsg( array( 'badaccess-groups' ) );
 80+ }
8081
8182 // Check warnings if necessary
8283 $warnings = $this->checkForWarnings();
@@ -198,9 +199,6 @@
199200 $this->dieUsage( 'The filename is not allowed', 'illegal-filename',
200201 0, array( 'filename' => $verification['filtered'] ) );
201202 break;
202 - case UploadBase::OVERWRITE_EXISTING_FILE:
203 - $this->dieUsage( 'Overwriting an existing file is not allowed', 'overwrite' );
204 - break;
205203 case UploadBase::VERIFICATION_ERROR:
206204 $this->getResult()->setIndexedTagName( $verification['details'], 'detail' );
207205 $this->dieUsage( 'This file did not pass file verification', 'verification-error',
@@ -286,9 +284,19 @@
287285
288286 if ( !$status->isGood() ) {
289287 $error = $status->getErrorsArray();
290 - $this->getResult()->setIndexedTagName( $error, 'error' );
 288+
 289+ if ( count( $error ) == 1 && $error[0][0] == 'async' ) {
 290+ // The upload can not be performed right now, because the user
 291+ // requested so
 292+ return array(
 293+ 'result' => 'Queued',
 294+ 'sessionkey' => $error[0][1],
 295+ );
 296+ } else {
 297+ $this->getResult()->setIndexedTagName( $error, 'error' );
291298
292 - $this->dieUsage( 'An internal error occurred', 'internal-error', 0, $error );
 299+ $this->dieUsage( 'An internal error occurred', 'internal-error', 0, $error );
 300+ }
293301 }
294302
295303 $file = $this->mUpload->getLocalFile();
@@ -331,15 +339,22 @@
332340 'ignorewarnings' => false,
333341 'file' => null,
334342 'url' => null,
335 - 'asyncdownload' => false,
336 - 'leavemessage' => false,
 343+
337344 'sessionkey' => null,
338345 );
 346+
 347+ global $wgAllowAsyncCopyUploads;
 348+ if ( $wgAllowAsyncCopyUploads ) {
 349+ $params += array(
 350+ 'asyncdownload' => false,
 351+ 'leavemessage' => false,
 352+ );
 353+ }
339354 return $params;
340355 }
341356
342357 public function getParamDescription() {
343 - return array(
 358+ $params = array(
344359 'filename' => 'Target filename',
345360 'token' => 'Edit token. You can get one of these through prop=info',
346361 'comment' => 'Upload comment. Also used as the initial page text for new files if "text" is not specified',
@@ -349,10 +364,19 @@
350365 'ignorewarnings' => 'Ignore any warnings',
351366 'file' => 'File contents',
352367 'url' => 'Url to fetch the file from',
353 - 'asyncdownload' => 'Make fetching a URL asyncronous',
354 - 'leavemessage' => 'If asyncdownload is used, leave a message on the user talk page if finished',
355368 'sessionkey' => 'Session key returned by a previous upload that failed due to warnings',
356369 );
 370+
 371+ global $wgAllowAsyncCopyUploads;
 372+ if ( $wgAllowAsyncCopyUploads ) {
 373+ $params += array(
 374+ 'asyncdownload' => 'Make fetching a URL asynchronous',
 375+ 'leavemessage' => 'If asyncdownload is used, leave a message on the user talk page if finished',
 376+ );
 377+ }
 378+
 379+ return $params;
 380+
357381 }
358382
359383 public function getDescription() {
Index: trunk/phase3/includes/job/UploadFromUrlJob.php
@@ -10,6 +10,8 @@
1111 * @ingroup JobQueue
1212 */
1313 class UploadFromUrlJob extends Job {
 14+ public $upload;
 15+ protected $user;
1416
1517 public function __construct( $title, $params, $id = 0 ) {
1618 parent::__construct( 'uploadFromUrl', $title, $params, $id );
@@ -17,8 +19,8 @@
1820
1921 public function run() {
2022 # Initialize this object and the upload object
21 - $upload = new UploadFromUrl();
22 - $upload->initialize(
 23+ $this->upload = new UploadFromUrl();
 24+ $this->upload->initialize(
2325 $this->title->getText(),
2426 $this->params['url'],
2527 false
@@ -26,34 +28,47 @@
2729 $this->user = User::newFromName( $this->params['userName'] );
2830
2931 # Fetch the file
30 - $status = $upload->fetchFile();
 32+ $status = $this->upload->fetchFile();
3133 if ( !$status->isOk() ) {
3234 $this->leaveMessage( $status );
3335 return;
3436 }
3537
 38+ # Verify upload
 39+ $result = $this->upload->verifyUpload();
 40+ if ( $result['status'] != UploadBase::OK ) {
 41+ $status = $this->upload->convertVerifyErrorToStatus( $result );
 42+ $this->leaveMessage( $status );
 43+ return;
 44+ }
 45+
3646 # Check warnings
3747 if ( !$this->params['ignoreWarnings'] ) {
38 - $warnings = $this->checkWarnings();
 48+ $warnings = $this->upload->checkWarnings();
3949 if ( $warnings ) {
4050 if ( $this->params['leaveMessage'] ) {
4151 $this->user->leaveUserMessage(
4252 wfMsg( 'upload-warning-subj' ),
43 - wfMsg( 'upload-warning-msg', $this->params['sessionKey'] )
 53+ wfMsg( 'upload-warning-msg',
 54+ $this->params['sessionKey'],
 55+ $this->params['url'] )
4456 );
4557 } else {
4658 $this->storeResultInSession( 'Warning',
4759 'warnings', $warnings );
4860 }
 61+
 62+ // FIXME: stash in session
4963 return;
5064 }
5165 }
5266
5367 # Perform the upload
54 - $status = $upload->performUpload(
 68+ $status = $this->upload->performUpload(
5569 $this->params['comment'],
5670 $this->params['pageText'],
57 - $this->params['watch']
 71+ $this->params['watch'],
 72+ $this->user
5873 );
5974 $this->leaveMessage( $status );
6075 }
@@ -67,13 +82,17 @@
6883 protected function leaveMessage( $status ) {
6984 if ( $this->params['leaveMessage'] ) {
7085 if ( $status->isGood() ) {
71 - $file = $this->getLocalFile();
72 -
73 - $this->user->leaveUserMessage( wfMsg( 'successfulupload' ),
74 - wfMsg( 'upload-success-msg', $file->getDescriptionUrl() ) );
 86+ $this->user->leaveUserMessage( wfMsg( 'upload-success-subj' ),
 87+ wfMsg( 'upload-success-msg',
 88+ $this->upload->getTitle()->getText(),
 89+ $this->params['url']
 90+ ) );
7591 } else {
7692 $this->user->leaveUserMessage( wfMsg( 'upload-failure-subj' ),
77 - wfMsg( 'upload-failure-msg', $status->getWikiText() ) );
 93+ wfMsg( 'upload-failure-msg',
 94+ $status->getWikiText(),
 95+ $this->params['url']
 96+ ) );
7897 }
7998 } else {
8099 if ( $status->isOk() ) {
@@ -89,6 +108,7 @@
90109
91110 /**
92111 * Store a result in the session data
 112+ * THIS IS BROKEN. $_SESSION does not exist when using runJobs.php
93113 *
94114 * @param $result string The result (Success|Warning|Failure)
95115 * @param $dataKey string The key of the extra data
Index: trunk/phase3/includes/DefaultSettings.php
@@ -425,6 +425,11 @@
426426 * The timeout for copy uploads is set by $wgHTTPTimeout.
427427 */
428428 $wgAllowCopyUploads = false;
 429+/**
 430+ * Allow asynchronous copy uploads.
 431+ * This feature is experimental.
 432+ */
 433+$wgAllowAsyncCopyUploads = false;
429434
430435 /**
431436 * Max size for uploads, in bytes. Applies to all uploads.

Follow-up revisions

RevisionCommit summaryAuthorDate
r70138Follow-up r70137, messages:...btongminh13:56, 29 July 2010
r72475Follow-up r70137: Made asynchronous upload working a bit more. It now fully w...btongminh10:18, 6 September 2010
r81910Follow-up r70137: Unconditionalize the asyncdownload params and throw asyncco...btongminh18:24, 10 February 2011

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r70135Uploading to a protected title will allow the user to choose a new name inste...btongminh13:04, 29 July 2010

Comments

#Comment by Nikerabbit (talk | contribs)   15:39, 29 July 2010

So is this related to the code that leaves broken messages for my talk page: http://translatewiki.net/wiki/User_talk:Nike#Successful_upload_(2)_4565

#Comment by Bryan (talk | contribs)   17:11, 29 July 2010

Yes, apparently somebody checked in code that was not tested. This is fixed with r70138.

#Comment by NeilK (talk | contribs)   22:00, 24 September 2010

The adding of conditional API parameters is a bit controversial. We don't do this anywhere else.

And it changes the assumption that all API parameters will be assigned default values or be correctly typed. You'll have to drag that global around to every method and check the global before you look at the params.

I consulted a bit with Roan Kattouw and we know what you're trying to do, but are wondering if this can't be done in some other way. No clear advice just yet. Maybe watch wikitech-l since i have a similar use case.

#Comment by Reedy (talk | contribs)   01:29, 8 February 2011

The only "other" way, is to do it unconditionally, and then just have an error thrown if asyncdownload is passed, but $wgAllowAsyncCopyUploads is false. leavemessage we don't care about...

#Comment by Reedy (talk | contribs)   01:42, 8 February 2011

Per the below, if you end up back on this revision in future, finding this is the cause for possibly confusing parameter existance, feel free to log a bug for it to be changed to being more explicit (ie give an error, rather than optionally showing the parameters), or poke me to do it.

<Reedy> TimStarling, it's 2 minutes work to refactor it to do it unconditionally, and error if the async isn't enabled. It keeps the same behaviour, and seems sensible to do so <TimStarling> Reedy: personally, I'm fine with it how it is <TimStarling> you either get a "parameter does not exist" message, or a "copy uploads disabled" message <Reedy> Hmm, yeah <Reedy> Which will easily point the person to the issue <TimStarling> maybe "copy uploads disabled" is slightly more informative, but either way, the client can handle it <Reedy> If it becomes a problem, we can always change it over <TimStarling> yes

Status & tagging log