# filerepo
'ArchivedFile' => 'includes/filerepo/ArchivedFile.php',
'File' => 'includes/filerepo/File.php',
+ 'FileRepo' => 'includes/filerepo/FileRepo.php',
'ForeignDBFile' => 'includes/filerepo/ForeignDBFile.php',
'ForeignDBRepo' => 'includes/filerepo/ForeignDBRepo.php',
'FSRepo' => 'includes/filerepo/FSRepo.php',
*
* @global array $wgAntivirusSetup
*/
-$wgAntivirusSetup= array(
+$wgAntivirusSetup = array(
#setup for clamav
'clamav' => array (
'command' => "clamscan --no-summary ",
- 'codemap'=> array (
- "0"=> AV_NO_VIRUS, #no virus
- "1"=> AV_VIRUS_FOUND, #virus found
- "52"=> AV_SCAN_ABORTED, #unsupported file format (probably imune)
- "*"=> AV_SCAN_FAILED, #else scan failed
+ 'codemap' => array (
+ "0" => AV_NO_VIRUS, # no virus
+ "1" => AV_VIRUS_FOUND, # virus found
+ "52" => AV_SCAN_ABORTED, # unsupported file format (probably imune)
+ "*" => AV_SCAN_FAILED, # else scan failed
),
- 'messagepattern'=> '/.*?:(.*)/sim',
+ 'messagepattern' => '/.*?:(.*)/sim',
),
#setup for f-prot
'f-prot' => array (
'command' => "f-prot ",
- 'codemap'=> array (
- "0"=> AV_NO_VIRUS, #no virus
- "3"=> AV_VIRUS_FOUND, #virus found
- "6"=> AV_VIRUS_FOUND, #virus found
- "*"=> AV_SCAN_FAILED, #else scan failed
+ 'codemap' => array (
+ "0" => AV_NO_VIRUS, # no virus
+ "3" => AV_VIRUS_FOUND, # virus found
+ "6" => AV_VIRUS_FOUND, # virus found
+ "*" => AV_SCAN_FAILED, # else scan failed
),
- 'messagepattern'=> '/.*?Infection:(.*)$/m',
+ 'messagepattern' => '/.*?Infection:(.*)$/m',
),
);
}
$sourcePath = $this->img->getArchiveVirtualUrl( $oldimage );
- $result = $this->img->publish( $sourcePath );
+ $comment = wfMsg( "reverted" );
+ $result = $this->img->upload( $sourcePath, $comment, $comment );
if ( WikiError::isError( $result ) ) {
$this->showError( $result );
# And update recentchanges
if ( $this->updateRecentChanges ) {
$titleObj = SpecialPage::getTitleFor( 'Log', $this->type );
- $rcComment = $this->actionText;
- if( '' != $this->comment ) {
- if ($rcComment == '')
- $rcComment = $this->comment;
- else
- $rcComment .= ': ' . $this->comment;
- }
-
+ $rcComment = $this->getRcComment();
RecentChange::notifyLog( $now, $titleObj, $wgUser, $rcComment, '',
$this->type, $this->action, $this->target, $this->comment, $this->params );
}
return true;
}
+ public function getRcComment() {
+ $rcComment = $this->actionText;
+ if( '' != $this->comment ) {
+ if ($rcComment == '')
+ $rcComment = $this->comment;
+ else
+ $rcComment .= ': ' . $this->comment;
+ }
+ return $rcComment;
+ }
+
/**
* @static
*/
/**#@+
* @access private
*/
- var $mUploadFile, $mUploadDescription, $mLicense ,$mIgnoreWarning, $mUploadError;
- var $mUploadSaveName, $mUploadTempName, $mUploadSize, $mUploadOldVersion;
- var $mUploadCopyStatus, $mUploadSource, $mReUpload, $mAction, $mUpload;
- var $mOname, $mSessionKey, $mStashed, $mDestFile, $mRemoveTempFile, $mSourceType;
- var $mUploadTempFileSize = 0;
- var $mImage;
+ var $mComment, $mLicense, $mIgnoreWarning, $mCurlError;
+ var $mDestName, $mTempPath, $mFileSize, $mFileProps;
+ var $mCopyrightStatus, $mCopyrightSource, $mReUpload, $mAction, $mUploadClicked;
+ var $mSrcName, $mSessionKey, $mStashed, $mDesiredDestName, $mRemoveTempFile, $mSourceType;
+ var $mCurlDestHandle;
+ var $mLocalFile;
# Placeholders for text injection by hooks (must be HTML)
# extensions should take care to _append_ to the present value
var $uploadFormTextTop;
var $uploadFormTextAfterSummary;
+ const SESSION_VERSION = 1;
/**#@-*/
/**
*/
function UploadForm( &$request ) {
global $wgAllowCopyUploads;
- $this->mDestFile = $request->getText( 'wpDestFile' );
+ $this->mDesiredDestName = $request->getText( 'wpDestFile' );
if( !$request->wasPosted() ) {
# GET requests just give the main form; no data except wpDestfile.
$this->mIgnoreWarning = $request->getCheck( 'wpIgnoreWarning' );
$this->mReUpload = $request->getCheck( 'wpReUpload' );
- $this->mUpload = $request->getCheck( 'wpUpload' );
+ $this->mUploadClicked = $request->getCheck( 'wpUpload' );
- $this->mUploadDescription = $request->getText( 'wpUploadDescription' );
+ $this->mComment = $request->getText( 'wpUploadDescription' );
$this->mLicense = $request->getText( 'wpLicense' );
- $this->mUploadCopyStatus = $request->getText( 'wpUploadCopyStatus' );
- $this->mUploadSource = $request->getText( 'wpUploadSource' );
+ $this->mCopyrightStatus = $request->getText( 'wpUploadCopyStatus' );
+ $this->mCopyrightSource = $request->getText( 'wpUploadSource' );
$this->mWatchthis = $request->getBool( 'wpWatchthis' );
- $this->mSourceType = $request->getText( 'wpSourceType' );
+ $this->mSourceType = $request->getText( 'wpSourceType' );
wfDebug( "UploadForm: watchthis is: '$this->mWatchthis'\n" );
$this->mAction = $request->getVal( 'action' );
$this->mSessionKey = $request->getInt( 'wpSessionKey' );
if( !empty( $this->mSessionKey ) &&
- isset( $_SESSION['wsUploadData'][$this->mSessionKey] ) ) {
+ isset( $_SESSION['wsUploadData'][$this->mSessionKey]['version'] ) &&
+ $_SESSION['wsUploadData'][$this->mSessionKey]['version'] == self::SESSION_VERSION ) {
/**
* Confirming a temporarily stashed upload.
* We don't want path names to be forged, so we keep
* an opaque key to the user agent.
*/
$data = $_SESSION['wsUploadData'][$this->mSessionKey];
- $this->mUploadTempName = $data['mUploadTempName'];
- $this->mUploadSize = $data['mUploadSize'];
- $this->mOname = $data['mOname'];
- $this->mUploadError = 0/*UPLOAD_ERR_OK*/;
+ $this->mTempPath = $data['mTempPath'];
+ $this->mFileSize = $data['mFileSize'];
+ $this->mSrcName = $data['mSrcName'];
+ $this->mFileProps = $data['mFileProps'];
+ $this->mCurlError = 0/*UPLOAD_ERR_OK*/;
$this->mStashed = true;
$this->mRemoveTempFile = false;
} else {
* @access private
*/
function initializeFromUpload( $request ) {
- $this->mUploadTempName = $request->getFileTempName( 'wpUploadFile' );
- $this->mUploadSize = $request->getFileSize( 'wpUploadFile' );
- $this->mOname = $request->getFileName( 'wpUploadFile' );
- $this->mUploadError = $request->getUploadError( 'wpUploadFile' );
+ $this->mTempPath = $request->getFileTempName( 'wpUploadFile' );
+ $this->mFileSize = $request->getFileSize( 'wpUploadFile' );
+ $this->mSrcName = $request->getFileName( 'wpUploadFile' );
+ $this->mCurlError = $request->getUploadError( 'wpUploadFile' );
+ $this->mFileProps = File::getPropsFromPath( $this->mTempPath );
$this->mSessionKey = false;
$this->mStashed = false;
$this->mRemoveTempFile = false; // PHP will handle this
$url = $request->getText( 'wpUploadFileURL' );
$local_file = tempnam( $wgTmpDirectory, 'WEBUPLOAD' );
- $this->mUploadTempName = $local_file;
- $this->mUploadError = $this->curlCopy( $url, $local_file );
- $this->mUploadSize = $this->mUploadTempFileSize;
- $this->mOname = array_pop( explode( '/', $url ) );
+ $this->mTempPath = $local_file;
+ $this->mFileSize = 0; # Will be set by curlCopy
+ $this->mCurlError = $this->curlCopy( $url, $local_file );
+ $this->mSrcName = array_pop( explode( '/', $url ) );
$this->mSessionKey = false;
$this->mStashed = false;
}
# Open temporary file
- $this->mUploadTempFile = @fopen( $this->mUploadTempName, "wb" );
- if( $this->mUploadTempFile === false ) {
+ $this->mCurlDestHandle = @fopen( $this->mTempPath, "wb" );
+ if( $this->mCurlDestHandle === false ) {
# Could not open temporary file to write in
$wgOut->errorPage( 'upload-file-error', 'upload-file-error-text');
return true;
// if ( $error ) print curl_error ( $ch ) ; # Debugging output
curl_close( $ch );
- fclose( $this->mUploadTempFile );
- unset( $this->mUploadTempFile );
+ fclose( $this->mCurlDestHandle );
+ unset( $this->mCurlDestHandle );
if( $error ) {
unlink( $dest );
if( wfEmptyMsg( "upload-curl-error$errornum", wfMsg("upload-curl-error$errornum") ) )
function uploadCurlCallback( $ch, $data ) {
global $wgMaxUploadSize;
$length = strlen( $data );
- $this->mUploadTempFileSize += $length;
- if( $this->mUploadTempFileSize > $wgMaxUploadSize ) {
+ $this->mFileSize += $length;
+ if( $this->mFileSize > $wgMaxUploadSize ) {
return 0;
}
- fwrite( $this->mUploadTempFile, $data );
+ fwrite( $this->mCurlDestHandle, $data );
return $length;
}
*/
function execute() {
global $wgUser, $wgOut;
- global $wgEnableUploads, $wgUploadDirectory;
+ global $wgEnableUploads;
# Check uploading enabled
if( !$wgEnableUploads ) {
- $wgOut->showErrorPage( 'uploaddisabled', 'uploaddisabledtext', array( $this->mDestFile ) );
+ $wgOut->showErrorPage( 'uploaddisabled', 'uploaddisabledtext', array( $this->mDesiredDestName ) );
return;
}
return;
}
- /** Check if the image directory is writeable, this is a common mistake */
- if( !is_writeable( $wgUploadDirectory ) ) {
- $wgOut->addWikiText( wfMsg( 'upload_directory_read_only', $wgUploadDirectory ) );
- return;
- }
-
if( $this->mReUpload ) {
if( !$this->unsaveUploadedFile() ) {
return;
}
$this->mainUploadForm();
- } else if( 'submit' == $this->mAction || $this->mUpload ) {
+ } else if( 'submit' == $this->mAction || $this->mUploadClicked ) {
$this->processUpload();
} else {
$this->mainUploadForm();
}
/* Check for PHP error if any, requires php 4.2 or newer */
- if( $this->mUploadError == 1/*UPLOAD_ERR_INI_SIZE*/ ) {
+ if( $this->mCurlError == 1/*UPLOAD_ERR_INI_SIZE*/ ) {
$this->mainUploadForm( wfMsgHtml( 'largefileserver' ) );
return;
}
/**
* If there was no filename or a zero size given, give up quick.
*/
- if( trim( $this->mOname ) == '' || empty( $this->mUploadSize ) ) {
+ if( trim( $this->mSrcName ) == '' || empty( $this->mFileSize ) ) {
$this->mainUploadForm( wfMsgHtml( 'emptyfile' ) );
return;
}
# Chop off any directories in the given filename
- if( $this->mDestFile ) {
- $basename = wfBaseName( $this->mDestFile );
+ if( $this->mDesiredDestName ) {
+ $basename = wfBaseName( $this->mDesiredDestName );
} else {
- $basename = wfBaseName( $this->mOname );
+ $basename = wfBaseName( $this->mSrcName );
}
/**
* out of it. We'll strip some silently that Title would die on.
*/
$filtered = preg_replace ( "/[^".Title::legalChars()."]|:/", '-', $basename );
- $nt = Title::newFromText( $filtered );
+ $nt = Title::makeTitleSafe( NS_IMAGE, $filtered );
if( is_null( $nt ) ) {
$this->uploadError( wfMsgWikiHtml( 'illegalfilename', htmlspecialchars( $filtered ) ) );
return;
}
- $nt =& Title::makeTitle( NS_IMAGE, $nt->getDBkey() );
- $this->mUploadSaveName = $nt->getDBkey();
+ $this->mLocalFile = wfLocalFile( $nt );
+ $this->mDestName = $this->mLocalFile->getName();
/**
* If the image is protected, non-sysop users won't be able
/**
* In some cases we may forbid overwriting of existing files.
*/
- $overwrite = $this->checkOverwrite( $this->mUploadSaveName );
+ $overwrite = $this->checkOverwrite( $this->mDestName );
if( WikiError::isError( $overwrite ) ) {
return $this->uploadError( $overwrite->toString() );
}
if ($finalExt == '') {
return $this->uploadError( wfMsgExt( 'filetype-missing', array ( 'parseinline' ) ) );
} elseif ( $this->checkFileExtensionList( $ext, $wgFileBlacklist ) ||
- ($wgStrictFileExtensions &&
- !$this->checkFileExtension( $finalExt, $wgFileExtensions ) ) ) {
- return $this->uploadError( wfMsgExt( 'filetype-badtype', array ( 'parseinline' ), htmlspecialchars( $finalExt ), implode ( ', ', $wgFileExtensions ) ) );
+ ($wgStrictFileExtensions && !$this->checkFileExtension( $finalExt, $wgFileExtensions ) ) ) {
+ return $this->uploadError( wfMsgExt( 'filetype-badtype', array ( 'parseinline' ),
+ htmlspecialchars( $finalExt ), implode ( ', ', $wgFileExtensions ) ) );
}
/**
*/
if( !$this->mStashed ) {
$this->checkMacBinary();
- $veri = $this->verify( $this->mUploadTempName, $finalExt );
+ $veri = $this->verify( $this->mTempPath, $finalExt );
if( $veri !== true ) { //it's a wiki error...
return $this->uploadError( $veri->toString() );
*/
$error = '';
if( !wfRunHooks( 'UploadVerification',
- array( $this->mUploadSaveName, $this->mUploadTempName, &$error ) ) ) {
+ array( $this->mDestName, $this->mTempPath, &$error ) ) ) {
return $this->uploadError( $error );
}
}
if( $wgCapitalLinks ) {
$filtered = ucfirst( $filtered );
}
- if( $this->mUploadSaveName != $filtered ) {
- $warning .= '<li>'.wfMsgHtml( 'badfilename', htmlspecialchars( $this->mUploadSaveName ) ).'</li>';
+ if( $this->mDestName != $filtered ) {
+ $warning .= '<li>'.wfMsgHtml( 'badfilename', htmlspecialchars( $this->mDestName ) ).'</li>';
}
global $wgCheckFileExtensions;
if ( $wgCheckFileExtensions ) {
if ( ! $this->checkFileExtension( $finalExt, $wgFileExtensions ) ) {
- $warning .= '<li>'.wfMsgExt( 'filetype-badtype', array ( 'parseinline' ), htmlspecialchars( $finalExt ), implode ( ', ', $wgFileExtensions ) ).'</li>';
+ $warning .= '<li>'.wfMsgExt( 'filetype-badtype', array ( 'parseinline' ),
+ htmlspecialchars( $finalExt ), implode ( ', ', $wgFileExtensions ) ).'</li>';
}
}
global $wgUploadSizeWarning;
- if ( $wgUploadSizeWarning && ( $this->mUploadSize > $wgUploadSizeWarning ) ) {
+ if ( $wgUploadSizeWarning && ( $this->mFileSize > $wgUploadSizeWarning ) ) {
$skin = $wgUser->getSkin();
$wsize = $skin->formatSize( $wgUploadSizeWarning );
- $asize = $skin->formatSize( $this->mUploadSize );
+ $asize = $skin->formatSize( $this->mFileSize );
$warning .= '<li>' . wfMsgHtml( 'large-file', $wsize, $asize ) . '</li>';
}
- if ( $this->mUploadSize == 0 ) {
+ if ( $this->mFileSize == 0 ) {
$warning .= '<li>'.wfMsgHtml( 'emptyfile' ).'</li>';
}
global $wgUser;
$sk = $wgUser->getSkin();
- $image = wfLocalFile( $nt );
// Check for uppercase extension. We allow these filenames but check if an image
// with lowercase extension exists already
$image_lc = wfLocalFile( $nt_lc );
}
- if( $image->exists() ) {
+ if( $this->mLocalFile->exists() ) {
$dlink = $sk->makeKnownLinkObj( $nt );
- if ( $image->allowInlineDisplay() ) {
- $dlink2 = $sk->makeImageLinkObj( $nt, wfMsgExt( 'fileexists-thumb', 'parseinline', $dlink ), $nt->getText(), 'right', array(), false, true );
- } elseif ( !$image->allowInlineDisplay() && $image->isSafeFile() ) {
- $icon = $image->iconThumb();
- $dlink2 = '<div style="float:right" id="mw-media-icon"><a href="' . $image->getURL() . '">' . $icon->toHtml() . '</a><br />' . $dlink . '</div>';
+ if ( $this->mLocalFile->allowInlineDisplay() ) {
+ $dlink2 = $sk->makeImageLinkObj( $nt, wfMsgExt( 'fileexists-thumb', 'parseinline', $dlink ),
+ $nt->getText(), 'right', array(), false, true );
+ } elseif ( !$this->mLocalFile->allowInlineDisplay() && $this->mLocalFile->isSafeFile() ) {
+ $icon = $this->mLocalFile->iconThumb();
+ $dlink2 = '<div style="float:right" id="mw-media-icon"><a href="' . $this->mLocalFile->getURL() . '">' .
+ $icon->toHtml() . '</a><br />' . $dlink . '</div>';
} else {
$dlink2 = '';
}
# It's not forbidden but in 99% it makes no sense to upload the same filename with uppercase extension
$dlink = $sk->makeKnownLinkObj( $nt_lc );
if ( $image_lc->allowInlineDisplay() ) {
- $dlink2 = $sk->makeImageLinkObj( $nt_lc, wfMsgExt( 'fileexists-thumb', 'parseinline', $dlink ), $nt_lc->getText(), 'right', array(), false, true );
+ $dlink2 = $sk->makeImageLinkObj( $nt_lc, wfMsgExt( 'fileexists-thumb', 'parseinline', $dlink ),
+ $nt_lc->getText(), 'right', array(), false, true );
} elseif ( !$image_lc->allowInlineDisplay() && $image_lc->isSafeFile() ) {
$icon = $image_lc->iconThumb();
- $dlink2 = '<div style="float:right" id="mw-media-icon"><a href="' . $image_lc->getURL() . '">' . $icon->toHtml() . '</a><br />' . $dlink . '</div>';
+ $dlink2 = '<div style="float:right" id="mw-media-icon"><a href="' . $image_lc->getURL() . '">' .
+ $icon->toHtml() . '</a><br />' . $dlink . '</div>';
} else {
$dlink2 = '';
}
- $warning .= '<li>' . wfMsgExt( 'fileexists-extension', 'parsemag' , $partname . '.' . $finalExt , $dlink ) . '</li>' . $dlink2;
+ $warning .= '<li>' . wfMsgExt( 'fileexists-extension', 'parsemag' , $partname . '.'
+ . $finalExt , $dlink ) . '</li>' . $dlink2;
- } elseif ( ( substr( $partname , 3, 3 ) == 'px-' || substr( $partname , 2, 3 ) == 'px-' ) && ereg( "[0-9]{2}" , substr( $partname , 0, 2) ) ) {
+ } elseif ( ( substr( $partname , 3, 3 ) == 'px-' || substr( $partname , 2, 3 ) == 'px-' )
+ && ereg( "[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 ) . '.' . $finalExt );
$image_thb = wfLocalFile( $nt_thb );
# Check if an image without leading '180px-' (or similiar) exists
$dlink = $sk->makeKnownLinkObj( $nt_thb);
if ( $image_thb->allowInlineDisplay() ) {
- $dlink2 = $sk->makeImageLinkObj( $nt_thb, wfMsgExt( 'fileexists-thumb', 'parseinline', $dlink ), $nt_thb->getText(), 'right', array(), false, true );
+ $dlink2 = $sk->makeImageLinkObj( $nt_thb,
+ wfMsgExt( 'fileexists-thumb', 'parseinline', $dlink ),
+ $nt_thb->getText(), 'right', array(), false, true );
} elseif ( !$image_thb->allowInlineDisplay() && $image_thb->isSafeFile() ) {
$icon = $image_thb->iconThumb();
- $dlink2 = '<div style="float:right" id="mw-media-icon"><a href="' . $image_thb->getURL() . '">' . $icon->toHtml() . '</a><br />' . $dlink . '</div>';
+ $dlink2 = '<div style="float:right" id="mw-media-icon"><a href="' .
+ $image_thb->getURL() . '">' . $icon->toHtml() . '</a><br />' .
+ $dlink . '</div>';
} else {
$dlink2 = '';
}
- $warning .= '<li>' . wfMsgExt( 'fileexists-thumbnail-yes', 'parsemag', $dlink ) . '</li>' . $dlink2;
+ $warning .= '<li>' . wfMsgExt( 'fileexists-thumbnail-yes', 'parsemag', $dlink ) .
+ '</li>' . $dlink2;
} else {
# Image w/o '180px-' does not exists, but we do not like these filenames
- $warning .= '<li>' . wfMsgExt( 'file-thumbnail-no', 'parseinline' , substr( $partname , 0, strpos( $partname , '-' ) +1 ) ) . '</li>';
+ $warning .= '<li>' . wfMsgExt( 'file-thumbnail-no', 'parseinline' ,
+ substr( $partname , 0, strpos( $partname , '-' ) +1 ) ) . '</li>';
}
}
- if ( $image->wasDeleted() ) {
+ if ( $this->mLocalFile->wasDeleted() ) {
# If the file existed before and was deleted, warn the user of this
# Don't bother doing so if the image exists now, however
$ltitle = SpecialPage::getTitleFor( 'Log' );
- $llink = $sk->makeKnownLinkObj( $ltitle, wfMsgHtml( 'deletionlog' ), 'type=delete&page=' . $nt->getPrefixedUrl() );
- $warning .= wfOpenElement( 'li' ) . wfMsgWikiHtml( 'filewasdeleted', $llink ) . wfCloseElement( 'li' );
+ $llink = $sk->makeKnownLinkObj( $ltitle, wfMsgHtml( 'deletionlog' ),
+ 'type=delete&page=' . $nt->getPrefixedUrl() );
+ $warning .= wfOpenElement( 'li' ) . wfMsgWikiHtml( 'filewasdeleted', $llink ) .
+ wfCloseElement( 'li' );
}
if( $warning != '' ) {
* Try actually saving the thing...
* It will show an error form on failure.
*/
- $hasBeenMunged = !empty( $this->mSessionKey ) || $this->mRemoveTempFile;
- if( $this->saveUploadedFile( $this->mUploadSaveName,
- $this->mUploadTempName,
- $hasBeenMunged ) ) {
- /**
- * Update the upload log and create the description page
- * if it's a new file.
- */
- $this->mImage = wfLocalFile( $this->mUploadSaveName );
- $success = $this->mImage->recordUpload( $this->mUploadOldVersion,
- $this->mUploadDescription,
- $this->mLicense,
- $this->mUploadCopyStatus,
- $this->mUploadSource,
- $this->mWatchthis );
-
- if ( $success ) {
- $this->showSuccess();
- wfRunHooks( 'UploadComplete', array( &$img ) );
- } else {
- // File::recordUpload() fails if the image went missing, which is
- // unlikely, hence the lack of a specialised message
- $wgOut->showFileNotFoundError( $this->mUploadSaveName );
- }
- }
- }
-
- /**
- * Move the uploaded file from its temporary location to the final
- * destination. If a previous version of the file exists, move
- * it into the archive subdirectory.
- *
- * @todo If the later save fails, we may have disappeared the original file.
- *
- * @param string $saveName
- * @param string $tempName full path to the temporary file
- * @param bool $useRename if true, doesn't check that the source file
- * is a PHP-managed upload temporary
- */
- function saveUploadedFile( $saveName, $tempName, $useRename = false ) {
- global $wgOut, $wgAllowCopyUploads;
+ $pageText = self::getInitialPageText( $this->mComment, $this->mLicense,
+ $this->mCopyrightStatus, $this->mCopyrightSource );
- $image = wfLocalFile( $saveName );
- $archiveName = $image->publish( $tempName, File::DELETE_SOURCE );
- if ( WikiError::isError( $archiveName ) ) {
- $this->showError( $archiveName );
- return false;
+ $error = $this->mLocalFile->upload( $this->mTempPath, $this->mComment, $pageText,
+ File::DELETE_SOURCE, $this->mFileProps );
+ if ( WikiError::isError( $error ) ) {
+ $this->showError( $error );
+ } else {
+ if ( $this->mWatchthis ) {
+ global $wgUser;
+ $wgUser->addWatch( $this->mLocalFile->getTitle() );
+ }
+ $this->showSuccess();
+ wfRunHooks( 'UploadComplete', array( &$img ) );
}
- $this->mUploadOldVersion = $archiveName;
- return true;
}
/**
* @access private
*/
function stashSession() {
- $stash = $this->saveTempUploadedFile(
- $this->mUploadSaveName, $this->mUploadTempName );
+ $stash = $this->saveTempUploadedFile( $this->mDestName, $this->mTempPath );
if( !$stash ) {
# Couldn't save the file.
$key = mt_rand( 0, 0x7fffffff );
$_SESSION['wsUploadData'][$key] = array(
- 'mUploadTempName' => $stash,
- 'mUploadSize' => $this->mUploadSize,
- 'mOname' => $this->mOname );
+ 'mTempPath' => $stash,
+ 'mFileSize' => $this->mFileSize,
+ 'mSrcName' => $this->mSrcName,
+ 'mFileProps' => $this->mFileProps,
+ 'version' => self::SESSION_VERSION,
+ );
return $key;
}
function unsaveUploadedFile() {
global $wgOut;
$repo = RepoGroup::singleton()->getLocalRepo();
- $success = $repo->freeTemp( $this->mUploadTempName );
+ $success = $repo->freeTemp( $this->mTempPath );
if ( ! $success ) {
- $wgOut->showFileDeleteError( $this->mUploadTempName );
+ $wgOut->showFileDeleteError( $this->mTempPath );
return false;
} else {
return true;
global $wgUser, $wgOut, $wgContLang;
$sk = $wgUser->getSkin();
- $ilink = $sk->makeMediaLinkObj( $this->mImage->getTitle() );
- $dname = $wgContLang->getNsText( NS_IMAGE ) . ':'.$this->mUploadSaveName;
+ $ilink = $sk->makeMediaLinkObj( $this->mLocalFile->getTitle() );
+ $dname = $wgContLang->getNsText( NS_IMAGE ) . ':'.$this->mDestName;
$dlink = $sk->makeKnownLink( $dname, $dname );
$wgOut->addHTML( '<h2>' . wfMsgHtml( 'successfulupload' ) . "</h2>\n" );
if ( $wgUseCopyrightUpload )
{
$copyright = "
- <input type='hidden' name='wpUploadCopyStatus' value=\"" . htmlspecialchars( $this->mUploadCopyStatus ) . "\" />
- <input type='hidden' name='wpUploadSource' value=\"" . htmlspecialchars( $this->mUploadSource ) . "\" />
+ <input type='hidden' name='wpUploadCopyStatus' value=\"" . htmlspecialchars( $this->mCopyrightStatus ) . "\" />
+ <input type='hidden' name='wpUploadSource' value=\"" . htmlspecialchars( $this->mCopyrightSource ) . "\" />
";
} else {
$copyright = "";
<form id='uploadwarning' method='post' enctype='multipart/form-data' action='$action'>
<input type='hidden' name='wpIgnoreWarning' value='1' />
<input type='hidden' name='wpSessionKey' value=\"" . htmlspecialchars( $this->mSessionKey ) . "\" />
- <input type='hidden' name='wpUploadDescription' value=\"" . htmlspecialchars( $this->mUploadDescription ) . "\" />
+ <input type='hidden' name='wpUploadDescription' value=\"" . htmlspecialchars( $this->mComment ) . "\" />
<input type='hidden' name='wpLicense' value=\"" . htmlspecialchars( $this->mLicense ) . "\" />
- <input type='hidden' name='wpDestFile' value=\"" . htmlspecialchars( $this->mDestFile ) . "\" />
+ <input type='hidden' name='wpDestFile' value=\"" . htmlspecialchars( $this->mDesiredDestName ) . "\" />
<input type='hidden' name='wpWatchthis' value=\"" . htmlspecialchars( intval( $this->mWatchthis ) ) . "\" />
{$copyright}
<table border='0'>
"<span class='error'>{$msg}</span>\n" );
}
$wgOut->addHTML( '<div id="uploadtext">' );
- $wgOut->addWikiText( wfMsgNoTrans( 'uploadtext', $this->mDestFile ) );
+ $wgOut->addWikiText( wfMsgNoTrans( 'uploadtext', $this->mDesiredDestName ) );
$wgOut->addHTML( '</div>' );
$sourcefilename = wfMsgHtml( 'sourcefilename' );
$titleObj = SpecialPage::getTitleFor( 'Upload' );
$action = $titleObj->escapeLocalURL();
- $encDestFile = htmlspecialchars( $this->mDestFile );
+ $encDestName = htmlspecialchars( $this->mDesiredDestName );
$watchChecked =
( $wgUser->getOption( 'watchdefault' ) ||
- ( $wgUser->getOption( 'watchcreations' ) && $this->mDestFile == '' ) )
+ ( $wgUser->getOption( 'watchcreations' ) && $this->mDesiredDestName == '' ) )
? 'checked="checked"'
: '';
// Prepare form for upload or upload/copy
if( $wgAllowCopyUploads && $wgUser->isAllowed( 'upload_by_url' ) ) {
$filename_form =
- "<input type='radio' id='wpSourceTypeFile' name='wpSourceType' value='file' onchange='toggle_element_activation(\"wpUploadFileURL\",\"wpUploadFile\")' checked />" .
- "<input tabindex='1' type='file' name='wpUploadFile' id='wpUploadFile' onfocus='toggle_element_activation(\"wpUploadFileURL\",\"wpUploadFile\");toggle_element_check(\"wpSourceTypeFile\",\"wpSourceTypeURL\")'" .
- ($this->mDestFile?"":"onchange='fillDestFilename(\"wpUploadFile\")' ") . "size='40' />" .
+ "<input type='radio' id='wpSourceTypeFile' name='wpSourceType' value='file' " .
+ "onchange='toggle_element_activation(\"wpUploadFileURL\",\"wpUploadFile\")' checked />" .
+ "<input tabindex='1' type='file' name='wpUploadFile' id='wpUploadFile' " .
+ "onfocus='" .
+ "toggle_element_activation(\"wpUploadFileURL\",\"wpUploadFile\");" .
+ "toggle_element_check(\"wpSourceTypeFile\",\"wpSourceTypeURL\")'" .
+ ($this->mDesiredDestName?"":"onchange='fillDestFilename(\"wpUploadFile\")' ") . "size='40' />" .
wfMsgHTML( 'upload_source_file' ) . "<br/>" .
- "<input type='radio' id='wpSourceTypeURL' name='wpSourceType' value='web' onchange='toggle_element_activation(\"wpUploadFile\",\"wpUploadFileURL\")' />" .
- "<input tabindex='1' type='text' name='wpUploadFileURL' id='wpUploadFileURL' onfocus='toggle_element_activation(\"wpUploadFile\",\"wpUploadFileURL\");toggle_element_check(\"wpSourceTypeURL\",\"wpSourceTypeFile\")'" .
- ($this->mDestFile?"":"onchange='fillDestFilename(\"wpUploadFileURL\")' ") . "size='40' DISABLED />" .
+ "<input type='radio' id='wpSourceTypeURL' name='wpSourceType' value='web' " .
+ "onchange='toggle_element_activation(\"wpUploadFile\",\"wpUploadFileURL\")' />" .
+ "<input tabindex='1' type='text' name='wpUploadFileURL' id='wpUploadFileURL' " .
+ "onfocus='" .
+ "toggle_element_activation(\"wpUploadFile\",\"wpUploadFileURL\");" .
+ "toggle_element_check(\"wpSourceTypeURL\",\"wpSourceTypeFile\")'" .
+ ($this->mDesiredDestName?"":"onchange='fillDestFilename(\"wpUploadFileURL\")' ") . "size='40' DISABLED />" .
wfMsgHtml( 'upload_source_url' ) ;
} else {
$filename_form =
"<input tabindex='1' type='file' name='wpUploadFile' id='wpUploadFile' " .
- ($this->mDestFile?"":"onchange='fillDestFilename(\"wpUploadFile\")' ") .
+ ($this->mDesiredDestName?"":"onchange='fillDestFilename(\"wpUploadFile\")' ") .
"size='40' />" .
"<input type='hidden' name='wpSourceType' value='file' />" ;
}
+ $encComment = htmlspecialchars( $this->mComment );
- $wgOut->addHTML( "
- <form id='upload' method='post' enctype='multipart/form-data' action=\"$action\">
+ $wgOut->addHTML( <<<EOT
+ <form id='upload' method='post' enctype='multipart/form-data' action="$action">
<table border='0'>
<tr>
{$this->uploadFormTextTop}
<tr>
<td align='right'><label for='wpDestFile'>{$destfilename}:</label></td>
<td align='left'>
- <input tabindex='2' type='text' name='wpDestFile' id='wpDestFile' size='40' value=\"$encDestFile\" />
+ <input tabindex='2' type='text' name='wpDestFile' id='wpDestFile' size='40' value="$encDestName" />
</td>
</tr>
<tr>
<td align='right'><label for='wpUploadDescription'>{$summary}</label></td>
<td align='left'>
- <textarea tabindex='3' name='wpUploadDescription' id='wpUploadDescription' rows='6' cols='{$cols}'{$ew}>" . htmlspecialchars( $this->mUploadDescription ) . "</textarea>
+ <textarea tabindex='3' name='wpUploadDescription' id='wpUploadDescription' rows='6'
+ cols='{$cols}'{$ew}>$encComment</textarea>
{$this->uploadFormTextAfterSummary}
</td>
</tr>
- <tr>" );
+ <tr>
+EOT
+ );
if ( $licenseshtml != '' ) {
global $wgStylePath;
if ( $wgUseCopyrightUpload ) {
$filestatus = wfMsgHtml ( 'filestatus' );
- $copystatus = htmlspecialchars( $this->mUploadCopyStatus );
+ $copystatus = htmlspecialchars( $this->mCopyrightStatus );
$filesource = wfMsgHtml ( 'filesource' );
- $uploadsource = htmlspecialchars( $this->mUploadSource );
+ $uploadsource = htmlspecialchars( $this->mCopyrightSource );
$wgOut->addHTML( "
<td align='right' nowrap='nowrap'><label for='wpUploadCopyStatus'>$filestatus:</label></td>
- <td><input tabindex='5' type='text' name='wpUploadCopyStatus' id='wpUploadCopyStatus' value=\"$copystatus\" size='40' /></td>
+ <td><input tabindex='5' type='text' name='wpUploadCopyStatus' id='wpUploadCopyStatus'
+ value=\"$copystatus\" size='40' /></td>
</tr>
<tr>
<td align='right'><label for='wpUploadCopyStatus'>$filesource:</label></td>
- <td><input tabindex='6' type='text' name='wpUploadSource' id='wpUploadCopyStatus' value=\"$uploadsource\" size='40' /></td>
+ <td><input tabindex='6' type='text' name='wpUploadSource' id='wpUploadCopyStatus'
+ value=\"$uploadsource\" size='40' /></td>
</tr>
<tr>
");
$magic=& MimeMagic::singleton();
$mime= $magic->guessMimeType($tmpfile,false);
- $fname= "SpecialUpload::verify";
-
#check mime type, if desired
global $wgVerifyMimeType;
if ($wgVerifyMimeType) {
return new WikiErrorMsg( 'uploadvirus', htmlspecialchars($virus) );
}
- wfDebug( "$fname: all clear; passing.\n" );
+ wfDebug( __METHOD__.": all clear; passing.\n" );
return true;
}
* @return bool
*/
function verifyExtension( $mime, $extension ) {
- $fname = 'SpecialUpload::verifyExtension';
-
$magic =& MimeMagic::singleton();
if ( ! $mime || $mime == 'unknown' || $mime == 'unknown/unknown' )
if ( ! $magic->isRecognizableExtension( $extension ) ) {
- wfDebug( "$fname: passing file with unknown detected mime type; unrecognized extension '$extension', can't verify\n" );
+ wfDebug( __METHOD__.": passing file with unknown detected mime type; " .
+ "unrecognized extension '$extension', can't verify\n" );
return true;
} else {
- wfDebug( "$fname: rejecting file with unknown detected mime type; recognized extension '$extension', so probably invalid file\n" );
+ wfDebug( __METHOD__.": rejecting file with unknown detected mime type; ".
+ "recognized extension '$extension', so probably invalid file\n" );
return false;
}
$match= $magic->isMatchingExtension($extension,$mime);
if ($match===NULL) {
- wfDebug( "$fname: no file extension known for mime type $mime, passing file\n" );
+ wfDebug( __METHOD__.": no file extension known for mime type $mime, passing file\n" );
return true;
} elseif ($match===true) {
- wfDebug( "$fname: mime type $mime matches extension $extension, passing file\n" );
+ wfDebug( __METHOD__.": mime type $mime matches extension $extension, passing file\n" );
#TODO: if it's a bitmap, make sure PHP or ImageMagic resp. can handle it!
return true;
} else {
- wfDebug( "$fname: mime type $mime mismatches file extension $extension, rejecting file\n" );
+ wfDebug( __METHOD__.": mime type $mime mismatches file extension $extension, rejecting file\n" );
return false;
}
}
- /** Heuristig for detecting files that *could* contain JavaScript instructions or
- * things that may look like HTML to a browser and are thus
- * potentially harmful. The present implementation will produce false positives in some situations.
- *
- * @param string $file Pathname to the temporary upload file
- * @param string $mime The mime type of the file
- * @param string $extension The extension of the file
- * @return bool true if the file contains something looking like embedded scripts
- */
+ /**
+ * Heuristic for detecting files that *could* contain JavaScript instructions or
+ * things that may look like HTML to a browser and are thus
+ * potentially harmful. The present implementation will produce false positives in some situations.
+ *
+ * @param string $file Pathname to the temporary upload file
+ * @param string $mime The mime type of the file
+ * @param string $extension The extension of the file
+ * @return bool true if the file contains something looking like embedded scripts
+ */
function detectScript($file, $mime, $extension) {
global $wgAllowTitlesInSVG;
return false;
}
- /** Generic wrapper function for a virus scanner program.
- * This relies on the $wgAntivirus and $wgAntivirusSetup variables.
- * $wgAntivirusRequired may be used to deny upload if the scan fails.
- *
- * @param string $file Pathname to the temporary upload file
- * @return mixed false if not virus is found, NULL if the scan fails or is disabled,
- * or a string containing feedback from the virus scanner if a virus was found.
- * If textual feedback is missing but a virus was found, this function returns true.
- */
+ /**
+ * Generic wrapper function for a virus scanner program.
+ * This relies on the $wgAntivirus and $wgAntivirusSetup variables.
+ * $wgAntivirusRequired may be used to deny upload if the scan fails.
+ *
+ * @param string $file Pathname to the temporary upload file
+ * @return mixed false if not virus is found, NULL if the scan fails or is disabled,
+ * or a string containing feedback from the virus scanner if a virus was found.
+ * If textual feedback is missing but a virus was found, this function returns true.
+ */
function detectVirus($file) {
global $wgAntivirus, $wgAntivirusSetup, $wgAntivirusRequired, $wgOut;
- $fname= "SpecialUpload::detectVirus";
-
- if (!$wgAntivirus) { #disabled?
- wfDebug("$fname: virus scanner disabled\n");
-
+ if ( !$wgAntivirus ) {
+ wfDebug( __METHOD__.": virus scanner disabled\n");
return NULL;
}
- if (!$wgAntivirusSetup[$wgAntivirus]) {
- wfDebug("$fname: unknown virus scanner: $wgAntivirus\n");
-
- $wgOut->addHTML( "<div class='error'>Bad configuration: unknown virus scanner: <i>$wgAntivirus</i></div>\n" ); #LOCALIZE
-
+ if ( !$wgAntivirusSetup[$wgAntivirus] ) {
+ wfDebug( __METHOD__.": unknown virus scanner: $wgAntivirus\n" );
+ # @TODO: localise
+ $wgOut->addHTML( "<div class='error'>Bad configuration: unknown virus scanner: <i>$wgAntivirus</i></div>\n" );
return "unknown antivirus: $wgAntivirus";
}
- #look up scanner configuration
- $virus_scanner= $wgAntivirusSetup[$wgAntivirus]["command"]; #command pattern
- $virus_scanner_codes= $wgAntivirusSetup[$wgAntivirus]["codemap"]; #exit-code map
- $msg_pattern= $wgAntivirusSetup[$wgAntivirus]["messagepattern"]; #message pattern
-
- $scanner= $virus_scanner; #copy, so we can resolve the pattern
+ # look up scanner configuration
+ $command = $wgAntivirusSetup[$wgAntivirus]["command"];
+ $exitCodeMap = $wgAntivirusSetup[$wgAntivirus]["codemap"];
+ $msgPattern = isset( $wgAntivirusSetup[$wgAntivirus]["messagepattern"] ) ?
+ $wgAntivirusSetup[$wgAntivirus]["messagepattern"] : null;
- if (strpos($scanner,"%f")===false) $scanner.= " ".wfEscapeShellArg($file); #simple pattern: append file to scan
- else $scanner= str_replace("%f",wfEscapeShellArg($file),$scanner); #complex pattern: replace "%f" with file to scan
+ if ( strpos( $command,"%f" ) === false ) {
+ # simple pattern: append file to scan
+ $command .= " " . wfEscapeShellArg( $file );
+ } else {
+ # complex pattern: replace "%f" with file to scan
+ $command = str_replace( "%f", wfEscapeShellArg( $file ), $command );
+ }
- wfDebug("$fname: running virus scan: $scanner \n");
+ wfDebug( __METHOD__.": running virus scan: $command \n" );
- #execute virus scanner
- $code= false;
+ # execute virus scanner
+ $exitCode = false;
#NOTE: there's a 50 line workaround to make stderr redirection work on windows, too.
# that does not seem to be worth the pain.
# Ask me (Duesentrieb) about it if it's ever needed.
$output = array();
- if (wfIsWindows()) exec("$scanner",$output,$code);
- else exec("$scanner 2>&1",$output,$code);
-
- $exit_code= $code; #remember for user feedback
+ if ( wfIsWindows() ) {
+ exec( "$command", $output, $exitCode );
+ } else {
+ exec( "$command 2>&1", $output, $exitCode );
+ }
- if ($virus_scanner_codes) { #map exit code to AV_xxx constants.
- if (isset($virus_scanner_codes[$code])) {
- $code= $virus_scanner_codes[$code]; # explicit mapping
- } else if (isset($virus_scanner_codes["*"])) {
- $code= $virus_scanner_codes["*"]; # fallback mapping
+ # map exit code to AV_xxx constants.
+ $mappedCode = $exitCode;
+ if ( $exitCodeMap ) {
+ if ( isset( $exitCodeMap[$exitCode] ) ) {
+ $mappedCode = $exitCodeMap[$exitCode];
+ } elseif ( isset( $exitCodeMap["*"] ) ) {
+ $mappedCode = $exitCodeMap["*"];
}
}
- if ($code===AV_SCAN_FAILED) { #scan failed (code was mapped to false by $virus_scanner_codes)
- wfDebug("$fname: failed to scan $file (code $exit_code).\n");
+ if ( $mappedCode === AV_SCAN_FAILED ) {
+ # scan failed (code was mapped to false by $exitCodeMap)
+ wfDebug( __METHOD__.": failed to scan $file (code $exitCode).\n" );
- if ($wgAntivirusRequired) { return "scan failed (code $exit_code)"; }
- else { return NULL; }
- }
- else if ($code===AV_SCAN_ABORTED) { #scan failed because filetype is unknown (probably imune)
- wfDebug("$fname: unsupported file type $file (code $exit_code).\n");
+ if ( $wgAntivirusRequired ) {
+ return "scan failed (code $exitCode)";
+ } else {
+ return NULL;
+ }
+ } else if ( $mappedCode === AV_SCAN_ABORTED ) {
+ # scan failed because filetype is unknown (probably imune)
+ wfDebug( __METHOD__.": unsupported file type $file (code $exitCode).\n" );
return NULL;
- }
- else if ($code===AV_NO_VIRUS) {
- wfDebug("$fname: file passed virus scan.\n");
- return false; #no virus found
- }
- else {
- $output= join("\n",$output);
- $output= trim($output);
-
- if (!$output) $output= true; #if there's no output, return true
- else if ($msg_pattern) {
- $groups= array();
- if (preg_match($msg_pattern,$output,$groups)) {
- if ($groups[1]) $output= $groups[1];
+ } else if ( $mappedCode === AV_NO_VIRUS ) {
+ # no virus found
+ wfDebug( __METHOD__.": file passed virus scan.\n" );
+ return false;
+ } else {
+ $output = join( "\n", $output );
+ $output = trim( $output );
+
+ if ( !$output ) {
+ $output = true; #if there's no output, return true
+ } elseif ( $msgPattern ) {
+ $groups = array();
+ if ( preg_match( $msgPattern, $output, $groups ) ) {
+ if ( $groups[1] ) {
+ $output = $groups[1];
+ }
}
}
- wfDebug("$fname: FOUND VIRUS! scanner feedback: $output");
+ wfDebug( __METHOD__.": FOUND VIRUS! scanner feedback: $output" );
return $output;
}
}
* @access private
*/
function checkMacBinary() {
- $macbin = new MacBinary( $this->mUploadTempName );
+ $macbin = new MacBinary( $this->mTempPath );
if( $macbin->isValid() ) {
$dataFile = tempnam( wfTempDir(), "WikiMacBinary" );
$dataHandle = fopen( $dataFile, 'wb' );
wfDebug( "SpecialUpload::checkMacBinary: Extracting MacBinary data fork to $dataFile\n" );
$macbin->extractData( $dataHandle );
- $this->mUploadTempName = $dataFile;
- $this->mUploadSize = $macbin->dataForkLength();
+ $this->mTempPath = $dataFile;
+ $this->mFileSize = $macbin->dataForkLength();
// We'll have to manually remove the new file if it's not kept.
$this->mRemoveTempFile = true;
* @access private
*/
function cleanupTempFile() {
- if( $this->mRemoveTempFile && file_exists( $this->mUploadTempName ) ) {
- wfDebug( "SpecialUpload::cleanupTempFile: Removing temporary file $this->mUploadTempName\n" );
- unlink( $this->mUploadTempName );
+ if ( $this->mRemoveTempFile && file_exists( $this->mTempPath ) ) {
+ wfDebug( "SpecialUpload::cleanupTempFile: Removing temporary file {$this->mTempPath}\n" );
+ unlink( $this->mTempPath );
}
}
$wgOut->enableClientCache( false );
$wgOut->addWikiText( $error->getMessage() );
}
+
+ /**
+ * Get the initial image page text based on a comment and optional file status information
+ */
+ static function getInitialPageText( $comment, $license, $copyStatus, $source ) {
+ global $wgUseCopyrightUpload;
+ if ( $wgUseCopyrightUpload ) {
+ if ( $license != '' ) {
+ $licensetxt = '== ' . wfMsgForContent( 'license' ) . " ==\n" . '{{' . $license . '}}' . "\n";
+ }
+ $pageText = '== ' . wfMsg ( 'filedesc' ) . " ==\n" . $comment . "\n" .
+ '== ' . wfMsgForContent ( 'filestatus' ) . " ==\n" . $copyStatus . "\n" .
+ "$licensetxt" .
+ '== ' . wfMsgForContent ( 'filesource' ) . " ==\n" . $source ;
+ } else {
+ if ( $license != '' ) {
+ $filedesc = $comment == '' ? '' : '== ' . wfMsg ( 'filedesc' ) . " ==\n" . $comment . "\n";
+ $pageText = $filedesc .
+ '== ' . wfMsgForContent ( 'license' ) . " ==\n" . '{{' . $license . '}}' . "\n";
+ } else {
+ $pageText = $comment;
+ }
+ }
+ return $pageText;
+ }
}
?>
/**
* A repository for files accessible via the local filesystem. Does not support
* database access or registration.
- *
- * TODO: split off abstract base FileRepo
*/
-class FSRepo {
- const DELETE_SOURCE = 1;
-
- var $directory, $url, $hashLevels, $thumbScriptUrl, $transformVia404;
- var $descBaseUrl, $scriptDirUrl, $articleUrl, $fetchDescription, $initialCapital;
+class FSRepo extends FileRepo {
+ var $directory, $url, $hashLevels;
var $fileFactory = array( 'UnregisteredLocalFile', 'newFromTitle' );
var $oldFileFactory = false;
function __construct( $info ) {
+ parent::__construct( $info );
+
// Required settings
- $this->name = $info['name'];
$this->directory = $info['directory'];
$this->url = $info['url'];
$this->hashLevels = $info['hashLevels'];
- $this->transformVia404 = !empty( $info['transformVia404'] );
-
- // Optional settings
- $this->initialCapital = true; // by default
- foreach ( array( 'descBaseUrl', 'scriptDirUrl', 'articleUrl', 'fetchDescription',
- 'thumbScriptUrl', 'initialCapital' ) as $var )
- {
- if ( isset( $info[$var] ) ) {
- $this->$var = $info[$var];
- }
- }
- }
-
- /**
- * Create a new File object from the local repository
- * @param mixed $title Title object or string
- * @param mixed $time Time at which the image is supposed to have existed.
- * If this is specified, the returned object will be an
- * instance of the repository's old file class instead of
- * a current file. Repositories not supporting version
- * control should return false if this parameter is set.
- */
- function newFile( $title, $time = false ) {
- if ( !($title instanceof Title) ) {
- $title = Title::makeTitleSafe( NS_IMAGE, $title );
- if ( !is_object( $title ) ) {
- return null;
- }
- }
- if ( $time ) {
- if ( $this->oldFileFactory ) {
- return call_user_func( $this->oldFileFactory, $title, $this, $time );
- } else {
- return false;
- }
- } else {
- return call_user_func( $this->fileFactory, $title, $this );
- }
- }
-
- /**
- * Find an instance of the named file that existed at the specified time
- * Returns false if the file did not exist. Repositories not supporting
- * version control should return false if the time is specified.
- *
- * @param mixed $time 14-character timestamp, or false for the current version
- */
- function findFile( $title, $time = false ) {
- # First try the current version of the file to see if it precedes the timestamp
- $img = $this->newFile( $title );
- if ( !$img ) {
- return false;
- }
- if ( $img->exists() && ( !$time || $img->getTimestamp() <= $time ) ) {
- return $img;
- }
- # Now try an old version of the file
- $img = $this->newFile( $title, $time );
- if ( $img->exists() ) {
- return $img;
- }
}
/**
return (bool)$this->hashLevels;
}
- /**
- * Get the URL of thumb.php
- */
- function getThumbScriptUrl() {
- return $this->thumbScriptUrl;
- }
-
- /**
- * Returns true if the repository can transform files via a 404 handler
- */
- function canTransformVia404() {
- return $this->transformVia404;
- }
-
/**
* Get the local directory corresponding to one of the three basic zones
*/
/**
* Get a URL referring to this repository, with the private mwrepo protocol.
+ * The suffix, if supplied, is considered to be unencoded, and will be
+ * URL-encoded before being returned.
*/
function getVirtualUrl( $suffix = false ) {
- $path = 'mwrepo://';
+ $path = 'mwrepo://' . $this->name;
if ( $suffix !== false ) {
- $path .= '/' . $suffix;
+ $path .= '/' . rawurlencode( $suffix );
}
return $path;
}
if ( count( $bits ) != 3 ) {
throw new MWException( __METHOD__.": invalid mwrepo URL: $url" );
}
- list( $host, $zone, $rel ) = $bits;
- if ( $host !== '' ) {
+ list( $repo, $zone, $rel ) = $bits;
+ if ( $repo !== $this->name ) {
throw new MWException( __METHOD__.": fetching from a foreign repo is not supported" );
}
$base = $this->getZonePath( $zone );
if ( !$base ) {
throw new MWException( __METHOD__.": invalid zone: $zone" );
}
- return $base . '/' . urldecode( $rel );
+ return $base . '/' . rawurldecode( $rel );
}
/**
* Store a file to a given destination.
*/
function store( $srcPath, $dstZone, $dstRel, $flags = 0 ) {
+ if ( !is_writable( $this->directory ) ) {
+ return new WikiErrorMsg( 'upload_directory_read_only', wfEscapeWikiText( $this->directory ) );
+ }
$root = $this->getZonePath( $dstZone );
if ( !$root ) {
throw new MWException( "Invalid zone: $dstZone" );
if ( !is_dir( dirname( $dstPath ) ) ) {
wfMkdirParents( dirname( $dstPath ) );
}
-
- if ( substr( $srcPath, 0, 9 ) == 'mwrepo://' ) {
+
+ if ( self::isVirtualUrl( $srcPath ) ) {
$srcPath = $this->resolveVirtualUrl( $srcPath );
}
* @return boolean True on success, false on failure
*/
function freeTemp( $virtualUrl ) {
- $temp = 'mwrepo:///temp';
+ $temp = "mwrepo://{$this->name}/temp";
if ( substr( $virtualUrl, 0, strlen( $temp ) ) != $temp ) {
wfDebug( __METHOD__.": Invalid virtual URL\n" );
return false;
* virtual URL, into this repository at the specified destination location.
*
* @param string $srcPath The source path or URL
- * @param string $dstPath The destination relative path
- * @param string $archivePath The relative path where the existing file is to
- * be archived, if there is one.
- * @param integer $flags Bitfield, may be FSRepo::DELETE_SOURCE to indicate
+ * @param string $dstRel The destination relative path
+ * @param string $archiveRel The relative path where the existing file is to
+ * be archived, if there is one. Relative to the public zone root.
+ * @param integer $flags Bitfield, may be FileRepo::DELETE_SOURCE to indicate
* that the source file should be deleted if possible
*/
- function publish( $srcPath, $dstPath, $archivePath, $flags = 0 ) {
+ function publish( $srcPath, $dstRel, $archiveRel, $flags = 0 ) {
+ if ( !is_writable( $this->directory ) ) {
+ return new WikiErrorMsg( 'upload_directory_read_only', wfEscapeWikiText( $this->directory ) );
+ }
if ( substr( $srcPath, 0, 9 ) == 'mwrepo://' ) {
$srcPath = $this->resolveVirtualUrl( $srcPath );
}
+ if ( !$this->validateFilename( $dstRel ) ) {
+ throw new MWException( 'Validation error in $dstRel' );
+ }
+ if ( !$this->validateFilename( $archiveRel ) ) {
+ throw new MWException( 'Validation error in $archiveRel' );
+ }
+ $dstPath = "{$this->directory}/$dstRel";
+ $archivePath = "{$this->directory}/$archiveRel";
+
$dstDir = dirname( $dstPath );
if ( !is_dir( $dstDir ) ) wfMkdirParents( $dstDir );
* If the repo is not hashed, returns an empty string
*/
function getHashPath( $name ) {
- if ( $this->isHashed() ) {
- $hash = md5( $name );
- $path = '';
- for ( $i = 1; $i <= $this->hashLevels; $i++ ) {
- $path .= substr( $hash, 0, $i ) . '/';
- }
- return $path;
- } else {
- return '';
- }
- }
-
- /**
- * Get the name of this repository, as specified by $info['name]' to the constructor
- */
- function getName() {
- return $this->name;
- }
-
- /**
- * Get the file description page base URL, or false if there isn't one.
- * @private
- */
- function getDescBaseUrl() {
- if ( is_null( $this->descBaseUrl ) ) {
- if ( !is_null( $this->articleUrl ) ) {
- $this->descBaseUrl = str_replace( '$1',
- urlencode( Namespace::getCanonicalName( NS_IMAGE ) ) . ':', $this->articleUrl );
- } elseif ( !is_null( $this->scriptDirUrl ) ) {
- $this->descBaseUrl = $this->scriptDirUrl . '/index.php?title=' .
- urlencode( Namespace::getCanonicalName( NS_IMAGE ) ) . ':';
- } else {
- $this->descBaseUrl = false;
- }
- }
- return $this->descBaseUrl;
- }
-
- /**
- * Get the URL of an image description page. May return false if it is
- * unknown or not applicable. In general this should only be called by the
- * File class, since it may return invalid results for certain kinds of
- * repositories. Use File::getDescriptionUrl() in user code.
- *
- * In particular, it uses the article paths as specified to the repository
- * constructor, whereas local repositories use the local Title functions.
- */
- function getDescriptionUrl( $name ) {
- $base = $this->getDescBaseUrl();
- if ( $base ) {
- return $base . wfUrlencode( $name );
- } else {
- return false;
- }
- }
-
- /**
- * Get the URL of the content-only fragment of the description page. For
- * MediaWiki this means action=render. This should only be called by the
- * repository's file class, since it may return invalid results. User code
- * should use File::getDescriptionText().
- */
- function getDescriptionRenderUrl( $name ) {
- if ( isset( $this->scriptDirUrl ) ) {
- return $this->scriptDirUrl . '/index.php?title=' .
- wfUrlencode( Namespace::getCanonicalName( NS_IMAGE ) . ':' . $name ) .
- '&action=render';
- } else {
- $descBase = $this->getDescBaseUrl();
- if ( $descBase ) {
- return wfAppendQuery( $descBase . wfUrlencode( $name ), 'action=render' );
- } else {
- return false;
- }
- }
+ return FileRepo::getHashPathForLevel( $name, $this->hashLevels );
}
/**
}
/**
- * Call a callaback function for every file in the repository
+ * Call a callback function for every file in the repository
* May use either the database or the filesystem
*/
function enumFiles( $callback ) {
}
/**
- * Get the name of an image from its title object
+ * Get properties of a file with a given virtual URL
+ * The virtual URL must refer to this repo
*/
- function getNameFromTitle( $title ) {
- global $wgCapitalLinks;
- if ( $this->initialCapital != $wgCapitalLinks ) {
- global $wgContLang;
- $name = $title->getUserCaseDBKey();
- if ( $this->initialCapital ) {
- $name = $wgContLang->ucfirst( $name );
- }
- } else {
- $name = $title->getDBkey();
- }
- return $name;
+ function getFileProps( $virtualUrl ) {
+ $path = $this->resolveVirtualUrl( $virtualUrl );
+ return File::getPropsFromPath( $path );
}
}
break;
}
- wfDebug( "Doing stat for $thumbPath\n" );
+ wfDebug( __METHOD__.": Doing stat for $thumbPath\n" );
$this->migrateThumbFile( $thumbName );
if ( file_exists( $thumbPath ) ) {
$thumb = $this->handler->getTransform( $this, $thumbPath, $thumbUrl, $params );
* Get urlencoded relative path of the file
*/
function getUrlRel() {
- return $this->getHashPath() . urlencode( $this->getName() );
+ return $this->getHashPath() . rawurlencode( $this->getName() );
}
/** Get the path of the archive directory, or a particular file if $suffix is specified */
if ( $suffix === false ) {
$path = substr( $path, 0, -1 );
} else {
- $path .= urlencode( $suffix );
+ $path .= rawurlencode( $suffix );
}
return $path;
}
function getThumbUrl( $suffix = false ) {
$path = $this->repo->getZoneUrl('public') . '/thumb/' . $this->getUrlRel();
if ( $suffix !== false ) {
- $path .= '/' . urlencode( $suffix );
+ $path .= '/' . rawurlencode( $suffix );
}
return $path;
}
if ( $suffix === false ) {
$path = substr( $path, 0, -1 );
} else {
- $path .= urlencode( $suffix );
+ $path .= rawurlencode( $suffix );
}
return $path;
}
function getThumbVirtualUrl( $suffix = false ) {
$path = $this->repo->getVirtualUrl() . '/public/thumb/' . $this->getUrlRel();
if ( $suffix !== false ) {
- $path .= '/' . urlencode( $suffix );
+ $path .= '/' . rawurlencode( $suffix );
}
return $path;
}
+ /** Get the virtual URL for the file itself */
+ function getVirtualUrl( $suffix = false ) {
+ $path = $this->repo->getVirtualUrl() . '/public/' . $this->getUrlRel();
+ if ( $suffix !== false ) {
+ $path .= '/' . rawurlencode( $suffix );
+ }
+ return $path;
+ }
+
/**
* @return bool
*/
function userCan( $field ) {
return true;
}
+
+ /**
+ * Get an associative array containing information about a file in the local filesystem
+ */
+ static function getPropsFromPath( $path ) {
+ wfProfileIn( __METHOD__ );
+ wfDebug( __METHOD__.": Getting file info for $path\n" );
+ $info = array( 'fileExists' => file_exists( $path ) );
+ $gis = false;
+ if ( $info['fileExists'] ) {
+ $magic = MimeMagic::singleton();
+
+ $info['mime'] = $magic->guessMimeType( $path, true );
+ list( $info['major_mime'], $info['minor_mime'] ) = self::splitMime( $info['mime'] );
+ $info['media_type'] = $magic->getMediaType( $path, $info['mime'] );
+
+ # Get size in bytes
+ $info['size'] = filesize( $path );
+
+ # Height, width and metadata
+ $handler = MediaHandler::getHandler( $info['mime'] );
+ if ( $handler ) {
+ $tempImage = (object)array();
+ $gis = $handler->getImageSize( $tempImage, $path );
+ $info['metadata'] = $handler->getMetadata( $tempImage, $path );
+ } else {
+ $gis = false;
+ $info['metadata'] = '';
+ }
+
+ wfDebug(__METHOD__.": $path loaded, {$info['size']} bytes, {$info['mime']}.\n");
+ } else {
+ $info['mime'] = NULL;
+ $info['media_type'] = MEDIATYPE_UNKNOWN;
+ $info['metadata'] = '';
+ wfDebug(__METHOD__.": $path NOT FOUND!\n");
+ }
+ if( $gis ) {
+ # NOTE: $gis[2] contains a code for the image type. This is no longer used.
+ $info['width'] = $gis[0];
+ $info['height'] = $gis[1];
+ if ( isset( $gis['bits'] ) ) {
+ $info['bits'] = $gis['bits'];
+ } else {
+ $info['bits'] = 0;
+ }
+ } else {
+ $info['width'] = 0;
+ $info['height'] = 0;
+ }
+ wfProfileOut( __METHOD__ );
+ return $info;
+ }
}
?>
--- /dev/null
+<?php
+
+/**
+ * Base class for file repositories
+ * Do not instantiate, use a derived class.
+ */
+abstract class FileRepo {
+ const DELETE_SOURCE = 1;
+
+ var $thumbScriptUrl, $transformVia404;
+ var $descBaseUrl, $scriptDirUrl, $articleUrl, $fetchDescription, $initialCapital;
+
+ /**
+ * Factory functions for creating new files
+ * Override these in the base class
+ */
+ var $fileFactory = false, $oldFileFactory = false;
+
+ function __construct( $info ) {
+ // Required settings
+ $this->name = $info['name'];
+
+ // Optional settings
+ $this->initialCapital = true; // by default
+ foreach ( array( 'descBaseUrl', 'scriptDirUrl', 'articleUrl', 'fetchDescription',
+ 'thumbScriptUrl', 'initialCapital' ) as $var )
+ {
+ if ( isset( $info[$var] ) ) {
+ $this->$var = $info[$var];
+ }
+ }
+ $this->transformVia404 = !empty( $info['transformVia404'] );
+ }
+
+ /**
+ * Determine if a string is an mwrepo:// URL
+ */
+ static function isVirtualUrl( $url ) {
+ return substr( $url, 0, 9 ) == 'mwrepo://';
+ }
+
+ /**
+ * Create a new File object from the local repository
+ * @param mixed $title Title object or string
+ * @param mixed $time Time at which the image is supposed to have existed.
+ * If this is specified, the returned object will be an
+ * instance of the repository's old file class instead of
+ * a current file. Repositories not supporting version
+ * control should return false if this parameter is set.
+ */
+ function newFile( $title, $time = false ) {
+ if ( !($title instanceof Title) ) {
+ $title = Title::makeTitleSafe( NS_IMAGE, $title );
+ if ( !is_object( $title ) ) {
+ return null;
+ }
+ }
+ if ( $time ) {
+ if ( $this->oldFileFactory ) {
+ return call_user_func( $this->oldFileFactory, $title, $this, $time );
+ } else {
+ return false;
+ }
+ } else {
+ return call_user_func( $this->fileFactory, $title, $this );
+ }
+ }
+
+ /**
+ * Find an instance of the named file that existed at the specified time
+ * Returns false if the file did not exist. Repositories not supporting
+ * version control should return false if the time is specified.
+ *
+ * @param mixed $time 14-character timestamp, or false for the current version
+ */
+ function findFile( $title, $time = false ) {
+ # First try the current version of the file to see if it precedes the timestamp
+ $img = $this->newFile( $title );
+ if ( !$img ) {
+ return false;
+ }
+ if ( $img->exists() && ( !$time || $img->getTimestamp() <= $time ) ) {
+ return $img;
+ }
+ # Now try an old version of the file
+ $img = $this->newFile( $title, $time );
+ if ( $img->exists() ) {
+ return $img;
+ }
+ }
+
+ /**
+ * Get the URL of thumb.php
+ */
+ function getThumbScriptUrl() {
+ return $this->thumbScriptUrl;
+ }
+
+ /**
+ * Returns true if the repository can transform files via a 404 handler
+ */
+ function canTransformVia404() {
+ return $this->transformVia404;
+ }
+
+ /**
+ * Get the name of an image from its title object
+ */
+ function getNameFromTitle( $title ) {
+ global $wgCapitalLinks;
+ if ( $this->initialCapital != $wgCapitalLinks ) {
+ global $wgContLang;
+ $name = $title->getUserCaseDBKey();
+ if ( $this->initialCapital ) {
+ $name = $wgContLang->ucfirst( $name );
+ }
+ } else {
+ $name = $title->getDBkey();
+ }
+ return $name;
+ }
+
+ static function getHashPathForLevel( $name, $levels ) {
+ if ( $levels == 0 ) {
+ return '';
+ } else {
+ $hash = md5( $name );
+ $path = '';
+ for ( $i = 1; $i <= $levels; $i++ ) {
+ $path .= substr( $hash, 0, $i ) . '/';
+ }
+ return $path;
+ }
+ }
+
+ /**
+ * Get the name of this repository, as specified by $info['name]' to the constructor
+ */
+ function getName() {
+ return $this->name;
+ }
+
+ /**
+ * Get the file description page base URL, or false if there isn't one.
+ * @private
+ */
+ function getDescBaseUrl() {
+ if ( is_null( $this->descBaseUrl ) ) {
+ if ( !is_null( $this->articleUrl ) ) {
+ $this->descBaseUrl = str_replace( '$1',
+ wfUrlencode( Namespace::getCanonicalName( NS_IMAGE ) ) . ':', $this->articleUrl );
+ } elseif ( !is_null( $this->scriptDirUrl ) ) {
+ $this->descBaseUrl = $this->scriptDirUrl . '/index.php?title=' .
+ wfUrlencode( Namespace::getCanonicalName( NS_IMAGE ) ) . ':';
+ } else {
+ $this->descBaseUrl = false;
+ }
+ }
+ return $this->descBaseUrl;
+ }
+
+ /**
+ * Get the URL of an image description page. May return false if it is
+ * unknown or not applicable. In general this should only be called by the
+ * File class, since it may return invalid results for certain kinds of
+ * repositories. Use File::getDescriptionUrl() in user code.
+ *
+ * In particular, it uses the article paths as specified to the repository
+ * constructor, whereas local repositories use the local Title functions.
+ */
+ function getDescriptionUrl( $name ) {
+ $base = $this->getDescBaseUrl();
+ if ( $base ) {
+ return $base . wfUrlencode( $name );
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get the URL of the content-only fragment of the description page. For
+ * MediaWiki this means action=render. This should only be called by the
+ * repository's file class, since it may return invalid results. User code
+ * should use File::getDescriptionText().
+ */
+ function getDescriptionRenderUrl( $name ) {
+ if ( isset( $this->scriptDirUrl ) ) {
+ return $this->scriptDirUrl . '/index.php?title=' .
+ wfUrlencode( Namespace::getCanonicalName( NS_IMAGE ) . ':' . $name ) .
+ '&action=render';
+ } else {
+ $descBase = $this->getDescBaseUrl();
+ if ( $descBase ) {
+ return wfAppendQuery( $descBase . wfUrlencode( $name ), 'action=render' );
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Store a file to a given destination.
+ */
+ abstract function store( $srcPath, $dstZone, $dstRel, $flags = 0 );
+
+ /**
+ * Pick a random name in the temp zone and store a file to it.
+ * Returns the URL, or a WikiError on failure.
+ * @param string $originalName The base name of the file as specified
+ * by the user. The file extension will be maintained.
+ * @param string $srcPath The current location of the file.
+ */
+ abstract function storeTemp( $originalName, $srcPath );
+
+ /**
+ * Remove a temporary file or mark it for garbage collection
+ * @param string $virtualUrl The virtual URL returned by storeTemp
+ * @return boolean True on success, false on failure
+ * STUB
+ */
+ function freeTemp( $virtualUrl ) {
+ return true;
+ }
+
+ /**
+ * Copy or move a file either from the local filesystem or from an mwrepo://
+ * virtual URL, into this repository at the specified destination location.
+ *
+ * @param string $srcPath The source path or URL
+ * @param string $dstRel The destination relative path
+ * @param string $archiveRel The relative path where the existing file is to
+ * be archived, if there is one. Relative to the public zone root.
+ * @param integer $flags Bitfield, may be FileRepo::DELETE_SOURCE to indicate
+ * that the source file should be deleted if possible
+ */
+ abstract function publish( $srcPath, $dstRel, $archiveRel, $flags = 0 );
+
+ /**
+ * Get properties of a file with a given virtual URL
+ * The virtual URL must refer to this repo
+ * Properties should ultimately be obtained via File::getPropsFromPath()
+ */
+ abstract function getFileProps( $virtualUrl );
+
+ /**
+ * Call a callback function for every file in the repository
+ * May use either the database or the filesystem
+ * STUB
+ */
+ function enumFiles( $callback ) {
+ throw new MWException( 'enumFiles is not supported by ' . get_class( $this ) );
+ }
+
+ /**
+ * Determine if a relative path is valid, i.e. not blank or involving directory traveral
+ */
+ function validateFilename( $filename ) {
+ if ( strval( $filename ) == '' ) {
+ return false;
+ }
+ if ( wfIsWindows() ) {
+ $filename = strtr( $filename, '\\', '/' );
+ }
+ /**
+ * Use the same traversal protection as Title::secureAndSplit()
+ */
+ if ( strpos( $filename, '.' ) !== false &&
+ ( $filename === '.' || $filename === '..' ||
+ strpos( $filename, './' ) === 0 ||
+ strpos( $filename, '../' ) === 0 ||
+ strpos( $filename, '/./' ) !== false ||
+ strpos( $filename, '/../' ) !== false ) )
+ {
+ return false;
+ } else {
+ return true;
+ }
+ }
+}
+?>
return $this->hasSharedCache;
}
- function store( /*...*/ ) {
+ function store( $srcPath, $dstZone, $dstRel, $flags = 0 ) {
throw new MWException( get_class($this) . ': write operations are not supported' );
}
}
* Load metadata from the file itself
*/
function loadFromFile() {
- wfProfileIn( __METHOD__ );
- $path = $this->getPath();
- $this->fileExists = file_exists( $path );
- $gis = array();
-
- if ( $this->fileExists ) {
- $magic=& MimeMagic::singleton();
-
- $this->mime = $magic->guessMimeType($path,true);
- list( $this->major_mime, $this->minor_mime ) = self::splitMime( $this->mime );
- $this->media_type = $magic->getMediaType($path,$this->mime);
- $handler = MediaHandler::getHandler( $this->mime );
-
- # Get size in bytes
- $this->size = filesize( $path );
-
- # Height, width and metadata
- if ( $handler ) {
- $gis = $handler->getImageSize( $this, $path );
- $this->metadata = $handler->getMetadata( $this, $path );
- } else {
- $gis = false;
- $this->metadata = '';
- }
-
- wfDebug(__METHOD__.": $path loaded, {$this->size} bytes, {$this->mime}.\n");
- } else {
- $this->mime = NULL;
- $this->media_type = MEDIATYPE_UNKNOWN;
- $this->metadata = '';
- wfDebug(__METHOD__.": $path NOT FOUND!\n");
- }
-
- if( $gis ) {
- $this->width = $gis[0];
- $this->height = $gis[1];
- } else {
- $this->width = 0;
- $this->height = 0;
- }
-
- #NOTE: $gis[2] contains a code for the image type. This is no longer used.
-
- #NOTE: we have to set this flag early to avoid load() to be called
- # be some of the functions below. This may lead to recursion or other bad things!
- # as ther's only one thread of execution, this should be safe anyway.
- $this->dataLoaded = true;
-
- if ( isset( $gis['bits'] ) ) $this->bits = $gis['bits'];
- else $this->bits = 0;
-
- wfProfileOut( __METHOD__ );
+ $this->setProps( self::getInfoFromPath( $this->getPath() ) );
}
function getCacheFields( $prefix = 'img_' ) {
wfProfileOut( __METHOD__ );
}
+ function setProps( $info ) {
+ $this->dataLoaded = true;
+ $fields = $this->getCacheFields( '' );
+ $fields[] = 'fileExists';
+ foreach ( $fields as $field ) {
+ if ( isset( $info[$field] ) ) {
+ $this->$field = $info[$field];
+ }
+ }
+ }
+
/** splitMime inherited */
/** getName inherited */
/** getTitle inherited */
* Refresh metadata in memcached, but don't touch thumbnails or squid
*/
function purgeMetadataCache() {
- clearstatcache();
- $this->loadFromFile();
+ $this->loadFromDB();
$this->saveToCache();
}
/**
* Delete all previously generated thumbnails, refresh metadata in memcached and purge the squid
*/
- function purgeCache( $archiveFiles = array() ) {
- global $wgUseSquid;
-
+ function purgeCache() {
// Refresh metadata cache
$this->purgeMetadataCache();
+ // Delete thumbnails
+ $this->purgeThumbnails();
+
+ // Purge squid cache for this file
+ wfPurgeSquidServers( array( $this->getURL() ) );
+ }
+
+ /**
+ * Delete cached transformed files
+ */
+ function purgeThumbnails() {
+ global $wgUseSquid;
// Delete thumbnails
$files = $this->getThumbnails();
$dir = $this->getThumbPath();
// Purge the squid
if ( $wgUseSquid ) {
- $urls[] = $this->getURL();
- foreach ( $archiveFiles as $file ) {
- $urls[] = $this->getArchiveUrl( $file );
- }
wfPurgeSquidServers( $urls );
}
}
/** getThumbVirtualUrl inherited */
/** isHashed inherited */
+ /**
+ * Upload a file and record it in the DB
+ * @param string $srcPath Source path or virtual URL
+ * @param string $comment Upload description
+ * @param string $pageText Text to use for the new description page, if a new description page is created
+ * @param integer $flags Flags for publish()
+ * @param array $props File properties, if known. This can be used to reduce the
+ * upload time when uploading virtual URLs for which the file info
+ * is already known
+ * @param string $timestamp Timestamp for img_timestamp, or false to use the current time
+ *
+ * @return Wikitext-formatted WikiError or true on success
+ */
+ function upload( $srcPath, $comment, $pageText, $flags = 0, $props = false, $timestamp = false ) {
+ $archive = $this->publish( $srcPath, $flags );
+ if ( WikiError::isError( $archive ) ){
+ return $archive;
+ }
+ if ( !$this->recordUpload2( $archive, $comment, $pageText, $props, $timestamp ) ) {
+ return new WikiErrorMsg( 'filenotfound', wfEscapeWikiText( $srcPath ) );
+ }
+ return true;
+ }
+
/**
* Record a file upload in the upload log and the image table
+ * @deprecated use upload()
*/
function recordUpload( $oldver, $desc, $license = '', $copyStatus = '', $source = '',
$watch = false, $timestamp = false )
{
- global $wgUser, $wgUseCopyrightUpload;
+ $pageText = UploadForm::getInitialPageText( $desc, $license, $copyStatus, $source );
+ if ( !$this->recordUpload2( $oldver, $desc, $pageText ) ) {
+ return false;
+ }
+ if ( $watch ) {
+ global $wgUser;
+ $wgUser->addWatch( $this->getTitle() );
+ }
+ return true;
+
+ }
+
+ /**
+ * Record a file upload in the upload log and the image table
+ */
+ function recordUpload2( $oldver, $comment, $pageText, $props = false, $timestamp = false )
+ {
+ global $wgUser;
$dbw = $this->repo->getMasterDB();
+ if ( !$props ) {
+ $props = $this->repo->getFileProps( $this->getVirtualUrl() );
+ }
+ $this->setProps( $props );
+
// Delete thumbnails and refresh the metadata cache
- $this->purgeCache();
+ $this->purgeThumbnails();
+ $this->saveToCache();
+ wfPurgeSquidServers( array( $this->getURL() ) );
// Fail now if the file isn't there
if ( !$this->fileExists ) {
return false;
}
- if ( $wgUseCopyrightUpload ) {
- if ( $license != '' ) {
- $licensetxt = '== ' . wfMsgForContent( 'license' ) . " ==\n" . '{{' . $license . '}}' . "\n";
- }
- $textdesc = '== ' . wfMsg ( 'filedesc' ) . " ==\n" . $desc . "\n" .
- '== ' . wfMsgForContent ( 'filestatus' ) . " ==\n" . $copyStatus . "\n" .
- "$licensetxt" .
- '== ' . wfMsgForContent ( 'filesource' ) . " ==\n" . $source ;
- } else {
- if ( $license != '' ) {
- $filedesc = $desc == '' ? '' : '== ' . wfMsg ( 'filedesc' ) . " ==\n" . $desc . "\n";
- $textdesc = $filedesc .
- '== ' . wfMsgForContent ( 'license' ) . " ==\n" . '{{' . $license . '}}' . "\n";
- } else {
- $textdesc = $desc;
- }
- }
-
if ( $timestamp === false ) {
$timestamp = $dbw->timestamp();
}
- #split mime type
- if (strpos($this->mime,'/')!==false) {
- list($major,$minor)= explode('/',$this->mime,2);
- }
- else {
- $major= $this->mime;
- $minor= "unknown";
- }
-
# Test to see if the row exists using INSERT IGNORE
# This avoids race conditions by locking the row until the commit, and also
# doesn't deadlock. SELECT FOR UPDATE causes a deadlock for every race condition.
'img_height' => intval( $this->height ),
'img_bits' => $this->bits,
'img_media_type' => $this->media_type,
- 'img_major_mime' => $major,
- 'img_minor_mime' => $minor,
+ 'img_major_mime' => $this->major_mime,
+ 'img_minor_mime' => $this->minor_mime,
'img_timestamp' => $timestamp,
- 'img_description' => $desc,
+ 'img_description' => $comment,
'img_user' => $wgUser->getID(),
'img_user_text' => $wgUser->getName(),
'img_metadata' => $this->metadata,
'img_height' => intval( $this->height ),
'img_bits' => $this->bits,
'img_media_type' => $this->media_type,
- 'img_major_mime' => $major,
- 'img_minor_mime' => $minor,
+ 'img_major_mime' => $this->major_mime,
+ 'img_minor_mime' => $this->minor_mime,
'img_timestamp' => $timestamp,
- 'img_description' => $desc,
+ 'img_description' => $comment,
'img_user' => $wgUser->getID(),
'img_user_text' => $wgUser->getName(),
'img_metadata' => $this->metadata,
$descTitle = $this->getTitle();
$article = new Article( $descTitle );
- $minor = false;
- $watch = $watch || $wgUser->isWatched( $descTitle );
- $suppressRC = true; // There's already a log entry, so don't double the RC load
+
+ # Add the log entry
+ $log = new LogPage( 'upload' );
+ $log->addEntry( 'upload', $descTitle, $comment );
if( $descTitle->exists() ) {
- // TODO: insert a null revision into the page history for this update.
- if( $watch ) {
- $wgUser->addWatch( $descTitle );
- }
+ # Create a null revision
+ $nullRevision = Revision::newNullRevision( $dbw, $descTitle->getArticleId(), $log->getRcComment(), false );
+ $nullRevision->insertOn( $dbw );
# Invalidate the cache for the description page
$descTitle->invalidateCache();
$descTitle->purgeSquid();
} else {
// New file; create the description page.
- $article->insertNewArticle( $textdesc, $desc, $minor, $watch, $suppressRC );
+ // There's already a log entry, so don't make a second RC entry
+ $article->doEdit( $pageText, $comment, EDIT_NEW | EDIT_SUPPRESS_RC );
}
# Hooks, hooks, the magic of hooks...
wfRunHooks( 'FileUpload', array( $this ) );
- # Add the log entry
- $log = new LogPage( 'upload' );
- $log->addEntry( 'upload', $descTitle, $desc );
-
# Commit the transaction now, in case something goes wrong later
# The most important thing is that files don't get lost, especially archives
$dbw->immediateCommit();
* file, and a wikitext-formatted WikiError object on failure.
*/
function publish( $srcPath, $flags = 0 ) {
- $dstPath = $this->getFullPath();
+ $dstRel = $this->getRel();
$archiveName = gmdate( 'YmdHis' ) . '!'. $this->getName();
- $archivePath = $this->getArchivePath( $archiveName );
+ $archiveRel = 'archive/' . $this->getHashPath() . $archiveName;
$flags = $flags & File::DELETE_SOURCE ? LocalRepo::DELETE_SOURCE : 0;
- $status = $this->repo->publish( $srcPath, $dstPath, $archivePath, $flags );
+ $status = $this->repo->publish( $srcPath, $dstRel, $archiveRel, $flags );
if ( WikiError::isError( $status ) ) {
return $status;
} elseif ( $status == 'new' ) {
$class = $info['class'];
return new $class( $info );
}
+
+ /**
+ * Split a virtual URL into repo, zone and rel parts
+ * @return an array containing repo, zone and rel
+ */
+ function splitVirtualUrl( $url ) {
+ if ( substr( $url, 0, 9 ) != 'mwrepo://' ) {
+ throw new MWException( __METHOD__.': unknown protoocl' );
+ }
+
+ $bits = explode( '/', substr( $url, 9 ), 3 );
+ if ( count( $bits ) != 3 ) {
+ throw new MWException( __METHOD__.": invalid mwrepo URL: $url" );
+ }
+ return $bits;
+ }
+
+ function getFileProps( $fileName ) {
+ if ( FileRepo::isVirtualUrl( $fileName ) ) {
+ list( $repoName, $zone, $rel ) = $this->splitVirtualUrl( $fileName );
+ if ( $repoName === '' ) {
+ $repoName = 'local';
+ }
+ $repo = $this->getRepo( $repoName );
+ return $repo->getFileProps( $fileName );
+ } else {
+ return File::getPropsFromPath( $fileName );
+ }
+ }
}
?>
function getURL() {
if ( $this->repo ) {
- return $this->repo->getZoneUrl( 'public' ) . $this->repo->getHashPath( $this->name ) . urlencode( $this->name );
+ return $this->repo->getZoneUrl( 'public' ) . '/' . $this->repo->getHashPath( $this->name ) . urlencode( $this->name );
} else {
return false;
}
}
function testGetArchiveVirtualUrl() {
- $this->assertEquals( 'mwrepo:///public/archive', $this->file_hl0->getArchiveVirtualUrl() );
- $this->assertEquals( 'mwrepo:///public/archive/a/a2', $this->file_hl2->getArchiveVirtualUrl() );
- $this->assertEquals( 'mwrepo:///public/archive/%21', $this->file_hl0->getArchiveVirtualUrl( '!' ) );
- $this->assertEquals( 'mwrepo:///public/archive/a/a2/%21', $this->file_hl2->getArchiveVirtualUrl( '!' ) );
+ $this->assertEquals( 'mwrepo://test/public/archive', $this->file_hl0->getArchiveVirtualUrl() );
+ $this->assertEquals( 'mwrepo://test/public/archive/a/a2', $this->file_hl2->getArchiveVirtualUrl() );
+ $this->assertEquals( 'mwrepo://test/public/archive/%21', $this->file_hl0->getArchiveVirtualUrl( '!' ) );
+ $this->assertEquals( 'mwrepo://test/public/archive/a/a2/%21', $this->file_hl2->getArchiveVirtualUrl( '!' ) );
}
function testGetThumbVirtualUrl() {
- $this->assertEquals( 'mwrepo:///public/thumb/Test%21', $this->file_hl0->getThumbVirtualUrl() );
- $this->assertEquals( 'mwrepo:///public/thumb/a/a2/Test%21', $this->file_hl2->getThumbVirtualUrl() );
- $this->assertEquals( 'mwrepo:///public/thumb/Test%21/%21', $this->file_hl0->getThumbVirtualUrl( '!' ) );
- $this->assertEquals( 'mwrepo:///public/thumb/a/a2/Test%21/%21', $this->file_hl2->getThumbVirtualUrl( '!' ) );
+ $this->assertEquals( 'mwrepo://test/public/thumb/Test%21', $this->file_hl0->getThumbVirtualUrl() );
+ $this->assertEquals( 'mwrepo://test/public/thumb/a/a2/Test%21', $this->file_hl2->getThumbVirtualUrl() );
+ $this->assertEquals( 'mwrepo://test/public/thumb/Test%21/%21', $this->file_hl0->getThumbVirtualUrl( '!' ) );
+ $this->assertEquals( 'mwrepo://test/public/thumb/a/a2/Test%21/%21', $this->file_hl2->getThumbVirtualUrl( '!' ) );
}
function testGetUrl() {