MediaWiki
REL1_20
|
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 == 'exists' ) { 00340 $msg = "\t<li>" . self::getExistsWarning( $args ) . "</li>\n"; 00341 } elseif( $warning == 'duplicate' ) { 00342 $msg = self::getDupeWarning( $args ); 00343 } elseif( $warning == 'duplicate-archive' ) { 00344 $msg = "\t<li>" . $this->msg( 'file-deleted-duplicate', 00345 Title::makeTitle( NS_FILE, $args )->getPrefixedText() )->parse() 00346 . "</li>\n"; 00347 } else { 00348 if ( $args === true ) { 00349 $args = array(); 00350 } elseif ( !is_array( $args ) ) { 00351 $args = array( $args ); 00352 } 00353 $msg = "\t<li>" . $this->msg( $warning, $args )->parse() . "</li>\n"; 00354 } 00355 $warningHtml .= $msg; 00356 } 00357 $warningHtml .= "</ul>\n"; 00358 $warningHtml .= $this->msg( 'uploadwarning-text' )->parseAsBlock(); 00359 00360 $form = $this->getUploadForm( $warningHtml, $sessionKey, /* $hideIgnoreWarning */ true ); 00361 $form->setSubmitText( $this->msg( 'upload-tryagain' )->text() ); 00362 $form->addButton( 'wpUploadIgnoreWarning', $this->msg( 'ignorewarning' )->text() ); 00363 $form->addButton( 'wpCancelUpload', $this->msg( 'reuploaddesc' )->text() ); 00364 00365 $this->showUploadForm( $form ); 00366 00367 # Indicate that we showed a form 00368 return true; 00369 } 00370 00376 protected function showUploadError( $message ) { 00377 $message = '<h2>' . $this->msg( 'uploadwarning' )->escaped() . "</h2>\n" . 00378 '<div class="error">' . $message . "</div>\n"; 00379 $this->showUploadForm( $this->getUploadForm( $message ) ); 00380 } 00381 00386 protected function processUpload() { 00387 // Fetch the file if required 00388 $status = $this->mUpload->fetchFile(); 00389 if( !$status->isOK() ) { 00390 $this->showUploadError( $this->getOutput()->parse( $status->getWikiText() ) ); 00391 return; 00392 } 00393 00394 if( !wfRunHooks( 'UploadForm:BeforeProcessing', array( &$this ) ) ) { 00395 wfDebug( "Hook 'UploadForm:BeforeProcessing' broke processing the file.\n" ); 00396 // This code path is deprecated. If you want to break upload processing 00397 // do so by hooking into the appropriate hooks in UploadBase::verifyUpload 00398 // and UploadBase::verifyFile. 00399 // If you use this hook to break uploading, the user will be returned 00400 // an empty form with no error message whatsoever. 00401 return; 00402 } 00403 00404 // Upload verification 00405 $details = $this->mUpload->verifyUpload(); 00406 if ( $details['status'] != UploadBase::OK ) { 00407 $this->processVerificationError( $details ); 00408 return; 00409 } 00410 00411 // Verify permissions for this title 00412 $permErrors = $this->mUpload->verifyTitlePermissions( $this->getUser() ); 00413 if( $permErrors !== true ) { 00414 $code = array_shift( $permErrors[0] ); 00415 $this->showRecoverableUploadError( $this->msg( $code, $permErrors[0] )->parse() ); 00416 return; 00417 } 00418 00419 $this->mLocalFile = $this->mUpload->getLocalFile(); 00420 00421 // Check warnings if necessary 00422 if( !$this->mIgnoreWarning ) { 00423 $warnings = $this->mUpload->checkWarnings(); 00424 if( $this->showUploadWarning( $warnings ) ) { 00425 return; 00426 } 00427 } 00428 00429 // Get the page text if this is not a reupload 00430 if( !$this->mForReUpload ) { 00431 $pageText = self::getInitialPageText( $this->mComment, $this->mLicense, 00432 $this->mCopyrightStatus, $this->mCopyrightSource ); 00433 } else { 00434 $pageText = false; 00435 } 00436 $status = $this->mUpload->performUpload( $this->mComment, $pageText, $this->mWatchthis, $this->getUser() ); 00437 if ( !$status->isGood() ) { 00438 $this->showUploadError( $this->getOutput()->parse( $status->getWikiText() ) ); 00439 return; 00440 } 00441 00442 // Success, redirect to description page 00443 $this->mUploadSuccessful = true; 00444 wfRunHooks( 'SpecialUploadComplete', array( &$this ) ); 00445 $this->getOutput()->redirect( $this->mLocalFile->getTitle()->getFullURL() ); 00446 } 00447 00456 public static function getInitialPageText( $comment = '', $license = '', $copyStatus = '', $source = '' ) { 00457 global $wgUseCopyrightUpload, $wgForceUIMsgAsContentMsg; 00458 $wgForceUIMsgAsContentMsg = (array) $wgForceUIMsgAsContentMsg; 00459 00460 $msg = array(); 00461 /* These messages are transcluded into the actual text of the description page. 00462 * Thus, forcing them as content messages makes the upload to produce an int: template 00463 * instead of hardcoding it there in the uploader language. 00464 */ 00465 foreach( array( 'license-header', 'filedesc', 'filestatus', 'filesource' ) as $msgName ) { 00466 if ( in_array( $msgName, $wgForceUIMsgAsContentMsg ) ) { 00467 $msg[$msgName] = "{{int:$msgName}}"; 00468 } else { 00469 $msg[$msgName] = wfMessage( $msgName )->inContentLanguage()->text(); 00470 } 00471 } 00472 00473 if ( $wgUseCopyrightUpload ) { 00474 $licensetxt = ''; 00475 if ( $license != '' ) { 00476 $licensetxt = '== ' . $msg[ 'license-header' ] . " ==\n" . '{{' . $license . '}}' . "\n"; 00477 } 00478 $pageText = '== ' . $msg[ 'filedesc' ] . " ==\n" . $comment . "\n" . 00479 '== ' . $msg[ 'filestatus' ] . " ==\n" . $copyStatus . "\n" . 00480 "$licensetxt" . 00481 '== ' . $msg[ 'filesource' ] . " ==\n" . $source; 00482 } else { 00483 if ( $license != '' ) { 00484 $filedesc = $comment == '' ? '' : '== ' . $msg[ 'filedesc' ] . " ==\n" . $comment . "\n"; 00485 $pageText = $filedesc . 00486 '== ' . $msg[ 'license-header' ] . " ==\n" . '{{' . $license . '}}' . "\n"; 00487 } else { 00488 $pageText = $comment; 00489 } 00490 } 00491 return $pageText; 00492 } 00493 00506 protected function getWatchCheck() { 00507 if( $this->getUser()->getOption( 'watchdefault' ) ) { 00508 // Watch all edits! 00509 return true; 00510 } 00511 00512 $local = wfLocalFile( $this->mDesiredDestName ); 00513 if( $local && $local->exists() ) { 00514 // We're uploading a new version of an existing file. 00515 // No creation, so don't watch it if we're not already. 00516 return $this->getUser()->isWatched( $local->getTitle() ); 00517 } else { 00518 // New page should get watched if that's our option. 00519 return $this->getUser()->getOption( 'watchcreations' ); 00520 } 00521 } 00522 00523 00529 protected function processVerificationError( $details ) { 00530 global $wgFileExtensions; 00531 00532 switch( $details['status'] ) { 00533 00535 case UploadBase::MIN_LENGTH_PARTNAME: 00536 $this->showRecoverableUploadError( $this->msg( 'minlength1' )->escaped() ); 00537 break; 00538 case UploadBase::ILLEGAL_FILENAME: 00539 $this->showRecoverableUploadError( $this->msg( 'illegalfilename', 00540 $details['filtered'] )->parse() ); 00541 break; 00542 case UploadBase::FILENAME_TOO_LONG: 00543 $this->showRecoverableUploadError( $this->msg( 'filename-toolong' )->escaped() ); 00544 break; 00545 case UploadBase::FILETYPE_MISSING: 00546 $this->showRecoverableUploadError( $this->msg( 'filetype-missing' )->parse() ); 00547 break; 00548 case UploadBase::WINDOWS_NONASCII_FILENAME: 00549 $this->showRecoverableUploadError( $this->msg( 'windows-nonascii-filename' )->parse() ); 00550 break; 00551 00553 case UploadBase::EMPTY_FILE: 00554 $this->showUploadError( $this->msg( 'emptyfile' )->escaped() ); 00555 break; 00556 case UploadBase::FILE_TOO_LARGE: 00557 $this->showUploadError( $this->msg( 'largefileserver' )->escaped() ); 00558 break; 00559 case UploadBase::FILETYPE_BADTYPE: 00560 $msg = $this->msg( 'filetype-banned-type' ); 00561 if ( isset( $details['blacklistedExt'] ) ) { 00562 $msg->params( $this->getLanguage()->commaList( $details['blacklistedExt'] ) ); 00563 } else { 00564 $msg->params( $details['finalExt'] ); 00565 } 00566 $msg->params( $this->getLanguage()->commaList( $wgFileExtensions ), 00567 count( $wgFileExtensions ) ); 00568 00569 // Add PLURAL support for the first parameter. This results 00570 // in a bit unlogical parameter sequence, but does not break 00571 // old translations 00572 if ( isset( $details['blacklistedExt'] ) ) { 00573 $msg->params( count( $details['blacklistedExt'] ) ); 00574 } else { 00575 $msg->params( 1 ); 00576 } 00577 00578 $this->showUploadError( $msg->parse() ); 00579 break; 00580 case UploadBase::VERIFICATION_ERROR: 00581 unset( $details['status'] ); 00582 $code = array_shift( $details['details'] ); 00583 $this->showUploadError( $this->msg( $code, $details['details'] )->parse() ); 00584 break; 00585 case UploadBase::HOOK_ABORTED: 00586 if ( is_array( $details['error'] ) ) { # allow hooks to return error details in an array 00587 $args = $details['error']; 00588 $error = array_shift( $args ); 00589 } else { 00590 $error = $details['error']; 00591 $args = null; 00592 } 00593 00594 $this->showUploadError( $this->msg( $error, $args )->parse() ); 00595 break; 00596 default: 00597 throw new MWException( __METHOD__ . ": Unknown value `{$details['status']}`" ); 00598 } 00599 } 00600 00606 protected function unsaveUploadedFile() { 00607 if ( !( $this->mUpload instanceof UploadFromStash ) ) { 00608 return true; 00609 } 00610 $success = $this->mUpload->unsaveUploadedFile(); 00611 if ( !$success ) { 00612 $this->getOutput()->showFileDeleteError( $this->mUpload->getTempPath() ); 00613 return false; 00614 } else { 00615 return true; 00616 } 00617 } 00618 00619 /*** Functions for formatting warnings ***/ 00620 00628 public static function getExistsWarning( $exists ) { 00629 if ( !$exists ) { 00630 return ''; 00631 } 00632 00633 $file = $exists['file']; 00634 $filename = $file->getTitle()->getPrefixedText(); 00635 $warning = ''; 00636 00637 if( $exists['warning'] == 'exists' ) { 00638 // Exact match 00639 $warning = wfMessage( 'fileexists', $filename )->parse(); 00640 } elseif( $exists['warning'] == 'page-exists' ) { 00641 // Page exists but file does not 00642 $warning = wfMessage( 'filepageexists', $filename )->parse(); 00643 } elseif ( $exists['warning'] == 'exists-normalized' ) { 00644 $warning = wfMessage( 'fileexists-extension', $filename, 00645 $exists['normalizedFile']->getTitle()->getPrefixedText() )->parse(); 00646 } elseif ( $exists['warning'] == 'thumb' ) { 00647 // Swapped argument order compared with other messages for backwards compatibility 00648 $warning = wfMessage( 'fileexists-thumbnail-yes', 00649 $exists['thumbFile']->getTitle()->getPrefixedText(), $filename )->parse(); 00650 } elseif ( $exists['warning'] == 'thumb-name' ) { 00651 // Image w/o '180px-' does not exists, but we do not like these filenames 00652 $name = $file->getName(); 00653 $badPart = substr( $name, 0, strpos( $name, '-' ) + 1 ); 00654 $warning = wfMessage( 'file-thumbnail-no', $badPart )->parse(); 00655 } elseif ( $exists['warning'] == 'bad-prefix' ) { 00656 $warning = wfMessage( 'filename-bad-prefix', $exists['prefix'] )->parse(); 00657 } elseif ( $exists['warning'] == 'was-deleted' ) { 00658 # If the file existed before and was deleted, warn the user of this 00659 $ltitle = SpecialPage::getTitleFor( 'Log' ); 00660 $llink = Linker::linkKnown( 00661 $ltitle, 00662 wfMessage( 'deletionlog' )->escaped(), 00663 array(), 00664 array( 00665 'type' => 'delete', 00666 'page' => $filename 00667 ) 00668 ); 00669 $warning = wfMessage( 'filewasdeleted' )->rawParams( $llink )->parseAsBlock(); 00670 } 00671 00672 return $warning; 00673 } 00674 00681 public static function ajaxGetExistsWarning( $filename ) { 00682 $file = wfFindFile( $filename ); 00683 if( !$file ) { 00684 // Force local file so we have an object to do further checks against 00685 // if there isn't an exact match... 00686 $file = wfLocalFile( $filename ); 00687 } 00688 $s = ' '; 00689 if ( $file ) { 00690 $exists = UploadBase::getExistsWarning( $file ); 00691 $warning = self::getExistsWarning( $exists ); 00692 if ( $warning !== '' ) { 00693 $s = "<div>$warning</div>"; 00694 } 00695 } 00696 return $s; 00697 } 00698 00704 public static function getDupeWarning( $dupes ) { 00705 if ( !$dupes ) { 00706 return ''; 00707 } 00708 00709 $gallery = new ImageGallery; 00710 $gallery->setShowBytes( false ); 00711 foreach( $dupes as $file ) { 00712 $gallery->add( $file->getTitle() ); 00713 } 00714 return '<li>' . 00715 wfMessage( 'file-exists-duplicate' )->numParams( count( $dupes ) )->parse() . 00716 $gallery->toHtml() . "</li>\n"; 00717 } 00718 00719 } 00720 00724 class UploadForm extends HTMLForm { 00725 protected $mWatch; 00726 protected $mForReUpload; 00727 protected $mSessionKey; 00728 protected $mHideIgnoreWarning; 00729 protected $mDestWarningAck; 00730 protected $mDestFile; 00731 00732 protected $mComment; 00733 protected $mTextTop; 00734 protected $mTextAfterSummary; 00735 00736 protected $mSourceIds; 00737 00738 protected $mMaxFileSize = array(); 00739 00740 protected $mMaxUploadSize = array(); 00741 00742 public function __construct( array $options = array(), IContextSource $context = null ) { 00743 $this->mWatch = !empty( $options['watch'] ); 00744 $this->mForReUpload = !empty( $options['forreupload'] ); 00745 $this->mSessionKey = isset( $options['sessionkey'] ) 00746 ? $options['sessionkey'] : ''; 00747 $this->mHideIgnoreWarning = !empty( $options['hideignorewarning'] ); 00748 $this->mDestWarningAck = !empty( $options['destwarningack'] ); 00749 $this->mDestFile = isset( $options['destfile'] ) ? $options['destfile'] : ''; 00750 00751 $this->mComment = isset( $options['description'] ) ? 00752 $options['description'] : ''; 00753 00754 $this->mTextTop = isset( $options['texttop'] ) 00755 ? $options['texttop'] : ''; 00756 00757 $this->mTextAfterSummary = isset( $options['textaftersummary'] ) 00758 ? $options['textaftersummary'] : ''; 00759 00760 $sourceDescriptor = $this->getSourceSection(); 00761 $descriptor = $sourceDescriptor 00762 + $this->getDescriptionSection() 00763 + $this->getOptionsSection(); 00764 00765 wfRunHooks( 'UploadFormInitDescriptor', array( &$descriptor ) ); 00766 parent::__construct( $descriptor, $context, 'upload' ); 00767 00768 # Set some form properties 00769 $this->setSubmitText( $this->msg( 'uploadbtn' )->text() ); 00770 $this->setSubmitName( 'wpUpload' ); 00771 # Used message keys: 'accesskey-upload', 'tooltip-upload' 00772 $this->setSubmitTooltip( 'upload' ); 00773 $this->setId( 'mw-upload-form' ); 00774 00775 # Build a list of IDs for javascript insertion 00776 $this->mSourceIds = array(); 00777 foreach ( $sourceDescriptor as $field ) { 00778 if ( !empty( $field['id'] ) ) { 00779 $this->mSourceIds[] = $field['id']; 00780 } 00781 } 00782 00783 } 00784 00791 protected function getSourceSection() { 00792 if ( $this->mSessionKey ) { 00793 return array( 00794 'SessionKey' => array( 00795 'type' => 'hidden', 00796 'default' => $this->mSessionKey, 00797 ), 00798 'SourceType' => array( 00799 'type' => 'hidden', 00800 'default' => 'Stash', 00801 ), 00802 ); 00803 } 00804 00805 $canUploadByUrl = UploadFromUrl::isEnabled() && UploadFromUrl::isAllowed( $this->getUser() ); 00806 $radio = $canUploadByUrl; 00807 $selectedSourceType = strtolower( $this->getRequest()->getText( 'wpSourceType', 'File' ) ); 00808 00809 $descriptor = array(); 00810 if ( $this->mTextTop ) { 00811 $descriptor['UploadFormTextTop'] = array( 00812 'type' => 'info', 00813 'section' => 'source', 00814 'default' => $this->mTextTop, 00815 'raw' => true, 00816 ); 00817 } 00818 00819 $this->mMaxUploadSize['file'] = UploadBase::getMaxUploadSize( 'file' ); 00820 # Limit to upload_max_filesize unless we are running under HipHop and 00821 # that setting doesn't exist 00822 if ( !wfIsHipHop() ) { 00823 $this->mMaxUploadSize['file'] = min( $this->mMaxUploadSize['file'], 00824 wfShorthandToInteger( ini_get( 'upload_max_filesize' ) ), 00825 wfShorthandToInteger( ini_get( 'post_max_size' ) ) 00826 ); 00827 } 00828 00829 $descriptor['UploadFile'] = array( 00830 'class' => 'UploadSourceField', 00831 'section' => 'source', 00832 'type' => 'file', 00833 'id' => 'wpUploadFile', 00834 'label-message' => 'sourcefilename', 00835 'upload-type' => 'File', 00836 'radio' => &$radio, 00837 'help' => $this->msg( 'upload-maxfilesize', 00838 $this->getContext()->getLanguage()->formatSize( $this->mMaxUploadSize['file'] ) 00839 )->parse() . ' ' . $this->msg( 'upload_source_file' )->escaped(), 00840 'checked' => $selectedSourceType == 'file', 00841 ); 00842 if ( $canUploadByUrl ) { 00843 $this->mMaxUploadSize['url'] = UploadBase::getMaxUploadSize( 'url' ); 00844 $descriptor['UploadFileURL'] = array( 00845 'class' => 'UploadSourceField', 00846 'section' => 'source', 00847 'id' => 'wpUploadFileURL', 00848 'label-message' => 'sourceurl', 00849 'upload-type' => 'url', 00850 'radio' => &$radio, 00851 'help' => $this->msg( 'upload-maxfilesize', 00852 $this->getContext()->getLanguage()->formatSize( $this->mMaxUploadSize['url'] ) 00853 )->parse() . ' ' . $this->msg( 'upload_source_url' )->escaped(), 00854 'checked' => $selectedSourceType == 'url', 00855 ); 00856 } 00857 wfRunHooks( 'UploadFormSourceDescriptors', array( &$descriptor, &$radio, $selectedSourceType ) ); 00858 00859 $descriptor['Extensions'] = array( 00860 'type' => 'info', 00861 'section' => 'source', 00862 'default' => $this->getExtensionsMessage(), 00863 'raw' => true, 00864 ); 00865 return $descriptor; 00866 } 00867 00873 protected function getExtensionsMessage() { 00874 # Print a list of allowed file extensions, if so configured. We ignore 00875 # MIME type here, it's incomprehensible to most people and too long. 00876 global $wgCheckFileExtensions, $wgStrictFileExtensions, 00877 $wgFileExtensions, $wgFileBlacklist; 00878 00879 if( $wgCheckFileExtensions ) { 00880 if( $wgStrictFileExtensions ) { 00881 # Everything not permitted is banned 00882 $extensionsList = 00883 '<div id="mw-upload-permitted">' . 00884 $this->msg( 'upload-permitted', $this->getContext()->getLanguage()->commaList( $wgFileExtensions ) )->parseAsBlock() . 00885 "</div>\n"; 00886 } else { 00887 # We have to list both preferred and prohibited 00888 $extensionsList = 00889 '<div id="mw-upload-preferred">' . 00890 $this->msg( 'upload-preferred', $this->getContext()->getLanguage()->commaList( $wgFileExtensions ) )->parseAsBlock() . 00891 "</div>\n" . 00892 '<div id="mw-upload-prohibited">' . 00893 $this->msg( 'upload-prohibited', $this->getContext()->getLanguage()->commaList( $wgFileBlacklist ) )->parseAsBlock() . 00894 "</div>\n"; 00895 } 00896 } else { 00897 # Everything is permitted. 00898 $extensionsList = ''; 00899 } 00900 return $extensionsList; 00901 } 00902 00909 protected function getDescriptionSection() { 00910 if ( $this->mSessionKey ) { 00911 $stash = RepoGroup::singleton()->getLocalRepo()->getUploadStash(); 00912 try { 00913 $file = $stash->getFile( $this->mSessionKey ); 00914 } catch ( MWException $e ) { 00915 $file = null; 00916 } 00917 if ( $file ) { 00918 global $wgContLang; 00919 00920 $mto = $file->transform( array( 'width' => 120 ) ); 00921 $this->addHeaderText( 00922 '<div class="thumb t' . $wgContLang->alignEnd() . '">' . 00923 Html::element( 'img', array( 00924 'src' => $mto->getUrl(), 00925 'class' => 'thumbimage', 00926 ) ) . '</div>', 'description' ); 00927 } 00928 } 00929 00930 $descriptor = array( 00931 'DestFile' => array( 00932 'type' => 'text', 00933 'section' => 'description', 00934 'id' => 'wpDestFile', 00935 'label-message' => 'destfilename', 00936 'size' => 60, 00937 'default' => $this->mDestFile, 00938 # @todo FIXME: Hack to work around poor handling of the 'default' option in HTMLForm 00939 'nodata' => strval( $this->mDestFile ) !== '', 00940 ), 00941 'UploadDescription' => array( 00942 'type' => 'textarea', 00943 'section' => 'description', 00944 'id' => 'wpUploadDescription', 00945 'label-message' => $this->mForReUpload 00946 ? 'filereuploadsummary' 00947 : 'fileuploadsummary', 00948 'default' => $this->mComment, 00949 'cols' => intval( $this->getUser()->getOption( 'cols' ) ), 00950 'rows' => 8, 00951 ) 00952 ); 00953 if ( $this->mTextAfterSummary ) { 00954 $descriptor['UploadFormTextAfterSummary'] = array( 00955 'type' => 'info', 00956 'section' => 'description', 00957 'default' => $this->mTextAfterSummary, 00958 'raw' => true, 00959 ); 00960 } 00961 00962 $descriptor += array( 00963 'EditTools' => array( 00964 'type' => 'edittools', 00965 'section' => 'description', 00966 'message' => 'edittools-upload', 00967 ) 00968 ); 00969 00970 if ( $this->mForReUpload ) { 00971 $descriptor['DestFile']['readonly'] = true; 00972 } else { 00973 $descriptor['License'] = array( 00974 'type' => 'select', 00975 'class' => 'Licenses', 00976 'section' => 'description', 00977 'id' => 'wpLicense', 00978 'label-message' => 'license', 00979 ); 00980 } 00981 00982 global $wgUseCopyrightUpload; 00983 if ( $wgUseCopyrightUpload ) { 00984 $descriptor['UploadCopyStatus'] = array( 00985 'type' => 'text', 00986 'section' => 'description', 00987 'id' => 'wpUploadCopyStatus', 00988 'label-message' => 'filestatus', 00989 ); 00990 $descriptor['UploadSource'] = array( 00991 'type' => 'text', 00992 'section' => 'description', 00993 'id' => 'wpUploadSource', 00994 'label-message' => 'filesource', 00995 ); 00996 } 00997 00998 return $descriptor; 00999 } 01000 01007 protected function getOptionsSection() { 01008 $user = $this->getUser(); 01009 if ( $user->isLoggedIn() ) { 01010 $descriptor = array( 01011 'Watchthis' => array( 01012 'type' => 'check', 01013 'id' => 'wpWatchthis', 01014 'label-message' => 'watchthisupload', 01015 'section' => 'options', 01016 'default' => $user->getOption( 'watchcreations' ), 01017 ) 01018 ); 01019 } 01020 if ( !$this->mHideIgnoreWarning ) { 01021 $descriptor['IgnoreWarning'] = array( 01022 'type' => 'check', 01023 'id' => 'wpIgnoreWarning', 01024 'label-message' => 'ignorewarnings', 01025 'section' => 'options', 01026 ); 01027 } 01028 01029 $descriptor['DestFileWarningAck'] = array( 01030 'type' => 'hidden', 01031 'id' => 'wpDestFileWarningAck', 01032 'default' => $this->mDestWarningAck ? '1' : '', 01033 ); 01034 01035 if ( $this->mForReUpload ) { 01036 $descriptor['ForReUpload'] = array( 01037 'type' => 'hidden', 01038 'id' => 'wpForReUpload', 01039 'default' => '1', 01040 ); 01041 } 01042 01043 return $descriptor; 01044 } 01045 01049 public function show() { 01050 $this->addUploadJS(); 01051 parent::show(); 01052 } 01053 01057 protected function addUploadJS() { 01058 global $wgUseAjax, $wgAjaxUploadDestCheck, $wgAjaxLicensePreview, $wgEnableAPI, $wgStrictFileExtensions; 01059 01060 $useAjaxDestCheck = $wgUseAjax && $wgAjaxUploadDestCheck; 01061 $useAjaxLicensePreview = $wgUseAjax && $wgAjaxLicensePreview && $wgEnableAPI; 01062 $this->mMaxUploadSize['*'] = UploadBase::getMaxUploadSize(); 01063 01064 $scriptVars = array( 01065 'wgAjaxUploadDestCheck' => $useAjaxDestCheck, 01066 'wgAjaxLicensePreview' => $useAjaxLicensePreview, 01067 'wgUploadAutoFill' => !$this->mForReUpload && 01068 // If we received mDestFile from the request, don't autofill 01069 // the wpDestFile textbox 01070 $this->mDestFile === '', 01071 'wgUploadSourceIds' => $this->mSourceIds, 01072 'wgStrictFileExtensions' => $wgStrictFileExtensions, 01073 'wgCapitalizeUploads' => MWNamespace::isCapitalized( NS_FILE ), 01074 'wgMaxUploadSize' => $this->mMaxUploadSize, 01075 ); 01076 01077 $out = $this->getOutput(); 01078 $out->addJsConfigVars( $scriptVars ); 01079 01080 01081 $out->addModules( array( 01082 'mediawiki.action.edit', // For <charinsert> support 01083 'mediawiki.legacy.upload', // Old form stuff... 01084 'mediawiki.special.upload', // Newer extras for thumbnail preview. 01085 ) ); 01086 } 01087 01093 function trySubmit() { 01094 return false; 01095 } 01096 01097 } 01098 01102 class UploadSourceField extends HTMLTextField { 01103 01108 function getLabelHtml( $cellAttributes = array() ) { 01109 $id = "wpSourceType{$this->mParams['upload-type']}"; 01110 $label = Html::rawElement( 'label', array( 'for' => $id ), $this->mLabel ); 01111 01112 if ( !empty( $this->mParams['radio'] ) ) { 01113 $attribs = array( 01114 'name' => 'wpSourceType', 01115 'type' => 'radio', 01116 'id' => $id, 01117 'value' => $this->mParams['upload-type'], 01118 ); 01119 if ( !empty( $this->mParams['checked'] ) ) { 01120 $attribs['checked'] = 'checked'; 01121 } 01122 $label .= Html::element( 'input', $attribs ); 01123 } 01124 01125 return Html::rawElement( 'td', array( 'class' => 'mw-label' ) + $cellAttributes, $label ); 01126 } 01127 01131 function getSize() { 01132 return isset( $this->mParams['size'] ) 01133 ? $this->mParams['size'] 01134 : 60; 01135 } 01136 } 01137