From 8395a4e8e7393e70a065c51f74ffb230166a6968 Mon Sep 17 00:00:00 2001 From: Tim Starling Date: Sat, 16 Apr 2005 11:05:41 +0000 Subject: [PATCH] Fixed various bugs with new image code, changed thumbnail paths as per JeLuF's suggestion --- config/index.php | 2 +- includes/Image.php | 402 +++++++++++++++++++++++++--------------- includes/ImagePage.php | 27 +-- includes/StreamFile.php | 3 +- thumb.php | 15 +- 5 files changed, 267 insertions(+), 182 deletions(-) diff --git a/config/index.php b/config/index.php index 328f34fb78..3766d77045 100644 --- a/config/index.php +++ b/config/index.php @@ -181,7 +181,7 @@ class ConfigData { size = $commonsCachedValues['size']; $this->fromSharedDirectory = true; $this->dataLoaded = true; + $this->imagePath = $this->getFullPath(true); } } } @@ -122,6 +123,7 @@ class Image $this->size = $cachedValues['size']; $this->fromSharedDirectory = false; $this->dataLoaded = true; + $this->imagePath = $this->getFullPath(); } } @@ -163,6 +165,7 @@ class Image wfProfileIn( $fname ); $this->imagePath = $this->getFullPath(); $this->fileExists = file_exists( $this->imagePath ); + $this->fromSharedDirectory = false; $gis = false; # If the file is not found, and a shared upload directory is used, look for it there. @@ -174,7 +177,7 @@ class Image $this->fileExists = file_exists( $sharedImage->getFullPath(true) ); if ( $this->fileExists ) { $this->name = $sharedImage->name; - $this->imagePath = $this->getFulPath(true); + $this->imagePath = $this->getFullPath(true); $this->fromSharedDirectory = true; } } @@ -246,7 +249,7 @@ class Image if ( $row ) { $this->fromSharedDirectory = true; $this->fileExists = true; - $this->imagePath = ''; + $this->imagePath = $this->getFullPath(true); $this->name = $name; $this->loadFromRow( $row ); } @@ -443,19 +446,39 @@ class Image */ function thumbUrl( $width, $subdir='thumb') { global $wgUploadPath, $wgUploadBaseUrl, - $wgSharedUploadPath,$wgSharedUploadDirectory; - $name = $this->thumbName( $width ); - if($this->fromSharedDirectory) { - $base = ''; - $path = $wgSharedUploadPath; + $wgSharedUploadPath,$wgSharedUploadDirectory, + $wgSharedThumbnailScriptPath, $wgThumbnailScriptPath; + + // Generate thumb.php URL if possible + $script = false; + $url = false; + + if ( $this->fromSharedDirectory ) { + if ( $wgSharedThumbnailScriptPath ) { + $script = $wgSharedThumbnailScriptPath; + } } else { - $base = $wgUploadBaseUrl; - $path = $wgUploadPath; + if ( $wgThumbnailScriptPath ) { + $script = $wgThumbnailScriptPath; + } } - $url = "{$base}{$path}/{$subdir}" . - wfGetHashPath($name, $this->fromSharedDirectory) - . "{$name}"; - return wfUrlencode($url); + if ( $script ) { + $url = $script . '?f=' . urlencode( $this->name ) . '&w=' . urlencode( $width ); + } else { + $name = $this->thumbName( $width ); + if($this->fromSharedDirectory) { + $base = ''; + $path = $wgSharedUploadPath; + } else { + $base = $wgUploadBaseUrl; + $path = $wgUploadPath; + } + $url = "{$base}{$path}/{$subdir}" . + wfGetHashPath($this->name, $this->fromSharedDirectory) + . $this->name.'/'.$name; + $url = wfUrlencode( $url ); + } + return array( $script !== false, $url ); } /** @@ -465,7 +488,7 @@ class Image * @param boolean $shared Does the thumbnail come from the shared repository? * @access private */ - function thumbName( $width, $shared=false ) { + function thumbName( $width ) { $thumb = $width."px-".$this->name; if( $this->extension == 'svg' ) { # Rasterize SVG vector images to PNG @@ -559,18 +582,12 @@ class Image * @access private */ function /* private */ renderThumb( $width, $useScript = true ) { - global $wgImageMagickConvertCommand; - global $wgUseImageMagick; global $wgUseSquid, $wgInternalServer; global $wgThumbnailScriptPath, $wgSharedThumbnailScriptPath; $width = IntVal( $width ); $this->load(); - $thumbName = $this->thumbName( $width, $this->fromSharedDirectory ); - $thumbPath = wfImageThumbDir( $thumbName, 'thumb', $this->fromSharedDirectory ).'/'.$thumbName; - $thumbUrl = $this->thumbUrl( $width ); - #wfDebug ( "Render name: $thumbName path: $thumbPath url: $thumbUrl\n"); if ( ! $this->exists() ) { # If there is no image, there will be no thumbnail @@ -590,137 +607,201 @@ class Image $height = floor( $this->height * ( $width/$this->width ) ); - // Generate thumb.php URL if possible - $thumbScript = false; - $scriptUrl = false; + list( $isScriptUrl, $url ) = $this->thumbUrl( $width ); + if ( $isScriptUrl && $useScript ) { + // Use thumb.php to render the image + return new ThumbnailImage( $url, $width, $height ); + } - if ( $this->fromSharedDirectory ) { - if ( $wgSharedThumbnailScriptPath ) { - $thumbScript = $wgSharedThumbnailScriptPath; - } - } else { - if ( $wgThumbnailScriptPath ) { - $thumbScript = $wgThumbnailScriptPath; + $thumbName = $this->thumbName( $width, $this->fromSharedDirectory ); + $thumbPath = wfImageThumbDir( $this->name, $this->fromSharedDirectory ).'/'.$thumbName; + + if ( !file_exists( $thumbPath ) ) { + $oldThumbPath = wfDeprecatedThumbDir( $thumbName, 'thumb', $this->fromSharedDirectory ). + '/'.$thumbName; + $done = false; + if ( file_exists( $oldThumbPath ) ) { + if ( filemtime($oldThumbPath) >= filemtime($this->imagePath) ) { + rename( $oldThumbPath, $thumbPath ); + $done = true; + } else { + unlink( $oldThumbPath ); + } } - } - if ( $thumbScript ) { - $scriptUrl = $thumbScript . '?f=' . urlencode( $this->name ) . '&w=' . urlencode( $width ); - if ( $useScript ) { - // Use thumb.php to render the image - return new ThumbnailImage( $scriptUrl, $width, $height ); + if ( !$done ) { + $this->reallyRenderThumb( $thumbPath, $width, $height ); + + # 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 ) { + if ( substr( $url, 0, 4 ) == 'http' ) { + $urlArr = array( $url ); + } else { + $urlArr = array( $wgInternalServer.$url ); + } + wfPurgeSquidServers($urlArr); + } } } + return new ThumbnailImage( $url, $width, $height, $thumbPath ); + } // END OF function renderThumb - if ( (! file_exists( $thumbPath ) ) || ( filemtime($thumbPath) < filemtime($this->imagePath) ) ) { - if( $this->extension == 'svg' ) { - global $wgSVGConverters, $wgSVGConverter; - if( isset( $wgSVGConverters[$wgSVGConverter] ) ) { - global $wgSVGConverterPath; - $cmd = str_replace( - array( '$path/', '$width', '$input', '$output' ), - array( $wgSVGConverterPath, - $width, - escapeshellarg( $this->imagePath ), - escapeshellarg( $thumbPath ) ), - $wgSVGConverters[$wgSVGConverter] ); - $conv = shell_exec( $cmd ); - } else { - $conv = false; - } - } elseif ( $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); + /** + * Really render a thumbnail + * + * @access private + */ + function /*private*/ reallyRenderThumb( $thumbPath, $width, $height ) { + global $wgSVGConverters, $wgSVGConverter, + $wgUseImageMagick, $wgImageMagickConvertCommand; + + $this->load(); + + if( $this->extension == 'svg' ) { + global $wgSVGConverters, $wgSVGConverter; + if( isset( $wgSVGConverters[$wgSVGConverter] ) ) { + global $wgSVGConverterPath; + $cmd = str_replace( + array( '$path/', '$width', '$input', '$output' ), + array( $wgSVGConverterPath, + $width, + escapeshellarg( $this->imagePath ), + escapeshellarg( $thumbPath ) ), + $wgSVGConverters[$wgSVGConverter] ); $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; - } - 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 ); + $conv = false; } + } elseif ( $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. # - # Check for zero-sized thumbnails. Those can be generated when - # no disk space is available or some other error occurs - # - if( file_exists( $thumbPath ) ) { - $thumbstat = stat( $thumbPath ); - if( $thumbstat['size'] == 0 ) { - unlink( $thumbPath ); - } + # 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; + } + 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 + imagepng( $dst_image, $thumbPath ); + break; + case 2: # JPEG + 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 + # + if( file_exists( $thumbPath ) ) { + $thumbstat = stat( $thumbPath ); + if( $thumbstat['size'] == 0 ) { + unlink( $thumbPath ); + } + } + } + + /** + * Get all thumbnail names previously generated for this image + */ + function getThumbnails( $shared = false ) { + $this->load(); + $files = array(); + $dir = wfImageThumbDir( $this->name, $shared ); - # 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 - ); - if ( $scriptUrl ) { - $urlArr[] = $scriptUrl; + // This generates an error on failure, hence the @ + $handle = @opendir( $dir ); + + if ( $handle ) { + while ( false !== ( $file = readdir($handle) ) ) { + if ( $file{0} != '.' ) { + $files[] = $file; } - wfPurgeSquidServers($urlArr); + } + closedir( $handle ); + } + + return $files; + } + + /** + * Delete all previously generated thumbnails, refresh metadata in memcached and purge the squid + */ + function purgeCache( $archiveFiles = array(), $shared = false ) { + global $wgInternalServer, $wgUseSquid; + + // Refresh metadata cache + $this->loadFromFile(); + $this->saveToCache(); + + // Delete thumbnails + $files = $this->getThumbnails( $shared ); + $dir = wfImageThumbDir( $this->name, $shared ); + $urls = array(); + foreach ( $files as $file ) { + if ( preg_match( '/^(\d+)px/', $file, $m ) ) { + $urls[] = $wgInternalServer . $this->thumbUrl( $m[1], $this->fromSharedDirectory ); + @unlink( "$dir/$file" ); } } - return new ThumbnailImage( $thumbUrl, $width, $height ); - } // END OF function createThumb + + // Purge the squid + if ( $wgUseSquid ) { + $urls[] = $wgInternalServer . $this->getViewURL(); + foreach ( $archiveFiles as $file ) { + $urls[] = $wgInternalServer . wfImageArchiveUrl( $file ); + } + wfPurgeSquidServers( $urls ); + } + } /** * Return the image history of this image, line by line. @@ -794,8 +875,14 @@ class Image $dir = $fromSharedRepository ? $wgSharedUploadDirectory : $wgUploadDirectory; - $name = $this->name; - $fullpath = $dir . wfGetHashPath($name, $fromSharedRepository) . $name; + + // $wgSharedUploadDirectory may be false, if thumb.php is used + if ( $dir ) { + $fullpath = $dir . wfGetHashPath($this->name, $fromSharedRepository) . $this->name; + } else { + $fullpath = false; + } + return $fullpath; } @@ -823,9 +910,8 @@ class Image wfDebugDieBacktrace( 'Database schema not up to date, please run maintenance/archives/patch-image_name_unique.sql' ); } - // Fill metadata fields by querying the file - $this->loadFromFile(); - $this->saveToCache(); + // Delete thumbnails and refresh the cache + $this->purgeCache(); // Fail now if the image isn't there if ( !$this->fileExists || $this->fromSharedDirectory ) { @@ -951,13 +1037,26 @@ function wfImageDir( $fname ) { * * This function is called from thumb.php before Setup.php is included * - * @param string $fname file name of the thumbnail file, including file size prefix + * @param string $fname file name of the original image file * @param string $subdir (optional) subdirectory of the image upload directory that should be used for storing the thumbnail. Default is 'thumb' * @param boolean $shared (optional) use the shared upload directory * @access public */ -function wfImageThumbDir( $fname , $subdir='thumb', $shared=false) { - return wfImageArchiveDir( $fname, $subdir, $shared ); +function wfImageThumbDir( $fname, $shared = false ) { + $dir = wfImageArchiveDir( $fname, 'thumb', $shared ) . "/$fname"; + if ( ! is_dir( $dir ) ) { + $oldumask = umask(0); + @mkdir( $dir, 0777 ); + umask( $oldumask ); + } + return $dir; +} + +/** + * Old thumbnail directory, kept for conversion + */ +function wfDeprecatedThumbDir( $thumbName , $subdir='thumb', $shared=false) { + return wfImageArchiveDir( $thumbName, $subdir, $shared ); } /** @@ -980,15 +1079,19 @@ function wfImageArchiveDir( $fname , $subdir='archive', $shared=false ) { if (!$hashdir) { return $dir.'/'.$subdir; } $hash = md5( $fname ); $oldumask = umask(0); + # Suppress warning messages here; if the file itself can't # be written we'll worry about it then. + wfSuppressWarnings(); + $archive = $dir.'/'.$subdir; - if ( ! is_dir( $archive ) ) { @mkdir( $archive, 0777 ); } + if ( ! is_dir( $archive ) ) { mkdir( $archive, 0777 ); } $archive .= '/' . $hash{0}; - if ( ! is_dir( $archive ) ) { @mkdir( $archive, 0777 ); } + if ( ! is_dir( $archive ) ) { mkdir( $archive, 0777 ); } $archive .= '/' . substr( $hash, 0, 2 ); - if ( ! is_dir( $archive ) ) { @mkdir( $archive, 0777 ); } + if ( ! is_dir( $archive ) ) { mkdir( $archive, 0777 ); } + wfRestoreWarnings(); umask( $oldumask ); return $archive; } @@ -1129,10 +1232,11 @@ class ThumbnailImage { * @param string $url URL path to the thumb * @access private */ - function ThumbnailImage( $url, $width, $height ) { + function ThumbnailImage( $url, $width, $height, $path = false ) { $this->url = $url; $this->width = $width; $this->height = $height; + $this->path = $path; } /** diff --git a/includes/ImagePage.php b/includes/ImagePage.php index 586fc8a7b4..c6eed1eee8 100644 --- a/includes/ImagePage.php +++ b/includes/ImagePage.php @@ -289,16 +289,7 @@ class ImagePage extends Article { return; } $dbw->delete( 'image', array( 'img_name' => $image ) ); - $res = $dbw->select( 'oldimage', array( 'oi_archive_name' ), array( 'oi_name' => $image ) ); - - # Squid purging - if ( $wgUseSquid ) { - $urlArr = Array( - $wgInternalServer . Image::imageUrl( $image ) - ); - wfPurgeSquidServers($urlArr); - } - + $res = $dbw->select( 'oldimage', array( 'oi_archive_name' ), array( 'oi_name' => $image ) ); $urlArr = Array(); while ( $s = $dbw->fetchObject( $res ) ) { @@ -306,7 +297,7 @@ class ImagePage extends Article { $urlArr[] = $wgInternalServer.wfImageArchiveUrl( $s->oi_archive_name ); } - # Squid purging, part II + # Defer purging of archived URLs if ( $wgUseSquid ) { /* this needs to be done after LinksUpdate */ $u = new SquidUpdate( $urlArr ); @@ -321,10 +312,9 @@ class ImagePage extends Article { $article = new Article( $this->mTitle ); $article->doDeleteArticle( $reason ); # ignore errors - /* refresh image metadata cache */ + /* Delete thumbnails and refresh image metadata cache */ $imgObj = new Image( $this->mTitle ); - $imgObj->loadFromFile(); - $imgObj->saveToCache(); + $imgObj->purgeCache(); $deleted = $image; } @@ -426,15 +416,6 @@ class ImagePage extends Article { $img = Image::newFromName( $name ); $img->recordUpload( $oldver, wfMsg( "reverted" ) ); - # Squid purging - if ( $wgUseSquid ) { - $urlArr = Array( - $wgInternalServer.wfImageArchiveUrl( $name ), - $wgInternalServer . Image::imageUrl( $name ), - ); - wfPurgeSquidServers($urlArr); - } - $wgOut->setPagetitle( wfMsg( 'actioncomplete' ) ); $wgOut->setRobotpolicy( 'noindex,nofollow' ); $wgOut->addHTML( wfMsg( 'imagereverted' ) ); diff --git a/includes/StreamFile.php b/includes/StreamFile.php index 1451f2e7ee..ae993d80f0 100644 --- a/includes/StreamFile.php +++ b/includes/StreamFile.php @@ -17,7 +17,8 @@ does not.

header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $stat['mtime'] ) . ' GMT' ); if ( !empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) { - $sinceTime = strtotime( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ); + $modsince = preg_replace( '/;.*$/', '', $_SERVER['HTTP_IF_MODIFIED_SINCE'] ); + $sinceTime = strtotime( $modsince ); if ( $stat['mtime'] <= $sinceTime ) { header( "HTTP/1.0 304 Not Modified" ); return; diff --git a/thumb.php b/thumb.php index ff7f9153e9..73d2ca9e4b 100644 --- a/thumb.php +++ b/thumb.php @@ -5,10 +5,13 @@ * If the file exists, we make do with abridged MediaWiki initialisation. */ -unset( $IP ); define( 'MEDIAWIKI', true ); +unset( $IP ); +$wgNoOutputBuffer = true; + require_once( './includes/Defines.php' ); require_once( './LocalSettings.php' ); +require_once( 'GlobalFunctions.php' ); require_once( 'Image.php' ); require_once( 'StreamFile.php' ); @@ -34,7 +37,7 @@ $thumbName = "{$width}px-$fileName"; if ( preg_match( '/\.svg$/', $fileName ) ) { $thumbName .= '.png'; } -$thumbPath = wfImageThumbDir( $thumbName ) . '/' . $thumbName; +$thumbPath = wfImageThumbDir( $fileName ) . '/' . $thumbName; if ( file_exists( $thumbPath ) && filemtime( $thumbPath ) >= filemtime( $imagePath ) ) { wfStreamFile( $thumbPath ); @@ -44,18 +47,14 @@ if ( file_exists( $thumbPath ) && filemtime( $thumbPath ) >= filemtime( $imagePa // OK, no valid thumbnail, time to get out the heavy machinery require_once( 'Setup.php' ); -// Force renderThumb() to actually do something -$wgThumbnailScriptPath = false; -$wgSharedThumbnailScriptPath = false; - $img = Image::newFromName( $fileName ); if ( $img ) { - $thumb = $img->renderThumb( $width ); + $thumb = $img->renderThumb( $width, false ); } else { $thumb = false; } -if ( $thumb ) { +if ( $thumb && $thumb->path ) { wfStreamFile( $thumb->path ); } else { $badtitle = wfMsg( 'badtitle' ); -- 2.20.1