wiki.techinc.nl/includes/Image.php
Brion Vibber 41e752879c Special:Log and the logging table -- unified logging scariness!
Replaces the ugly, often breaking, manually archived log pages with
a nice clean table which can be sorted, trimmed, viewed in pieces, etc.
You can see all logged actions by some user, or affecting some page,
and can combine the views of all all logs in one.

There are probably still some broken things in here, but I want this
committed before the patch gets any bigger.

recentchanges table is altered to make rc_namespace signed so Special:
links can be listed in it.
2004-08-24 08:11:46 +00:00

460 lines
12 KiB
PHP

<?php
# Class to represent an image
# Provides methods to retrieve paths (physical, logical, URL),
# to generate thumbnails or for uploading.
class Image
{
/* private */
var $name, # name of the image
$imagePath, # Path of the image
$url, # Image URL
$title, # Title object for this image. Initialized when needed.
$fileExists, # does the image file exist on disk?
$historyLine, # Number of line to return by nextHistoryLine()
$historyRes, # result of the query for the image's history
$width, # \
$height, # |
$bits, # --- returned by getimagesize, see http://de3.php.net/manual/en/function.getimagesize.php
$type, # |
$attr; # /
function Image( $name )
{
global $wgUploadDirectory;
$this->name = $name;
$this->title = Title::makeTitleSafe( NS_IMAGE, $this->name );
//$this->imagePath = wfImagePath( $name );
$hash = md5( $this->title->getDBkey() );
$this->imagePath = $wgUploadDirectory . '/' . $hash{0} . '/' .substr( $hash, 0, 2 ) . "/{$name}";
$this->url = $this->wfImageUrl( $name );
if ( $this->fileExists = file_exists( $this->imagePath ) ) // Sic!, "=" is intended
{
@$gis = getimagesize( $this->imagePath );
if( $gis !== false ) {
$this->width = $gis[0];
$this->height = $gis[1];
$this->type = $gis[2];
$this->attr = $gis[3];
if ( isset( $gis['bits'] ) ) {
$this->bits = $gis['bits'];
} else {
$this->bits = 0;
}
}
}
$this->historyLine = 0;
}
function newFromTitle( $nt )
{
$img = new Image( $nt->getDBKey() );
$img->title = $nt;
return $img;
}
function getName()
{
return $this->name;
}
function getURL()
{
return $this->url;
}
function getImagePath()
{
return $this->imagePath;
}
function getWidth()
{
return $this->width;
}
function getHeight()
{
return $this->height;
}
function getType()
{
return $this->type;
}
function getEscapeLocalURL()
{
return $this->title->escapeLocalURL();
}
function wfImageUrl( $name )
{
global $wgUploadPath;
$hash = md5( $name );
$url = "{$wgUploadPath}/" . $hash{0} . "/" .
substr( $hash, 0, 2 ) . "/{$name}";
return wfUrlencode( $url );
}
function exists()
{
return $this->fileExists;
}
function thumbUrl( $width, $subdir='thumb' ) {
global $wgUploadPath;
$name = $this->thumbName( $width );
$hash = md5( $name );
$url = "{$wgUploadPath}/{$subdir}/" . $hash{0} . "/" . substr( $hash, 0, 2 ) . "/{$name}";
return wfUrlencode($url);
}
function thumbName( $width ) {
return $width."px-".$this->name;
}
//**********************************************************************
// Create a thumbnail of the image having the specified width.
// The thumbnail will not be created if the width is larger than the
// image's width. Let the browser do the scaling in this case.
// The thumbnail is stored on disk and is only computed if the thumbnail
// file does not exist OR if it is older than the image.
// Returns the URL.
function createThumb( $width ) {
global $wgUploadDirectory;
global $wgImageMagickConvertCommand;
global $wgUseImageMagick;
global $wgUseSquid, $wgInternalServer;
$width = IntVal( $width );
$thumbName = $this->thumbName( $width );
$thumbPath = wfImageThumbDir( $thumbName ).'/'.$thumbName;
$thumbUrl = $this->thumbUrl( $width );
if ( ! $this->exists() )
{
# If there is no image, there will be no thumbnail
return '';
}
# Sanity check $width
if( $width <= 0 ) {
# BZZZT
return '';
}
if( $width > $this->width ) {
# Don't make an image bigger than the source
return $this->getURL();
}
if ( (! file_exists( $thumbPath ) ) || ( filemtime($thumbPath) < filemtime($this->imagePath) ) ) {
if ( $wgUseImageMagick ) {
# use ImageMagick
# Specify white background color, will be used for transparent images
# in Internet Explorer/Windows instead of default black.
$cmd = $wgImageMagickConvertCommand .
" -quality 85 -background white -geometry {$width} ".
escapeshellarg($this->imagePath) . " " .
escapeshellarg($thumbPath);
$conv = shell_exec( $cmd );
} else {
# Use PHP's builtin GD library functions.
#
# First find out what kind of file this is, and select the correct
# input routine for this.
$truecolor = false;
switch( $this->type ) {
case 1: # GIF
$src_image = imagecreatefromgif( $this->imagePath );
break;
case 2: # JPG
$src_image = imagecreatefromjpeg( $this->imagePath );
$truecolor = true;
break;
case 3: # PNG
$src_image = imagecreatefrompng( $this->imagePath );
$truecolor = ( $this->bits > 8 );
break;
case 15: # WBMP for WML
$src_image = imagecreatefromwbmp( $this->imagePath );
break;
case 16: # XBM
$src_image = imagecreatefromxbm( $this->imagePath );
break;
default:
return 'Image type not supported';
break;
}
$height = floor( $this->height * ( $width/$this->width ) );
if ( $truecolor ) {
$dst_image = imagecreatetruecolor( $width, $height );
} else {
$dst_image = imagecreate( $width, $height );
}
imagecopyresampled( $dst_image, $src_image,
0,0,0,0,
$width, $height, $this->width, $this->height );
switch( $this->type ) {
case 1: # GIF
case 3: # PNG
case 15: # WBMP
case 16: # XBM
#$thumbUrl .= ".png";
#$thumbPath .= ".png";
imagepng( $dst_image, $thumbPath );
break;
case 2: # JPEG
#$thumbUrl .= ".jpg";
#$thumbPath .= ".jpg";
imageinterlace( $dst_image );
imagejpeg( $dst_image, $thumbPath, 95 );
break;
default:
break;
}
imagedestroy( $dst_image );
imagedestroy( $src_image );
}
#
# Check for zero-sized thumbnails. Those can be generated when
# no disk space is available or some other error occurs
#
$thumbstat = stat( $thumbPath );
if( $thumbstat['size'] == 0 )
{
unlink( $thumbPath );
}
# Purge squid
# This has to be done after the image is updated and present for all machines on NFS,
# or else the old version might be stored into the squid again
if ( $wgUseSquid ) {
$urlArr = Array(
$wgInternalServer.$thumbUrl
);
wfPurgeSquidServers($urlArr);
}
}
return $thumbUrl;
} // END OF function createThumb
//**********************************************************************
// Return the image history of this image, line by line.
// starts with current version, then old versions.
// uses $this->historyLine to check which line to return:
// 0 return line for current version
// 1 query for old versions, return first one
// 2, ... return next old version from above query
function nextHistoryLine()
{
$fname = 'Image::nextHistoryLine()';
$dbr =& wfGetDB( DB_SLAVE );
if ( $this->historyLine == 0 ) {// called for the first time, return line from cur
$this->historyRes = $dbr->select( 'image',
array( 'img_size','img_description','img_user','img_user_text','img_timestamp', "'' AS oi_archive_name" ),
array( 'img_name' => $this->title->getDBkey() ),
$fname
);
if ( 0 == wfNumRows( $this->historyRes ) ) {
return FALSE;
}
} else if ( $this->historyLine == 1 ) {
$this->historyRes = $dbr->select( 'oldimage',
array( 'oi_size AS img_size', 'oi_description AS img_description', 'oi_user AS img_user',
'oi_user_text AS img_user_text', 'oi_timestamp AS img_timestamp', 'oi_archive_name'
), array( 'oi_name' => $this->title->getDBkey() ), $fname, array( 'ORDER BY' => 'oi_timestamp DESC' )
);
}
$this->historyLine ++;
return $dbr->fetchObject( $this->historyRes );
}
function resetHistory()
{
$this->historyLine = 0;
}
} //class
function wfImageDir( $fname )
{
global $wgUploadDirectory;
$hash = md5( $fname );
$oldumask = umask(0);
$dest = $wgUploadDirectory . '/' . $hash{0};
if ( ! is_dir( $dest ) ) { mkdir( $dest, 0777 ); }
$dest .= '/' . substr( $hash, 0, 2 );
if ( ! is_dir( $dest ) ) { mkdir( $dest, 0777 ); }
umask( $oldumask );
return $dest;
}
function wfImageThumbDir( $fname , $subdir='thumb')
{
return wfImageArchiveDir( $fname, $subdir );
}
function wfImageArchiveDir( $fname , $subdir='archive')
{
global $wgUploadDirectory;
$hash = md5( $fname );
$oldumask = umask(0);
# Suppress warning messages here; if the file itself can't
# be written we'll worry about it then.
$archive = $wgUploadDirectory.'/'.$subdir;
if ( ! is_dir( $archive ) ) { @mkdir( $archive, 0777 ); }
$archive .= '/' . $hash{0};
if ( ! is_dir( $archive ) ) { @mkdir( $archive, 0777 ); }
$archive .= '/' . substr( $hash, 0, 2 );
if ( ! is_dir( $archive ) ) { @mkdir( $archive, 0777 ); }
umask( $oldumask );
return $archive;
}
function wfRecordUpload( $name, $oldver, $size, $desc, $copyStatus = "", $source = "" )
{
global $wgUser, $wgLang, $wgTitle, $wgOut, $wgDeferredUpdateList;
global $wgUseCopyrightUpload;
$fname = 'wfRecordUpload';
$dbw =& wfGetDB( DB_MASTER );
# img_name must be unique
if ( !$dbw->indexUnique( 'image', 'img_name' ) ) {
wfDebugDieBacktrace( 'Database schema not up to date, please run maintenance/archives/patch-image_name_unique.sql' );
}
$now = wfTimestampNow();
$won = wfInvertTimestamp( $now );
$size = IntVal( $size );
if ( $wgUseCopyrightUpload )
{
$textdesc = '== ' . wfMsg ( 'filedesc' ) . " ==\n" . $desc . "\n" .
'== ' . wfMsg ( 'filestatus' ) . " ==\n" . $copyStatus . "\n" .
'== ' . wfMsg ( 'filesource' ) . " ==\n" . $source ;
}
else $textdesc = $desc ;
$now = wfTimestampNow();
$won = wfInvertTimestamp( $now );
# 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.
$dbw->insert( 'image',
array(
'img_name' => $name,
'img_size'=> $size,
'img_timestamp' => $now,
'img_description' => $desc,
'img_user' => $wgUser->getID(),
'img_user_text' => $wgUser->getName(),
), $fname, 'IGNORE'
);
$descTitle = Title::makeTitleSafe( NS_IMAGE, $name );
if ( $dbw->affectedRows() ) {
# Successfully inserted, this is a new image
$id = $descTitle->getArticleID();
if ( $id == 0 ) {
$seqVal = $dbw->nextSequenceValue( 'cur_cur_id_seq' );
$dbw->insertArray( 'cur',
array(
'cur_id' => $seqVal,
'cur_namespace' => NS_IMAGE,
'cur_title' => $name,
'cur_comment' => $desc,
'cur_user' => $wgUser->getID(),
'cur_user_text' => $wgUser->getName(),
'cur_timestamp' => $now,
'cur_is_new' => 1,
'cur_text' => $textdesc,
'inverse_timestamp' => $won,
'cur_touched' => $now
), $fname
);
$id = $dbw->insertId() or 0; # We should throw an error instead
RecentChange::notifyNew( $now, $descTitle, 0, $wgUser, $desc );
$u = new SearchUpdate( $id, $name, $desc );
$u->doUpdate();
}
} else {
# Collision, this is an update of an image
# Get current image row for update
$s = $dbw->getArray( 'image', array( 'img_name','img_size','img_timestamp','img_description',
'img_user','img_user_text' ), array( 'img_name' => $name ), $fname, 'FOR UPDATE' );
# Insert it into oldimage
$dbw->insertArray( 'oldimage',
array(
'oi_name' => $s->img_name,
'oi_archive_name' => $oldver,
'oi_size' => $s->img_size,
'oi_timestamp' => $s->img_timestamp,
'oi_description' => $s->img_description,
'oi_user' => $s->img_user,
'oi_user_text' => $s->img_user_text
), $fname
);
# Update the current image row
$dbw->updateArray( 'image',
array( /* SET */
'img_size' => $size,
'img_timestamp' => wfTimestampNow(),
'img_user' => $wgUser->getID(),
'img_user_text' => $wgUser->getName(),
'img_description' => $desc,
), array( /* WHERE */
'img_name' => $name
), $fname
);
# Invalidate the cache for the description page
$descTitle->invalidateCache();
}
$log = new LogPage( 'upload' );
$log->addEntry( 'upload', $descTitle, $desc );
}
function wfImageArchiveUrl( $name )
{
global $wgUploadPath;
$hash = md5( substr( $name, 15) );
$url = $wgUploadPath.'/archive/' . $hash{0} . '/' .
substr( $hash, 0, 2 ) . '/'.$name;
return wfUrlencode($url);
}
?>