r57868 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r57867‎ | r57868 | r57869 >
Date:19:41, 18 October 2009
Author:btongminh
Status:resolved (Comments)
Tags:
Comment:
Rewrote Special:Upload to allow easier extension. Mostly backwards compatible towards the end user: tested with Commons' upload scripts.
* Special:Upload now uses HTMLForm for form generation
* Upload errors that can be solved by changing the filename now do not require reuploading.
Modified paths:
  • /trunk/phase3/RELEASE-NOTES (modified) (history)
  • /trunk/phase3/includes/AutoLoader.php (modified) (history)
  • /trunk/phase3/includes/Licenses.php (modified) (history)
  • /trunk/phase3/includes/Setup.php (modified) (history)
  • /trunk/phase3/includes/SpecialPage.php (modified) (history)
  • /trunk/phase3/includes/api/ApiUpload.php (modified) (history)
  • /trunk/phase3/includes/specials/SpecialUpload.php (modified) (history)
  • /trunk/phase3/includes/upload/UploadBase.php (modified) (history)
  • /trunk/phase3/includes/upload/UploadFromStash.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesEn.php (modified) (history)
  • /trunk/phase3/skins/common/upload.js (modified) (history)
  • /trunk/phase3/skins/common/wikibits.js (modified) (history)

Diff [purge]

Index: trunk/phase3/skins/common/upload.js
@@ -34,8 +34,77 @@
3535 }
3636 }
3737 }
 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+ };
3867 }
3968
 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+
