MediaWiki
REL1_19
|
00001 <?php 00031 class SpecialUpload extends SpecialPage { 00037 public function __construct( $request = null ) { 00038 parent::__construct( 'Upload', 'upload' ); 00039 } 00040 00042 public $mRequest; // The WebRequest or FauxRequest this form is supposed to handle 00043 public $mSourceType; 00044 00048 public $mUpload; 00049 00053 public $mLocalFile; 00054 public $mUploadClicked; 00055 00057 public $mDesiredDestName; // The requested target file name 00058 public $mComment; 00059 public $mLicense; 00060 00062 public $mIgnoreWarning; 00063 public $mWatchThis; 00064 public $mCopyrightStatus; 00065 public $mCopyrightSource; 00066 00068 public $mDestWarningAck; 00069 public $mForReUpload; // The user followed an "overwrite this file" link 00070 public $mCancelUpload; // The user clicked "Cancel and return to upload form" button 00071 public $mTokenOk; 00072 public $mUploadSuccessful = false; // Subclasses can use this to determine whether a file was uploaded 00073 00075 public $uploadFormTextTop; 00076 public $uploadFormTextAfterSummary; 00077 00078 public $mWatchthis; 00079 00083 protected function loadRequest() { 00084 $this->mRequest = $request = $this->getRequest(); 00085 $this->mSourceType = $request->getVal( 'wpSourceType', 'file' ); 00086 $this->mUpload = UploadBase::createFromRequest( $request ); 00087 $this->mUploadClicked = $request->wasPosted() 00088 && ( $request->getCheck( 'wpUpload' ) 00089 || $request->getCheck( 'wpUploadIgnoreWarning' ) ); 00090 00091 // Guess the desired name from the filename if not provided 00092 $this->mDesiredDestName = $request->getText( 'wpDestFile' ); 00093 if( !$this->mDesiredDestName && $request->getFileName( 'wpUploadFile' ) !== null ) { 00094 $this->mDesiredDestName = $request->getFileName( 'wpUploadFile' ); 00095 } 00096 $this->mComment = $request->getText( 'wpUploadDescription' ); 00097 $this->mLicense = $request->getText( 'wpLicense' ); 00098 00099 00100 $this->mDestWarningAck = $request->getText( 'wpDestFileWarningAck' ); 00101 $this->mIgnoreWarning = $request->getCheck( 'wpIgnoreWarning' ) 00102 || $request->getCheck( 'wpUploadIgnoreWarning' ); 00103 $this->mWatchthis = $request->getBool( 'wpWatchthis' ) && $this->getUser()->isLoggedIn(); 00104 $this->mCopyrightStatus = $request->getText( 'wpUploadCopyStatus' ); 00105 $this->mCopyrightSource = $request->getText( 'wpUploadSource' ); 00106 00107 00108 $this->mForReUpload = $request->getBool( 'wpForReUpload' ); // updating a file 00109 $this->mCancelUpload = $request->getCheck( 'wpCancelUpload' ) 00110 || $request->getCheck( 'wpReUpload' ); // b/w compat 00111 00112 // If it was posted check for the token (no remote POST'ing with user credentials) 00113 $token = $request->getVal( 'wpEditToken' ); 00114 $this->mTokenOk = $this->getUser()->matchEditToken( $token ); 00115 00116 $this->uploadFormTextTop = ''; 00117 $this->uploadFormTextAfterSummary = ''; 00118 } 00119 00128 public function userCanExecute( User $user ) { 00129 return UploadBase::isEnabled() && parent::userCanExecute( $user ); 00130 } 00131 00135 public function execute( $par ) { 00136 $this->setHeaders(); 00137 $this->outputHeader(); 00138 00139 # Check uploading enabled 00140 if( !UploadBase::isEnabled() ) { 00141 throw new ErrorPageError( 'uploaddisabled', 'uploaddisabledtext' ); 00142 } 00143 00144 # Check permissions 00145 $user = $this->getUser(); 00146 $permissionRequired = UploadBase::isAllowed( $user ); 00147 if( $permissionRequired !== true ) { 00148 throw new PermissionsError( $permissionRequired ); 00149 } 00150 00151 # Check blocks 00152 if( $user->isBlocked() ) { 00153 throw new UserBlockedError( $user->mBlock ); 00154 } 00155 00156 # Check whether we actually want to allow changing stuff 00157 $this->checkReadOnly(); 00158 00159 $this->loadRequest(); 00160 00161 # Unsave the temporary file in case this was a cancelled upload 00162 if ( $this->mCancelUpload ) { 00163 if ( !$this->unsaveUploadedFile() ) { 00164 # Something went wrong, so unsaveUploadedFile showed a warning 00165 return; 00166 } 00167 } 00168 00169 # Process upload or show a form 00170 if ( 00171 $this->mTokenOk && !$this->mCancelUpload && 00172 ( $this->mUpload && $this->mUploadClicked ) 00173 ) { 00174 $this->processUpload(); 00175 } else { 00176 # Backwards compatibility hook 00177 if( !wfRunHooks( 'UploadForm:initial', array( &$this ) ) ) { 00178 wfDebug( "Hook 'UploadForm:initial' broke output of the upload form" ); 00179 return; 00180 } 00181 $this->showUploadForm( $this->getUploadForm() ); 00182 } 00183 00184 # Cleanup 00185 if ( $this->mUpload ) { 00186 $this->mUpload->cleanupTempFile(); 00187 } 00188 } 00189 00195 protected function showUploadForm( $form ) { 00196 # Add links if file was previously deleted 00197 if ( $this->mDesiredDestName ) { 00198 $this->showViewDeletedLinks(); 00199 } 00200 00201 if ( $form instanceof HTMLForm ) { 00202 $form->show(); 00203 } else { 00204 $this->getOutput()->addHTML( $form ); 00205 } 00206 00207 } 00208 00217 protected function getUploadForm( $message = '', $sessionKey = '', $hideIgnoreWarning = false ) { 00218 # Initialize form 00219 $form = new UploadForm( array( 00220 'watch' => $this->getWatchCheck(), 00221 'forreupload' => $this->mForReUpload, 00222 'sessionkey' => $sessionKey, 00223 'hideignorewarning' => $hideIgnoreWarning, 00224 'destwarningack' => (bool)$this->mDestWarningAck, 00225 00226 'description' => $this->mComment, 00227 'texttop' => $this->uploadFormTextTop, 00228 'textaftersummary' => $this->uploadFormTextAfterSummary, 00229 'destfile' => $this->mDesiredDestName, 00230 ), $this->getContext() ); 00231 $form->setTitle( $this->getTitle() ); 00232 00233 # Check the token, but only if necessary 00234 if( 00235 !$this->mTokenOk && !$this->mCancelUpload && 00236 ( $this->mUpload && $this->mUploadClicked ) 00237 ) { 00238 $form->addPreText( wfMsgExt( 'session_fail_preview', 'parseinline' ) ); 00239 } 00240 00241 # Give a notice if the user is uploading a file that has been deleted or moved 00242 # Note that this is independent from the message 'filewasdeleted' that requires JS 00243 $desiredTitleObj = Title::makeTitleSafe( NS_FILE, $this->mDesiredDestName ); 00244 $delNotice = ''; // empty by default 00245 if ( $desiredTitleObj instanceof Title && !$desiredTitleObj->exists() ) { 00246 LogEventsList::showLogExtract( $delNotice, array( 'delete', 'move' ), 00247 $desiredTitleObj, 00248 '', array( 'lim' => 10, 00249 'conds' => array( "log_action != 'revision'" ), 00250 'showIfEmpty' => false, 00251 'msgKey' => array( 'upload-recreate-warning' ) ) 00252 ); 00253 } 00254 $form->addPreText( $delNotice ); 00255 00256 # Add text to form 00257 $form->addPreText( '<div id="uploadtext">' . 00258 wfMsgExt( 'uploadtext', 'parse', array( $this->mDesiredDestName ) ) . 00259 '</div>' ); 00260 # Add upload error message 00261 $form->addPreText( $message ); 00262 00263 # Add footer to form 00264 $uploadFooter = wfMessage( 'uploadfooter' ); 00265 if ( !$uploadFooter->isDisabled() ) { 00266 $form->addPostText( '<div id="mw-upload-footer-message">' 00267 . $this->getOutput()->parse( $uploadFooter->plain() ) . "</div>\n" ); 00268 } 00269 00270 return $form; 00271 } 00272 00276 protected function showViewDeletedLinks() { 00277 $title = Title::makeTitleSafe( NS_FILE, $this->mDesiredDestName ); 00278 $user = $this->getUser(); 00279 // Show a subtitle link to deleted revisions (to sysops et al only) 00280 if( $title instanceof Title ) { 00281 $count = $title->isDeleted(); 00282 if ( $count > 0 && $user->isAllowed( 'deletedhistory' ) ) { 00283 $link = wfMsgExt( 00284 $user->isAllowed( 'delete' ) ? 'thisisdeleted' : 'viewdeleted', 00285 array( 'parse', 'replaceafter' ), 00286 Linker::linkKnown( 00287 SpecialPage::getTitleFor( 'Undelete', $title->getPrefixedText() ), 00288 wfMsgExt( 'restorelink', array( 'parsemag', 'escape' ), $count ) 00289 ) 00290 ); 00291 $this->getOutput()->addHTML( "<div id=\"contentSub2\">{$link}</div>" ); 00292 } 00293 } 00294 } 00295 00307 protected function showRecoverableUploadError( $message ) { 00308 $sessionKey = $this->mUpload->stashSession(); 00309 $message = '<h2>' . wfMsgHtml( 'uploaderror' ) . "</h2>\n" . 00310 '<div class="error">' . $message . "</div>\n"; 00311 00312 $form = $this->getUploadForm( $message, $sessionKey ); 00313 $form->setSubmitText( wfMsg( 'upload-tryagain' ) ); 00314 $this->showUploadForm( $form ); 00315 } 00324 protected function showUploadWarning( $warnings ) { 00325 # If there are no warnings, or warnings we can ignore, return early. 00326 # mDestWarningAck is set when some javascript has shown the warning 00327 # to the user. mForReUpload is set when the user clicks the "upload a 00328 # new version" link. 00329 if ( !$warnings || ( count( $warnings ) == 1 && 00330 isset( $warnings['exists'] ) && 00331 ( $this->mDestWarningAck || $this->mForReUpload ) ) ) 00332 { 00333 return false; 00334 } 00335 00336 $sessionKey = $this->mUpload->stashSession(); 00337 00338 $warningHtml = '<h2>' . wfMsgHtml( 'uploadwarning' ) . "</h2>\n" 00339 . '<ul class="warning">'; 00340 foreach( $warnings as $warning => $args ) { 00341 if( $warning == 'exists' ) { 00342 $msg = "\t<li>" . self::getExistsWarning( $args ) . "</li>\n"; 00343 } elseif( $warning == 'duplicate' ) { 00344 $msg = self::getDupeWarning( $args ); 00345 } elseif( $warning == 'duplicate-archive' ) { 00346 $msg = "\t<li>" . wfMsgExt( 'file-deleted-duplicate', 'parseinline', 00347 array( Title::makeTitle( NS_FILE, $args )->getPrefixedText() ) ) 00348 . "</li>\n"; 00349 } else { 00350 if ( $args === true ) { 00351 $args = array(); 00352 } elseif ( !is_array( $args ) ) { 00353 $args = array( $args ); 00354 } 00355 $msg = "\t<li>" . wfMsgExt( $warning, 'parseinline', $args ) . "</li>\n"; 00356 } 00357 $warningHtml .= $msg; 00358 } 00359 $warningHtml .= "</ul>\n"; 00360 $warningHtml .= wfMsgExt( 'uploadwarning-text', 'parse' ); 00361 00362 $form = $this->getUploadForm( $warningHtml, $sessionKey, /* $hideIgnoreWarning */ true ); 00363 $form->setSubmitText( wfMsg( 'upload-tryagain' ) ); 00364 $form->addButton( 'wpUploadIgnoreWarning', wfMsg( 'ignorewarning' ) ); 00365 $form->addButton( 'wpCancelUpload', wfMsg( 'reuploaddesc' ) ); 00366 00367 $this->showUploadForm( $form ); 00368 00369 # Indicate that we showed a form 00370 return true; 00371 } 00372 00378 protected function showUploadError( $message ) { 00379 $message = '<h2>' . wfMsgHtml( 'uploadwarning' ) . "</h2>\n" . 00380 '<div class="error">' . $message . "</div>\n"; 00381 $this->showUploadForm( $this->getUploadForm( $message ) ); 00382 } 00383 00388 protected function processUpload() { 00389 // Fetch the file if required 00390 $status = $this->mUpload->fetchFile(); 00391 if( !$status->isOK() ) { 00392 $this->showUploadError( $this->getOutput()->parse( $status->getWikiText() ) ); 00393 return; 00394 } 00395 00396 if( !wfRunHooks( 'UploadForm:BeforeProcessing', array( &$this ) ) ) { 00397 wfDebug( "Hook 'UploadForm:BeforeProcessing' broke processing the file.\n" ); 00398 // This code path is deprecated. If you want to break upload processing 00399 // do so by hooking into the appropriate hooks in UploadBase::verifyUpload 00400 // and UploadBase::verifyFile. 00401 // If you use this hook to break uploading, the user will be returned 00402 // an empty form with no error message whatsoever. 00403 return; 00404 } 00405 00406 // Upload verification 00407 $details = $this->mUpload->verifyUpload(); 00408 if ( $details['status'] != UploadBase::OK ) { 00409 $this->processVerificationError( $details ); 00410 return; 00411 } 00412 00413 // Verify permissions for this title 00414 $permErrors = $this->mUpload->verifyTitlePermissions( $this->getUser() ); 00415 if( $permErrors !== true ) { 00416 $code = array_shift( $permErrors[0] ); 00417 $this->showRecoverableUploadError( wfMsgExt( $code, 00418 'parseinline', $permErrors[0] ) ); 00419 return; 00420 } 00421 00422 $this->mLocalFile = $this->mUpload->getLocalFile(); 00423 00424 // Check warnings if necessary 00425 if( !$this->mIgnoreWarning ) { 00426 $warnings = $this->mUpload->checkWarnings(); 00427 if( $this->showUploadWarning( $warnings ) ) { 00428 return; 00429 } 00430 } 00431 00432 // Get the page text if this is not a reupload 00433 if( !$this->mForReUpload ) { 00434 $pageText = self::getInitialPageText( $this->mComment, $this->mLicense, 00435 $this->mCopyrightStatus, $this->mCopyrightSource ); 00436 } else { 00437 $pageText = false; 00438 } 00439 $status = $this->mUpload->performUpload( $this->mComment, $pageText, $this->mWatchthis, $this->getUser() ); 00440 if ( !$status->isGood() ) { 00441 $this->showUploadError( $this->getOutput()->parse( $status->getWikiText() ) ); 00442 return; 00443 } 00444 00445 // Success, redirect to description page 00446 $this->mUploadSuccessful = true; 00447 wfRunHooks( 'SpecialUploadComplete', array( &$this ) ); 00448 $this->getOutput()->redirect( $this->mLocalFile->getTitle()->getFullURL() ); 00449 } 00450 00459 public static function getInitialPageText( $comment = '', $license = '', $copyStatus = '', $source = '' ) { 00460 global $wgUseCopyrightUpload, $wgForceUIMsgAsContentMsg; 00461 $wgForceUIMsgAsContentMsg = (array) $wgForceUIMsgAsContentMsg; 00462 00463 $msg = array(); 00464 /* These messages are transcluded into the actual text of the description page. 00465 * Thus, forcing them as content messages makes the upload to produce an int: template 00466 * instead of hardcoding it there in the uploader language. 00467 */ 00468 foreach( array( 'license-header', 'filedesc', 'filestatus', 'filesource' ) as $msgName ) { 00469 if ( in_array( $msgName, $wgForceUIMsgAsContentMsg ) ) { 00470 $msg[$msgName] = "{{int:$msgName}}"; 00471 } else { 00472 $msg[$msgName] = wfMsgForContent( $msgName ); 00473 } 00474 } 00475 00476 if ( $wgUseCopyrightUpload ) { 00477 $licensetxt = ''; 00478 if ( $license != '' ) { 00479 $licensetxt = '== ' . $msg[ 'license-header' ] . " ==\n" . '{{' . $license . '}}' . "\n"; 00480 } 00481 $pageText = '== ' . $msg[ 'filedesc' ] . " ==\n" . $comment . "\n" . 00482 '== ' . $msg[ 'filestatus' ] . " ==\n" . $copyStatus . "\n" . 00483 "$licensetxt" . 00484 '== ' . $msg[ 'filesource' ] . " ==\n" . $source; 00485 } else { 00486 if ( $license != '' ) { 00487 $filedesc = $comment == '' ? '' : '== ' . $msg[ 'filedesc' ] . " ==\n" . $comment . "\n"; 00488 $pageText = $filedesc . 00489 '== ' . $msg[ 'license-header' ] . " ==\n" . '{{' . $license . '}}' . "\n"; 00490 } else { 00491 $pageText = $comment; 00492 } 00493 } 00494 return $pageText; 00495 } 00496 00509 protected function getWatchCheck() { 00510 if( $this->getUser()->getOption( 'watchdefault' ) ) { 00511 // Watch all edits! 00512 return true; 00513 } 00514 00515 $local = wfLocalFile( $this->mDesiredDestName ); 00516 if( $local && $local->exists() ) { 00517 // We're uploading a new version of an existing file. 00518 // No creation, so don't watch it if we're not already. 00519 return $local->getTitle()->userIsWatching(); 00520 } else { 00521 // New page should get watched if that's our option. 00522 return $this->getUser()->getOption( 'watchcreations' ); 00523 } 00524 } 00525 00526 00532 protected function processVerificationError( $details ) { 00533 global $wgFileExtensions; 00534 00535 switch( $details['status'] ) { 00536 00538 case UploadBase::MIN_LENGTH_PARTNAME: 00539 $this->showRecoverableUploadError( wfMsgHtml( 'minlength1' ) ); 00540 break; 00541 case UploadBase::ILLEGAL_FILENAME: 00542 $this->showRecoverableUploadError( wfMsgExt( 'illegalfilename', 00543 'parseinline', $details['filtered'] ) ); 00544 break; 00545 case UploadBase::FILENAME_TOO_LONG: 00546 $this->showRecoverableUploadError( wfMsgHtml( 'filename-toolong' ) ); 00547 break; 00548 case UploadBase::FILETYPE_MISSING: 00549 $this->showRecoverableUploadError( wfMsgExt( 'filetype-missing', 00550 'parseinline' ) ); 00551 break; 00552 case UploadBase::WINDOWS_NONASCII_FILENAME: 00553 $this->showRecoverableUploadError( wfMsgExt( 'windows-nonascii-filename', 00554 'parseinline' ) ); 00555 break; 00556 00558 case UploadBase::EMPTY_FILE: 00559 $this->showUploadError( wfMsgHtml( 'emptyfile' ) ); 00560 break; 00561 case UploadBase::FILE_TOO_LARGE: 00562 $this->showUploadError( wfMsgHtml( 'largefileserver' ) ); 00563 break; 00564 case UploadBase::FILETYPE_BADTYPE: 00565 $msg = wfMessage( 'filetype-banned-type' ); 00566 if ( isset( $details['blacklistedExt'] ) ) { 00567 $msg->params( $this->getLanguage()->commaList( $details['blacklistedExt'] ) ); 00568 } else { 00569 $msg->params( $details['finalExt'] ); 00570 } 00571 $msg->params( $this->getLanguage()->commaList( $wgFileExtensions ), 00572 count( $wgFileExtensions ) ); 00573 00574 // Add PLURAL support for the first parameter. This results 00575 // in a bit unlogical parameter sequence, but does not break 00576 // old translations 00577 if ( isset( $details['blacklistedExt'] ) ) { 00578 $msg->params( count( $details['blacklistedExt'] ) ); 00579 } else { 00580 $msg->params( 1 ); 00581 } 00582 00583 $this->showUploadError( $msg->parse() ); 00584 break; 00585 case UploadBase::VERIFICATION_ERROR: 00586 unset( $details['status'] ); 00587 $code = array_shift( $details['details'] ); 00588 $this->showUploadError( wfMsgExt( $code, 'parseinline', $details['details'] ) ); 00589 break; 00590 case UploadBase::HOOK_ABORTED: 00591 if ( is_array( $details['error'] ) ) { # allow hooks to return error details in an array 00592 $args = $details['error']; 00593 $error = array_shift( $args ); 00594 } else { 00595 $error = $details['error']; 00596 $args = null; 00597 } 00598 00599 $this->showUploadError( wfMsgExt( $error, 'parseinline', $args ) ); 00600 break; 00601 default: 00602 throw new MWException( __METHOD__ . ": Unknown value `{$details['status']}`" ); 00603 } 00604 } 00605 00611 protected function unsaveUploadedFile() { 00612 if ( !( $this->mUpload instanceof UploadFromStash ) ) { 00613 return true; 00614 } 00615 $success = $this->mUpload->unsaveUploadedFile(); 00616 if ( !$success ) { 00617 $this->getOutput()->showFileDeleteError( $this->mUpload->getTempPath() ); 00618 return false; 00619 } else { 00620 return true; 00621 } 00622 } 00623 00624 /*** Functions for formatting warnings ***/ 00625 00633 public static function getExistsWarning( $exists ) { 00634 if ( !$exists ) { 00635 return ''; 00636 } 00637 00638 $file = $exists['file']; 00639 $filename = $file->getTitle()->getPrefixedText(); 00640 $warning = ''; 00641 00642 if( $exists['warning'] == 'exists' ) { 00643 // Exact match 00644 $warning = wfMsgExt( 'fileexists', 'parseinline', $filename ); 00645 } elseif( $exists['warning'] == 'page-exists' ) { 00646 // Page exists but file does not 00647 $warning = wfMsgExt( 'filepageexists', 'parseinline', $filename ); 00648 } elseif ( $exists['warning'] == 'exists-normalized' ) { 00649 $warning = wfMsgExt( 'fileexists-extension', 'parseinline', $filename, 00650 $exists['normalizedFile']->getTitle()->getPrefixedText() ); 00651 } elseif ( $exists['warning'] == 'thumb' ) { 00652 // Swapped argument order compared with other messages for backwards compatibility 00653 $warning = wfMsgExt( 'fileexists-thumbnail-yes', 'parseinline', 00654 $exists['thumbFile']->getTitle()->getPrefixedText(), $filename ); 00655 } elseif ( $exists['warning'] == 'thumb-name' ) { 00656 // Image w/o '180px-' does not exists, but we do not like these filenames 00657 $name = $file->getName(); 00658 $badPart = substr( $name, 0, strpos( $name, '-' ) + 1 ); 00659 $warning = wfMsgExt( 'file-thumbnail-no', 'parseinline', $badPart ); 00660 } elseif ( $exists['warning'] == 'bad-prefix' ) { 00661 $warning = wfMsgExt( 'filename-bad-prefix', 'parseinline', $exists['prefix'] ); 00662 } elseif ( $exists['warning'] == 'was-deleted' ) { 00663 # If the file existed before and was deleted, warn the user of this 00664 $ltitle = SpecialPage::getTitleFor( 'Log' ); 00665 $llink = Linker::linkKnown( 00666 $ltitle, 00667 wfMsgHtml( 'deletionlog' ), 00668 array(), 00669 array( 00670 'type' => 'delete', 00671 'page' => $filename 00672 ) 00673 ); 00674 $warning = wfMsgExt( 'filewasdeleted', array( 'parse', 'replaceafter' ), $llink ); 00675 } 00676 00677 return $warning; 00678 } 00679 00686 public static function ajaxGetExistsWarning( $filename ) { 00687 $file = wfFindFile( $filename ); 00688 if( !$file ) { 00689 // Force local file so we have an object to do further checks against 00690 // if there isn't an exact match... 00691 $file = wfLocalFile( $filename ); 00692 } 00693 $s = ' '; 00694 if ( $file ) { 00695 $exists = UploadBase::getExistsWarning( $file ); 00696 $warning = self::getExistsWarning( $exists ); 00697 if ( $warning !== '' ) { 00698 $s = "<div>$warning</div>"; 00699 } 00700 } 00701 return $s; 00702 } 00703 00709 public static function getDupeWarning( $dupes ) { 00710 global $wgOut; 00711 if( $dupes ) { 00712 $msg = '<gallery>'; 00713 foreach( $dupes as $file ) { 00714 $title = $file->getTitle(); 00715 $msg .= $title->getPrefixedText() . 00716 '|' . $title->getText() . "\n"; 00717 } 00718 $msg .= '</gallery>'; 00719 return '<li>' . 00720 wfMsgExt( 'file-exists-duplicate', array( 'parse' ), count( $dupes ) ) . 00721 $wgOut->parse( $msg ) . 00722 "</li>\n"; 00723 } else { 00724 return ''; 00725 } 00726 } 00727 00728 } 00729 00733 class UploadForm extends HTMLForm { 00734 protected $mWatch; 00735 protected $mForReUpload; 00736 protected $mSessionKey; 00737 protected $mHideIgnoreWarning; 00738 protected $mDestWarningAck; 00739 protected $mDestFile; 00740 00741 protected $mComment; 00742 protected $mTextTop; 00743 protected $mTextAfterSummary; 00744 00745 protected $mSourceIds; 00746 00747 protected $mMaxFileSize = array(); 00748 00749 protected $mMaxUploadSize = array(); 00750 00751 public function __construct( array $options = array(), IContextSource $context = null ) { 00752 $this->mWatch = !empty( $options['watch'] ); 00753 $this->mForReUpload = !empty( $options['forreupload'] ); 00754 $this->mSessionKey = isset( $options['sessionkey'] ) 00755 ? $options['sessionkey'] : ''; 00756 $this->mHideIgnoreWarning = !empty( $options['hideignorewarning'] ); 00757 $this->mDestWarningAck = !empty( $options['destwarningack'] ); 00758 $this->mDestFile = isset( $options['destfile'] ) ? $options['destfile'] : ''; 00759 00760 $this->mComment = isset( $options['description'] ) ? 00761 $options['description'] : ''; 00762 00763 $this->mTextTop = isset( $options['texttop'] ) 00764 ? $options['texttop'] : ''; 00765 00766 $this->mTextAfterSummary = isset( $options['textaftersummary'] ) 00767 ? $options['textaftersummary'] : ''; 00768 00769 $sourceDescriptor = $this->getSourceSection(); 00770 $descriptor = $sourceDescriptor 00771 + $this->getDescriptionSection() 00772 + $this->getOptionsSection(); 00773 00774 wfRunHooks( 'UploadFormInitDescriptor', array( &$descriptor ) ); 00775 parent::__construct( $descriptor, $context, 'upload' ); 00776 00777 # Set some form properties 00778 $this->setSubmitText( wfMsg( 'uploadbtn' ) ); 00779 $this->setSubmitName( 'wpUpload' ); 00780 # Used message keys: 'accesskey-upload', 'tooltip-upload' 00781 $this->setSubmitTooltip( 'upload' ); 00782 $this->setId( 'mw-upload-form' ); 00783 00784 # Build a list of IDs for javascript insertion 00785 $this->mSourceIds = array(); 00786 foreach ( $sourceDescriptor as $field ) { 00787 if ( !empty( $field['id'] ) ) { 00788 $this->mSourceIds[] = $field['id']; 00789 } 00790 } 00791 00792 } 00793 00800 protected function getSourceSection() { 00801 if ( $this->mSessionKey ) { 00802 return array( 00803 'SessionKey' => array( 00804 'type' => 'hidden', 00805 'default' => $this->mSessionKey, 00806 ), 00807 'SourceType' => array( 00808 'type' => 'hidden', 00809 'default' => 'Stash', 00810 ), 00811 ); 00812 } 00813 00814 $canUploadByUrl = UploadFromUrl::isEnabled() && UploadFromUrl::isAllowed( $this->getUser() ); 00815 $radio = $canUploadByUrl; 00816 $selectedSourceType = strtolower( $this->getRequest()->getText( 'wpSourceType', 'File' ) ); 00817 00818 $descriptor = array(); 00819 if ( $this->mTextTop ) { 00820 $descriptor['UploadFormTextTop'] = array( 00821 'type' => 'info', 00822 'section' => 'source', 00823 'default' => $this->mTextTop, 00824 'raw' => true, 00825 ); 00826 } 00827 00828 $this->mMaxUploadSize['file'] = UploadBase::getMaxUploadSize( 'file' ); 00829 # Limit to upload_max_filesize unless we are running under HipHop and 00830 # that setting doesn't exist 00831 if ( !wfIsHipHop() ) { 00832 $this->mMaxUploadSize['file'] = min( $this->mMaxUploadSize['file'], 00833 wfShorthandToInteger( ini_get( 'upload_max_filesize' ) ) ); 00834 } 00835 00836 $descriptor['UploadFile'] = array( 00837 'class' => 'UploadSourceField', 00838 'section' => 'source', 00839 'type' => 'file', 00840 'id' => 'wpUploadFile', 00841 'label-message' => 'sourcefilename', 00842 'upload-type' => 'File', 00843 'radio' => &$radio, 00844 'help' => wfMsgExt( 'upload-maxfilesize', 00845 array( 'parseinline', 'escapenoentities' ), 00846 $this->getContext()->getLanguage()->formatSize( $this->mMaxUploadSize['file'] ) 00847 ) . ' ' . wfMsgHtml( 'upload_source_file' ), 00848 'checked' => $selectedSourceType == 'file', 00849 ); 00850 if ( $canUploadByUrl ) { 00851 $this->mMaxUploadSize['url'] = UploadBase::getMaxUploadSize( 'url' ); 00852 $descriptor['UploadFileURL'] = array( 00853 'class' => 'UploadSourceField', 00854 'section' => 'source', 00855 'id' => 'wpUploadFileURL', 00856 'label-message' => 'sourceurl', 00857 'upload-type' => 'url', 00858 'radio' => &$radio, 00859 'help' => wfMsgExt( 'upload-maxfilesize', 00860 array( 'parseinline', 'escapenoentities' ), 00861 $this->getContext()->getLanguage()->formatSize( $this->mMaxUploadSize['url'] ) 00862 ) . ' ' . wfMsgHtml( 'upload_source_url' ), 00863 'checked' => $selectedSourceType == 'url', 00864 ); 00865 } 00866 wfRunHooks( 'UploadFormSourceDescriptors', array( &$descriptor, &$radio, $selectedSourceType ) ); 00867 00868 $descriptor['Extensions'] = array( 00869 'type' => 'info', 00870 'section' => 'source', 00871 'default' => $this->getExtensionsMessage(), 00872 'raw' => true, 00873 ); 00874 return $descriptor; 00875 } 00876 00882 protected function getExtensionsMessage() { 00883 # Print a list of allowed file extensions, if so configured. We ignore 00884 # MIME type here, it's incomprehensible to most people and too long. 00885 global $wgCheckFileExtensions, $wgStrictFileExtensions, 00886 $wgFileExtensions, $wgFileBlacklist; 00887 00888 if( $wgCheckFileExtensions ) { 00889 if( $wgStrictFileExtensions ) { 00890 # Everything not permitted is banned 00891 $extensionsList = 00892 '<div id="mw-upload-permitted">' . 00893 wfMsgExt( 'upload-permitted', 'parse', $this->getContext()->getLanguage()->commaList( $wgFileExtensions ) ) . 00894 "</div>\n"; 00895 } else { 00896 # We have to list both preferred and prohibited 00897 $extensionsList = 00898 '<div id="mw-upload-preferred">' . 00899 wfMsgExt( 'upload-preferred', 'parse', $this->getContext()->getLanguage()->commaList( $wgFileExtensions ) ) . 00900 "</div>\n" . 00901 '<div id="mw-upload-prohibited">' . 00902 wfMsgExt( 'upload-prohibited', 'parse', $this->getContext()->getLanguage()->commaList( $wgFileBlacklist ) ) . 00903 "</div>\n"; 00904 } 00905 } else { 00906 # Everything is permitted. 00907 $extensionsList = ''; 00908 } 00909 return $extensionsList; 00910 } 00911 00918 protected function getDescriptionSection() { 00919 if ( $this->mSessionKey ) { 00920 $stash = RepoGroup::singleton()->getLocalRepo()->getUploadStash(); 00921 try { 00922 $file = $stash->getFile( $this->mSessionKey ); 00923 } catch ( MWException $e ) { 00924 $file = null; 00925 } 00926 if ( $file ) { 00927 global $wgContLang; 00928 00929 $mto = $file->transform( array( 'width' => 120 ) ); 00930 $this->addHeaderText( 00931 '<div class="thumb t' . $wgContLang->alignEnd() . '">' . 00932 Html::element( 'img', array( 00933 'src' => $mto->getUrl(), 00934 'class' => 'thumbimage', 00935 ) ) . '</div>', 'description' ); 00936 } 00937 } 00938 00939 $descriptor = array( 00940 'DestFile' => array( 00941 'type' => 'text', 00942 'section' => 'description', 00943 'id' => 'wpDestFile', 00944 'label-message' => 'destfilename', 00945 'size' => 60, 00946 'default' => $this->mDestFile, 00947 # @todo FIXME: Hack to work around poor handling of the 'default' option in HTMLForm 00948 'nodata' => strval( $this->mDestFile ) !== '', 00949 ), 00950 'UploadDescription' => array( 00951 'type' => 'textarea', 00952 'section' => 'description', 00953 'id' => 'wpUploadDescription', 00954 'label-message' => $this->mForReUpload 00955 ? 'filereuploadsummary' 00956 : 'fileuploadsummary', 00957 'default' => $this->mComment, 00958 'cols' => intval( $this->getUser()->getOption( 'cols' ) ), 00959 'rows' => 8, 00960 ) 00961 ); 00962 if ( $this->mTextAfterSummary ) { 00963 $descriptor['UploadFormTextAfterSummary'] = array( 00964 'type' => 'info', 00965 'section' => 'description', 00966 'default' => $this->mTextAfterSummary, 00967 'raw' => true, 00968 ); 00969 } 00970 00971 $descriptor += array( 00972 'EditTools' => array( 00973 'type' => 'edittools', 00974 'section' => 'description', 00975 'message' => 'edittools-upload', 00976 ) 00977 ); 00978 00979 if ( $this->mForReUpload ) { 00980 $descriptor['DestFile']['readonly'] = true; 00981 } else { 00982 $descriptor['License'] = array( 00983 'type' => 'select', 00984 'class' => 'Licenses', 00985 'section' => 'description', 00986 'id' => 'wpLicense', 00987 'label-message' => 'license', 00988 ); 00989 } 00990 00991 global $wgUseCopyrightUpload; 00992 if ( $wgUseCopyrightUpload ) { 00993 $descriptor['UploadCopyStatus'] = array( 00994 'type' => 'text', 00995 'section' => 'description', 00996 'id' => 'wpUploadCopyStatus', 00997 'label-message' => 'filestatus', 00998 ); 00999 $descriptor['UploadSource'] = array( 01000 'type' => 'text', 01001 'section' => 'description', 01002 'id' => 'wpUploadSource', 01003 'label-message' => 'filesource', 01004 ); 01005 } 01006 01007 return $descriptor; 01008 } 01009 01016 protected function getOptionsSection() { 01017 $user = $this->getUser(); 01018 if ( $user->isLoggedIn() ) { 01019 $descriptor = array( 01020 'Watchthis' => array( 01021 'type' => 'check', 01022 'id' => 'wpWatchthis', 01023 'label-message' => 'watchthisupload', 01024 'section' => 'options', 01025 'default' => $user->getOption( 'watchcreations' ), 01026 ) 01027 ); 01028 } 01029 if ( !$this->mHideIgnoreWarning ) { 01030 $descriptor['IgnoreWarning'] = array( 01031 'type' => 'check', 01032 'id' => 'wpIgnoreWarning', 01033 'label-message' => 'ignorewarnings', 01034 'section' => 'options', 01035 ); 01036 } 01037 01038 $descriptor['DestFileWarningAck'] = array( 01039 'type' => 'hidden', 01040 'id' => 'wpDestFileWarningAck', 01041 'default' => $this->mDestWarningAck ? '1' : '', 01042 ); 01043 01044 if ( $this->mForReUpload ) { 01045 $descriptor['ForReUpload'] = array( 01046 'type' => 'hidden', 01047 'id' => 'wpForReUpload', 01048 'default' => '1', 01049 ); 01050 } 01051 01052 return $descriptor; 01053 } 01054 01058 public function show() { 01059 $this->addUploadJS(); 01060 parent::show(); 01061 } 01062 01066 protected function addUploadJS() { 01067 global $wgUseAjax, $wgAjaxUploadDestCheck, $wgAjaxLicensePreview, $wgEnableAPI, $wgStrictFileExtensions; 01068 01069 $useAjaxDestCheck = $wgUseAjax && $wgAjaxUploadDestCheck; 01070 $useAjaxLicensePreview = $wgUseAjax && $wgAjaxLicensePreview && $wgEnableAPI; 01071 $this->mMaxUploadSize['*'] = UploadBase::getMaxUploadSize(); 01072 01073 $scriptVars = array( 01074 'wgAjaxUploadDestCheck' => $useAjaxDestCheck, 01075 'wgAjaxLicensePreview' => $useAjaxLicensePreview, 01076 'wgUploadAutoFill' => !$this->mForReUpload && 01077 // If we received mDestFile from the request, don't autofill 01078 // the wpDestFile textbox 01079 $this->mDestFile === '', 01080 'wgUploadSourceIds' => $this->mSourceIds, 01081 'wgStrictFileExtensions' => $wgStrictFileExtensions, 01082 'wgCapitalizeUploads' => MWNamespace::isCapitalized( NS_FILE ), 01083 'wgMaxUploadSize' => $this->mMaxUploadSize, 01084 ); 01085 01086 $out = $this->getOutput(); 01087 $out->addJsConfigVars( $scriptVars ); 01088 01089 01090 $out->addModules( array( 01091 'mediawiki.action.edit', // For <charinsert> support 01092 'mediawiki.legacy.upload', // Old form stuff... 01093 'mediawiki.special.upload', // Newer extras for thumbnail preview. 01094 ) ); 01095 } 01096 01102 function trySubmit() { 01103 return false; 01104 } 01105 01106 } 01107 01111 class UploadSourceField extends HTMLTextField { 01112 01117 function getLabelHtml( $cellAttributes = array() ) { 01118 $id = "wpSourceType{$this->mParams['upload-type']}"; 01119 $label = Html::rawElement( 'label', array( 'for' => $id ), $this->mLabel ); 01120 01121 if ( !empty( $this->mParams['radio'] ) ) { 01122 $attribs = array( 01123 'name' => 'wpSourceType', 01124 'type' => 'radio', 01125 'id' => $id, 01126 'value' => $this->mParams['upload-type'], 01127 ); 01128 if ( !empty( $this->mParams['checked'] ) ) { 01129 $attribs['checked'] = 'checked'; 01130 } 01131 $label .= Html::element( 'input', $attribs ); 01132 } 01133 01134 return Html::rawElement( 'td', array( 'class' => 'mw-label' ) + $cellAttributes, $label ); 01135 } 01136 01140 function getSize() { 01141 return isset( $this->mParams['size'] ) 01142 ? $this->mParams['size'] 01143 : 60; 01144 } 01145 } 01146