MediaWiki
REL1_21
|
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 $this->mDestWarningAck = $request->getText( 'wpDestFileWarningAck' ); 00100 $this->mIgnoreWarning = $request->getCheck( 'wpIgnoreWarning' ) 00101 || $request->getCheck( 'wpUploadIgnoreWarning' ); 00102 $this->mWatchthis = $request->getBool( 'wpWatchthis' ) && $this->getUser()->isLoggedIn(); 00103 $this->mCopyrightStatus = $request->getText( 'wpUploadCopyStatus' ); 00104 $this->mCopyrightSource = $request->getText( 'wpUploadSource' ); 00105 00106 $this->mForReUpload = $request->getBool( 'wpForReUpload' ); // updating a file 00107 $this->mCancelUpload = $request->getCheck( 'wpCancelUpload' ) 00108 || $request->getCheck( 'wpReUpload' ); // b/w compat 00109 00110 // If it was posted check for the token (no remote POST'ing with user credentials) 00111 $token = $request->getVal( 'wpEditToken' ); 00112 $this->mTokenOk = $this->getUser()->matchEditToken( $token ); 00113 00114 $this->uploadFormTextTop = ''; 00115 $this->uploadFormTextAfterSummary = ''; 00116 } 00117 00126 public function userCanExecute( User $user ) { 00127 return UploadBase::isEnabled() && parent::userCanExecute( $user ); 00128 } 00129 00133 public function execute( $par ) { 00134 $this->setHeaders(); 00135 $this->outputHeader(); 00136 00137 # Check uploading enabled 00138 if( !UploadBase::isEnabled() ) { 00139 throw new ErrorPageError( 'uploaddisabled', 'uploaddisabledtext' ); 00140 } 00141 00142 # Check permissions 00143 $user = $this->getUser(); 00144 $permissionRequired = UploadBase::isAllowed( $user ); 00145 if( $permissionRequired !== true ) { 00146 throw new PermissionsError( $permissionRequired ); 00147 } 00148 00149 # Check blocks 00150 if( $user->isBlocked() ) { 00151 throw new UserBlockedError( $user->getBlock() ); 00152 } 00153 00154 # Check whether we actually want to allow changing stuff 00155 $this->checkReadOnly(); 00156 00157 $this->loadRequest(); 00158 00159 # Unsave the temporary file in case this was a cancelled upload 00160 if ( $this->mCancelUpload ) { 00161 if ( !$this->unsaveUploadedFile() ) { 00162 # Something went wrong, so unsaveUploadedFile showed a warning 00163 return; 00164 } 00165 } 00166 00167 # Process upload or show a form 00168 if ( 00169 $this->mTokenOk && !$this->mCancelUpload && 00170 ( $this->mUpload && $this->mUploadClicked ) 00171 ) { 00172 $this->processUpload(); 00173 } else { 00174 # Backwards compatibility hook 00175 if( !wfRunHooks( 'UploadForm:initial', array( &$this ) ) ) { 00176 wfDebug( "Hook 'UploadForm:initial' broke output of the upload form" ); 00177 return; 00178 } 00179 $this->showUploadForm( $this->getUploadForm() ); 00180 } 00181 00182 # Cleanup 00183 if ( $this->mUpload ) { 00184 $this->mUpload->cleanupTempFile(); 00185 } 00186 } 00187 00193 protected function showUploadForm( $form ) { 00194 # Add links if file was previously deleted 00195 if ( $this->mDesiredDestName ) { 00196 $this->showViewDeletedLinks(); 00197 } 00198 00199 if ( $form instanceof HTMLForm ) { 00200 $form->show(); 00201 } else { 00202 $this->getOutput()->addHTML( $form ); 00203 } 00204 00205 } 00206 00215 protected function getUploadForm( $message = '', $sessionKey = '', $hideIgnoreWarning = false ) { 00216 # Initialize form 00217 $form = new UploadForm( array( 00218 'watch' => $this->getWatchCheck(), 00219 'forreupload' => $this->mForReUpload, 00220 'sessionkey' => $sessionKey, 00221 'hideignorewarning' => $hideIgnoreWarning, 00222 'destwarningack' => (bool)$this->mDestWarningAck, 00223 00224 'description' => $this->mComment, 00225 'texttop' => $this->uploadFormTextTop, 00226 'textaftersummary' => $this->uploadFormTextAfterSummary, 00227 'destfile' => $this->mDesiredDestName, 00228 ), $this->getContext() ); 00229 $form->setTitle( $this->getTitle() ); 00230 00231 # Check the token, but only if necessary 00232 if( 00233 !$this->mTokenOk && !$this->mCancelUpload && 00234 ( $this->mUpload && $this->mUploadClicked ) 00235 ) { 00236 $form->addPreText( $this->msg( 'session_fail_preview' )->parse() ); 00237 } 00238 00239 # Give a notice if the user is uploading a file that has been deleted or moved 00240 # Note that this is independent from the message 'filewasdeleted' that requires JS 00241 $desiredTitleObj = Title::makeTitleSafe( NS_FILE, $this->mDesiredDestName ); 00242 $delNotice = ''; // empty by default 00243 if ( $desiredTitleObj instanceof Title && !$desiredTitleObj->exists() ) { 00244 LogEventsList::showLogExtract( $delNotice, array( 'delete', 'move' ), 00245 $desiredTitleObj, 00246 '', array( 'lim' => 10, 00247 'conds' => array( "log_action != 'revision'" ), 00248 'showIfEmpty' => false, 00249 'msgKey' => array( 'upload-recreate-warning' ) ) 00250 ); 00251 } 00252 $form->addPreText( $delNotice ); 00253 00254 # Add text to form 00255 $form->addPreText( '<div id="uploadtext">' . 00256 $this->msg( 'uploadtext', array( $this->mDesiredDestName ) )->parseAsBlock() . 00257 '</div>' ); 00258 # Add upload error message 00259 $form->addPreText( $message ); 00260 00261 # Add footer to form 00262 $uploadFooter = $this->msg( 'uploadfooter' ); 00263 if ( !$uploadFooter->isDisabled() ) { 00264 $form->addPostText( '<div id="mw-upload-footer-message">' 00265 . $uploadFooter->parseAsBlock() . "</div>\n" ); 00266 } 00267 00268 return $form; 00269 } 00270 00274 protected function showViewDeletedLinks() { 00275 $title = Title::makeTitleSafe( NS_FILE, $this->mDesiredDestName ); 00276 $user = $this->getUser(); 00277 // Show a subtitle link to deleted revisions (to sysops et al only) 00278 if( $title instanceof Title ) { 00279 $count = $title->isDeleted(); 00280 if ( $count > 0 && $user->isAllowed( 'deletedhistory' ) ) { 00281 $restorelink = Linker::linkKnown( 00282 SpecialPage::getTitleFor( 'Undelete', $title->getPrefixedText() ), 00283 $this->msg( 'restorelink' )->numParams( $count )->escaped() 00284 ); 00285 $link = $this->msg( $user->isAllowed( 'delete' ) ? 'thisisdeleted' : 'viewdeleted' ) 00286 ->rawParams( $restorelink )->parseAsBlock(); 00287 $this->getOutput()->addHTML( "<div id=\"contentSub2\">{$link}</div>" ); 00288 } 00289 } 00290 } 00291 00303 protected function showRecoverableUploadError( $message ) { 00304 $sessionKey = $this->mUpload->stashSession(); 00305 $message = '<h2>' . $this->msg( 'uploaderror' )->escaped() . "</h2>\n" . 00306 '<div class="error">' . $message . "</div>\n"; 00307 00308 $form = $this->getUploadForm( $message, $sessionKey ); 00309 $form->setSubmitText( $this->msg( 'upload-tryagain' )->escaped() ); 00310 $this->showUploadForm( $form ); 00311 } 00320 protected function showUploadWarning( $warnings ) { 00321 # If there are no warnings, or warnings we can ignore, return early. 00322 # mDestWarningAck is set when some javascript has shown the warning 00323 # to the user. mForReUpload is set when the user clicks the "upload a 00324 # new version" link. 00325 if ( !$warnings || ( count( $warnings ) == 1 && 00326 isset( $warnings['exists'] ) && 00327 ( $this->mDestWarningAck || $this->mForReUpload ) ) ) 00328 { 00329 return false; 00330 } 00331 00332 $sessionKey = $this->mUpload->stashSession(); 00333 00334 $warningHtml = '<h2>' . $this->msg( 'uploadwarning' )->escaped() . "</h2>\n" 00335 . '<ul class="warning">'; 00336 foreach( $warnings as $warning => $args ) { 00337 if( $warning == 'badfilename' ) { 00338 $this->mDesiredDestName = Title::makeTitle( NS_FILE, $args )->getText(); 00339 } 00340 if( $warning == 'exists' ) { 00341 $msg = "\t<li>" . self::getExistsWarning( $args ) . "</li>\n"; 00342 } elseif( $warning == 'duplicate' ) { 00343 $msg = self::getDupeWarning( $args ); 00344 } elseif( $warning == 'duplicate-archive' ) { 00345 $msg = "\t<li>" . $this->msg( 'file-deleted-duplicate', 00346 Title::makeTitle( NS_FILE, $args )->getPrefixedText() )->parse() 00347 . "</li>\n"; 00348 } else { 00349 if ( $args === true ) { 00350 $args = array(); 00351 } elseif ( !is_array( $args ) ) { 00352 $args = array( $args ); 00353 } 00354 $msg = "\t<li>" . $this->msg( $warning, $args )->parse() . "</li>\n"; 00355 } 00356 $warningHtml .= $msg; 00357 } 00358 $warningHtml .= "</ul>\n"; 00359 $warningHtml .= $this->msg( 'uploadwarning-text' )->parseAsBlock(); 00360 00361 $form = $this->getUploadForm( $warningHtml, $sessionKey, /* $hideIgnoreWarning */ true ); 00362 $form->setSubmitText( $this->msg( 'upload-tryagain' )->text() ); 00363 $form->addButton( 'wpUploadIgnoreWarning', $this->msg( 'ignorewarning' )->text() ); 00364 $form->addButton( 'wpCancelUpload', $this->msg( 'reuploaddesc' )->text() ); 00365 00366 $this->showUploadForm( $form ); 00367 00368 # Indicate that we showed a form 00369 return true; 00370 } 00371 00377 protected function showUploadError( $message ) { 00378 $message = '<h2>' . $this->msg( 'uploadwarning' )->escaped() . "</h2>\n" . 00379 '<div class="error">' . $message . "</div>\n"; 00380 $this->showUploadForm( $this->getUploadForm( $message ) ); 00381 } 00382 00387 protected function processUpload() { 00388 // Fetch the file if required 00389 $status = $this->mUpload->fetchFile(); 00390 if( !$status->isOK() ) { 00391 $this->showUploadError( $this->getOutput()->parse( $status->getWikiText() ) ); 00392 return; 00393 } 00394 00395 if( !wfRunHooks( 'UploadForm:BeforeProcessing', array( &$this ) ) ) { 00396 wfDebug( "Hook 'UploadForm:BeforeProcessing' broke processing the file.\n" ); 00397 // This code path is deprecated. If you want to break upload processing 00398 // do so by hooking into the appropriate hooks in UploadBase::verifyUpload 00399 // and UploadBase::verifyFile. 00400 // If you use this hook to break uploading, the user will be returned 00401 // an empty form with no error message whatsoever. 00402 return; 00403 } 00404 00405 // Upload verification 00406 $details = $this->mUpload->verifyUpload(); 00407 if ( $details['status'] != UploadBase::OK ) { 00408 $this->processVerificationError( $details ); 00409 return; 00410 } 00411 00412 // Verify permissions for this title 00413 $permErrors = $this->mUpload->verifyTitlePermissions( $this->getUser() ); 00414 if( $permErrors !== true ) { 00415 $code = array_shift( $permErrors[0] ); 00416 $this->showRecoverableUploadError( $this->msg( $code, $permErrors[0] )->parse() ); 00417 return; 00418 } 00419 00420 $this->mLocalFile = $this->mUpload->getLocalFile(); 00421 00422 // Check warnings if necessary 00423 if( !$this->mIgnoreWarning ) { 00424 $warnings = $this->mUpload->checkWarnings(); 00425 if( $this->showUploadWarning( $warnings ) ) { 00426 return; 00427 } 00428 } 00429 00430 // Get the page text if this is not a reupload 00431 if( !$this->mForReUpload ) { 00432 $pageText = self::getInitialPageText( $this->mComment, $this->mLicense, 00433 $this->mCopyrightStatus, $this->mCopyrightSource ); 00434 } else { 00435 $pageText = false; 00436 } 00437 $status = $this->mUpload->performUpload( $this->mComment, $pageText, $this->mWatchthis, $this->getUser() ); 00438 if ( !$status->isGood() ) { 00439 $this->showUploadError( $this->getOutput()->parse( $status->getWikiText() ) ); 00440 return; 00441 } 00442 00443 // Success, redirect to description page 00444 $this->mUploadSuccessful = true; 00445 wfRunHooks( 'SpecialUploadComplete', array( &$this ) ); 00446 $this->getOutput()->redirect( $this->mLocalFile->getTitle()->getFullURL() ); 00447 } 00448 00457 public static function getInitialPageText( $comment = '', $license = '', $copyStatus = '', $source = '' ) { 00458 global $wgUseCopyrightUpload, $wgForceUIMsgAsContentMsg; 00459 $wgForceUIMsgAsContentMsg = (array) $wgForceUIMsgAsContentMsg; 00460 00461 $msg = array(); 00462 /* These messages are transcluded into the actual text of the description page. 00463 * Thus, forcing them as content messages makes the upload to produce an int: template 00464 * instead of hardcoding it there in the uploader language. 00465 */ 00466 foreach( array( 'license-header', 'filedesc', 'filestatus', 'filesource' ) as $msgName ) { 00467 if ( in_array( $msgName, $wgForceUIMsgAsContentMsg ) ) { 00468 $msg[$msgName] = "{{int:$msgName}}"; 00469 } else { 00470 $msg[$msgName] = wfMessage( $msgName )->inContentLanguage()->text(); 00471 } 00472 } 00473 00474 if ( $wgUseCopyrightUpload ) { 00475 $licensetxt = ''; 00476 if ( $license != '' ) { 00477 $licensetxt = '== ' . $msg['license-header'] . " ==\n" . '{{' . $license . '}}' . "\n"; 00478 } 00479 $pageText = '== ' . $msg['filedesc'] . " ==\n" . $comment . "\n" . 00480 '== ' . $msg['filestatus'] . " ==\n" . $copyStatus . "\n" . 00481 "$licensetxt" . 00482 '== ' . $msg['filesource'] . " ==\n" . $source; 00483 } else { 00484 if ( $license != '' ) { 00485 $filedesc = $comment == '' ? '' : '== ' . $msg['filedesc'] . " ==\n" . $comment . "\n"; 00486 $pageText = $filedesc . 00487 '== ' . $msg['license-header'] . " ==\n" . '{{' . $license . '}}' . "\n"; 00488 } else { 00489 $pageText = $comment; 00490 } 00491 } 00492 return $pageText; 00493 } 00494 00507 protected function getWatchCheck() { 00508 if( $this->getUser()->getOption( 'watchdefault' ) ) { 00509 // Watch all edits! 00510 return true; 00511 } 00512 00513 $local = wfLocalFile( $this->mDesiredDestName ); 00514 if( $local && $local->exists() ) { 00515 // We're uploading a new version of an existing file. 00516 // No creation, so don't watch it if we're not already. 00517 return $this->getUser()->isWatched( $local->getTitle() ); 00518 } else { 00519 // New page should get watched if that's our option. 00520 return $this->getUser()->getOption( 'watchcreations' ); 00521 } 00522 } 00523 00530 protected function processVerificationError( $details ) { 00531 global $wgFileExtensions; 00532 00533 switch( $details['status'] ) { 00534 00536 case UploadBase::MIN_LENGTH_PARTNAME: 00537 $this->showRecoverableUploadError( $this->msg( 'minlength1' )->escaped() ); 00538 break; 00539 case UploadBase::ILLEGAL_FILENAME: 00540 $this->showRecoverableUploadError( $this->msg( 'illegalfilename', 00541 $details['filtered'] )->parse() ); 00542 break; 00543 case UploadBase::FILENAME_TOO_LONG: 00544 $this->showRecoverableUploadError( $this->msg( 'filename-toolong' )->escaped() ); 00545 break; 00546 case UploadBase::FILETYPE_MISSING: 00547 $this->showRecoverableUploadError( $this->msg( 'filetype-missing' )->parse() ); 00548 break; 00549 case UploadBase::WINDOWS_NONASCII_FILENAME: 00550 $this->showRecoverableUploadError( $this->msg( 'windows-nonascii-filename' )->parse() ); 00551 break; 00552 00554 case UploadBase::EMPTY_FILE: 00555 $this->showUploadError( $this->msg( 'emptyfile' )->escaped() ); 00556 break; 00557 case UploadBase::FILE_TOO_LARGE: 00558 $this->showUploadError( $this->msg( 'largefileserver' )->escaped() ); 00559 break; 00560 case UploadBase::FILETYPE_BADTYPE: 00561 $msg = $this->msg( 'filetype-banned-type' ); 00562 if ( isset( $details['blacklistedExt'] ) ) { 00563 $msg->params( $this->getLanguage()->commaList( $details['blacklistedExt'] ) ); 00564 } else { 00565 $msg->params( $details['finalExt'] ); 00566 } 00567 $msg->params( $this->getLanguage()->commaList( $wgFileExtensions ), 00568 count( $wgFileExtensions ) ); 00569 00570 // Add PLURAL support for the first parameter. This results 00571 // in a bit unlogical parameter sequence, but does not break 00572 // old translations 00573 if ( isset( $details['blacklistedExt'] ) ) { 00574 $msg->params( count( $details['blacklistedExt'] ) ); 00575 } else { 00576 $msg->params( 1 ); 00577 } 00578 00579 $this->showUploadError( $msg->parse() ); 00580 break; 00581 case UploadBase::VERIFICATION_ERROR: 00582 unset( $details['status'] ); 00583 $code = array_shift( $details['details'] ); 00584 $this->showUploadError( $this->msg( $code, $details['details'] )->parse() ); 00585 break; 00586 case UploadBase::HOOK_ABORTED: 00587 if ( is_array( $details['error'] ) ) { # allow hooks to return error details in an array 00588 $args = $details['error']; 00589 $error = array_shift( $args ); 00590 } else { 00591 $error = $details['error']; 00592 $args = null; 00593 } 00594 00595 $this->showUploadError( $this->msg( $error, $args )->parse() ); 00596 break; 00597 default: 00598 throw new MWException( __METHOD__ . ": Unknown value `{$details['status']}`" ); 00599 } 00600 } 00601 00607 protected function unsaveUploadedFile() { 00608 if ( !( $this->mUpload instanceof UploadFromStash ) ) { 00609 return true; 00610 } 00611 $success = $this->mUpload->unsaveUploadedFile(); 00612 if ( !$success ) { 00613 $this->getOutput()->showFileDeleteError( $this->mUpload->getTempPath() ); 00614 return false; 00615 } else { 00616 return true; 00617 } 00618 } 00619 00620 /*** Functions for formatting warnings ***/ 00621 00629 public static function getExistsWarning( $exists ) { 00630 if ( !$exists ) { 00631 return ''; 00632 } 00633 00634 $file = $exists['file']; 00635 $filename = $file->getTitle()->getPrefixedText(); 00636 $warning = ''; 00637 00638 if( $exists['warning'] == 'exists' ) { 00639 // Exact match 00640 $warning = wfMessage( 'fileexists', $filename )->parse(); 00641 } elseif( $exists['warning'] == 'page-exists' ) { 00642 // Page exists but file does not 00643 $warning = wfMessage( 'filepageexists', $filename )->parse(); 00644 } elseif ( $exists['warning'] == 'exists-normalized' ) { 00645 $warning = wfMessage( 'fileexists-extension', $filename, 00646 $exists['normalizedFile']->getTitle()->getPrefixedText() )->parse(); 00647 } elseif ( $exists['warning'] == 'thumb' ) { 00648 // Swapped argument order compared with other messages for backwards compatibility 00649 $warning = wfMessage( 'fileexists-thumbnail-yes', 00650 $exists['thumbFile']->getTitle()->getPrefixedText(), $filename )->parse(); 00651 } elseif ( $exists['warning'] == 'thumb-name' ) { 00652 // Image w/o '180px-' does not exists, but we do not like these filenames 00653 $name = $file->getName(); 00654 $badPart = substr( $name, 0, strpos( $name, '-' ) + 1 ); 00655 $warning = wfMessage( 'file-thumbnail-no', $badPart )->parse(); 00656 } elseif ( $exists['warning'] == 'bad-prefix' ) { 00657 $warning = wfMessage( 'filename-bad-prefix', $exists['prefix'] )->parse(); 00658 } elseif ( $exists['warning'] == 'was-deleted' ) { 00659 # If the file existed before and was deleted, warn the user of this 00660 $ltitle = SpecialPage::getTitleFor( 'Log' ); 00661 $llink = Linker::linkKnown( 00662 $ltitle, 00663 wfMessage( 'deletionlog' )->escaped(), 00664 array(), 00665 array( 00666 'type' => 'delete', 00667 'page' => $filename 00668 ) 00669 ); 00670 $warning = wfMessage( 'filewasdeleted' )->rawParams( $llink )->parseAsBlock(); 00671 } 00672 00673 return $warning; 00674 } 00675 00682 public static function ajaxGetExistsWarning( $filename ) { 00683 $file = wfFindFile( $filename ); 00684 if( !$file ) { 00685 // Force local file so we have an object to do further checks against 00686 // if there isn't an exact match... 00687 $file = wfLocalFile( $filename ); 00688 } 00689 $s = ' '; 00690 if ( $file ) { 00691 $exists = UploadBase::getExistsWarning( $file ); 00692 $warning = self::getExistsWarning( $exists ); 00693 if ( $warning !== '' ) { 00694 $s = "<div>$warning</div>"; 00695 } 00696 } 00697 return $s; 00698 } 00699 00705 public static function getDupeWarning( $dupes ) { 00706 if ( !$dupes ) { 00707 return ''; 00708 } 00709 00710 $gallery = new ImageGallery; 00711 $gallery->setShowBytes( false ); 00712 foreach( $dupes as $file ) { 00713 $gallery->add( $file->getTitle() ); 00714 } 00715 return '<li>' . 00716 wfMessage( 'file-exists-duplicate' )->numParams( count( $dupes ) )->parse() . 00717 $gallery->toHtml() . "</li>\n"; 00718 } 00719 00720 protected function getGroupName() { 00721 return 'media'; 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' => $this->getUser()->getIntOption( '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 $out->addModules( array( 01089 'mediawiki.action.edit', // For <charinsert> support 01090 'mediawiki.legacy.upload', // Old form stuff... 01091 'mediawiki.special.upload', // Newer extras for thumbnail preview. 01092 ) ); 01093 } 01094 01100 function trySubmit() { 01101 return false; 01102 } 01103 01104 } 01105 01109 class UploadSourceField extends HTMLTextField { 01110 01115 function getLabelHtml( $cellAttributes = array() ) { 01116 $id = $this->mParams['id']; 01117 $label = Html::rawElement( 'label', array( 'for' => $id ), $this->mLabel ); 01118 01119 if ( !empty( $this->mParams['radio'] ) ) { 01120 $attribs = array( 01121 'name' => 'wpSourceType', 01122 'type' => 'radio', 01123 'id' => $id, 01124 'value' => $this->mParams['upload-type'], 01125 ); 01126 if ( !empty( $this->mParams['checked'] ) ) { 01127 $attribs['checked'] = 'checked'; 01128 } 01129 $label .= Html::element( 'input', $attribs ); 01130 } 01131 01132 return Html::rawElement( 'td', array( 'class' => 'mw-label' ) + $cellAttributes, $label ); 01133 } 01134 01138 function getSize() { 01139 return isset( $this->mParams['size'] ) 01140 ? $this->mParams['size'] 01141 : 60; 01142 } 01143 }