r88242 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r88241‎ | r88242 | r88243 >
Date:15:19, 16 May 2011
Author:dale
Status:deferred
Tags:
Comment:
update to ResumableUploadHandler names and api updates ( see r88236 )
Modified paths:
  • /trunk/extensions/ResumableUpload/ApiFirefoggChunkedUpload.php (deleted) (history)
  • /trunk/extensions/ResumableUpload/ApiResumableUpload.php (added) (history)
  • /trunk/extensions/ResumableUpload/FirefoggChunkedUpload.i18n.php (deleted) (history)
  • /trunk/extensions/ResumableUpload/FirefoggChunkedUpload.php (deleted) (history)
  • /trunk/extensions/ResumableUpload/FirefoggChunkedUploadHandler.php (deleted) (history)
  • /trunk/extensions/ResumableUpload/README (added) (history)
  • /trunk/extensions/ResumableUpload/ResumableUpload.i18n.php (added) (history)
  • /trunk/extensions/ResumableUpload/ResumableUpload.php (added) (history)
  • /trunk/extensions/ResumableUpload/ResumableUploadHandler.php (added) (history)
  • /trunk/extensions/ResumableUpload/tests/UploadFromChunksTest.php (deleted) (history)
  • /trunk/extensions/ResumableUpload/tests/bootstrap.php (deleted) (history)
  • /trunk/extensions/ResumableUpload/tests/phpunit (added) (history)
  • /trunk/extensions/ResumableUpload/tests/phpunit.xml (deleted) (history)
  • /trunk/extensions/ResumableUpload/tests/phpunit/ResumableUploadTest.php (added) (history)
  • /trunk/extensions/ResumableUpload/tests/phpunit/bootstrap.php (added) (history)
  • /trunk/extensions/ResumableUpload/tests/phpunit/phpunit.xml (added) (history)
  • /trunk/extensions/ResumableUpload/tests/qunit (added) (history)
  • /trunk/extensions/ResumableUpload/tests/qunit/README (added) (history)
  • /trunk/extensions/ResumableUpload/tests/qunit/ResumableUploadTest.js (added) (history)
  • /trunk/extensions/ResumableUpload/tests/qunit/index.html (added) (history)

Diff [purge]

