r70095 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r70094‎ | r70095 | r70096 >
Date:17:14, 28 July 2010
Author:btongminh
Status:ok (Comments)
Tags:
Comment:
Restructured upload-by-url:
* In ApiUpload: moved stuff that is checking instead of actual uploading out of performUpload method
* Made UploadFromUrl conform to standards:
** In initialize* do only initialization, no actual work
** Moved file fetching to fetchFile
** Consistent use of tempnam()
** Perform the uploading in performUpload, don't define our own doUpload method
* Moved almost all job magic to the UploadFromUrlJob class. This way the job is almost a regular client, and we don't need many special cases to deal with async uploading.
* Made leaving a message optional; results will be stored in the session otherwise

I did not actually test the async uploading, because I first wanted to commit a properly working synchronous upload-by-url system.
Modified paths:
  • /trunk/phase3/includes/api/ApiUpload.php (modified) (history)
  • /trunk/phase3/includes/job/UploadFromUrlJob.php (modified) (history)
  • /trunk/phase3/includes/specials/SpecialUpload.php (modified) (history)
  • /trunk/phase3/includes/upload/UploadBase.php (modified) (history)
  • /trunk/phase3/includes/upload/UploadFromUrl.php (modified) (history)

Diff [purge]

Index: trunk/phase3/includes/upload/UploadFromUrl.php
@@ -9,8 +9,7 @@
1010 * @author Michael Dale
1111 */
1212 class UploadFromUrl extends UploadBase {
13 - protected $mTempDownloadPath;
14 - protected $comment, $watchList, $ignoreWarnings;
 13+ protected $mAsync, $mUrl;
1514
1615 /**
1716 * Checks if the user is allowed to use the upload-by-URL feature. If the
@@ -33,57 +32,25 @@
3433
3534 /**
3635 * Entry point for API upload
37 - * @return bool true on success
 36+ *
 37+ * @param $name string
 38+ * @param $url string
 39+ * @param $async mixed Whether the download should be performed
 40+ * asynchronous. False for synchronous, async or async-leavemessage for
 41+ * asynchronous download.
3842 */
39 - public function initialize( $name, $url, $comment, $watchList = null, $ignoreWarn = null, $async = 'async' ) {
 43+ public function initialize( $name, $url, $async = false ) {
4044 global $wgUser;
41 -
42 - if ( !Http::isValidURI( $url ) ) {
43 - return Status::newFatal( 'http-invalid-url' );
44 - }
45 - $params = array(
46 - 'userName' => $wgUser->getName(),
47 - 'userID' => $wgUser->getID(),
48 - 'url' => trim( $url ),
49 - 'timestamp' => wfTimestampNow(),
50 - 'comment' => $comment,
51 - 'watchlist' => $watchList,
52 - 'ignorewarnings' => $ignoreWarn );
53 -
54 - $title = Title::newFromText( $name );
55 -
56 - if ( $async == 'async' ) {
57 - $job = new UploadFromUrlJob( $title, $params );
58 - return $job->insert();
59 - } else {
60 - $this->mUrl = trim( $url );
61 - $this->comment = $comment;
62 - $this->watchList = $watchList;
63 - $this->ignoreWarnings = $ignoreWarn;
64 - $this->mDesiredDestName = $title;
65 - $this->getTitle();
66 -
67 - return true;
68 - }
 45+
 46+ $this->mUrl = $url;
 47+ $this->mAsync = $async;
 48+
 49+ $tempPath = $async ? null : $this->makeTemporaryFile();
 50+ # File size and removeTempFile will be filled in later
 51+ $this->initializePathInfo( $name, $tempPath, 0, false );
6952 }
7053
7154 /**
72 - * Initialize a queued download
73 - * @param $job Job
74 - */
75 - public function initializeFromJob( $job ) {
76 - global $wgTmpDirectory;
77 -
78 - $this->mUrl = $job->params['url'];
79 - $this->mTempPath = tempnam( $wgTmpDirectory, 'COPYUPLOAD' );
80 - $this->mDesiredDestName = $job->title;
81 - $this->comment = $job->params['comment'];
82 - $this->watchList = $job->params['watchlist'];
83 - $this->ignoreWarnings = $job->params['ignorewarnings'];
84 - $this->getTitle();
85 - }
86 -
87 - /**
8855 * Entry point for SpecialUpload
8956 * @param $request Object: WebRequest object
9057 */
@@ -94,10 +61,7 @@
9562 return $this->initialize(
9663 $desiredDestName,
9764 $request->getVal( 'wpUploadFileURL' ),
98 - $request->getVal( 'wpUploadDescription' ),
99 - $request->getVal( 'wpWatchThis' ),
100 - $request->getVal( 'wpIgnoreWarnings' ),
101 - 'async'
 65+ false
10266 );
10367 }
10468
@@ -112,23 +76,34 @@
11377 && Http::isValidURI( $url )
11478 && $wgUser->isAllowed( 'upload_by_url' );
11579 }
 80+