40109 var wgUploadWarningObj = {
41110 'responseCache' : { '' : '&nbsp;' },
42111 'nameToCheck' : '',
@@ -86,7 +155,7 @@
87156 // ajax requests can be supported.
88157 var obj = this;
89158 var fileName = this.nameToCheck;
90 - sajax_do_call( 'UploadForm::ajaxGetExistsWarning', [this.nameToCheck],
 159+ sajax_do_call( 'SpecialUpload::ajaxGetExistsWarning', [this.nameToCheck],
91160 function (result) {
92161 obj.processResult(result, fileName)
93162 }
@@ -101,16 +170,7 @@
102171
103172 'setWarning' : function (warning) {
104173 var warningElt = document.getElementById( 'wpDestFile-warning' );
105 - var ackElt = document.getElementById( 'wpDestFileWarningAck' );
106174 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 == '&nbsp;' ) {
111 - ackElt.value = '';
112 - } else {
113 - ackElt.value = '1';
114 - }
115175 },
116176 'setInnerHTML' : function (element, text) {
117177 // Check for no change to avoid flicker in IE 7
@@ -180,6 +240,7 @@
181241 }
182242
183243 // Capitalise first letter and replace spaces by underscores
 244+ // FIXME: $wgCapitalizedNamespaces
184245 fname = fname.charAt(0).toUpperCase().concat(fname.substring(1,10000)).replace(/ /g, '_');
185246
186247 // Output result
@@ -214,7 +275,7 @@
215276 }
216277 }
217278 injectSpinner( document.getElementById( 'wpLicense' ), 'license' );
218 - sajax_do_call( 'UploadForm::ajaxGetLicensePreview', [license],
 279+ sajax_do_call( 'SpecialUpload::ajaxGetLicensePreview', [license],
219280 function( result ) {
220281 wgUploadLicenseObj.processResult( result, license );
221282 }
Index: trunk/phase3/skins/common/wikibits.js
@@ -469,37 +469,7 @@
470470 return true;
471471 }
472472
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";
481473
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 -
504474 /*
505475 Written by Jonathan Snook, http://www.snook.ca/jonathan
506476 Add-ons by Robert Nyman, http://www.robertnyman.com
Index: trunk/phase3/includes/upload/UploadFromStash.php
@@ -41,6 +41,7 @@
4242 false
4343 );
4444
 45+ $this->mVirtualTempPath = $sessionData['mTempPath'];
4546 $this->mFileProps = $sessionData['mFileProps'];
4647 }
4748
@@ -68,4 +69,14 @@
6970 return array();
7071 }
7172
 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+
7283 }
\ No newline at end of file
Index: trunk/phase3/includes/upload/UploadBase.php
@@ -1,18 +1,18 @@
22 <?php
33 /**
4 - * @file
 4+ * @file
55 * @ingroup upload
6 - *
 6+ *
77 * UploadBase and subclasses are the backend of MediaWiki's file uploads.
88 * The frontends are formed by ApiUpload and SpecialUpload.
9 - *
 9+ *
1010 * See also includes/docs/upload.txt
11 - *
 11+ *
1212 * @author Brion Vibber
1313 * @author Bryan Tong Minh
1414 * @author Michael Dale
1515 */
16 -
 16+
1717 abstract class UploadBase {
1818 protected $mTempPath;
1919 protected $mDesiredDestName, $mDestName, $mRemoveTempFile, $mSourceType;
@@ -22,19 +22,15 @@
2323
2424 const SUCCESS = 0;
2525 const OK = 0;
26 - const BEFORE_PROCESSING = 1;
27 - const LARGE_FILE_SERVER = 2;
2826 const EMPTY_FILE = 3;
2927 const MIN_LENGTH_PARTNAME = 4;
3028 const ILLEGAL_FILENAME = 5;
31 - const PROTECTED_PAGE = 6;
3229 const OVERWRITE_EXISTING_FILE = 7;
3330 const FILETYPE_MISSING = 8;
3431 const FILETYPE_BADTYPE = 9;
3532 const VERIFICATION_ERROR = 10;
3633 const UPLOAD_VERIFICATION_ERROR = 11;
37 - const UPLOAD_WARNING = 12;
38 - const INTERNAL_ERROR = 13;
 34+ const HOOK_ABORTED = 11;
3935 const MIN_LENGHT_PARTNAME = 14;
4036
4137 const SESSION_VERSION = 2;
@@ -117,7 +113,7 @@
118114 $this->mFileSize = $fileSize;
119115 $this->mRemoveTempFile = $removeTempFile;
120116 }
121 -
 117+
122118 /**
123119 * Initialize from a WebRequest. Override this in a subclass.
124120 */
@@ -136,12 +132,12 @@
137133 public function isEmptyFile(){
138134 return empty( $this->mFileSize );
139135 }
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+ */
146142 function getRealPath( $srcPath ){
147143 $repo = RepoGroup::singleton()->getLocalRepo();
148144 if ( $repo->isVirtualUrl( $srcPath ) ) {
@@ -160,7 +156,21 @@
161157 */
162158 if( $this->isEmptyFile() )
163159 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 );
164172
 173+ }
 174+
165175 $nt = $this->getTitle();
166176 if( is_null( $nt ) ) {
167177 $result = array( 'status' => $this->mTitleError );
@@ -179,25 +189,11 @@
180190 if( $overwrite !== true )
181191 return array( 'status' => self::OVERWRITE_EXISTING_FILE, 'overwrite' => $overwrite );
182192
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 -
197193 $error = '';
198194 if( !wfRunHooks( 'UploadVerification',
199195 array( $this->mDestName, $this->mTempPath, &$error ) ) ) {
200196 // This status needs another name...
201 - return array( 'status' => self::UPLOAD_VERIFICATION_ERROR, 'error' => $error );
 197+ return array( 'status' => self::HOOK_ABORTED, 'error' => $error );
202198 }
203199
204200 return array( 'status' => self::OK );
@@ -221,7 +217,7 @@
222218
223219 #check mime type, if desired
224220 global $wgVerifyMimeType;
225 - if ( $wgVerifyMimeType ) {
 221+ if ( $wgVerifyMimeType ) {
226222 global $wgMimeTypeBlacklist;
227223 if ( $this->checkFileExtension( $mime, $wgMimeTypeBlacklist ) )
228224 return array( 'filetype-badmime', $mime );
@@ -262,7 +258,7 @@
263259
264260 /**
265261 * Check whether the user can edit, upload and create the image.
266 - *
 262+ *
267263 * @param User $user the user to verify the permissions against
268264 * @return mixed An array as returned by getUserPermissionsErrors or true
269265 * in case the user has proper permissions.
@@ -288,7 +284,7 @@
289285
290286 /**
291287 * Check for non fatal problems with the file
292 - *
 288+ *
293289 * @return array Array of warnings
294290 */
295291 public function checkWarnings() {
@@ -304,7 +300,10 @@
305301 * but ignore things like ucfirst() and spaces/underscore things
306302 */
307303 $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+ }
309308 if( $this->mDesiredDestName != $filename && $comparableName != $filename )
310309 $warnings['badfilename'] = $filename;
311310
@@ -348,9 +347,9 @@
349348 }
350349
351350 /**
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
353352 * if necessary and runs the UploadComplete hook.
354 - *
 353+ *
355354 * @return mixed Status indicating the whether the upload succeeded.
356355 */
357356 public function performUpload( $comment, $pageText, $watch, $user ) {
@@ -358,13 +357,8 @@
359358 $status = $this->getLocalFile()->upload( $this->mTempPath, $comment, $pageText,
360359 File::DELETE_SOURCE, $this->mFileProps, false, $user );
361360
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() );
369363
370364 if( $status->isGood() )
371365 wfRunHooks( 'UploadComplete', array( &$this ) );
@@ -375,7 +369,7 @@
376370 /**
377371 * Returns the title of the file to be uploaded. Sets mTitleError in case
378372 * the name was illegal.
379 - *
 373+ *
380374 * @return Title The title of the file or null in case the name was illegal
381375 */
382376 public function getTitle() {
@@ -444,7 +438,7 @@
445439 }
446440
447441 /**
448 - * Return the local file and initializes if necessary.
 442+ * Return the local file and initializes if necessary.
449443 */
450444 public function getLocalFile() {
451445 if( is_null( $this->mLocalFile ) ) {
@@ -472,9 +466,9 @@
473467 return $status;
474468 }
475469
476 - /**
 470+ /**
477471 * Append a file to a stashed file.
478 - *
 472+ *
479473 * @param string $srcPath Path to file to append from
480474 * @param string $toAppendPath Path to file to append to
481475 * @return Status Status
@@ -507,7 +501,7 @@
508502 'mFileSize' => $this->mFileSize,
509503 'mFileProps' => $this->mFileProps,
510504 'version' => self::SESSION_VERSION,
511 - );
 505+ );
512506 return $key;
513507 }
514508
@@ -520,15 +514,6 @@
521515 return $key;
522516 }
523517
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 - }
533518
534519 /**
535520 * If we've modified the upload file we need to manually remove it
@@ -817,9 +802,12 @@
818803 # that does not seem to be worth the pain.
819804 # Ask me (Duesentrieb) about it if it's ever needed.
820805 $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+ }
822811
823 -
824812 # map exit code to AV_xxx constants.
825813 $mappedCode = $exitCode;
826814 if ( $exitCodeMap ) {
@@ -830,7 +818,6 @@
831819 }
832820 }
833821
834 -
835822 if ( $mappedCode === AV_SCAN_FAILED ) {
836823 # scan failed (code was mapped to false by $exitCodeMap)
837824 wfDebug( __METHOD__ . ": failed to scan $file (code $exitCode).\n" );
@@ -912,9 +899,9 @@
913900 return true;
914901 }
915902
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+ */
919906 $file = wfFindFile( $this->getTitle() );
920907 if ( $file && !$wgUser->isAllowed( 'reupload-shared' ) )
921908 return 'fileexists-shared-forbidden';
@@ -944,13 +931,13 @@
945932
946933 /**
947934 * Helper function that does various existence checks for a file.
948 - * The following checks are performed:
 935+ * The following checks are performed:
949936 * - The file exists
950937 * - Article with the same name as the file exists
951938 * - File exists with normalized extension
952939 * - 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
955942 * @return mixed False if the file does not exists, else an array
956943 */
957944 public static function getExistsWarning( $file ) {
@@ -959,10 +946,10 @@
960947
961948 if( $file->getTitle()->getArticleID() )
962949 return array( 'warning' => 'page-exists', 'file' => $file );
963 -
 950+
964951 if ( $file->wasDeleted() && !$file->exists() )
965 - return array( 'warning' => 'was-deleted', 'file' => $file );
966 -
 952+ return array( 'warning' => 'was-deleted', 'file' => $file );
 953+
967954 if( strpos( $file->getName(), '.' ) == false ) {
968955 $partname = $file->getName();
969956 $extension = '';
@@ -996,15 +983,15 @@
997984 // File does not exist, but we just don't like the name
998985 return array( 'warning' => 'thumb-name', 'file' => $file, 'thumbFile' => $file_thb );
999986 }
 987+
1000988
1001 -
1002989 foreach( self::getFilenamePrefixBlacklist() as $prefix ) {
1003990 if ( substr( $partname, 0, strlen( $prefix ) ) == $prefix )
1004991 return array( 'warning' => 'bad-prefix', 'file' => $file, 'prefix' => $prefix );
1005992 }
 993+
1006994
1007995
1008 -
1009996 return false;
1010997 }
1011998
Index: trunk/phase3/includes/Licenses.php
@@ -9,46 +9,39 @@
1010 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
1111 */
1212
13 -class Licenses {
14 - /**#@+
15 - * @private
16 - */
 13+class Licenses extends HTMLFormField {
1714 /**
1815 * @var string
1916 */
20 - var $msg;
 17+ protected $msg;
2118
2219 /**
2320 * @var array
2421 */
25 - var $licenses = array();
 22+ protected $licenses = array();
2623
2724 /**
2825 * @var string
2926 */
30 - var $html;
 27+ protected $html;
3128 /**#@-*/
3229
3330 /**
3431 * Constructor
35 - *
36 - * @param $str String: the string to build the licenses member from, will use
37 - * wfMsgForContent( 'licenses' ) if null (default: null)
3832 */
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;
4338
4439 $this->makeLicenses();
45 - $tmp = $this->getLicenses();
46 - $this->makeHtml( $tmp );
4740 }
4841
4942 /**#@+
5043 * @private
5144 */
52 - function makeLicenses() {
 45+ protected function makeLicenses() {
5346 $levels = array();
5447 $lines = explode( "\n", $this->msg );
5548
@@ -75,18 +68,17 @@
7669 }
7770 }
7871
79 - function trimStars( $str ) {
 72+ protected static function trimStars( $str ) {
8073 $i = $count = 0;
8174
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+ }
8880 }
8981
90 - function stackItem( &$list, $path, $item ) {
 82+ protected function stackItem( &$list, $path, $item ) {
9183 $position =& $list;
9284 if ( $path )
9385 foreach( $path as $key )
@@ -94,13 +86,12 @@
9587 $position[] = $item;
9688 }
9789
98 - function makeHtml( &$tagset, $depth = 0 ) {
 90+ protected function makeHtml( &$tagset, $depth = 0 ) {
9991 foreach ( $tagset as $key => $val )
10092 if ( is_array( $val ) ) {
10193 $this->html .= $this->outputOption(
102 - $this->msg( $key ),
 94+ $this->msg( $key ), '',
10395 array(
104 - 'value' => '',
10596 'disabled' => 'disabled',
10697 'style' => 'color: GrayText', // for MSIE
10798 ),
@@ -109,22 +100,22 @@
110101 $this->makeHtml( $val, $depth + 1 );
111102 } else {
112103 $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 . '}}' ),
118106 $depth
119107 );
120108 }
121109 }
122110
123 - function outputOption( $val, $attribs = null, $depth ) {
124 - $val = str_repeat( /* &nbsp */ "\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( /* &nbsp */ "\xc2\xa0", $depth * 2 ) . $text;
125116 return str_repeat( "\t", $depth ) . Xml::element( 'option', $attribs, $val ) . "\n";
126117 }
127118
128 - function msg( $str ) {
 119+ protected function msg( $str ) {
129120 $out = wfMsg( $str );
130121 return wfEmptyMsg( $str, $out ) ? $str : $out;
131122 }
@@ -136,14 +127,29 @@
137128 *
138129 * @return array
139130 */
140 - function getLicenses() { return $this->licenses; }
 131+ public function getLicenses() { return $this->licenses; }
141132
142133 /**
143134 * Accessor for $this->html
144135 *
145136 * @return string
146137 */
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+ }
148154 }
149155
150156 /**
Index: trunk/phase3/includes/Setup.php
@@ -306,9 +306,9 @@
307307 $wgPostCommitUpdateList = array();
308308
309309 if ( $wgAjaxWatch ) $wgAjaxExportList[] = 'wfAjaxWatch';
310 -if ( $wgAjaxUploadDestCheck ) $wgAjaxExportList[] = 'UploadForm::ajaxGetExistsWarning';
 310+if ( $wgAjaxUploadDestCheck ) $wgAjaxExportList[] = 'SpecialUpload::ajaxGetExistsWarning';
311311 if( $wgAjaxLicensePreview )
312 - $wgAjaxExportList[] = 'UploadForm::ajaxGetLicensePreview';
 312+ $wgAjaxExportList[] = 'SpecialUpload::ajaxGetLicensePreview';
313313
314314 # Placeholders in case of DB error
315315 $wgTitle = null;
Index: trunk/phase3/includes/api/ApiUpload.php
@@ -230,7 +230,7 @@
231231 $this->dieUsage( 'This file did not pass file verification', 'verification-error',
232232 0, array( 'details' => $verification['details'] ) );
233233 break;
234 - case UploadBase::UPLOAD_VERIFICATION_ERROR:
 234+ case UploadBase::HOOK_ABORTED:
235235 $this->dieUsage( "The modification you tried to make was aborted by an extension hook",
236236 'hookaborted', 0, array( 'error' => $verification['error'] ) );
237237 break;
Index: trunk/phase3/includes/AutoLoader.php
@@ -550,6 +550,7 @@
551551 'SpecialSearch' => 'includes/specials/SpecialSearch.php',
552552 'SpecialStatistics' => 'includes/specials/SpecialStatistics.php',
553553 'SpecialTags' => 'includes/specials/SpecialTags.php',
 554+ 'SpecialUpload' => 'includes/specials/SpecialUpload.php',
554555 'SpecialVersion' => 'includes/specials/SpecialVersion.php',
555556 'SpecialWhatlinkshere' => 'includes/specials/SpecialWhatlinkshere.php',
556557 'UncategorizedCategoriesPage' => 'includes/specials/SpecialUncategorizedcategories.php',
Index: trunk/phase3/includes/specials/SpecialUpload.php
@@ -2,82 +2,80 @@
33 /**
44 * @file
55 * @ingroup SpecialPage
 6+ * @ingroup Upload
 7+ *
 8+ * Form for handling uploads and special page.
 9+ *
610 */
711
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 {
3113 /**
3214 * Constructor : initialise object
3315 * Get data POSTed through the form and assign them to the object
34 - * @param $request Data posted.
 16+ * @param WebRequest $request Data posted.
3517 */
36 - function __construct( $request = null ) {
 18+ public function __construct( $request = null ) {
 19+ global $wgRequest;
 20+
3721 parent::__construct( 'Upload', 'upload' );
38 - $this->mRequest = $request;
 22+
 23+ $this->loadRequest( is_null( $request ) ? $wgRequest : $request );
3924 }
 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;
4056
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+
4962 // Guess the desired name from the filename if not provided
5063 $this->mDesiredDestName = $request->getText( 'wpDestFile' );
5164 if( !$this->mDesiredDestName )
5265 $this->mDesiredDestName = $request->getText( 'wpUploadFile' );
53 -
54 - $this->mForReUpload = $request->getBool( 'wpForReUpload' ); // updating a file
55 - $this->mIgnoreWarning = $request->getCheck( 'wpIgnoreWarning' );
5666 $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' );
7172 $this->mCopyrightStatus = $request->getText( 'wpUploadCopyStatus' );
7273 $this->mCopyrightSource = $request->getText( 'wpUploadSource' );
73 - $this->mWatchthis = $request->getBool( 'wpWatchthis' );
74 - $this->mSourceType = $request->getVal( 'wpSourceType', 'file' );
75 - $this->mDestWarningAck = $request->getText( 'wpDestFileWarningAck' );
7674
77 - $this->mReUpload = $request->getCheck( 'wpReUpload' ); // retrying upload
7875
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+
8280 // If it was posted check for the token (no remote POST'ing with user credentials)
8381 $token = $request->getVal( 'wpEditToken' );
8482 if( $this->mSourceType == 'file' && $token == null ) {
@@ -89,23 +87,28 @@
9088 $this->mTokenOk = $wgUser->matchEditToken( $token );
9189 }
9290 }
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+ */
94100 public function userCanExecute( $user ) {
95101 return UploadBase::isEnabled() && parent::userCanExecute( $user );
96102 }
97 -
 103+
98104 /**
99 - * Start doing stuff
100 - * @access public
 105+ * Special page entry point
101106 */
102 - function execute( $par ) {
 107+ public function execute() {
103108 global $wgUser, $wgOut, $wgRequest;
104 -
 109+
105110 $this->setHeaders();
106111 $this->outputHeader();
107 -
108 - $this->initForm();
109 -
 112+
110113 # Check uploading enabled
111114 if( !UploadBase::isEnabled() ) {
112115 $wgOut->showErrorPage( 'uploaddisabled', 'uploaddisabledtext' );
@@ -131,91 +134,302 @@
132135 return;
133136 }
134137
 138+ # Check whether we actually want to allow changing stuff
135139 if( wfReadOnly() ) {
136140 $wgOut->readOnlyPage();
137141 return;
138142 }
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
151148 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 ) ) {
160154 $this->processUpload();
161155 } else {
162 - $this->mainUploadForm();
 156+ $this->showUploadForm( $this->getUploadForm() );
163157 }
164 -
165 - if( $this->mUpload )
 158+
 159+ # Cleanup
 160+ if ( $this->mUpload )
166161 $this->mUpload->cleanupTempFile();
167162 }
 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' ) );
168195
 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+
169204 /**
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.
171301 * 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.
172377 *
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.
176380 *
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.
178383 */
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+
182411 switch( $details['status'] ) {
183 - case UploadBase::SUCCESS:
184 - $wgOut->redirect( $this->mLocalFile->getTitle()->getFullURL() );
185 - break;
186412
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 **/
198414 case UploadBase::MIN_LENGTH_PARTNAME:
199 - $this->mainUploadForm( wfMsgHtml( 'minlength1' ) );
 415+ $this->recoverableUploadError( wfMsgHtml( 'minlength1' ) );
200416 break;
201 -
202417 case UploadBase::ILLEGAL_FILENAME:
203 - $this->uploadError( wfMsgExt( 'illegalfilename',
 418+ $this->recoverableUploadError( wfMsgExt( 'illegalfilename',
204419 'parseinline', $details['filtered'] ) );
205420 break;
206 -
207 - case UploadBase::PROTECTED_PAGE:
208 - $wgOut->showPermissionsErrorPage( $details['permissionserrors'] );
209 - break;
210 -
211421 case UploadBase::OVERWRITE_EXISTING_FILE:
212 - $this->uploadError( wfMsgExt( $details['overwrite'],
 422+ $this->recoverableUploadError( wfMsgExt( $details['overwrite'],
213423 'parseinline' ) );
214424 break;
215 -
216425 case UploadBase::FILETYPE_MISSING:
217 - $this->uploadError( wfMsgExt( 'filetype-missing', array ( 'parseinline' ) ) );
 426+ $this->recoverableUploadError( wfMsgExt( 'filetype-missing',
 427+ 'parseinline' ) );
218428 break;
219429
 430+ /** Statuses that require reuploading **/
 431+ case UploadBase::EMPTY_FILE:
 432+ $this->mainUploadForm( wfMsgHtml( 'emptyfile' ) );
 433+ break;
220434 case UploadBase::FILETYPE_BADTYPE:
221435 $finalExt = $details['finalExt'];
222436 $this->uploadError(
@@ -230,103 +444,40 @@
231445 )
232446 );
233447 break;
234 -
235448 case UploadBase::VERIFICATION_ERROR:
236449 unset( $details['status'] );
237450 $code = array_shift( $details['details'] );
238451 $this->uploadError( wfMsgExt( $code, 'parseinline', $details['details'] ) );
239452 break;
240 -
241 - case UploadBase::UPLOAD_VERIFICATION_ERROR:
 453+ case UploadBase::HOOK_ABORTED:
242454 $error = $details['error'];
243455 $this->uploadError( wfMsgExt( $error, 'parseinline' ) );
244456 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 -
256457 default:
257458 throw new MWException( __METHOD__ . ": Unknown value `{$details['status']}`" );
258459 }
259460 }
260 -
 461+
261462 /**
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().
267464 * @access private
 465+ * @return success
268466 */
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;
317475 } else {
318 - $pageText = false;
 476+ return true;
319477 }
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 - }
329478 }
330 -
 479+
 480+ /*** Functions for formatting warnings ***/
 481+
331482 /**
332483 * Formats a result of UploadBase::getExistsWarning as HTML
333484 * This check is static and can be done pre-upload via AJAX
@@ -361,7 +512,7 @@
362513 $warning[] = '<li>' . wfMsgExt( 'fileexists-thumbnail-yes', 'parseinline',
363514 $exists['thumbFile']->getTitle()->getPrefixedText(), $filename ) . '</li>';
364515 } 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
366517 $name = $file->getName();
367518 $badPart = substr( $name, 0, strpos( $name, '-' ) + 1 );
368519 $warning[] = '<li>' . wfMsgExt( 'file-thumbnail-no', 'parseinline', $badPart ) . '</li>';
@@ -391,7 +542,7 @@
392543 * @param string local filename, e.g. 'file exists', 'non-descriptive filename'
393544 * @return array list of warning messages
394545 */
395 - static function ajaxGetExistsWarning( $filename ) {
 546+ public static function ajaxGetExistsWarning( $filename ) {
396547 $file = wfFindFile( $filename );
397548 if( !$file ) {
398549 // Force local file so we have an object to do further checks against
@@ -402,7 +553,6 @@
403554 if ( $file ) {
404555 $exists = UploadBase::getExistsWarning( $file );
405556 $warning = self::getExistsWarning( $exists );
406 - // FIXME: We probably also want the prefix blacklist and the wasdeleted check here
407557 if ( $warning !== '' ) {
408558 $s = "<ul>$warning</ul>";
409559 }
@@ -430,7 +580,7 @@
431581 }
432582
433583 /**
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.
435585 */
436586 public static function getDupeWarning( $dupes ) {
437587 if( $dupes ) {
@@ -450,200 +600,122 @@
451601 return '';
452602 }
453603 }
 604+
 605+}
454606
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'];
469642 }
 643+
470644 }
471 -
472 - /* Interface code starts below this line *
473 - * -------------------------------------------------------------- */
474 -
475 -
 645+
476646 /**
477 - * @param string $error as HTML
478 - * @access private
 647+ *
479648 */
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+ );
503663 }
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+ );
533701 }
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,
558709 );
 710+ return $descriptor;