Index: trunk/extensions/ResumableUpload/ApiFirefoggChunkedUpload.php
@@ -1,200 +0,0 @@
2 -<?php
3 -if ( !defined( 'MEDIAWIKI' ) ) die();
4 -/**
5 - * @copyright Copyright © 2010 Mark A. Hershberger <mah@everybody.org>
6 - * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
7 - */
8 -
9 -class ApiFirefoggChunkedUpload extends ApiUpload {
10 - private $mUpload = null, $mParams = null;
11 -
12 - public function execute() {
13 - global $wgUser;
14 -
15 - // Check whether upload is enabled
16 - if ( !UploadBase::isEnabled() ) {
17 - $this->dieUsageMsg( array( 'uploaddisabled' ) );
18 - }
19 -
20 - $this->mParams = $this->extractRequestParams();
21 -
22 - $this->validateParams( $this->mParams );
23 -
24 - $request = $this->getMain()->getRequest();
25 - $this->mUpload = new FirefoggChunkedUploadHandler;
26 -
27 - $status = $this->mUpload->initialize(
28 - $request->getVal( 'done', null ),
29 - $request->getVal( 'filename', null ),
30 - $request->getVal( 'chunksession', null ),
31 - $request->getFileTempName( 'chunk' ),
32 - $request->getFileSize( 'chunk' ),
33 - $request->getSessionData( UploadBase::getSessionKeyname() )
34 - );
35 -
36 - if ( $status !== true ) {
37 - $this->dieUsage( $status, 'chunk-init-error' );
38 - }
39 -
40 - $ret = $this->performUpload( );
41 -
42 - if(is_array($ret)) {
43 - foreach($ret as $key => $val) {
44 - $this->getResult()->addValue(null, $key, $val);
45 - }
46 - } else {
47 - $this->dieUsage($ret, 'error');
48 - }
49 - }
50 -
51 - public function getUpload() { return $this->mUpload; }
52 -
53 - public function performUploadInit($comment, $pageText, $watchlist, $user) {
54 - $check = $this->mUpload->validateNameAndOverwrite();
55 - if( $check !== true ) {
56 - $this->getVerificationError( $check );
57 - }
58 -
59 - $session = $this->mUpload->setupChunkSession( $comment, $pageText, $watchlist );
60 - return array('uploadUrl' =>
61 - wfExpandUrl( wfScript( 'api' ) ) . "?" .
62 - wfArrayToCGI( array(
63 - 'action' => 'firefoggupload',
64 - 'token' => $user->editToken(),
65 - 'format' => 'json',
66 - 'chunksession' => $session,
67 - 'filename' => $this->mUpload->getDesiredName(),
68 - ) ) );
69 - }
70 -
71 - public function performUploadChunk() {
72 - $this->mUpload->setupChunkSession();
73 - $status = $this->mUpload->appendChunk();
74 - if ( !$status->isOK() ) {
75 - $this->dieUsage($status->getWikiText(), 'error');
76 - }
77 - return array('result' => 1, 'filesize' => $this->mUpload->getFileSize() );
78 - }
79 -
80 - public function performUploadDone($user) {
81 - $this->mUpload->finalizeFile();
82 - $status = parent::performUpload( $this->comment, $this->pageText, $this->watchlist, $user );
83 -
84 - if ( $status['result'] !== 'Success' ) {
85 - return $status;
86 - }
87 - $file = $this->mUpload->getLocalFile();
88 - return array('result' => 1, 'done' => 1, 'resultUrl' => wfExpandUrl( $file->getDescriptionUrl() ) );
89 - }
90 -
91 - /**
92 - * Handle a chunk of the upload. Overrides the parent method
93 - * because Chunked Uploading clients (i.e. Firefogg) require
94 - * specific API responses.
95 - * @see UploadBase::performUpload
96 - */
97 - public function performUpload( ) {
98 - wfDebug( "\n\n\performUpload(chunked): comment: " . $this->comment .
99 - ' pageText: ' . $this->pageText . ' watch: ' . $this->watchlist );
100 - $ret = "unknown error";
101 -
102 - global $wgUser;
103 - if ( $this->mUpload->getChunkMode() == FirefoggChunkedUploadHandler::INIT ) {
104 - $ret = $this->performUploadInit($this->comment, $this->pageText, $this->watchlist, $wgUser);
105 - } else if ( $this->mUpload->getChunkMode() == FirefoggChunkedUploadHandler::CHUNK ) {
106 - $ret = $this->performUploadChunk();
107 - } else if ( $this->mUpload->getChunkMode() == FirefoggChunkedUploadHandler::DONE ) {
108 - $ret = $this->performUploadDone($wgUser);
109 - }
110 -
111 - return $ret;
112 - }
113 -
114 - public function mustBePosted() {
115 - return true;
116 - }
117 -
118 - public function isWriteMode() {
119 - return true;
120 - }
121 -
122 - protected function validateParams( $params ) {
123 - if( $params['done'] ) {
124 - $required[] = 'chunksession';
125 - }
126 - if( $params['chunksession'] === null ) {
127 - $required[] = 'filename';
128 - $required[] = 'comment';
129 - $required[] = 'watchlist';
130 - $required[] = 'ignorewarnings';
131 - }
132 -
133 - foreach( $required as $arg ) {
134 - if ( !isset( $params[$arg] ) ) {
135 - $this->dieUsageMsg( array( 'missingparam', $arg ) );
136 - }
137 - }
138 - }
139 -
140 - public function getAllowedParams() {
141 - return array(
142 - 'filename' => null,
143 - 'token' => null,
144 - 'comment' => null,
145 - 'ignorewarnings' => false,
146 - 'chunksession' => null,
147 - 'chunk' => null,
148 - 'done' => false,
149 - 'watchlist' => array(
150 - ApiBase::PARAM_DFLT => 'preferences',
151 - ApiBase::PARAM_TYPE => array(
152 - 'watch',
153 - 'unwatch',
154 - 'preferences',
155 - 'nochange'
156 - ),
157 - ),
158 - );
159 - }
160 -
161 - public function getParamDescription() {
162 - return array(
163 - 'filename' => 'Target filename',
164 - 'token' => 'Edit token. You can get one of these through prop=info',
165 - 'comment' => 'Upload comment',
166 - 'watchlist' => 'Unconditionally add or remove the page from your watchlist, use preferences or do not change watch',
167 - 'ignorewarnings' => 'Ignore any warnings',
168 - 'chunksession' => 'The session key, established on the first contact during the chunked upload',
169 - 'chunk' => 'The data in this chunk of a chunked upload',
170 - 'done' => 'Set to 1 on the last chunk of a chunked upload',
171 - );
172 - }
173 -
174 - public function getDescription() {
175 - return array(
176 - 'Upload a file in chunks using the protocol documented at http://firefogg.org/dev/chunk_post.html'
177 - );
178 - }
179 -
180 - public function getPossibleErrors() {
181 - return array_merge(
182 - parent::getPossibleErrors(),
183 - array(
184 - array( 'missingparam' ),
185 - array( 'chunk-init-error' ),
186 - array( 'code' => 'chunk-init-error', 'info' => 'Insufficient information for initialization.' ),
187 - array( 'code' => 'chunked-error', 'info' => 'There was a problem initializing the chunked upload.' ),
188 - )
189 - );
190 - }
191 -
192 - public function getExamples() {
193 - return array(
194 - 'api.php?action=firefoggupload&filename=Wiki.png',
195 - );
196 - }
197 -
198 - public function getVersion() {
199 - return __CLASS__ . ': $Id$';
200 - }
201 -}
Index: trunk/extensions/ResumableUpload/FirefoggChunkedUpload.i18n.php
@@ -1,313 +0,0 @@
2 -<?php
3 -/**
4 - * Internationalisation file for extension FirefoggChunkedUploading.
5 - *
6 - * @file
7 - * @ingroup Extensions
8 - */
9 -
10 -$messages = array();
11 -
12 -$messages['en'] = array(
13 - 'firefoggcu-desc' => "Firefogg's Chunked Uploading protocol",
14 -);
15 -
16 -/** Message documentation (Message documentation)
17 - * @author Umherirrender
18 - */
19 -$messages['qqq'] = array(
20 - 'firefoggcu-desc' => '{{desc}}',
21 -);
22 -
23 -/** Belarusian (Taraškievica orthography) (Беларуская (тарашкевіца))
24 - * @author Wizardist
25 - */
26 -$messages['be-tarask'] = array(
27 - 'firefoggcu-desc' => 'Парцыённы пратакол загрузкі Firefogg',
28 -);
29 -
30 -/** Breton (Brezhoneg)
31 - * @author Fulup
32 - */
33 -$messages['br'] = array(
34 - 'firefoggcu-desc' => 'Protokol pellgargañ a-dammoù Firefogg',
35 -);
36 -
37 -/** Bosnian (Bosanski)
38 - * @author CERminator
39 - */
40 -$messages['bs'] = array(
41 - 'firefoggcu-desc' => 'Firefogg-ov protokol otpremanja',
42 -);
43 -
44 -/** Catalan (Català)
45 - * @author Paucabot
46 - */
47 -$messages['ca'] = array(
48 - 'firefoggcu-desc' => 'Protocol de Càrrega Fragmentada de Firefogg',
49 -);
50 -
51 -/** Welsh (Cymraeg)
52 - * @author Lloffiwr
53 - * @author Xxglennxx
54 - */
55 -$messages['cy'] = array(
56 - 'firefoggcu-desc' => "Protocol Firefogg i Uwchlwytho'n Dalpiau",
57 -);
58 -
59 -/** German (Deutsch)
60 - * @author Purodha
61 - * @author Umherirrender
62 - */
63 -$messages['de'] = array(
64 - 'firefoggcu-desc' => 'Etappenweises Protokoll zum Hochladen für Firefogg',
65 -);
66 -
67 -/** Lower Sorbian (Dolnoserbski)
68 - * @author Michawiki
69 - */
70 -$messages['dsb'] = array(
71 - 'firefoggcu-desc' => 'Protokol Firefogg za nagraśe pó porcijach',
72 -);
73 -
74 -/** Greek (Ελληνικά)
75 - * @author Περίεργος
76 - */
77 -$messages['el'] = array(
78 - 'firefoggcu-desc' => 'πρωτόκολλο Κατατεμνόμενης Φόρτωσης της Firefogg',
79 -);
80 -
81 -/** Spanish (Español)
82 - * @author Pertile
83 - */
84 -$messages['es'] = array(
85 - 'firefoggcu-desc' => 'Protocolo de carga por trozos de Firefogg',
86 -);
87 -
88 -/** Basque (Euskara)
89 - * @author An13sa
90 - */
91 -$messages['eu'] = array(
92 - 'firefoggcu-desc' => 'Firefoggen Chunked igoera protokoloa',
93 -);
94 -
95 -/** Finnish (Suomi)
96 - * @author Nike
97 - */
98 -$messages['fi'] = array(
99 - 'firefoggcu-desc' => 'Firefoggin pilkottu latausprotokolla',
100 -);
101 -
102 -/** French (Français)
103 - * @author Peter17
104 - */
105 -$messages['fr'] = array(
106 - 'firefoggcu-desc' => 'Protocole de téléversement par morceaux de Firefogg',
107 -);
108 -
109 -/** Galician (Galego)
110 - * @author Toliño
111 - */
112 -$messages['gl'] = array(
113 - 'firefoggcu-desc' => 'Protocolo de carga por anacos do Firefogg',
114 -);
115 -
116 -/** Swiss German (Alemannisch)
117 - * @author Als-Holder
118 - */
119 -$messages['gsw'] = array(
120 - 'firefoggcu-desc' => 'Stuckwys-Uffeladigs-Protokoll vu Firefogg',
121 -);
122 -
123 -/** Hebrew (עברית)
124 - * @author YaronSh
125 - */
126 -$messages['he'] = array(
127 - 'firefoggcu-desc' => 'פרוטוקול השליחה בחלקים של Firefogg',
128 -);
129 -
130 -/** Upper Sorbian (Hornjoserbsce)
131 - * @author Michawiki
132 - */
133 -$messages['hsb'] = array(
134 - 'firefoggcu-desc' => 'Protokol Firefogg za nahraće po porcijach',
135 -);
136 -
137 -/** Hungarian (Magyar)
138 - * @author Glanthor Reviol
139 - */
140 -$messages['hu'] = array(
141 - 'firefoggcu-desc' => 'A Firefogg daraboló feltöltési protokollja',
142 -);
143 -
144 -/** Interlingua (Interlingua)
145 - * @author McDutchie
146 - */
147 -$messages['ia'] = array(
148 - 'firefoggcu-desc' => 'Protocollo de incargamento in portiones de Firefogg',
149 -);
150 -
151 -/** Indonesian (Bahasa Indonesia)
152 - * @author Iwan Novirion
153 - */
154 -$messages['id'] = array(
155 - 'firefoggcu-desc' => 'Protokol mengunggah berkas Firefogg',
156 -);
157 -
158 -/** Italian (Italiano)
159 - * @author Civvì
160 - */
161 -$messages['it'] = array(
162 - 'firefoggcu-desc' => 'Protocollo di caricamento di Firefogg Chunked',
163 -);
164 -
165 -/** Japanese (日本語)
166 - * @author Aotake
167 - */
168 -$messages['ja'] = array(
169 - 'firefoggcu-desc' => 'Firefogg 一括アップロードプロトコル',
170 -);
171 -
172 -/** Colognian (Ripoarisch)
173 - * @author Purodha
174 - */
175 -$messages['ksh'] = array(
176 - 'firefoggcu-desc' => 'Dem Firefogg sing Protokoll zum Huhlaade en Etappe.',
177 -);
178 -
179 -/** Luxembourgish (Lëtzebuergesch)
180 - * @author Robby
181 - */
182 -$messages['lb'] = array(
183 - 'firefoggcu-desc' => 'Eropluedprotokoll vu Firefogg dee mat eenzelne Stecker schafft',
184 -);
185 -
186 -/** Macedonian (Македонски)
187 - * @author Bjankuloski06
188 - */
189 -$messages['mk'] = array(
190 - 'firefoggcu-desc' => 'Firefogg-овиот протокол на подигање дел по дел',
191 -);
192 -
193 -/** Dutch (Nederlands)
194 - * @author Siebrand
195 - */
196 -$messages['nl'] = array(
197 - 'firefoggcu-desc' => 'Firefogg-protocol voor uploaden in delen (Chunked Uploading)',
198 -);
199 -
200 -/** Norwegian (bokmål)‬ (‪Norsk (bokmål)‬)
201 - * @author Nghtwlkr
202 - */
203 -$messages['no'] = array(
204 - 'firefoggcu-desc' => 'Firefoggs bulkopplastingsprotokoll',
205 -);
206 -
207 -/** Occitan (Occitan)
208 - * @author Cedric31
209 - */
210 -$messages['oc'] = array(
211 - 'firefoggcu-desc' => 'Protocòl de mandadís per tròces de Firefogg',
212 -);
213 -
214 -/** Polish (Polski)
215 - * @author Sp5uhe
216 - */
217 -$messages['pl'] = array(
218 - 'firefoggcu-desc' => 'Protokół przesyłania kawałkami Firefogg',
219 -);
220 -
221 -/** Piedmontese (Piemontèis)
222 - * @author Dragonòt
223 - */
224 -$messages['pms'] = array(
225 - 'firefoggcu-desc' => 'Protocòl Firefogg Chunked Upload',
226 -);
227 -
228 -/** Portuguese (Português)
229 - * @author Hamilton Abreu
230 - */
231 -$messages['pt'] = array(
232 - 'firefoggcu-desc' => 'Protocolo de Chunked Uploading do Firefogg',
233 -);
234 -
235 -/** Brazilian Portuguese (Português do Brasil)
236 - * @author Giro720
237 - */
238 -$messages['pt-br'] = array(
239 - 'firefoggcu-desc' => 'Protocolo de Chunked Uploading do Firefogg',
240 -);
241 -
242 -/** Tarandíne (Tarandíne)
243 - * @author Joetaras
244 - */
245 -$messages['roa-tara'] = array(
246 - 'firefoggcu-desc' => 'Protocolle de carecamende Chunked Firefogg',
247 -);
248 -
249 -/** Russian (Русский)
250 - * @author Александр Сигачёв
251 - */
252 -$messages['ru'] = array(
253 - 'firefoggcu-desc' => 'Порционный протокол загрузки Firefogg',
254 -);
255 -
256 -/** Serbian Cyrillic ekavian (Српски (ћирилица))
257 - * @author Charmed94
258 - */
259 -$messages['sr-ec'] = array(
260 - 'firefoggcu-desc' => 'Firefogg-ов протокол отпремања',
261 -);
262 -
263 -/** Serbian Latin ekavian (Srpski (latinica)) */
264 -$messages['sr-el'] = array(
265 - 'firefoggcu-desc' => 'Firefogg-ov protokol otpremanja',
266 -);
267 -
268 -/** Swedish (Svenska)
269 - * @author Ainali
270 - */
271 -$messages['sv'] = array(
272 - 'firefoggcu-desc' => 'Firefoggs segmenterade uppladdningskontroll',
273 -);
274 -
275 -/** Telugu (తెలుగు)
276 - * @author Ravichandra
277 - */
278 -$messages['te'] = array(
279 - 'firefoggcu-desc' => 'ఫైర్‌ఫాగ్ విడిభాగాలుగా ఎక్కించే నియమావళి',
280 -);
281 -
282 -/** Tagalog (Tagalog)
283 - * @author AnakngAraw
284 - */
285 -$messages['tl'] = array(
286 - 'firefoggcu-desc' => 'Pamamaraan sa Matipak na Pagkakargang Paitaas ng Firefogg',
287 -);
288 -
289 -/** Turkish (Türkçe)
290 - * @author Joseph
291 - */
292 -$messages['tr'] = array(
293 - 'firefoggcu-desc' => "Firefogg'un Toplu Yükleme iletişim kuralı",
294 -);
295 -
296 -/** Ukrainian (Українська)
297 - * @author Ytsukeng Fyvaprol
298 - */
299 -$messages['uk'] = array(
300 - 'firefoggcu-desc' => 'Порційний протокол завантаження Firefogg',
301 -);
302 -
303 -/** Simplified Chinese (‪中文(简体)‬) */
304 -$messages['zh-hans'] = array(
305 - 'firefoggcu-desc' => 'Firefogg的分块上传协议',
306 -);
307 -
308 -/** Traditional Chinese (‪中文(繁體)‬)
309 - * @author Frankou
310 - */
311 -$messages['zh-hant'] = array(
312 - 'firefoggcu-desc' => 'Firefogg的分塊上傳協議',
313 -);
314 -
Index: trunk/extensions/ResumableUpload/FirefoggChunkedUploadHandler.php
@@ -1,165 +0,0 @@
2 -<?php
3 -if ( !defined( 'MEDIAWIKI' ) ) die();
4 -/**
5 - * @copyright Copyright © 2010 Mark A. Hershberger <mah@everybody.org>
6 - * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
7 - */
8 -
9 -class FirefoggChunkedUploadHandler extends UploadBase {
10 - const INIT = 1;
11 - const CHUNK = 2;
12 - const DONE = 3;
13 -
14 - protected $chunkMode; // INIT, CHUNK, DONE
15 - protected $sessionKey;
16 - protected $comment;
17 - protected $repoPath;
18 - protected $pageText;
19 - protected $watchlist;
20 -
21 - public $status;
22 -
23 - public function initializeFromRequest(&$request) {}
24 - public function getChunkMode() {return $this->chunkMode;}
25 - public function getDesiredName() {return $this->mDesiredDestName;}
26 -
27 - /**
28 - * Set session information for chunked uploads and allocate a unique key.
29 - * @param $comment string
30 - * @param $pageText string
31 - * @param $watchlist bodolean
32 - *
33 - * @returns string the session key for this chunked upload
34 - */
35 - public function setupChunkSession( $comment, $pageText, $watchlist ) {
36 - if ( !isset( $this->sessionKey ) ) {
37 - $this->sessionKey = $this->getSessionKey();
38 - }
39 - foreach ( array( 'mFilteredName', 'repoPath', 'mFileSize', 'mDesiredDestName' )
40 - as $key ) {
41 - if ( isset( $this->$key ) ) {
42 - $_SESSION[UploadBase::SESSION_KEYNAME][$this->sessionKey][$key] = $this->$key;
43 - }
44 - }
45 - if ( isset( $comment ) ) {
46 - $_SESSION[UploadBase::SESSION_KEYNAME][$this->sessionKey]['commment'] = $comment;
47 - }
48 - if ( isset( $pageText ) ) {
49 - $_SESSION[UploadBase::SESSION_KEYNAME][$this->sessionKey]['pageText'] = $pageText;
50 - }
51 - if ( isset( $watchlist ) ) {
52 - $_SESSION[UploadBase::SESSION_KEYNAME][$this->sessionKey]['watchlist'] = $watchlist;
53 - }
54 - $_SESSION[UploadBase::SESSION_KEYNAME][$this->sessionKey]['version'] = UploadBase::SESSION_VERSION;
55 -
56 - return $this->sessionKey;
57 - }
58 -
59 - /**
60 - * Initialize a request
61 - * @param $done boolean Set if this is the last chunk
62 - * @param $filename string The desired filename, set only on first request.
63 - * @param $sessionKey string The chunksession parameter
64 - * @param $path string The path to the temp file containing this chunk
65 - * @param $chunkSize integer The size of this chunk
66 - * @param $sessionData array sessiondata
67 - *
68 - * @return mixed True if there was no error, otherwise an error description suitable for passing to dieUsage()
69 - */
70 - public function initialize( $done, $filename, $sessionKey, $path, $chunkSize, $sessionData ) {
71 - if( $filename ) $this->mDesiredDestName = $filename;
72 - $this->mTempPath = $path;
73 -
74 - if ( $sessionKey !== null ) {
75 - $status = $this->initFromSessionKey( $sessionKey, $sessionData, $chunkSize );
76 - if( $status !== true ) {
77 - return $status;
78 - }
79 -
80 - if ( $done ) {
81 - $this->chunkMode = self::DONE;
82 - } else {
83 - $this->mTempPath = $path;
84 - $this->chunkMode = self::CHUNK;
85 - }
86 - } else {
87 - // session key not set, init the chunk upload system:
88 - $this->chunkMode = self::INIT;
89 - }
90 -
91 - if ( $this->mDesiredDestName === null ) {
92 - return 'Insufficient information for initialization.';
93 - }
94 -
95 - return true;
96 - }
97 -
98 - /**
99 - * Initialize a continuation of a chunked upload from a session key
100 - * @param $sessionKey string
101 - * @param $request WebRequest
102 - * @param $fileSize int Size of this chunk
103 - *
104 - * @returns void
105 - */
106 - protected function initFromSessionKey( $sessionKey, $sessionData, $fileSize ) {
107 - // testing against null because we don't want to cause obscure
108 - // bugs when $sessionKey is full of "0"
109 - $this->sessionKey = $sessionKey;
110 -
111 - if ( isset( $sessionData[$this->sessionKey]['version'] )
112 - && $sessionData[$this->sessionKey]['version'] == UploadBase::SESSION_VERSION )
113 - {
114 - foreach ( array( 'comment', 'pageText', 'watchlist', 'mFilteredName', 'repoPath', 'mFileSize', 'mDesiredDestName' )
115 - as $key ) {
116 - if ( isset( $sessionData[$this->sessionKey][$key] ) ) {
117 - $this->$key = $sessionData[$this->sessionKey][$key];
118 - }
119 - }
120 -
121 - $this->mFileSize += $fileSize;
122 - } else {
123 - return 'Not a valid session key';
124 - }
125 -
126 - return true;
127 - }
128 -
129 - /**
130 - * Append a chunk to the temporary file.
131 - *
132 - * @return void
133 - */
134 - public function appendChunk() {
135 - global $wgMaxUploadSize;
136 -
137 - if ( !$this->repoPath ) {
138 - $this->status = $this->saveTempUploadedFile( $this->mDesiredDestName, $this->mTempPath );
139 -
140 - if ( $this->status->isOK() ) {
141 - $this->repoPath = $this->status->value;
142 - $_SESSION[UploadBase::SESSION_KEYNAME][$this->sessionKey]['repoPath'] = $this->repoPath;
143 - }
144 - return $this->status;
145 - }
146 - if ( $this->getRealPath( $this->repoPath ) ) {
147 - $this->status = $this->appendToUploadFile( $this->repoPath, $this->mTempPath );
148 -
149 - if ( $this->mFileSize > $wgMaxUploadSize )
150 - $this->status = Status::newFatal( 'largefileserver' );
151 -
152 - } else {
153 - $this->status = Status::newFatal( 'filenotfound', $this->repoPath );
154 - }
155 - return $this->status;
156 - }
157 -
158 - /**
159 - * Append the final chunk and ready file for parent::performUpload()
160 - * @return void
161 - */
162 - public function finalizeFile() {
163 - $this->appendChunk();
164 - $this->mTempPath = $this->getRealPath( $this->repoPath );
165 - }
166 -}
\ No newline at end of file
Index: trunk/extensions/ResumableUpload/FirefoggChunkedUpload.php
@@ -1,21 +0,0 @@
2 -<?php
3 -if ( !defined( 'MEDIAWIKI' ) ) die();
4 -/**
5 - * @copyright Copyright © 2010 Mark A. Hershberger <mah@everybody.org>
6 - * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
7 - */
8 -
9 -$wgExtensionCredits['other'][] = array(
10 - 'path' => __FILE__,
11 - 'name' => 'FirefoggChunkedUpload',
12 - 'url' => 'http://www.mediawiki.org/wiki/Extension:FirefoggChunkedUpload',
13 - 'author' => array( 'Mark A. Hershberger' ),
14 - 'descriptionmsg' => 'firefoggcu-desc',
15 -);
16 -
17 -$dir = dirname( __FILE__ ) . '/';
18 -$wgExtensionMessagesFiles['FirefoggChunkedUpload'] = $dir . 'FirefoggChunkedUpload.i18n.php';
19 -$wgAutoloadClasses['ApiFirefoggChunkedUpload'] = $dir . 'ApiFirefoggChunkedUpload.php';
20 -$wgAutoloadClasses['FirefoggChunkedUploadHandler'] = $dir . 'FirefoggChunkedUploadHandler.php';
21 -
22 -$wgAPIModules['firefoggupload'] = 'ApiFirefoggChunkedUpload';
Index: trunk/extensions/ResumableUpload/tests/bootstrap.php
@@ -1,27 +0,0 @@
2 -<?php
3 -
4 -/**
5 - * Set up the MediaWiki environment when running tests with "phpunit" command
6 - *
7 - * Warning: this file is not included from global scope!
8 - * @file
9 - */
10 -
11 -global $wgCommandLineMode, $IP, $optionsWithArgs;
12 -$IP = dirname( dirname( dirname( dirname( __FILE__ ) ) ) ) . "/mw-svn";
13 -define( 'MW_PHPUNIT_TEST', true );
14 -
15 -require_once( "$IP/maintenance/commandLine.inc" );
16 -
17 -if( !version_compare(PHPUnit_Runner_Version::id(), "3.4.1", ">") ) {
18 - echo <<<EOF
19 -************************************************************
20 -
21 -These tests run best with version PHPUnit 3.4.2 or better.
22 -Earlier versions may show failures because earlier versions
23 -of PHPUnit do not properly implement dependencies.
24 -
25 -************************************************************
26 -
27 -EOF;
28 -}
\ No newline at end of file
Index: trunk/extensions/ResumableUpload/tests/phpunit.xml
@@ -1,19 +0,0 @@
2 -<!-- See http://www.phpunit.de/manual/3.3/en/appendixes.configuration.html -->
3 -<phpunit bootstrap="./bootstrap.php"
4 - colors="false"
5 - backupGlobals="false"
6 - convertErrorsToExceptions="true"
7 - convertNoticesToExceptions="true"
8 - convertWarningsToExceptions="true"
9 - stopOnFailure="false">
10 - <testsuite name="MediaWiki Test Suite">
11 - <!-- <directory>.</directory> -->
12 - <file>UploadFromChunksTest.php</file>
13 - </testsuite>
14 - <groups>
15 - <exclude>
16 - <group>Broken</group>
17 - <group>Stub</group>
18 - </exclude>
19 - </groups>
20 -</phpunit>
\ No newline at end of file
Index: trunk/extensions/ResumableUpload/tests/UploadFromChunksTest.php
@@ -1,371 +0,0 @@
2 -<?php
3 -
4 -global $IP;
5 -require_once( "$IP/maintenance/tests/ApiSetup.php" );
6 -require_once( "$IP/maintenance/deleteArchivedFiles.inc" );
7 -require_once( "$IP/maintenance/deleteArchivedRevisions.inc" );
8 -require_once( dirname( dirname( __FILE__ ) ) . '/FirefoggChunkedUpload.php' );
9 -
10 -class nullClass {
11 - public function handleOutput(){}
12 - public function purgeRedundantText(){}
13 -}
14 -
15 -class UploadFromChunksTest extends ApiSetup {
16 -
17 - // a base64 encoded image;
18 - private $pngimg = "iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAASUExURf///8zMzJmZmWZmZjMzMwAAAPOPemkAAAM1SURBVHjaYmBgYGBkYQUBFkYWFiCPCchixQAMCCZAACF0MAMVM4K4TFh0IGsBCCAkOxhYmBnAAKaHhZkZmxaAAGJgYIbpYGBihGgBWsTMzMwE4jIhaWGAYoAAYmCECDExYAcwGxkg5oNIgABigDqLARdgZmGB2wICrKwAAcSA3xKgIxlZ0PwCEEAMBCxhgHoWSQtAADFAAxgfYEJ1GEAAQbQw4tUCsocBYQVAADEgu4uRkREeUCwszEwwLhOKLQABhNDCBA4aSDgwwhIAJKqYUPwCEEAMUK/AUwnc9aywJMCI7DAgAAggBohZ8JTBhGIJzCoWZL8ABBCYidAB8RUjWppkYUG2BSCAGMDqEMZiswUtXgACiAHsFYixTMywGGLGpgUWYgABxAA2mQkWCMyMqFoYmdD8ACQAAogBHJHMrCxg1cyIiICmCkYWDFsAAgiihYmZCewFFpR0BfI3LLch+QUggBiQ0iQjEyMDmh54qCBlUIAAYsCRJsElADQvgWKTlRGeKwECiAF3XgGmMEYQYADZzcoA9z5AAMG9RQCAtEC9DxBADFiyFyMjVi0wABBAWLQwQdIiuhYGWJIACCBg+KKUJ9BoBRdS2LQALQMIIGDQIEmwAO1kYcVWHCDZAhBAqFqYmOAxj2YNtAwDAYAAYmDEiBYWzHKKkRERYiwAAYSphZEZwxZGZiZQVEJTJkAAMTCyokc7M5oORlC5wcoEjxeAAAJqQXU0UB6W5WFmABMtEzMi1wEEEFAbE0YyAUuzMMEsYQalMkQSBQggUDmNPU3C9IA4LCxI+QUggEBiKOU8yExgqccCL3chnkPKlQABhGo6ejHBDKmdUHMlQAAhhQvQaGZGkBIkjcAMywLmI+VKgABCSowsTJhZkhlWXiBpAQggYBqBZl9GVOdBcz0LZqEEEEAMqLULMBLg1THWog9IAwQQA0qiZcRW5aPbAhBADCg1El4tMAAQQAxoiZYZXnTh1AIQQAzo2QlYpDDjcBgrxGEAAcSAJTthswmiBUwDBBC2GpkZJTaRvQ+mAQKIAUuuxdZWQvILQABBmSxMjBj5EpcWgACCMoFOYYSpZyHQHgMIMACt2hmoVEikCQAAAABJRU5ErkJggg==";
19 -
20 - function setUp() {
21 - global $wgEnableUploads, $wgLocalFileRepo;
22 -
23 - $wgEnableUploads = true;
24 - parent::setup();
25 - $wgLocalFileRepo = array(
26 - 'class' => 'LocalRepo',
27 - 'name' => 'local',
28 - 'directory' => 'test-repo',
29 - 'url' => 'http://example.com/images',
30 - 'hashLevels' => 2,
31 - 'transformVia404' => false,
32 - );
33 -
34 - ini_set( 'log_errors', 1 );
35 - ini_set( 'error_reporting', 1 );
36 - ini_set( 'display_errors', 1 );
37 - }
38 -
39 - function makeChunk( $content ) {
40 - $file = tempnam( wfTempDir(), "" );
41 - $fh = fopen( $file, "wb" );
42 - if ( $fh == false ) {
43 - $this->markTestIncomplete( "Couldn't open $file!\n" );
44 - return;
45 - }
46 - fwrite( $fh, $content );
47 - fclose( $fh );
48 -
49 - $_FILES['chunk']['tmp_name'] = $file;
50 - $_FILES['chunk']['size'] = 3;
51 - $_FILES['chunk']['error'] = null;
52 - $_FILES['chunk']['name'] = "test.txt";
53 - }
54 -
55 - function cleanChunk() {
56 - if ( file_exists( $_FILES['chunk']['tmp_name'] ) )
57 - unlink( $_FILES['chunk']['tmp_name'] );
58 - }
59 -
60 - function doApiRequest( $params, $data = null ) {
61 - $session = isset( $data[2] ) ? $data[2] : array();
62 - $_SESSION = $session;
63 -
64 - $req = new FauxRequest( $params, true, $session );
65 - $module = new ApiMain( $req, true );
66 - $module->execute();
67 -
68 - return array( $module->getResultData(), $req, $_SESSION );
69 - }
70 -
71 - /* function testGetTitle() { */
72 - /* $filename = tempnam( wfTempDir(), "" ); */
73 - /* $c = new ApiFirefoggChunkedUpload; */
74 - /* $c->initialize( false, "temp.txt", null, $filename, 0, null ); */
75 - /* $this->assertEquals( null, $c->getUpload()->getTitle() ); */
76 -
77 - /* $c = new ApiFirefoggChunkedUpload; */
78 - /* $c->initialize( false, "Temp.png", null, $filename, 0, null ); */
79 - /* $this->assertEquals( Title::makeTitleSafe( NS_FILE, "Temp.png" ), $c->getUpload()->getTitle() ); */
80 - /* } */
81 -
82 - function testLogin() {
83 - $data = $this->doApiRequest( array(
84 - 'action' => 'login',
85 - 'lgname' => self::$userName,
86 - 'lgpassword' => self::$passWord ) );
87 - $this->assertArrayHasKey( "login", $data[0] );
88 - $this->assertArrayHasKey( "result", $data[0]['login'] );
89 - $this->assertEquals( "NeedToken", $data[0]['login']['result'] );
90 - $token = $data[0]['login']['token'];
91 -
92 - $data = $this->doApiRequest( array(
93 - 'action' => 'login',
94 - "lgtoken" => $token,
95 - "lgname" => self::$userName,
96 - "lgpassword" => self::$passWord ) );
97 -
98 - $this->assertArrayHasKey( "login", $data[0] );
99 - $this->assertArrayHasKey( "result", $data[0]['login'] );
100 - $this->assertEquals( "Success", $data[0]['login']['result'] );
101 - $this->assertArrayHasKey( 'lgtoken', $data[0]['login'] );
102 -
103 - return $data;
104 - }
105 -
106 - /**
107 - * @depends testLogin
108 - */
109 - function testSetupChunkBannedFileType( $data ) {
110 - global $wgUser;
111 - $wgUser = User::newFromName( self::$userName );
112 - $wgUser->load();
113 - $data[2]['wsEditToken'] = $data[2]['wsToken'];
114 - $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
115 - $exception = false;
116 -
117 - try {
118 - $this->doApiRequest( array(
119 - 'action' => 'firefoggupload',
120 - 'comment' => 'test',
121 - 'watchlist' => 'watch',
122 - 'filename' => 'tmp.txt',
123 - 'token' => $token ), $data );
124 - } catch ( UsageException $e ) {
125 - $exception = true;
126 - $this->assertEquals( "This type of file is banned", $e->getMessage() );
127 - }
128 -
129 - $this->assertTrue( $exception, "Got exception" );
130 - }
131 -
132 - /**
133 - * @depends testLogin
134 - */
135 - function testSetupChunkSession( $data ) {
136 - global $wgUser;
137 - $wgUser = User::newFromName( self::$userName );
138 - $wgUser->load();
139 - $data[2]['wsEditToken'] = $data[2]['wsToken'];
140 - $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
141 -
142 - $data = $this->doApiRequest( array(
143 - 'action' => 'firefoggupload',
144 - 'comment' => 'test',
145 - 'watchlist' => 'watch',
146 - 'filename' => 'TestPic.png',
147 - 'token' => $token ), $data );
148 -
149 - $this->assertArrayHasKey( 'uploadUrl', $data[0] );
150 - $this->assertRegexp( '/action=firefoggupload/', $data[0]['uploadUrl'] );
151 - $this->assertRegexp( '/chunksession=/', $data[0]['uploadUrl'] );
152 - $this->assertRegexp( '/token=/', $data[0]['uploadUrl'] );
153 -
154 - return $data;
155 - }
156 -
157 - /**
158 - * @depends testLogin
159 - */
160 - function testInvalidSessionKey( $data ) {
161 - global $wgUser;
162 - $wgUser = User::newFromName( self::$userName );
163 - $wgUser->load();
164 - $data[2]['wsEditToken'] = $data[2]['wsToken'];
165 - $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
166 - $exception = false;
167 -
168 - try {
169 - $this->doApiRequest( array(
170 - 'action' => 'firefoggupload',
171 - 'enablechunks' => true,
172 - 'token' => $token,
173 - 'chunksession' => 'bogus' ), $data );
174 - } catch ( UsageException $e ) {
175 - $exception = true;
176 - $this->assertEquals( "Not a valid session key", $e->getMessage() );
177 - }
178 -
179 - $this->assertTrue( $exception, "Got exception" );
180 - }
181 -
182 - function testPerformUploadInitError() {
183 - global $wgUser;
184 - $wgUser = User::newFromId( 1 );
185 -
186 - $req = new FauxRequest(
187 - array(
188 - 'action' => 'firefoggupload',
189 - 'sessionkey' => '1',
190 - 'filename' => 'test.png',
191 - ) );
192 - $module = new ApiMain( $req, true );
193 - $gotException = false;
194 - try {
195 - $module->execute();
196 - } catch ( UsageException $e ) {
197 - $this->assertEquals( "The token parameter must be set", $e->getMessage() );
198 - $gotException = true;
199 - }
200 -
201 - $this->assertTrue( $gotException );
202 - }
203 -
204 -
205 - /**
206 - * @depends testLogin
207 - */
208 - function testSetupChunkForBannedContent( $data ) {
209 - global $wgUser;
210 - $wgUser = User::newFromName( self::$userName );
211 - $wgUser->load();
212 - $data[2]['wsEditToken'] = $data[2]['wsToken'];
213 - $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
214 - $exception = false;
215 -
216 - $this->makeChunk( "123" );
217 - $data = $this->doApiRequest( array(
218 - 'action' => 'firefoggupload',
219 - 'comment' => 'test',
220 - 'watchlist' => 'watch',
221 - 'filename' => 'tmp.png',
222 - 'token' => $token ), $data );
223 - return $data;
224 - }
225 -
226 - /**
227 - * @depends testSetupChunkForBannedContent
228 - */
229 - function testChunkUploadBannedContent ( $data ) {
230 - global $wgUser;
231 - $wgUser = User::newFromName( self::$userName );
232 - $wgUser->load();
233 - $data[2]['wsEditToken'] = $data[2]['wsToken'];
234 - $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
235 - $exception = false;
236 - $url = $data[0]['uploadUrl'];
237 - $params = wfCgiToArray( substr( $url, strpos( $url, "?" ) ) );
238 - $params['done'] = true;
239 -
240 - $this->makeChunk( "123" );
241 - $gotException = false;
242 - try {
243 - $data = $this->doApiRequest( $params, $data );
244 - } catch ( UsageException $e ) {
245 - $this->assertEquals( "This file did not pass file verification",
246 - $e->getMessage() );
247 - $gotException = true;
248 - }
249 - $this->cleanChunk();
250 - $this->assertTrue( $gotException );
251 - }
252 -
253 - /**
254 - * @depends testLogin
255 - */
256 - function testUploadChunkDoneGood( $data ) {
257 - global $wgUser, $wgVerifyMimeType;
258 - $wgVerifyMimeType = false;
259 -
260 - $this->markTestIncomplete("Not working yet ... fails every other time b/c we're not dealing with a temporary db");
261 -
262 - DeleteArchivedFilesImplementation::doDelete(new nullClass, true);
263 - DeleteArchivedRevisionsImplementation::doDelete(new nullClass);
264 -
265 - $id = Title::newFromText( "Twar.png", NS_FILE )->getArticleID();
266 - $oldFile = Article::newFromID( $id );
267 - if ( $oldFile ) {
268 - $oldFile->doDeleteArticle();
269 - $oldFile->doPurge();
270 - }
271 -
272 - $oldFile = wfFindFile( "Twar.png" );
273 - if ( $oldFile ) {
274 - $oldFile->delete();
275 - }
276 - $id = Title::newFromText( "Twar.png", NS_FILE )->getArticleID();
277 - $this->assertEquals(0, $id);
278 -
279 - $oldFile = Article::newFromID( $id );
280 - $this->assertEquals(null, $oldFile);
281 -
282 - $wgUser = User::newFromName( self::$userName );
283 - $data[2]['wsEditToken'] = $data[2]['wsToken'];
284 - $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
285 - $data = $this->doApiRequest( array(
286 - 'action' => 'firefoggupload',
287 - 'comment' => 'test',
288 - 'watchlist' => 'watch',
289 - 'filename' => 'twar.png',
290 - 'token' => $token ), $data );
291 -
292 - $url = $data[0]['uploadUrl'];
293 - $params = wfCgiToArray( substr( $url, strpos( $url, "?" ) ) );
294 - $size = 0;
295 - for ( $i = 0; $i < 5; $i++ ) {
296 - $this->makeChunk( "123" );
297 - $size += $_FILES['chunk']['size'];
298 -
299 - $data = $this->doApiRequest( $params, $data );
300 - $this->assertArrayHasKey( "result", $data[0] );
301 - $this->assertTrue( (bool)$data[0]["result"] );
302 -
303 - $this->assertArrayHasKey( "filesize", $data[0] );
304 - $this->assertEquals( $size, $data[0]['filesize'] );
305 -
306 - $this->cleanChunk();
307 - }
308 -
309 - $params['done'] = true;
310 -
311 - $this->makeChunk( "456" );
312 - $data = $this->doApiRequest( $params, $data );
313 -
314 - $this->cleanChunk();
315 - $this->assertArrayHasKey( 'result', $data[0] );
316 -
317 - $this->assertEquals( 1, $data[0]['result'] );
318 -
319 - $this->assertArrayHasKey( 'done', $data[0] );
320 - $this->assertEquals( 1, $data[0]['done'] );
321 -
322 - $this->assertArrayHasKey( 'resultUrl', $data[0] );
323 - $this->assertRegExp( '/File:Twar.png/', $data[0]['resultUrl'] );
324 - }
325 -
326 - /**
327 - * @depends testLogin
328 - */
329 - function testUploadChunkDoneDuplicate( $data ) {
330 - global $wgUser, $wgVerifyMimeType;
331 -
332 - $this->markTestIncomplete("Not working yet");
333 -
334 - $wgVerifyMimeType = false;
335 - $wgUser = User::newFromName( self::$userName );
336 - $data[2]['wsEditToken'] = $data[2]['wsToken'];
337 - $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
338 - $data = $this->doApiRequest( array(
339 - 'filename' => 'twar.png',
340 - 'action' => 'firefoggupload',
341 - 'token' => $token ), $data );
342 -
343 - $url = $data[0]['uploadUrl'];
344 - $params = wfCgiToArray( substr( $url, strpos( $url, "?" ) ) );
345 - $size = 0;
346 - $gotException = false;
347 - for ( $i = 0; $i < 30; $i++ ) {
348 - $this->makeChunk( "123" );
349 - $size += $_FILES['chunk']['size'];
350 - try {
351 - $data = $this->doApiRequest( $params, $data );
352 - } catch (UsageException $e) {
353 - $arr = $e->getMessageArray();
354 - $this->assertArrayHasKey( "code", $arr );
355 - $this->assertEquals( "internal-error", $arr['code'] );
356 -
357 - $this->assertEquals( "fileexistserror", $arr[0][0] );
358 - $gotException = true;
359 - }
360 - }
361 - $this->cleanChunk();
362 - $this->assertTrue($gotException);
363 - }
364 -
365 - function testCleanup() {
366 - $dbw = wfGetDB( DB_MASTER );
367 - $dbw->begin();
368 - $dbw->delete("image", array('img_user_text' => self::$userName ));
369 - $dbw->commit();
370 - $this->assertTrue(true);
371 - }
372 -}
Index: trunk/extensions/ResumableUpload/tests/qunit/ResumableUploadTest.js
@@ -0,0 +1,21 @@
 2+/**
 3+ * Stubs for resumable upload javascript test
 4+ */
 5+
 6+module( 'resumableUploadApi.js' );
 7+
 8+test( 'getToken', function(){
 9+
 10+});
 11+
 12+test( 'instaciateUpload', function(){
 13+
 14+});
 15+
 16+test( 'uploadChunks', function(){
 17+
 18+})
 19+
 20+test( 'handleResult', function(){
 21+
 22+})
