MediaWiki  REL1_21
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                 $this->mDestWarningAck = $request->getText( 'wpDestFileWarningAck' );
00100                 $this->mIgnoreWarning = $request->getCheck( 'wpIgnoreWarning' )
00101                         || $request->getCheck( 'wpUploadIgnoreWarning' );
00102                 $this->mWatchthis = $request->getBool( 'wpWatchthis' ) && $this->getUser()->isLoggedIn();
00103                 $this->mCopyrightStatus = $request->getText( 'wpUploadCopyStatus' );
00104                 $this->mCopyrightSource = $request->getText( 'wpUploadSource' );
00105 
00106                 $this->mForReUpload = $request->getBool( 'wpForReUpload' ); // updating a file
00107                 $this->mCancelUpload = $request->getCheck( 'wpCancelUpload' )
00108                         || $request->getCheck( 'wpReUpload' ); // b/w compat
00109 
00110                 // If it was posted check for the token (no remote POST'ing with user credentials)
00111                 $token = $request->getVal( 'wpEditToken' );
00112                 $this->mTokenOk = $this->getUser()->matchEditToken( $token );
00113 
00114                 $this->uploadFormTextTop = '';
00115                 $this->uploadFormTextAfterSummary = '';
00116         }
00117 
00126         public function userCanExecute( User $user ) {
00127                 return UploadBase::isEnabled() && parent::userCanExecute( $user );
00128         }
00129 
00133         public function execute( $par ) {
00134                 $this->setHeaders();
00135                 $this->outputHeader();
00136 
00137                 # Check uploading enabled
00138                 if( !UploadBase::isEnabled() ) {
00139                         throw new ErrorPageError( 'uploaddisabled', 'uploaddisabledtext' );
00140                 }
00141 
00142                 # Check permissions
00143                 $user = $this->getUser();
00144                 $permissionRequired = UploadBase::isAllowed( $user );
00145                 if( $permissionRequired !== true ) {
00146                         throw new PermissionsError( $permissionRequired );
00147                 }
00148 
00149                 # Check blocks
00150                 if( $user->isBlocked() ) {
00151                         throw new UserBlockedError( $user->getBlock() );
00152                 }
00153 
00154                 # Check whether we actually want to allow changing stuff
00155                 $this->checkReadOnly();
00156 
00157                 $this->loadRequest();
00158 
00159                 # Unsave the temporary file in case this was a cancelled upload
00160                 if ( $this->mCancelUpload ) {
00161                         if ( !$this->unsaveUploadedFile() ) {
00162                                 # Something went wrong, so unsaveUploadedFile showed a warning
00163                                 return;
00164                         }
00165                 }
00166 
00167                 # Process upload or show a form
00168                 if (
00169                         $this->mTokenOk && !$this->mCancelUpload &&
00170                         ( $this->mUpload && $this->mUploadClicked )
00171                 ) {
00172                         $this->processUpload();
00173                 } else {
00174                         # Backwards compatibility hook
00175                         if( !wfRunHooks( 'UploadForm:initial', array( &$this ) ) ) {
00176                                 wfDebug( "Hook 'UploadForm:initial' broke output of the upload form" );
00177                                 return;
00178                         }
00179                         $this->showUploadForm( $this->getUploadForm() );
00180                 }
00181 
00182                 # Cleanup
00183                 if ( $this->mUpload ) {
00184                         $this->mUpload->cleanupTempFile();
00185                 }
00186         }
00187 
00193         protected function showUploadForm( $form ) {
00194                 # Add links if file was previously deleted
00195                 if ( $this->mDesiredDestName ) {
00196                         $this->showViewDeletedLinks();
00197                 }
00198 
00199                 if ( $form instanceof HTMLForm ) {
00200                         $form->show();
00201                 } else {
00202                         $this->getOutput()->addHTML( $form );
00203                 }
00204 
00205         }
00206 
00215         protected function getUploadForm( $message = '', $sessionKey = '', $hideIgnoreWarning = false ) {
00216                 # Initialize form
00217                 $form = new UploadForm( array(
00218                         'watch' => $this->getWatchCheck(),
00219                         'forreupload' => $this->mForReUpload,
00220                         'sessionkey' => $sessionKey,
00221                         'hideignorewarning' => $hideIgnoreWarning,
00222                         'destwarningack' => (bool)$this->mDestWarningAck,
00223 
00224                         'description' => $this->mComment,
00225                         'texttop' => $this->uploadFormTextTop,
00226                         'textaftersummary' => $this->uploadFormTextAfterSummary,
00227                         'destfile' => $this->mDesiredDestName,
00228                 ), $this->getContext() );
00229                 $form->setTitle( $this->getTitle() );
00230 
00231                 # Check the token, but only if necessary
00232                 if(
00233                         !$this->mTokenOk && !$this->mCancelUpload &&
00234                         ( $this->mUpload && $this->mUploadClicked )
00235                 ) {
00236                         $form->addPreText( $this->msg( 'session_fail_preview' )->parse() );
00237                 }
00238 
00239                 # Give a notice if the user is uploading a file that has been deleted or moved
00240                 # Note that this is independent from the message 'filewasdeleted' that requires JS
00241                 $desiredTitleObj = Title::makeTitleSafe( NS_FILE, $this->mDesiredDestName );
00242                 $delNotice = ''; // empty by default
00243                 if ( $desiredTitleObj instanceof Title && !$desiredTitleObj->exists() ) {
00244                         LogEventsList::showLogExtract( $delNotice, array( 'delete', 'move' ),
00245                                 $desiredTitleObj,
00246                                 '', array( 'lim' => 10,
00247                                         'conds' => array( "log_action != 'revision'" ),
00248                                         'showIfEmpty' => false,
00249                                         'msgKey' => array( 'upload-recreate-warning' ) )
00250                         );
00251                 }
00252                 $form->addPreText( $delNotice );
00253 
00254                 # Add text to form
00255                 $form->addPreText( '<div id="uploadtext">' .
00256                         $this->msg( 'uploadtext', array( $this->mDesiredDestName ) )->parseAsBlock() .
00257                         '</div>' );
00258                 # Add upload error message
00259                 $form->addPreText( $message );
00260 
00261                 # Add footer to form
00262                 $uploadFooter = $this->msg( 'uploadfooter' );
00263                 if ( !$uploadFooter->isDisabled() ) {
00264                         $form->addPostText( '<div id="mw-upload-footer-message">'
00265                                 . $uploadFooter->parseAsBlock() . "</div>\n" );
00266                 }
00267 
00268                 return $form;
00269         }
00270 
00274         protected function showViewDeletedLinks() {
00275                 $title = Title::makeTitleSafe( NS_FILE, $this->mDesiredDestName );
00276                 $user = $this->getUser();
00277                 // Show a subtitle link to deleted revisions (to sysops et al only)
00278                 if( $title instanceof Title ) {
00279                         $count = $title->isDeleted();
00280                         if ( $count > 0 && $user->isAllowed( 'deletedhistory' ) ) {
00281                                 $restorelink = Linker::linkKnown(
00282                                         SpecialPage::getTitleFor( 'Undelete', $title->getPrefixedText() ),
00283                                         $this->msg( 'restorelink' )->numParams( $count )->escaped()
00284                                 );
00285                                 $link = $this->msg( $user->isAllowed( 'delete' ) ? 'thisisdeleted' : 'viewdeleted' )
00286                                         ->rawParams( $restorelink )->parseAsBlock();
00287                                 $this->getOutput()->addHTML( "<div id=\"contentSub2\">{$link}</div>" );
00288                         }
00289                 }
00290         }
00291 
00303         protected function showRecoverableUploadError( $message ) {
00304                 $sessionKey = $this->mUpload->stashSession();
00305                 $message = '<h2>' . $this->msg( 'uploaderror' )->escaped() . "</h2>\n" .
00306                         '<div class="error">' . $message . "</div>\n";
00307 
00308                 $form = $this->getUploadForm( $message, $sessionKey );
00309                 $form->setSubmitText( $this->msg( 'upload-tryagain' )->escaped() );
00310                 $this->showUploadForm( $form );
00311         }
00320         protected function showUploadWarning( $warnings ) {
00321                 # If there are no warnings, or warnings we can ignore, return early.
00322                 # mDestWarningAck is set when some javascript has shown the warning
00323                 # to the user. mForReUpload is set when the user clicks the "upload a
00324                 # new version" link.
00325                 if ( !$warnings || ( count( $warnings ) == 1 &&
00326                         isset( $warnings['exists'] ) &&
00327                         ( $this->mDestWarningAck || $this->mForReUpload ) ) )
00328                 {
00329                         return false;
00330                 }
00331 
00332                 $sessionKey = $this->mUpload->stashSession();
00333 
00334                 $warningHtml = '<h2>' . $this->msg( 'uploadwarning' )->escaped() . "</h2>\n"
00335                         . '<ul class="warning">';
00336                 foreach( $warnings as $warning => $args ) {
00337                         if( $warning == 'badfilename' ) {
00338                                 $this->mDesiredDestName = Title::makeTitle( NS_FILE, $args )->getText();
00339                         }
00340                         if( $warning == 'exists' ) {
00341                                 $msg = "\t<li>" . self::getExistsWarning( $args ) . "</li>\n";
00342                         } elseif( $warning == 'duplicate' ) {
00343                                 $msg = self::getDupeWarning( $args );
00344                         } elseif( $warning == 'duplicate-archive' ) {
00345                                 $msg = "\t<li>" . $this->msg( 'file-deleted-duplicate',
00346                                                 Title::makeTitle( NS_FILE, $args )->getPrefixedText() )->parse()
00347                                         . "</li>\n";
00348                         } else {
00349                                 if ( $args === true ) {
00350                                         $args = array();
00351                                 } elseif ( !is_array( $args ) ) {
00352                                         $args = array( $args );
00353                                 }
00354                                 $msg = "\t<li>" . $this->msg( $warning, $args )->parse() . "</li>\n";
00355                         }
00356                         $warningHtml .= $msg;
00357                 }
00358                 $warningHtml .= "</ul>\n";
00359                 $warningHtml .= $this->msg( 'uploadwarning-text' )->parseAsBlock();
00360 
00361                 $form = $this->getUploadForm( $warningHtml, $sessionKey, /* $hideIgnoreWarning */ true );
00362                 $form->setSubmitText( $this->msg( 'upload-tryagain' )->text() );
00363                 $form->addButton( 'wpUploadIgnoreWarning', $this->msg( 'ignorewarning' )->text() );
00364                 $form->addButton( 'wpCancelUpload', $this->msg( 'reuploaddesc' )->text() );
00365 
00366                 $this->showUploadForm( $form );
00367 
00368                 # Indicate that we showed a form
00369                 return true;
00370         }
00371 
00377         protected function showUploadError( $message ) {
00378                 $message = '<h2>' . $this->msg( 'uploadwarning' )->escaped() . "</h2>\n" .
00379                         '<div class="error">' . $message . "</div>\n";
00380                 $this->showUploadForm( $this->getUploadForm( $message ) );
00381         }
00382 
00387         protected function processUpload() {
00388                 // Fetch the file if required
00389                 $status = $this->mUpload->fetchFile();
00390                 if( !$status->isOK() ) {
00391                         $this->showUploadError( $this->getOutput()->parse( $status->getWikiText() ) );
00392                         return;
00393                 }
00394 
00395                 if( !wfRunHooks( 'UploadForm:BeforeProcessing', array( &$this ) ) ) {
00396                         wfDebug( "Hook 'UploadForm:BeforeProcessing' broke processing the file.\n" );
00397                         // This code path is deprecated. If you want to break upload processing
00398                         // do so by hooking into the appropriate hooks in UploadBase::verifyUpload
00399                         // and UploadBase::verifyFile.
00400                         // If you use this hook to break uploading, the user will be returned
00401                         // an empty form with no error message whatsoever.
00402                         return;
00403                 }
00404 
00405                 // Upload verification
00406                 $details = $this->mUpload->verifyUpload();
00407                 if ( $details['status'] != UploadBase::OK ) {
00408                         $this->processVerificationError( $details );
00409                         return;
00410                 }
00411 
00412                 // Verify permissions for this title
00413                 $permErrors = $this->mUpload->verifyTitlePermissions( $this->getUser() );
00414                 if( $permErrors !== true ) {
00415                         $code = array_shift( $permErrors[0] );
00416                         $this->showRecoverableUploadError( $this->msg( $code, $permErrors[0] )->parse() );
00417                         return;
00418                 }
00419 
00420                 $this->mLocalFile = $this->mUpload->getLocalFile();
00421 
00422                 // Check warnings if necessary
00423                 if( !$this->mIgnoreWarning ) {
00424                         $warnings = $this->mUpload->checkWarnings();
00425                         if( $this->showUploadWarning( $warnings ) ) {
00426                                 return;
00427                         }
00428                 }
00429 
00430                 // Get the page text if this is not a reupload
00431                 if( !$this->mForReUpload ) {
00432                         $pageText = self::getInitialPageText( $this->mComment, $this->mLicense,
00433                                 $this->mCopyrightStatus, $this->mCopyrightSource );
00434                 } else {
00435                         $pageText = false;
00436                 }
00437                 $status = $this->mUpload->performUpload( $this->mComment, $pageText, $this->mWatchthis, $this->getUser() );
00438                 if ( !$status->isGood() ) {
00439                         $this->showUploadError( $this->getOutput()->parse( $status->getWikiText() ) );
00440                         return;
00441                 }
00442 
00443                 // Success, redirect to description page
00444                 $this->mUploadSuccessful = true;
00445                 wfRunHooks( 'SpecialUploadComplete', array( &$this ) );
00446                 $this->getOutput()->redirect( $this->mLocalFile->getTitle()->getFullURL() );
00447         }
00448 
00457         public static function getInitialPageText( $comment = '', $license = '', $copyStatus = '', $source = '' ) {
00458                 global $wgUseCopyrightUpload, $wgForceUIMsgAsContentMsg;
00459                 $wgForceUIMsgAsContentMsg = (array) $wgForceUIMsgAsContentMsg;
00460 
00461                 $msg = array();
00462                 /* These messages are transcluded into the actual text of the description page.
00463                  * Thus, forcing them as content messages makes the upload to produce an int: template
00464                  * instead of hardcoding it there in the uploader language.
00465                  */
00466                 foreach( array( 'license-header', 'filedesc', 'filestatus', 'filesource' ) as $msgName ) {
00467                         if ( in_array( $msgName, $wgForceUIMsgAsContentMsg ) ) {
00468                                 $msg[$msgName] = "{{int:$msgName}}";
00469                         } else {
00470                                 $msg[$msgName] = wfMessage( $msgName )->inContentLanguage()->text();
00471                         }
00472                 }
00473 
00474                 if ( $wgUseCopyrightUpload ) {
00475                         $licensetxt = '';
00476                         if ( $license != '' ) {
00477                                 $licensetxt = '== ' . $msg['license-header'] . " ==\n" . '{{' . $license . '}}' . "\n";
00478                         }
00479                         $pageText = '== ' . $msg['filedesc'] . " ==\n" . $comment . "\n" .
00480                                 '== ' . $msg['filestatus'] . " ==\n" . $copyStatus . "\n" .
00481                                 "$licensetxt" .
00482                                 '== ' . $msg['filesource'] . " ==\n" . $source;
00483                 } else {
00484                         if ( $license != '' ) {
00485                                 $filedesc = $comment == '' ? '' : '== ' . $msg['filedesc'] . " ==\n" . $comment . "\n";
00486                                         $pageText = $filedesc .
00487                                         '== ' . $msg['license-header'] . " ==\n" . '{{' . $license . '}}' . "\n";
00488                         } else {
00489                                 $pageText = $comment;
00490                         }
00491                 }
00492                 return $pageText;
00493         }
00494 
00507         protected function getWatchCheck() {
00508                 if( $this->getUser()->getOption( 'watchdefault' ) ) {
00509                         // Watch all edits!
00510                         return true;
00511                 }
00512 
00513                 $local = wfLocalFile( $this->mDesiredDestName );
00514                 if( $local && $local->exists() ) {
00515                         // We're uploading a new version of an existing file.
00516                         // No creation, so don't watch it if we're not already.
00517                         return $this->getUser()->isWatched( $local->getTitle() );
00518                 } else {
00519                         // New page should get watched if that's our option.
00520                         return $this->getUser()->getOption( 'watchcreations' );
00521                 }
00522         }
00523 
00530         protected function processVerificationError( $details ) {
00531                 global $wgFileExtensions;
00532 
00533                 switch( $details['status'] ) {
00534 
00536                         case UploadBase::MIN_LENGTH_PARTNAME:
00537                                 $this->showRecoverableUploadError( $this->msg( 'minlength1' )->escaped() );
00538                                 break;
00539                         case UploadBase::ILLEGAL_FILENAME:
00540                                 $this->showRecoverableUploadError( $this->msg( 'illegalfilename',
00541                                         $details['filtered'] )->parse() );
00542                                 break;
00543                         case UploadBase::FILENAME_TOO_LONG:
00544                                 $this->showRecoverableUploadError( $this->msg( 'filename-toolong' )->escaped() );
00545                                 break;
00546                         case UploadBase::FILETYPE_MISSING:
00547                                 $this->showRecoverableUploadError( $this->msg( 'filetype-missing' )->parse() );
00548                                 break;
00549                         case UploadBase::WINDOWS_NONASCII_FILENAME:
00550                                 $this->showRecoverableUploadError( $this->msg( 'windows-nonascii-filename' )->parse() );
00551                                 break;
00552 
00554                         case UploadBase::EMPTY_FILE:
00555                                 $this->showUploadError( $this->msg( 'emptyfile' )->escaped() );
00556                                 break;
00557                         case UploadBase::FILE_TOO_LARGE:
00558                                 $this->showUploadError( $this->msg( 'largefileserver' )->escaped() );
00559                                 break;
00560                         case UploadBase::FILETYPE_BADTYPE:
00561                                 $msg = $this->msg( 'filetype-banned-type' );
00562                                 if ( isset( $details['blacklistedExt'] ) ) {
00563                                         $msg->params( $this->getLanguage()->commaList( $details['blacklistedExt'] ) );
00564                                 } else {
00565                                         $msg->params( $details['finalExt'] );
00566                                 }
00567                                 $msg->params( $this->getLanguage()->commaList( $wgFileExtensions ),
00568                                         count( $wgFileExtensions ) );
00569 
00570                                 // Add PLURAL support for the first parameter. This results
00571                                 // in a bit unlogical parameter sequence, but does not break
00572                                 // old translations
00573                                 if ( isset( $details['blacklistedExt'] ) ) {
00574                                         $msg->params( count( $details['blacklistedExt'] ) );
00575                                 } else {
00576                                         $msg->params( 1 );
00577                                 }
00578 
00579                                 $this->showUploadError( $msg->parse() );
00580                                 break;
00581                         case UploadBase::VERIFICATION_ERROR:
00582                                 unset( $details['status'] );
00583                                 $code = array_shift( $details['details'] );
00584                                 $this->showUploadError( $this->msg( $code, $details['details'] )->parse() );
00585                                 break;
00586                         case UploadBase::HOOK_ABORTED:
00587                                 if ( is_array( $details['error'] ) ) { # allow hooks to return error details in an array
00588                                         $args = $details['error'];
00589                                         $error = array_shift( $args );
00590                                 } else {
00591                                         $error = $details['error'];
00592                                         $args = null;
00593                                 }
00594 
00595                                 $this->showUploadError( $this->msg( $error, $args )->parse() );
00596                                 break;
00597                         default:
00598                                 throw new MWException( __METHOD__ . ": Unknown value `{$details['status']}`" );
00599                 }
00600         }
00601 
00607         protected function unsaveUploadedFile() {
00608                 if ( !( $this->mUpload instanceof UploadFromStash ) ) {
00609                         return true;
00610                 }
00611                 $success = $this->mUpload->unsaveUploadedFile();
00612                 if ( !$success ) {
00613                         $this->getOutput()->showFileDeleteError( $this->mUpload->getTempPath() );
00614                         return false;
00615                 } else {
00616                         return true;
00617                 }
00618         }
00619 
00620         /*** Functions for formatting warnings ***/
00621 
00629         public static function getExistsWarning( $exists ) {
00630                 if ( !$exists ) {
00631                         return '';
00632                 }
00633 
00634                 $file = $exists['file'];
00635                 $filename = $file->getTitle()->getPrefixedText();
00636                 $warning = '';
00637 
00638                 if( $exists['warning'] == 'exists' ) {
00639                         // Exact match
00640                         $warning = wfMessage( 'fileexists', $filename )->parse();
00641                 } elseif( $exists['warning'] == 'page-exists' ) {
00642                         // Page exists but file does not
00643                         $warning = wfMessage( 'filepageexists', $filename )->parse();
00644                 } elseif ( $exists['warning'] == 'exists-normalized' ) {
00645                         $warning = wfMessage( 'fileexists-extension', $filename,
00646                                 $exists['normalizedFile']->getTitle()->getPrefixedText() )->parse();
00647                 } elseif ( $exists['warning'] == 'thumb' ) {
00648                         // Swapped argument order compared with other messages for backwards compatibility
00649                         $warning = wfMessage( 'fileexists-thumbnail-yes',
00650                                 $exists['thumbFile']->getTitle()->getPrefixedText(), $filename )->parse();
00651                 } elseif ( $exists['warning'] == 'thumb-name' ) {
00652                         // Image w/o '180px-' does not exists, but we do not like these filenames
00653                         $name = $file->getName();
00654                         $badPart = substr( $name, 0, strpos( $name, '-' ) + 1 );
00655                         $warning = wfMessage( 'file-thumbnail-no', $badPart )->parse();
00656                 } elseif ( $exists['warning'] == 'bad-prefix' ) {
00657                         $warning = wfMessage( 'filename-bad-prefix', $exists['prefix'] )->parse();
00658                 } elseif ( $exists['warning'] == 'was-deleted' ) {
00659                         # If the file existed before and was deleted, warn the user of this
00660                         $ltitle = SpecialPage::getTitleFor( 'Log' );
00661                         $llink = Linker::linkKnown(
00662                                 $ltitle,
00663                                 wfMessage( 'deletionlog' )->escaped(),
00664                                 array(),
00665                                 array(
00666                                         'type' => 'delete',
00667                                         'page' => $filename
00668                                 )
00669                         );
00670                         $warning = wfMessage( 'filewasdeleted' )->rawParams( $llink )->parseAsBlock();
00671                 }
00672 
00673                 return $warning;
00674         }
00675 
00682         public static function ajaxGetExistsWarning( $filename ) {
00683                 $file = wfFindFile( $filename );
00684                 if( !$file ) {
00685                         // Force local file so we have an object to do further checks against
00686                         // if there isn't an exact match...
00687                         $file = wfLocalFile( $filename );
00688                 }
00689                 $s = '&#160;';
00690                 if ( $file ) {
00691                         $exists = UploadBase::getExistsWarning( $file );
00692                         $warning = self::getExistsWarning( $exists );
00693                         if ( $warning !== '' ) {
00694                                 $s = "<div>$warning</div>";
00695                         }
00696                 }
00697                 return $s;
00698         }
00699 
00705         public static function getDupeWarning( $dupes ) {
00706                 if ( !$dupes ) {
00707                         return '';
00708                 }
00709 
00710                 $gallery = new ImageGallery;
00711                 $gallery->setShowBytes( false );
00712                 foreach( $dupes as $file ) {
00713                         $gallery->add( $file->getTitle() );
00714                 }
00715                 return '<li>' .
00716                         wfMessage( 'file-exists-duplicate' )->numParams( count( $dupes ) )->parse() .
00717                         $gallery->toHtml() . "</li>\n";
00718         }
00719 
00720         protected function getGroupName() {
00721                 return 'media';
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' => $this->getUser()->getIntOption( '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                 $out->addModules( array(
01089                         'mediawiki.action.edit', // For <charinsert> support
01090                         'mediawiki.legacy.upload', // Old form stuff...
01091                         'mediawiki.special.upload', // Newer extras for thumbnail preview.
01092                 ) );
01093         }
01094 
01100         function trySubmit() {
01101                 return false;
01102         }
01103 
01104 }
01105 
01109 class UploadSourceField extends HTMLTextField {
01110 
01115         function getLabelHtml( $cellAttributes = array() ) {
01116                 $id = $this->mParams['id'];
01117                 $label = Html::rawElement( 'label', array( 'for' => $id ), $this->mLabel );
01118 
01119                 if ( !empty( $this->mParams['radio'] ) ) {
01120                         $attribs = array(
01121                                 'name' => 'wpSourceType',
01122                                 'type' => 'radio',
01123                                 'id' => $id,
01124                                 'value' => $this->mParams['upload-type'],
01125                         );
01126                         if ( !empty( $this->mParams['checked'] ) ) {
01127                                 $attribs['checked'] = 'checked';
01128                         }
01129                         $label .= Html::element( 'input', $attribs );
01130                 }
01131 
01132                 return Html::rawElement( 'td', array( 'class' => 'mw-label' ) + $cellAttributes, $label );
01133         }
01134 
01138         function getSize() {
01139                 return isset( $this->mParams['size'] )
01140                         ? $this->mParams['size']
01141                         : 60;
01142         }
01143 }