Index: branches/new-upload/phase3/includes/filerepo/FSRepo.php |
— | — | @@ -220,8 +220,7 @@ |
221 | 221 | if ( !is_file( $toAppendPath ) ) |
222 | 222 | $status->fatal( 'filenotfound', $toAppendPath ); |
223 | 223 | |
224 | | - //I assume fopen($src, 'a') fopen ($$toAppend .. etc would be faster / more memory friendly |
225 | | - //but ideally we don't append "big" files so it does not matter |
| 224 | + //do the append: |
226 | 225 | if( ! file_put_contents( $srcPath, file_get_contents( $toAppendPath ), FILE_APPEND ) ) |
227 | 226 | $status->fatal( 'fileappenderror', $toAppendPath, $srcPath); |
228 | 227 | |
— | — | @@ -260,10 +259,8 @@ |
261 | 260 | } |
262 | 261 | /*append to a temporary file (used in chunks uploads) */ |
263 | 262 | function appendToTemp( $srcPath, $appendDataPath ) { |
264 | | - //open the source file |
265 | | - |
266 | | - $result = $this->append( $srcPath, $appendDataPath ); |
267 | | - $result->value = $this->getVirtualUrl( 'temp' ) . '/' . $dstUrlRel; |
| 263 | + //append to the "shared" temporary file |
| 264 | + $result = $this->append( $srcPath, $appendDataPath ); |
268 | 265 | return $result; |
269 | 266 | } |
270 | 267 | |
Index: branches/new-upload/phase3/includes/api/ApiUpload.php |
— | — | @@ -27,7 +27,7 @@ |
28 | 28 | require_once ("ApiBase.php"); |
29 | 29 | } |
30 | 30 | |
31 | | - |
| 31 | + |
32 | 32 | /** |
33 | 33 | * @ingroup API |
34 | 34 | */ |
— | — | @@ -38,10 +38,13 @@ |
39 | 39 | } |
40 | 40 | |
41 | 41 | public function execute() { |
42 | | - global $wgUser; |
| 42 | + global $wgUser; |
| 43 | + |
| 44 | + |
43 | 45 | $this->getMain()->requestWriteMode(); |
44 | 46 | $this->mParams = $this->extractRequestParams(); |
45 | | - $request = $this->getMain()->getRequest(); |
| 47 | + $request = $this->getMain()->getRequest(); |
| 48 | + |
46 | 49 | // Add the uploaded file to the params array |
47 | 50 | $this->mParams['file'] = $request->getFileName( 'file' ); |
48 | 51 | |
— | — | @@ -56,19 +59,17 @@ |
57 | 60 | if( $this->mParams['enablechunks'] ){ |
58 | 61 | //chunks upload enabled |
59 | 62 | $this->mUpload = new UploadFromChunks(); |
60 | | - $this->mUpload->initializeFromParams( $this->mParams ); |
61 | | - |
62 | | - if( isset( $this->mUpload->status[ 'error' ] ) ) |
63 | | - $this->dieUsageMsg( $this->mUpload->status[ 'error' ] ); |
64 | | - |
| 63 | + $this->mUpload->initializeFromParams( $this->mParams, $request ); |
| 64 | + //if getAPIresult did not exit report the status error: |
| 65 | + if( isset( $this->mUpload->status[ 'error' ] ) ) |
| 66 | + $this->dieUsageMsg( $this->mUpload->status[ 'error' ] ); |
| 67 | + |
65 | 68 | } else if( $this->mParams['sessionkey'] ) { |
66 | 69 | // Stashed upload |
67 | 70 | $this->mUpload = new UploadFromStash(); |
68 | | - $this->mUpload->initialize( $this->mParams['sessionkey'] ); |
69 | | - |
| 71 | + $this->mUpload->initialize( $this->mParams['sessionkey'] ); |
70 | 72 | }else{ |
71 | | - // Upload from url or file or start a chunks request |
72 | | - |
| 73 | + // Upload from url or file |
73 | 74 | // Parameter filename is required |
74 | 75 | if( !isset( $this->mParams['filename'] ) ) |
75 | 76 | $this->dieUsageMsg( array( 'missingparam', 'filename' ) ); |
— | — | @@ -77,15 +78,16 @@ |
78 | 79 | if( isset( $this->mParams['file'] ) ) { |
79 | 80 | $this->mUpload = new UploadFromUpload(); |
80 | 81 | $this->mUpload->initialize( |
| 82 | + $request->getFileName( 'file' ), |
81 | 83 | $request->getFileTempName( 'file' ), |
82 | | - $request->getFileSize( 'file' ), |
83 | | - $request->getFileName( 'file' ) |
| 84 | + $request->getFileSize( 'file' ) |
84 | 85 | ); |
85 | 86 | } elseif( isset( $this->mParams['url'] ) ) { |
86 | 87 | $this->mUpload = new UploadFromUrl(); |
87 | 88 | $this->mUpload->initialize( $this->mParams['filename'], $this->mParams['url'] ); |
88 | 89 | } |
89 | 90 | } |
| 91 | + |
90 | 92 | if( !isset( $this->mUpload ) ) |
91 | 93 | $this->dieUsage( 'No upload module set', 'nomodule' ); |
92 | 94 | |
— | — | @@ -99,8 +101,7 @@ |
100 | 102 | $this->dieUsageMsg( array( 'mustbeloggedin', 'upload' ) ); |
101 | 103 | else |
102 | 104 | $this->dieUsageMsg( array( 'badaccess-groups' ) ); |
103 | | - } |
104 | | - |
| 105 | + } |
105 | 106 | // Perform the upload |
106 | 107 | $result = $this->performUpload(); |
107 | 108 | |
— | — | @@ -111,7 +112,7 @@ |
112 | 113 | } |
113 | 114 | |
114 | 115 | private function performUpload() { |
115 | | - global $wgUser; |
| 116 | + global $wgUser; |
116 | 117 | $result = array(); |
117 | 118 | $resultDetails = null; |
118 | 119 | |
— | — | @@ -122,7 +123,7 @@ |
123 | 124 | return $result; |
124 | 125 | } |
125 | 126 | |
126 | | - $verification = $this->mUpload->verifyUpload( $resultDetails ); |
| 127 | + $verification = $this->mUpload->verifyUpload( $resultDetails ); |
127 | 128 | if( $verification != UploadBase::OK ) { |
128 | 129 | $result['result'] = 'Failure'; |
129 | 130 | switch( $verification ) { |
— | — | @@ -166,8 +167,7 @@ |
167 | 168 | break; |
168 | 169 | } |
169 | 170 | return $result; |
170 | | - } |
171 | | - |
| 171 | + } |
172 | 172 | if( !$this->mParams['ignorewarnings'] ) { |
173 | 173 | $warnings = $this->mUpload->checkWarnings(); |
174 | 174 | if( $warnings ) { |
— | — | @@ -183,13 +183,7 @@ |
184 | 184 | $result['sessionkey'] = $sessionKey; |
185 | 185 | return $result; |
186 | 186 | } |
187 | | - } |
188 | | - |
189 | | - //check for special API upload response: |
190 | | - $upApiResult = $this->mUpload->getAPIresult( $this->mParams['comment'], $this->mParams['watch'] ); |
191 | | - if( $upApiResult != UploadBase::OK ) //if we have a result override return it |
192 | | - return $upApiResult; |
193 | | - |
| 187 | + } |
194 | 188 | //do the upload |
195 | 189 | $status = $this->mUpload->performUpload( $this->mParams['comment'], |
196 | 190 | $this->mParams['comment'], $this->mParams['watch'], $wgUser ); |
— | — | @@ -222,6 +216,7 @@ |
223 | 217 | return array ( |
224 | 218 | 'filename' => null, |
225 | 219 | 'file' => null, |
| 220 | + 'chunk' => null, |
226 | 221 | 'url' => null, |
227 | 222 | 'comment' => array( |
228 | 223 | ApiBase :: PARAM_DFLT => '' |
— | — | @@ -231,6 +226,7 @@ |
232 | 227 | 'enablechunks' => false, |
233 | 228 | 'done' => false, |
234 | 229 | 'sessionkey' => null, |
| 230 | + 'chunksessionkey'=> null, |
235 | 231 | ); |
236 | 232 | } |
237 | 233 | |
— | — | @@ -244,7 +240,8 @@ |
245 | 241 | 'ignorewarnings' => 'Ignore any warnings', |
246 | 242 | 'enablechunks' => 'Boolean If we are in chunk mode; accepts many small file POSTs', |
247 | 243 | 'done' => 'When used with "chunks", Is sent to notify the api The last chunk is being uploaded.', |
248 | | - 'sessionkey' => 'Session key in case there were any warnings, or uploading chunks' |
| 244 | + 'sessionkey' => 'Session key in case there were any warnings.', |
| 245 | + 'chunksessionkey'=> 'Used to sync uploading of chunks', |
249 | 246 | ); |
250 | 247 | } |
251 | 248 | |
Index: branches/new-upload/phase3/includes/UploadFromChunks.php |
— | — | @@ -18,28 +18,33 @@ |
19 | 19 | const CHUNK = 2; |
20 | 20 | const DONE = 3; |
21 | 21 | |
22 | | - function initializeFromParams( $param ) { |
23 | | - $this->initFromSessionKey( $param['sessionkey'] ); |
24 | | - |
| 22 | + function initializeFromParams( &$param , &$request) { |
| 23 | + $this->initFromSessionKey( $param['chunksessionkey'] ); |
25 | 24 | //set the chunk mode: |
26 | | - if( !$this->mSessionKey && !$parm['done'] ){ |
| 25 | + if( !$this->mSessionKey && !$param['done'] ){ |
27 | 26 | //session key not set init the chunk upload system: |
28 | | - $this->chunk_mode = UploadFromChunks::INIT; |
29 | | - }else if( $this->mSessionKey && !$parm['done']){ |
| 27 | + $this->chunk_mode = UploadFromChunks::INIT; |
| 28 | + $this->mDesiredDestName = $param['filename']; |
| 29 | + |
| 30 | + }else if( $this->mSessionKey && !$param['done']){ |
30 | 31 | //this is a chunk piece |
31 | 32 | $this->chunk_mode = UploadFromChunks::CHUNK; |
32 | | - |
33 | | - }else if( $this->mSessionKey && $parm['done']){ |
| 33 | + |
| 34 | + //set chunk related vars: |
| 35 | + $this->mTempPath = $request->getFileTempName( 'chunk' ); |
| 36 | + $this->mFileSize = $request->getFileSize( 'chunk' ); |
| 37 | + |
| 38 | + }else if( $this->mSessionKey && $param['done']){ |
34 | 39 | //this is the last chunk |
35 | 40 | $this->chunk_mode = UploadFromChunks::DONE; |
36 | 41 | } |
37 | 42 | return $this->status; |
38 | 43 | } |
39 | 44 | |
40 | | - function initFromSessionKey( $sessionKey ){ |
| 45 | + function initFromSessionKey( $sessionKey ){ |
41 | 46 | if( !$sessionKey || empty( $sessionKey ) ){ |
42 | 47 | return false; |
43 | | - } |
| 48 | + } |
44 | 49 | $this->mSessionKey = $sessionKey; |
45 | 50 | if( isset( $_SESSION['wsUploadData'][$this->mSessionKey]['version'] ) && |
46 | 51 | $_SESSION['wsUploadData'][$this->mSessionKey]['version'] == self::SESSION_VERSION ) { |
— | — | @@ -48,11 +53,11 @@ |
49 | 54 | $this->mWatch = $_SESSION[ 'wsUploadData' ][ $this->mSessionKey ][ 'mWatch' ]; |
50 | 55 | $this->mFilteredName = $_SESSION[ 'wsUploadData' ][ $this->mSessionKey ][ 'mFilteredName' ]; |
51 | 56 | $this->mTempAppendPath = $_SESSION[ 'wsUploadData' ][ $this->mSessionKey ][ 'mTempAppendPath' ]; |
| 57 | + $this->mDesiredDestName = $_SESSION[ 'wsUploadData' ][ $this->mSessionKey ][ 'mDesiredDestName' ]; |
52 | 58 | }else{ |
53 | 59 | $this->status = Array( 'error'=> 'missing session data'); |
54 | 60 | return false; |
55 | | - } |
56 | | - |
| 61 | + } |
57 | 62 | } |
58 | 63 | static function isValidRequest( $request ) { |
59 | 64 | $sessionData = $request->getSessionData('wsUploadData'); |
— | — | @@ -69,88 +74,113 @@ |
70 | 75 | $warning = array(); |
71 | 76 | return $warning; |
72 | 77 | } |
73 | | - |
| 78 | + function isEmptyFile(){ |
| 79 | + //does not apply to chunk init |
| 80 | + if( $this->chunk_mode == UploadFromChunks::INIT ){ |
| 81 | + return false; |
| 82 | + }else{ |
| 83 | + return parent::isEmptyFile(); |
| 84 | + } |
| 85 | + } |
74 | 86 | /* Verify whether the upload is sane. |
75 | 87 | * Returns self::OK or else an array with error information |
76 | 88 | */ |
77 | | - function verifyUpload( $resultDetails ) { |
78 | | - /* |
79 | | - * check the internal chunk mode for alternative Verify path |
80 | | - * (for now just return "OK" |
81 | | - */ |
82 | | - if( $this->chunk_mode == UploadFromChunks::INIT) |
| 89 | + function verifyUpload( $resultDetails ) { |
| 90 | + //no checks on chunk upload mode: |
| 91 | + if( $this->chunk_mode == UploadFromChunks::INIT ) |
83 | 92 | return self::OK; |
84 | | - |
85 | | - return parent::verifyUpload( $resultDetails ); |
| 93 | + |
| 94 | + //verify on init and last chunk request |
| 95 | + if( $this->chunk_mode == UploadFromChunks::CHUNK || |
| 96 | + $this->chunk_mode == UploadFromChunks::DONE ) |
| 97 | + return parent::verifyUpload( $resultDetails ); |
86 | 98 | } |
87 | | - |
| 99 | + //only run verifyFile on completed uploaded chunks |
| 100 | + function verifyFile( $tmpFile ){ |
| 101 | + if( $this->chunk_mode == UploadFromChunks::DONE){ |
| 102 | + return parent::verifyFile($tmpFile); |
| 103 | + }else{ |
| 104 | + return true; |
| 105 | + } |
| 106 | + } |
88 | 107 | function setupChunkSession( $comment, $watch ) { |
89 | | - $key = $this->getSessionKey(); |
90 | | - //since we can't pass things along in POST store them in the Session: |
91 | | - $_SESSION['wsUploadData'][$key] = array( |
| 108 | + $this->mSessionKey = $this->getSessionKey(); |
| 109 | + |
| 110 | + $_SESSION['wsUploadData'][ $this->mSessionKey ] = array( |
92 | 111 | 'mComment' => $comment, |
93 | 112 | 'mWatch' => $watch, |
94 | 113 | 'mFilteredName' => $this->mFilteredName, |
95 | | - 'mTempAppendPath' => null, |
| 114 | + 'mTempAppendPath' => null, //the repo append path (not temporary local node mTempPath) |
| 115 | + 'mDesiredDestName' => $this->mDesiredDestName, |
96 | 116 | 'version' => self::SESSION_VERSION, |
97 | 117 | ); |
98 | | - return $key; |
| 118 | + |
| 119 | + return $this->mSessionKey; |
99 | 120 | } |
100 | 121 | |
101 | 122 | //lets us return an api result (as flow for chunk uploads is kind of different than others. |
102 | | - function getAPIresult($comment, $watch){ |
103 | | - if( $this->chunk_mode == UploadFromChunks::INIT ){ |
104 | | - //verifyUpload & checkWarnings have already run .. just create the upload store return the upload session key |
| 123 | + function performUpload($summary='', $comment='', $watch='', $user){ |
| 124 | + global $wgServer, $wgScriptPath; |
| 125 | + if( $this->chunk_mode == UploadFromChunks::INIT ){ |
| 126 | + |
| 127 | + //firefogg expects a specific result per: |
| 128 | + //http://www.firefogg.org/dev/chunk_post.html |
| 129 | + print "{\"uploadUrl\": \"{$wgServer}{$wgScriptPath}/api.php?action=upload&format=json&enablechunks=true&chunksessionkey=". |
| 130 | + $this->setupChunkSession( $comment, $watch ) . "\" }"; |
| 131 | + exit(0); |
| 132 | + |
| 133 | + /* |
| 134 | + * @@todo would be more ideal to have firefogg pass results back to the client to construct next chunk url |
105 | 135 | return array( |
106 | 136 | 'sessionkey'=> $this->setupChunkSession( $comment, $watch ) |
107 | 137 | ); |
108 | | - }else if( $this->chunk_mode == UploadFromChunks::CHUNK ){ |
109 | | - |
110 | | - $this->doChunkAppend(); |
111 | | - |
112 | | - //return success: |
113 | | - return array( |
114 | | - 'result' => 1 |
115 | | - ); |
116 | | - |
| 138 | + */ |
| 139 | + }else if( $this->chunk_mode == UploadFromChunks::CHUNK ){ |
| 140 | + $status = $this->doChunkAppend(); |
| 141 | + if( $status->isOK() ){ |
| 142 | + //return success: |
| 143 | + //firefogg expects a specific result per: |
| 144 | + //http://www.firefogg.org/dev/chunk_post.html |
| 145 | + print "{\"result\": 1}"; |
| 146 | + exit(0); |
| 147 | + /*return array( |
| 148 | + 'result' => 1 |
| 149 | + );*/ |
| 150 | + }else{ |
| 151 | + return $status; |
| 152 | + } |
117 | 153 | }else if( $this->chunk_mode == UploadFromChunks::DONE ){ |
118 | 154 | //append the last chunk: |
119 | 155 | if( $this->doChunkAppend() ){ |
120 | 156 | //process the upload normally: |
121 | | - return UploadFrom::OK; |
| 157 | + return Status::newGood('chunk upload done'); |
122 | 158 | } |
123 | 159 | } |
124 | 160 | } |
125 | 161 | //append the given chunk to the temporary uploaded file. (if no temporary uploaded file exists created it. |
126 | 162 | function doChunkAppend(){ |
127 | | - //if we don't have a mTempAppendPath to append to generate that: |
| 163 | + //if we don't have a mTempAppendPath to generate a file from the chunk packaged var: |
128 | 164 | if( ! $this->mTempAppendPath ){ |
129 | | - //make a chunk store path. (append tmp file to chunk) |
130 | | - print "save Temp: " . $this->mTempPath . ' '. $this->mDestName . "\n"; |
131 | | - if( isset( $this->mDestName ) ){ |
132 | | - $stash = $this->saveTempUploadedFile( $this->mDestName, $this->mTempPath ); |
133 | | - if( !$stash ) { |
134 | | - # Couldn't save the file. |
135 | | - return false; |
136 | | - } |
137 | | - //update the mDestName |
138 | | - $this->mTempAppendPath = $stash; |
139 | | - $_SESSION[ 'wsUploadData' ][ $this->mSessionKey ] = $this->mTempAppendPath; |
140 | | - } |
| 165 | + //die(); |
| 166 | + //get temp name: |
| 167 | + //make a chunk store path. (append tmp file to chunk) |
| 168 | + $status = $this->saveTempUploadedFile( $this->mDestName, $this->mTempPath ); |
| 169 | + |
| 170 | + if( $status->isOK() ) { |
| 171 | + $this->mTempAppendPath = $status->value; |
| 172 | + $_SESSION[ 'wsUploadData' ][ $this->mSessionKey ][ 'mTempAppendPath' ] = $this->mTempAppendPath; |
| 173 | + print "did save to $status->value \n"; |
| 174 | + } |
| 175 | + return $status; |
141 | 176 | }else{ |
142 | 177 | //make sure the file exists: |
143 | 178 | if( is_file( $this->mTempAppendPath ) ){ |
144 | 179 | print "append: " . $this->mTempPath . ' to ' . $this->mTempAppendPath . "\n"; |
145 | | - $this->appendToUploadFile( $this->mTempAppendPath, $this->mTempPath ); |
| 180 | + $status = $this->appendToUploadFile( $this->mTempAppendPath, $this->mTempPath ); |
| 181 | + return $status; |
| 182 | + }else{ |
| 183 | + return Status::newFatal('chunk-file-append-missing'); |
146 | 184 | } |
147 | 185 | } |
148 | | - } |
149 | | - |
150 | | - function checkAPIresultOverride(){ |
151 | | - if( $this->chunk_mode == UploadFromChunks::INIT ){ |
152 | | - return true; |
153 | | - }else{ |
154 | | - return false; |
155 | | - } |
156 | | - } |
| 186 | + } |
157 | 187 | } |
Index: branches/new-upload/phase3/includes/DefaultSettings.php |
— | — | @@ -437,6 +437,7 @@ |
438 | 438 | */ |
439 | 439 | $wgMaxUploadSize = 1024*1024*100; # 100MB |
440 | 440 | |
| 441 | + |
441 | 442 | /** |
442 | 443 | * Point the upload navigation link to an external URL |
443 | 444 | * Useful if you want to use a shared repository by default |
Index: branches/new-upload/phase3/includes/UploadBase.php |
— | — | @@ -100,19 +100,23 @@ |
101 | 101 | function fetchFile() { |
102 | 102 | return self::OK; |
103 | 103 | } |
104 | | - |
| 104 | + //return the file size |
| 105 | + function isEmptyFile(){ |
| 106 | + return empty( $this->mFileSize); |
| 107 | + } |
105 | 108 | /** |
106 | 109 | * Verify whether the upload is sane. |
107 | 110 | * Returns self::OK or else an array with error information |
108 | 111 | */ |
109 | | - function verifyUpload() { |
| 112 | + function verifyUpload() { |
110 | 113 | /** |
111 | 114 | * If there was no filename or a zero size given, give up quick. |
112 | 115 | */ |
113 | | - if( empty( $this->mFileSize ) ) |
| 116 | + |
| 117 | + if( $this->isEmptyFile() ) |
114 | 118 | return array( 'status' => self::EMPTY_FILE ); |
115 | | - |
116 | | - $nt = $this->getTitle(); |
| 119 | + |
| 120 | + $nt = $this->getTitle(); |
117 | 121 | if( is_null( $nt ) ) { |
118 | 122 | $result = array( 'status' => $this->mTitleError ); |
119 | 123 | if( $this->mTitleError == self::ILLEGAL_FILENAME ) |
— | — | @@ -122,20 +126,20 @@ |
123 | 127 | return $result; |
124 | 128 | } |
125 | 129 | $this->mLocalFile = wfLocalFile( $nt ); |
126 | | - $this->mDestName = $this->mLocalFile->getName(); |
127 | | - |
| 130 | + $this->mDestName = $this->mLocalFile->getName(); |
| 131 | + |
128 | 132 | /** |
129 | 133 | * In some cases we may forbid overwriting of existing files. |
130 | 134 | */ |
131 | 135 | $overwrite = $this->checkOverwrite(); |
132 | 136 | if( $overwrite !== true ) |
133 | 137 | return array( 'status' => self::OVERWRITE_EXISTING_FILE, 'overwrite' => $overwrite ); |
134 | | - |
| 138 | + |
135 | 139 | /** |
136 | 140 | * Look at the contents of the file; if we can recognize the |
137 | 141 | * type but it's corrupt or data of the wrong type, we should |
138 | 142 | * probably not accept it. |
139 | | - */ |
| 143 | + */ |
140 | 144 | $verification = $this->verifyFile( $this->mTempPath ); |
141 | 145 | |
142 | 146 | if( $verification !== true ) { |
— | — | @@ -423,7 +427,7 @@ |
424 | 428 | $status = $repo->storeTemp( $saveName, $tempName ); |
425 | 429 | return $status; |
426 | 430 | } |
427 | | - /* append to a stached file */ |
| 431 | + /* append to a stashed file */ |
428 | 432 | function appendToUploadFile($srcPath, $toAppendPath ){ |
429 | 433 | $repo = RepoGroup::singleton()->getLocalRepo(); |
430 | 434 | $status = $repo->append($srcPath, $toAppendPath); |
— | — | @@ -858,11 +862,6 @@ |
859 | 863 | return true; |
860 | 864 | |
861 | 865 | } |
862 | | - /* allow for getAPIresult override (normally just return UploadFrom::OK to continue form processing */ |
863 | | - function getAPIresult(){ |
864 | | - return UploadFrom::OK; |
865 | | - } |
866 | | - |
867 | 866 | /** |
868 | 867 | * Check if a user is the last uploader |
869 | 868 | * |