\ No newline at end of file
Property changes on: trunk/extensions/ResumableUpload/tests/qunit/ResumableUploadTest.js
___________________________________________________________________
Added: svn:mime-type
123 + text/plain
Index: trunk/extensions/ResumableUpload/tests/qunit/index.html
@@ -0,0 +1,48 @@
 2+<!DOCTYPE html>
 3+<html>
 4+<head>
 5+ <title>ResumableUpload JavaScript Test Suite</title>
 6+
 7+ <!-- MediaWiki Modules -->
 8+ <meta name="ResourceLoaderDynamicStyles" content="" />
 9+
 10+ <script src="../../../../resources/jquery/jquery.js"></script>
 11+
 12+ <script> function startUp(){} </script>
 13+ <script src="../mediawiki/mediawiki.js"></script>
 14+ <script> mw.config = new mw.Map( false ); </script>
 15+
 16+ <script src="../../../../resources/jquery/jquery.checkboxShiftClick.js"></script>
 17+ <script src="../../../../resources/jquery/jquery.client.js"></script>
 18+ <script src="../../../../resources/jquery/jquery.cookie.js"></script>
 19+ <script src="../../../../resources/jquery/jquery.messageBox.js"></script>
 20+ <script src="../../../../resources/jquery/jquery.makeCollapsible.js"></script>
 21+ <script src="../../../../resources/jquery/jquery.placeholder.js"></script>
 22+
 23+ <script src="../../../../resources/jquery/jquery.colorUtil.js"></script>
 24+
 25+ <!-- QUnit dependancies and scripts -->
 26+ <link rel="stylesheet" href="../../../../resources/jquery/jquery.qunit.css" />
 27+ <script src="../../../../resources/jquery/jquery.qunit.js"></script>
 28+
 29+ <!-- Your tests styles go here -->
 30+ <link rel="stylesheet" href="unit/main.css" />
 31+
 32+ <!-- Your test suites go here -->
 33+ <script src="ResumableUploadTest.js"></script>
 34+
 35+ <!-- TestSwarm -->
 36+ <script src="http://toolserver.org/~krinkle/testswarm/js/inject.js"></script>
 37+</head>
 38+<body>
 39+ <h1 id="qunit-header">MediaWiki JavaScript Test Suite</h1>
 40+ <h2 id="qunit-banner"></h2>
 41+ <div id="qunit-testrunner-toolbar"></div>
 42+ <h2 id="qunit-userAgent"></h2>
 43+ <ol id="qunit-tests"></ol>
 44+
 45+<div id="mw-content">
 46+ <div id="bodyContent"></div>
 47+</div>
 48+</body>
 49+</html>
