Index: trunk/phase3/skins/common/upload.js |
— | — | @@ -34,8 +34,77 @@ |
35 | 35 | } |
36 | 36 | } |
37 | 37 | } |
| 38 | + |
| 39 | + // Toggle source type |
| 40 | + var sourceTypeCheckboxes = document.getElementsByName( 'wpSourceType' ); |
| 41 | + for ( var i = 0; i < sourceTypeCheckboxes.length; i++ ) { |
| 42 | + sourceTypeCheckboxes[i].onchange = toggleUploadInputs; |
| 43 | + } |
| 44 | + |
| 45 | + // AJAX wpDestFile warnings |
| 46 | + if ( wgAjaxUploadDestCheck ) { |
| 47 | + document.getElementById( 'wpDestFile' ).onchange = function ( e ) { |
| 48 | + wgUploadWarningObj.checkNow(this.value); |
| 49 | + }; |
| 50 | + var optionsTable = document.getElementById( 'mw-htmlform-options' ).tBodies[0]; |
| 51 | + var row = document.createElement( 'tr' ); |
| 52 | + var td = document.createElement( 'td' ); |
| 53 | + td.id = 'wpDestFile-warning'; |
| 54 | + td.colSpan = 2; |
| 55 | + row.appendChild( td ); |
| 56 | + optionsTable.appendChild( row ); |
| 57 | + } |
| 58 | + |
| 59 | + // License selector check |
| 60 | + document.getElementById( 'wpLicense' ).onchange = licenseSelectorCheck; |
| 61 | + |
| 62 | + // fillDestFile setup |
| 63 | + for ( var i = 0; i < wgUploadSourceIds.length; i++ ) |
| 64 | + document.getElementById( wgUploadSourceIds[i] ).onchange = function (e) { |
| 65 | + fillDestFilename( this.id ); |
| 66 | + }; |
38 | 67 | } |
39 | 68 | |
| 69 | +/** |
| 70 | + * Iterate over all upload source fields and disable all except the selected one. |
| 71 | + * |
| 72 | + * @param enabledId The id of the selected radio button |
| 73 | + * @return emptiness |
| 74 | + */ |
| 75 | +function toggleUploadInputs() { |
| 76 | + // Iterate over all rows with UploadSourceField |
| 77 | + var rows; |
| 78 | + if ( document.getElementsByClassName ) { |
| 79 | + rows = document.getElementsByClassName( 'mw-htmlform-field-UploadSourceField' ); |
| 80 | + } else { |
| 81 | + // Older browsers don't support getElementsByClassName |
| 82 | + rows = new Array(); |
| 83 | + |
| 84 | + var allRows = document.getElementsByTagName( 'tr' ); |
| 85 | + for ( var i = 0; i < allRows.length; i++ ) { |
| 86 | + if ( allRows[i].className == 'mw-htmlform-field-UploadSourceField' ) |
| 87 | + rows.push( allRows[i] ); |
| 88 | + } |
| 89 | + } |
| 90 | + |
| 91 | + for ( var i = 0; i < rows.length; i++ ) { |
| 92 | + var inputs = rows[i].getElementsByTagName( 'input' ); |
| 93 | + |
| 94 | + // Check if this row is selected |
| 95 | + var isChecked = true; // Default true in case wpSourceType is not found |
| 96 | + for ( var j = 0; j < inputs.length; j++ ) { |
| 97 | + if ( inputs[j].name == 'wpSourceType' ) |
| 98 | + isChecked = inputs[j].checked; |
| 99 | + } |
| 100 | + |
| 101 | + // Disable all unselected rows |
| 102 | + for ( var j = 0; j < inputs.length; j++ ) { |
| 103 | + if ( inputs[j].type != 'radio') |
| 104 | + inputs[j].disabled = !isChecked; |
| 105 | + } |
| 106 | + } |
| 107 | +} |
| 108 | + |
40 | 109 | var wgUploadWarningObj = { |
41 | 110 | 'responseCache' : { '' : ' ' }, |
42 | 111 | 'nameToCheck' : '', |
— | — | @@ -86,7 +155,7 @@ |
87 | 156 | // ajax requests can be supported. |
88 | 157 | var obj = this; |
89 | 158 | var fileName = this.nameToCheck; |
90 | | - sajax_do_call( 'UploadForm::ajaxGetExistsWarning', [this.nameToCheck], |
| 159 | + sajax_do_call( 'SpecialUpload::ajaxGetExistsWarning', [this.nameToCheck], |
91 | 160 | function (result) { |
92 | 161 | obj.processResult(result, fileName) |
93 | 162 | } |
— | — | @@ -101,16 +170,7 @@ |
102 | 171 | |
103 | 172 | 'setWarning' : function (warning) { |
104 | 173 | var warningElt = document.getElementById( 'wpDestFile-warning' ); |
105 | | - var ackElt = document.getElementById( 'wpDestFileWarningAck' ); |
106 | 174 | this.setInnerHTML(warningElt, warning); |
107 | | - |
108 | | - // Set a value in the form indicating that the warning is acknowledged and |
109 | | - // doesn't need to be redisplayed post-upload |
110 | | - if ( warning == '' || warning == ' ' ) { |
111 | | - ackElt.value = ''; |
112 | | - } else { |
113 | | - ackElt.value = '1'; |
114 | | - } |
115 | 175 | }, |
116 | 176 | 'setInnerHTML' : function (element, text) { |
117 | 177 | // Check for no change to avoid flicker in IE 7 |
— | — | @@ -180,6 +240,7 @@ |
181 | 241 | } |
182 | 242 | |
183 | 243 | // Capitalise first letter and replace spaces by underscores |
| 244 | + // FIXME: $wgCapitalizedNamespaces |
184 | 245 | fname = fname.charAt(0).toUpperCase().concat(fname.substring(1,10000)).replace(/ /g, '_'); |
185 | 246 | |
186 | 247 | // Output result |
— | — | @@ -214,7 +275,7 @@ |
215 | 276 | } |
216 | 277 | } |
217 | 278 | injectSpinner( document.getElementById( 'wpLicense' ), 'license' ); |
218 | | - sajax_do_call( 'UploadForm::ajaxGetLicensePreview', [license], |
| 279 | + sajax_do_call( 'SpecialUpload::ajaxGetLicensePreview', [license], |
219 | 280 | function( result ) { |
220 | 281 | wgUploadLicenseObj.processResult( result, license ); |
221 | 282 | } |
Index: trunk/phase3/skins/common/wikibits.js |
— | — | @@ -469,37 +469,7 @@ |
470 | 470 | return true; |
471 | 471 | } |
472 | 472 | |
473 | | -function toggle_element_activation(ida,idb) { |
474 | | - if ( !document.getElementById ) { |
475 | | - return; |
476 | | - } |
477 | | - // Show the appropriate upload size limit message |
478 | | - if( idb == 'wpUploadFileURL' ) { |
479 | | - var e = document.getElementById( 'mw-upload-maxfilesize' ); |
480 | | - if( e ) e.style.display = "none"; |
481 | 473 | |
482 | | - var e = document.getElementById( 'mw-upload-maxfilesize-url' ); |
483 | | - if( e ) e.style.display = "block"; |
484 | | - } |
485 | | - if( idb == 'wpUploadFile' ) { |
486 | | - var e = document.getElementById( 'mw-upload-maxfilesize-url' ); |
487 | | - if( e ) e.style.display = "none"; |
488 | | - |
489 | | - var e = document.getElementById( 'mw-upload-maxfilesize' ); |
490 | | - if( e ) e.style.display = "block"; |
491 | | - } |
492 | | - document.getElementById( ida ).disabled = true; |
493 | | - document.getElementById( idb ).disabled = false; |
494 | | -} |
495 | | - |
496 | | -function toggle_element_check(ida,idb) { |
497 | | - if (!document.getElementById) { |
498 | | - return; |
499 | | - } |
500 | | - document.getElementById(ida).checked=true; |
501 | | - document.getElementById(idb).checked=false; |
502 | | -} |
503 | | - |
504 | 474 | /* |
505 | 475 | Written by Jonathan Snook, http://www.snook.ca/jonathan |
506 | 476 | Add-ons by Robert Nyman, http://www.robertnyman.com |
Index: trunk/phase3/includes/upload/UploadFromStash.php |
— | — | @@ -41,6 +41,7 @@ |
42 | 42 | false |
43 | 43 | ); |
44 | 44 | |
| 45 | + $this->mVirtualTempPath = $sessionData['mTempPath']; |
45 | 46 | $this->mFileProps = $sessionData['mFileProps']; |
46 | 47 | } |
47 | 48 | |
— | — | @@ -68,4 +69,14 @@ |
69 | 70 | return array(); |
70 | 71 | } |
71 | 72 | |
| 73 | + /** |
| 74 | + * Remove a temporarily kept file stashed by saveTempUploadedFile(). |
| 75 | + * @return success |
| 76 | + */ |
| 77 | + public function unsaveUploadedFile() { |
| 78 | + $repo = RepoGroup::singleton()->getLocalRepo(); |
| 79 | + $success = $repo->freeTemp( $this->mVirtualTempPath ); |
| 80 | + return $success; |
| 81 | + } |
| 82 | + |
72 | 83 | } |
\ No newline at end of file |
Index: trunk/phase3/includes/upload/UploadBase.php |
— | — | @@ -1,18 +1,18 @@ |
2 | 2 | <?php |
3 | 3 | /** |
4 | | - * @file |
| 4 | + * @file |
5 | 5 | * @ingroup upload |
6 | | - * |
| 6 | + * |
7 | 7 | * UploadBase and subclasses are the backend of MediaWiki's file uploads. |
8 | 8 | * The frontends are formed by ApiUpload and SpecialUpload. |
9 | | - * |
| 9 | + * |
10 | 10 | * See also includes/docs/upload.txt |
11 | | - * |
| 11 | + * |
12 | 12 | * @author Brion Vibber |
13 | 13 | * @author Bryan Tong Minh |
14 | 14 | * @author Michael Dale |
15 | 15 | */ |
16 | | - |
| 16 | + |
17 | 17 | abstract class UploadBase { |
18 | 18 | protected $mTempPath; |
19 | 19 | protected $mDesiredDestName, $mDestName, $mRemoveTempFile, $mSourceType; |
— | — | @@ -22,19 +22,15 @@ |
23 | 23 | |
24 | 24 | const SUCCESS = 0; |
25 | 25 | const OK = 0; |
26 | | - const BEFORE_PROCESSING = 1; |
27 | | - const LARGE_FILE_SERVER = 2; |
28 | 26 | const EMPTY_FILE = 3; |
29 | 27 | const MIN_LENGTH_PARTNAME = 4; |
30 | 28 | const ILLEGAL_FILENAME = 5; |
31 | | - const PROTECTED_PAGE = 6; |
32 | 29 | const OVERWRITE_EXISTING_FILE = 7; |
33 | 30 | const FILETYPE_MISSING = 8; |
34 | 31 | const FILETYPE_BADTYPE = 9; |
35 | 32 | const VERIFICATION_ERROR = 10; |
36 | 33 | const UPLOAD_VERIFICATION_ERROR = 11; |
37 | | - const UPLOAD_WARNING = 12; |
38 | | - const INTERNAL_ERROR = 13; |
| 34 | + const HOOK_ABORTED = 11; |
39 | 35 | const MIN_LENGHT_PARTNAME = 14; |
40 | 36 | |
41 | 37 | const SESSION_VERSION = 2; |
— | — | @@ -117,7 +113,7 @@ |
118 | 114 | $this->mFileSize = $fileSize; |
119 | 115 | $this->mRemoveTempFile = $removeTempFile; |
120 | 116 | } |
121 | | - |
| 117 | + |
122 | 118 | /** |
123 | 119 | * Initialize from a WebRequest. Override this in a subclass. |
124 | 120 | */ |
— | — | @@ -136,12 +132,12 @@ |
137 | 133 | public function isEmptyFile(){ |
138 | 134 | return empty( $this->mFileSize ); |
139 | 135 | } |
140 | | - |
141 | | - /* |
142 | | - * getRealPath |
143 | | - * @param string $srcPath the source path |
144 | | - * @returns the real path if it was a virtual url |
145 | | - */ |
| 136 | + |
| 137 | + /** |
| 138 | + * getRealPath |
| 139 | + * @param string $srcPath the source path |
| 140 | + * @returns the real path if it was a virtual url |
| 141 | + */ |
146 | 142 | function getRealPath( $srcPath ){ |
147 | 143 | $repo = RepoGroup::singleton()->getLocalRepo(); |
148 | 144 | if ( $repo->isVirtualUrl( $srcPath ) ) { |
— | — | @@ -160,7 +156,21 @@ |
161 | 157 | */ |
162 | 158 | if( $this->isEmptyFile() ) |
163 | 159 | return array( 'status' => self::EMPTY_FILE ); |
| 160 | + |
| 161 | + /** |
| 162 | + * Look at the contents of the file; if we can recognize the |
| 163 | + * type but it's corrupt or data of the wrong type, we should |
| 164 | + * probably not accept it. |
| 165 | + */ |
| 166 | + $verification = $this->verifyFile(); |
| 167 | + if( $verification !== true ) { |
| 168 | + if( !is_array( $verification ) ) |
| 169 | + $verification = array( $verification ); |
| 170 | + return array( 'status' => self::VERIFICATION_ERROR, |
| 171 | + 'details' => $verification ); |
164 | 172 | |
| 173 | + } |
| 174 | + |
165 | 175 | $nt = $this->getTitle(); |
166 | 176 | if( is_null( $nt ) ) { |
167 | 177 | $result = array( 'status' => $this->mTitleError ); |
— | — | @@ -179,25 +189,11 @@ |
180 | 190 | if( $overwrite !== true ) |
181 | 191 | return array( 'status' => self::OVERWRITE_EXISTING_FILE, 'overwrite' => $overwrite ); |
182 | 192 | |
183 | | - /** |
184 | | - * Look at the contents of the file; if we can recognize the |
185 | | - * type but it's corrupt or data of the wrong type, we should |
186 | | - * probably not accept it. |
187 | | - */ |
188 | | - $verification = $this->verifyFile(); |
189 | | - |
190 | | - if( $verification !== true ) { |
191 | | - if( !is_array( $verification ) ) |
192 | | - $verification = array( $verification ); |
193 | | - return array( 'status' => self::VERIFICATION_ERROR, |
194 | | - 'details' => $verification ); |
195 | | - } |
196 | | - |
197 | 193 | $error = ''; |
198 | 194 | if( !wfRunHooks( 'UploadVerification', |
199 | 195 | array( $this->mDestName, $this->mTempPath, &$error ) ) ) { |
200 | 196 | // This status needs another name... |
201 | | - return array( 'status' => self::UPLOAD_VERIFICATION_ERROR, 'error' => $error ); |
| 197 | + return array( 'status' => self::HOOK_ABORTED, 'error' => $error ); |
202 | 198 | } |
203 | 199 | |
204 | 200 | return array( 'status' => self::OK ); |
— | — | @@ -221,7 +217,7 @@ |
222 | 218 | |
223 | 219 | #check mime type, if desired |
224 | 220 | global $wgVerifyMimeType; |
225 | | - if ( $wgVerifyMimeType ) { |
| 221 | + if ( $wgVerifyMimeType ) { |
226 | 222 | global $wgMimeTypeBlacklist; |
227 | 223 | if ( $this->checkFileExtension( $mime, $wgMimeTypeBlacklist ) ) |
228 | 224 | return array( 'filetype-badmime', $mime ); |
— | — | @@ -262,7 +258,7 @@ |
263 | 259 | |
264 | 260 | /** |
265 | 261 | * Check whether the user can edit, upload and create the image. |
266 | | - * |
| 262 | + * |
267 | 263 | * @param User $user the user to verify the permissions against |
268 | 264 | * @return mixed An array as returned by getUserPermissionsErrors or true |
269 | 265 | * in case the user has proper permissions. |
— | — | @@ -288,7 +284,7 @@ |
289 | 285 | |
290 | 286 | /** |
291 | 287 | * Check for non fatal problems with the file |
292 | | - * |
| 288 | + * |
293 | 289 | * @return array Array of warnings |
294 | 290 | */ |
295 | 291 | public function checkWarnings() { |
— | — | @@ -304,7 +300,10 @@ |
305 | 301 | * but ignore things like ucfirst() and spaces/underscore things |
306 | 302 | */ |
307 | 303 | $comparableName = str_replace( ' ', '_', $this->mDesiredDestName ); |
308 | | - $comparableName = Title::capitalize( $comparableName, NS_FILE ); |
| 304 | + global $wgCapitalLinks, $wgContLang; |
| 305 | + if ( $wgCapitalLinks ) { |
| 306 | + $comparableName = $wgContLang->ucfirst( $comparableName ); |
| 307 | + } |
309 | 308 | if( $this->mDesiredDestName != $filename && $comparableName != $filename ) |
310 | 309 | $warnings['badfilename'] = $filename; |
311 | 310 | |
— | — | @@ -348,9 +347,9 @@ |
349 | 348 | } |
350 | 349 | |
351 | 350 | /** |
352 | | - * Really perform the upload. Stores the file in the local repo, watches |
| 351 | + * Really perform the upload. Stores the file in the local repo, watches |
353 | 352 | * if necessary and runs the UploadComplete hook. |
354 | | - * |
| 353 | + * |
355 | 354 | * @return mixed Status indicating the whether the upload succeeded. |
356 | 355 | */ |
357 | 356 | public function performUpload( $comment, $pageText, $watch, $user ) { |
— | — | @@ -358,13 +357,8 @@ |
359 | 358 | $status = $this->getLocalFile()->upload( $this->mTempPath, $comment, $pageText, |
360 | 359 | File::DELETE_SOURCE, $this->mFileProps, false, $user ); |
361 | 360 | |
362 | | - if( $status->isGood() && $watch ){ |
363 | | - //make sure the watch commit happens inline |
364 | | - $dbw = wfGetDB(DB_MASTER); |
365 | | - $dbw->begin(); |
366 | | - $user->addWatch( $this->getLocalFile()->getTitle() ); |
367 | | - $dbw->commit(); |
368 | | - } |
| 361 | + if( $status->isGood() && $watch ) |
| 362 | + $user->addWatch( $this->getLocalFile()->getTitle() ); |
369 | 363 | |
370 | 364 | if( $status->isGood() ) |
371 | 365 | wfRunHooks( 'UploadComplete', array( &$this ) ); |
— | — | @@ -375,7 +369,7 @@ |
376 | 370 | /** |
377 | 371 | * Returns the title of the file to be uploaded. Sets mTitleError in case |
378 | 372 | * the name was illegal. |
379 | | - * |
| 373 | + * |
380 | 374 | * @return Title The title of the file or null in case the name was illegal |
381 | 375 | */ |
382 | 376 | public function getTitle() { |
— | — | @@ -444,7 +438,7 @@ |
445 | 439 | } |
446 | 440 | |
447 | 441 | /** |
448 | | - * Return the local file and initializes if necessary. |
| 442 | + * Return the local file and initializes if necessary. |
449 | 443 | */ |
450 | 444 | public function getLocalFile() { |
451 | 445 | if( is_null( $this->mLocalFile ) ) { |
— | — | @@ -472,9 +466,9 @@ |
473 | 467 | return $status; |
474 | 468 | } |
475 | 469 | |
476 | | - /** |
| 470 | + /** |
477 | 471 | * Append a file to a stashed file. |
478 | | - * |
| 472 | + * |
479 | 473 | * @param string $srcPath Path to file to append from |
480 | 474 | * @param string $toAppendPath Path to file to append to |
481 | 475 | * @return Status Status |
— | — | @@ -507,7 +501,7 @@ |
508 | 502 | 'mFileSize' => $this->mFileSize, |
509 | 503 | 'mFileProps' => $this->mFileProps, |
510 | 504 | 'version' => self::SESSION_VERSION, |
511 | | - ); |
| 505 | + ); |
512 | 506 | return $key; |
513 | 507 | } |
514 | 508 | |
— | — | @@ -520,15 +514,6 @@ |
521 | 515 | return $key; |
522 | 516 | } |
523 | 517 | |
524 | | - /** |
525 | | - * Remove a temporarily kept file stashed by saveTempUploadedFile(). |
526 | | - * @return success |
527 | | - */ |
528 | | - public function unsaveUploadedFile() { |
529 | | - $repo = RepoGroup::singleton()->getLocalRepo(); |
530 | | - $success = $repo->freeTemp( $this->mTempPath ); |
531 | | - return $success; |
532 | | - } |
533 | 518 | |
534 | 519 | /** |
535 | 520 | * If we've modified the upload file we need to manually remove it |
— | — | @@ -817,9 +802,12 @@ |
818 | 803 | # that does not seem to be worth the pain. |
819 | 804 | # Ask me (Duesentrieb) about it if it's ever needed. |
820 | 805 | $output = array(); |
821 | | - $output = wfShellExec("$command 2>&1", $exitCode); |
| 806 | + if ( wfIsWindows() ) { |
| 807 | + exec( "$command", $output, $exitCode ); |
| 808 | + } else { |
| 809 | + exec( "$command 2>&1", $output, $exitCode ); |
| 810 | + } |
822 | 811 | |
823 | | - |
824 | 812 | # map exit code to AV_xxx constants. |
825 | 813 | $mappedCode = $exitCode; |
826 | 814 | if ( $exitCodeMap ) { |
— | — | @@ -830,7 +818,6 @@ |
831 | 819 | } |
832 | 820 | } |
833 | 821 | |
834 | | - |
835 | 822 | if ( $mappedCode === AV_SCAN_FAILED ) { |
836 | 823 | # scan failed (code was mapped to false by $exitCodeMap) |
837 | 824 | wfDebug( __METHOD__ . ": failed to scan $file (code $exitCode).\n" ); |
— | — | @@ -912,9 +899,9 @@ |
913 | 900 | return true; |
914 | 901 | } |
915 | 902 | |
916 | | - /* Check shared conflicts: if the local file does not exist, but |
917 | | - * wfFindFile finds a file, it exists in a shared repository. |
918 | | - */ |
| 903 | + /* Check shared conflicts: if the local file does not exist, but |
| 904 | + * wfFindFile finds a file, it exists in a shared repository. |
| 905 | + */ |
919 | 906 | $file = wfFindFile( $this->getTitle() ); |
920 | 907 | if ( $file && !$wgUser->isAllowed( 'reupload-shared' ) ) |
921 | 908 | return 'fileexists-shared-forbidden'; |
— | — | @@ -944,13 +931,13 @@ |
945 | 932 | |
946 | 933 | /** |
947 | 934 | * Helper function that does various existence checks for a file. |
948 | | - * The following checks are performed: |
| 935 | + * The following checks are performed: |
949 | 936 | * - The file exists |
950 | 937 | * - Article with the same name as the file exists |
951 | 938 | * - File exists with normalized extension |
952 | 939 | * - The file looks like a thumbnail and the original exists |
953 | | - * |
954 | | - * @param File $file The file to check |
| 940 | + * |
| 941 | + * @param File $file The file to check |
955 | 942 | * @return mixed False if the file does not exists, else an array |
956 | 943 | */ |
957 | 944 | public static function getExistsWarning( $file ) { |
— | — | @@ -959,10 +946,10 @@ |
960 | 947 | |
961 | 948 | if( $file->getTitle()->getArticleID() ) |
962 | 949 | return array( 'warning' => 'page-exists', 'file' => $file ); |
963 | | - |
| 950 | + |
964 | 951 | if ( $file->wasDeleted() && !$file->exists() ) |
965 | | - return array( 'warning' => 'was-deleted', 'file' => $file ); |
966 | | - |
| 952 | + return array( 'warning' => 'was-deleted', 'file' => $file ); |
| 953 | + |
967 | 954 | if( strpos( $file->getName(), '.' ) == false ) { |
968 | 955 | $partname = $file->getName(); |
969 | 956 | $extension = ''; |
— | — | @@ -996,15 +983,15 @@ |
997 | 984 | // File does not exist, but we just don't like the name |
998 | 985 | return array( 'warning' => 'thumb-name', 'file' => $file, 'thumbFile' => $file_thb ); |
999 | 986 | } |
| 987 | + |
1000 | 988 | |
1001 | | - |
1002 | 989 | foreach( self::getFilenamePrefixBlacklist() as $prefix ) { |
1003 | 990 | if ( substr( $partname, 0, strlen( $prefix ) ) == $prefix ) |
1004 | 991 | return array( 'warning' => 'bad-prefix', 'file' => $file, 'prefix' => $prefix ); |
1005 | 992 | } |
| 993 | + |
1006 | 994 | |
1007 | 995 | |
1008 | | - |
1009 | 996 | return false; |
1010 | 997 | } |
1011 | 998 | |
Index: trunk/phase3/includes/Licenses.php |
— | — | @@ -9,46 +9,39 @@ |
10 | 10 | * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later |
11 | 11 | */ |
12 | 12 | |
13 | | -class Licenses { |
14 | | - /**#@+ |
15 | | - * @private |
16 | | - */ |
| 13 | +class Licenses extends HTMLFormField { |
17 | 14 | /** |
18 | 15 | * @var string |
19 | 16 | */ |
20 | | - var $msg; |
| 17 | + protected $msg; |
21 | 18 | |
22 | 19 | /** |
23 | 20 | * @var array |
24 | 21 | */ |
25 | | - var $licenses = array(); |
| 22 | + protected $licenses = array(); |
26 | 23 | |
27 | 24 | /** |
28 | 25 | * @var string |
29 | 26 | */ |
30 | | - var $html; |
| 27 | + protected $html; |
31 | 28 | /**#@-*/ |
32 | 29 | |
33 | 30 | /** |
34 | 31 | * Constructor |
35 | | - * |
36 | | - * @param $str String: the string to build the licenses member from, will use |
37 | | - * wfMsgForContent( 'licenses' ) if null (default: null) |
38 | 32 | */ |
39 | | - function __construct( $str = null ) { |
40 | | - // PHP sucks, this should be possible in the constructor |
41 | | - $this->msg = is_null( $str ) ? wfMsgForContent( 'licenses' ) : $str; |
42 | | - $this->html = ''; |
| 33 | + public function __construct( $params ) { |
| 34 | + parent::__construct( $params ); |
| 35 | + |
| 36 | + $this->msg = empty( $params['licenses'] ) ? wfMsgForContent( 'licenses' ) : $params['licenses']; |
| 37 | + $this->selected = null; |
43 | 38 | |
44 | 39 | $this->makeLicenses(); |
45 | | - $tmp = $this->getLicenses(); |
46 | | - $this->makeHtml( $tmp ); |
47 | 40 | } |
48 | 41 | |
49 | 42 | /**#@+ |
50 | 43 | * @private |
51 | 44 | */ |
52 | | - function makeLicenses() { |
| 45 | + protected function makeLicenses() { |
53 | 46 | $levels = array(); |
54 | 47 | $lines = explode( "\n", $this->msg ); |
55 | 48 | |
— | — | @@ -75,18 +68,17 @@ |
76 | 69 | } |
77 | 70 | } |
78 | 71 | |
79 | | - function trimStars( $str ) { |
| 72 | + protected static function trimStars( $str ) { |
80 | 73 | $i = $count = 0; |
81 | 74 | |
82 | | - wfSuppressWarnings(); |
83 | | - while ($str[$i++] == '*') |
84 | | - ++$count; |
85 | | - wfRestoreWarnings(); |
86 | | - |
87 | | - return array( $count, ltrim( $str, '* ' ) ); |
| 75 | + $length = strlen( $str ); |
| 76 | + for ( $i = 0; $i < $length; $i++ ) { |
| 77 | + if ( $str[$i] != '*' ) |
| 78 | + return array( $i, ltrim( $str, '* ' ) ); |
| 79 | + } |
88 | 80 | } |
89 | 81 | |
90 | | - function stackItem( &$list, $path, $item ) { |
| 82 | + protected function stackItem( &$list, $path, $item ) { |
91 | 83 | $position =& $list; |
92 | 84 | if ( $path ) |
93 | 85 | foreach( $path as $key ) |
— | — | @@ -94,13 +86,12 @@ |
95 | 87 | $position[] = $item; |
96 | 88 | } |
97 | 89 | |
98 | | - function makeHtml( &$tagset, $depth = 0 ) { |
| 90 | + protected function makeHtml( &$tagset, $depth = 0 ) { |
99 | 91 | foreach ( $tagset as $key => $val ) |
100 | 92 | if ( is_array( $val ) ) { |
101 | 93 | $this->html .= $this->outputOption( |
102 | | - $this->msg( $key ), |
| 94 | + $this->msg( $key ), '', |
103 | 95 | array( |
104 | | - 'value' => '', |
105 | 96 | 'disabled' => 'disabled', |
106 | 97 | 'style' => 'color: GrayText', // for MSIE |
107 | 98 | ), |
— | — | @@ -109,22 +100,22 @@ |
110 | 101 | $this->makeHtml( $val, $depth + 1 ); |
111 | 102 | } else { |
112 | 103 | $this->html .= $this->outputOption( |
113 | | - $this->msg( $val->text ), |
114 | | - array( |
115 | | - 'value' => $val->template, |
116 | | - 'title' => '{{' . $val->template . '}}' |
117 | | - ), |
| 104 | + $this->msg( $val->text ), $val->template, |
| 105 | + array( 'title' => '{{' . $val->template . '}}' ), |
118 | 106 | $depth |
119 | 107 | ); |
120 | 108 | } |
121 | 109 | } |
122 | 110 | |
123 | | - function outputOption( $val, $attribs = null, $depth ) { |
124 | | - $val = str_repeat( /*   */ "\xc2\xa0", $depth * 2 ) . $val; |
| 111 | + protected function outputOption( $text, $value, $attribs = null, $depth = 0 ) { |
| 112 | + $attribs['value'] = $value; |
| 113 | + if ( $value === $this->selected ) |
| 114 | + $attribs['selected'] = 'selected'; |
| 115 | + $val = str_repeat( /*   */ "\xc2\xa0", $depth * 2 ) . $text; |
125 | 116 | return str_repeat( "\t", $depth ) . Xml::element( 'option', $attribs, $val ) . "\n"; |
126 | 117 | } |
127 | 118 | |
128 | | - function msg( $str ) { |
| 119 | + protected function msg( $str ) { |
129 | 120 | $out = wfMsg( $str ); |
130 | 121 | return wfEmptyMsg( $str, $out ) ? $str : $out; |
131 | 122 | } |
— | — | @@ -136,14 +127,29 @@ |
137 | 128 | * |
138 | 129 | * @return array |
139 | 130 | */ |
140 | | - function getLicenses() { return $this->licenses; } |
| 131 | + public function getLicenses() { return $this->licenses; } |
141 | 132 | |
142 | 133 | /** |
143 | 134 | * Accessor for $this->html |
144 | 135 | * |
145 | 136 | * @return string |
146 | 137 | */ |
147 | | - function getHtml() { return $this->html; } |
| 138 | + public function getInputHTML( $value ) { |
| 139 | + $this->selected = $value; |
| 140 | + |
| 141 | + $this->html = $this->outputOption( wfMsg( 'nolicense' ), '', |
| 142 | + (bool)$this->selected ? null : array( 'selected' => 'selected' ) ); |
| 143 | + $this->makeHtml( $this->getLicenses() ); |
| 144 | + |
| 145 | + $attribs = array( |
| 146 | + 'name' => $this->mName, |
| 147 | + 'id' => $this->mID |
| 148 | + ); |
| 149 | + if ( !empty( $this->mParams['disabled'] ) ) |
| 150 | + $attibs['disabled'] = 'disabled'; |
| 151 | + |
| 152 | + return Html::rawElement( 'select', $attribs, $this->html ); |
| 153 | + } |
148 | 154 | } |
149 | 155 | |
150 | 156 | /** |
Index: trunk/phase3/includes/Setup.php |
— | — | @@ -306,9 +306,9 @@ |
307 | 307 | $wgPostCommitUpdateList = array(); |
308 | 308 | |
309 | 309 | if ( $wgAjaxWatch ) $wgAjaxExportList[] = 'wfAjaxWatch'; |
310 | | -if ( $wgAjaxUploadDestCheck ) $wgAjaxExportList[] = 'UploadForm::ajaxGetExistsWarning'; |
| 310 | +if ( $wgAjaxUploadDestCheck ) $wgAjaxExportList[] = 'SpecialUpload::ajaxGetExistsWarning'; |
311 | 311 | if( $wgAjaxLicensePreview ) |
312 | | - $wgAjaxExportList[] = 'UploadForm::ajaxGetLicensePreview'; |
| 312 | + $wgAjaxExportList[] = 'SpecialUpload::ajaxGetLicensePreview'; |
313 | 313 | |
314 | 314 | # Placeholders in case of DB error |
315 | 315 | $wgTitle = null; |
Index: trunk/phase3/includes/api/ApiUpload.php |
— | — | @@ -230,7 +230,7 @@ |
231 | 231 | $this->dieUsage( 'This file did not pass file verification', 'verification-error', |
232 | 232 | 0, array( 'details' => $verification['details'] ) ); |
233 | 233 | break; |
234 | | - case UploadBase::UPLOAD_VERIFICATION_ERROR: |
| 234 | + case UploadBase::HOOK_ABORTED: |
235 | 235 | $this->dieUsage( "The modification you tried to make was aborted by an extension hook", |
236 | 236 | 'hookaborted', 0, array( 'error' => $verification['error'] ) ); |
237 | 237 | break; |
Index: trunk/phase3/includes/AutoLoader.php |
— | — | @@ -550,6 +550,7 @@ |
551 | 551 | 'SpecialSearch' => 'includes/specials/SpecialSearch.php', |
552 | 552 | 'SpecialStatistics' => 'includes/specials/SpecialStatistics.php', |
553 | 553 | 'SpecialTags' => 'includes/specials/SpecialTags.php', |
| 554 | + 'SpecialUpload' => 'includes/specials/SpecialUpload.php', |
554 | 555 | 'SpecialVersion' => 'includes/specials/SpecialVersion.php', |
555 | 556 | 'SpecialWhatlinkshere' => 'includes/specials/SpecialWhatlinkshere.php', |
556 | 557 | 'UncategorizedCategoriesPage' => 'includes/specials/SpecialUncategorizedcategories.php', |
Index: trunk/phase3/includes/specials/SpecialUpload.php |
— | — | @@ -2,82 +2,80 @@ |
3 | 3 | /** |
4 | 4 | * @file |
5 | 5 | * @ingroup SpecialPage |
| 6 | + * @ingroup Upload |
| 7 | + * |
| 8 | + * Form for handling uploads and special page. |
| 9 | + * |
6 | 10 | */ |
7 | 11 | |
8 | | -/** |
9 | | - * implements Special:Upload |
10 | | - * @ingroup SpecialPage |
11 | | - */ |
12 | | -class UploadForm extends SpecialPage { |
13 | | - /**#@+ |
14 | | - * @access private |
15 | | - */ |
16 | | - var $mComment, $mLicense, $mIgnoreWarning; |
17 | | - var $mCopyrightStatus, $mCopyrightSource, $mReUpload, $mAction, $mUploadClicked; |
18 | | - var $mDestWarningAck; |
19 | | - var $mLocalFile; |
20 | | - |
21 | | - var $mUpload; // Instance of UploadBase or derivative |
22 | | - |
23 | | - # Placeholders for text injection by hooks (must be HTML) |
24 | | - # extensions should take care to _append_ to the present value |
25 | | - var $uploadFormTextTop; |
26 | | - var $uploadFormTextAfterSummary; |
27 | | - var $mTokenOk = false; |
28 | | - var $mForReUpload = false; |
29 | | - /**#@-*/ |
30 | | - |
| 12 | +class SpecialUpload extends SpecialPage { |
31 | 13 | /** |
32 | 14 | * Constructor : initialise object |
33 | 15 | * Get data POSTed through the form and assign them to the object |
34 | | - * @param $request Data posted. |
| 16 | + * @param WebRequest $request Data posted. |
35 | 17 | */ |
36 | | - function __construct( $request = null ) { |
| 18 | + public function __construct( $request = null ) { |
| 19 | + global $wgRequest; |
| 20 | + |
37 | 21 | parent::__construct( 'Upload', 'upload' ); |
38 | | - $this->mRequest = $request; |
| 22 | + |
| 23 | + $this->loadRequest( is_null( $request ) ? $wgRequest : $request ); |
39 | 24 | } |
| 25 | + |
| 26 | + /** Misc variables **/ |
| 27 | + protected $mRequest; // The WebRequest or FauxRequest this form is supposed to handle |
| 28 | + protected $mSourceType; |
| 29 | + protected $mUpload; |
| 30 | + protected $mLocalFile; |
| 31 | + protected $mUploadClicked; |
| 32 | + |
| 33 | + /** User input variables from the "description" section **/ |
| 34 | + protected $mDesiredDestName; // The requested target file name |
| 35 | + protected $mComment; |
| 36 | + protected $mLicense; |
| 37 | + |
| 38 | + /** User input variables from the root section **/ |
| 39 | + protected $mIgnoreWarning; |
| 40 | + protected $mWatchThis; |
| 41 | + protected $mCopyrightStatus; |
| 42 | + protected $mCopyrightSource; |
| 43 | + |
| 44 | + /** Hidden variables **/ |
| 45 | + protected $mForReUpload; // The user followed an "overwrite this file" link |
| 46 | + protected $mCancelUpload; // The user clicked "Cancel and return to upload form" button |
| 47 | + protected $mTokenOk; |
| 48 | + |
| 49 | + /** |
| 50 | + * Initialize instance variables from request and create an Upload handler |
| 51 | + * |
| 52 | + * @param WebRequest $request The request to extract variables from |
| 53 | + */ |
| 54 | + protected function loadRequest( $request ) { |
| 55 | + global $wgUser; |
40 | 56 | |
41 | | - protected function initForm() { |
42 | | - global $wgRequest, $wgUser; |
43 | | - |
44 | | - if ( is_null( $this->mRequest ) ) { |
45 | | - $request = $wgRequest; |
46 | | - } else { |
47 | | - $request = $this->mRequest; |
48 | | - } |
| 57 | + $this->mRequest = $request; |
| 58 | + $this->mSourceType = $request->getVal( 'wpSourceType', 'file' ); |
| 59 | + $this->mUpload = UploadBase::createFromRequest( $request ); |
| 60 | + $this->mUploadClicked = $request->getCheck( 'wpUpload' ) && $request->wasPosted(); |
| 61 | + |
49 | 62 | // Guess the desired name from the filename if not provided |
50 | 63 | $this->mDesiredDestName = $request->getText( 'wpDestFile' ); |
51 | 64 | if( !$this->mDesiredDestName ) |
52 | 65 | $this->mDesiredDestName = $request->getText( 'wpUploadFile' ); |
53 | | - |
54 | | - $this->mForReUpload = $request->getBool( 'wpForReUpload' ); // updating a file |
55 | | - $this->mIgnoreWarning = $request->getCheck( 'wpIgnoreWarning' ); |
56 | 66 | $this->mComment = $request->getText( 'wpUploadDescription' ); |
57 | | - |
58 | | - if( !$request->wasPosted() ) { |
59 | | - # GET requests just give the main form; no data except destination |
60 | | - # filename and description |
61 | | - return; |
62 | | - } |
63 | | - |
64 | | - # Placeholders for text injection by hooks (empty per default) |
65 | | - $this->uploadFormTextTop = ""; |
66 | | - $this->uploadFormTextAfterSummary = ""; |
67 | | - |
68 | | - $this->mUploadClicked = $request->getCheck( 'wpUpload' ); |
69 | | - |
70 | | - $this->mLicense = $request->getText( 'wpLicense' ); |
| 67 | + $this->mLicense = $request->getText( 'wpLicense' ); |
| 68 | + |
| 69 | + |
| 70 | + $this->mIgnoreWarning = $request->getCheck( 'wpIgnoreWarning' ); |
| 71 | + $this->mWatchthis = $request->getBool( 'wpWatchthis' ); |
71 | 72 | $this->mCopyrightStatus = $request->getText( 'wpUploadCopyStatus' ); |
72 | 73 | $this->mCopyrightSource = $request->getText( 'wpUploadSource' ); |
73 | | - $this->mWatchthis = $request->getBool( 'wpWatchthis' ); |
74 | | - $this->mSourceType = $request->getVal( 'wpSourceType', 'file' ); |
75 | | - $this->mDestWarningAck = $request->getText( 'wpDestFileWarningAck' ); |
76 | 74 | |
77 | | - $this->mReUpload = $request->getCheck( 'wpReUpload' ); // retrying upload |
78 | 75 | |
79 | | - $this->mAction = $request->getVal( 'action' ); |
80 | | - $this->mUpload = UploadBase::createFromRequest( $request ); |
81 | | - |
| 76 | + $this->mForReUpload = $request->getBool( 'wpForReUpload' ); // updating a file |
| 77 | + $this->mCancelUpload = $request->getCheck( 'wpCancelUpload' ) |
| 78 | + || $request->getCheck( 'wpReUpload' ); // b/w compat |
| 79 | + |
82 | 80 | // If it was posted check for the token (no remote POST'ing with user credentials) |
83 | 81 | $token = $request->getVal( 'wpEditToken' ); |
84 | 82 | if( $this->mSourceType == 'file' && $token == null ) { |
— | — | @@ -89,23 +87,28 @@ |
90 | 88 | $this->mTokenOk = $wgUser->matchEditToken( $token ); |
91 | 89 | } |
92 | 90 | } |
93 | | - |
| 91 | + |
| 92 | + /** |
| 93 | + * This page can be shown if uploading is enabled. |
| 94 | + * Handle permission checking elsewhere in order to be able to show |
| 95 | + * custom error messages. |
| 96 | + * |
| 97 | + * @param User $user |
| 98 | + * @return bool |
| 99 | + */ |
94 | 100 | public function userCanExecute( $user ) { |
95 | 101 | return UploadBase::isEnabled() && parent::userCanExecute( $user ); |
96 | 102 | } |
97 | | - |
| 103 | + |
98 | 104 | /** |
99 | | - * Start doing stuff |
100 | | - * @access public |
| 105 | + * Special page entry point |
101 | 106 | */ |
102 | | - function execute( $par ) { |
| 107 | + public function execute() { |
103 | 108 | global $wgUser, $wgOut, $wgRequest; |
104 | | - |
| 109 | + |
105 | 110 | $this->setHeaders(); |
106 | 111 | $this->outputHeader(); |
107 | | - |
108 | | - $this->initForm(); |
109 | | - |
| 112 | + |
110 | 113 | # Check uploading enabled |
111 | 114 | if( !UploadBase::isEnabled() ) { |
112 | 115 | $wgOut->showErrorPage( 'uploaddisabled', 'uploaddisabledtext' ); |
— | — | @@ -131,91 +134,302 @@ |
132 | 135 | return; |
133 | 136 | } |
134 | 137 | |
| 138 | + # Check whether we actually want to allow changing stuff |
135 | 139 | if( wfReadOnly() ) { |
136 | 140 | $wgOut->readOnlyPage(); |
137 | 141 | return; |
138 | 142 | } |
139 | | - //check token if uploading or reUploading |
140 | | - if( !$this->mTokenOk && !$this->mReUpload && ($this->mUpload && ( |
141 | | - 'submit' == $this->mAction || $this->mUploadClicked ) ) ) |
142 | | - { |
143 | | - $this->mainUploadForm ( wfMsgExt( 'session_fail_preview', 'parseinline' ) ); |
144 | | - return ; |
145 | | - } |
146 | | - |
147 | | - |
148 | | - if( $this->mReUpload && $this->mUpload) { |
149 | | - // User choose to cancel upload |
150 | | - if( !$this->mUpload->unsaveUploadedFile() ) { |
| 143 | + |
| 144 | + # Unsave the temporary file in case this was a cancelled upload |
| 145 | + if ( $this->mCancelUpload ) { |
| 146 | + if ( !$this->unsaveUploadedFile() ) |
| 147 | + # Something went wrong, so unsaveUploadedFile showed a warning |
151 | 148 | return; |
152 | | - } |
153 | | - # Because it is probably checked and shouldn't be |
154 | | - $this->mIgnoreWarning = false; |
155 | | - $this->mainUploadForm(); |
156 | | - } elseif( $this->mUpload && ( |
157 | | - 'submit' == $this->mAction || |
158 | | - $this->mUploadClicked |
159 | | - ) ) { |
| 149 | + } |
| 150 | + |
| 151 | + # Process upload or show a form |
| 152 | + if ( $this->mTokenOk && !$this->mCancelUpload |
| 153 | + && ( $this->mUpload && $this->mUploadClicked ) ) { |
160 | 154 | $this->processUpload(); |
161 | 155 | } else { |
162 | | - $this->mainUploadForm(); |
| 156 | + $this->showUploadForm( $this->getUploadForm() ); |
163 | 157 | } |
164 | | - |
165 | | - if( $this->mUpload ) |
| 158 | + |
| 159 | + # Cleanup |
| 160 | + if ( $this->mUpload ) |
166 | 161 | $this->mUpload->cleanupTempFile(); |
167 | 162 | } |
| 163 | + |
| 164 | + /** |
| 165 | + * Show the main upload form and optionally add the session key to the |
| 166 | + * output. This hides the source selection. |
| 167 | + * |
| 168 | + * @param string $message HTML message to be shown at top of form |
| 169 | + * @param string $sessionKey Session key of the stashed upload |
| 170 | + */ |
| 171 | + protected function showUploadForm( $form ) { |
| 172 | + # Add links if file was previously deleted |
| 173 | + if ( !$this->mDesiredDestName ) |
| 174 | + $this->showViewDeletedLinks(); |
| 175 | + |
| 176 | + $form->show(); |
| 177 | + } |
| 178 | + |
| 179 | + /** |
| 180 | + * Get an UploadForm instance with title and text properly set. |
| 181 | + * |
| 182 | + * @param string $message HTML string to add to the form |
| 183 | + * @param string $sessionKey Session key in case this is a stashed upload |
| 184 | + * @return UploadForm |
| 185 | + */ |
| 186 | + protected function getUploadForm( $message = '', $sessionKey = '' ) { |
| 187 | + # Initialize form |
| 188 | + $form = new UploadForm( $this->watchCheck(), $this->mForReUpload, $sessionKey ); |
| 189 | + $form->setTitle( $this->getTitle() ); |
| 190 | + |
| 191 | + # Check the token, but only if necessary |
| 192 | + if( !$this->mTokenOk && !$this->mCancelUpload |
| 193 | + && ( $this->mUpload && $this->mUploadClicked ) ) |
| 194 | + $form->addPreText( wfMsgExt( 'session_fail_preview', 'parseinline' ) ); |
168 | 195 | |
| 196 | + # Add text to form |
| 197 | + $form->addPreText( '<div id="uploadtext">' . wfMsgExt( 'uploadtext', 'parse' ) . '</div>'); |
| 198 | + # Add upload error message |
| 199 | + $form->addPreText( $message ); |
| 200 | + |
| 201 | + return $form; |
| 202 | + } |
| 203 | + |
169 | 204 | /** |
170 | | - * Do the upload |
| 205 | + * TODO: DOCUMENT |
| 206 | + */ |
| 207 | + protected function showViewDeletedLinks() { |
| 208 | + global $wgOut, $wgUser; |
| 209 | + |
| 210 | + $title = Title::makeTitleSafe( NS_FILE, $this->mDesiredDestName ); |
| 211 | + // Show a subtitle link to deleted revisions (to sysops et al only) |
| 212 | + if( $title instanceof Title && ( $count = $title->isDeleted() ) > 0 && $wgUser->isAllowed( 'deletedhistory' ) ) { |
| 213 | + $link = wfMsgExt( |
| 214 | + $wgUser->isAllowed( 'delete' ) ? 'thisisdeleted' : 'viewdeleted', |
| 215 | + array( 'parse', 'replaceafter' ), |
| 216 | + $wgUser->getSkin()->linkKnown( |
| 217 | + SpecialPage::getTitleFor( 'Undelete', $title->getPrefixedText() ), |
| 218 | + wfMsgExt( 'restorelink', array( 'parsemag', 'escape' ), $count ) |
| 219 | + ) |
| 220 | + ); |
| 221 | + $wgOut->addHTML( "<div id=\"contentSub2\">{$link}</div>" ); |
| 222 | + } |
| 223 | + |
| 224 | + // Show the relevant lines from deletion log (for still deleted files only) |
| 225 | + if( $title instanceof Title && $title->isDeletedQuick() && !$title->exists() ) { |
| 226 | + $this->showDeletionLog( $wgOut, $title->getPrefixedText() ); |
| 227 | + } |
| 228 | + } |
| 229 | + |
| 230 | + /** |
| 231 | + * Stashes the upload and shows the main upload form. |
| 232 | + * |
| 233 | + * Note: only errors that can be handled by changing the name or |
| 234 | + * description should be redirected here. It should be assumed that the |
| 235 | + * file itself is sane and has passed UploadBase::verifyFile. This |
| 236 | + * essentially means that UploadBase::VERIFICATION_ERROR and |
| 237 | + * UploadBase::EMPTY_FILE should not be passed here. |
| 238 | + * |
| 239 | + * @param string $message HTML message to be passed to mainUploadForm |
| 240 | + */ |
| 241 | + protected function recoverableUploadError( $message ) { |
| 242 | + $sessionKey = $this->mUpload->stashSession(); |
| 243 | + $message = '<h2>' . wfMsgHtml( 'uploadwarning' ) . "</h2>\n" . |
| 244 | + '<div class="error">' . $message . "</div>\n"; |
| 245 | + $this->showUploadForm( $this->getUploadForm( $message, $sessionKey ) ); |
| 246 | + } |
| 247 | + /** |
| 248 | + * Stashes the upload, shows the main form, but adds an "continue anyway button" |
| 249 | + * |
| 250 | + * @param array $warnings |
| 251 | + */ |
| 252 | + protected function uploadWarning( $warnings ) { |
| 253 | + global $wgUser; |
| 254 | + |
| 255 | + $sessionKey = $this->mUpload->stashSession(); |
| 256 | + |
| 257 | + $sk = $wgUser->getSkin(); |
| 258 | + |
| 259 | + $warningHtml = '<h2>' . wfMsgHtml( 'uploadwarning' ) . "</h2>\n" |
| 260 | + . '<ul class="warning">'; |
| 261 | + foreach( $warnings as $warning => $args ) { |
| 262 | + $msg = ''; |
| 263 | + if( $warning == 'exists' ) { |
| 264 | + $msg = self::getExistsWarning( $args ); |
| 265 | + } elseif( $warning == 'duplicate' ) { |
| 266 | + $msg = self::getDupeWarning( $args ); |
| 267 | + } elseif( $warning == 'duplicate-archive' ) { |
| 268 | + $msg = "\t<li>" . wfMsgExt( 'file-deleted-duplicate', 'parseinline', |
| 269 | + array( Title::makeTitle( NS_FILE, $args )->getPrefixedText() ) ) |
| 270 | + . "</li>\n"; |
| 271 | + } else { |
| 272 | + if ( is_bool( $args ) ) |
| 273 | + $args = array(); |
| 274 | + elseif ( !is_array( $args ) ) |
| 275 | + $args = array( $args ); |
| 276 | + $msg = "\t<li>" . wfMsgExt( $warning, 'parseinline', $args ) . "</li>\n"; |
| 277 | + } |
| 278 | + $warningHtml .= $msg; |
| 279 | + } |
| 280 | + |
| 281 | + $form = $this->getUploadForm( $warningHtml, $sessionKey ); |
| 282 | + $form->setSubmitText( wfMsg( 'ignorewarning' ) ); |
| 283 | + $form->addButton( 'wpCancelUpload', wfMsg( 'reuploaddesc' ) ); |
| 284 | + |
| 285 | + $this->showUploadForm( $form ); |
| 286 | + } |
| 287 | + |
| 288 | + /** |
| 289 | + * Show the upload form with error message, but do not stash the file. |
| 290 | + * |
| 291 | + * @param string $message |
| 292 | + */ |
| 293 | + protected function uploadError( $message ) { |
| 294 | + $message = '<h2>' . wfMsgHtml( 'uploadwarning' ) . "</h2>\n" . |
| 295 | + '<div class="error">' . $message . "</div>\n"; |
| 296 | + $this->showUploadForm( $this->getUploadForm( $message ) ); |
| 297 | + } |
| 298 | + |
| 299 | + /** |
| 300 | + * Do the upload. |
171 | 301 | * Checks are made in SpecialUpload::execute() |
| 302 | + */ |
| 303 | + protected function processUpload() { |
| 304 | + global $wgUser, $wgOut; |
| 305 | + |
| 306 | + // Verify permissions |
| 307 | + $permErrors = $this->mUpload->verifyPermissions( $wgUser ); |
| 308 | + if( $permErrors !== true ) |
| 309 | + return $wgOut->showPermissionsErrorPage( $permErrors ); |
| 310 | + |
| 311 | + // Fetch the file if required |
| 312 | + $status = $this->mUpload->fetchFile(); |
| 313 | + if( !$status->isOK() ) |
| 314 | + return $this->mainUploadForm( $wgOut->parse( $status->getWikiText() ) ); |
| 315 | + |
| 316 | + // Upload verification |
| 317 | + $details = $this->mUpload->verifyUpload(); |
| 318 | + if ( $details['status'] != UploadBase::OK ) |
| 319 | + return $this->processVerificationError( $details ); |
| 320 | + |
| 321 | + $this->mLocalFile = $this->mUpload->getLocalFile(); |
| 322 | + |
| 323 | + // Check warnings if necessary |
| 324 | + if( !$this->mIgnoreWarning ) { |
| 325 | + $warnings = $this->mUpload->checkWarnings(); |
| 326 | + if( count( $warnings ) ) |
| 327 | + return $this->uploadWarning( $warnings ); |
| 328 | + } |
| 329 | + |
| 330 | + // Get the page text if this is not a reupload |
| 331 | + if( !$this->mForReUpload ) { |
| 332 | + $pageText = self::getInitialPageText( $this->mComment, $this->mLicense, |
| 333 | + $this->mCopyrightStatus, $this->mCopyrightSource ); |
| 334 | + } else { |
| 335 | + $pageText = false; |
| 336 | + } |
| 337 | + $status = $this->mUpload->performUpload( $this->mComment, $pageText, $this->mWatchthis, $wgUser ); |
| 338 | + if ( !$status->isGood() ) |
| 339 | + return $this->uploadError( $wgOut->parse( $status->getWikiText() ) ); |
| 340 | + |
| 341 | + // Success, redirect to description page |
| 342 | + wfRunHooks( 'SpecialUploadComplete', array( &$this ) ); |
| 343 | + $wgOut->redirect( $this->mLocalFile->getTitle()->getFullURL() ); |
| 344 | + |
| 345 | + } |
| 346 | + |
| 347 | + /** |
| 348 | + * Get the initial image page text based on a comment and optional file status information |
| 349 | + */ |
| 350 | + public static function getInitialPageText( $comment = '', $license = '', $copyStatus = '', $source = '' ) { |
| 351 | + global $wgUseCopyrightUpload; |
| 352 | + if ( $wgUseCopyrightUpload ) { |
| 353 | + $licensetxt = ''; |
| 354 | + if ( $license != '' ) { |
| 355 | + $licensetxt = '== ' . wfMsgForContent( 'license-header' ) . " ==\n" . '{{' . $license . '}}' . "\n"; |
| 356 | + } |
| 357 | + $pageText = '== ' . wfMsgForContent ( 'filedesc' ) . " ==\n" . $comment . "\n" . |
| 358 | + '== ' . wfMsgForContent ( 'filestatus' ) . " ==\n" . $copyStatus . "\n" . |
| 359 | + "$licensetxt" . |
| 360 | + '== ' . wfMsgForContent ( 'filesource' ) . " ==\n" . $source ; |
| 361 | + } else { |
| 362 | + if ( $license != '' ) { |
| 363 | + $filedesc = $comment == '' ? '' : '== ' . wfMsgForContent ( 'filedesc' ) . " ==\n" . $comment . "\n"; |
| 364 | + $pageText = $filedesc . |
| 365 | + '== ' . wfMsgForContent ( 'license-header' ) . " ==\n" . '{{' . $license . '}}' . "\n"; |
| 366 | + } else { |
| 367 | + $pageText = $comment; |
| 368 | + } |
| 369 | + } |
| 370 | + return $pageText; |
| 371 | + } |
| 372 | + |
| 373 | + /** |
| 374 | + * See if we should check the 'watch this page' checkbox on the form |
| 375 | + * based on the user's preferences and whether we're being asked |
| 376 | + * to create a new file or update an existing one. |
172 | 377 | * |
173 | | - * FIXME this should really use the standard Status class (instead of associative array) |
174 | | - * FIXME would be nice if we refactored this into the upload api. |
175 | | - * (the special upload page is not the only response point that needs clean localized error msgs) |
| 378 | + * In the case where 'watch edits' is off but 'watch creations' is on, |
| 379 | + * we'll leave the box unchecked. |
176 | 380 | * |
177 | | - * @access private |
| 381 | + * Note that the page target can be changed *on the form*, so our check |
| 382 | + * state can get out of sync. |
178 | 383 | */ |
179 | | - function processUpload() { |
180 | | - global $wgOut, $wgFileExtensions, $wgLang; |
181 | | - $details = $this->internalProcessUpload(); |
| 384 | + protected function watchCheck() { |
| 385 | + global $wgUser; |
| 386 | + if( $wgUser->getOption( 'watchdefault' ) ) { |
| 387 | + // Watch all edits! |
| 388 | + return true; |
| 389 | + } |
| 390 | + |
| 391 | + $local = wfLocalFile( $this->mDesiredDestName ); |
| 392 | + if( $local && $local->exists() ) { |
| 393 | + // We're uploading a new version of an existing file. |
| 394 | + // No creation, so don't watch it if we're not already. |
| 395 | + return $local->getTitle()->userIsWatching(); |
| 396 | + } else { |
| 397 | + // New page should get watched if that's our option. |
| 398 | + return $wgUser->getOption( 'watchcreations' ); |
| 399 | + } |
| 400 | + } |
| 401 | + |
| 402 | + |
| 403 | + /** |
| 404 | + * Provides output to the user for a result of UploadBase::verifyUpload |
| 405 | + * |
| 406 | + * @param array $details Result of UploadBase::verifyUpload |
| 407 | + */ |
| 408 | + protected function processVerificationError( $details ) { |
| 409 | + global $wgFileExtensions; |
| 410 | + |
182 | 411 | switch( $details['status'] ) { |
183 | | - case UploadBase::SUCCESS: |
184 | | - $wgOut->redirect( $this->mLocalFile->getTitle()->getFullURL() ); |
185 | | - break; |
186 | 412 | |
187 | | - case UploadBase::BEFORE_PROCESSING: |
188 | | - $this->uploadError( $details['error'] ); |
189 | | - break; |
190 | | - case UploadBase::LARGE_FILE_SERVER: |
191 | | - $this->mainUploadForm( wfMsgHtml( 'largefileserver' ) ); |
192 | | - break; |
193 | | - |
194 | | - case UploadBase::EMPTY_FILE: |
195 | | - $this->mainUploadForm( wfMsgHtml( 'emptyfile' ) ); |
196 | | - break; |
197 | | - |
| 413 | + /** Statuses that only require name changing **/ |
198 | 414 | case UploadBase::MIN_LENGTH_PARTNAME: |
199 | | - $this->mainUploadForm( wfMsgHtml( 'minlength1' ) ); |
| 415 | + $this->recoverableUploadError( wfMsgHtml( 'minlength1' ) ); |
200 | 416 | break; |
201 | | - |
202 | 417 | case UploadBase::ILLEGAL_FILENAME: |
203 | | - $this->uploadError( wfMsgExt( 'illegalfilename', |
| 418 | + $this->recoverableUploadError( wfMsgExt( 'illegalfilename', |
204 | 419 | 'parseinline', $details['filtered'] ) ); |
205 | 420 | break; |
206 | | - |
207 | | - case UploadBase::PROTECTED_PAGE: |
208 | | - $wgOut->showPermissionsErrorPage( $details['permissionserrors'] ); |
209 | | - break; |
210 | | - |
211 | 421 | case UploadBase::OVERWRITE_EXISTING_FILE: |
212 | | - $this->uploadError( wfMsgExt( $details['overwrite'], |
| 422 | + $this->recoverableUploadError( wfMsgExt( $details['overwrite'], |
213 | 423 | 'parseinline' ) ); |
214 | 424 | break; |
215 | | - |
216 | 425 | case UploadBase::FILETYPE_MISSING: |
217 | | - $this->uploadError( wfMsgExt( 'filetype-missing', array ( 'parseinline' ) ) ); |
| 426 | + $this->recoverableUploadError( wfMsgExt( 'filetype-missing', |
| 427 | + 'parseinline' ) ); |
218 | 428 | break; |
219 | 429 | |
| 430 | + /** Statuses that require reuploading **/ |
| 431 | + case UploadBase::EMPTY_FILE: |
| 432 | + $this->mainUploadForm( wfMsgHtml( 'emptyfile' ) ); |
| 433 | + break; |
220 | 434 | case UploadBase::FILETYPE_BADTYPE: |
221 | 435 | $finalExt = $details['finalExt']; |
222 | 436 | $this->uploadError( |
— | — | @@ -230,103 +444,40 @@ |
231 | 445 | ) |
232 | 446 | ); |
233 | 447 | break; |
234 | | - |
235 | 448 | case UploadBase::VERIFICATION_ERROR: |
236 | 449 | unset( $details['status'] ); |
237 | 450 | $code = array_shift( $details['details'] ); |
238 | 451 | $this->uploadError( wfMsgExt( $code, 'parseinline', $details['details'] ) ); |
239 | 452 | break; |
240 | | - |
241 | | - case UploadBase::UPLOAD_VERIFICATION_ERROR: |
| 453 | + case UploadBase::HOOK_ABORTED: |
242 | 454 | $error = $details['error']; |
243 | 455 | $this->uploadError( wfMsgExt( $error, 'parseinline' ) ); |
244 | 456 | break; |
245 | | - |
246 | | - case UploadBase::UPLOAD_WARNING: |
247 | | - unset( $details['status'] ); |
248 | | - $this->uploadWarning( $details ); |
249 | | - break; |
250 | | - |
251 | | - case UploadBase::INTERNAL_ERROR: |
252 | | - $status = $details['internal']; |
253 | | - $this->showError( $wgOut->parse( $status->getWikiText() ) ); |
254 | | - break; |
255 | | - |
256 | 457 | default: |
257 | 458 | throw new MWException( __METHOD__ . ": Unknown value `{$details['status']}`" ); |
258 | 459 | } |
259 | 460 | } |
260 | | - |
| 461 | + |
261 | 462 | /** |
262 | | - * Really do the upload |
263 | | - * Checks are made in SpecialUpload::execute() |
264 | | - * |
265 | | - * @param array $resultDetails contains result-specific dict of additional values |
266 | | - * |
| 463 | + * Remove a temporarily kept file stashed by saveTempUploadedFile(). |
267 | 464 | * @access private |
| 465 | + * @return success |
268 | 466 | */ |
269 | | - function internalProcessUpload() { |
270 | | - global $wgUser; |
271 | | - |
272 | | - if( !wfRunHooks( 'UploadForm:BeforeProcessing', array( &$this ) ) ) |
273 | | - { |
274 | | - wfDebug( "Hook 'UploadForm:BeforeProcessing' broke processing the file.\n" ); |
275 | | - return array( 'status' => UploadBase::BEFORE_PROCESSING ); |
276 | | - } |
277 | | - |
278 | | - /** |
279 | | - * If the image is protected, non-sysop users won't be able |
280 | | - * to modify it by uploading a new revision. |
281 | | - */ |
282 | | - $permErrors = $this->mUpload->verifyPermissions( $wgUser ); |
283 | | - if( $permErrors !== true ) { |
284 | | - return array( 'status' => UploadBase::PROTECTED_PAGE, 'permissionserrors' => $permErrors ); |
285 | | - } |
286 | | - |
287 | | - // Fetch the file if required |
288 | | - $status = $this->mUpload->fetchFile(); |
289 | | - if( !$status->isOK() ) { |
290 | | - return array( 'status' => UploadBase::BEFORE_PROCESSING, 'error'=> $status->getWikiText() ); |
291 | | - } |
292 | | - |
293 | | - // Check whether this is a sane upload |
294 | | - $result = $this->mUpload->verifyUpload(); |
295 | | - if( $result['status'] != UploadBase::OK ) |
296 | | - return $result; |
297 | | - |
298 | | - $this->mLocalFile = $this->mUpload->getLocalFile(); |
299 | | - |
300 | | - if( !$this->mIgnoreWarning ) { |
301 | | - $warnings = $this->mUpload->checkWarnings(); |
302 | | - |
303 | | - if( count( $warnings ) ) { |
304 | | - $warnings['status'] = UploadBase::UPLOAD_WARNING; |
305 | | - return $warnings; |
306 | | - } |
307 | | - } |
308 | | - |
309 | | - |
310 | | - /** |
311 | | - * Try actually saving the thing... |
312 | | - * It will show an error form on failure. No it will not. |
313 | | - */ |
314 | | - if( !$this->mForReUpload ) { |
315 | | - $pageText = self::getInitialPageText( $this->mComment, $this->mLicense, |
316 | | - $this->mCopyrightStatus, $this->mCopyrightSource ); |
| 467 | + protected function unsaveUploadedFile() { |
| 468 | + global $wgOut; |
| 469 | + if ( !( $this->mUpload instanceof UploadFromStash ) ) |
| 470 | + return true; |
| 471 | + $success = $this->mUpload->unsaveUploadedFile(); |
| 472 | + if ( ! $success ) { |
| 473 | + $wgOut->showFileDeleteError( $this->mUpload->getTempPath() ); |
| 474 | + return false; |
317 | 475 | } else { |
318 | | - $pageText = false; |
| 476 | + return true; |
319 | 477 | } |
320 | | - $status = $this->mUpload->performUpload( $this->mComment, $pageText, $this->mWatchthis, $wgUser ); |
321 | | - |
322 | | - if ( !$status->isGood() ) { |
323 | | - return array( 'status' => UploadBase::INTERNAL_ERROR, 'internal' => $status ); |
324 | | - } else { |
325 | | - // Success, redirect to description page |
326 | | - wfRunHooks( 'SpecialUploadComplete', array( &$this ) ); |
327 | | - return UploadBase::SUCCESS; |
328 | | - } |
329 | 478 | } |
330 | | - |
| 479 | + |
| 480 | + /*** Functions for formatting warnings ***/ |
| 481 | + |
331 | 482 | /** |
332 | 483 | * Formats a result of UploadBase::getExistsWarning as HTML |
333 | 484 | * This check is static and can be done pre-upload via AJAX |
— | — | @@ -361,7 +512,7 @@ |
362 | 513 | $warning[] = '<li>' . wfMsgExt( 'fileexists-thumbnail-yes', 'parseinline', |
363 | 514 | $exists['thumbFile']->getTitle()->getPrefixedText(), $filename ) . '</li>'; |
364 | 515 | } elseif ( $exists['warning'] == 'thumb-name' ) { |
365 | | - # Image w/o '180px-' does not exists, but we do not like these filenames |
| 516 | + // Image w/o '180px-' does not exists, but we do not like these filenames |
366 | 517 | $name = $file->getName(); |
367 | 518 | $badPart = substr( $name, 0, strpos( $name, '-' ) + 1 ); |
368 | 519 | $warning[] = '<li>' . wfMsgExt( 'file-thumbnail-no', 'parseinline', $badPart ) . '</li>'; |
— | — | @@ -391,7 +542,7 @@ |
392 | 543 | * @param string local filename, e.g. 'file exists', 'non-descriptive filename' |
393 | 544 | * @return array list of warning messages |
394 | 545 | */ |
395 | | - static function ajaxGetExistsWarning( $filename ) { |
| 546 | + public static function ajaxGetExistsWarning( $filename ) { |
396 | 547 | $file = wfFindFile( $filename ); |
397 | 548 | if( !$file ) { |
398 | 549 | // Force local file so we have an object to do further checks against |
— | — | @@ -402,7 +553,6 @@ |
403 | 554 | if ( $file ) { |
404 | 555 | $exists = UploadBase::getExistsWarning( $file ); |
405 | 556 | $warning = self::getExistsWarning( $exists ); |
406 | | - // FIXME: We probably also want the prefix blacklist and the wasdeleted check here |
407 | 557 | if ( $warning !== '' ) { |
408 | 558 | $s = "<ul>$warning</ul>"; |
409 | 559 | } |
— | — | @@ -430,7 +580,7 @@ |
431 | 581 | } |
432 | 582 | |
433 | 583 | /** |
434 | | - * Construct the human readable warning message from an array of duplicate files |
| 584 | + * Construct a warning and a gallery from an array of duplicate files. |
435 | 585 | */ |
436 | 586 | public static function getDupeWarning( $dupes ) { |
437 | 587 | if( $dupes ) { |
— | — | @@ -450,200 +600,122 @@ |
451 | 601 | return ''; |
452 | 602 | } |
453 | 603 | } |
| 604 | + |
| 605 | +} |
454 | 606 | |
455 | | - |
456 | | - /** |
457 | | - * Remove a temporarily kept file stashed by saveTempUploadedFile(). |
458 | | - * @access private |
459 | | - * @return success |
460 | | - */ |
461 | | - function unsaveUploadedFile() { |
462 | | - global $wgOut; |
463 | | - $success = $this->mUpload->unsaveUploadedFile(); |
464 | | - if ( ! $success ) { |
465 | | - $wgOut->showFileDeleteError( $this->mUpload->getTempPath() ); |
466 | | - return false; |
467 | | - } else { |
468 | | - return true; |
| 607 | +/** |
| 608 | + * Sub class of HTMLForm that provides the form section of SpecialUpload |
| 609 | + */ |
| 610 | +class UploadForm extends HTMLForm { |
| 611 | + protected $mWatch; |
| 612 | + protected $mForReUpload; |
| 613 | + protected $mSessionKey; |
| 614 | + protected $mSourceIds; |
| 615 | + |
| 616 | + public function __construct( $watch, $forReUpload = false, $sessionKey = '' ) { |
| 617 | + global $wgLang; |
| 618 | + |
| 619 | + $this->mWatch = $watch; |
| 620 | + $this->mForReUpload = $forReUpload; |
| 621 | + $this->mSessionKey = $sessionKey; |
| 622 | + |
| 623 | + $sourceDescriptor = $this->getSourceSection(); |
| 624 | + $descriptor = $sourceDescriptor |
| 625 | + + $this->getDescriptionSection() |
| 626 | + + $this->getOptionsSection(); |
| 627 | + |
| 628 | + wfRunHooks( 'UploadFormInitDescriptor', array( $descriptor ) ); |
| 629 | + parent::__construct( $descriptor, 'upload' ); |
| 630 | + |
| 631 | + # Set some form properties |
| 632 | + $this->setSubmitText( wfMsg( 'uploadbtn' ) ); |
| 633 | + $this->setSubmitName( 'wpUpload' ); |
| 634 | + $this->setSubmitTooltip( 'upload' ); |
| 635 | + $this->setId( 'mw-upload-form' ); |
| 636 | + |
| 637 | + # Build a list of IDs for javascript insertion |
| 638 | + $this->mSourceIds = array(); |
| 639 | + foreach ( $sourceDescriptor as $key => $field ) { |
| 640 | + if ( !empty( $field['id'] ) ) |
| 641 | + $this->mSourceIds[] = $field['id']; |
469 | 642 | } |
| 643 | + |
470 | 644 | } |
471 | | - |
472 | | - /* Interface code starts below this line * |
473 | | - * -------------------------------------------------------------- */ |
474 | | - |
475 | | - |
| 645 | + |
476 | 646 | /** |
477 | | - * @param string $error as HTML |
478 | | - * @access private |
| 647 | + * |
479 | 648 | */ |
480 | | - function uploadError( $error ) { |
481 | | - global $wgOut; |
482 | | - $wgOut->addHTML( '<h2>' . wfMsgHtml( 'uploadwarning' ) . "</h2>\n" ); |
483 | | - $wgOut->addHTML( '<span class="error">' . $error . '</span>' ); |
484 | | - } |
485 | | - |
486 | | - /** |
487 | | - * There's something wrong with this file, not enough to reject it |
488 | | - * totally but we require manual intervention to save it for real. |
489 | | - * Stash it away, then present a form asking to confirm or cancel. |
490 | | - * |
491 | | - * @param string $warning as HTML |
492 | | - * @access private |
493 | | - */ |
494 | | - function uploadWarning( $warnings ) { |
495 | | - global $wgOut, $wgUser; |
496 | | - global $wgUseCopyrightUpload; |
497 | | - |
498 | | - $this->mSessionKey = $this->mUpload->stashSession(); |
499 | | - |
500 | | - if( $this->mSessionKey === false ) { |
501 | | - # Couldn't save file; an error has been displayed so let's go. |
502 | | - return; |
| 649 | + protected function getSourceSection() { |
| 650 | + global $wgLang, $wgUser, $wgRequest; |
| 651 | + |
| 652 | + if ( $this->mSessionKey ) { |
| 653 | + return array( |
| 654 | + 'wpSessionKey' => array( |
| 655 | + 'type' => 'hidden', |
| 656 | + 'default' => $this->mSessionKey, |
| 657 | + ), |
| 658 | + 'wpSourceType' => array( |
| 659 | + 'type' => 'hidden', |
| 660 | + 'default' => 'Stash', |
| 661 | + ), |
| 662 | + ); |
503 | 663 | } |
504 | | - |
505 | | - $sk = $wgUser->getSkin(); |
506 | | - |
507 | | - $wgOut->addHTML( '<h2>' . wfMsgHtml( 'uploadwarning' ) . "</h2>\n" ); |
508 | | - $wgOut->addHTML( '<ul class="warning">' ); |
509 | | - foreach( $warnings as $warning => $args ) { |
510 | | - $msg = null; |
511 | | - if( $warning == 'exists' ) { |
512 | | - |
513 | | - //we should not have produced this warning if the user already acknowledged the destination warning |
514 | | - //at any rate I don't see why we should hid this warning if mDestWarningAck has been checked |
515 | | - //(it produces an empty warning page when no other warnings are fired) |
516 | | - //if ( !$this->mDestWarningAck ) |
517 | | - $msg = self::getExistsWarning( $args ); |
518 | | - |
519 | | - } elseif( $warning == 'duplicate' ) { |
520 | | - $msg = $this->getDupeWarning( $args ); |
521 | | - } elseif( $warning == 'duplicate-archive' ) { |
522 | | - $titleText = Title::makeTitle( NS_FILE, $args )->getPrefixedText(); |
523 | | - $msg = Xml::tags( 'li', null, wfMsgExt( 'file-deleted-duplicate', array( 'parseinline' ), array( $titleText ) ) ); |
524 | | - } else { |
525 | | - if( is_bool( $args ) ) |
526 | | - $args = array(); |
527 | | - elseif( !is_array( $args ) ) |
528 | | - $args = array( $args ); |
529 | | - $msg = "\t<li>" . wfMsgExt( $warning, 'parseinline', $args ) . "</li>\n"; |
530 | | - } |
531 | | - if( $msg ) |
532 | | - $wgOut->addHTML( $msg ); |
| 664 | + |
| 665 | + $canUploadByUrl = UploadFromUrl::isEnabled() && $wgUser->isAllowed( 'upload_by_url' ); |
| 666 | + $radio = $canUploadByUrl; |
| 667 | + $selectedSourceType = strtolower( $wgRequest->getText( 'wpSourceType', 'File' ) ); |
| 668 | + |
| 669 | + $descriptor = array(); |
| 670 | + $descriptor['UploadFile'] = array( |
| 671 | + 'class' => 'UploadSourceField', |
| 672 | + 'section' => 'source', |
| 673 | + 'type' => 'file', |
| 674 | + 'id' => 'wpUploadFile', |
| 675 | + 'label-message' => 'sourcefilename', |
| 676 | + 'upload-type' => 'File', |
| 677 | + 'radio' => &$radio, |
| 678 | + 'help' => wfMsgExt( 'upload-maxfilesize', |
| 679 | + array( 'parseinline', 'escapenoentities' ), |
| 680 | + $wgLang->formatSize( |
| 681 | + wfShorthandToInteger( ini_get( 'upload_max_filesize' ) ) |
| 682 | + ) |
| 683 | + ) . ' ' . wfMsgHtml( 'upload_source_file' ), |
| 684 | + 'checked' => $selectedSourceType == 'file', |
| 685 | + ); |
| 686 | + if ( $canUploadByUrl ) { |
| 687 | + global $wgMaxUploadSize; |
| 688 | + $descriptor['UploadFileURL'] = array( |
| 689 | + 'class' => 'UploadSourceField', |
| 690 | + 'section' => 'source', |
| 691 | + 'id' => 'wpUploadFileURL', |
| 692 | + 'label-message' => 'sourceurl', |
| 693 | + 'upload-type' => 'Url', |
| 694 | + 'radio' => &$radio, |
| 695 | + 'help' => wfMsgExt( 'upload-maxfilesize', |
| 696 | + array( 'parseinline', 'escapenoentities' ), |
| 697 | + $wgLang->formatSize( $wgMaxUploadSize ) |
| 698 | + ) . ' ' . wfMsgHtml( 'upload_source_url' ), |
| 699 | + 'checked' => $selectedSourceType == 'url', |
| 700 | + ); |
533 | 701 | } |
534 | | - |
535 | | - $titleObj = SpecialPage::getTitleFor( 'Upload' ); |
536 | | - |
537 | | - if ( $wgUseCopyrightUpload ) { |
538 | | - $copyright = Xml::hidden( 'wpUploadCopyStatus', $this->mCopyrightStatus ) . "\n" . |
539 | | - Xml::hidden( 'wpUploadSource', $this->mCopyrightSource ) . "\n"; |
540 | | - } else { |
541 | | - $copyright = ''; |
542 | | - } |
543 | | - $wgOut->addHTML( |
544 | | - Xml::openElement( 'form', array( 'method' => 'post', 'action' => $titleObj->getLocalURL( 'action=submit' ), |
545 | | - 'enctype' => 'multipart/form-data', 'id' => 'uploadwarning' ) ) . "\n" . |
546 | | - Xml::hidden('wpEditToken', $wgUser->editToken(), array("id" => 'wpEditToken')) . |
547 | | - Xml::hidden( 'wpIgnoreWarning', '1' ) . "\n" . |
548 | | - Xml::hidden( 'wpSourceType', 'stash' ) . "\n" . |
549 | | - Xml::hidden( 'wpSessionKey', $this->mSessionKey ) . "\n" . |
550 | | - Xml::hidden( 'wpUploadDescription', $this->mComment ) . "\n" . |
551 | | - Xml::hidden( 'wpLicense', $this->mLicense ) . "\n" . |
552 | | - Xml::hidden( 'wpDestFile', $this->mDesiredDestName ) . "\n" . |
553 | | - Xml::hidden( 'wpWatchthis', $this->mWatchthis ) . "\n" . |
554 | | - "{$copyright}<br />" . |
555 | | - Xml::submitButton( wfMsg( 'ignorewarning' ), array ( 'name' => 'wpUpload', 'id' => 'wpUpload', 'checked' => 'checked' ) ) . ' ' . |
556 | | - Xml::submitButton( wfMsg( 'reuploaddesc' ), array ( 'name' => 'wpReUpload', 'id' => 'wpReUpload' ) ) . |
557 | | - Xml::closeElement( 'form' ) . "\n" |
| 702 | + wfRunHooks( 'UploadFormSourceDescriptors', array( $descriptor, &$radio, $selectedSourceType ) ); |
| 703 | + |
| 704 | + $descriptor['Extensions'] = array( |
| 705 | + 'type' => 'info', |
| 706 | + 'section' => 'source', |
| 707 | + 'default' => $this->getExtensionsMessage(), |
| 708 | + 'raw' => true, |
558 | 709 | ); |
| 710 | + return $descriptor; |
559 | 711 | } |
560 | | - |
| 712 | + |
561 | 713 | /** |
562 | | - * Displays the main upload form, optionally with a highlighted |
563 | | - * error message up at the top. |
564 | | - * |
565 | | - * @param string $msg as HTML |
566 | | - * @access private |
| 714 | + * |
567 | 715 | */ |
568 | | - function mainUploadForm( $msg='' ) { |
569 | | - global $wgOut, $wgUser, $wgLang, $wgMaxUploadSize, $wgEnableFirefogg; |
570 | | - global $wgUseCopyrightUpload, $wgUseAjax, $wgAjaxUploadDestCheck, $wgAjaxLicensePreview; |
571 | | - global $wgRequest; |
572 | | - global $wgStylePath, $wgStyleVersion; |
573 | | - global $wgEnableJS2system; |
574 | | - |
575 | | - $useAjaxDestCheck = $wgUseAjax && $wgAjaxUploadDestCheck; |
576 | | - $useAjaxLicensePreview = $wgUseAjax && $wgAjaxLicensePreview; |
577 | | - |
578 | | - $adc = wfBoolToStr( $useAjaxDestCheck ); |
579 | | - $alp = wfBoolToStr( $useAjaxLicensePreview ); |
580 | | - $uef = wfBoolToStr( $wgEnableFirefogg ); |
581 | | - $autofill = wfBoolToStr( $this->mDesiredDestName == '' ); |
582 | | - |
583 | | - |
584 | | - $wgOut->addScript( "<script type=\"text/javascript\"> |
585 | | -wgAjaxUploadDestCheck = {$adc}; |
586 | | -wgAjaxLicensePreview = {$alp}; |
587 | | -wgEnableFirefogg = {$uef}; |
588 | | -wgUploadAutoFill = {$autofill}; |
589 | | -</script>" ); |
590 | | - |
591 | | - if( $wgEnableJS2system ) { |
592 | | - //js2version of upload page: |
593 | | - $wgOut->addScriptClass( 'uploadPage' ); |
594 | | - }else{ |
595 | | - //legacy upload code: |
596 | | - $wgOut->addScriptFile( 'upload.js' ); |
597 | | - $wgOut->addScriptFile( 'edit.js' ); // For <charinsert> support |
598 | | - } |
599 | | - |
600 | | - if( !wfRunHooks( 'UploadForm:initial', array( &$this ) ) ) |
601 | | - { |
602 | | - wfDebug( "Hook 'UploadForm:initial' broke output of the upload form" ); |
603 | | - return false; |
604 | | - } |
605 | | - |
606 | | - if( $this->mDesiredDestName != '' ) { |
607 | | - $title = Title::makeTitleSafe( NS_FILE, $this->mDesiredDestName ); |
608 | | - // Show a subtitle link to deleted revisions (to sysops et al only) |
609 | | - if( $title instanceof Title && ( $count = $title->isDeleted() ) > 0 && $wgUser->isAllowed( 'deletedhistory' ) ) { |
610 | | - $link = wfMsgExt( |
611 | | - $wgUser->isAllowed( 'delete' ) ? 'thisisdeleted' : 'viewdeleted', |
612 | | - array( 'parse', 'replaceafter' ), |
613 | | - $wgUser->getSkin()->linkKnown( |
614 | | - SpecialPage::getTitleFor( 'Undelete', $title->getPrefixedText() ), |
615 | | - wfMsgExt( 'restorelink', array( 'parsemag', 'escape' ), $count ) |
616 | | - ) |
617 | | - ); |
618 | | - $wgOut->addHTML( "<div id=\"contentSub2\">{$link}</div>" ); |
619 | | - } |
620 | | - |
621 | | - // Show the relevant lines from deletion log (for still deleted files only) |
622 | | - if( $title instanceof Title && $title->isDeletedQuick() && !$title->exists() ) { |
623 | | - $this->showDeletionLog( $wgOut, $title->getPrefixedText() ); |
624 | | - } |
625 | | - } |
626 | | - |
627 | | - $cols = intval($wgUser->getOption( 'cols' )); |
628 | | - |
629 | | - if( $wgUser->getOption( 'editwidth' ) ) { |
630 | | - $width = " style=\"width:100%\""; |
631 | | - } else { |
632 | | - $width = ''; |
633 | | - } |
634 | | - |
635 | | - if ( '' != $msg ) { |
636 | | - $sub = wfMsgHtml( 'uploaderror' ); |
637 | | - $wgOut->addHTML( "<h2>{$sub}</h2>\n" . |
638 | | - "<span class='error'>{$msg}</span>\n" ); |
639 | | - } |
640 | | - |
641 | | - $wgOut->addHTML( '<div id="uploadtext">' ); |
642 | | - $wgOut->addWikiMsg( 'uploadtext', $this->mDesiredDestName ); |
643 | | - $wgOut->addHTML( "</div>\n" ); |
644 | | - |
| 716 | + protected function getExtensionsMessage() { |
645 | 717 | # Print a list of allowed file extensions, if so configured. We ignore |
646 | 718 | # MIME type here, it's incomprehensible to most people and too long. |
647 | | - global $wgCheckFileExtensions, $wgStrictFileExtensions, |
| 719 | + global $wgLang, $wgCheckFileExtensions, $wgStrictFileExtensions, |
648 | 720 | $wgFileExtensions, $wgFileBlacklist; |
649 | 721 | |
650 | 722 | $allowedExtensions = ''; |
— | — | @@ -661,387 +733,202 @@ |
662 | 734 | wfMsgWikiHtml( 'upload-preferred', $wgLang->commaList( $wgFileExtensions ) ) . |
663 | 735 | "</div>\n" . |
664 | 736 | '<div id="mw-upload-prohibited">' . |
665 | | - wfMsgWikiHtml( 'upload-prohibited', $wgLang->commaList( $wgFileExtensions ) ) . |
| 737 | + wfMsgWikiHtml( 'upload-prohibited', $wgLang->commaList( $wgFileBlacklist ) ) . |
666 | 738 | "</div>\n"; |
667 | 739 | } |
668 | 740 | } else { |
669 | 741 | # Everything is permitted. |
670 | 742 | $extensionsList = ''; |
671 | 743 | } |
672 | | - |
673 | | - # Get the maximum file size from php.ini as $wgMaxUploadSize works for uploads from URL via CURL only |
674 | | - # See http://www.php.net/manual/en/ini.core.php#ini.upload-max-filesize for possible values of upload_max_filesize |
675 | | - $val = wfShorthandToInteger( ini_get( 'upload_max_filesize' ) ); |
676 | | - $maxUploadSize = '<div id="mw-upload-maxfilesize">' . |
677 | | - wfMsgExt( 'upload-maxfilesize', array( 'parseinline', 'escapenoentities' ), |
678 | | - $wgLang->formatSize( $val ) ) . |
679 | | - "</div>\n"; |
680 | | - //add a hidden filed for upload by url (uses the $wgMaxUploadSize var) |
681 | | - if( UploadFromUrl::isEnabled() ) { |
682 | | - $maxUploadSize.='<div id="mw-upload-maxfilesize-url" style="display:none">' . |
683 | | - wfMsgExt( 'upload-maxfilesize', array( 'parseinline', 'escapenoentities' ), |
684 | | - $wgLang->formatSize( $wgMaxUploadSize ) ) . |
685 | | - "</div>\n"; |
| 744 | + return $extensionsList; |
| 745 | + } |
| 746 | + |
| 747 | + /** |
| 748 | + * |
| 749 | + */ |
| 750 | + protected function getDescriptionSection() { |
| 751 | + global $wgUser, $wgOut; |
| 752 | + |
| 753 | + $cols = intval( $wgUser->getOption( 'cols' ) ); |
| 754 | + if( $wgUser->getOption( 'editwidth' ) ) { |
| 755 | + $wgOut->addInlineStyle( '#mw-htmlform-description { width: 100%; }' ); |
686 | 756 | } |
687 | | - |
688 | | - $sourcefilename = wfMsgExt( 'sourcefilename', array( 'parseinline', 'escapenoentities' ) ); |
689 | | - $destfilename = wfMsgExt( 'destfilename', array( 'parseinline', 'escapenoentities' ) ); |
690 | | - |
691 | | - $msg = ( $this->mForReUpload ) ? 'filereuploadsummary' : 'fileuploadsummary'; |
692 | | - $summary = wfMsgExt( $msg, 'parseinline' ); |
693 | | - |
694 | | - $licenses = new Licenses(); |
695 | | - $license = wfMsgExt( 'license', array( 'parseinline' ) ); |
696 | | - $nolicense = wfMsgHtml( 'nolicense' ); |
697 | | - $licenseshtml = $licenses->getHtml(); |
698 | | - |
699 | | - $ulb = wfMsgHtml( 'uploadbtn' ); |
700 | | - |
701 | | - |
702 | | - $titleObj = SpecialPage::getTitleFor( 'Upload' ); |
703 | | - |
704 | | - $encDestName = htmlspecialchars( $this->mDesiredDestName ); |
705 | | - |
706 | | - $watchChecked = $this->watchCheck() ? 'checked="checked"' : ''; |
707 | | - # Re-uploads should not need "file exist already" warnings |
708 | | - $warningChecked = ($this->mIgnoreWarning || $this->mForReUpload) ? 'checked="checked"' : ''; |
709 | | - |
710 | | - // Prepare form for upload or upload/copy |
711 | | - //javascript moved from inline calls to setup: |
712 | | - if( UploadFromUrl::isEnabled() && $wgUser->isAllowed( 'upload_by_url' ) ) { |
713 | | - if( $wgEnableJS2system ) { |
714 | | - $filename_form = |
715 | | - Xml::input( 'wpSourceType', false, 'file', |
716 | | - array( 'id' => 'wpSourceTypeFile', 'type' => 'radio', 'checked' => 'checked' ) ) . |
717 | | - Xml::input( 'wpUploadFile', 60, false, |
718 | | - array( 'id' => 'wpUploadFile', 'type' => 'file', 'tabindex' => '1' ) ) . |
719 | | - wfMsgHTML( 'upload_source_file' ) . "<br/>" . |
720 | | - Xml::input( 'wpSourceType', false, 'Url', |
721 | | - array( 'id' => 'wpSourceTypeURL', 'type' => 'radio' ) ) . |
722 | | - Xml::input( 'wpUploadFileURL', 60, false, |
723 | | - array( 'id' => 'wpUploadFileURL', 'type' => 'text', 'tabindex' => '1' ) ) . |
724 | | - wfMsgHtml( 'upload_source_url' ) ; |
725 | | - } else { |
726 | | - //@@todo deprecate (not needed once $wgEnableJS2system is turned on) |
727 | | - $filename_form = |
728 | | - "<input type='radio' id='wpSourceTypeFile' name='wpSourceType' value='file' " . |
729 | | - "onchange='toggle_element_activation(\"wpUploadFileURL\",\"wpUploadFile\")' checked='checked' />" . |
730 | | - "<input tabindex='1' type='file' name='wpUploadFile' id='wpUploadFile' " . |
731 | | - " onfocus='" . |
732 | | - "toggle_element_activation(\"wpUploadFileURL\",\"wpUploadFile\");" . |
733 | | - "toggle_element_check(\"wpSourceTypeFile\",\"wpSourceTypeURL\")' " . |
734 | | - "onchange='fillDestFilename(\"wpUploadFile\")' size='60' />" . |
735 | | - wfMsgHTML( 'upload_source_file' ) . "<br/>" . |
736 | | - "<input type='radio' id='wpSourceTypeURL' name='wpSourceType' value='Url' " . |
737 | | - "onchange='toggle_element_activation(\"wpUploadFile\",\"wpUploadFileURL\")' />" . |
738 | | - "<input tabindex='1' type='text' name='wpUploadFileURL' id='wpUploadFileURL' " . |
739 | | - "onfocus='" . |
740 | | - "toggle_element_activation(\"wpUploadFile\",\"wpUploadFileURL\");" . |
741 | | - "toggle_element_check(\"wpSourceTypeURL\",\"wpSourceTypeFile\")' " . |
742 | | - "onchange='fillDestFilename(\"wpUploadFileURL\")' size='60' />" . |
743 | | - wfMsgHtml( 'upload_source_url' ) ; |
744 | | - |
745 | | - } |
746 | | - } else { |
747 | | - if( $wgEnableJS2system ) { |
748 | | - $filename_form = |
749 | | - Xml::input( 'wpUploadFile', 60, false, |
750 | | - array( 'id' => 'wpUploadFile', 'type' => 'file', 'tabindex' => '1' ) ) . |
751 | | - Xml::hidden( 'wpSourceType', 'file'); |
752 | | - } else { |
753 | | - $filename_form = |
754 | | - "<input tabindex='1' type='file' name='wpUploadFile' id='wpUploadFile' size='60' ". |
755 | | - "onchange='fillDestFilename(\"wpUploadFile\")' />" . |
756 | | - "<input type='hidden' name='wpSourceType' value='file' />" ; |
757 | | - } |
758 | | - } |
759 | | - $warningRow = ''; |
760 | | - $destOnkeyup = ''; |
761 | | - if( $wgEnableJS2system ) { |
762 | | - $warningRow = "<tr><td colspan='2' id='wpDestFile-warning'> </td></tr>"; |
763 | | - } else { |
764 | | - if ( $useAjaxDestCheck ) { |
765 | | - $warningRow = "<tr><td colspan='2' id='wpDestFile-warning'> </td></tr>"; |
766 | | - $destOnkeyup = 'onchange=\'wgUploadWarningObj.checkNow(this.value);\''; |
767 | | - } |
768 | | - } |
769 | | - # Uploading a new version? If so, the name is fixed. |
770 | | - $on = $this->mForReUpload ? "readonly='readonly'" : ""; |
771 | | - |
772 | | - $encComment = htmlspecialchars( $this->mComment ); |
773 | | - |
774 | | - //add the wpEditToken |
775 | | - $wgOut->addHTML( |
776 | | - Xml::openElement( 'form', |
777 | | - array( 'method' => 'post', 'action' => $titleObj->getLocalURL( 'action=submit' ), |
778 | | - 'enctype' => 'multipart/form-data', 'id' => 'mw-upload-form' ) ) . |
779 | | - Xml::hidden( 'wpEditToken', $wgUser->editToken(), array( 'id' => 'wpEditToken' ) ) . |
780 | | - Xml::openElement( 'fieldset' ) . |
781 | | - Xml::element( 'legend', null, wfMsg( 'upload' ) ) . |
782 | | - Xml::openElement( 'table', array( 'border' => '0', 'id' => 'mw-upload-table' ) ) . |
783 | | - "<tr> |
784 | | - {$this->uploadFormTextTop} |
785 | | - <td class='mw-label'> |
786 | | - <label for='wpUploadFile'>{$sourcefilename}</label> |
787 | | - </td> |
788 | | - <td class='mw-input'> |
789 | | - {$filename_form} |
790 | | - </td> |
791 | | - </tr> |
792 | | - <tr> |
793 | | - <td></td> |
794 | | - <td> |
795 | | - {$maxUploadSize} |
796 | | - {$extensionsList} |
797 | | - </td> |
798 | | - </tr> |
799 | | - <tr> |
800 | | - <td class='mw-label'> |
801 | | - <label for='wpDestFile'>{$destfilename}</label> |
802 | | - </td> |
803 | | - <td class='mw-input'>" |
| 757 | + |
| 758 | + $descriptor = array( |
| 759 | + 'DestFile' => array( |
| 760 | + 'type' => 'text', |
| 761 | + 'section' => 'description', |
| 762 | + 'id' => 'wpDestFile', |
| 763 | + 'label-message' => 'destfilename', |
| 764 | + 'size' => 60, |
| 765 | + ), |
| 766 | + 'UploadDescription' => array( |
| 767 | + 'type' => 'textarea', |
| 768 | + 'section' => 'description', |
| 769 | + 'id' => 'wpUploadDescription', |
| 770 | + 'label-message' => $this->mForReUpload |
| 771 | + ? 'filereuploadsummary' |
| 772 | + : 'fileuploadsummary', |
| 773 | + 'cols' => $cols, |
| 774 | + 'rows' => 8, |
| 775 | + ), |
| 776 | + 'License' => array( |
| 777 | + 'type' => 'select', |
| 778 | + 'class' => 'Licenses', |
| 779 | + 'section' => 'description', |
| 780 | + 'id' => 'wpLicense', |
| 781 | + 'label-message' => 'license', |
| 782 | + ), |
804 | 783 | ); |
805 | | - if( $this->mForReUpload ) { |
806 | | - $wgOut->addHTML( |
807 | | - Xml::hidden( 'wpDestFile', $this->mDesiredDestName, array('id'=>'wpDestFile','tabindex'=>2) ) . |
808 | | - "<tt>" . |
809 | | - $encDestName . |
810 | | - "</tt>" |
| 784 | + if ( $this->mForReUpload ) |
| 785 | + $descriptor['DestFile']['readonly'] = true; |
| 786 | + |
| 787 | + global $wgUseAjax, $wgAjaxLicensePreview; |
| 788 | + if ( $wgUseAjax && $wgAjaxLicensePreview ) |
| 789 | + $descriptor['AjaxLicensePreview'] = array( |
| 790 | + 'class' => 'UploadAjaxLicensePreview', |
| 791 | + 'section' => 'description' |
811 | 792 | ); |
812 | | - } |
813 | | - else { |
814 | | - $wgOut->addHTML( |
815 | | - "<input tabindex='2' type='text' name='wpDestFile' id='wpDestFile' size='60' |
816 | | - value=\"{$encDestName}\" $destOnkeyup />" |
| 793 | + |
| 794 | + global $wgUseCopyrightUpload; |
| 795 | + if ( $wgUseCopyrightUpload ) { |
| 796 | + $descriptor['UploadCopyStatus'] = array( |
| 797 | + 'type' => 'text', |
| 798 | + 'section' => 'description', |
| 799 | + 'id' => 'wpUploadCopyStatus', |
| 800 | + 'label-message' => 'filestatus', |
817 | 801 | ); |
818 | | - } |
819 | | - |
820 | | - |
821 | | - $wgOut->addHTML( |
822 | | - "</td> |
823 | | - </tr> |
824 | | - <tr> |
825 | | - <td class='mw-label'> |
826 | | - <label for='wpUploadDescription'>{$summary}</label> |
827 | | - </td> |
828 | | - <td class='mw-input'> |
829 | | - <textarea tabindex='3' name='wpUploadDescription' id='wpUploadDescription' rows='6' |
830 | | - cols='{$cols}'{$width}>$encComment</textarea> |
831 | | - {$this->uploadFormTextAfterSummary} |
832 | | - </td> |
833 | | - </tr> |
834 | | - <tr>" |
835 | | - ); |
836 | | - |
837 | | - # Re-uploads should not need license info |
838 | | - if ( !$this->mForReUpload && $licenseshtml != '' ) { |
839 | | - global $wgStylePath; |
840 | | - $wgOut->addHTML( " |
841 | | - <td class='mw-label'> |
842 | | - <label for='wpLicense'>$license</label> |
843 | | - </td> |
844 | | - <td class='mw-input'> |
845 | | - <select name='wpLicense' id='wpLicense' tabindex='4' |
846 | | - onchange='licenseSelectorCheck()'> |
847 | | - <option value=''>$nolicense</option> |
848 | | - $licenseshtml |
849 | | - </select> |
850 | | - </td> |
851 | | - </tr> |
852 | | - <tr>" |
| 802 | + $descriptor['UploadSource'] = array( |
| 803 | + 'type' => 'text', |
| 804 | + 'section' => 'description', |
| 805 | + 'id' => 'wpUploadSource', |
| 806 | + 'label-message' => 'filesource', |
853 | 807 | ); |
854 | | - if( $useAjaxLicensePreview ) { |
855 | | - $wgOut->addHTML( " |
856 | | - <td></td> |
857 | | - <td id=\"mw-license-preview\"></td> |
858 | | - </tr> |
859 | | - <tr>" |
860 | | - ); |
861 | | - } |
862 | 808 | } |
863 | | - |
864 | | - if ( !$this->mForReUpload && $wgUseCopyrightUpload ) { |
865 | | - $filestatus = wfMsgExt( 'filestatus', 'escapenoentities' ); |
866 | | - $copystatus = htmlspecialchars( $this->mCopyrightStatus ); |
867 | | - $filesource = wfMsgExt( 'filesource', 'escapenoentities' ); |
868 | | - $uploadsource = htmlspecialchars( $this->mCopyrightSource ); |
869 | | - |
870 | | - $wgOut->addHTML( " |
871 | | - <td class='mw-label' style='white-space: nowrap;'> |
872 | | - <label for='wpUploadCopyStatus'>$filestatus</label></td> |
873 | | - <td class='mw-input'> |
874 | | - <input tabindex='5' type='text' name='wpUploadCopyStatus' id='wpUploadCopyStatus' |
875 | | - value=\"$copystatus\" size='60' /> |
876 | | - </td> |
877 | | - </tr> |
878 | | - <tr> |
879 | | - <td class='mw-label'> |
880 | | - <label for='wpUploadCopyStatus'>$filesource</label> |
881 | | - </td> |
882 | | - <td class='mw-input'> |
883 | | - <input tabindex='6' type='text' name='wpUploadSource' id='wpUploadCopyStatus' |
884 | | - value=\"$uploadsource\" size='60' /> |
885 | | - </td> |
886 | | - </tr> |
887 | | - <tr>" |
888 | | - ); |
889 | | - } |
890 | | - |
891 | | - $wgOut->addHTML( " |
892 | | - <td></td> |
893 | | - <td> |
894 | | - <input tabindex='7' type='checkbox' name='wpWatchthis' id='wpWatchthis' $watchChecked value='true' /> |
895 | | - <label for='wpWatchthis'>" . wfMsgHtml( 'watchthisupload' ) . "</label> |
896 | | - <input tabindex='8' type='checkbox' name='wpIgnoreWarning' id='wpIgnoreWarning' value='true' $warningChecked /> |
897 | | - <label for='wpIgnoreWarning'>" . wfMsgHtml( 'ignorewarnings' ) . "</label> |
898 | | - </td> |
899 | | - </tr> |
900 | | - $warningRow |
901 | | - <tr> |
902 | | - <td></td> |
903 | | - <td class='mw-input'> |
904 | | - <input tabindex='9' type='submit' name='wpUpload' value=\"{$ulb}\"" . |
905 | | - $wgUser->getSkin()->tooltipAndAccesskey( 'upload' ) . " /> |
906 | | - </td> |
907 | | - </tr> |
908 | | - <tr> |
909 | | - <td></td> |
910 | | - <td class='mw-input'>" |
911 | | - ); |
912 | | - $wgOut->addHTML( '<div class="mw-editTools">' ); |
913 | | - $wgOut->addWikiMsgArray( 'edittools', array(), array( 'content' ) ); |
914 | | - $wgOut->addHTML( '</div>' ); |
915 | | - $wgOut->addHTML( " |
916 | | - </td> |
917 | | - </tr>" . |
918 | | - Xml::closeElement( 'table' ) . |
919 | | - Xml::hidden( 'wpDestFileWarningAck', '', array( 'id' => 'wpDestFileWarningAck' ) ) . |
920 | | - Xml::hidden( 'wpForReUpload', $this->mForReUpload, array( 'id' => 'wpForReUpload' ) ) . |
921 | | - Xml::closeElement( 'fieldset' ) . |
922 | | - Xml::closeElement( 'form' ) |
923 | | - ); |
924 | | - $uploadfooter = wfMsgNoTrans( 'uploadfooter' ); |
925 | | - if( $uploadfooter != '-' && !wfEmptyMsg( 'uploadfooter', $uploadfooter ) ){ |
926 | | - $wgOut->addWikiText( '<div id="mw-upload-footer-message">' . $uploadfooter . '</div>' ); |
927 | | - } |
| 809 | + |
| 810 | + return $descriptor; |
928 | 811 | } |
929 | | - |
930 | | - /* -------------------------------------------------------------- */ |
931 | | - |
| 812 | + |
932 | 813 | /** |
933 | | - * See if we should check the 'watch this page' checkbox on the form |
934 | | - * based on the user's preferences and whether we're being asked |
935 | | - * to create a new file or update an existing one. |
936 | | - * |
937 | | - * In the case where 'watch edits' is off but 'watch creations' is on, |
938 | | - * we'll leave the box unchecked. |
939 | | - * |
940 | | - * Note that the page target can be changed *on the form*, so our check |
941 | | - * state can get out of sync. |
| 814 | + * |
942 | 815 | */ |
943 | | - function watchCheck() { |
944 | | - global $wgUser; |
945 | | - if( $wgUser->getOption( 'watchdefault' ) ) { |
946 | | - // Watch all edits! |
947 | | - return true; |
948 | | - } |
949 | | - |
950 | | - $local = wfLocalFile( $this->mDesiredDestName ); |
951 | | - if( $local && $local->exists() ) { |
952 | | - // We're uploading a new version of an existing file. |
953 | | - // No creation, so don't watch it if we're not already. |
954 | | - return $local->getTitle()->userIsWatching(); |
955 | | - } else { |
956 | | - // New page should get watched if that's our option. |
957 | | - return $wgUser->getOption( 'watchcreations' ); |
958 | | - } |
| 816 | + protected function getOptionsSection() { |
| 817 | + global $wgOut; |
| 818 | + |
| 819 | + $descriptor = array( |
| 820 | + 'Watchthis' => array( |
| 821 | + 'type' => 'check', |
| 822 | + 'id' => 'wpWatchthis', |
| 823 | + 'label-message' => 'watchthisupload', |
| 824 | + 'section' => 'options', |
| 825 | + ), |
| 826 | + 'IgnoreWarning' => array( |
| 827 | + 'type' => 'check', |
| 828 | + 'id' => 'wpIgnoreWarning', |
| 829 | + 'label-message' => 'ignorewarnings', |
| 830 | + 'section' => 'options', |
| 831 | + ), |
| 832 | + 'EditTools' => array( |
| 833 | + 'type' => 'edittools', |
| 834 | + 'section' => 'options', |
| 835 | + ), |
| 836 | + ); |
| 837 | + |
| 838 | + $uploadFooter = wfMsgNoTrans( 'uploadfooter' ); |
| 839 | + if ( $uploadFooter != '-' && !wfEmptyMsg( 'uploadfooter', $uploadFooter ) ) |
| 840 | + $descriptor['UploadFooter'] = array( |
| 841 | + 'type' => 'info', |
| 842 | + 'id' => 'mw-upload-footer-message', |
| 843 | + 'default' => $wgOut->parse( $uploadFooter ), |
| 844 | + ); |
| 845 | + |
| 846 | + return $descriptor; |
| 847 | + |
959 | 848 | } |
960 | | - |
| 849 | + |
961 | 850 | /** |
962 | | - * Check if a user is the last uploader |
963 | | - * |
964 | | - * @param User $user |
965 | | - * @param string $img, image name |
966 | | - * @return bool |
967 | | - * @deprecated Use UploadBase::userCanReUpload |
| 851 | + * |
968 | 852 | */ |
969 | | - public static function userCanReUpload( User $user, $img ) { |
970 | | - wfDeprecated( __METHOD__ ); |
971 | | - |
972 | | - if( $user->isAllowed( 'reupload' ) ) |
973 | | - return true; // non-conditional |
974 | | - if( !$user->isAllowed( 'reupload-own' ) ) |
975 | | - return false; |
976 | | - |
977 | | - $dbr = wfGetDB( DB_SLAVE ); |
978 | | - $row = $dbr->selectRow('image', |
979 | | - /* SELECT */ 'img_user', |
980 | | - /* WHERE */ array( 'img_name' => $img ) |
981 | | - ); |
982 | | - if ( !$row ) |
983 | | - return false; |
984 | | - |
985 | | - return $user->getId() == $row->img_user; |
| 853 | + public function show() { |
| 854 | + $this->addUploadJS(); |
| 855 | + parent::show(); |
986 | 856 | } |
987 | | - |
| 857 | + |
988 | 858 | /** |
989 | | - * Display an error with a wikitext description |
| 859 | + * |
990 | 860 | */ |
991 | | - function showError( $description ) { |
| 861 | + protected function addUploadJS( $autofill = true ) { |
| 862 | + global $wgUseAjax, $wgAjaxUploadDestCheck, $wgAjaxLicensePreview; |
| 863 | + global $wgEnableFirefogg, $wgEnableJS2system; |
992 | 864 | global $wgOut; |
993 | | - $wgOut->setPageTitle( wfMsg( "upload-file-error" ) ); |
994 | | - $wgOut->setRobotPolicy( "noindex,nofollow" ); |
995 | | - $wgOut->setArticleRelated( false ); |
996 | | - $wgOut->enableClientCache( false ); |
997 | | - $wgOut->addWikiText( $description ); |
998 | | - } |
| 865 | + |
| 866 | + $useAjaxDestCheck = $wgUseAjax && $wgAjaxUploadDestCheck; |
| 867 | + $useAjaxLicensePreview = $wgUseAjax && $wgAjaxLicensePreview; |
| 868 | + |
| 869 | + $scriptVars = array( |
| 870 | + 'wgAjaxUploadDestCheck' => $wgUseAjax && $wgAjaxUploadDestCheck, |
| 871 | + 'wgAjaxLicensePreview' => $wgUseAjax && $wgAjaxLicensePreview, |
| 872 | + 'wgEnableFirefogg' => (bool)$wgEnableFirefogg, |
| 873 | + 'wgUploadAutoFill' => (bool)$autofill, |
| 874 | + 'wgUploadSourceIds' => $this->mSourceIds, |
| 875 | + ); |
999 | 876 | |
1000 | | - /** |
1001 | | - * Get the initial image page text based on a comment and optional file status information |
1002 | | - */ |
1003 | | - static function getInitialPageText( $comment = '', $license = '', $copyStatus = '', $source = '' ) { |
1004 | | - global $wgUseCopyrightUpload; |
1005 | | - if ( $wgUseCopyrightUpload ) { |
1006 | | - $licensetxt = ''; |
1007 | | - if ( $license != '' ) { |
1008 | | - $licensetxt = '== ' . wfMsgForContent( 'license-header' ) . " ==\n" . '{{' . $license . '}}' . "\n"; |
1009 | | - } |
1010 | | - $pageText = '== ' . wfMsgForContent ( 'filedesc' ) . " ==\n" . $comment . "\n" . |
1011 | | - '== ' . wfMsgForContent ( 'filestatus' ) . " ==\n" . $copyStatus . "\n" . |
1012 | | - "$licensetxt" . |
1013 | | - '== ' . wfMsgForContent ( 'filesource' ) . " ==\n" . $source ; |
| 877 | + $wgOut->addScript( Skin::makeVariablesScript( $scriptVars ) ); |
| 878 | + |
| 879 | + if ( $wgEnableJS2system ) { |
| 880 | + //js2version of upload page: |
| 881 | + $wgOut->addScriptClass( 'uploadPage' ); |
1014 | 882 | } else { |
1015 | | - if ( $license != '' ) { |
1016 | | - $filedesc = $comment == '' ? '' : '== ' . wfMsgForContent ( 'filedesc' ) . " ==\n" . $comment . "\n"; |
1017 | | - $pageText = $filedesc . |
1018 | | - '== ' . wfMsgForContent ( 'license-header' ) . " ==\n" . '{{' . $license . '}}' . "\n"; |
1019 | | - } else { |
1020 | | - $pageText = $comment; |
1021 | | - } |
| 883 | + //legacy upload code: |
| 884 | + $wgOut->addScriptFile( 'upload.js' ); |
| 885 | + $wgOut->addScriptFile( 'edit.js' ); // For <charinsert> support |
1022 | 886 | } |
1023 | | - return $pageText; |
1024 | 887 | } |
| 888 | + |
| 889 | + function trySubmit() { |
| 890 | + return false; |
| 891 | + } |
1025 | 892 | |
1026 | | - /** |
1027 | | - * If there are rows in the deletion log for this file, show them, |
1028 | | - * along with a nice little note for the user |
1029 | | - * |
1030 | | - * @param OutputPage $out |
1031 | | - * @param string filename |
1032 | | - */ |
1033 | | - private function showDeletionLog( $out, $filename ) { |
1034 | | - global $wgUser; |
1035 | | - $loglist = new LogEventsList( $wgUser->getSkin(), $out ); |
1036 | | - $pager = new LogPager( $loglist, 'delete', false, $filename ); |
1037 | | - if( $pager->getNumRows() > 0 ) { |
1038 | | - $out->addHTML( '<div class="mw-warning-with-logexcerpt">' ); |
1039 | | - $out->addWikiMsg( 'upload-wasdeleted' ); |
1040 | | - $out->addHTML( |
1041 | | - $loglist->beginLogEventsList() . |
1042 | | - $pager->getBody() . |
1043 | | - $loglist->endLogEventsList() |
| 893 | +} |
| 894 | + |
| 895 | +/** |
| 896 | + * TODO: DOCUMENT |
| 897 | + */ |
| 898 | +class UploadSourceField extends HTMLTextField { |
| 899 | + function getLabelHtml() { |
| 900 | + $id = "wpSourceType{$this->mParams['upload-type']}"; |
| 901 | + $label = Html::rawElement( 'label', array( 'for' => $id ), $this->mLabel ); |
| 902 | + |
| 903 | + if ( !empty( $this->mParams['radio'] ) ) { |
| 904 | + $attribs = array( |
| 905 | + 'name' => 'wpSourceType', |
| 906 | + 'type' => 'radio', |
| 907 | + 'id' => $id, |
| 908 | + 'value' => $this->mParams['upload-type'], |
1044 | 909 | ); |
1045 | | - $out->addHTML( '</div>' ); |
| 910 | + if ( !empty( $this->mParams['checked'] ) ) |
| 911 | + $attribs['checked'] = 'checked'; |
| 912 | + $label .= Html::element( 'input', $attribs ); |
1046 | 913 | } |
| 914 | + |
| 915 | + return Html::rawElement( 'td', array( 'class' => 'mw-label' ), $label ); |
1047 | 916 | } |
| 917 | + function getSize() { |
| 918 | + return isset( $this->mParams['size'] ) |
| 919 | + ? $this->mParams['size'] |
| 920 | + : 60; |
| 921 | + } |
1048 | 922 | } |
| 923 | + |
| 924 | +/** |
| 925 | + * TODO: Document |
| 926 | + * TODO: This can be migrated to JS only |
| 927 | + */ |
| 928 | +class UploadAjaxLicensePreview extends HTMLFormField { |
| 929 | + public function getTableRow( $value ) { |
| 930 | + return "<tr><td></td><td id=\"mw-license-preview\"></td></tr>\n"; |
| 931 | + } |
| 932 | + public function getInputHTML( $value ) { |
| 933 | + return ''; |
| 934 | + } |
| 935 | +} |
\ No newline at end of file |
Index: trunk/phase3/includes/SpecialPage.php |
— | — | @@ -141,7 +141,7 @@ |
142 | 142 | 'Filepath' => array( 'SpecialPage', 'Filepath' ), |
143 | 143 | 'MIMEsearch' => array( 'SpecialPage', 'MIMEsearch' ), |
144 | 144 | 'FileDuplicateSearch' => array( 'SpecialPage', 'FileDuplicateSearch' ), |
145 | | - 'Upload' => 'UploadForm', |
| 145 | + 'Upload' => 'SpecialUpload', |
146 | 146 | |
147 | 147 | # Wiki data and tools |
148 | 148 | 'Statistics' => 'SpecialStatistics', |
Index: trunk/phase3/languages/messages/MessagesEn.php |
— | — | @@ -2038,8 +2038,7 @@ |
2039 | 2039 | # Upload |
2040 | 2040 | 'upload' => 'Upload file', |
2041 | 2041 | 'uploadbtn' => 'Upload file', |
2042 | | -'reupload' => 'Re-upload', |
2043 | | -'reuploaddesc' => 'Cancel upload and return to the upload form', |
| 2042 | +'reuploaddesc' => 'Cancel upload and return to the upload form ', |
2044 | 2043 | 'uploadnologin' => 'Not logged in', |
2045 | 2044 | 'uploadnologintext' => 'You must be [[Special:UserLogin|logged in]] to upload files.', |
2046 | 2045 | 'upload_directory_missing' => 'The upload directory ($1) is missing and could not be created by the webserver.', |
— | — | @@ -2126,9 +2125,13 @@ |
2127 | 2126 | 'uploadcorrupt' => 'The file is corrupt or has an incorrect extension. |
2128 | 2127 | Please check the file and upload again.', |
2129 | 2128 | 'uploadvirus' => 'The file contains a virus! Details: $1', |
| 2129 | +'upload-source' => 'Source file', |
2130 | 2130 | 'sourcefilename' => 'Source filename:', |
| 2131 | +'sourceurl' => 'Source URL:', |
2131 | 2132 | 'destfilename' => 'Destination filename:', |
2132 | 2133 | 'upload-maxfilesize' => 'Maximum file size: $1', |
| 2134 | +'upload-description' => 'File description', |
| 2135 | +'upload-options' => '', |
2133 | 2136 | 'watchthisupload' => 'Watch this file', |
2134 | 2137 | 'filewasdeleted' => 'A file of this name has been previously uploaded and subsequently deleted. |
2135 | 2138 | You should check the $1 before proceeding to upload it again.', |
Index: trunk/phase3/RELEASE-NOTES |
— | — | @@ -249,7 +249,11 @@ |
250 | 250 | limit exceeded, __NOINDEX__ tracking, etc) can now be disabled by setting the |
251 | 251 | system message ([[MediaWiki:expensive-parserfunction-category]] etc) to "-". |
252 | 252 | * Added maintenance script sqlite.php for SQLite-specific maintenance tasks. |
| 253 | +* Rewrote Special:Upload to allow easier extension. |
| 254 | +* Upload errors that can be solved by changing the filename now do not require |
| 255 | + reuploading. |
253 | 256 | |
| 257 | + |
254 | 258 | === Bug fixes in 1.16 === |
255 | 259 | |
256 | 260 | * (bug 18031) Make namespace selector on Special:Export remember the previous |