559711 }
560 -
 712+
561713 /**
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+ *
567715 */
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() {
645717 # Print a list of allowed file extensions, if so configured. We ignore
646718 # MIME type here, it's incomprehensible to most people and too long.
647 - global $wgCheckFileExtensions, $wgStrictFileExtensions,
 719+ global $wgLang, $wgCheckFileExtensions, $wgStrictFileExtensions,
648720 $wgFileExtensions, $wgFileBlacklist;
649721
650722 $allowedExtensions = '';
@@ -661,387 +733,202 @@
662734 wfMsgWikiHtml( 'upload-preferred', $wgLang->commaList( $wgFileExtensions ) ) .
663735 "</div>\n" .
664736 '<div id="mw-upload-prohibited">' .
665 - wfMsgWikiHtml( 'upload-prohibited', $wgLang->commaList( $wgFileExtensions ) ) .
 737+ wfMsgWikiHtml( 'upload-prohibited', $wgLang->commaList( $wgFileBlacklist ) ) .
666738 "</div>\n";
667739 }
668740 } else {
669741 # Everything is permitted.
670742 $extensionsList = '';
671743 }
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%; }' );
686756 }
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'>&nbsp;</td></tr>";
763 - } else {
764 - if ( $useAjaxDestCheck ) {
765 - $warningRow = "<tr><td colspan='2' id='wpDestFile-warning'>&nbsp;</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+ ),
804783 );
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'
811792 );
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',
817801 );
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',
853807 );
854 - if( $useAjaxLicensePreview ) {
855 - $wgOut->addHTML( "
856 - <td></td>
857 - <td id=\"mw-license-preview\"></td>
858 - </tr>
859 - <tr>"
860 - );
861 - }
862808 }
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;
928811 }
929 -
930 - /* -------------------------------------------------------------- */
931 -
 812+