Property changes on: trunk/extensions/ResumableUpload/tests/qunit/index.html
___________________________________________________________________
Added: svn:mime-type
150 + text/plain
Index: trunk/extensions/ResumableUpload/tests/qunit/README
@@ -0,0 +1,2 @@
 2+How this works with testSwarm is yet to be determined
 3+See https://bugzilla.wikimedia.org/show_bug.cgi?id=28915
\ No newline at end of file
Index: trunk/extensions/ResumableUpload/tests/phpunit/bootstrap.php
@@ -0,0 +1,27 @@
 2+<?php
 3+
 4+/**
 5+ * Set up the MediaWiki environment when running tests with "phpunit" command
 6+ *
 7+ * Warning: this file is not included from global scope!
 8+ * @file
 9+ */
 10+
 11+global $wgCommandLineMode, $IP, $optionsWithArgs;
 12+$IP = dirname( dirname( dirname( dirname( dirname( __FILE__ ) ) ) ) ). "/mw-svn";
 13+define( 'MW_PHPUNIT_TEST', true );
 14+
 15+require_once( "$IP/maintenance/commandLine.inc" );
 16+
 17+if( !version_compare(PHPUnit_Runner_Version::id(), "3.4.1", ">") ) {
 18+ echo <<<EOF
 19+************************************************************
 20+
 21+These tests run best with version PHPUnit 3.4.2 or better.
 22+Earlier versions may show failures because earlier versions
 23+of PHPUnit do not properly implement dependencies.
 24+
 25+************************************************************
 26+
 27+EOF;
 28+}
