mRequest = $request; } protected function initForm() { global $wgRequest, $wgUser; if ( is_null( $this->mRequest ) ) { $request = $wgRequest; } else { $request = $this->mRequest; } // Guess the desired name from the filename if not provided $this->mDesiredDestName = $request->getText( 'wpDestFile' ); if( !$this->mDesiredDestName ) $this->mDesiredDestName = $request->getText( 'wpUploadFile' ); $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; } //if it was posted check for the token (no remote POST'ing with user credentials) $token = $request->getVal( 'wpEditToken' ); $this->mTokenOk = $wgUser->matchEditToken( $token ); # Placeholders for text injection by hooks (empty per default) $this->uploadFormTextTop = ""; $this->uploadFormTextAfterSummary = ""; $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->mForReUpload = $request->getBool( 'wpForReUpload' ); $this->mReUpload = $request->getCheck( 'wpReUpload' ); $this->mAction = $request->getVal( 'action' ); $this->mUpload = UploadBase::createFromRequest( $request ); } public function userCanExecute( $user ) { return UploadBase::isEnabled() && parent::userCanExecute( $user ); } /** * Start doing stuff * @access public */ function execute( $par ) { global $wgUser, $wgOut, $wgRequest; $this->setHeaders(); $this->outputHeader(); $this->initForm(); # Check uploading enabled if( !UploadBase::isEnabled() ) { $wgOut->showErrorPage( 'uploaddisabled', 'uploaddisabledtext' ); return; } # Check permissions global $wgGroupPermissions; if( !$wgUser->isAllowed( 'upload' ) ) { if( !$wgUser->isLoggedIn() && ( $wgGroupPermissions['user']['upload'] || $wgGroupPermissions['autoconfirmed']['upload'] ) ) { // Custom message if logged-in users without any special rights can upload $wgOut->showErrorPage( 'uploadnologin', 'uploadnologintext' ); } else { $wgOut->permissionRequired( 'upload' ); } return; } # Check blocks if( $wgUser->isBlocked() ) { $wgOut->blockedPage(); return; } if( wfReadOnly() ) { $wgOut->readOnlyPage(); return; } //check token if uploading or reUploading if( !$this->mTokenOk && !$this->mReUpload && ($this->mUpload && ( 'submit' == $this->mAction || $this->mUploadClicked ) ) ){ $this->mainUploadForm ( wfMsg( 'session_fail_preview' ) ); return ; } if( $this->mReUpload && $this->mUpload) { // User choose to cancel upload 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() * * FIXME this should really use the standard Status class (instead of associative array) * FIXME would be nice if we refactored this into the upload api. * (the special upload page is not the only response point that needs clean localized error msgs) * * @access private */ function processUpload(){ global $wgOut, $wgFileExtensions, $wgLang; $details = $this->internalProcessUpload(); switch( $details['status'] ) { case UploadBase::SUCCESS: $wgOut->redirect( $this->mLocalFile->getTitle()->getFullURL() ); break; case UploadBase::BEFORE_PROCESSING: $this->uploadError( $details['error'] ); break; case UploadBase::LARGE_FILE_SERVER: $this->mainUploadForm( wfMsgHtml( 'largefileserver' ) ); break; case UploadBase::EMPTY_FILE: $this->mainUploadForm( wfMsgHtml( 'emptyfile' ) ); break; case UploadBase::MIN_LENGTH_PARTNAME: $this->mainUploadForm( wfMsgHtml( 'minlength1' ) ); break; case UploadBase::ILLEGAL_FILENAME: $this->uploadError( wfMsgExt( 'illegalfilename', 'parseinline', $details['filtered'] ) ); break; case UploadBase::PROTECTED_PAGE: $wgOut->showPermissionsErrorPage( $details['permissionserrors'] ); break; case UploadBase::OVERWRITE_EXISTING_FILE: $this->uploadError( wfMsgExt( $details['overwrite'], 'parseinline' ) ); break; case UploadBase::FILETYPE_MISSING: $this->uploadError( wfMsgExt( 'filetype-missing', array ( 'parseinline' ) ) ); break; case UploadBase::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 UploadBase::VERIFICATION_ERROR: unset( $details['status'] ); $code = array_shift( $details ); $this->uploadError( wfMsgExt( $code, 'parseinline', $details ) ); break; case UploadBase::UPLOAD_VERIFICATION_ERROR: $error = $details['error']; $this->uploadError( wfMsgExt( $error, 'parseinline' ) ); break; case UploadBase::UPLOAD_WARNING: unset( $details['status'] ); $this->uploadWarning( $details ); break; case UploadBase::INTERNAL_ERROR: $status = $details['internal']; $this->showError( $wgOut->parse( $status->getWikiText() ) ); break; default: throw new MWException( __METHOD__ . ": Unknown value `{$details['status']}`" ); } } /** * Really do the upload * Checks are made in SpecialUpload::execute() * * @param array $resultDetails contains result-specific dict of additional values * * @access private */ function internalProcessUpload() { global $wgUser; if( !wfRunHooks( 'UploadForm:BeforeProcessing', array( &$this ) ) ) { wfDebug( "Hook 'UploadForm:BeforeProcessing' broke processing the file.\n" ); return array( 'status' => UploadBase::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 ) { return array( 'status' => UploadBase::PROTECTED_PAGE, 'permissionserrors' => $permErrors ); } // Fetch the file if required $status = $this->mUpload->fetchFile(); if( !$status->isOK() ){ return array( 'status' =>UploadBase::BEFORE_PROCESSING, 'error'=>$status->getWikiText() ); } // Check whether this is a sane upload $result = $this->mUpload->verifyUpload(); if( $result != UploadBase::OK ) return $result; $this->mLocalFile = $this->mUpload->getLocalFile(); if( !$this->mIgnoreWarning ) { $warnings = $this->mUpload->checkWarnings(); if( count( $warnings ) ) { $warnings['status'] = UploadBase::UPLOAD_WARNING; return $warnings; } } /** * Try actually saving the thing... * It will show an error form on failure. No it will not. */ if( !$this->mForReUpload ) { $pageText = self::getInitialPageText( $this->mComment, $this->mLicense, $this->mCopyrightStatus, $this->mCopyrightSource ); } else { $pageText = false; } $status = $this->mUpload->performUpload( $this->mComment, $pageText, $this->mWatchthis, $wgUser ); if ( !$status->isGood() ) { return array( 'status' => UploadBase::INTERNAL_ERROR, 'internal' => $status ); } else { // Success, redirect to description page wfRunHooks( 'SpecialUploadComplete', array( &$this ) ); return UploadBase::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; // Check for uppercase extension. We allow these filenames but check if an image // with lowercase extension exists already $warning = ''; $align = $wgContLang->isRtl() ? 'left' : 'right'; if( $exists === false ) return ''; $warning = ''; $align = $wgContLang->isRtl() ? 'left' : 'right'; list( $existsType, $file ) = $exists; $sk = $wgUser->getSkin(); if( $existsType == 'exists' ) { // Exact match $dlink = $sk->linkKnown( $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->linkKnown( $file->getTitle(), '', '',array('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->linkKnown( $nt_lc ); if ( $file_lc->allowInlineDisplay() ) { // FIXME: replace deprecated makeImageLinkObj by link() $dlink2 = $sk->makeImageLinkObj( $nt_lc, wfMsgExt( 'fileexists-thumb', 'parseinline' ), $nt_lc->getText(), $align, array(), false, true ); } elseif ( !$file_lc->allowInlineDisplay() && $file_lc->isSafeFile() ) { $icon = $file_lc->iconThumb(); $dlink2 = '
    ' . $icon->toHtml( array( 'desc-link' => true ) ) . '
    ' . $dlink . '
    '; } else { $dlink2 = ''; } $warning .= '
  • ' . wfMsgExt( 'fileexists-extension', 'parsemag', $file->getTitle()->getPrefixedText(), $dlink ) . '
  • ' . $dlink2; } elseif ( ( substr( $partname , 3, 3 ) == 'px-' || substr( $partname , 2, 3 ) == 'px-' ) && preg_match( "/[0-9]{2}/" , substr( $partname , 0, 2 ) ) ) { # Check for filenames like 50px- or 180px-, these are mostly thumbnails $nt_thb = Title::newFromText( substr( $partname , strpos( $partname , '-' ) +1 ) . '.' . $rawExtension ); $file_thb = wfLocalFile( $nt_thb ); if ($file_thb->exists() ) { # Check if an image without leading '180px-' (or similiar) exists $dlink = $sk->linkKnown( $nt_thb ); if ( $file_thb->allowInlineDisplay() ) { // FIXME: replace deprecated makeImageLinkObj by link() $dlink2 = $sk->makeImageLinkObj( $nt_thb, wfMsgExt( 'fileexists-thumb', 'parseinline' ), $nt_thb->getText(), $align, array(), false, true ); } elseif ( !$file_thb->allowInlineDisplay() && $file_thb->isSafeFile() ) { $icon = $file_thb->iconThumb(); $dlink2 = '
    ' . $icon->toHtml( array( 'desc-link' => true ) ) . '
    ' . $dlink . '
    '; } else { $dlink2 = ''; } $warning .= '
  • ' . wfMsgExt( 'fileexists-thumbnail-yes', 'parsemag', $dlink ) . '
  • ' . $dlink2; } else { # Image w/o '180px-' does not exists, but we do not like these filenames $warning .= '
  • ' . wfMsgExt( 'file-thumbnail-no', 'parseinline' , substr( $partname , 0, strpos( $partname , '-' ) +1 ) ) . '
  • '; } } $filenamePrefixBlacklist = UploadBase::getFilenamePrefixBlacklist(); # Do the match if(!isset($partname)) $partname = ''; foreach( $filenamePrefixBlacklist as $prefix ) { if ( substr( $partname, 0, strlen( $prefix ) ) == $prefix ) { $warning .= '
  • ' . wfMsgExt( 'filename-bad-prefix', 'parseinline', $prefix ) . '
  • '; break; } } if ( $file->wasDeleted() && !$file->exists() ) { # If the file existed before and was deleted, warn the user of this # Don't bother doing so if the file exists now, however $ltitle = SpecialPage::getTitleFor( 'Log' ); $llink = $sk->linkKnown( $ltitle, wfMsgHtml( 'deletionlog' ), array(), array( 'type' => 'delete', 'page' => $file->getTitle()->getPrefixedText() ) ); $warning .= '
  • ' . wfMsgWikiHtml( 'filewasdeleted', $llink ) . '
  • '; } 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 = UploadBase::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_FILE, '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(); } /** * Construct the human readable warning message from an array of duplicate files */ 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; } } /* Interface code starts below this line * * -------------------------------------------------------------- */ /** * @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, $wgUser; global $wgUseCopyrightUpload; $this->mSessionKey = $this->mUpload->stashSession(); if( $this->mSessionKey === false ) { # Couldn't save file; an error has been displayed so let's go. return; } $sk = $wgUser->getSkin(); $wgOut->addHTML( '

    ' . wfMsgHtml( 'uploadwarning' ) . "

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