932813 /**
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+ *
942815 */
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+
959848 }
960 -
 849+
961850 /**
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+ *
968852 */
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();
986856 }
987 -
 857+
988858 /**
989 - * Display an error with a wikitext description
 859+ *
990860 */
991 - function showError( $description ) {
 861+ protected function addUploadJS( $autofill = true ) {
 862+ global $wgUseAjax, $wgAjaxUploadDestCheck, $wgAjaxLicensePreview;
 863+ global $wgEnableFirefogg, $wgEnableJS2system;
992864 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+ );
999876
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' );
1014882 } 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
1022886 }
1023 - return $pageText;
1024887 }
 888+
 889+ function trySubmit() {
 890+ return false;
 891+ }
1025892
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'],
1044909 );
1045 - $out->addHTML( '</div>' );
 910+ if ( !empty( $this->mParams['checked'] ) )
 911+ $attribs['checked'] = 'checked';
 912+ $label .= Html::element( 'input', $attribs );
1046913 }
 914+
 915+ return Html::rawElement( 'td', array( 'class' => 'mw-label' ), $label );
1047916 }
 917+ function getSize() {
 918+ return isset( $this->mParams['size'] )
 919+ ? $this->mParams['size']
 920+ : 60;
 921+ }
1048922 }
 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 @@
