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