Index: trunk/phase3/maintenance/tests/UploadFromChunksTest.php |
— | — | @@ -9,8 +9,40 @@ |
10 | 10 | |
11 | 11 | $wgEnableUploads=true; |
12 | 12 | ini_set('file_loads', true); |
| 13 | + parent::setup(); |
| 14 | + |
13 | 15 | } |
14 | 16 | |
| 17 | + function makeChunk() { |
| 18 | + $file = tempnam( wfTempDir(), "" ); |
| 19 | + $fh = fopen($file, "w"); |
| 20 | + if($fh == false) { |
| 21 | + $this->markTestIncomplete("Couldn't open $file!\n"); |
| 22 | + return; |
| 23 | + } |
| 24 | + fwrite($fh, "123"); |
| 25 | + fclose($fh); |
| 26 | + |
| 27 | + $_FILES['chunk']['tmp_name'] = $file; |
| 28 | + $_FILES['chunk']['size'] = 3; |
| 29 | + $_FILES['chunk']['error'] = null; |
| 30 | + $_FILES['chunk']['name'] = "test.txt"; |
| 31 | + } |
| 32 | + |
| 33 | + function cleanChunk() { |
| 34 | + if(file_exists($_FILES['chunk']['tmp_name'])) |
| 35 | + unlink($_FILES['chunk']['tmp_name']); |
| 36 | + } |
| 37 | + |
| 38 | + function doApiRequest($params) { |
| 39 | + $session = isset( $_SESSION ) ? $_SESSION : array(); |
| 40 | + $req = new FauxRequest($params, true, $session); |
| 41 | + $module = new ApiMain($req, true); |
| 42 | + $module->execute(); |
| 43 | + |
| 44 | + return $module->getResultData(); |
| 45 | + } |
| 46 | + |
15 | 47 | function testGetTitle() { |
16 | 48 | $filename = tempnam( wfTempDir(), "" ); |
17 | 49 | $c = new UploadFromChunks(); |
— | — | @@ -22,89 +54,110 @@ |
23 | 55 | $this->assertEquals(Title::makeTitleSafe(NS_FILE, "Temp.png"), $c->getTitle()); |
24 | 56 | } |
25 | 57 | |
26 | | - function testGetEditToken() { |
| 58 | + function testLogin() { |
| 59 | + $data = $this->doApiRequest(array('action' => 'login', |
| 60 | + "lgname" => self::$userName, |
| 61 | + "lgpassword" => self::$passWord ) ); |
| 62 | + $this->assertArrayHasKey("login", $data); |
| 63 | + $this->assertArrayHasKey("result", $data['login']); |
| 64 | + $this->assertEquals("Success", $data['login']['result']); |
| 65 | + |
| 66 | + return $data; |
27 | 67 | } |
28 | 68 | |
29 | | - function testInitFromSessionKey() { |
30 | 69 | |
31 | | - } |
| 70 | + /** |
| 71 | + * @depends testLogin |
| 72 | + */ |
| 73 | + function testGetEditToken($data) { |
| 74 | + global $wgUser; |
| 75 | + $wgUser = User::newFromName(self::$userName); |
| 76 | + $wgUser->load(); |
32 | 77 | |
33 | | - function testInitialize() { |
| 78 | + $data = $this->doApiRequest(array('action' => 'query', |
| 79 | + 'prop' => 'info', |
| 80 | + 'intoken' => 'edit')); |
34 | 81 | } |
35 | 82 | |
36 | 83 | function testSetupChunkSession() { |
37 | 84 | } |
38 | 85 | |
39 | 86 | |
40 | | - function makeChunk() { |
41 | | - $file = tempnam( wfTempDir(), "" ); |
42 | | - $fh = fopen($file, "w"); |
43 | | - if($fh == false) { |
44 | | - $this->markTestIncomplete("Couldn't open $file!\n"); |
45 | | - return; |
46 | | - } |
47 | | - fwrite($fh, "123"); |
48 | | - fclose($fh); |
49 | | - |
50 | | - $_FILES['chunk']['tmp_name'] = $file; |
51 | | - $_FILES['chunk']['size'] = 3; |
52 | | - $_FILES['chunk']['error'] = null; |
53 | | - $_FILES['chunk']['name'] = "test.txt"; |
54 | | - } |
55 | | - |
56 | | - function cleanChunk() { |
57 | | - unlink($_FILES['chunk']['tmp_name']); |
58 | | - } |
59 | | - |
60 | 87 | /** |
61 | 88 | * @expectedException UsageException |
62 | 89 | */ |
63 | 90 | function testPerformUploadInitError() { |
64 | 91 | global $wgUser; |
65 | | - |
66 | 92 | $wgUser = User::newFromId(1); |
67 | | - $token = $wgUser->editToken(); |
68 | | - $this->makeChunk(); |
69 | 93 | |
70 | 94 | $req = new FauxRequest( |
71 | 95 | array('action' => 'upload', |
72 | 96 | 'enablechunks' => '1', |
73 | 97 | 'filename' => 'test.png', |
74 | | - 'token' => $token, |
75 | 98 | )); |
76 | 99 | $module = new ApiMain($req, true); |
77 | 100 | $module->execute(); |
78 | 101 | } |
79 | 102 | |
80 | | - function testPerformUploadInitSuccess() { |
| 103 | + /** |
| 104 | + * @depends testLogin |
| 105 | + */ |
| 106 | + function testPerformUploadInitSuccess($login) { |
81 | 107 | global $wgUser; |
82 | 108 | |
83 | | - $wgUser = User::newFromId(1); |
| 109 | + $wgUser = User::newFromName(self::$userName); |
84 | 110 | $token = $wgUser->editToken(); |
85 | | - $this->makeChunk(); |
86 | 111 | |
87 | | - $req = new FauxRequest( |
| 112 | + $data = $this->doApiRequest( |
88 | 113 | array('action' => 'upload', |
89 | 114 | 'enablechunks' => '1', |
90 | 115 | 'filename' => 'test.png', |
91 | 116 | 'token' => $token, |
92 | 117 | )); |
93 | | - $module = new ApiMain($req, true); |
94 | | - $module->execute(); |
95 | | - } |
96 | 118 | |
97 | | - function testAppendToUploadFile() { |
98 | | - } |
| 119 | + $this->assertArrayHasKey("upload", $data); |
| 120 | + $this->assertArrayHasKey("uploadUrl", $data['upload']); |
99 | 121 | |
100 | | - function testAppendChunk() { |
| 122 | + return array('data' => $data, 'session' => $_SESSION, 'token' => $token); |
101 | 123 | } |
102 | 124 | |
103 | | - function testPeformUploadChunk() { |
104 | | - } |
| 125 | + /** |
| 126 | + * @depends testPerformUploadInitSuccess |
| 127 | + */ |
| 128 | + function testAppendChunk($combo) { |
| 129 | + global $wgUser; |
| 130 | + $data = $combo['data']; |
| 131 | + $_SESSION = $combo['session']; |
| 132 | + $wgUser = User::newFromName(self::$userName); |
| 133 | + $token = $wgUser->editToken(); |
105 | 134 | |
106 | | - function testPeformUploadDone() { |
| 135 | + $url = $data['upload']['uploadUrl']; |
| 136 | + $params = wfCgiToArray(substr($url, strpos($url, "?"))); |
| 137 | + |
| 138 | + for($i=0;$i<10;$i++) { |
| 139 | + $this->makeChunk(); |
| 140 | + $data = $this->doApiRequest($params); |
| 141 | + $this->cleanChunk(); |
| 142 | + } |
| 143 | + |
| 144 | + return array('data' => $data, 'session' => $_SESSION, 'token' => $token, 'params' => $params); |
107 | 145 | } |
108 | 146 | |
| 147 | + /** |
| 148 | + * @depends testAppendChunk |
| 149 | + */ |
| 150 | + function testUploadChunkDone($combo) { |
| 151 | + global $wgUser; |
| 152 | + $data = $combo['data']; |
| 153 | + $params = $combo['params']; |
| 154 | + $_SESSION = $combo['session']; |
| 155 | + $wgUser = User::newFromName(self::$userName); |
| 156 | + $token = $wgUser->editToken(); |
109 | 157 | |
| 158 | + $params['done'] = 1; |
110 | 159 | |
| 160 | + $this->makeChunk(); |
| 161 | + $data = $this->doApiRequest($params); |
| 162 | + $this->cleanChunk(); |
| 163 | + } |
111 | 164 | } |
Index: trunk/phase3/includes/upload/UploadFromChunks.php |
— | — | @@ -22,7 +22,6 @@ |
23 | 23 | protected $chunkMode; // INIT, CHUNK, DONE |
24 | 24 | protected $sessionKey; |
25 | 25 | protected $comment; |
26 | | - protected $fileSize = 0; |
27 | 26 | protected $repoPath; |
28 | 27 | protected $pageText; |
29 | 28 | protected $watch; |
— | — | @@ -37,9 +36,8 @@ |
38 | 37 | throw new MWException( 'not implemented' ); |
39 | 38 | } |
40 | 39 | |
41 | | - public function initialize( $done, $filename, $sessionKey, $path, |
42 | | - $fileSize, $sessionData ) |
43 | | - { |
| 40 | + public function initialize( $done, $filename, $sessionKey, $path, $fileSize, $sessionData ) { |
| 41 | + global $wgTmpDirectory; |
44 | 42 | $this->status = new Status; |
45 | 43 | |
46 | 44 | $this->initFromSessionKey( $sessionKey, $sessionData ); |
— | — | @@ -47,7 +45,7 @@ |
48 | 46 | if ( !$this->sessionKey && !$done ) { |
49 | 47 | // session key not set, init the chunk upload system: |
50 | 48 | $this->chunkMode = self::INIT; |
51 | | - $this->mDesiredDestName = $filename; |
| 49 | + $this->initializePathInfo( $filename, $path, 0, true); |
52 | 50 | } else if ( $this->sessionKey && !$done ) { |
53 | 51 | $this->chunkMode = self::CHUNK; |
54 | 52 | } else if ( $this->sessionKey && $done ) { |
— | — | @@ -55,7 +53,7 @@ |
56 | 54 | } |
57 | 55 | if ( $this->chunkMode == self::CHUNK || $this->chunkMode == self::DONE ) { |
58 | 56 | $this->mTempPath = $path; |
59 | | - $this->fileSize += $fileSize; |
| 57 | + $this->mFileSize += $fileSize; |
60 | 58 | } |
61 | 59 | } |
62 | 60 | |
— | — | @@ -128,24 +126,25 @@ |
129 | 127 | // a) the user must have requested the token to get here and |
130 | 128 | // b) should only happen over POST |
131 | 129 | // c) we need the token to validate chunks are coming from a non-xss request |
132 | | - $token = urlencode( $wgUser->editToken() ); |
133 | | - echo FormatJson::encode( array( |
134 | | - 'uploadUrl' => wfExpandUrl( wfScript( 'api' ) ) . "?action=upload&" . |
135 | | - "token={$token}&format=json&enablechunks=true&chunksessionkey=" . |
136 | | - $this->setupChunkSession( $comment, $pageText, $watch ) ) ); |
137 | | - $wgOut->disable(); |
| 130 | + return Status::newGood( |
| 131 | + array('uploadUrl' => wfExpandUrl( wfScript( 'api' ) ) . "?" . |
| 132 | + wfArrayToCGI(array('action' => 'upload', |
| 133 | + 'token' => $wgUser->editToken(), |
| 134 | + 'format' => 'json', |
| 135 | + 'filename' => $pageText, |
| 136 | + 'enablechunks' => 'true', |
| 137 | + 'chunksession' => $this->setupChunkSession( $comment, $pageText, $watch ) ) ) ) ); |
138 | 138 | } else if ( $this->chunkMode == self::CHUNK ) { |
139 | | - $status = $this->appendChunk(); |
140 | | - if ( !$status->isOK() ) { |
141 | | - return $status; |
| 139 | + $this->appendChunk(); |
| 140 | + if ( !$this->status->isOK() ) { |
| 141 | + return $this->status; |
142 | 142 | } |
143 | 143 | // return success: |
144 | 144 | // firefogg expects a specific result |
145 | 145 | // http://www.firefogg.org/dev/chunk_post.html |
146 | | - echo FormatJson::encode( |
147 | | - array( 'result' => 1, 'filesize' => $this->fileSize ) |
| 146 | + return Status::newGood( |
| 147 | + array( 'result' => 1, 'filesize' => $this->mFileSize ) |
148 | 148 | ); |
149 | | - $wgOut->disable(); |
150 | 149 | } else if ( $this->chunkMode == self::DONE ) { |
151 | 150 | if ( !$comment ) |
152 | 151 | $comment = $this->comment; |
— | — | @@ -164,12 +163,9 @@ |
165 | 164 | |
166 | 165 | // firefogg expects a specific result |
167 | 166 | // http://www.firefogg.org/dev/chunk_post.html |
168 | | - echo FormatJson::encode( array( |
169 | | - 'result' => 1, |
170 | | - 'done' => 1, |
171 | | - 'resultUrl' => $file->getDescriptionUrl() ) |
| 167 | + return Status::newGood( |
| 168 | + array('result' => 1, 'done' => 1, 'resultUrl' => $file->getDescriptionUrl() ) |
172 | 169 | ); |
173 | | - $wgOut->disable(); |
174 | 170 | } |
175 | 171 | |
176 | 172 | return Status::newGood(); |
— | — | @@ -199,18 +195,18 @@ |
200 | 196 | if ( !$this->repoPath ) { |
201 | 197 | $this->status = $this->saveTempUploadedFile( $this->mDesiredDestName, $this->mTempPath ); |
202 | 198 | |
203 | | - if ( $status->isOK() ) { |
204 | | - $this->repoPath = $status->value; |
| 199 | + if ( $this->status->isOK() ) { |
| 200 | + $this->repoPath = $this->status->value; |
205 | 201 | $_SESSION['wsUploadData'][$this->sessionKey]['repoPath'] = $this->repoPath; |
206 | 202 | } |
207 | | - return $status; |
| 203 | + return; |
208 | 204 | } |
209 | 205 | if ( $this->getRealPath( $this->repoPath ) ) { |
210 | 206 | $this->status = $this->appendToUploadFile( $this->repoPath, $this->mTempPath ); |
211 | 207 | } else { |
212 | 208 | $this->status = Status::newFatal( 'filenotfound', $this->repoPath ); |
213 | 209 | } |
214 | | - if ( $this->fileSize > $wgMaxUploadSize ) |
| 210 | + if ( $this->mFileSize > $wgMaxUploadSize ) |
215 | 211 | $this->status = Status::newFatal( 'largefileserver' ); |
216 | 212 | } |
217 | 213 | |
Index: trunk/phase3/includes/filerepo/FSRepo.php |
— | — | @@ -227,6 +227,33 @@ |
228 | 228 | return $status; |
229 | 229 | } |
230 | 230 | |
| 231 | + function append( $srcPath, $toAppendPath ) { |
| 232 | + $status = $this->newGood(); |
| 233 | + |
| 234 | + // Resolve the virtual URL |
| 235 | + if ( self::isVirtualUrl( $srcPath ) ) { |
| 236 | + $srcPath = $this->resolveVirtualUrl( $srcPath ); |
| 237 | + } |
| 238 | + // Make sure the files are there |
| 239 | + if ( !is_file( $srcPath ) ) |
| 240 | + $status->fatal( 'append-src-filenotfound', $srcPath ); |
| 241 | + |
| 242 | + if ( !is_file( $toAppendPath ) ) |
| 243 | + $status->fatal( 'append-toappend-filenotfound', $toAppendPath ); |
| 244 | + |
| 245 | + // Do the append |
| 246 | + if( file_put_contents( $srcPath, file_get_contents( $toAppendPath ), FILE_APPEND ) ) { |
| 247 | + $status->value = $srcPath; |
| 248 | + } else { |
| 249 | + $status->fatal( 'fileappenderror', $toAppendPath, $srcPath); |
| 250 | + } |
| 251 | + |
| 252 | + // Remove the source file |
| 253 | + unlink( $toAppendPath ); |
| 254 | + |
| 255 | + return $status; |
| 256 | + } |
| 257 | + |
231 | 258 | /** |
232 | 259 | * Checks existence of specified array of files. |
233 | 260 | * |
— | — | @@ -575,7 +602,7 @@ |
576 | 603 | } |
577 | 604 | return strtr( $param, $this->simpleCleanPairs ); |
578 | 605 | } |
579 | | - |
| 606 | + |
580 | 607 | /** |
581 | 608 | * Chmod a file, supressing the warnings. |
582 | 609 | * @param String $path The path to change |
Index: trunk/phase3/includes/filerepo/NullRepo.php |
— | — | @@ -14,6 +14,9 @@ |
15 | 15 | function storeTemp( $originalName, $srcPath ) { |
16 | 16 | return false; |
17 | 17 | } |
| 18 | + function append( $srcPath, $toAppendPath ){ |
| 19 | + return false; |
| 20 | + } |
18 | 21 | function publishBatch( $triplets, $flags = 0 ) { |
19 | 22 | return false; |
20 | 23 | } |
Index: trunk/phase3/includes/filerepo/FileRepo.php |
— | — | @@ -30,7 +30,7 @@ |
31 | 31 | // Optional settings |
32 | 32 | $this->initialCapital = MWNamespace::isCapitalized( NS_FILE ); |
33 | 33 | foreach ( array( 'descBaseUrl', 'scriptDirUrl', 'articleUrl', 'fetchDescription', |
34 | | - 'thumbScriptUrl', 'initialCapital', 'pathDisclosureProtection', |
| 34 | + 'thumbScriptUrl', 'initialCapital', 'pathDisclosureProtection', |
35 | 35 | 'descriptionCacheExpiry', 'hashLevels', 'url', 'thumbUrl' ) as $var ) |
36 | 36 | { |
37 | 37 | if ( isset( $info[$var] ) ) { |
— | — | @@ -87,7 +87,7 @@ |
88 | 88 | * |
89 | 89 | * ignoreRedirect: If true, do not follow file redirects |
90 | 90 | * |
91 | | - * private: If true, return restricted (deleted) files if the current |
| 91 | + * private: If true, return restricted (deleted) files if the current |
92 | 92 | * user is allowed to view them. Otherwise, such files will not |
93 | 93 | * be found. |
94 | 94 | */ |
— | — | @@ -123,12 +123,12 @@ |
124 | 124 | } |
125 | 125 | } |
126 | 126 | } |
127 | | - |
| 127 | + |
128 | 128 | # Now try redirects |
129 | 129 | if ( !empty( $options['ignoreRedirect'] ) ) { |
130 | 130 | return false; |
131 | 131 | } |
132 | | - $redir = $this->checkRedirect( $title ); |
| 132 | + $redir = $this->checkRedirect( $title ); |
133 | 133 | if( $redir && $redir->getNamespace() == NS_FILE) { |
134 | 134 | $img = $this->newFile( $redir ); |
135 | 135 | if( !$img ) { |
— | — | @@ -141,9 +141,9 @@ |
142 | 142 | } |
143 | 143 | return false; |
144 | 144 | } |
145 | | - |
| 145 | + |
146 | 146 | /* |
147 | | - * Find many files at once. |
| 147 | + * Find many files at once. |
148 | 148 | * @param array $items, an array of titles, or an array of findFile() options with |
149 | 149 | * the "title" option giving the title. Example: |
150 | 150 | * |
— | — | @@ -168,7 +168,7 @@ |
169 | 169 | } |
170 | 170 | return $result; |
171 | 171 | } |
172 | | - |
| 172 | + |
173 | 173 | /** |
174 | 174 | * Create a new File object from the local repository |
175 | 175 | * @param mixed $sha1 SHA-1 key |
— | — | @@ -189,14 +189,14 @@ |
190 | 190 | return call_user_func( $this->fileFactoryKey, $sha1, $this ); |
191 | 191 | } |
192 | 192 | } |
193 | | - |
| 193 | + |
194 | 194 | /** |
195 | 195 | * Find an instance of the file with this key, created at the specified time |
196 | 196 | * Returns false if the file does not exist. Repositories not supporting |
197 | 197 | * version control should return false if the time is specified. |
198 | 198 | * |
199 | 199 | * @param string $sha1 string |
200 | | - * @param array $options Option array, same as findFile(). |
| 200 | + * @param array $options Option array, same as findFile(). |
201 | 201 | */ |
202 | 202 | function findFileFromKey( $sha1, $options = array() ) { |
203 | 203 | if ( !is_array( $options ) ) { |
— | — | @@ -234,7 +234,7 @@ |
235 | 235 | function getThumbScriptUrl() { |
236 | 236 | return $this->thumbScriptUrl; |
237 | 237 | } |
238 | | - |
| 238 | + |
239 | 239 | /** |
240 | 240 | * Get the URL corresponding to one of the four basic zones |
241 | 241 | * @param String $zone One of: public, deleted, temp, thumb |
— | — | @@ -280,7 +280,7 @@ |
281 | 281 | return $path; |
282 | 282 | } |
283 | 283 | } |
284 | | - |
| 284 | + |
285 | 285 | /** |
286 | 286 | * Get a relative path including trailing slash, e.g. f/fa/ |
287 | 287 | * If the repo is not hashed, returns an empty string |
— | — | @@ -397,6 +397,8 @@ |
398 | 398 | */ |
399 | 399 | abstract function storeTemp( $originalName, $srcPath ); |
400 | 400 | |
| 401 | + abstract function append( $srcPath, $toAppendPath ); |
| 402 | + |
401 | 403 | /** |
402 | 404 | * Remove a temporary file or mark it for garbage collection |
403 | 405 | * @param string $virtualUrl The virtual URL returned by storeTemp |
— | — | @@ -587,14 +589,14 @@ |
588 | 590 | /** |
589 | 591 | * Invalidates image redirect cache related to that image |
590 | 592 | * Doesn't do anything for repositories that don't support image redirects. |
591 | | - * |
| 593 | + * |
592 | 594 | * STUB |
593 | 595 | * @param Title $title Title of image |
594 | | - */ |
| 596 | + */ |
595 | 597 | function invalidateImageRedirect( $title ) {} |
596 | | - |
| 598 | + |
597 | 599 | /** |
598 | | - * Get an array or iterator of file objects for files that have a given |
| 600 | + * Get an array or iterator of file objects for files that have a given |
599 | 601 | * SHA-1 content hash. |
600 | 602 | * |
601 | 603 | * STUB |
— | — | @@ -602,9 +604,9 @@ |
603 | 605 | function findBySha1( $hash ) { |
604 | 606 | return array(); |
605 | 607 | } |
606 | | - |
| 608 | + |
607 | 609 | /** |
608 | | - * Get the human-readable name of the repo. |
| 610 | + * Get the human-readable name of the repo. |
609 | 611 | * @return string |
610 | 612 | */ |
611 | 613 | public function getDisplayName() { |
— | — | @@ -616,12 +618,12 @@ |
617 | 619 | if ( !wfEmptyMsg( 'shared-repo-name-' . $this->name, $repoName ) ) { |
618 | 620 | return $repoName; |
619 | 621 | } |
620 | | - return wfMsg( 'shared-repo' ); |
| 622 | + return wfMsg( 'shared-repo' ); |
621 | 623 | } |
622 | | - |
| 624 | + |
623 | 625 | /** |
624 | 626 | * Get a key on the primary cache for this repository. |
625 | | - * Returns false if the repository's cache is not accessible at this site. |
| 627 | + * Returns false if the repository's cache is not accessible at this site. |
626 | 628 | * The parameters are the parts of the key, as for wfMemcKey(). |
627 | 629 | * |
628 | 630 | * STUB |
— | — | @@ -631,7 +633,7 @@ |
632 | 634 | } |
633 | 635 | |
634 | 636 | /** |
635 | | - * Get a key for this repo in the local cache domain. These cache keys are |
| 637 | + * Get a key for this repo in the local cache domain. These cache keys are |
636 | 638 | * not shared with remote instances of the repo. |
637 | 639 | * The parameters are the parts of the key, as for wfMemcKey(). |
638 | 640 | */ |
Index: trunk/phase3/includes/filerepo/ForeignAPIRepo.php |
— | — | @@ -22,7 +22,7 @@ |
23 | 23 | var $apiThumbCacheExpiry = 86400; |
24 | 24 | protected $mQueryCache = array(); |
25 | 25 | protected $mFileExists = array(); |
26 | | - |
| 26 | + |
27 | 27 | function __construct( $info ) { |
28 | 28 | parent::__construct( $info ); |
29 | 29 | $this->mApiBase = $info['apibase']; // http://commons.wikimedia.org/w/api.php |
— | — | @@ -42,7 +42,7 @@ |
43 | 43 | $this->thumbUrl = $this->url . '/thumb'; |
44 | 44 | } |
45 | 45 | } |
46 | | - |
| 46 | + |
47 | 47 | /** |
48 | 48 | * Per docs in FileRepo, this needs to return false if we don't support versioned |
49 | 49 | * files. Well, we don't. |
— | — | @@ -63,14 +63,17 @@ |
64 | 64 | function storeTemp( $originalName, $srcPath ) { |
65 | 65 | return false; |
66 | 66 | } |
| 67 | + function append( $srcPath, $toAppendPath ){ |
| 68 | + return false; |
| 69 | + } |
67 | 70 | function publishBatch( $triplets, $flags = 0 ) { |
68 | 71 | return false; |
69 | 72 | } |
70 | 73 | function deleteBatch( $sourceDestPairs ) { |
71 | 74 | return false; |
72 | 75 | } |
73 | | - |
74 | 76 | |
| 77 | + |
75 | 78 | function fileExistsBatch( $files, $flags = 0 ) { |
76 | 79 | $results = array(); |
77 | 80 | foreach ( $files as $k => $f ) { |
— | — | @@ -99,10 +102,10 @@ |
100 | 103 | function getFileProps( $virtualUrl ) { |
101 | 104 | return false; |
102 | 105 | } |
103 | | - |
| 106 | + |
104 | 107 | protected function queryImage( $query ) { |
105 | 108 | $data = $this->fetchImageQuery( $query ); |
106 | | - |
| 109 | + |
107 | 110 | if( isset( $data['query']['pages'] ) ) { |
108 | 111 | foreach( $data['query']['pages'] as $pageid => $info ) { |
109 | 112 | if( isset( $info['imageinfo'][0] ) ) { |
— | — | @@ -112,10 +115,10 @@ |
113 | 116 | } |
114 | 117 | return false; |
115 | 118 | } |
116 | | - |
| 119 | + |
117 | 120 | protected function fetchImageQuery( $query ) { |
118 | 121 | global $wgMemc; |
119 | | - |
| 122 | + |
120 | 123 | $url = $this->mApiBase . |
121 | 124 | '?' . |
122 | 125 | wfArrayToCgi( |
— | — | @@ -123,7 +126,7 @@ |
124 | 127 | array( |
125 | 128 | 'format' => 'json', |
126 | 129 | 'action' => 'query' ) ) ); |
127 | | - |
| 130 | + |
128 | 131 | if( !isset( $this->mQueryCache[$url] ) ) { |
129 | 132 | $key = $this->getLocalCacheKey( 'ForeignAPIRepo', 'Metadata', md5( $url ) ); |
130 | 133 | $data = $wgMemc->get( $key ); |
— | — | @@ -143,14 +146,14 @@ |
144 | 147 | } |
145 | 148 | return FormatJson::decode( $this->mQueryCache[$url], true ); |
146 | 149 | } |
147 | | - |
| 150 | + |
148 | 151 | function getImageInfo( $title, $time = false ) { |
149 | 152 | return $this->queryImage( array( |
150 | 153 | 'titles' => 'Image:' . $title->getText(), |
151 | 154 | 'iiprop' => 'timestamp|user|comment|url|size|sha1|metadata|mime', |
152 | 155 | 'prop' => 'imageinfo' ) ); |
153 | 156 | } |
154 | | - |
| 157 | + |
155 | 158 | function findBySha1( $hash ) { |
156 | 159 | $results = $this->fetchImageQuery( array( |
157 | 160 | 'aisha1base36' => $hash, |
— | — | @@ -164,7 +167,7 @@ |
165 | 168 | } |
166 | 169 | return $ret; |
167 | 170 | } |
168 | | - |
| 171 | + |
169 | 172 | function getThumbUrl( $name, $width=-1, $height=-1 ) { |
170 | 173 | $info = $this->queryImage( array( |
171 | 174 | 'titles' => 'Image:' . $name, |
— | — | @@ -179,14 +182,14 @@ |
180 | 183 | return false; |
181 | 184 | } |
182 | 185 | } |
183 | | - |
| 186 | + |
184 | 187 | function getThumbUrlFromCache( $name, $width, $height ) { |
185 | 188 | global $wgMemc, $wgUploadPath, $wgServer, $wgUploadDirectory; |
186 | | - |
| 189 | + |
187 | 190 | if ( !$this->canCacheThumbs() ) { |
188 | 191 | return $this->getThumbUrl( $name, $width, $height ); |
189 | 192 | } |
190 | | - |
| 193 | + |
191 | 194 | $key = $this->getLocalCacheKey( 'ForeignAPIRepo', 'ThumbUrl', $name ); |
192 | 195 | if ( $thumbUrl = $wgMemc->get($key) ) { |
193 | 196 | wfDebug("Got thumb from local cache. $thumbUrl \n"); |
— | — | @@ -194,7 +197,7 @@ |
195 | 198 | } |
196 | 199 | else { |
197 | 200 | $foreignUrl = $this->getThumbUrl( $name, $width, $height ); |
198 | | - |
| 201 | + |
199 | 202 | // We need the same filename as the remote one :) |
200 | 203 | $fileName = rawurldecode( pathinfo( $foreignUrl, PATHINFO_BASENAME ) ); |
201 | 204 | $path = 'thumb/' . $this->getHashPath( $name ) . $name . "/"; |
— | — | @@ -213,7 +216,7 @@ |
214 | 217 | return $localUrl; |
215 | 218 | } |
216 | 219 | } |
217 | | - |
| 220 | + |
218 | 221 | /** |
219 | 222 | * @see FileRepo::getZoneUrl() |
220 | 223 | */ |
Index: trunk/phase3/includes/api/ApiUpload.php |
— | — | @@ -40,6 +40,10 @@ |
41 | 41 | public function execute() { |
42 | 42 | global $wgUser, $wgAllowCopyUploads; |
43 | 43 | |
| 44 | + // Check whether upload is enabled |
| 45 | + if ( !UploadBase::isEnabled() ) |
| 46 | + $this->dieUsageMsg( array( 'uploaddisabled' ) ); |
| 47 | + |
44 | 48 | $this->getMain()->isWriteMode(); |
45 | 49 | $this->mParams = $this->extractRequestParams(); |
46 | 50 | $request = $this->getMain()->getRequest(); |
— | — | @@ -53,15 +57,27 @@ |
54 | 58 | // Add the uploaded file to the params array |
55 | 59 | $this->mParams['file'] = $request->getFileName( 'file' ); |
56 | 60 | |
57 | | - // Check whether upload is enabled |
58 | | - if ( !UploadBase::isEnabled() ) |
59 | | - $this->dieUsageMsg( array( 'uploaddisabled' ) ); |
60 | | - |
61 | 61 | // One and only one of the following parameters is needed |
62 | 62 | $this->requireOnlyOneParameter( $this->mParams, |
63 | 63 | 'sessionkey', 'file', 'url', 'enablechunks' ); |
64 | 64 | |
65 | | - if ( $this->mParams['sessionkey'] ) { |
| 65 | + // Initialize $this->mUpload |
| 66 | + if ( $this->mParams['enablechunks'] ) { |
| 67 | + $this->mUpload = new UploadFromChunks(); |
| 68 | + |
| 69 | + $this->mUpload->initialize( |
| 70 | + $request->getVal( 'done', null ), |
| 71 | + $request->getVal( 'filename', null ), |
| 72 | + $request->getVal( 'chunksession', null ), |
| 73 | + $request->getFileTempName( 'chunk' ), |
| 74 | + $request->getFileSize( 'chunk' ), |
| 75 | + $request->getSessionData( 'wsUploadData' ) |
| 76 | + ); |
| 77 | + |
| 78 | + if ( !$this->mUpload->status->isOK() ) { |
| 79 | + return $this->dieUsageMsg( $this->mUpload->status->getErrorsArray() ); |
| 80 | + } |
| 81 | + } elseif ( $this->mParams['sessionkey'] ) { |
66 | 82 | /** |
67 | 83 | * Upload stashed in a previous request |
68 | 84 | */ |
— | — | @@ -72,30 +88,13 @@ |
73 | 89 | $this->mUpload = new UploadFromStash(); |
74 | 90 | $this->mUpload->initialize( $this->mParams['filename'], |
75 | 91 | $_SESSION['wsUploadData'][$this->mParams['sessionkey']] ); |
76 | | - } else { |
| 92 | + } elseif ( isset( $this->mParams['filename'] ) ) { |
77 | 93 | /** |
78 | 94 | * Upload from url, etc |
79 | 95 | * Parameter filename is required |
80 | 96 | */ |
81 | | - if ( !isset( $this->mParams['filename'] ) ) |
82 | | - $this->dieUsageMsg( array( 'missingparam', 'filename' ) ); |
83 | 97 | |
84 | | - // Initialize $this->mUpload |
85 | | - if ( $this->mParams['enablechunks'] ) { |
86 | | - $this->mUpload = new UploadFromChunks(); |
87 | | - $this->mUpload->initialize( |
88 | | - $request->getVal( 'done', null ), |
89 | | - $request->getVal( 'filename', null ), |
90 | | - $request->getVal( 'chunksessionkey', null ), |
91 | | - $request->getFileTempName( 'chunk' ), |
92 | | - $request->getFileSize( 'chunk' ), |
93 | | - $request->getSessionData( 'wsUploadData' ) |
94 | | - ); |
95 | | - |
96 | | - if ( !$this->mUpload->status->isOK() ) { |
97 | | - return $this->dieUsageMsg( $this->mUpload->status->getErrorsArray() ); |
98 | | - } |
99 | | - } elseif ( isset( $this->mParams['file'] ) ) { |
| 98 | + if ( isset( $this->mParams['file'] ) ) { |
100 | 99 | $this->mUpload = new UploadFromFile(); |
101 | 100 | $this->mUpload->initialize( |
102 | 101 | $this->mParams['filename'], |
— | — | @@ -120,7 +119,7 @@ |
121 | 120 | return $this->dieUsage( $status->getWikiText(), 'fetchfileerror' ); |
122 | 121 | } |
123 | 122 | } |
124 | | - } |
| 123 | + } else $this->dieUsageMsg( array( 'missingparam', 'filename' ) ); |
125 | 124 | |
126 | 125 | if ( !isset( $this->mUpload ) ) |
127 | 126 | $this->dieUsage( 'No upload module set', 'nomodule' ); |
— | — | @@ -242,6 +241,8 @@ |
243 | 242 | $this->getResult()->setIndexedTagName( $result['details'], 'error' ); |
244 | 243 | |
245 | 244 | $this->dieUsage( 'An internal error occurred', 'internal-error', 0, $error ); |
| 245 | + } elseif( isset($status->value->uploadUrl) ) { |
| 246 | + return $status->value; |
246 | 247 | } |
247 | 248 | |
248 | 249 | $file = $this->mUpload->getLocalFile(); |
— | — | @@ -272,7 +273,7 @@ |
273 | 274 | 'ignorewarnings' => false, |
274 | 275 | 'file' => null, |
275 | 276 | 'enablechunks' => false, |
276 | | - 'chunksessionkey' => null, |
| 277 | + 'chunksession' => null, |
277 | 278 | 'chunk' => null, |
278 | 279 | 'done' => false, |
279 | 280 | 'url' => null, |
— | — | @@ -295,7 +296,7 @@ |
296 | 297 | 'ignorewarnings' => 'Ignore any warnings', |
297 | 298 | 'file' => 'File contents', |
298 | 299 | 'enablechunks' => 'Set to use chunk mode; see http://firefogg.org/dev/chunk_post.html for protocol', |
299 | | - 'chunksessionkey' => 'The session key, established on the first contact during the chunked upload', |
| 300 | + 'chunksession' => 'The session key, established on the first contact during the chunked upload', |
300 | 301 | 'chunk' => 'The data in this chunk of a chunked upload', |
301 | 302 | 'done' => 'Set to 1 on the last chunk of a chunked upload', |
302 | 303 | 'url' => 'Url to fetch the file from', |
Index: trunk/phase3/includes/WebRequest.php |
— | — | @@ -712,6 +712,7 @@ |
713 | 713 | class FauxRequest extends WebRequest { |
714 | 714 | private $wasPosted = false; |
715 | 715 | private $session = array(); |
| 716 | + private $response; |
716 | 717 | |
717 | 718 | /** |
718 | 719 | * @param $data Array of *non*-urlencoded key => value pairs, the |
— | — | @@ -767,9 +768,8 @@ |
768 | 769 | } |
769 | 770 | |
770 | 771 | public function getSessionData( $key ) { |
771 | | - if( !isset( $this->session[$key] ) ) |
772 | | - return null; |
773 | | - return $this->session[$key]; |
| 772 | + if( isset( $this->session[$key] ) ) |
| 773 | + return $this->session[$key]; |
774 | 774 | } |
775 | 775 | |
776 | 776 | public function setSessionData( $key, $data ) { |
— | — | @@ -780,4 +780,11 @@ |
781 | 781 | return false; |
782 | 782 | } |
783 | 783 | |
| 784 | + public function response() { |
| 785 | + /* Lazy initialization of response object for this request */ |
| 786 | + if ( !is_object( $this->response ) ) { |
| 787 | + $this->response = new FauxResponse; |
| 788 | + } |
| 789 | + return $this->response; |
| 790 | + } |
784 | 791 | } |
Index: trunk/phase3/includes/AutoLoader.php |
— | — | @@ -84,6 +84,7 @@ |
85 | 85 | 'FakeTitle' => 'includes/FakeTitle.php', |
86 | 86 | 'FakeMemCachedClient' => 'includes/ObjectCache.php', |
87 | 87 | 'FauxRequest' => 'includes/WebRequest.php', |
| 88 | + 'FauxResponse' => 'includes/WebResponse.php', |
88 | 89 | 'FeedItem' => 'includes/Feed.php', |
89 | 90 | 'FeedUtils' => 'includes/FeedUtils.php', |
90 | 91 | 'FileDeleteForm' => 'includes/FileDeleteForm.php', |
Index: trunk/phase3/includes/WebResponse.php |
— | — | @@ -6,7 +6,7 @@ |
7 | 7 | */ |
8 | 8 | class WebResponse { |
9 | 9 | |
10 | | - /** |
| 10 | + /** |
11 | 11 | * Output a HTTP header, wrapper for PHP's |
12 | 12 | * header() |
13 | 13 | * @param $string String: header to output |
— | — | @@ -58,3 +58,31 @@ |
59 | 59 | } |
60 | 60 | } |
61 | 61 | } |
| 62 | + |
| 63 | + |
| 64 | +class FauxResponse extends WebResponse { |
| 65 | + private $headers; |
| 66 | + private $cookies; |
| 67 | + |
| 68 | + public function header($string, $replace=true) { |
| 69 | + list($key, $val) = explode(":", $string, 2); |
| 70 | + |
| 71 | + if($replace || !isset($this->headers[$key])) { |
| 72 | + $this->headers[$key] = $val; |
| 73 | + } |
| 74 | + } |
| 75 | + |
| 76 | + public function getheader($key) { |
| 77 | + return $this->headers[$key]; |
| 78 | + } |
| 79 | + |
| 80 | + public function setcookie( $name, $value, $expire = 0 ) { |
| 81 | + $this->cookies[$name] = $value; |
| 82 | + } |
| 83 | + |
| 84 | + public function getcookie( $name ) { |
| 85 | + if ( isset($this->cookies[$name]) ) { |
| 86 | + return $this->cookies[$name]; |
| 87 | + } |
| 88 | + } |
| 89 | +} |
\ No newline at end of file |