142142 'Filepath' => array( 'SpecialPage', 'Filepath' ),
143143 'MIMEsearch' => array( 'SpecialPage', 'MIMEsearch' ),
144144 'FileDuplicateSearch' => array( 'SpecialPage', 'FileDuplicateSearch' ),
145 - 'Upload' => 'UploadForm',
 145+ 'Upload' => 'SpecialUpload',
146146
147147 # Wiki data and tools
148148 'Statistics' => 'SpecialStatistics',
Index: trunk/phase3/languages/messages/MessagesEn.php
@@ -2038,8 +2038,7 @@
20392039 # Upload
20402040 'upload' => 'Upload file',
20412041 '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 ',
20442043 'uploadnologin' => 'Not logged in',
20452044 'uploadnologintext' => 'You must be [[Special:UserLogin|logged in]] to upload files.',
20462045 'upload_directory_missing' => 'The upload directory ($1) is missing and could not be created by the webserver.',
@@ -2126,9 +2125,13 @@
21272126 'uploadcorrupt' => 'The file is corrupt or has an incorrect extension.
21282127 Please check the file and upload again.',
21292128 'uploadvirus' => 'The file contains a virus! Details: $1',
 2129+'upload-source' => 'Source file',
21302130 'sourcefilename' => 'Source filename:',
 2131+'sourceurl' => 'Source URL:',
