MediaWiki
REL1_23
|
00001 <?php 00031 class SpecialUpload extends SpecialPage { 00037 public function __construct( $request = null ) { 00038 parent::__construct( 'Upload', 'upload' ); 00039 } 00040 00044 public $mRequest; 00045 public $mSourceType; 00046 00048 public $mUpload; 00049 00051 public $mLocalFile; 00052 public $mUploadClicked; 00053 00057 public $mDesiredDestName; 00058 public $mComment; 00059 public $mLicense; 00060 00063 public $mIgnoreWarning; 00064 public $mWatchthis; 00065 public $mCopyrightStatus; 00066 public $mCopyrightSource; 00067 00070 public $mDestWarningAck; 00071 00073 public $mForReUpload; 00074 00076 public $mCancelUpload; 00077 public $mTokenOk; 00078 00080 public $mUploadSuccessful = false; 00081 00083 public $uploadFormTextTop; 00084 public $uploadFormTextAfterSummary; 00085 00089 protected function loadRequest() { 00090 $this->mRequest = $request = $this->getRequest(); 00091 $this->mSourceType = $request->getVal( 'wpSourceType', 'file' ); 00092 $this->mUpload = UploadBase::createFromRequest( $request ); 00093 $this->mUploadClicked = $request->wasPosted() 00094 && ( $request->getCheck( 'wpUpload' ) 00095 || $request->getCheck( 'wpUploadIgnoreWarning' ) ); 00096 00097 // Guess the desired name from the filename if not provided 00098 $this->mDesiredDestName = $request->getText( 'wpDestFile' ); 00099 if ( !$this->mDesiredDestName && $request->getFileName( 'wpUploadFile' ) !== null ) { 00100 $this->mDesiredDestName = $request->getFileName( 'wpUploadFile' ); 00101 } 00102 $this->mLicense = $request->getText( 'wpLicense' ); 00103 00104 $this->mDestWarningAck = $request->getText( 'wpDestFileWarningAck' ); 00105 $this->mIgnoreWarning = $request->getCheck( 'wpIgnoreWarning' ) 00106 || $request->getCheck( 'wpUploadIgnoreWarning' ); 00107 $this->mWatchthis = $request->getBool( 'wpWatchthis' ) && $this->getUser()->isLoggedIn(); 00108 $this->mCopyrightStatus = $request->getText( 'wpUploadCopyStatus' ); 00109 $this->mCopyrightSource = $request->getText( 'wpUploadSource' ); 00110 00111 $this->mForReUpload = $request->getBool( 'wpForReUpload' ); // updating a file 00112 00113 $commentDefault = ''; 00114 $commentMsg = wfMessage( 'upload-default-description' )->inContentLanguage(); 00115 if ( !$this->mForReUpload && !$commentMsg->isDisabled() ) { 00116 $commentDefault = $commentMsg->plain(); 00117 } 00118 $this->mComment = $request->getText( 'wpUploadDescription', $commentDefault ); 00119 00120 $this->mCancelUpload = $request->getCheck( 'wpCancelUpload' ) 00121 || $request->getCheck( 'wpReUpload' ); // b/w compat 00122 00123 // If it was posted check for the token (no remote POST'ing with user credentials) 00124 $token = $request->getVal( 'wpEditToken' ); 00125 $this->mTokenOk = $this->getUser()->matchEditToken( $token ); 00126 00127 $this->uploadFormTextTop = ''; 00128 $this->uploadFormTextAfterSummary = ''; 00129 } 00130 00139 public function userCanExecute( User $user ) { 00140 return UploadBase::isEnabled() && parent::userCanExecute( $user ); 00141 } 00142 00146 public function execute( $par ) { 00147 $this->setHeaders(); 00148 $this->outputHeader(); 00149 00150 # Check uploading enabled 00151 if ( !UploadBase::isEnabled() ) { 00152 throw new ErrorPageError( 'uploaddisabled', 'uploaddisabledtext' ); 00153 } 00154 00155 # Check permissions 00156 $user = $this->getUser(); 00157 $permissionRequired = UploadBase::isAllowed( $user ); 00158 if ( $permissionRequired !== true ) { 00159 throw new PermissionsError( $permissionRequired ); 00160 } 00161 00162 # Check blocks 00163 if ( $user->isBlocked() ) { 00164 throw new UserBlockedError( $user->getBlock() ); 00165 } 00166 00167 # Check whether we actually want to allow changing stuff 00168 $this->checkReadOnly(); 00169 00170 $this->loadRequest(); 00171 00172 # Unsave the temporary file in case this was a cancelled upload 00173 if ( $this->mCancelUpload ) { 00174 if ( !$this->unsaveUploadedFile() ) { 00175 # Something went wrong, so unsaveUploadedFile showed a warning 00176 return; 00177 } 00178 } 00179 00180 # Process upload or show a form 00181 if ( 00182 $this->mTokenOk && !$this->mCancelUpload && 00183 ( $this->mUpload && $this->mUploadClicked ) 00184 ) { 00185 $this->processUpload(); 00186 } else { 00187 # Backwards compatibility hook 00188 if ( !wfRunHooks( 'UploadForm:initial', array( &$this ) ) ) { 00189 wfDebug( "Hook 'UploadForm:initial' broke output of the upload form\n" ); 00190 00191 return; 00192 } 00193 $this->showUploadForm( $this->getUploadForm() ); 00194 } 00195 00196 # Cleanup 00197 if ( $this->mUpload ) { 00198 $this->mUpload->cleanupTempFile(); 00199 } 00200 } 00201 00207 protected function showUploadForm( $form ) { 00208 # Add links if file was previously deleted 00209 if ( $this->mDesiredDestName ) { 00210 $this->showViewDeletedLinks(); 00211 } 00212 00213 if ( $form instanceof HTMLForm ) { 00214 $form->show(); 00215 } else { 00216 $this->getOutput()->addHTML( $form ); 00217 } 00218 } 00219 00228 protected function getUploadForm( $message = '', $sessionKey = '', $hideIgnoreWarning = false ) { 00229 # Initialize form 00230 $context = new DerivativeContext( $this->getContext() ); 00231 $context->setTitle( $this->getPageTitle() ); // Remove subpage 00232 $form = new UploadForm( array( 00233 'watch' => $this->getWatchCheck(), 00234 'forreupload' => $this->mForReUpload, 00235 'sessionkey' => $sessionKey, 00236 'hideignorewarning' => $hideIgnoreWarning, 00237 'destwarningack' => (bool)$this->mDestWarningAck, 00238 00239 'description' => $this->mComment, 00240 'texttop' => $this->uploadFormTextTop, 00241 'textaftersummary' => $this->uploadFormTextAfterSummary, 00242 'destfile' => $this->mDesiredDestName, 00243 ), $context ); 00244 00245 # Check the token, but only if necessary 00246 if ( 00247 !$this->mTokenOk && !$this->mCancelUpload && 00248 ( $this->mUpload && $this->mUploadClicked ) 00249 ) { 00250 $form->addPreText( $this->msg( 'session_fail_preview' )->parse() ); 00251 } 00252 00253 # Give a notice if the user is uploading a file that has been deleted or moved 00254 # Note that this is independent from the message 'filewasdeleted' that requires JS 00255 $desiredTitleObj = Title::makeTitleSafe( NS_FILE, $this->mDesiredDestName ); 00256 $delNotice = ''; // empty by default 00257 if ( $desiredTitleObj instanceof Title && !$desiredTitleObj->exists() ) { 00258 LogEventsList::showLogExtract( $delNotice, array( 'delete', 'move' ), 00259 $desiredTitleObj, 00260 '', array( 'lim' => 10, 00261 'conds' => array( "log_action != 'revision'" ), 00262 'showIfEmpty' => false, 00263 'msgKey' => array( 'upload-recreate-warning' ) ) 00264 ); 00265 } 00266 $form->addPreText( $delNotice ); 00267 00268 # Add text to form 00269 $form->addPreText( '<div id="uploadtext">' . 00270 $this->msg( 'uploadtext', array( $this->mDesiredDestName ) )->parseAsBlock() . 00271 '</div>' ); 00272 # Add upload error message 00273 $form->addPreText( $message ); 00274 00275 # Add footer to form 00276 $uploadFooter = $this->msg( 'uploadfooter' ); 00277 if ( !$uploadFooter->isDisabled() ) { 00278 $form->addPostText( '<div id="mw-upload-footer-message">' 00279 . $uploadFooter->parseAsBlock() . "</div>\n" ); 00280 } 00281 00282 return $form; 00283 } 00284 00288 protected function showViewDeletedLinks() { 00289 $title = Title::makeTitleSafe( NS_FILE, $this->mDesiredDestName ); 00290 $user = $this->getUser(); 00291 // Show a subtitle link to deleted revisions (to sysops et al only) 00292 if ( $title instanceof Title ) { 00293 $count = $title->isDeleted(); 00294 if ( $count > 0 && $user->isAllowed( 'deletedhistory' ) ) { 00295 $restorelink = Linker::linkKnown( 00296 SpecialPage::getTitleFor( 'Undelete', $title->getPrefixedText() ), 00297 $this->msg( 'restorelink' )->numParams( $count )->escaped() 00298 ); 00299 $link = $this->msg( $user->isAllowed( 'delete' ) ? 'thisisdeleted' : 'viewdeleted' ) 00300 ->rawParams( $restorelink )->parseAsBlock(); 00301 $this->getOutput()->addHTML( "<div id=\"contentSub2\">{$link}</div>" ); 00302 } 00303 } 00304 } 00305 00317 protected function showRecoverableUploadError( $message ) { 00318 $sessionKey = $this->mUpload->stashSession(); 00319 $message = '<h2>' . $this->msg( 'uploaderror' )->escaped() . "</h2>\n" . 00320 '<div class="error">' . $message . "</div>\n"; 00321 00322 $form = $this->getUploadForm( $message, $sessionKey ); 00323 $form->setSubmitText( $this->msg( 'upload-tryagain' )->escaped() ); 00324 $this->showUploadForm( $form ); 00325 } 00326 00335 protected function showUploadWarning( $warnings ) { 00336 # If there are no warnings, or warnings we can ignore, return early. 00337 # mDestWarningAck is set when some javascript has shown the warning 00338 # to the user. mForReUpload is set when the user clicks the "upload a 00339 # new version" link. 00340 if ( !$warnings || ( count( $warnings ) == 1 00341 && isset( $warnings['exists'] ) 00342 && ( $this->mDestWarningAck || $this->mForReUpload ) ) 00343 ) { 00344 return false; 00345 } 00346 00347 $sessionKey = $this->mUpload->stashSession(); 00348 00349 $warningHtml = '<h2>' . $this->msg( 'uploadwarning' )->escaped() . "</h2>\n" 00350 . '<ul class="warning">'; 00351 foreach ( $warnings as $warning => $args ) { 00352 if ( $warning == 'badfilename' ) { 00353 $this->mDesiredDestName = Title::makeTitle( NS_FILE, $args )->getText(); 00354 } 00355 if ( $warning == 'exists' ) { 00356 $msg = "\t<li>" . self::getExistsWarning( $args ) . "</li>\n"; 00357 } elseif ( $warning == 'duplicate' ) { 00358 $msg = $this->getDupeWarning( $args ); 00359 } elseif ( $warning == 'duplicate-archive' ) { 00360 if ( $args === '' ) { 00361 $msg = "\t<li>" . $this->msg( 'file-deleted-duplicate-notitle' )->parse() 00362 . "</li>\n"; 00363 } else { 00364 $msg = "\t<li>" . $this->msg( 'file-deleted-duplicate', 00365 Title::makeTitle( NS_FILE, $args )->getPrefixedText() )->parse() 00366 . "</li>\n"; 00367 } 00368 } else { 00369 if ( $args === true ) { 00370 $args = array(); 00371 } elseif ( !is_array( $args ) ) { 00372 $args = array( $args ); 00373 } 00374 $msg = "\t<li>" . $this->msg( $warning, $args )->parse() . "</li>\n"; 00375 } 00376 $warningHtml .= $msg; 00377 } 00378 $warningHtml .= "</ul>\n"; 00379 $warningHtml .= $this->msg( 'uploadwarning-text' )->parseAsBlock(); 00380 00381 $form = $this->getUploadForm( $warningHtml, $sessionKey, /* $hideIgnoreWarning */ true ); 00382 $form->setSubmitText( $this->msg( 'upload-tryagain' )->text() ); 00383 $form->addButton( 'wpUploadIgnoreWarning', $this->msg( 'ignorewarning' )->text() ); 00384 $form->addButton( 'wpCancelUpload', $this->msg( 'reuploaddesc' )->text() ); 00385 00386 $this->showUploadForm( $form ); 00387 00388 # Indicate that we showed a form 00389 return true; 00390 } 00391 00397 protected function showUploadError( $message ) { 00398 $message = '<h2>' . $this->msg( 'uploadwarning' )->escaped() . "</h2>\n" . 00399 '<div class="error">' . $message . "</div>\n"; 00400 $this->showUploadForm( $this->getUploadForm( $message ) ); 00401 } 00402 00407 protected function processUpload() { 00408 // Fetch the file if required 00409 $status = $this->mUpload->fetchFile(); 00410 if ( !$status->isOK() ) { 00411 $this->showUploadError( $this->getOutput()->parse( $status->getWikiText() ) ); 00412 00413 return; 00414 } 00415 00416 if ( !wfRunHooks( 'UploadForm:BeforeProcessing', array( &$this ) ) ) { 00417 wfDebug( "Hook 'UploadForm:BeforeProcessing' broke processing the file.\n" ); 00418 // This code path is deprecated. If you want to break upload processing 00419 // do so by hooking into the appropriate hooks in UploadBase::verifyUpload 00420 // and UploadBase::verifyFile. 00421 // If you use this hook to break uploading, the user will be returned 00422 // an empty form with no error message whatsoever. 00423 return; 00424 } 00425 00426 // Upload verification 00427 $details = $this->mUpload->verifyUpload(); 00428 if ( $details['status'] != UploadBase::OK ) { 00429 $this->processVerificationError( $details ); 00430 00431 return; 00432 } 00433 00434 // Verify permissions for this title 00435 $permErrors = $this->mUpload->verifyTitlePermissions( $this->getUser() ); 00436 if ( $permErrors !== true ) { 00437 $code = array_shift( $permErrors[0] ); 00438 $this->showRecoverableUploadError( $this->msg( $code, $permErrors[0] )->parse() ); 00439 00440 return; 00441 } 00442 00443 $this->mLocalFile = $this->mUpload->getLocalFile(); 00444 00445 // Check warnings if necessary 00446 if ( !$this->mIgnoreWarning ) { 00447 $warnings = $this->mUpload->checkWarnings(); 00448 if ( $this->showUploadWarning( $warnings ) ) { 00449 return; 00450 } 00451 } 00452 00453 // Get the page text if this is not a reupload 00454 if ( !$this->mForReUpload ) { 00455 $pageText = self::getInitialPageText( $this->mComment, $this->mLicense, 00456 $this->mCopyrightStatus, $this->mCopyrightSource ); 00457 } else { 00458 $pageText = false; 00459 } 00460 00461 $status = $this->mUpload->performUpload( 00462 $this->mComment, 00463 $pageText, 00464 $this->mWatchthis, 00465 $this->getUser() 00466 ); 00467 00468 if ( !$status->isGood() ) { 00469 $this->showUploadError( $this->getOutput()->parse( $status->getWikiText() ) ); 00470 00471 return; 00472 } 00473 00474 // Success, redirect to description page 00475 $this->mUploadSuccessful = true; 00476 wfRunHooks( 'SpecialUploadComplete', array( &$this ) ); 00477 $this->getOutput()->redirect( $this->mLocalFile->getTitle()->getFullURL() ); 00478 } 00479 00488 public static function getInitialPageText( $comment = '', $license = '', 00489 $copyStatus = '', $source = '' 00490 ) { 00491 global $wgUseCopyrightUpload, $wgForceUIMsgAsContentMsg; 00492 00493 $msg = array(); 00494 /* These messages are transcluded into the actual text of the description page. 00495 * Thus, forcing them as content messages makes the upload to produce an int: template 00496 * instead of hardcoding it there in the uploader language. 00497 */ 00498 foreach ( array( 'license-header', 'filedesc', 'filestatus', 'filesource' ) as $msgName ) { 00499 if ( in_array( $msgName, (array)$wgForceUIMsgAsContentMsg ) ) { 00500 $msg[$msgName] = "{{int:$msgName}}"; 00501 } else { 00502 $msg[$msgName] = wfMessage( $msgName )->inContentLanguage()->text(); 00503 } 00504 } 00505 00506 if ( $wgUseCopyrightUpload ) { 00507 $licensetxt = ''; 00508 if ( $license != '' ) { 00509 $licensetxt = '== ' . $msg['license-header'] . " ==\n" . '{{' . $license . '}}' . "\n"; 00510 } 00511 $pageText = '== ' . $msg['filedesc'] . " ==\n" . $comment . "\n" . 00512 '== ' . $msg['filestatus'] . " ==\n" . $copyStatus . "\n" . 00513 "$licensetxt" . 00514 '== ' . $msg['filesource'] . " ==\n" . $source; 00515 } else { 00516 if ( $license != '' ) { 00517 $filedesc = $comment == '' ? '' : '== ' . $msg['filedesc'] . " ==\n" . $comment . "\n"; 00518 $pageText = $filedesc . 00519 '== ' . $msg['license-header'] . " ==\n" . '{{' . $license . '}}' . "\n"; 00520 } else { 00521 $pageText = $comment; 00522 } 00523 } 00524 00525 return $pageText; 00526 } 00527 00540 protected function getWatchCheck() { 00541 if ( $this->getUser()->getOption( 'watchdefault' ) ) { 00542 // Watch all edits! 00543 return true; 00544 } 00545 00546 $desiredTitleObj = Title::makeTitleSafe( NS_FILE, $this->mDesiredDestName ); 00547 if ( $desiredTitleObj instanceof Title && $this->getUser()->isWatched( $desiredTitleObj ) ) { 00548 // Already watched, don't change that 00549 return true; 00550 } 00551 00552 $local = wfLocalFile( $this->mDesiredDestName ); 00553 if ( $local && $local->exists() ) { 00554 // We're uploading a new version of an existing file. 00555 // No creation, so don't watch it if we're not already. 00556 return false; 00557 } else { 00558 // New page should get watched if that's our option. 00559 return $this->getUser()->getOption( 'watchcreations' ); 00560 } 00561 } 00562 00569 protected function processVerificationError( $details ) { 00570 global $wgFileExtensions; 00571 00572 switch ( $details['status'] ) { 00573 00575 case UploadBase::MIN_LENGTH_PARTNAME: 00576 $this->showRecoverableUploadError( $this->msg( 'minlength1' )->escaped() ); 00577 break; 00578 case UploadBase::ILLEGAL_FILENAME: 00579 $this->showRecoverableUploadError( $this->msg( 'illegalfilename', 00580 $details['filtered'] )->parse() ); 00581 break; 00582 case UploadBase::FILENAME_TOO_LONG: 00583 $this->showRecoverableUploadError( $this->msg( 'filename-toolong' )->escaped() ); 00584 break; 00585 case UploadBase::FILETYPE_MISSING: 00586 $this->showRecoverableUploadError( $this->msg( 'filetype-missing' )->parse() ); 00587 break; 00588 case UploadBase::WINDOWS_NONASCII_FILENAME: 00589 $this->showRecoverableUploadError( $this->msg( 'windows-nonascii-filename' )->parse() ); 00590 break; 00591 00593 case UploadBase::EMPTY_FILE: 00594 $this->showUploadError( $this->msg( 'emptyfile' )->escaped() ); 00595 break; 00596 case UploadBase::FILE_TOO_LARGE: 00597 $this->showUploadError( $this->msg( 'largefileserver' )->escaped() ); 00598 break; 00599 case UploadBase::FILETYPE_BADTYPE: 00600 $msg = $this->msg( 'filetype-banned-type' ); 00601 if ( isset( $details['blacklistedExt'] ) ) { 00602 $msg->params( $this->getLanguage()->commaList( $details['blacklistedExt'] ) ); 00603 } else { 00604 $msg->params( $details['finalExt'] ); 00605 } 00606 $extensions = array_unique( $wgFileExtensions ); 00607 $msg->params( $this->getLanguage()->commaList( $extensions ), 00608 count( $extensions ) ); 00609 00610 // Add PLURAL support for the first parameter. This results 00611 // in a bit unlogical parameter sequence, but does not break 00612 // old translations 00613 if ( isset( $details['blacklistedExt'] ) ) { 00614 $msg->params( count( $details['blacklistedExt'] ) ); 00615 } else { 00616 $msg->params( 1 ); 00617 } 00618 00619 $this->showUploadError( $msg->parse() ); 00620 break; 00621 case UploadBase::VERIFICATION_ERROR: 00622 unset( $details['status'] ); 00623 $code = array_shift( $details['details'] ); 00624 $this->showUploadError( $this->msg( $code, $details['details'] )->parse() ); 00625 break; 00626 case UploadBase::HOOK_ABORTED: 00627 if ( is_array( $details['error'] ) ) { # allow hooks to return error details in an array 00628 $args = $details['error']; 00629 $error = array_shift( $args ); 00630 } else { 00631 $error = $details['error']; 00632 $args = null; 00633 } 00634 00635 $this->showUploadError( $this->msg( $error, $args )->parse() ); 00636 break; 00637 default: 00638 throw new MWException( __METHOD__ . ": Unknown value `{$details['status']}`" ); 00639 } 00640 } 00641 00647 protected function unsaveUploadedFile() { 00648 if ( !( $this->mUpload instanceof UploadFromStash ) ) { 00649 return true; 00650 } 00651 $success = $this->mUpload->unsaveUploadedFile(); 00652 if ( !$success ) { 00653 $this->getOutput()->showFileDeleteError( $this->mUpload->getTempPath() ); 00654 00655 return false; 00656 } else { 00657 return true; 00658 } 00659 } 00660 00661 /*** Functions for formatting warnings ***/ 00662 00670 public static function getExistsWarning( $exists ) { 00671 if ( !$exists ) { 00672 return ''; 00673 } 00674 00675 $file = $exists['file']; 00676 $filename = $file->getTitle()->getPrefixedText(); 00677 $warning = ''; 00678 00679 if ( $exists['warning'] == 'exists' ) { 00680 // Exact match 00681 $warning = wfMessage( 'fileexists', $filename )->parse(); 00682 } elseif ( $exists['warning'] == 'page-exists' ) { 00683 // Page exists but file does not 00684 $warning = wfMessage( 'filepageexists', $filename )->parse(); 00685 } elseif ( $exists['warning'] == 'exists-normalized' ) { 00686 $warning = wfMessage( 'fileexists-extension', $filename, 00687 $exists['normalizedFile']->getTitle()->getPrefixedText() )->parse(); 00688 } elseif ( $exists['warning'] == 'thumb' ) { 00689 // Swapped argument order compared with other messages for backwards compatibility 00690 $warning = wfMessage( 'fileexists-thumbnail-yes', 00691 $exists['thumbFile']->getTitle()->getPrefixedText(), $filename )->parse(); 00692 } elseif ( $exists['warning'] == 'thumb-name' ) { 00693 // Image w/o '180px-' does not exists, but we do not like these filenames 00694 $name = $file->getName(); 00695 $badPart = substr( $name, 0, strpos( $name, '-' ) + 1 ); 00696 $warning = wfMessage( 'file-thumbnail-no', $badPart )->parse(); 00697 } elseif ( $exists['warning'] == 'bad-prefix' ) { 00698 $warning = wfMessage( 'filename-bad-prefix', $exists['prefix'] )->parse(); 00699 } elseif ( $exists['warning'] == 'was-deleted' ) { 00700 # If the file existed before and was deleted, warn the user of this 00701 $ltitle = SpecialPage::getTitleFor( 'Log' ); 00702 $llink = Linker::linkKnown( 00703 $ltitle, 00704 wfMessage( 'deletionlog' )->escaped(), 00705 array(), 00706 array( 00707 'type' => 'delete', 00708 'page' => $filename 00709 ) 00710 ); 00711 $warning = wfMessage( 'filewasdeleted' )->rawParams( $llink )->parseAsBlock(); 00712 } 00713 00714 return $warning; 00715 } 00716 00722 public function getDupeWarning( $dupes ) { 00723 if ( !$dupes ) { 00724 return ''; 00725 } 00726 00727 $gallery = ImageGalleryBase::factory(); 00728 $gallery->setContext( $this->getContext() ); 00729 $gallery->setShowBytes( false ); 00730 foreach ( $dupes as $file ) { 00731 $gallery->add( $file->getTitle() ); 00732 } 00733 00734 return '<li>' . 00735 wfMessage( 'file-exists-duplicate' )->numParams( count( $dupes ) )->parse() . 00736 $gallery->toHtml() . "</li>\n"; 00737 } 00738 00739 protected function getGroupName() { 00740 return 'media'; 00741 } 00742 } 00743 00747 class UploadForm extends HTMLForm { 00748 protected $mWatch; 00749 protected $mForReUpload; 00750 protected $mSessionKey; 00751 protected $mHideIgnoreWarning; 00752 protected $mDestWarningAck; 00753 protected $mDestFile; 00754 00755 protected $mComment; 00756 protected $mTextTop; 00757 protected $mTextAfterSummary; 00758 00759 protected $mSourceIds; 00760 00761 protected $mMaxFileSize = array(); 00762 00763 protected $mMaxUploadSize = array(); 00764 00765 public function __construct( array $options = array(), IContextSource $context = null ) { 00766 $this->mWatch = !empty( $options['watch'] ); 00767 $this->mForReUpload = !empty( $options['forreupload'] ); 00768 $this->mSessionKey = isset( $options['sessionkey'] ) ? $options['sessionkey'] : ''; 00769 $this->mHideIgnoreWarning = !empty( $options['hideignorewarning'] ); 00770 $this->mDestWarningAck = !empty( $options['destwarningack'] ); 00771 $this->mDestFile = isset( $options['destfile'] ) ? $options['destfile'] : ''; 00772 00773 $this->mComment = isset( $options['description'] ) ? 00774 $options['description'] : ''; 00775 00776 $this->mTextTop = isset( $options['texttop'] ) 00777 ? $options['texttop'] : ''; 00778 00779 $this->mTextAfterSummary = isset( $options['textaftersummary'] ) 00780 ? $options['textaftersummary'] : ''; 00781 00782 $sourceDescriptor = $this->getSourceSection(); 00783 $descriptor = $sourceDescriptor 00784 + $this->getDescriptionSection() 00785 + $this->getOptionsSection(); 00786 00787 wfRunHooks( 'UploadFormInitDescriptor', array( &$descriptor ) ); 00788 parent::__construct( $descriptor, $context, 'upload' ); 00789 00790 # Set some form properties 00791 $this->setSubmitText( $this->msg( 'uploadbtn' )->text() ); 00792 $this->setSubmitName( 'wpUpload' ); 00793 # Used message keys: 'accesskey-upload', 'tooltip-upload' 00794 $this->setSubmitTooltip( 'upload' ); 00795 $this->setId( 'mw-upload-form' ); 00796 00797 # Build a list of IDs for javascript insertion 00798 $this->mSourceIds = array(); 00799 foreach ( $sourceDescriptor as $field ) { 00800 if ( !empty( $field['id'] ) ) { 00801 $this->mSourceIds[] = $field['id']; 00802 } 00803 } 00804 } 00805 00812 protected function getSourceSection() { 00813 global $wgCopyUploadsFromSpecialUpload; 00814 00815 if ( $this->mSessionKey ) { 00816 return array( 00817 'SessionKey' => array( 00818 'type' => 'hidden', 00819 'default' => $this->mSessionKey, 00820 ), 00821 'SourceType' => array( 00822 'type' => 'hidden', 00823 'default' => 'Stash', 00824 ), 00825 ); 00826 } 00827 00828 $canUploadByUrl = UploadFromUrl::isEnabled() 00829 && UploadFromUrl::isAllowed( $this->getUser() ) 00830 && $wgCopyUploadsFromSpecialUpload; 00831 $radio = $canUploadByUrl; 00832 $selectedSourceType = strtolower( $this->getRequest()->getText( 'wpSourceType', 'File' ) ); 00833 00834 $descriptor = array(); 00835 if ( $this->mTextTop ) { 00836 $descriptor['UploadFormTextTop'] = array( 00837 'type' => 'info', 00838 'section' => 'source', 00839 'default' => $this->mTextTop, 00840 'raw' => true, 00841 ); 00842 } 00843 00844 $this->mMaxUploadSize['file'] = UploadBase::getMaxUploadSize( 'file' ); 00845 # Limit to upload_max_filesize unless we are running under HipHop and 00846 # that setting doesn't exist 00847 if ( !wfIsHHVM() ) { 00848 $this->mMaxUploadSize['file'] = min( $this->mMaxUploadSize['file'], 00849 wfShorthandToInteger( ini_get( 'upload_max_filesize' ) ), 00850 wfShorthandToInteger( ini_get( 'post_max_size' ) ) 00851 ); 00852 } 00853 00854 $descriptor['UploadFile'] = array( 00855 'class' => 'UploadSourceField', 00856 'section' => 'source', 00857 'type' => 'file', 00858 'id' => 'wpUploadFile', 00859 'radio-id' => 'wpSourceTypeFile', 00860 'label-message' => 'sourcefilename', 00861 'upload-type' => 'File', 00862 'radio' => &$radio, 00863 'help' => $this->msg( 'upload-maxfilesize', 00864 $this->getContext()->getLanguage()->formatSize( $this->mMaxUploadSize['file'] ) 00865 )->parse() . 00866 $this->msg( 'word-separator' )->escaped() . 00867 $this->msg( 'upload_source_file' )->escaped(), 00868 'checked' => $selectedSourceType == 'file', 00869 ); 00870 00871 if ( $canUploadByUrl ) { 00872 $this->mMaxUploadSize['url'] = UploadBase::getMaxUploadSize( 'url' ); 00873 $descriptor['UploadFileURL'] = array( 00874 'class' => 'UploadSourceField', 00875 'section' => 'source', 00876 'id' => 'wpUploadFileURL', 00877 'radio-id' => 'wpSourceTypeurl', 00878 'label-message' => 'sourceurl', 00879 'upload-type' => 'url', 00880 'radio' => &$radio, 00881 'help' => $this->msg( 'upload-maxfilesize', 00882 $this->getContext()->getLanguage()->formatSize( $this->mMaxUploadSize['url'] ) 00883 )->parse() . 00884 $this->msg( 'word-separator' )->escaped() . 00885 $this->msg( 'upload_source_url' )->escaped(), 00886 'checked' => $selectedSourceType == 'url', 00887 ); 00888 } 00889 wfRunHooks( 'UploadFormSourceDescriptors', array( &$descriptor, &$radio, $selectedSourceType ) ); 00890 00891 $descriptor['Extensions'] = array( 00892 'type' => 'info', 00893 'section' => 'source', 00894 'default' => $this->getExtensionsMessage(), 00895 'raw' => true, 00896 ); 00897 00898 return $descriptor; 00899 } 00900 00906 protected function getExtensionsMessage() { 00907 # Print a list of allowed file extensions, if so configured. We ignore 00908 # MIME type here, it's incomprehensible to most people and too long. 00909 global $wgCheckFileExtensions, $wgStrictFileExtensions, 00910 $wgFileExtensions, $wgFileBlacklist; 00911 00912 if ( $wgCheckFileExtensions ) { 00913 if ( $wgStrictFileExtensions ) { 00914 # Everything not permitted is banned 00915 $extensionsList = 00916 '<div id="mw-upload-permitted">' . 00917 $this->msg( 00918 'upload-permitted', 00919 $this->getContext()->getLanguage()->commaList( array_unique( $wgFileExtensions ) ) 00920 )->parseAsBlock() . 00921 "</div>\n"; 00922 } else { 00923 # We have to list both preferred and prohibited 00924 $extensionsList = 00925 '<div id="mw-upload-preferred">' . 00926 $this->msg( 00927 'upload-preferred', 00928 $this->getContext()->getLanguage()->commaList( array_unique( $wgFileExtensions ) ) 00929 )->parseAsBlock() . 00930 "</div>\n" . 00931 '<div id="mw-upload-prohibited">' . 00932 $this->msg( 00933 'upload-prohibited', 00934 $this->getContext()->getLanguage()->commaList( array_unique( $wgFileBlacklist ) ) 00935 )->parseAsBlock() . 00936 "</div>\n"; 00937 } 00938 } else { 00939 # Everything is permitted. 00940 $extensionsList = ''; 00941 } 00942 00943 return $extensionsList; 00944 } 00945 00952 protected function getDescriptionSection() { 00953 if ( $this->mSessionKey ) { 00954 $stash = RepoGroup::singleton()->getLocalRepo()->getUploadStash(); 00955 try { 00956 $file = $stash->getFile( $this->mSessionKey ); 00957 } catch ( MWException $e ) { 00958 $file = null; 00959 } 00960 if ( $file ) { 00961 global $wgContLang; 00962 00963 $mto = $file->transform( array( 'width' => 120 ) ); 00964 $this->addHeaderText( 00965 '<div class="thumb t' . $wgContLang->alignEnd() . '">' . 00966 Html::element( 'img', array( 00967 'src' => $mto->getUrl(), 00968 'class' => 'thumbimage', 00969 ) ) . '</div>', 'description' ); 00970 } 00971 } 00972 00973 $descriptor = array( 00974 'DestFile' => array( 00975 'type' => 'text', 00976 'section' => 'description', 00977 'id' => 'wpDestFile', 00978 'label-message' => 'destfilename', 00979 'size' => 60, 00980 'default' => $this->mDestFile, 00981 # @todo FIXME: Hack to work around poor handling of the 'default' option in HTMLForm 00982 'nodata' => strval( $this->mDestFile ) !== '', 00983 ), 00984 'UploadDescription' => array( 00985 'type' => 'textarea', 00986 'section' => 'description', 00987 'id' => 'wpUploadDescription', 00988 'label-message' => $this->mForReUpload 00989 ? 'filereuploadsummary' 00990 : 'fileuploadsummary', 00991 'default' => $this->mComment, 00992 'cols' => $this->getUser()->getIntOption( 'cols' ), 00993 'rows' => 8, 00994 ) 00995 ); 00996 if ( $this->mTextAfterSummary ) { 00997 $descriptor['UploadFormTextAfterSummary'] = array( 00998 'type' => 'info', 00999 'section' => 'description', 01000 'default' => $this->mTextAfterSummary, 01001 'raw' => true, 01002 ); 01003 } 01004 01005 $descriptor += array( 01006 'EditTools' => array( 01007 'type' => 'edittools', 01008 'section' => 'description', 01009 'message' => 'edittools-upload', 01010 ) 01011 ); 01012 01013 if ( $this->mForReUpload ) { 01014 $descriptor['DestFile']['readonly'] = true; 01015 } else { 01016 $descriptor['License'] = array( 01017 'type' => 'select', 01018 'class' => 'Licenses', 01019 'section' => 'description', 01020 'id' => 'wpLicense', 01021 'label-message' => 'license', 01022 ); 01023 } 01024 01025 global $wgUseCopyrightUpload; 01026 if ( $wgUseCopyrightUpload ) { 01027 $descriptor['UploadCopyStatus'] = array( 01028 'type' => 'text', 01029 'section' => 'description', 01030 'id' => 'wpUploadCopyStatus', 01031 'label-message' => 'filestatus', 01032 ); 01033 $descriptor['UploadSource'] = array( 01034 'type' => 'text', 01035 'section' => 'description', 01036 'id' => 'wpUploadSource', 01037 'label-message' => 'filesource', 01038 ); 01039 } 01040 01041 return $descriptor; 01042 } 01043 01050 protected function getOptionsSection() { 01051 $user = $this->getUser(); 01052 if ( $user->isLoggedIn() ) { 01053 $descriptor = array( 01054 'Watchthis' => array( 01055 'type' => 'check', 01056 'id' => 'wpWatchthis', 01057 'label-message' => 'watchthisupload', 01058 'section' => 'options', 01059 'default' => $this->mWatch, 01060 ) 01061 ); 01062 } 01063 if ( !$this->mHideIgnoreWarning ) { 01064 $descriptor['IgnoreWarning'] = array( 01065 'type' => 'check', 01066 'id' => 'wpIgnoreWarning', 01067 'label-message' => 'ignorewarnings', 01068 'section' => 'options', 01069 ); 01070 } 01071 01072 $descriptor['DestFileWarningAck'] = array( 01073 'type' => 'hidden', 01074 'id' => 'wpDestFileWarningAck', 01075 'default' => $this->mDestWarningAck ? '1' : '', 01076 ); 01077 01078 if ( $this->mForReUpload ) { 01079 $descriptor['ForReUpload'] = array( 01080 'type' => 'hidden', 01081 'id' => 'wpForReUpload', 01082 'default' => '1', 01083 ); 01084 } 01085 01086 return $descriptor; 01087 } 01088 01092 public function show() { 01093 $this->addUploadJS(); 01094 parent::show(); 01095 } 01096 01100 protected function addUploadJS() { 01101 global $wgUseAjax, $wgAjaxUploadDestCheck, $wgAjaxLicensePreview, 01102 $wgEnableAPI, $wgStrictFileExtensions; 01103 01104 $useAjaxDestCheck = $wgUseAjax && $wgAjaxUploadDestCheck; 01105 $useAjaxLicensePreview = $wgUseAjax && $wgAjaxLicensePreview && $wgEnableAPI; 01106 $this->mMaxUploadSize['*'] = UploadBase::getMaxUploadSize(); 01107 01108 $scriptVars = array( 01109 'wgAjaxUploadDestCheck' => $useAjaxDestCheck, 01110 'wgAjaxLicensePreview' => $useAjaxLicensePreview, 01111 'wgUploadAutoFill' => !$this->mForReUpload && 01112 // If we received mDestFile from the request, don't autofill 01113 // the wpDestFile textbox 01114 $this->mDestFile === '', 01115 'wgUploadSourceIds' => $this->mSourceIds, 01116 'wgStrictFileExtensions' => $wgStrictFileExtensions, 01117 'wgCapitalizeUploads' => MWNamespace::isCapitalized( NS_FILE ), 01118 'wgMaxUploadSize' => $this->mMaxUploadSize, 01119 ); 01120 01121 $out = $this->getOutput(); 01122 $out->addJsConfigVars( $scriptVars ); 01123 01124 $out->addModules( array( 01125 'mediawiki.action.edit', // For <charinsert> support 01126 'mediawiki.legacy.upload', // Old form stuff... 01127 'mediawiki.special.upload', // Newer extras for thumbnail preview. 01128 ) ); 01129 } 01130 01136 function trySubmit() { 01137 return false; 01138 } 01139 } 01140 01144 class UploadSourceField extends HTMLTextField { 01145 01150 function getLabelHtml( $cellAttributes = array() ) { 01151 $id = $this->mParams['id']; 01152 $label = Html::rawElement( 'label', array( 'for' => $id ), $this->mLabel ); 01153 01154 if ( !empty( $this->mParams['radio'] ) ) { 01155 if ( isset( $this->mParams['radio-id'] ) ) { 01156 $radioId = $this->mParams['radio-id']; 01157 } else { 01158 // Old way. For the benefit of extensions that do not define 01159 // the 'radio-id' key. 01160 $radioId = 'wpSourceType' . $this->mParams['upload-type']; 01161 } 01162 01163 $attribs = array( 01164 'name' => 'wpSourceType', 01165 'type' => 'radio', 01166 'id' => $radioId, 01167 'value' => $this->mParams['upload-type'], 01168 ); 01169 01170 if ( !empty( $this->mParams['checked'] ) ) { 01171 $attribs['checked'] = 'checked'; 01172 } 01173 01174 $label .= Html::element( 'input', $attribs ); 01175 } 01176 01177 return Html::rawElement( 'td', array( 'class' => 'mw-label' ) + $cellAttributes, $label ); 01178 } 01179 01183 function getSize() { 01184 return isset( $this->mParams['size'] ) 01185 ? $this->mParams['size'] 01186 : 60; 01187 } 01188 }