Index: trunk/phase3/includes/upload/UploadFromUrl.php |
— | — | @@ -9,8 +9,7 @@ |
10 | 10 | * @author Michael Dale |
11 | 11 | */ |
12 | 12 | class UploadFromUrl extends UploadBase { |
13 | | - protected $mTempDownloadPath; |
14 | | - protected $comment, $watchList, $ignoreWarnings; |
| 13 | + protected $mAsync, $mUrl; |
15 | 14 | |
16 | 15 | /** |
17 | 16 | * Checks if the user is allowed to use the upload-by-URL feature. If the |
— | — | @@ -33,57 +32,25 @@ |
34 | 33 | |
35 | 34 | /** |
36 | 35 | * 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. |
38 | 42 | */ |
39 | | - public function initialize( $name, $url, $comment, $watchList = null, $ignoreWarn = null, $async = 'async' ) { |
| 43 | + public function initialize( $name, $url, $async = false ) { |
40 | 44 | 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 ); |
69 | 52 | } |
70 | 53 | |
71 | 54 | /** |
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 | | - /** |
88 | 55 | * Entry point for SpecialUpload |
89 | 56 | * @param $request Object: WebRequest object |
90 | 57 | */ |
— | — | @@ -94,10 +61,7 @@ |
95 | 62 | return $this->initialize( |
96 | 63 | $desiredDestName, |
97 | 64 | $request->getVal( 'wpUploadFileURL' ), |
98 | | - $request->getVal( 'wpUploadDescription' ), |
99 | | - $request->getVal( 'wpWatchThis' ), |
100 | | - $request->getVal( 'wpIgnoreWarnings' ), |
101 | | - 'async' |
| 65 | + false |
102 | 66 | ); |
103 | 67 | } |
104 | 68 | |
— | — | @@ -112,23 +76,34 @@ |
113 | 77 | && Http::isValidURI( $url ) |
114 | 78 | && $wgUser->isAllowed( 'upload_by_url' ); |
115 | 79 | } |
| 80 | + |
116 | 81 | |
| 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 | + } |
117 | 95 | private function saveTempFile( $req ) { |
118 | | - $filename = tempnam( wfTempDir(), 'URL' ); |
119 | | - if ( $filename === false ) { |
| 96 | + if ( $this->mTempPath === false ) { |
120 | 97 | return Status::newFatal( 'tmp-create-error' ); |
121 | 98 | } |
122 | | - if ( file_put_contents( $filename, $req->getContent() ) === false ) { |
| 99 | + if ( file_put_contents( $this->mTempPath, $req->getContent() ) === false ) { |
123 | 100 | return Status::newFatal( 'tmp-write-error' ); |
124 | 101 | } |
125 | 102 | |
126 | | - $this->mTempPath = $filename; |
127 | | - $this->mFileSize = filesize( $filename ); |
| 103 | + $this->mFileSize = filesize( $this->mTempPath ); |
128 | 104 | |
129 | 105 | return Status::newGood(); |
130 | 106 | } |
131 | | - |
132 | | - public function retrieveFileFromUrl() { |
| 107 | + protected function reallyFetchFile() { |
133 | 108 | $req = HttpRequest::factory( $this->mUrl ); |
134 | 109 | $status = $req->execute(); |
135 | 110 | |
— | — | @@ -145,32 +120,34 @@ |
146 | 121 | return $status; |
147 | 122 | } |
148 | 123 | |
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; |
163 | 131 | } |
| 132 | + |
| 133 | + return parent::performUpload( $comment, $pageText, $watch, $user ); |
| 134 | + } |
164 | 135 | |
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; |
176 | 151 | } |
| 152 | + |
| 153 | + |
177 | 154 | } |
Index: trunk/phase3/includes/upload/UploadBase.php |
— | — | @@ -606,14 +606,16 @@ |
607 | 607 | * |
608 | 608 | * @return Integer: session key |
609 | 609 | */ |
610 | | - public function stashSession() { |
| 610 | + public function stashSession( $key = null ) { |
611 | 611 | $status = $this->saveTempUploadedFile( $this->mDestName, $this->mTempPath ); |
612 | 612 | if( !$status->isOK() ) { |
613 | 613 | # Couldn't save the file. |
614 | 614 | return false; |
615 | 615 | } |
616 | 616 | |
617 | | - $key = $this->getSessionKey(); |
| 617 | + if ( is_null( $key ) ) { |
| 618 | + $key = $this->getSessionKey(); |
| 619 | + } |
618 | 620 | $_SESSION[self::SESSION_KEYNAME][$key] = array( |
619 | 621 | 'mTempPath' => $status->value, |
620 | 622 | 'mFileSize' => $this->mFileSize, |
Index: trunk/phase3/includes/api/ApiUpload.php |
— | — | @@ -59,7 +59,6 @@ |
60 | 60 | $this->dieUsageMsg( array( 'missingparam', 'filename' ) ); |
61 | 61 | } |
62 | 62 | |
63 | | - |
64 | 63 | if ( $this->mParams['sessionkey'] ) { |
65 | 64 | // Upload stashed in a previous request |
66 | 65 | $sessionData = $request->getSessionData( UploadBase::SESSION_KEYNAME ); |
— | — | @@ -81,52 +80,59 @@ |
82 | 81 | ); |
83 | 82 | } elseif ( isset( $this->mParams['url'] ) ) { |
84 | 83 | // Make sure upload by URL is enabled: |
85 | | - if ( !$wgAllowCopyUploads ) { |
| 84 | + if ( !UploadFromUrl::isEnabled() ) { |
86 | 85 | $this->dieUsageMsg( array( 'copyuploaddisabled' ) ); |
87 | 86 | } |
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 | + } |
89 | 96 | $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 ); |
92 | 99 | |
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 | | - } |
115 | 100 | } |
116 | | - |
117 | | - |
118 | | - $this->checkPermissions( $wgUser ); |
119 | | - |
120 | 101 | if ( !isset( $this->mUpload ) ) { |
121 | 102 | $this->dieUsage( 'No upload module set', 'nomodule' ); |
122 | 103 | } |
| 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 | + } |
123 | 121 | |
124 | | - // Perform the upload |
125 | | - $result = $this->performUpload(); |
| 122 | + // Check if the uploaded file is sane |
| 123 | + $this->verifyUpload(); |
126 | 124 | |
| 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 | + |
127 | 135 | // Cleanup any temporary mess |
128 | 136 | $this->mUpload->cleanupTempFile(); |
129 | | - |
130 | | - $this->getResult()->addValue( null, $this->getModuleName(), $result ); |
131 | 137 | } |
132 | 138 | |
133 | 139 | /** |
— | — | @@ -252,18 +258,7 @@ |
253 | 259 | |
254 | 260 | protected function performUpload() { |
255 | 261 | 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 | + |
268 | 263 | // Use comment as initial page text by default |
269 | 264 | if ( is_null( $this->mParams['text'] ) ) { |
270 | 265 | $this->mParams['text'] = $this->mParams['comment']; |
— | — | @@ -329,6 +324,7 @@ |
330 | 325 | 'file' => null, |
331 | 326 | 'url' => null, |
332 | 327 | 'asyncdownload' => false, |
| 328 | + 'leavemessage' => false, |
333 | 329 | 'sessionkey' => null, |
334 | 330 | ); |
335 | 331 | return $params; |
— | — | @@ -346,9 +342,8 @@ |
347 | 343 | 'file' => 'File contents', |
348 | 344 | 'url' => 'Url to fetch the file from', |
349 | 345 | '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', |
353 | 348 | ); |
354 | 349 | } |
355 | 350 | |
Index: trunk/phase3/includes/job/UploadFromUrlJob.php |
— | — | @@ -1,7 +1,11 @@ |
2 | 2 | <?php |
3 | 3 | |
4 | 4 | /** |
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. |
6 | 10 | * |
7 | 11 | * @ingroup JobQueue |
8 | 12 | */ |
— | — | @@ -12,19 +16,87 @@ |
13 | 17 | } |
14 | 18 | |
15 | 19 | 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 | + } |
20 | 79 | } 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 | + |
22 | 88 | } |
23 | | - $wgUser->mEffectiveGroups[] = 'sysop'; |
24 | | - $wgUser->mRights = null; |
| 89 | + } |
25 | 90 | |
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; |
30 | 102 | } |
31 | 103 | } |
Index: trunk/phase3/includes/specials/SpecialUpload.php |
— | — | @@ -422,10 +422,6 @@ |
423 | 423 | return; |
424 | 424 | } |
425 | 425 | |
426 | | - if( $this->mUpload instanceOf UploadFromUrl ) { |
427 | | - return $this->showUploadError( wfMsg( 'uploadfromurl-queued' ) ); |
428 | | - } |
429 | | - |
430 | 426 | // Fetch the file if required |
431 | 427 | $status = $this->mUpload->fetchFile(); |
432 | 428 | if( !$status->isOK() ) { |