MediaWiki  REL1_20
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 == 'exists' ) {
00340                                 $msg = "\t<li>" . self::getExistsWarning( $args ) . "</li>\n";
00341                         } elseif( $warning == 'duplicate' ) {
00342                                 $msg = self::getDupeWarning( $args );
00343                         } elseif( $warning == 'duplicate-archive' ) {
00344                                 $msg = "\t<li>" . $this->msg( 'file-deleted-duplicate',
00345                                                 Title::makeTitle( NS_FILE, $args )->getPrefixedText() )->parse()
00346                                         . "</li>\n";
00347                         } else {
00348                                 if ( $args === true ) {
00349                                         $args = array();
00350                                 } elseif ( !is_array( $args ) ) {
00351                                         $args = array( $args );
00352                                 }
00353                                 $msg = "\t<li>" . $this->msg( $warning, $args )->parse() . "</li>\n";
00354                         }
00355                         $warningHtml .= $msg;
00356                 }
00357                 $warningHtml .= "</ul>\n";
00358                 $warningHtml .= $this->msg( 'uploadwarning-text' )->parseAsBlock();
00359 
00360                 $form = $this->getUploadForm( $warningHtml, $sessionKey, /* $hideIgnoreWarning */ true );
00361                 $form->setSubmitText( $this->msg( 'upload-tryagain' )->text() );
00362                 $form->addButton( 'wpUploadIgnoreWarning', $this->msg( 'ignorewarning' )->text() );
00363                 $form->addButton( 'wpCancelUpload', $this->msg( 'reuploaddesc' )->text() );
00364 
00365                 $this->showUploadForm( $form );
00366 
00367                 # Indicate that we showed a form
00368                 return true;
00369         }
00370 
00376         protected function showUploadError( $message ) {
00377                 $message = '<h2>' . $this->msg( 'uploadwarning' )->escaped() . "</h2>\n" .
00378                         '<div class="error">' . $message . "</div>\n";
00379                 $this->showUploadForm( $this->getUploadForm( $message ) );
00380         }
00381 
00386         protected function processUpload() {
00387                 // Fetch the file if required
00388                 $status = $this->mUpload->fetchFile();
00389                 if( !$status->isOK() ) {
00390                         $this->showUploadError( $this->getOutput()->parse( $status->getWikiText() ) );
00391                         return;
00392                 }
00393 
00394                 if( !wfRunHooks( 'UploadForm:BeforeProcessing', array( &$this ) ) ) {
00395                         wfDebug( "Hook 'UploadForm:BeforeProcessing' broke processing the file.\n" );
00396                         // This code path is deprecated. If you want to break upload processing
00397                         // do so by hooking into the appropriate hooks in UploadBase::verifyUpload
00398                         // and UploadBase::verifyFile.
00399                         // If you use this hook to break uploading, the user will be returned
00400                         // an empty form with no error message whatsoever.
00401                         return;
00402                 }
00403 
00404                 // Upload verification
00405                 $details = $this->mUpload->verifyUpload();
00406                 if ( $details['status'] != UploadBase::OK ) {
00407                         $this->processVerificationError( $details );
00408                         return;
00409                 }
00410 
00411                 // Verify permissions for this title
00412                 $permErrors = $this->mUpload->verifyTitlePermissions( $this->getUser() );
00413                 if( $permErrors !== true ) {
00414                         $code = array_shift( $permErrors[0] );
00415                         $this->showRecoverableUploadError( $this->msg( $code, $permErrors[0] )->parse() );
00416                         return;
00417                 }
00418 
00419                 $this->mLocalFile = $this->mUpload->getLocalFile();
00420 
00421                 // Check warnings if necessary
00422                 if( !$this->mIgnoreWarning ) {
00423                         $warnings = $this->mUpload->checkWarnings();
00424                         if( $this->showUploadWarning( $warnings ) ) {
00425                                 return;
00426                         }
00427                 }
00428 
00429                 // Get the page text if this is not a reupload
00430                 if( !$this->mForReUpload ) {
00431                         $pageText = self::getInitialPageText( $this->mComment, $this->mLicense,
00432                                 $this->mCopyrightStatus, $this->mCopyrightSource );
00433                 } else {
00434                         $pageText = false;
00435                 }
00436                 $status = $this->mUpload->performUpload( $this->mComment, $pageText, $this->mWatchthis, $this->getUser() );
00437                 if ( !$status->isGood() ) {
00438                         $this->showUploadError( $this->getOutput()->parse( $status->getWikiText() ) );
00439                         return;
00440                 }
00441 
00442                 // Success, redirect to description page
00443                 $this->mUploadSuccessful = true;
00444                 wfRunHooks( 'SpecialUploadComplete', array( &$this ) );
00445                 $this->getOutput()->redirect( $this->mLocalFile->getTitle()->getFullURL() );
00446         }
00447 
00456         public static function getInitialPageText( $comment = '', $license = '', $copyStatus = '', $source = '' ) {
00457                 global $wgUseCopyrightUpload, $wgForceUIMsgAsContentMsg;
00458                 $wgForceUIMsgAsContentMsg = (array) $wgForceUIMsgAsContentMsg;
00459 
00460                 $msg = array();
00461                 /* These messages are transcluded into the actual text of the description page.
00462                  * Thus, forcing them as content messages makes the upload to produce an int: template
00463                  * instead of hardcoding it there in the uploader language.
00464                  */
00465                 foreach( array( 'license-header', 'filedesc', 'filestatus', 'filesource' ) as $msgName ) {
00466                         if ( in_array( $msgName, $wgForceUIMsgAsContentMsg ) ) {
00467                                 $msg[$msgName] = "{{int:$msgName}}";
00468                         } else {
00469                                 $msg[$msgName] = wfMessage( $msgName )->inContentLanguage()->text();
00470                         }
00471                 }
00472 
00473                 if ( $wgUseCopyrightUpload ) {
00474                         $licensetxt = '';
00475                         if ( $license != '' ) {
00476                                 $licensetxt = '== ' . $msg[ 'license-header' ] . " ==\n" . '{{' . $license . '}}' . "\n";
00477                         }
00478                         $pageText = '== ' . $msg[ 'filedesc' ] . " ==\n" . $comment . "\n" .
00479                                 '== ' . $msg[ 'filestatus' ] . " ==\n" . $copyStatus . "\n" .
00480                                 "$licensetxt" .
00481                                 '== ' . $msg[ 'filesource' ] . " ==\n" . $source;
00482                 } else {
00483                         if ( $license != '' ) {
00484                                 $filedesc = $comment == '' ? '' : '== ' . $msg[ 'filedesc' ] . " ==\n" . $comment . "\n";
00485                                         $pageText = $filedesc .
00486                                         '== ' . $msg[ 'license-header' ] . " ==\n" . '{{' . $license . '}}' . "\n";
00487                         } else {
00488                                 $pageText = $comment;
00489                         }
00490                 }
00491                 return $pageText;
00492         }
00493 
00506         protected function getWatchCheck() {
00507                 if( $this->getUser()->getOption( 'watchdefault' ) ) {
00508                         // Watch all edits!
00509                         return true;
00510                 }
00511 
00512                 $local = wfLocalFile( $this->mDesiredDestName );
00513                 if( $local && $local->exists() ) {
00514                         // We're uploading a new version of an existing file.
00515                         // No creation, so don't watch it if we're not already.
00516                         return $this->getUser()->isWatched( $local->getTitle() );
00517                 } else {
00518                         // New page should get watched if that's our option.
00519                         return $this->getUser()->getOption( 'watchcreations' );
00520                 }
00521         }
00522 
00523 
00529         protected function processVerificationError( $details ) {
00530                 global $wgFileExtensions;
00531 
00532                 switch( $details['status'] ) {
00533 
00535                         case UploadBase::MIN_LENGTH_PARTNAME:
00536                                 $this->showRecoverableUploadError( $this->msg( 'minlength1' )->escaped() );
00537                                 break;
00538                         case UploadBase::ILLEGAL_FILENAME:
00539                                 $this->showRecoverableUploadError( $this->msg( 'illegalfilename',
00540                                         $details['filtered'] )->parse() );
00541                                 break;
00542                         case UploadBase::FILENAME_TOO_LONG:
00543                                 $this->showRecoverableUploadError( $this->msg( 'filename-toolong' )->escaped() );
00544                                 break;
00545                         case UploadBase::FILETYPE_MISSING:
00546                                 $this->showRecoverableUploadError( $this->msg( 'filetype-missing' )->parse() );
00547                                 break;
00548                         case UploadBase::WINDOWS_NONASCII_FILENAME:
00549                                 $this->showRecoverableUploadError( $this->msg( 'windows-nonascii-filename' )->parse() );
00550                                 break;
00551 
00553                         case UploadBase::EMPTY_FILE:
00554                                 $this->showUploadError( $this->msg( 'emptyfile' )->escaped() );
00555                                 break;
00556                         case UploadBase::FILE_TOO_LARGE:
00557                                 $this->showUploadError( $this->msg( 'largefileserver' )->escaped() );
00558                                 break;
00559                         case UploadBase::FILETYPE_BADTYPE:
00560                                 $msg = $this->msg( 'filetype-banned-type' );
00561                                 if ( isset( $details['blacklistedExt'] ) ) {
00562                                         $msg->params( $this->getLanguage()->commaList( $details['blacklistedExt'] ) );
00563                                 } else {
00564                                         $msg->params( $details['finalExt'] );
00565                                 }
00566                                 $msg->params( $this->getLanguage()->commaList( $wgFileExtensions ),
00567                                         count( $wgFileExtensions ) );
00568 
00569                                 // Add PLURAL support for the first parameter. This results
00570                                 // in a bit unlogical parameter sequence, but does not break
00571                                 // old translations
00572                                 if ( isset( $details['blacklistedExt'] ) ) {
00573                                         $msg->params( count( $details['blacklistedExt'] ) );
00574                                 } else {
00575                                         $msg->params( 1 );
00576                                 }
00577 
00578                                 $this->showUploadError( $msg->parse() );
00579                                 break;
00580                         case UploadBase::VERIFICATION_ERROR:
00581                                 unset( $details['status'] );
00582                                 $code = array_shift( $details['details'] );
00583                                 $this->showUploadError( $this->msg( $code, $details['details'] )->parse() );
00584                                 break;
00585                         case UploadBase::HOOK_ABORTED:
00586                                 if ( is_array( $details['error'] ) ) { # allow hooks to return error details in an array
00587                                         $args = $details['error'];
00588                                         $error = array_shift( $args );
00589                                 } else {
00590                                         $error = $details['error'];
00591                                         $args = null;
00592                                 }
00593 
00594                                 $this->showUploadError( $this->msg( $error, $args )->parse() );
00595                                 break;
00596                         default:
00597                                 throw new MWException( __METHOD__ . ": Unknown value `{$details['status']}`" );
00598                 }
00599         }
00600 
00606         protected function unsaveUploadedFile() {
00607                 if ( !( $this->mUpload instanceof UploadFromStash ) ) {
00608                         return true;
00609                 }
00610                 $success = $this->mUpload->unsaveUploadedFile();
00611                 if ( !$success ) {
00612                         $this->getOutput()->showFileDeleteError( $this->mUpload->getTempPath() );
00613                         return false;
00614                 } else {
00615                         return true;
00616                 }
00617         }
00618 
00619         /*** Functions for formatting warnings ***/
00620 
00628         public static function getExistsWarning( $exists ) {
00629                 if ( !$exists ) {
00630                         return '';
00631                 }
00632 
00633                 $file = $exists['file'];
00634                 $filename = $file->getTitle()->getPrefixedText();
00635                 $warning = '';
00636 
00637                 if( $exists['warning'] == 'exists' ) {
00638                         // Exact match
00639                         $warning = wfMessage( 'fileexists', $filename )->parse();
00640                 } elseif( $exists['warning'] == 'page-exists' ) {
00641                         // Page exists but file does not
00642                         $warning = wfMessage( 'filepageexists', $filename )->parse();
00643                 } elseif ( $exists['warning'] == 'exists-normalized' ) {
00644                         $warning = wfMessage( 'fileexists-extension', $filename,
00645                                 $exists['normalizedFile']->getTitle()->getPrefixedText() )->parse();
00646                 } elseif ( $exists['warning'] == 'thumb' ) {
00647                         // Swapped argument order compared with other messages for backwards compatibility
00648                         $warning = wfMessage( 'fileexists-thumbnail-yes', 
00649                                 $exists['thumbFile']->getTitle()->getPrefixedText(), $filename )->parse();
00650                 } elseif ( $exists['warning'] == 'thumb-name' ) {
00651                         // Image w/o '180px-' does not exists, but we do not like these filenames
00652                         $name = $file->getName();
00653                         $badPart = substr( $name, 0, strpos( $name, '-' ) + 1 );
00654                         $warning = wfMessage( 'file-thumbnail-no', $badPart )->parse();
00655                 } elseif ( $exists['warning'] == 'bad-prefix' ) {
00656                         $warning = wfMessage( 'filename-bad-prefix', $exists['prefix'] )->parse();
00657                 } elseif ( $exists['warning'] == 'was-deleted' ) {
00658                         # If the file existed before and was deleted, warn the user of this
00659                         $ltitle = SpecialPage::getTitleFor( 'Log' );
00660                         $llink = Linker::linkKnown(
00661                                 $ltitle,
00662                                 wfMessage( 'deletionlog' )->escaped(),
00663                                 array(),
00664                                 array(
00665                                         'type' => 'delete',
00666                                         'page' => $filename
00667                                 )
00668                         );
00669                         $warning = wfMessage( 'filewasdeleted' )->rawParams( $llink )->parseAsBlock();
00670                 }
00671 
00672                 return $warning;
00673         }
00674 
00681         public static function ajaxGetExistsWarning( $filename ) {
00682                 $file = wfFindFile( $filename );
00683                 if( !$file ) {
00684                         // Force local file so we have an object to do further checks against
00685                         // if there isn't an exact match...
00686                         $file = wfLocalFile( $filename );
00687                 }
00688                 $s = '&#160;';
00689                 if ( $file ) {
00690                         $exists = UploadBase::getExistsWarning( $file );
00691                         $warning = self::getExistsWarning( $exists );
00692                         if ( $warning !== '' ) {
00693                                 $s = "<div>$warning</div>";
00694                         }
00695                 }
00696                 return $s;
00697         }
00698 
00704         public static function getDupeWarning( $dupes ) {
00705                 if ( !$dupes ) {
00706                         return '';
00707                 }
00708 
00709                 $gallery = new ImageGallery;
00710                 $gallery->setShowBytes( false );
00711                 foreach( $dupes as $file ) {
00712                         $gallery->add( $file->getTitle() );
00713                 }
00714                 return '<li>' .
00715                         wfMessage( 'file-exists-duplicate' )->numParams( count( $dupes ) )->parse() .
00716                         $gallery->toHtml() . "</li>\n";
00717         }
00718 
00719 }
00720 
00724 class UploadForm extends HTMLForm {
00725         protected $mWatch;
00726         protected $mForReUpload;
00727         protected $mSessionKey;
00728         protected $mHideIgnoreWarning;
00729         protected $mDestWarningAck;
00730         protected $mDestFile;
00731 
00732         protected $mComment;
00733         protected $mTextTop;
00734         protected $mTextAfterSummary;
00735 
00736         protected $mSourceIds;
00737 
00738         protected $mMaxFileSize = array();
00739 
00740         protected $mMaxUploadSize = array();
00741 
00742         public function __construct( array $options = array(), IContextSource $context = null ) {
00743                 $this->mWatch = !empty( $options['watch'] );
00744                 $this->mForReUpload = !empty( $options['forreupload'] );
00745                 $this->mSessionKey = isset( $options['sessionkey'] )
00746                                 ? $options['sessionkey'] : '';
00747                 $this->mHideIgnoreWarning = !empty( $options['hideignorewarning'] );
00748                 $this->mDestWarningAck = !empty( $options['destwarningack'] );
00749                 $this->mDestFile = isset( $options['destfile'] ) ? $options['destfile'] : '';
00750 
00751                 $this->mComment = isset( $options['description'] ) ?
00752                         $options['description'] : '';
00753 
00754                 $this->mTextTop = isset( $options['texttop'] )
00755                         ? $options['texttop'] : '';
00756 
00757                 $this->mTextAfterSummary = isset( $options['textaftersummary'] )
00758                         ? $options['textaftersummary'] : '';
00759 
00760                 $sourceDescriptor = $this->getSourceSection();
00761                 $descriptor = $sourceDescriptor
00762                         + $this->getDescriptionSection()
00763                         + $this->getOptionsSection();
00764 
00765                 wfRunHooks( 'UploadFormInitDescriptor', array( &$descriptor ) );
00766                 parent::__construct( $descriptor, $context, 'upload' );
00767 
00768                 # Set some form properties
00769                 $this->setSubmitText( $this->msg( 'uploadbtn' )->text() );
00770                 $this->setSubmitName( 'wpUpload' );
00771                 # Used message keys: 'accesskey-upload', 'tooltip-upload'
00772                 $this->setSubmitTooltip( 'upload' );
00773                 $this->setId( 'mw-upload-form' );
00774 
00775                 # Build a list of IDs for javascript insertion
00776                 $this->mSourceIds = array();
00777                 foreach ( $sourceDescriptor as $field ) {
00778                         if ( !empty( $field['id'] ) ) {
00779                                 $this->mSourceIds[] = $field['id'];
00780                         }
00781                 }
00782 
00783         }
00784 
00791         protected function getSourceSection() {
00792                 if ( $this->mSessionKey ) {
00793                         return array(
00794                                 'SessionKey' => array(
00795                                         'type' => 'hidden',
00796                                         'default' => $this->mSessionKey,
00797                                 ),
00798                                 'SourceType' => array(
00799                                         'type' => 'hidden',
00800                                         'default' => 'Stash',
00801                                 ),
00802                         );
00803                 }
00804 
00805                 $canUploadByUrl = UploadFromUrl::isEnabled() && UploadFromUrl::isAllowed( $this->getUser() );
00806                 $radio = $canUploadByUrl;
00807                 $selectedSourceType = strtolower( $this->getRequest()->getText( 'wpSourceType', 'File' ) );
00808 
00809                 $descriptor = array();
00810                 if ( $this->mTextTop ) {
00811                         $descriptor['UploadFormTextTop'] = array(
00812                                 'type' => 'info',
00813                                 'section' => 'source',
00814                                 'default' => $this->mTextTop,
00815                                 'raw' => true,
00816                         );
00817                 }
00818 
00819                 $this->mMaxUploadSize['file'] = UploadBase::getMaxUploadSize( 'file' );
00820                 # Limit to upload_max_filesize unless we are running under HipHop and
00821                 # that setting doesn't exist
00822                 if ( !wfIsHipHop() ) {
00823                         $this->mMaxUploadSize['file'] = min( $this->mMaxUploadSize['file'],
00824                                 wfShorthandToInteger( ini_get( 'upload_max_filesize' ) ),
00825                                 wfShorthandToInteger( ini_get( 'post_max_size' ) )
00826                         );
00827                 }
00828 
00829                 $descriptor['UploadFile'] = array(
00830                         'class' => 'UploadSourceField',
00831                         'section' => 'source',
00832                         'type' => 'file',
00833                         'id' => 'wpUploadFile',
00834                         'label-message' => 'sourcefilename',
00835                         'upload-type' => 'File',
00836                         'radio' => &$radio,
00837                         'help' => $this->msg( 'upload-maxfilesize',
00838                                         $this->getContext()->getLanguage()->formatSize( $this->mMaxUploadSize['file'] )
00839                                 )->parse() . ' ' . $this->msg( 'upload_source_file' )->escaped(),
00840                         'checked' => $selectedSourceType == 'file',
00841                 );
00842                 if ( $canUploadByUrl ) {
00843                         $this->mMaxUploadSize['url'] = UploadBase::getMaxUploadSize( 'url' );
00844                         $descriptor['UploadFileURL'] = array(
00845                                 'class' => 'UploadSourceField',
00846                                 'section' => 'source',
00847                                 'id' => 'wpUploadFileURL',
00848                                 'label-message' => 'sourceurl',
00849                                 'upload-type' => 'url',
00850                                 'radio' => &$radio,
00851                                 'help' => $this->msg( 'upload-maxfilesize',
00852                                                 $this->getContext()->getLanguage()->formatSize( $this->mMaxUploadSize['url'] )
00853                                         )->parse() . ' ' . $this->msg( 'upload_source_url' )->escaped(),
00854                                 'checked' => $selectedSourceType == 'url',
00855                         );
00856                 }
00857                 wfRunHooks( 'UploadFormSourceDescriptors', array( &$descriptor, &$radio, $selectedSourceType ) );
00858 
00859                 $descriptor['Extensions'] = array(
00860                         'type' => 'info',
00861                         'section' => 'source',
00862                         'default' => $this->getExtensionsMessage(),
00863                         'raw' => true,
00864                 );
00865                 return $descriptor;
00866         }
00867 
00873         protected function getExtensionsMessage() {
00874                 # Print a list of allowed file extensions, if so configured.  We ignore
00875                 # MIME type here, it's incomprehensible to most people and too long.
00876                 global $wgCheckFileExtensions, $wgStrictFileExtensions,
00877                 $wgFileExtensions, $wgFileBlacklist;
00878 
00879                 if( $wgCheckFileExtensions ) {
00880                         if( $wgStrictFileExtensions ) {
00881                                 # Everything not permitted is banned
00882                                 $extensionsList =
00883                                         '<div id="mw-upload-permitted">' .
00884                                         $this->msg( 'upload-permitted', $this->getContext()->getLanguage()->commaList( $wgFileExtensions ) )->parseAsBlock() .
00885                                         "</div>\n";
00886                         } else {
00887                                 # We have to list both preferred and prohibited
00888                                 $extensionsList =
00889                                         '<div id="mw-upload-preferred">' .
00890                                                 $this->msg( 'upload-preferred', $this->getContext()->getLanguage()->commaList( $wgFileExtensions ) )->parseAsBlock() .
00891                                         "</div>\n" .
00892                                         '<div id="mw-upload-prohibited">' .
00893                                                 $this->msg( 'upload-prohibited', $this->getContext()->getLanguage()->commaList( $wgFileBlacklist ) )->parseAsBlock() .
00894                                         "</div>\n";
00895                         }
00896                 } else {
00897                         # Everything is permitted.
00898                         $extensionsList = '';
00899                 }
00900                 return $extensionsList;
00901         }
00902 
00909         protected function getDescriptionSection() {
00910                 if ( $this->mSessionKey ) {
00911                         $stash = RepoGroup::singleton()->getLocalRepo()->getUploadStash();
00912                         try {
00913                                 $file = $stash->getFile( $this->mSessionKey );
00914                         } catch ( MWException $e ) {
00915                                 $file = null;
00916                         }
00917                         if ( $file ) {
00918                                 global $wgContLang;
00919 
00920                                 $mto = $file->transform( array( 'width' => 120 ) );
00921                                 $this->addHeaderText(
00922                                         '<div class="thumb t' . $wgContLang->alignEnd() . '">' .
00923                                         Html::element( 'img', array(
00924                                                 'src' => $mto->getUrl(),
00925                                                 'class' => 'thumbimage',
00926                                         ) ) . '</div>', 'description' );
00927                         }
00928                 }
00929 
00930                 $descriptor = array(
00931                         'DestFile' => array(
00932                                 'type' => 'text',
00933                                 'section' => 'description',
00934                                 'id' => 'wpDestFile',
00935                                 'label-message' => 'destfilename',
00936                                 'size' => 60,
00937                                 'default' => $this->mDestFile,
00938                                 # @todo FIXME: Hack to work around poor handling of the 'default' option in HTMLForm
00939                                 'nodata' => strval( $this->mDestFile ) !== '',
00940                         ),
00941                         'UploadDescription' => array(
00942                                 'type' => 'textarea',
00943                                 'section' => 'description',
00944                                 'id' => 'wpUploadDescription',
00945                                 'label-message' => $this->mForReUpload
00946                                         ? 'filereuploadsummary'
00947                                         : 'fileuploadsummary',
00948                                 'default' => $this->mComment,
00949                                 'cols' => intval( $this->getUser()->getOption( 'cols' ) ),
00950                                 'rows' => 8,
00951                         )
00952                 );
00953                 if ( $this->mTextAfterSummary ) {
00954                         $descriptor['UploadFormTextAfterSummary'] = array(
00955                                 'type' => 'info',
00956                                 'section' => 'description',
00957                                 'default' => $this->mTextAfterSummary,
00958                                 'raw' => true,
00959                         );
00960                 }
00961 
00962                 $descriptor += array(
00963                         'EditTools' => array(
00964                                 'type' => 'edittools',
00965                                 'section' => 'description',
00966                                 'message' => 'edittools-upload',
00967                         )
00968                 );
00969 
00970                 if ( $this->mForReUpload ) {
00971                         $descriptor['DestFile']['readonly'] = true;
00972                 } else {
00973                         $descriptor['License'] = array(
00974                                 'type' => 'select',
00975                                 'class' => 'Licenses',
00976                                 'section' => 'description',
00977                                 'id' => 'wpLicense',
00978                                 'label-message' => 'license',
00979                         );
00980                 }
00981 
00982                 global $wgUseCopyrightUpload;
00983                 if ( $wgUseCopyrightUpload ) {
00984                         $descriptor['UploadCopyStatus'] = array(
00985                                 'type' => 'text',
00986                                 'section' => 'description',
00987                                 'id' => 'wpUploadCopyStatus',
00988                                 'label-message' => 'filestatus',
00989                         );
00990                         $descriptor['UploadSource'] = array(
00991                                 'type' => 'text',
00992                                 'section' => 'description',
00993                                 'id' => 'wpUploadSource',
00994                                 'label-message' => 'filesource',
00995                         );
00996                 }
00997 
00998                 return $descriptor;
00999         }
01000 
01007         protected function getOptionsSection() {
01008                 $user = $this->getUser();
01009                 if ( $user->isLoggedIn() ) {
01010                         $descriptor = array(
01011                                 'Watchthis' => array(
01012                                         'type' => 'check',
01013                                         'id' => 'wpWatchthis',
01014                                         'label-message' => 'watchthisupload',
01015                                         'section' => 'options',
01016                                         'default' => $user->getOption( 'watchcreations' ),
01017                                 )
01018                         );
01019                 }
01020                 if ( !$this->mHideIgnoreWarning ) {
01021                         $descriptor['IgnoreWarning'] = array(
01022                                 'type' => 'check',
01023                                 'id' => 'wpIgnoreWarning',
01024                                 'label-message' => 'ignorewarnings',
01025                                 'section' => 'options',
01026                         );
01027                 }
01028 
01029                 $descriptor['DestFileWarningAck'] = array(
01030                         'type' => 'hidden',
01031                         'id' => 'wpDestFileWarningAck',
01032                         'default' => $this->mDestWarningAck ? '1' : '',
01033                 );
01034 
01035                 if ( $this->mForReUpload ) {
01036                         $descriptor['ForReUpload'] = array(
01037                                 'type' => 'hidden',
01038                                 'id' => 'wpForReUpload',
01039                                 'default' => '1',
01040                         );
01041                 }
01042 
01043                 return $descriptor;
01044         }
01045 
01049         public function show() {
01050                 $this->addUploadJS();
01051                 parent::show();
01052         }
01053 
01057         protected function addUploadJS() {
01058                 global $wgUseAjax, $wgAjaxUploadDestCheck, $wgAjaxLicensePreview, $wgEnableAPI, $wgStrictFileExtensions;
01059 
01060                 $useAjaxDestCheck = $wgUseAjax && $wgAjaxUploadDestCheck;
01061                 $useAjaxLicensePreview = $wgUseAjax && $wgAjaxLicensePreview && $wgEnableAPI;
01062                 $this->mMaxUploadSize['*'] = UploadBase::getMaxUploadSize();
01063 
01064                 $scriptVars = array(
01065                         'wgAjaxUploadDestCheck' => $useAjaxDestCheck,
01066                         'wgAjaxLicensePreview' => $useAjaxLicensePreview,
01067                         'wgUploadAutoFill' => !$this->mForReUpload &&
01068                                 // If we received mDestFile from the request, don't autofill
01069                                 // the wpDestFile textbox
01070                                 $this->mDestFile === '',
01071                         'wgUploadSourceIds' => $this->mSourceIds,
01072                         'wgStrictFileExtensions' => $wgStrictFileExtensions,
01073                         'wgCapitalizeUploads' => MWNamespace::isCapitalized( NS_FILE ),
01074                         'wgMaxUploadSize' => $this->mMaxUploadSize,
01075                 );
01076 
01077                 $out = $this->getOutput();
01078                 $out->addJsConfigVars( $scriptVars );
01079 
01080 
01081                 $out->addModules( array(
01082                         'mediawiki.action.edit', // For <charinsert> support
01083                         'mediawiki.legacy.upload', // Old form stuff...
01084                         'mediawiki.special.upload', // Newer extras for thumbnail preview.
01085                 ) );
01086         }
01087 
01093         function trySubmit() {
01094                 return false;
01095         }
01096 
01097 }
01098 
01102 class UploadSourceField extends HTMLTextField {
01103 
01108         function getLabelHtml( $cellAttributes = array() ) {
01109                 $id = "wpSourceType{$this->mParams['upload-type']}";
01110                 $label = Html::rawElement( 'label', array( 'for' => $id ), $this->mLabel );
01111 
01112                 if ( !empty( $this->mParams['radio'] ) ) {
01113                         $attribs = array(
01114                                 'name' => 'wpSourceType',
01115                                 'type' => 'radio',
01116                                 'id' => $id,
01117                                 'value' => $this->mParams['upload-type'],
01118                         );
01119                         if ( !empty( $this->mParams['checked'] ) ) {
01120                                 $attribs['checked'] = 'checked';
01121                         }
01122                         $label .= Html::element( 'input', $attribs );
01123                 }
01124 
01125                 return Html::rawElement( 'td', array( 'class' => 'mw-label' ) + $cellAttributes, $label );
01126         }
01127 
01131         function getSize() {
01132                 return isset( $this->mParams['size'] )
01133                         ? $this->mParams['size']
01134                         : 60;
01135         }
01136 }
01137