\ No newline at end of file
Index: trunk/extensions/ResumableUpload/tests/phpunit/phpunit.xml
@@ -0,0 +1,19 @@
 2+<!-- See http://www.phpunit.de/manual/3.3/en/appendixes.configuration.html -->
 3+<phpunit bootstrap="./bootstrap.php"
 4+ colors="false"
 5+ backupGlobals="false"
 6+ convertErrorsToExceptions="true"
 7+ convertNoticesToExceptions="true"
 8+ convertWarningsToExceptions="true"
 9+ stopOnFailure="false">
 10+ <testsuite name="MediaWiki Test Suite">
 11+ <!-- <directory>.</directory> -->
 12+ <file>UploadFromChunksTest.php</file>
 13+ </testsuite>
 14+ <groups>
 15+ <exclude>
 16+ <group>Broken</group>
 17+ <group>Stub</group>
 18+ </exclude>
 19+ </groups>
 20+</phpunit>
\ No newline at end of file
Property changes on: trunk/extensions/ResumableUpload/tests/phpunit/phpunit.xml
___________________________________________________________________
Added: svn:mime-type
121 + text/plain
Index: trunk/extensions/ResumableUpload/tests/phpunit/ResumableUploadTest.php
@@ -0,0 +1,371 @@
 2+<?php
 3+
 4+global $IP;
 5+require_once( "$IP/maintenance/tests/ApiSetup.php" );
 6+require_once( "$IP/maintenance/deleteArchivedFiles.inc" );
 7+require_once( "$IP/maintenance/deleteArchivedRevisions.inc" );
 8+require_once( dirname( dirname( __FILE__ ) ) . '/ResumableUpload.php' );
 9+
 10+class nullClass {
 11+ public function handleOutput(){}
 12+ public function purgeRedundantText(){}
 13+}
 14+
 15+class UploadFromChunksTest extends ApiSetup {
 16+
 17+ // a base64 encoded image;
 18+ private $pngimg = "iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAASUExURf///8zMzJmZmWZmZjMzMwAAAPOPemkAAAM1SURBVHjaYmBgYGBkYQUBFkYWFiCPCchixQAMCCZAACF0MAMVM4K4TFh0IGsBCCAkOxhYmBnAAKaHhZkZmxaAAGJgYIbpYGBihGgBWsTMzMwE4jIhaWGAYoAAYmCECDExYAcwGxkg5oNIgABigDqLARdgZmGB2wICrKwAAcSA3xKgIxlZ0PwCEEAMBCxhgHoWSQtAADFAAxgfYEJ1GEAAQbQw4tUCsocBYQVAADEgu4uRkREeUCwszEwwLhOKLQABhNDCBA4aSDgwwhIAJKqYUPwCEEAMUK/AUwnc9aywJMCI7DAgAAggBohZ8JTBhGIJzCoWZL8ABBCYidAB8RUjWppkYUG2BSCAGMDqEMZiswUtXgACiAHsFYixTMywGGLGpgUWYgABxAA2mQkWCMyMqFoYmdD8ACQAAogBHJHMrCxg1cyIiICmCkYWDFsAAgiihYmZCewFFpR0BfI3LLch+QUggBiQ0iQjEyMDmh54qCBlUIAAYsCRJsElADQvgWKTlRGeKwECiAF3XgGmMEYQYADZzcoA9z5AAMG9RQCAtEC9DxBADFiyFyMjVi0wABBAWLQwQdIiuhYGWJIACCBg+KKUJ9BoBRdS2LQALQMIIGDQIEmwAO1kYcVWHCDZAhBAqFqYmOAxj2YNtAwDAYAAYmDEiBYWzHKKkRERYiwAAYSphZEZwxZGZiZQVEJTJkAAMTCyokc7M5oORlC5wcoEjxeAAAJqQXU0UB6W5WFmABMtEzMi1wEEEFAbE0YyAUuzMMEsYQalMkQSBQggUDmNPU3C9IA4LCxI+QUggEBiKOU8yExgqccCL3chnkPKlQABhGo6ejHBDKmdUHMlQAAhhQvQaGZGkBIkjcAMywLmI+VKgABCSowsTJhZkhlWXiBpAQggYBqBZl9GVOdBcz0LZqEEEEAMqLULMBLg1THWog9IAwQQA0qiZcRW5aPbAhBADCg1El4tMAAQQAxoiZYZXnTh1AIQQAzo2QlYpDDjcBgrxGEAAcSAJTthswmiBUwDBBC2GpkZJTaRvQ+mAQKIAUuuxdZWQvILQABBmSxMjBj5EpcWgACCMoFOYYSpZyHQHgMIMACt2hmoVEikCQAAAABJRU5ErkJggg==";
 19+
 20+ function setUp() {
 21+ global $wgEnableUploads, $wgLocalFileRepo;
 22+
 23+ $wgEnableUploads = true;
 24+ parent::setup();
 25+ $wgLocalFileRepo = array(
 26+ 'class' => 'LocalRepo',
 27+ 'name' => 'local',
 28+ 'directory' => 'test-repo',
 29+ 'url' => 'http://example.com/images',
 30+ 'hashLevels' => 2,
 31+ 'transformVia404' => false,
 32+ );
 33+
 34+ ini_set( 'log_errors', 1 );
 35+ ini_set( 'error_reporting', 1 );
 36+ ini_set( 'display_errors', 1 );
 37+ }
 38+
 39+ function makeChunk( $content ) {
 40+ $file = tempnam( wfTempDir(), "" );
 41+ $fh = fopen( $file, "wb" );
 42+ if ( $fh == false ) {
 43+ $this->markTestIncomplete( "Couldn't open $file!\n" );
 44+ return;
 45+ }
 46+ fwrite( $fh, $content );
 47+ fclose( $fh );
 48+
 49+ $_FILES['chunk']['tmp_name'] = $file;
 50+ $_FILES['chunk']['size'] = 3;
 51+ $_FILES['chunk']['error'] = null;
 52+ $_FILES['chunk']['name'] = "test.txt";
 53+ }
 54+
 55+ function cleanChunk() {
 56+ if ( file_exists( $_FILES['chunk']['tmp_name'] ) )
 57+ unlink( $_FILES['chunk']['tmp_name'] );
 58+ }
 59+
 60+ function doApiRequest( $params, $data = null ) {
 61+ $session = isset( $data[2] ) ? $data[2] : array();
 62+ $_SESSION = $session;
 63+
 64+ $req = new FauxRequest( $params, true, $session );
 65+ $module = new ApiMain( $req, true );
 66+ $module->execute();
 67+
 68+ return array( $module->getResultData(), $req, $_SESSION );
 69+ }
 70+
 71+ /* function testGetTitle() { */
 72+ /* $filename = tempnam( wfTempDir(), "" ); */
 73+ /* $c = new ApiResumableUpload; */
 74+ /* $c->initialize( false, "temp.txt", null, $filename, 0, null ); */
 75+ /* $this->assertEquals( null, $c->getUpload()->getTitle() ); */
 76+
 77+ /* $c = new ApiResumableUpload; */
 78+ /* $c->initialize( false, "Temp.png", null, $filename, 0, null ); */
 79+ /* $this->assertEquals( Title::makeTitleSafe( NS_FILE, "Temp.png" ), $c->getUpload()->getTitle() ); */
 80+ /* } */
 81+
 82+ function testLogin() {
 83+ $data = $this->doApiRequest( array(
 84+ 'action' => 'login',
 85+ 'lgname' => self::$userName,
 86+ 'lgpassword' => self::$passWord ) );
 87+ $this->assertArrayHasKey( "login", $data[0] );
 88+ $this->assertArrayHasKey( "result", $data[0]['login'] );
 89+ $this->assertEquals( "NeedToken", $data[0]['login']['result'] );
 90+ $token = $data[0]['login']['token'];
 91+
 92+ $data = $this->doApiRequest( array(
 93+ 'action' => 'login',
 94+ "lgtoken" => $token,
 95+ "lgname" => self::$userName,
 96+ "lgpassword" => self::$passWord ) );
 97+
 98+ $this->assertArrayHasKey( "login", $data[0] );
 99+ $this->assertArrayHasKey( "result", $data[0]['login'] );
 100+ $this->assertEquals( "Success", $data[0]['login']['result'] );
 101+ $this->assertArrayHasKey( 'lgtoken', $data[0]['login'] );
 102+
 103+ return $data;
 104+ }
 105+
 106+ /**
 107+ * @depends testLogin
 108+ */
 109+ function testSetupChunkBannedFileType( $data ) {
 110+ global $wgUser;
 111+ $wgUser = User::newFromName( self::$userName );
 112+ $wgUser->load();
 113+ $data[2]['wsEditToken'] = $data[2]['wsToken'];
 114+ $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
 115+ $exception = false;
 116+
 117+ try {
 118+ $this->doApiRequest( array(
 119+ 'action' => 'resumableupload',
 120+ 'comment' => 'test',
 121+ 'watchlist' => 'watch',
 122+ 'filename' => 'tmp.txt',
 123+ 'token' => $token ), $data );
 124+ } catch ( UsageException $e ) {
 125+ $exception = true;
 126+ $this->assertEquals( "This type of file is banned", $e->getMessage() );
 127+ }
 128+
 129+ $this->assertTrue( $exception, "Got exception" );
 130+ }
 131+
 132+ /**
 133+ * @depends testLogin
 134+ */
 135+ function testSetupChunkSession( $data ) {
 136+ global $wgUser;
 137+ $wgUser = User::newFromName( self::$userName );
 138+ $wgUser->load();
 139+ $data[2]['wsEditToken'] = $data[2]['wsToken'];
 140+ $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
 141+
 142+ $data = $this->doApiRequest( array(
 143+ 'action' => 'resumableupload',
 144+ 'comment' => 'test',
 145+ 'watchlist' => 'watch',
 146+ 'filename' => 'TestPic.png',
 147+ 'token' => $token ), $data );
 148+
 149+ $this->assertArrayHasKey( 'uploadUrl', $data[0] );
 150+ $this->assertRegexp( '/action=resumableupload/', $data[0]['uploadUrl'] );
 151+ $this->assertRegexp( '/chunksession=/', $data[0]['uploadUrl'] );
 152+ $this->assertRegexp( '/token=/', $data[0]['uploadUrl'] );
 153+
 154+ return $data;
 155+ }
 156+
 157+ /**
 158+ * @depends testLogin
 159+ */
 160+ function testInvalidSessionKey( $data ) {
 161+ global $wgUser;
 162+ $wgUser = User::newFromName( self::$userName );
 163+ $wgUser->load();
 164+ $data[2]['wsEditToken'] = $data[2]['wsToken'];
 165+ $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
 166+ $exception = false;
 167+
 168+ try {
 169+ $this->doApiRequest( array(
 170+ 'action' => 'resumableupload',
 171+ 'enablechunks' => true,
 172+ 'token' => $token,
 173+ 'chunksession' => 'bogus' ), $data );
 174+ } catch ( UsageException $e ) {
 175+ $exception = true;
 176+ $this->assertEquals( "Not a valid session key", $e->getMessage() );
 177+ }
 178+
 179+ $this->assertTrue( $exception, "Got exception" );
 180+ }
 181+
 182+ function testPerformUploadInitError() {
 183+ global $wgUser;
 184+ $wgUser = User::newFromId( 1 );
 185+
 186+ $req = new FauxRequest(
 187+ array(
 188+ 'action' => 'resumableupload',
 189+ 'sessionkey' => '1',
 190+ 'filename' => 'test.png',
 191+ ) );
 192+ $module = new ApiMain( $req, true );
 193+ $gotException = false;
 194+ try {
 195+ $module->execute();
 196+ } catch ( UsageException $e ) {
 197+ $this->assertEquals( "The token parameter must be set", $e->getMessage() );
 198+ $gotException = true;
 199+ }
 200+
 201+ $this->assertTrue( $gotException );
 202+ }
 203+
 204+
 205+ /**
 206+ * @depends testLogin
 207+ */
 208+ function testSetupChunkForBannedContent( $data ) {
 209+ global $wgUser;
 210+ $wgUser = User::newFromName( self::$userName );
 211+ $wgUser->load();
 212+ $data[2]['wsEditToken'] = $data[2]['wsToken'];
 213+ $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
 214+ $exception = false;
 215+
 216+ $this->makeChunk( "123" );
 217+ $data = $this->doApiRequest( array(
 218+ 'action' => 'resumableupload',
 219+ 'comment' => 'test',
 220+ 'watchlist' => 'watch',
 221+ 'filename' => 'tmp.png',
 222+ 'token' => $token ), $data );
 223+ return $data;
 224+ }
 225+
 226+ /**
 227+ * @depends testSetupChunkForBannedContent
 228+ */
 229+ function testChunkUploadBannedContent ( $data ) {
 230+ global $wgUser;
 231+ $wgUser = User::newFromName( self::$userName );
 232+ $wgUser->load();
 233+ $data[2]['wsEditToken'] = $data[2]['wsToken'];
 234+ $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
 235+ $exception = false;
 236+ $url = $data[0]['uploadUrl'];
 237+ $params = wfCgiToArray( substr( $url, strpos( $url, "?" ) ) );
 238+ $params['done'] = true;
 239+
 240+ $this->makeChunk( "123" );
 241+ $gotException = false;
 242+ try {
 243+ $data = $this->doApiRequest( $params, $data );
 244+ } catch ( UsageException $e ) {
 245+ $this->assertEquals( "This file did not pass file verification",
 246+ $e->getMessage() );
 247+ $gotException = true;
 248+ }
 249+ $this->cleanChunk();
 250+ $this->assertTrue( $gotException );
 251+ }
 252+
 253+ /**
 254+ * @depends testLogin
 255+ */
 256+ function testUploadChunkDoneGood( $data ) {
 257+ global $wgUser, $wgVerifyMimeType;
 258+ $wgVerifyMimeType = false;
 259+
 260+ $this->markTestIncomplete("Not working yet ... fails every other time b/c we're not dealing with a temporary db");
 261+
 262+ DeleteArchivedFilesImplementation::doDelete(new nullClass, true);
 263+ DeleteArchivedRevisionsImplementation::doDelete(new nullClass);
 264+
 265+ $id = Title::newFromText( "Twar.png", NS_FILE )->getArticleID();
 266+ $oldFile = Article::newFromID( $id );
 267+ if ( $oldFile ) {
 268+ $oldFile->doDeleteArticle();
 269+ $oldFile->doPurge();
 270+ }
 271+
 272+ $oldFile = wfFindFile( "Twar.png" );
 273+ if ( $oldFile ) {
 274+ $oldFile->delete();
 275+ }
 276+ $id = Title::newFromText( "Twar.png", NS_FILE )->getArticleID();
 277+ $this->assertEquals(0, $id);
 278+
 279+ $oldFile = Article::newFromID( $id );
 280+ $this->assertEquals(null, $oldFile);
 281+
 282+ $wgUser = User::newFromName( self::$userName );
 283+ $data[2]['wsEditToken'] = $data[2]['wsToken'];
 284+ $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
 285+ $data = $this->doApiRequest( array(
 286+ 'action' => 'resumableupload',
 287+ 'comment' => 'test',
 288+ 'watchlist' => 'watch',
 289+ 'filename' => 'twar.png',
 290+ 'token' => $token ), $data );
 291+
 292+ $url = $data[0]['uploadUrl'];
 293+ $params = wfCgiToArray( substr( $url, strpos( $url, "?" ) ) );
 294+ $size = 0;
 295+ for ( $i = 0; $i < 5; $i++ ) {
 296+ $this->makeChunk( "123" );
 297+ $size += $_FILES['chunk']['size'];
 298+
 299+ $data = $this->doApiRequest( $params, $data );
 300+ $this->assertArrayHasKey( "result", $data[0] );
 301+ $this->assertTrue( (bool)$data[0]["result"] );
 302+
 303+ $this->assertArrayHasKey( "filesize", $data[0] );
 304+ $this->assertEquals( $size, $data[0]['filesize'] );
 305+
 306+ $this->cleanChunk();
 307+ }
 308+
 309+ $params['done'] = true;
 310+
 311+ $this->makeChunk( "456" );
 312+ $data = $this->doApiRequest( $params, $data );
 313+
 314+ $this->cleanChunk();
 315+ $this->assertArrayHasKey( 'result', $data[0] );
 316+
 317+ $this->assertEquals( 1, $data[0]['result'] );
 318+
 319+ $this->assertArrayHasKey( 'done', $data[0] );
 320+ $this->assertEquals( 1, $data[0]['done'] );
 321+
 322+ $this->assertArrayHasKey( 'resultUrl', $data[0] );
 323+ $this->assertRegExp( '/File:Twar.png/', $data[0]['resultUrl'] );
 324+ }
 325+
 326+ /**
 327+ * @depends testLogin
 328+ */
 329+ function testUploadChunkDoneDuplicate( $data ) {
 330+ global $wgUser, $wgVerifyMimeType;
 331+
 332+ $this->markTestIncomplete("Not working yet");
 333+
 334+ $wgVerifyMimeType = false;
 335+ $wgUser = User::newFromName( self::$userName );
 336+ $data[2]['wsEditToken'] = $data[2]['wsToken'];
 337+ $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX;
 338+ $data = $this->doApiRequest( array(
 339+ 'filename' => 'twar.png',
 340+ 'action' => 'resumableupload',
 341+ 'token' => $token ), $data );
 342+
 343+ $url = $data[0]['uploadUrl'];
 344+ $params = wfCgiToArray( substr( $url, strpos( $url, "?" ) ) );
 345+ $size = 0;
 346+ $gotException = false;
 347+ for ( $i = 0; $i < 30; $i++ ) {
 348+ $this->makeChunk( "123" );
 349+ $size += $_FILES['chunk']['size'];
 350+ try {
 351+ $data = $this->doApiRequest( $params, $data );
 352+ } catch (UsageException $e) {
 353+ $arr = $e->getMessageArray();
 354+ $this->assertArrayHasKey( "code", $arr );
 355+ $this->assertEquals( "internal-error", $arr['code'] );
 356+
 357+ $this->assertEquals( "fileexistserror", $arr[0][0] );
 358+ $gotException = true;
 359+ }
 360+ }
 361+ $this->cleanChunk();
 362+ $this->assertTrue($gotException);
 363+ }
 364+
 365+ function testCleanup() {
 366+ $dbw = wfGetDB( DB_MASTER );
 367+ $dbw->begin();
 368+ $dbw->delete("image", array('img_user_text' => self::$userName ));
 369+ $dbw->commit();
 370+ $this->assertTrue(true);
 371+ }
 372+}
