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