21312132 'destfilename' => 'Destination filename:',
21322133 'upload-maxfilesize' => 'Maximum file size: $1',
 2134+'upload-description' => 'File description',
 2135+'upload-options' => '',
21332136 'watchthisupload' => 'Watch this file',
21342137 'filewasdeleted' => 'A file of this name has been previously uploaded and subsequently deleted.
21352138 You should check the $1 before proceeding to upload it again.',
Index: trunk/phase3/RELEASE-NOTES
@@ -249,7 +249,11 @@
250250 limit exceeded, __NOINDEX__ tracking, etc) can now be disabled by setting the
251251 system message ([[MediaWiki:expensive-parserfunction-category]] etc) to "-".
252252 * 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.
253256
 257+
254258 === Bug fixes in 1.16 ===
255259
256260 * (bug 18031) Make namespace selector on Special:Export remember the previous

Follow-up revisions

RevisionCommit summaryAuthorDate
r57869Follow-up r57868: Update language maintenance scriptsraymond19:54, 18 October 2009
r57870Followup to r57868: Fix two strict standards warningsbtongminh20:06, 18 October 2009
r57911Parse the edittools message so that it actually works (follow up to r57868)....btongminh19:55, 19 October 2009
r57935* fixed js2 fileexist checking related to rewrite of special upload page in r...dale23:50, 19 October 2009
r57991* Bump wgStyleVersion for various upload javascript edits in this revision an...btongminh21:29, 21 October 2009
r57996Followup to r57868: some maint scripts still call recordUpload which did not ...overlordq08:22, 22 October 2009
r58147Follow-up to r57868: Add edit.js unconditionally like EditPage does. This wil...btongminh16:54, 26 October 2009
r60232Fixed certain cases where license construction would be incorrect....btongminh21:23, 19 December 2009
r60277Restore hooks UploadForm:initial and UploadForm:BeforeProcessing removed in r...btongminh19:57, 21 December 2009
r60368Follow up to r57868: Fix virus scanner on Windows (r57801 et al)btongminh21:02, 24 December 2009
r64846(bug 23127) Fix for r57868, pass destination file name as $1 to uploadtext me...tstarling03:25, 10 April 2010
r71903Make SpecialUpload members public again...simetrical21:40, 29 August 2010
r71904Backport r71903 "Make SpecialUpload members public again"...simetrical21:45, 29 August 2010
r91566(follow-up r57868) Check if there exists a deleted page if we have a filename...bawolff17:27, 6 July 2011
r91594Remove 'upload-wasdeleted' message. Its no longer used....bawolff20:07, 6 July 2011