Index: trunk/extensions/ResumableUpload/ApiResumableUpload.php
@@ -0,0 +1,241 @@
 2+<?php
 3+if ( !defined( 'MEDIAWIKI' ) ) die();
 4+/**
 5+ * @copyright Copyright © 2010 Mark A. Hershberger <mah@everybody.org>
 6+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
 7+ */
 8+
 9+class ApiResumableUpload extends ApiUpload {
 10+ protected $mUpload = null, $mParams = null;
 11+
 12+ public function execute() {
 13+ global $wgUser;
 14+
 15+ // Check whether upload is enabled
 16+ if ( !UploadBase::isEnabled() ) {
 17+ $this->dieUsageMsg( array( 'uploaddisabled' ) );
 18+ }
 19+
 20+ $this->mParams = $this->extractRequestParams();
 21+
 22+ $this->validateParams( $this->mParams );
 23+
 24+ $request = $this->getMain()->getRequest();
 25+ $this->mUpload = new ResumableUploadHandler;
 26+
 27+ $status = $this->mUpload->initialize(
 28+ $request->getVal( 'done', null ),
 29+ $request->getVal( 'filename', null ),
 30+ $request->getVal( 'chunksession', null ),
 31+ $request->getFileTempName( 'chunk' ),
 32+ $request->getFileSize( 'chunk' ),
 33+ $request->getSessionData( UploadBase::getSessionKeyname() )
 34+ );
 35+
 36+ if ( $status !== true ) {
 37+ $this->dieUsage( $status, 'chunk-init-error' );
 38+ }
 39+
 40+ $ret = $this->performUpload( );
 41+
 42+ if(is_array($ret)) {
 43+ foreach($ret as $key => $val) {
 44+ $this->getResult()->addValue(null, $key, $val);
 45+ }
 46+ } else {
 47+ $this->dieUsage($ret, 'error');
 48+ }
 49+ }
 50+
 51+ public function getUpload() {
 52+ return $this->mUpload;
 53+ }
 54+
 55+ public function performUploadInit($comment, $pageText, $watchlist, $user) {
 56+ // Verify the initial upload request
 57+ $this->verifyUploadInit();
 58+
 59+ $session = $this->mUpload->setupChunkSession( $comment, $pageText, $watchlist );
 60+ return array('uploadUrl' =>
 61+ wfExpandUrl( wfScript( 'api' ) ) . "?" .
 62+ wfArrayToCGI( array(
 63+ 'action' => 'resumableupload',
 64+ 'token' => $user->editToken(),
 65+ 'format' => 'json',
 66+ 'chunksession' => $session,
 67+ 'filename' => $this->mUpload->getDesiredName(),
 68+ ) ) );
 69+ }
 70+
 71+ /**
 72+ * Check the upload
 73+ */
 74+ public function verifyUploadInit(){
 75+
 76+ // Check for valid name:
 77+ $check = $this->mUpload->validateName();
 78+ if( $check !== true ) {
 79+ return $this->getVerificationError( $check );
 80+ }
 81+
 82+ // Check proposed file size
 83+ $maxSize = $this->getMaxUploadSize( '*' );
 84+ if( $this->mFileSize > $maxSize ) {
 85+ // We have to return an array here instead of getVerificationError so that we can include
 86+ // the max size info.
 87+ return array(
 88+ 'status' => self::FILE_TOO_LARGE,
 89+ 'max' => $maxSize,
 90+ );
 91+ }
 92+
 93+ return true;
 94+ }
 95+
 96+ public function performUploadChunk() {
 97+ $this->mUpload->setupChunkSession();
 98+ $status = $this->mUpload->appendChunk();
 99+ if ( !$status->isOK() ) {
 100+ $this->dieUsage($status->getWikiText(), 'error');
 101+ }
 102+ return array( 'result' => 1, 'filesize' => $this->mUpload->getFileSize() );
 103+ }
 104+
 105+ public function performUploadDone( $user ) {
 106+ $this->mUpload->finalizeFile();
 107+ $status = parent::performUpload( $this->comment, $this->pageText, $this->watchlist, $user );
 108+
 109+ if ( $status['result'] !== 'Success' ) {
 110+ return $status;
 111+ }
 112+ $file = $this->mUpload->getLocalFile();
 113+ return array('result' => 1, 'done' => 1, 'resultUrl' => wfExpandUrl( $file->getDescriptionUrl() ) );
 114+ }
 115+
 116+ /**
 117+ * Handle a chunk of the upload.
 118+ * @see UploadBase::performUpload
 119+ */
 120+ public function performUpload( ) {
 121+ wfDebug( "\n\n\performUpload(chunked): comment: " . $this->comment .
 122+ ' pageText: ' . $this->pageText . ' watch: ' . $this->watchlist );
 123+ $ret = "unknown error";
 124+
 125+ global $wgUser;
 126+ switch( $this->mUpload->getChunkMode() ){
 127+ case ResumableUploadHandler::INIT:
 128+ return $this->performUploadInit($this->comment, $this->pageText, $this->watchlist, $wgUser);
 129+ break;
 130+ case ResumableUploadHandler::CHUNK:
 131+ return $this->performUploadChunk();
 132+ break;
 133+ case ResumableUploadHandler::DONE:
 134+ return $this->performUploadDone($wgUser);;
 135+ break;
 136+ }
 137+ return $ret;
 138+ }
 139+
 140+ public function mustBePosted() {
 141+ return true;
 142+ }
 143+
 144+ public function isWriteMode() {
 145+ return true;
 146+ }
 147+
 148+ protected function validateParams( $params ) {
 149+ $required = array();
 150+ // Check required params for each upload mode:
 151+ switch( $this->mUpload->getChunkMode() ){
 152+ case ResumableUploadHandler::INIT:
 153+ $required[] = 'filename';
 154+ $required[] = 'comment';
 155+ $required[] = 'token';
 156+ $required[] = 'filesize';
 157+ break;
 158+ case ResumableUploadHandler::CHUNK:
 159+ $required[] = 'byteoffset';
 160+ $required[] = 'chunksession';
 161+ // The actual file payload:
 162+ $required[] = 'chunk';
 163+ break;
 164+ case ResumableUploadHandler::DONE:
 165+ $required[] = 'chunksession';
 166+ break;
 167+ }
 168+ foreach( $required as $arg ) {
 169+ if ( !isset( $params[$arg] ) ) {
 170+ $this->dieUsageMsg( array( 'missingparam', $arg ) );
 171+ }
 172+ }
 173+ }
 174+
 175+ public function getAllowedParams() {
 176+ return array(
 177+ 'filename' => null,
 178+ 'token' => null,
 179+ 'comment' => null,
 180+ 'ignorewarnings' => false,
 181+ 'chunksession' => null,
 182+ 'chunk' => null,
 183+ 'byteoffset' => null,
 184+ 'done' => false,
 185+ 'watchlist' => array(
 186+ ApiBase::PARAM_DFLT => 'preferences',
 187+ ApiBase::PARAM_TYPE => array(
 188+ 'watch',
 189+ 'unwatch',
 190+ 'preferences',
 191+ 'nochange'
 192+ ),
 193+ ),
 194+ );
 195+ }
 196+
 197+ public function getParamDescription() {
 198+ return array(
 199+ 'filename' => 'Target filename',
 200+ 'filesize' => 'The total size of the file being uploaded',
 201+ 'token' => 'Edit token. You can get one of these through prop=info',
 202+ 'comment' => 'Upload comment',
 203+ 'watchlist' => 'Unconditionally add or remove the page from your watchlist, use preferences or do not change watch',
 204+ 'ignorewarnings' => 'Ignore any warnings',
 205+ 'chunksession' => 'The session key, established on the first contact during the chunked upload',
 206+ 'chunk' => 'The data in this chunk of a chunked upload',
 207+ 'byteoffset' => 'The byte offset range of the uploaded chunk, relative to the complete file',
 208+ 'done' => 'Set to 1 on the last chunk of a chunked upload',
 209+
 210+ 'sessionkey' => 'Session key that identifies a previous upload that was stashed temporarily.',
 211+ 'stash' => 'If set, the server will not add the file to the repository and stash it temporarily.',
 212+ );
 213+ }
 214+
 215+ public function getDescription() {
 216+ return array(
 217+ 'Upload a file in chunks'
 218+ );
 219+ }
 220+
 221+ public function getPossibleErrors() {
 222+ return array_merge(
 223+ parent::getPossibleErrors(),
 224+ array(
 225+ array( 'missingparam' ),
 226+ array( 'chunk-init-error' ),
 227+ array( 'code' => 'chunk-init-error', 'info' => 'Insufficient information for initialization.' ),
 228+ array( 'code' => 'chunked-error', 'info' => 'There was a problem initializing the chunked upload.' ),
 229+ )
 230+ );
 231+ }
 232+
 233+ public function getExamples() {
 234+ return array(
 235+ 'api.php?action=resumableupload&filename=Wiki.png',
 236+ );
 237+ }
 238+
 239+ public function getVersion() {
 240+ return __CLASS__ . ': $Id: ApiResumableUpload.php 83770 2011-03-12 18:09:59Z reedy $';
 241+ }
 242+}
