execute(); } /** * implements Special:Upload * @ingroup SpecialPage */ class UploadForm { /**#@+ * @access private */ var $mComment, $mLicense, $mIgnoreWarning; var $mCopyrightStatus, $mCopyrightSource, $mReUpload, $mAction, $mUploadClicked; var $mDestWarningAck; var $mLocalFile; var $mUpload; // Instance of UploadFromBase or derivative # Placeholders for text injection by hooks (must be HTML) # extensions should take care to _append_ to the present value var $uploadFormTextTop; var $uploadFormTextAfterSummary; /**#@-*/ /** * Constructor : initialise object * Get data POSTed through the form and assign them to the object * @param $request Data posted. */ function __construct( &$request ) { $this->mDesiredDestName = $request->getText( 'wpDestFile' ); $this->mIgnoreWarning = $request->getCheck( 'wpIgnoreWarning' ); $this->mComment = $request->getText( 'wpUploadDescription' ); if( !$request->wasPosted() ) { # GET requests just give the main form; no data except destination # filename and description return; } # Placeholders for text injection by hooks (empty per default) $this->uploadFormTextTop = ""; $this->uploadFormTextAfterSummary = ""; $this->mReUpload = $request->getCheck( 'wpReUpload' ); $this->mUploadClicked = $request->getCheck( 'wpUpload' ); $this->mLicense = $request->getText( 'wpLicense' ); $this->mCopyrightStatus = $request->getText( 'wpUploadCopyStatus' ); $this->mCopyrightSource = $request->getText( 'wpUploadSource' ); $this->mWatchthis = $request->getBool( 'wpWatchthis' ); $this->mSourceType = $request->getText( 'wpSourceType' ); $this->mDestWarningAck = $request->getText( 'wpDestFileWarningAck' ); $this->mAction = $request->getVal( 'action' ); $desiredDestName = $request->getText( 'wpDestFile' ); if( !$desiredDestName ) $desiredDestName = $request->getText( 'wpUploadFile' ); $this->mSessionKey = $request->getInt( 'wpSessionKey' ); if( !empty( $this->mSessionKey ) && isset( $_SESSION['wsUploadData'][$this->mSessionKey]['version'] ) && $_SESSION['wsUploadData'][$this->mSessionKey]['version'] == UploadFromBase::SESSION_VERSION ) { /** * Confirming a temporarily stashed upload. * We don't want path names to be forged, so we keep * them in the session on the server and just give * an opaque key to the user agent. */ $this->mUpload = new UploadFromStash( $desiredDestName ); $data = $_SESSION['wsUploadData'][$this->mSessionKey]; $this->mUpload->initialize( $data ); } else { /** *Check for a newly uploaded file. */ if( UploadFromUrl::isEnabled() && $this->mSourceType == 'web' ) { $this->mUpload = new UploadFromUrl( $desiredDestName ); $this->mUpload->initialize( $request->getText( 'wpUploadFileURL' ) ); } else { $this->mUpload = new UploadFromUpload( $desiredDestName ); $this->mUpload->initialize( $request->getFileTempName( 'wpUploadFile' ), $request->getFileSize( 'wpUploadFile' ), $request->getFileName( 'wpUploadFile' ) ); } } } /** * Start doing stuff * @access public */ function execute() { global $wgUser, $wgOut; # Check uploading enabled if( !UploadFromBase::isEnabled() ) { $wgOut->showErrorPage( 'uploaddisabled', 'uploaddisabledtext', array( $this->mDesiredDestName ) ); return; } # Check permissions $permission = $this->mUpload->isAllowed( $wgUser ); if( $permission !== true ) { if( !$wgUser->isLoggedIn() ) { $wgOut->showErrorPage( 'uploadnologin', 'uploadnologintext' ); } else { $wgOut->permissionRequired( $permission ); } return; } # Check blocks if( $wgUser->isBlocked() ) { $wgOut->blockedPage(); return; } if( wfReadOnly() ) { $wgOut->readOnlyPage(); return; } if( $this->mReUpload ) { // User did not choose to ignore warnings if( !$this->mUpload->unsaveUploadedFile() ) { return; } # Because it is probably checked and shouldn't be $this->mIgnoreWarning = false; $this->mainUploadForm(); } elseif( $this->mUpload && ( 'submit' == $this->mAction || $this->mUploadClicked ) ) { $this->processUpload(); } else { $this->mainUploadForm(); } if( $this->mUpload ) $this->mUpload->cleanupTempFile(); } /** * Do the upload * Checks are made in SpecialUpload::execute() * * @access private */ function processUpload(){ global $wgUser, $wgOut, $wgFileExtensions, $wgLang; $details = null; $value = null; $value = $this->internalProcessUpload( $details ); switch($value) { case UploadFromBase::SUCCESS: $wgOut->redirect( $this->mLocalFile->getTitle()->getFullURL() ); break; case UploadFromBase::BEFORE_PROCESSING: // Do... nothing? Why? break; case UploadFromBase::LARGE_FILE_SERVER: $this->mainUploadForm( wfMsgHtml( 'largefileserver' ) ); break; case UploadFromBase::EMPTY_FILE: $this->mainUploadForm( wfMsgHtml( 'emptyfile' ) ); break; case UploadFromBase::MIN_LENGHT_PARTNAME: $this->mainUploadForm( wfMsgHtml( 'minlength1' ) ); break; case UploadFromBase::ILLEGAL_FILENAME: $this->uploadError( wfMsgExt( 'illegalfilename', 'parseinline', $details['filtered'] ) ); break; case UploadFromBase::PROTECTED_PAGE: $wgOut->showPermissionsErrorPage( $details['permissionserrors'] ); break; case UploadFromBase::OVERWRITE_EXISTING_FILE: $this->uploadError( wfMsgExt( $details['overwrite'], 'parseinline' ) ); break; case UploadFromBase::FILETYPE_MISSING: $this->uploadError( wfMsgExt( 'filetype-missing', array ( 'parseinline' ) ) ); break; case UploadFromBase::FILETYPE_BADTYPE: $finalExt = $details['finalExt']; $this->uploadError( wfMsgExt( 'filetype-banned-type', array( 'parseinline' ), htmlspecialchars( $finalExt ), implode( wfMsgExt( 'comma-separator', array( 'escapenoentities' ) ), $wgFileExtensions ), $wgLang->formatNum( count( $wgFileExtensions ) ) ) ); break; case UploadFromBase::VERIFICATION_ERROR: $args = $details['veri']; $code = array_shift( $args ); $this->uploadError( wfMsgExt( $code, 'parseinline', $args ) ); break; case UploadFromBase::UPLOAD_VERIFICATION_ERROR: $error = $details['error']; $this->uploadError( wfMsgExt( $error, 'parseinline' ) ); break; case UploadFromBase::UPLOAD_WARNING: $warning = $details['warning']; $this->uploadWarning( $warning ); break; case UploadFromBase::INTERNAL_ERROR: $status = $details['internal']; $this->showError( $wgOut->parse( $status->getWikiText() ) ); break; default: throw new MWException( __METHOD__ . ": Unknown value `{$value}`" ); } } /** * Really do the upload * Checks are made in SpecialUpload::execute() * * @param array $resultDetails contains result-specific dict of additional values * * @access private */ function internalProcessUpload( &$resultDetails ) { global $wgUser; if( !wfRunHooks( 'UploadForm:BeforeProcessing', array( &$this ) ) ) { wfDebug( "Hook 'UploadForm:BeforeProcessing' broke processing the file." ); return UploadFromBase::BEFORE_PROCESSING; } /** * If the image is protected, non-sysop users won't be able * to modify it by uploading a new revision. */ $permErrors = $this->mUpload->verifyPermissions( $wgUser ); if( $permErrors !== true ) { $resultDetails = array( 'permissionserrors' => $permErrors ); return UploadFromBase::PROTECTED_PAGE; } // Check whether this is a sane upload $result = $this->mUpload->verifyUpload( $resultDetails ); if( $result != UploadFromBase::OK ) return $result; $this->mLocalFile = $this->mUpload->getLocalFile(); if( !$this->mIgnoreWarning ) { $warnings = $this->mUpload->checkWarnings(); if( count( $warnings ) ) { $resultDetails = array( 'warning' => $warnings ); return UploadFromBase::UPLOAD_WARNING; } } /** * Try actually saving the thing... * It will show an error form on failure. No it will not. */ $pageText = self::getInitialPageText( $this->mComment, $this->mLicense, $this->mCopyrightStatus, $this->mCopyrightSource ); $status = $this->mUpload->performUpload( $this->mComment, $pageText, $this->mWatchthis, $wgUser ); if ( !$status->isGood() ) { $resultDetails = array( 'internal' => $status ); return UploadFromBase::INTERNAL_ERROR; } else { // Success, redirect to description page // WTF WTF WTF? $img = null; // @todo: added to avoid passing a ref to null - should this be defined somewhere? wfRunHooks( 'SpecialUploadComplete', array( &$this ) ); return UploadFromBase::SUCCESS; } } /** * Do existence checks on a file and produce a warning * This check is static and can be done pre-upload via AJAX * Returns an HTML fragment consisting of one or more LI elements if there is a warning * Returns an empty string if there is no warning */ static function getExistsWarning( $exists ) { global $wgUser, $wgContLang; if( $exists === false ) return ''; $warning = ''; $align = $wgContLang->isRtl() ? 'left' : 'right'; list( $existsType, $file ) = $exists; $sk = $wgUser->getSkin(); if( $existsType == 'exists' ) { // Exact match $dlink = $sk->makeKnownLinkObj( $file->getTitle() ); if ( $file->allowInlineDisplay() ) { $dlink2 = $sk->makeImageLinkObj( $file->getTitle(), wfMsgExt( 'fileexists-thumb', 'parseinline' ), $file->getName(), $align, array(), false, true ); } elseif ( !$file->allowInlineDisplay() && $file->isSafeFile() ) { $icon = $file->iconThumb(); $dlink2 = '
' . $icon->toHtml( array( 'desc-link' => true ) ) . '
' . $dlink . '
'; } else { $dlink2 = ''; } $warning .= '
  • ' . wfMsgExt( 'fileexists', array('parseinline','replaceafter'), $dlink ) . '
  • ' . $dlink2; } elseif( $existsType == 'page-exists' ) { $lnk = $sk->makeKnownLinkObj( $file->getTitle(), '', 'redirect=no' ); $warning .= '
  • ' . wfMsgExt( 'filepageexists', array( 'parseinline', 'replaceafter' ), $lnk ) . '
  • '; } elseif ( $existsType == 'exists-normalized' ) { # Check if image with lowercase extension exists. # It's not forbidden but in 99% it makes no sense to upload the same filename with uppercase extension $dlink = $sk->makeKnownLinkObj( $file->getTitle() ); if ( $file->allowInlineDisplay() ) { $dlink2 = $sk->makeImageLinkObj( $file->getTitle(), wfMsgExt( 'fileexists-thumb', 'parseinline' ), $file->getTitle()->getText(), $align, array(), false, true ); } elseif ( !$file->allowInlineDisplay() && $file->isSafeFile() ) { $icon = $file->iconThumb(); $dlink2 = '
    ' . $icon->toHtml( array( 'desc-link' => true ) ) . '
    ' . $dlink . '
    '; } else { $dlink2 = ''; } $warning .= '
  • ' . wfMsgExt( 'fileexists-extension', 'parsemag', $file->getTitle()->getPrefixedText(), $dlink ) . '
  • ' . $dlink2; } elseif ( $existsType == 'thumb' ) { # Check if an image without leading '180px-' (or similiar) exists $dlink = $sk->makeKnownLinkObj( $file->getTitle() ); if ( $file->allowInlineDisplay() ) { $dlink2 = $sk->makeImageLinkObj( $file->getTitle(), wfMsgExt( 'fileexists-thumb', 'parseinline' ), $file->getTitle()->getText(), $align, array(), false, true ); } elseif ( !$file->allowInlineDisplay() && $file->isSafeFile() ) { $icon = $file->iconThumb(); $dlink2 = '
    ' . $icon->toHtml( array( 'desc-link' => true ) ) . '
    ' . $dlink . '
    '; } else { $dlink2 = ''; } $warning .= '
  • ' . wfMsgExt( 'fileexists-thumbnail-yes', 'parsemag', $dlink ) . '
  • ' . $dlink2; } return $warning; } /** * Get a list of warnings * * @param string local filename, e.g. 'file exists', 'non-descriptive filename' * @return array list of warning messages */ static function ajaxGetExistsWarning( $filename ) { $file = wfFindFile( $filename ); if( !$file ) { // Force local file so we have an object to do further checks against // if there isn't an exact match... $file = wfLocalFile( $filename ); } $s = ' '; if ( $file ) { $exists = UploadFromBase::getExistsWarning( $file ); $warning = self::getExistsWarning( $exists ); // FIXME: We probably also want the prefix blacklist and the wasdeleted check here if ( $warning !== '' ) { $s = ""; } } return $s; } /** * Render a preview of a given license for the AJAX preview on upload * * @param string $license * @return string */ public static function ajaxGetLicensePreview( $license ) { global $wgParser, $wgUser; $text = '{{' . $license . '}}'; $title = Title::makeTitle( NS_IMAGE, 'Sample.jpg' ); $options = ParserOptions::newFromUser( $wgUser ); // Expand subst: first, then live templates... $text = $wgParser->preSaveTransform( $text, $title, $wgUser, $options ); $output = $wgParser->parse( $text, $title, $options ); return $output->getText(); } /** * Check for duplicate files and throw up a warning before the upload * completes. */ public static function getDupeWarning( $dupes ) { if( $dupes ) { global $wgOut; $msg = ""; foreach( $dupes as $file ) { $title = $file->getTitle(); $msg .= $title->getPrefixedText() . "|" . $title->getText() . "\n"; } $msg .= ""; return "
  • " . wfMsgExt( "file-exists-duplicate", array( "parse" ), count( $dupes ) ) . $wgOut->parse( $msg ) . "
  • \n"; } else { return ''; } } /** * Remove a temporarily kept file stashed by saveTempUploadedFile(). * @access private * @return success */ function unsaveUploadedFile() { global $wgOut; $success = $this->mUpload->unsaveUploadedFile(); if ( ! $success ) { $wgOut->showFileDeleteError( $this->mUpload->getTempPath() ); return false; } else { return true; } } /* -------------------------------------------------------------- */ /** * @param string $error as HTML * @access private */ function uploadError( $error ) { global $wgOut; $wgOut->addHTML( '

    ' . wfMsgHtml( 'uploadwarning' ) . "

    \n" ); $wgOut->addHTML( '' . $error . '' ); } /** * There's something wrong with this file, not enough to reject it * totally but we require manual intervention to save it for real. * Stash it away, then present a form asking to confirm or cancel. * * @param string $warning as HTML * @access private */ function uploadWarning( $warnings ) { global $wgOut; global $wgUseCopyrightUpload; $this->mSessionKey = $this->mUpload->stashSession(); if( !$this->mSessionKey ) { # Couldn't save file; an error has been displayed so let's go. return; } $wgOut->addHTML( '

    ' . wfMsgHtml( 'uploadwarning' ) . "

    \n" ); $wgOut->addHTML( '