Comments

#Comment by Siebrand (talk | contribs)   20:00, 18 October 2009

PHP Strict Standards: Declaration of SpecialUpload::execute() should be compatible with that of SpecialPage::execute() in /var/www/w/includes/specials/SpecialUpload.php on line 604

#Comment by Raymond (talk | contribs)   20:23, 18 October 2009

Testing on translatewiki.net (user language: 'de'):

  • The check for existence of the filename is not triggered (related message: 'fileexists')
  • The characters of the edittools are not clickable
  • The box with the edittools should be in the "File description" fieldst (IMO)
  • The submit button is outside of every fieldset which looks inconsistent to all other forms
    • Suggestion: The submit button should be, together with the checkboxes, in the "File description" fieldst
#Comment by MaxSem (talk | contribs)   20:39, 18 October 2009

Strict Standards: Only variables should be passed by reference in C:\Projects\MediaWiki\includes\Licenses.php on line 142

#Comment by Nikerabbit (talk | contribs)   05:31, 19 October 2009

fname = fname.charAt(0).toUpperCase().concat(fname.substring(1,10000)).replace(/ /g, '_');

The second parameter to substring seems to be optional.

#Comment by Bryan (talk | contribs)   14:14, 21 October 2009
  • Strict standards fixed in r57870
  • Existence check in js2 mode fixed by mdale in r57935
  • Edittools fixed in r57911