Index: trunk/extensions/ResumableUpload/ResumableUpload.i18n.php
@@ -0,0 +1,13 @@
 2+<?php
 3+/**
 4+ * Internationalisation file for extension Resumable Uploading.
 5+ *
 6+ * @file
 7+ * @ingroup Extensions
 8+ */
 9+
 10+$messages = array();
 11+
 12+$messages['en'] = array(
 13+ 'resumableupload-desc' => "Resumable Uploading support",
 14+);
Index: trunk/extensions/ResumableUpload/ResumableUploadHandler.php
@@ -0,0 +1,166 @@
 2+<?php
 3+if ( !defined( 'MEDIAWIKI' ) ) die();
 4+/**
 5+ * @copyright Copyright © 2010 Mark A. Hershberger <mah@everybody.org>
 6+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
 7+ */
 8+
 9+class ResumableUploadHandler extends UploadBase {
 10+ const INIT = 1;
 11+ const CHUNK = 2;
 12+ const DONE = 3;
 13+
 14+ protected $chunkMode; // INIT, CHUNK, DONE
 15+ protected $sessionKey;
 16+ protected $comment;
 17+ protected $repoPath;
 18+ protected $pageText;
 19+ protected $watchlist;
 20+
 21+ public $status;
 22+
 23+ public function initializeFromRequest(&$request) {}
 24+ public function getChunkMode() {return $this->chunkMode;}
 25+ public function getDesiredName() {return $this->mDesiredDestName;}
 26+
 27+ /**
 28+ * Set session information for chunked uploads and allocate a unique key.
 29+ * @param $comment string
 30+ * @param $pageText string
 31+ * @param $watchlist bodolean
 32+ *
 33+ * @returns string the session key for this chunked upload
 34+ */
 35+ public function setupChunkSession( $comment, $pageText, $watchlist ) {
 36+ if ( !isset( $this->sessionKey ) ) {
 37+ $this->sessionKey = $this->getSessionKey();
 38+ }
 39+ foreach ( array( 'mFilteredName', 'repoPath', 'mFileSize', 'mDesiredDestName' )
 40+ as $key ) {
 41+ if ( isset( $this->$key ) ) {
 42+ $_SESSION[UploadBase::SESSION_KEYNAME][$this->sessionKey][$key] = $this->$key;
 43+ }
 44+ }
 45+ if ( isset( $comment ) ) {
 46+ $_SESSION[UploadBase::SESSION_KEYNAME][$this->sessionKey]['commment'] = $comment;
 47+ }
 48+ if ( isset( $pageText ) ) {
 49+ $_SESSION[UploadBase::SESSION_KEYNAME][$this->sessionKey]['pageText'] = $pageText;
 50+ }
 51+ if ( isset( $watchlist ) ) {
 52+ $_SESSION[UploadBase::SESSION_KEYNAME][$this->sessionKey]['watchlist'] = $watchlist;
 53+ }
 54+ $_SESSION[UploadBase::SESSION_KEYNAME][$this->sessionKey]['version'] = UploadBase::SESSION_VERSION;
 55+
 56+ return $this->sessionKey;
 57+ }
 58+
 59+ /**
 60+ * Initialize a request
 61+ * @param $done boolean Set if this is the last chunk
 62+ * @param $filename string The desired filename, set only on first request.
 63+ * @param $sessionKey string The chunksession parameter
 64+ * @param $path string The path to the temp file containing this chunk
 65+ * @param $chunkSize integer The size of this chunk
 66+ * @param $sessionData array sessiondata
 67+ *
 68+ * @return mixed True if there was no error, otherwise an error description suitable for passing to dieUsage()
 69+ */
 70+ public function initialize( $done, $filename, $sessionKey, $path, $chunkSize, $sessionData ) {
 71+ if( $filename ) $this->mDesiredDestName = $filename;
 72+ $this->mTempPath = $path;
 73+
 74+ if ( $sessionKey !== null ) {
 75+ $status = $this->initFromSessionKey( $sessionKey, $sessionData, $chunkSize );
 76+ if( $status !== true ) {
 77+ return $status;
 78+ }
 79+
 80+ if ( $done ) {
 81+ $this->chunkMode = self::DONE;
 82+ } else {
 83+ $this->mTempPath = $path;
 84+ $this->chunkMode = self::CHUNK;
 85+ }
 86+ } else {
 87+ // session key not set, init the chunk upload system:
 88+ $this->chunkMode = self::INIT;
 89+ }
 90+
 91+ if ( $this->mDesiredDestName === null ) {
 92+ return 'Insufficient information for initialization.';
 93+ }
 94+
 95+ return true;
 96+ }
 97+
 98+ /**
 99+ * Initialize a continuation of a chunked upload from a session key
 100+ * @param $sessionKey string
 101+ * @param $request WebRequest
 102+ * @param $fileSize int Size of this chunk
 103+ *
 104+ * @returns void
 105+ */
 106+ protected function initFromSessionKey( $sessionKey, $sessionData, $fileSize ) {
 107+ // testing against null because we don't want to cause obscure
 108+ // bugs when $sessionKey is full of "0"
 109+ $this->sessionKey = $sessionKey;
 110+
 111+ if ( isset( $sessionData[$this->sessionKey]['version'] )
 112+ && $sessionData[$this->sessionKey]['version'] == UploadBase::SESSION_VERSION )
 113+ {
 114+ foreach ( array( 'comment', 'pageText', 'watchlist', 'mFilteredName', 'repoPath', 'mFileSize', 'mDesiredDestName' )
 115+ as $key ) {
 116+ if ( isset( $sessionData[$this->sessionKey][$key] ) ) {
 117+ $this->$key = $sessionData[$this->sessionKey][$key];
 118+ }
 119+ }
 120+
 121+ $this->mFileSize += $fileSize;
 122+ } else {
 123+ return 'Not a valid session key';
 124+ }
 125+
 126+ return true;
 127+ }
 128+
 129+ /**
 130+ * Append a chunk to the temporary file.
 131+ *
 132+ * @return void
 133+ */
 134+ public function appendChunk() {
 135+ global $wgMaxUploadSize;
 136+
 137+ if ( !$this->repoPath ) {
 138+ $this->status = $this->saveTempUploadedFile( $this->mDesiredDestName, $this->mTempPath );
 139+
 140+ if ( $this->status->isOK() ) {
 141+ $this->repoPath = $this->status->value;
 142+ $_SESSION[UploadBase::SESSION_KEYNAME][$this->sessionKey]['repoPath'] = $this->repoPath;
 143+ }
 144+ return $this->status;
 145+ }
 146+
 147+ if ( $this->getRealPath( $this->repoPath ) ) {
 148+ $this->status = $this->appendToUploadFile( $this->repoPath, $this->mTempPath );
 149+
 150+ if ( $this->mFileSize > $wgMaxUploadSize )
 151+ $this->status = Status::newFatal( 'largefileserver' );
 152+
 153+ } else {
 154+ $this->status = Status::newFatal( 'filenotfound', $this->repoPath );
 155+ }
 156+ return $this->status;
 157+ }
 158+
 159+ /**
 160+ * Append the final chunk and ready file for parent::performUpload()
 161+ * @return void
 162+ */
 163+ public function finalizeFile() {
 164+ $this->appendChunk();
 165+ $this->mTempPath = $this->getRealPath( $this->repoPath );
 166+ }
 167+}
