MediaWiki
master
|
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->getBlock() ); 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( $this->msg( 'session_fail_preview' )->parse() ); 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 $this->msg( 'uploadtext', array( $this->mDesiredDestName ) )->parseAsBlock() . 00259 '</div>' ); 00260 # Add upload error message 00261 $form->addPreText( $message ); 00262 00263 # Add footer to form 00264 $uploadFooter = $this->msg( 'uploadfooter' ); 00265 if ( !$uploadFooter->isDisabled() ) { 00266 $form->addPostText( '<div id="mw-upload-footer-message">' 00267 . $uploadFooter->parseAsBlock() . "</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 $restorelink = Linker::linkKnown( 00284 SpecialPage::getTitleFor( 'Undelete', $title->getPrefixedText() ), 00285 $this->msg( 'restorelink' )->numParams( $count )->escaped() 00286 ); 00287 $link = $this->msg( $user->isAllowed( 'delete' ) ? 'thisisdeleted' : 'viewdeleted' ) 00288 ->rawParams( $restorelink )->parseAsBlock(); 00289 $this->getOutput()->addHTML( "<div id=\"contentSub2\">{$link}</div>" ); 00290 } 00291 } 00292 } 00293 00305 protected function showRecoverableUploadError( $message ) { 00306 $sessionKey = $this->mUpload->stashSession(); 00307 $message = '<h2>' . $this->msg( 'uploaderror' )->escaped() . "</h2>\n" . 00308 '<div class="error">' . $message . "</div>\n"; 00309 00310 $form = $this->getUploadForm( $message, $sessionKey ); 00311 $form->setSubmitText( $this->msg( 'upload-tryagain' )->escaped() ); 00312 $this->showUploadForm( $form ); 00313 } 00322 protected function showUploadWarning( $warnings ) { 00323 # If there are no warnings, or warnings we can ignore, return early. 00324 # mDestWarningAck is set when some javascript has shown the warning 00325 # to the user. mForReUpload is set when the user clicks the "upload a 00326 # new version" link. 00327 if ( !$warnings || ( count( $warnings ) == 1 && 00328 isset( $warnings['exists'] ) && 00329 ( $this->mDestWarningAck || $this->mForReUpload ) ) ) 00330 { 00331 return false; 00332 } 00333 00334 $sessionKey = $this->mUpload->stashSession(); 00335 00336 $warningHtml = '<h2>' . $this->msg( 'uploadwarning' )->escaped() . "</h2>\n" 00337 . '<ul class="warning">'; 00338 foreach( $warnings as $warning => $args ) { 00339 if( $warning == 'badfilename' ) { 00340 $this->mDesiredDestName = Title::makeTitle( NS_FILE, $args )->getText(); 00341 } 00342 if( $warning == 'exists' ) { 00343 $msg = "\t<li>" . self::getExistsWarning( $args ) . "</li>\n"; 00344 } elseif( $warning == 'duplicate' ) { 00345 $msg = self::getDupeWarning( $args ); 00346 } elseif( $warning == 'duplicate-archive' ) { 00347 $msg = "\t<li>" . $this->msg( 'file-deleted-duplicate', 00348 Title::makeTitle( NS_FILE, $args )->getPrefixedText() )->parse() 00349 . "</li>\n"; 00350 } else { 00351 if ( $args === true ) { 00352 $args = array(); 00353 } elseif ( !is_array( $args ) ) { 00354 $args = array( $args ); 00355 } 00356 $msg = "\t<li>" . $this->msg( $warning, $args )->parse() . "</li>\n"; 00357 } 00358 $warningHtml .= $msg; 00359 } 00360 $warningHtml .= "</ul>\n"; 00361 $warningHtml .= $this->msg( 'uploadwarning-text' )->parseAsBlock(); 00362 00363 $form = $this->getUploadForm( $warningHtml, $sessionKey, /* $hideIgnoreWarning */ true ); 00364 $form->setSubmitText( $this->msg( 'upload-tryagain' )->text() ); 00365 $form->addButton( 'wpUploadIgnoreWarning', $this->msg( 'ignorewarning' )->text() ); 00366 $form->addButton( 'wpCancelUpload', $this->msg( 'reuploaddesc' )->text() ); 00367 00368 $this->showUploadForm( $form ); 00369 00370 # Indicate that we showed a form 00371 return true; 00372 } 00373 00379 protected function showUploadError( $message ) { 00380 $message = '<h2>' . $this->msg( 'uploadwarning' )->escaped() . "</h2>\n" . 00381 '<div class="error">' . $message . "</div>\n"; 00382 $this->showUploadForm( $this->getUploadForm( $message ) ); 00383 } 00384 00389 protected function processUpload() { 00390 // Fetch the file if required 00391 $status = $this->mUpload->fetchFile(); 00392 if( !$status->isOK() ) { 00393 $this->showUploadError( $this->getOutput()->parse( $status->getWikiText() ) ); 00394 return; 00395 } 00396 00397 if( !wfRunHooks( 'UploadForm:BeforeProcessing', array( &$this ) ) ) { 00398 wfDebug( "Hook 'UploadForm:BeforeProcessing' broke processing the file.\n" ); 00399 // This code path is deprecated. If you want to break upload processing 00400 // do so by hooking into the appropriate hooks in UploadBase::verifyUpload 00401 // and UploadBase::verifyFile. 00402 // If you use this hook to break uploading, the user will be returned 00403 // an empty form with no error message whatsoever. 00404 return; 00405 } 00406 00407 // Upload verification 00408 $details = $this->mUpload->verifyUpload(); 00409 if ( $details['status'] != UploadBase::OK ) { 00410 $this->processVerificationError( $details ); 00411 return; 00412 } 00413 00414 // Verify permissions for this title 00415 $permErrors = $this->mUpload->verifyTitlePermissions( $this->getUser() ); 00416 if( $permErrors !== true ) { 00417 $code = array_shift( $permErrors[0] ); 00418 $this->showRecoverableUploadError( $this->msg( $code, $permErrors[0] )->parse() ); 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] = wfMessage( $msgName )->inContentLanguage()->text(); 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 $this->getUser()->isWatched( $local->getTitle() ); 00520 } else { 00521 // New page should get watched if that's our option. 00522 return $this->getUser()->getOption( 'watchcreations' ); 00523 } 00524 } 00525 00526 00533 protected function processVerificationError( $details ) { 00534 global $wgFileExtensions; 00535 00536 switch( $details['status'] ) { 00537 00539 case UploadBase::MIN_LENGTH_PARTNAME: 00540 $this->showRecoverableUploadError( $this->msg( 'minlength1' )->escaped() ); 00541 break; 00542 case UploadBase::ILLEGAL_FILENAME: 00543 $this->showRecoverableUploadError( $this->msg( 'illegalfilename', 00544 $details['filtered'] )->parse() ); 00545 break; 00546 case UploadBase::FILENAME_TOO_LONG: 00547 $this->showRecoverableUploadError( $this->msg( 'filename-toolong' )->escaped() ); 00548 break; 00549 case UploadBase::FILETYPE_MISSING: 00550 $this->showRecoverableUploadError( $this->msg( 'filetype-missing' )->parse() ); 00551 break; 00552 case UploadBase::WINDOWS_NONASCII_FILENAME: 00553 $this->showRecoverableUploadError( $this->msg( 'windows-nonascii-filename' )->parse() ); 00554 break; 00555 00557 case UploadBase::EMPTY_FILE: 00558 $this->showUploadError( $this->msg( 'emptyfile' )->escaped() ); 00559 break; 00560 case UploadBase::FILE_TOO_LARGE: 00561 $this->showUploadError( $this->msg( 'largefileserver' )->escaped() ); 00562 break; 00563 case UploadBase::FILETYPE_BADTYPE: 00564 $msg = $this->msg( 'filetype-banned-type' ); 00565 if ( isset( $details['blacklistedExt'] ) ) { 00566 $msg->params( $this->getLanguage()->commaList( $details['blacklistedExt'] ) ); 00567 } else { 00568 $msg->params( $details['finalExt'] ); 00569 } 00570 $msg->params( $this->getLanguage()->commaList( $wgFileExtensions ), 00571 count( $wgFileExtensions ) ); 00572 00573 // Add PLURAL support for the first parameter. This results 00574 // in a bit unlogical parameter sequence, but does not break 00575 // old translations 00576 if ( isset( $details['blacklistedExt'] ) ) { 00577 $msg->params( count( $details['blacklistedExt'] ) ); 00578 } else { 00579 $msg->params( 1 ); 00580 } 00581 00582 $this->showUploadError( $msg->parse() ); 00583 break; 00584 case UploadBase::VERIFICATION_ERROR: 00585 unset( $details['status'] ); 00586 $code = array_shift( $details['details'] ); 00587 $this->showUploadError( $this->msg( $code, $details['details'] )->parse() ); 00588 break; 00589 case UploadBase::HOOK_ABORTED: 00590 if ( is_array( $details['error'] ) ) { # allow hooks to return error details in an array 00591 $args = $details['error']; 00592 $error = array_shift( $args ); 00593 } else { 00594 $error = $details['error']; 00595 $args = null; 00596 } 00597 00598 $this->showUploadError( $this->msg( $error, $args )->parse() ); 00599 break; 00600 default: 00601 throw new MWException( __METHOD__ . ": Unknown value `{$details['status']}`" ); 00602 } 00603 } 00604 00610 protected function unsaveUploadedFile() { 00611 if ( !( $this->mUpload instanceof UploadFromStash ) ) { 00612 return true; 00613 } 00614 $success = $this->mUpload->unsaveUploadedFile(); 00615 if ( !$success ) { 00616 $this->getOutput()->showFileDeleteError( $this->mUpload->getTempPath() ); 00617 return false; 00618 } else { 00619 return true; 00620 } 00621 } 00622 00623 /*** Functions for formatting warnings ***/ 00624 00632 public static function getExistsWarning( $exists ) { 00633 if ( !$exists ) { 00634 return ''; 00635 } 00636 00637 $file = $exists['file']; 00638 $filename = $file->getTitle()->getPrefixedText(); 00639 $warning = ''; 00640 00641 if( $exists['warning'] == 'exists' ) { 00642 // Exact match 00643 $warning = wfMessage( 'fileexists', $filename )->parse(); 00644 } elseif( $exists['warning'] == 'page-exists' ) { 00645 // Page exists but file does not 00646 $warning = wfMessage( 'filepageexists', $filename )->parse(); 00647 } elseif ( $exists['warning'] == 'exists-normalized' ) { 00648 $warning = wfMessage( 'fileexists-extension', $filename, 00649 $exists['normalizedFile']->getTitle()->getPrefixedText() )->parse(); 00650 } elseif ( $exists['warning'] == 'thumb' ) { 00651 // Swapped argument order compared with other messages for backwards compatibility 00652 $warning = wfMessage( 'fileexists-thumbnail-yes', 00653 $exists['thumbFile']->getTitle()->getPrefixedText(), $filename )->parse(); 00654 } elseif ( $exists['warning'] == 'thumb-name' ) { 00655 // Image w/o '180px-' does not exists, but we do not like these filenames 00656 $name = $file->getName(); 00657 $badPart = substr( $name, 0, strpos( $name, '-' ) + 1 ); 00658 $warning = wfMessage( 'file-thumbnail-no', $badPart )->parse(); 00659 } elseif ( $exists['warning'] == 'bad-prefix' ) { 00660 $warning = wfMessage( 'filename-bad-prefix', $exists['prefix'] )->parse(); 00661 } elseif ( $exists['warning'] == 'was-deleted' ) { 00662 # If the file existed before and was deleted, warn the user of this 00663 $ltitle = SpecialPage::getTitleFor( 'Log' ); 00664 $llink = Linker::linkKnown( 00665 $ltitle, 00666 wfMessage( 'deletionlog' )->escaped(), 00667 array(), 00668 array( 00669 'type' => 'delete', 00670 'page' => $filename 00671 ) 00672 ); 00673 $warning = wfMessage( 'filewasdeleted' )->rawParams( $llink )->parseAsBlock(); 00674 } 00675 00676 return $warning; 00677 } 00678 00685 public static function ajaxGetExistsWarning( $filename ) { 00686 $file = wfFindFile( $filename ); 00687 if( !$file ) { 00688 // Force local file so we have an object to do further checks against 00689 // if there isn't an exact match... 00690 $file = wfLocalFile( $filename ); 00691 } 00692 $s = ' '; 00693 if ( $file ) { 00694 $exists = UploadBase::getExistsWarning( $file ); 00695 $warning = self::getExistsWarning( $exists ); 00696 if ( $warning !== '' ) { 00697 $s = "<div>$warning</div>"; 00698 } 00699 } 00700 return $s; 00701 } 00702 00708 public static function getDupeWarning( $dupes ) { 00709 if ( !$dupes ) { 00710 return ''; 00711 } 00712 00713 $gallery = new ImageGallery; 00714 $gallery->setShowBytes( false ); 00715 foreach( $dupes as $file ) { 00716 $gallery->add( $file->getTitle() ); 00717 } 00718 return '<li>' . 00719 wfMessage( 'file-exists-duplicate' )->numParams( count( $dupes ) )->parse() . 00720 $gallery->toHtml() . "</li>\n"; 00721 } 00722 00723 } 00724 00728 class UploadForm extends HTMLForm { 00729 protected $mWatch; 00730 protected $mForReUpload; 00731 protected $mSessionKey; 00732 protected $mHideIgnoreWarning; 00733 protected $mDestWarningAck; 00734 protected $mDestFile; 00735 00736 protected $mComment; 00737 protected $mTextTop; 00738 protected $mTextAfterSummary; 00739 00740 protected $mSourceIds; 00741 00742 protected $mMaxFileSize = array(); 00743 00744 protected $mMaxUploadSize = array(); 00745 00746 public function __construct( array $options = array(), IContextSource $context = null ) { 00747 $this->mWatch = !empty( $options['watch'] ); 00748 $this->mForReUpload = !empty( $options['forreupload'] ); 00749 $this->mSessionKey = isset( $options['sessionkey'] ) 00750 ? $options['sessionkey'] : ''; 00751 $this->mHideIgnoreWarning = !empty( $options['hideignorewarning'] ); 00752 $this->mDestWarningAck = !empty( $options['destwarningack'] ); 00753 $this->mDestFile = isset( $options['destfile'] ) ? $options['destfile'] : ''; 00754 00755 $this->mComment = isset( $options['description'] ) ? 00756 $options['description'] : ''; 00757 00758 $this->mTextTop = isset( $options['texttop'] ) 00759 ? $options['texttop'] : ''; 00760 00761 $this->mTextAfterSummary = isset( $options['textaftersummary'] ) 00762 ? $options['textaftersummary'] : ''; 00763 00764 $sourceDescriptor = $this->getSourceSection(); 00765 $descriptor = $sourceDescriptor 00766 + $this->getDescriptionSection() 00767 + $this->getOptionsSection(); 00768 00769 wfRunHooks( 'UploadFormInitDescriptor', array( &$descriptor ) ); 00770 parent::__construct( $descriptor, $context, 'upload' ); 00771 00772 # Set some form properties 00773 $this->setSubmitText( $this->msg( 'uploadbtn' )->text() ); 00774 $this->setSubmitName( 'wpUpload' ); 00775 # Used message keys: 'accesskey-upload', 'tooltip-upload' 00776 $this->setSubmitTooltip( 'upload' ); 00777 $this->setId( 'mw-upload-form' ); 00778 00779 # Build a list of IDs for javascript insertion 00780 $this->mSourceIds = array(); 00781 foreach ( $sourceDescriptor as $field ) { 00782 if ( !empty( $field['id'] ) ) { 00783 $this->mSourceIds[] = $field['id']; 00784 } 00785 } 00786 00787 } 00788 00795 protected function getSourceSection() { 00796 global $wgCopyUploadsFromSpecialUpload; 00797 00798 if ( $this->mSessionKey ) { 00799 return array( 00800 'SessionKey' => array( 00801 'type' => 'hidden', 00802 'default' => $this->mSessionKey, 00803 ), 00804 'SourceType' => array( 00805 'type' => 'hidden', 00806 'default' => 'Stash', 00807 ), 00808 ); 00809 } 00810 00811 $canUploadByUrl = UploadFromUrl::isEnabled() 00812 && UploadFromUrl::isAllowed( $this->getUser() ) 00813 && $wgCopyUploadsFromSpecialUpload; 00814 $radio = $canUploadByUrl; 00815 $selectedSourceType = strtolower( $this->getRequest()->getText( 'wpSourceType', 'File' ) ); 00816 00817 $descriptor = array(); 00818 if ( $this->mTextTop ) { 00819 $descriptor['UploadFormTextTop'] = array( 00820 'type' => 'info', 00821 'section' => 'source', 00822 'default' => $this->mTextTop, 00823 'raw' => true, 00824 ); 00825 } 00826 00827 $this->mMaxUploadSize['file'] = UploadBase::getMaxUploadSize( 'file' ); 00828 # Limit to upload_max_filesize unless we are running under HipHop and 00829 # that setting doesn't exist 00830 if ( !wfIsHipHop() ) { 00831 $this->mMaxUploadSize['file'] = min( $this->mMaxUploadSize['file'], 00832 wfShorthandToInteger( ini_get( 'upload_max_filesize' ) ), 00833 wfShorthandToInteger( ini_get( 'post_max_size' ) ) 00834 ); 00835 } 00836 00837 $descriptor['UploadFile'] = array( 00838 'class' => 'UploadSourceField', 00839 'section' => 'source', 00840 'type' => 'file', 00841 'id' => 'wpUploadFile', 00842 'label-message' => 'sourcefilename', 00843 'upload-type' => 'File', 00844 'radio' => &$radio, 00845 'help' => $this->msg( 'upload-maxfilesize', 00846 $this->getContext()->getLanguage()->formatSize( $this->mMaxUploadSize['file'] ) 00847 )->parse() . ' ' . $this->msg( 'upload_source_file' )->escaped(), 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' => $this->msg( 'upload-maxfilesize', 00860 $this->getContext()->getLanguage()->formatSize( $this->mMaxUploadSize['url'] ) 00861 )->parse() . ' ' . $this->msg( 'upload_source_url' )->escaped(), 00862 'checked' => $selectedSourceType == 'url', 00863 ); 00864 } 00865 wfRunHooks( 'UploadFormSourceDescriptors', array( &$descriptor, &$radio, $selectedSourceType ) ); 00866 00867 $descriptor['Extensions'] = array( 00868 'type' => 'info', 00869 'section' => 'source', 00870 'default' => $this->getExtensionsMessage(), 00871 'raw' => true, 00872 ); 00873 return $descriptor; 00874 } 00875 00881 protected function getExtensionsMessage() { 00882 # Print a list of allowed file extensions, if so configured. We ignore 00883 # MIME type here, it's incomprehensible to most people and too long. 00884 global $wgCheckFileExtensions, $wgStrictFileExtensions, 00885 $wgFileExtensions, $wgFileBlacklist; 00886 00887 if( $wgCheckFileExtensions ) { 00888 if( $wgStrictFileExtensions ) { 00889 # Everything not permitted is banned 00890 $extensionsList = 00891 '<div id="mw-upload-permitted">' . 00892 $this->msg( 'upload-permitted', $this->getContext()->getLanguage()->commaList( $wgFileExtensions ) )->parseAsBlock() . 00893 "</div>\n"; 00894 } else { 00895 # We have to list both preferred and prohibited 00896 $extensionsList = 00897 '<div id="mw-upload-preferred">' . 00898 $this->msg( 'upload-preferred', $this->getContext()->getLanguage()->commaList( $wgFileExtensions ) )->parseAsBlock() . 00899 "</div>\n" . 00900 '<div id="mw-upload-prohibited">' . 00901 $this->msg( 'upload-prohibited', $this->getContext()->getLanguage()->commaList( $wgFileBlacklist ) )->parseAsBlock() . 00902 "</div>\n"; 00903 } 00904 } else { 00905 # Everything is permitted. 00906 $extensionsList = ''; 00907 } 00908 return $extensionsList; 00909 } 00910 00917 protected function getDescriptionSection() { 00918 if ( $this->mSessionKey ) { 00919 $stash = RepoGroup::singleton()->getLocalRepo()->getUploadStash(); 00920 try { 00921 $file = $stash->getFile( $this->mSessionKey ); 00922 } catch ( MWException $e ) { 00923 $file = null; 00924 } 00925 if ( $file ) { 00926 global $wgContLang; 00927 00928 $mto = $file->transform( array( 'width' => 120 ) ); 00929 $this->addHeaderText( 00930 '<div class="thumb t' . $wgContLang->alignEnd() . '">' . 00931 Html::element( 'img', array( 00932 'src' => $mto->getUrl(), 00933 'class' => 'thumbimage', 00934 ) ) . '</div>', 'description' ); 00935 } 00936 } 00937 00938 $descriptor = array( 00939 'DestFile' => array( 00940 'type' => 'text', 00941 'section' => 'description', 00942 'id' => 'wpDestFile', 00943 'label-message' => 'destfilename', 00944 'size' => 60, 00945 'default' => $this->mDestFile, 00946 # @todo FIXME: Hack to work around poor handling of the 'default' option in HTMLForm 00947 'nodata' => strval( $this->mDestFile ) !== '', 00948 ), 00949 'UploadDescription' => array( 00950 'type' => 'textarea', 00951 'section' => 'description', 00952 'id' => 'wpUploadDescription', 00953 'label-message' => $this->mForReUpload 00954 ? 'filereuploadsummary' 00955 : 'fileuploadsummary', 00956 'default' => $this->mComment, 00957 'cols' => intval( $this->getUser()->getOption( 'cols' ) ), 00958 'rows' => 8, 00959 ) 00960 ); 00961 if ( $this->mTextAfterSummary ) { 00962 $descriptor['UploadFormTextAfterSummary'] = array( 00963 'type' => 'info', 00964 'section' => 'description', 00965 'default' => $this->mTextAfterSummary, 00966 'raw' => true, 00967 ); 00968 } 00969 00970 $descriptor += array( 00971 'EditTools' => array( 00972 'type' => 'edittools', 00973 'section' => 'description', 00974 'message' => 'edittools-upload', 00975 ) 00976 ); 00977 00978 if ( $this->mForReUpload ) { 00979 $descriptor['DestFile']['readonly'] = true; 00980 } else { 00981 $descriptor['License'] = array( 00982 'type' => 'select', 00983 'class' => 'Licenses', 00984 'section' => 'description', 00985 'id' => 'wpLicense', 00986 'label-message' => 'license', 00987 ); 00988 } 00989 00990 global $wgUseCopyrightUpload; 00991 if ( $wgUseCopyrightUpload ) { 00992 $descriptor['UploadCopyStatus'] = array( 00993 'type' => 'text', 00994 'section' => 'description', 00995 'id' => 'wpUploadCopyStatus', 00996 'label-message' => 'filestatus', 00997 ); 00998 $descriptor['UploadSource'] = array( 00999 'type' => 'text', 01000 'section' => 'description', 01001 'id' => 'wpUploadSource', 01002 'label-message' => 'filesource', 01003 ); 01004 } 01005 01006 return $descriptor; 01007 } 01008 01015 protected function getOptionsSection() { 01016 $user = $this->getUser(); 01017 if ( $user->isLoggedIn() ) { 01018 $descriptor = array( 01019 'Watchthis' => array( 01020 'type' => 'check', 01021 'id' => 'wpWatchthis', 01022 'label-message' => 'watchthisupload', 01023 'section' => 'options', 01024 'default' => $user->getOption( 'watchcreations' ), 01025 ) 01026 ); 01027 } 01028 if ( !$this->mHideIgnoreWarning ) { 01029 $descriptor['IgnoreWarning'] = array( 01030 'type' => 'check', 01031 'id' => 'wpIgnoreWarning', 01032 'label-message' => 'ignorewarnings', 01033 'section' => 'options', 01034 ); 01035 } 01036 01037 $descriptor['DestFileWarningAck'] = array( 01038 'type' => 'hidden', 01039 'id' => 'wpDestFileWarningAck', 01040 'default' => $this->mDestWarningAck ? '1' : '', 01041 ); 01042 01043 if ( $this->mForReUpload ) { 01044 $descriptor['ForReUpload'] = array( 01045 'type' => 'hidden', 01046 'id' => 'wpForReUpload', 01047 'default' => '1', 01048 ); 01049 } 01050 01051 return $descriptor; 01052 } 01053 01057 public function show() { 01058 $this->addUploadJS(); 01059 parent::show(); 01060 } 01061 01065 protected function addUploadJS() { 01066 global $wgUseAjax, $wgAjaxUploadDestCheck, $wgAjaxLicensePreview, $wgEnableAPI, $wgStrictFileExtensions; 01067 01068 $useAjaxDestCheck = $wgUseAjax && $wgAjaxUploadDestCheck; 01069 $useAjaxLicensePreview = $wgUseAjax && $wgAjaxLicensePreview && $wgEnableAPI; 01070 $this->mMaxUploadSize['*'] = UploadBase::getMaxUploadSize(); 01071 01072 $scriptVars = array( 01073 'wgAjaxUploadDestCheck' => $useAjaxDestCheck, 01074 'wgAjaxLicensePreview' => $useAjaxLicensePreview, 01075 'wgUploadAutoFill' => !$this->mForReUpload && 01076 // If we received mDestFile from the request, don't autofill 01077 // the wpDestFile textbox 01078 $this->mDestFile === '', 01079 'wgUploadSourceIds' => $this->mSourceIds, 01080 'wgStrictFileExtensions' => $wgStrictFileExtensions, 01081 'wgCapitalizeUploads' => MWNamespace::isCapitalized( NS_FILE ), 01082 'wgMaxUploadSize' => $this->mMaxUploadSize, 01083 ); 01084 01085 $out = $this->getOutput(); 01086 $out->addJsConfigVars( $scriptVars ); 01087 01088 01089 $out->addModules( array( 01090 'mediawiki.action.edit', // For <charinsert> support 01091 'mediawiki.legacy.upload', // Old form stuff... 01092 'mediawiki.special.upload', // Newer extras for thumbnail preview. 01093 ) ); 01094 } 01095 01101 function trySubmit() { 01102 return false; 01103 } 01104 01105 } 01106 01110 class UploadSourceField extends HTMLTextField { 01111 01116 function getLabelHtml( $cellAttributes = array() ) { 01117 $id = $this->mParams['id']; 01118 $label = Html::rawElement( 'label', array( 'for' => $id ), $this->mLabel ); 01119 01120 if ( !empty( $this->mParams['radio'] ) ) { 01121 $attribs = array( 01122 'name' => 'wpSourceType', 01123 'type' => 'radio', 01124 'id' => $id, 01125 'value' => $this->mParams['upload-type'], 01126 ); 01127 if ( !empty( $this->mParams['checked'] ) ) { 01128 $attribs['checked'] = 'checked'; 01129 } 01130 $label .= Html::element( 'input', $attribs ); 01131 } 01132 01133 return Html::rawElement( 'td', array( 'class' => 'mw-label' ) + $cellAttributes, $label ); 01134 } 01135 01139 function getSize() { 01140 return isset( $this->mParams['size'] ) 01141 ? $this->mParams['size'] 01142 : 60; 01143 } 01144 } 01145