The edittools should actually be moved entirely to the bottom, because it can be possibly a very large box.

I see I forgot the message for the checkboxes, upload-options; that should be "Upload options" or something similar. I could place the submit button into that fieldset.

#Comment by Raymond (talk | contribs)   20:31, 23 October 2009

Edittools: Click on any the characters on http://translatewiki.net/wiki/Special:Upload does not have an effect.

#Comment by Bryan (talk | contribs)   20:42, 23 October 2009

Has something to do with js2, it works without. I have no idea on how to fix it though, since all spaces seem to be removed from your JS, making it totally undebugable.

#Comment by MaxSem (talk | contribs)   06:22, 24 October 2009

$wgDebugJavaScript = true;

#Comment by Bryan (talk | contribs)   16:55, 26 October 2009

Should be fixed in r58147.

#Comment by Bryan (talk | contribs)   22:56, 5 December 2009

This incidentally reverted r57728, r57729, r57801; needs to be put back in.

#Comment by Tim Starling (talk | contribs)   08:30, 15 December 2009

A more detailed commit message would have made this change easier to review.

What happened to wpDestFileWarningAck?

-		$comparableName = Title::capitalize( $comparableName, NS_FILE );
+		global $wgCapitalLinks, $wgContLang;
+		if ( $wgCapitalLinks ) {
+			$comparableName = $wgContLang->ucfirst( $comparableName );
+		}

What is this about? Is it just a botched conflict merge?

-	function trimStars( $str ) {
+	protected static function trimStars( $str ) {

The only call of this function is non-static.

+		$length = strlen( $str );
+		for ( $i = 0; $i < $length; $i++ ) {
+			if ( $str[$i] != '*' )
+				return array( $i, ltrim( $str, '* ' ) );
+		}

PHP is not the sort of language where you want to be writing a loop that iterates through each character. You could use strspn here. Also the ltrim appears to be a shortcut, trimming strings like "* * * * *" instead of removing the initial sequence of asterisks like you intended. And the case where the string is all asterisks seems to be broken. Please don't take shortcuts that alter the function of the code. My suggestion is:

$numStars = strspn( $str, '*' );
return array( $numStars, ltrim( substr( $str, $numStars ), ' ' );
+		if( !$this->mTokenOk && !$this->mCancelUpload 
+				&& ( $this->mUpload && $this->mUploadClicked ) ) 
+			$form->addPreText( wfMsgExt( 'session_fail_preview', 'parseinline' ) );

Please read Manual:Coding conventions and write your multi-line conditionals in the usual way, i.e. with braces.

+		if( $title instanceof Title && ( $count = $title->isDeleted() ) > 0 && $wgUser->isAllowed( 'deletedhistory' ) ) {

Don't use assignment in an expression, it's hard to read and looks like an error. Break your lines at 100 characters or less.

We use verbs for functions, and nouns for variables:

  • recoverableUploadError() should be showRecoverableUploadError()
  • uploadWarning() should be showUploadWarning()
  • watchCheck() should be getWatchCheck()
+			return $wgOut->showPermissionsErrorPage( $permErrors );

Don't pretend to pass back the return value of a function that doesn't return a value, it's confusing. Several more instances.

The following extensions contain references to the UploadForm class or to one of the hooks you have removed here:

  • ImportFreeImages
  • MogileClient
  • MultiUpload
  • uniwiki/CustomToolbar
  • CategoryOnUpload
  • NSFileRepo
  • SelectCategory
  • SemanticForms

They are presumably all broken by this change. That's quite a tally for someone who claims to be doing extension authors a favour.


#Comment by Bryan (talk | contribs)   10:03, 15 December 2009

> What happened to wpDestFileWarningAck? It was commented out when i started working on it, so presuming that it has never worked, I removed it. It appears to have been introduced in mdale's branch merge in r53282.

> What is this about? Is it just a botched conflict merge? Yes, I'm not sure where that came from.


Thank you for taking the time reviewing this, I will reply on the remaining issues later.

#Comment by Tim Starling (talk | contribs)   23:44, 16 December 2009

No, wpDestFileWarningAck was introduced by me in r23608. I assure you it worked just fine at the time. The idea of it is that if the user is shown a destination name conflict warning via AJAX, they don't need to see a second one post-submit.

#Comment by Bryan (talk | contribs)   21:35, 21 December 2009

Code style fixes in r60232.

wpDestFileWarningAck restored in r60279 and also fixed the case that caused it to be outcommented in r53282.

As for extension compatibility I restored the hooks in r60277 and text injection points in r60280, so extensions that only add text to the upload form should be unbroken. I do think that a lot of extensions that depended on the fact that UploadForm was responsible for handling the upload itself were already broken by the earlier upload rewrite. And if we are breaking backwards compatibility with respect to uploading anyway in 1.16, it is the opportunity to also the front end with what I believe is a much saner implementation. I'll leave the judgement of the sanity of my implementations to you and others though ;)


#Comment by Tim Starling (talk | contribs)   06:59, 12 January 2010

Thanks for the fixes. Marking this resolved now.

Status & tagging log