\ No newline at end of file
Index: trunk/extensions/ResumableUpload/ResumableUpload.php
@@ -0,0 +1,22 @@
 2+<?php
 3+if ( !defined( 'MEDIAWIKI' ) ) die();
 4+/**
 5+ * @copyright Copyright © 2010 Mark A. Hershberger <mah@everybody.org>
 6+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
 7+ */
 8+
 9+$wgExtensionCredits['other'][] = array(
 10+ 'path' => __FILE__,
 11+ 'name' => 'Resumable Upload',
 12+ 'url' => 'http://www.mediawiki.org/wiki/Extension:ResumableUpload',
 13+ 'author' => array( 'Mark A. Hershberger', 'Michael Dale' ),
 14+ 'descriptionmsg' => 'resumableupload-desc',
 15+);
 16+
 17+$dir = dirname( __FILE__ ) . '/';
 18+$wgExtensionMessagesFiles['ResumableUpload'] = $dir . 'ResumableUpload.i18n.php';
 19+$wgAutoloadClasses['ApiResumableUpload'] = $dir . 'ApiResumableUpload.php';
 20+$wgAutoloadClasses['ResumableUploadHandler'] = $dir . 'ResumableUploadHandler.php';
 21+
 22+$wgAPIModules['resumableupload'] = 'ApiResumableUpload';
 23+
Index: trunk/extensions/ResumableUpload/README
@@ -0,0 +1,2 @@
 2+Adds Resumable upload support to MediaWiki upload api based on Googles resumable upload protocol:
 3+http://code.google.com/apis/gdata/docs/resumable_upload.html
\ No newline at end of file

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r88236Renamed FirefoggChunkedUpload to ResumableUpload...dale14:23, 16 May 2011

Status & tagging log