11681
 82+ public function fetchFile() {
 83+ if ( !Http::isValidURI( $this->mUrl ) ) {
 84+ return Status::newFatal( 'http-invalid-url' );
 85+ }
 86+
 87+ if ( !$this->mAsync ) {
 88+ return $this->reallyFetchFile();
 89+ }
 90+ return Status::newGood();
 91+ }
 92+ protected function makeTemporaryFile() {
 93+ return tempnam( wfTempDir(), 'URL' );
 94+ }
11795 private function saveTempFile( $req ) {
118 - $filename = tempnam( wfTempDir(), 'URL' );
119 - if ( $filename === false ) {
 96+ if ( $this->mTempPath === false ) {
12097 return Status::newFatal( 'tmp-create-error' );
12198 }
122 - if ( file_put_contents( $filename, $req->getContent() ) === false ) {
 99+ if ( file_put_contents( $this->mTempPath, $req->getContent() ) === false ) {
123100 return Status::newFatal( 'tmp-write-error' );
124101 }
125102
126 - $this->mTempPath = $filename;
127 - $this->mFileSize = filesize( $filename );
 103+ $this->mFileSize = filesize( $this->mTempPath );
128104
129105 return Status::newGood();
130106 }
131 -
132 - public function retrieveFileFromUrl() {
 107+ protected function reallyFetchFile() {
133108 $req = HttpRequest::factory( $this->mUrl );
134109 $status = $req->execute();
135110
@@ -145,32 +120,34 @@
146121 return $status;
147122 }
148123
149 - public function doUpload() {
150 - global $wgUser;
151 -
152 - $status = $this->retrieveFileFromUrl();
153 -
154 - if ( $status->isGood() ) {
155 -
156 - $v = $this->verifyUpload();
157 - if ( $v['status'] !== UploadBase::OK ) {
158 - return $this->convertVerifyErrorToStatus( $v['status'], $v['details'] );
159 - }
160 -
161 - $status = $this->getLocalFile()->upload( $this->mTempPath, $this->comment,
162 - $this->comment, File::DELETE_SOURCE, $this->mFileProps, false, $wgUser );
 124+ public function performUpload( $comment, $pageText, $watch, $user ) {
 125+ if ( $this->mAsync ) {
 126+ $sessionKey = $this->insertJob( $comment, $pageText, $watch, $user );
 127+
 128+ $status = new Status;
 129+ $status->error( 'async', $sessionKey );
 130+ return $status;
163131 }
 132+
 133+ return parent::performUpload( $comment, $pageText, $watch, $user );
 134+ }
164135
165 - if ( $status->isGood() ) {
166 - $file = $this->getLocalFile();
167 -
168 - $wgUser->leaveUserMessage( wfMsg( 'successfulupload' ),
169 - wfMsg( 'upload-success-msg', $file->getDescriptionUrl() ) );
170 - } else {
171 - $wgUser->leaveUserMessage( wfMsg( 'upload-failure-subj' ),
172 - wfMsg( 'upload-failure-msg', $status->getWikiText() ) );
173 - }
174 -
175 - return $status;
 136+
 137+ protected function insertJob( $comment, $pageText, $watch, $user ) {
 138+ $sessionKey = $this->getSessionKey();
 139+ $job = new UploadFromUrlJob( $this->getTitle(), array(
 140+ 'url' => $this->mUrl,
 141+ 'comment' => $comment,
 142+ 'pageText' => $pageText,
 143+ 'watch' => $watch,
 144+ 'userName' => $user->getName(),
 145+ 'leaveMessage' => $this->mAsync == 'async-leavemessage',
 146+ 'ignoreWarnings' => $this->mIgnoreWarnings,
 147+ 'sessionKey' => $sessionKey,
 148+ ) );
 149+ $job->insert();
 150+ return $sessionKey;
176151 }
 152+
 153+
177154 }
Index: trunk/phase3/includes/upload/UploadBase.php
@@ -606,14 +606,16 @@
607607 *
608608 * @return Integer: session key
609609 */
610 - public function stashSession() {
 610+ public function stashSession( $key = null ) {
611611 $status = $this->saveTempUploadedFile( $this->mDestName, $this->mTempPath );
612612 if( !$status->isOK() ) {
613613 # Couldn't save the file.
614614 return false;
615615 }
616616
617 - $key = $this->getSessionKey();
 617+ if ( is_null( $key ) ) {
 618+ $key = $this->getSessionKey();
 619+ }
618620 $_SESSION[self::SESSION_KEYNAME][$key] = array(
619621 'mTempPath' => $status->value,
620622 'mFileSize' => $this->mFileSize,
Index: trunk/phase3/includes/api/ApiUpload.php
@@ -59,7 +59,6 @@
6060 $this->dieUsageMsg( array( 'missingparam', 'filename' ) );
6161 }
6262
63 -
6463 if ( $this->mParams['sessionkey'] ) {
6564 // Upload stashed in a previous request
6665 $sessionData = $request->getSessionData( UploadBase::SESSION_KEYNAME );
@@ -81,52 +80,59 @@
8281 );
8382 } elseif ( isset( $this->mParams['url'] ) ) {
8483 // Make sure upload by URL is enabled:
85 - if ( !$wgAllowCopyUploads ) {
 84+ if ( !UploadFromUrl::isEnabled() ) {
8685 $this->dieUsageMsg( array( 'copyuploaddisabled' ) );
8786 }
88 -
 87+
 88+ $async = false;
 89+ if ( $this->mParams['asyncdownload'] ) {
 90+ if ( $this->mParams['leavemessage'] ) {
 91+ $async = 'async-leavemessage';
 92+ } else {
 93+ $async = 'async';
 94+ }
 95+ }
8996 $this->mUpload = new UploadFromUrl;
90 - $async = $this->mParams['asyncdownload'] ? 'async' : null;
91 - $this->checkPermissions( $wgUser );
 97+ $this->mUpload->initialize( $this->mParams['filename'],
 98+ $this->mParams['url'], $async );
9299
93 - $result = $this->mUpload->initialize(
94 - $this->mParams['filename'],
95 - $this->mParams['url'],
96 - $this->mParams['comment'],
97 - $this->mParams['watchlist'],
98 - $this->mParams['ignorewarnings'],
99 - $async );
100 -
101 - if ( $async ) {
102 - $this->getResult()->addValue( null,
103 - $this->getModuleName(),
104 - array( 'queued' => $result ) );
105 - return;
106 - }
107 -
108 - $status = $this->mUpload->retrieveFileFromUrl();
109 - if ( !$status->isGood() ) {
110 - $this->getResult()->addValue( null,
111 - $this->getModuleName(),
112 - array( 'error' => $status ) );
113 - return;
114 - }
115100 }
116 -
117 -
118 - $this->checkPermissions( $wgUser );
119 -
120101 if ( !isset( $this->mUpload ) ) {
121102 $this->dieUsage( 'No upload module set', 'nomodule' );
122103 }
 104+
 105+ // First check permission to upload
 106+ $this->checkPermissions( $wgUser );
 107+ // Check permission to upload this file
 108+ $permErrors = $this->mUpload->verifyPermissions( $wgUser );
 109+ if ( $permErrors !== true ) {
 110+ // Todo: more specific error message
 111+ $this->dieUsageMsg( array( 'badaccess-groups' ) );
 112+ }
 113+
 114+ // Fetch the file
 115+ $status = $this->mUpload->fetchFile();
 116+ if ( !$status->isGood() ) {
 117+ $errors = $status->getErrorsArray();
 118+ $error = array_shift( $errors[0] );
 119+ $this->dieUsage( 'Error fetching file from remote source', $error, 0, $errors[0] );
 120+ }
123121
124 - // Perform the upload
125 - $result = $this->performUpload();
 122+ // Check if the uploaded file is sane
 123+ $this->verifyUpload();
126124
 125+ // Check warnings if necessary
 126+ $warnings = $this->checkForWarnings();
 127+ if ( $warnings ) {
 128+ $this->getResult()->addValue( null, $this->getModuleName(), $warnings );
 129+ } else {
 130+ // Perform the upload
 131+ $result = $this->performUpload();
 132+ $this->getResult()->addValue( null, $this->getModuleName(), $result );
 133+ }
 134+
127135 // Cleanup any temporary mess
128136 $this->mUpload->cleanupTempFile();
129 -
130 - $this->getResult()->addValue( null, $this->getModuleName(), $result );
131137 }
132138
133139 /**
@@ -252,18 +258,7 @@
253259
254260 protected function performUpload() {
255261 global $wgUser;
256 - $permErrors = $this->mUpload->verifyPermissions( $wgUser );
257 - if ( $permErrors !== true ) {
258 - $this->dieUsageMsg( array( 'badaccess-groups' ) );
259 - }
260 -
261 - $this->verifyUpload();
262 -
263 - $warnings = $this->checkForWarnings();
264 - if ( isset( $warnings ) ) {
265 - return $warnings;
266 - }
267 -
 262+
268263 // Use comment as initial page text by default
269264 if ( is_null( $this->mParams['text'] ) ) {
270265 $this->mParams['text'] = $this->mParams['comment'];
@@ -329,6 +324,7 @@
330325 'file' => null,
331326 'url' => null,
332327 'asyncdownload' => false,
 328+ 'leavemessage' => false,
333329 'sessionkey' => null,
334330 );
335331 return $params;
@@ -346,9 +342,8 @@
347343 'file' => 'File contents',
348344 'url' => 'Url to fetch the file from',
349345 'asyncdownload' => 'Make fetching a URL asyncronous',
350 - 'sessionkey' => array(
351 - 'Session key returned by a previous upload that failed due to warnings',
352 - ),
 346+ 'leavemessage' => 'If asyncdownload is used, leave a message on the user talk page if finished',
 347+ 'sessionkey' => 'Session key returned by a previous upload that failed due to warnings',
353348 );
354349 }
355350
Index: trunk/phase3/includes/job/UploadFromUrlJob.php
@@ -1,7 +1,11 @@
22 <?php
33
44 /**
5 - * Job for email notification mails
 5+ * Job for asynchronous upload-by-url.
 6+ *
 7+ * This job is in fact an interface to UploadFromUrl, which is designed such
 8+ * that it does not require any globals. If it does, fix it elsewhere, do not
 9+ * add globals in here.
610 *
711 * @ingroup JobQueue
812 */
@@ -12,19 +16,87 @@
1317 }
1418
1519 public function run() {
16 - global $wgUser;
17 -
18 - if ( $this->params['userID'] ) {
19 - $wgUser = User::newFromId( $this->params['userID'] );
 20+ # Initialize this object and the upload object
 21+ $upload = new UploadFromUrl();
 22+ $upload->initialize(
 23+ $this->title->getText(),
 24+ $this->params['url'],
 25+ false
 26+ );
 27+ $this->user = User::newFromName( $this->params['userName'] );
 28+
 29+ # Fetch the file
 30+ $status = $upload->fetchFile();
 31+ if ( !$status->isOk() ) {
 32+ $this->leaveMessage( $status );
 33+ return;
 34+ }
 35+
 36+ # Check warnings
 37+ if ( !$this->params['ignoreWarnings'] ) {
 38+ $warnings = $this->checkWarnings();
 39+ if ( $warnings ) {
 40+ if ( $this->params['leaveMessage'] ) {
 41+ $this->user->leaveUserMessage(
 42+ wfMsg( 'upload-warning-subj' ),
 43+ wfMsg( 'upload-warning-msg', $this->params['sessionKey'] )
 44+ );
 45+ } else {
 46+ $this->storeResultInSession( 'Warning',
 47+ 'warnings', $warnings );
 48+ }
 49+ return;
 50+ }
 51+ }
 52+
 53+ # Perform the upload
 54+ $status = $upload->performUpload(
 55+ $this->params['comment'],
 56+ $this->params['pageText'],
 57+ $this->params['watch']
 58+ );
 59+ $this->leaveMessage( $status );
 60+ }
 61+
 62+ /**
 63+ * Leave a message on the user talk page or in the session according to
 64+ * $params['leaveMessage'].
 65+ *
 66+ * @param $status Status
 67+ */
 68+ protected function leaveMessage( $status ) {
 69+ if ( $this->params['leaveMessage'] ) {
 70+ if ( $status->isGood() ) {
 71+ $file = $this->getLocalFile();
 72+
 73+ $this->user->leaveUserMessage( wfMsg( 'successfulupload' ),
 74+ wfMsg( 'upload-success-msg', $file->getDescriptionUrl() ) );
 75+ } else {
 76+ $this->user->leaveUserMessage( wfMsg( 'upload-failure-subj' ),
 77+ wfMsg( 'upload-failure-msg', $status->getWikiText() ) );
 78+ }
2079 } else {
21 - $wgUser = new User;
 80+ if ( $status->isOk() ) {
 81+ $this->storeResultInSession( 'Success',
 82+ 'filename', $this->getLocalFile()->getName() );
 83+ } else {
 84+ $this->storeResultInSession( 'Failure',
 85+ 'errors', $status->getErrorsArray() );
 86+ }
 87+
2288 }
23 - $wgUser->mEffectiveGroups[] = 'sysop';
24 - $wgUser->mRights = null;
 89+ }
2590
26 - $upload = new UploadFromUrl();
27 - $upload->initializeFromJob( $this );
28 -
29 - return $upload->doUpload();
 91+ /**
 92+ * Store a result in the session data
 93+ *
 94+ * @param $result string The result (Success|Warning|Failure)
 95+ * @param $dataKey string The key of the extra data
 96+ * @param $dataKey mixed The extra data itself
 97+ */
 98+ protected function storeResultInSession( $result, $dataKey, $dataValue ) {
 99+ $session &= $_SESSION[UploadBase::getSessionKeyname()][$this->params['sessionKey']];
 100+ $session['result'] = $result;
 101+ $session[$dataKey] = $dataValue;
30102 }
31103 }
Index: trunk/phase3/includes/specials/SpecialUpload.php
@@ -422,10 +422,6 @@
423423 return;
424424 }
425425
426 - if( $this->mUpload instanceOf UploadFromUrl ) {
427 - return $this->showUploadError( wfMsg( 'uploadfromurl-queued' ) );
428 - }
429 -
430426 // Fetch the file if required
431427 $status = $this->mUpload->fetchFile();
432428 if( !$status->isOK() ) {

Follow-up revisions

RevisionCommit summaryAuthorDate
r70252Follow up r70095. Remove the now unused $wgAllowCopyUploads global.platonides23:26, 31 July 2010

Comments

#Comment by Bryan (talk | contribs)   07:17, 8 February 2011

Only the changes in ApiUpload need review for now; the other code paths are disabled on WMF.

#Comment by 😂 (talk | contribs)   03:23, 11 February 2011

Skipping review of the upload-by-url stuff, but the changes to ApiUpload look fine.

Status & tagging log