MediaWiki  REL1_22
SpecialUpload.php
Go to the documentation of this file.
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 }