Optional thumbnail generation by client request, using thumb.php. This removes any...
authorTim Starling <tstarling@users.mediawiki.org>
Sat, 16 Apr 2005 04:33:34 +0000 (04:33 +0000)
committerTim Starling <tstarling@users.mediawiki.org>
Sat, 16 Apr 2005 04:33:34 +0000 (04:33 +0000)
img_auth.php
includes/DefaultSettings.php
includes/Image.php
includes/ImageGallery.php
includes/SpecialNewimages.php
includes/StreamFile.php [new file with mode: 0644]
thumb.php [new file with mode: 0644]

index 875eb32..6f6152e 100644 (file)
@@ -13,6 +13,7 @@ define( 'MEDIAWIKI', true );
 require_once( 'includes/Defines.php' );
 require_once( './LocalSettings.php' );
 require_once( 'includes/Setup.php' );
+require_once( 'includes/StreamFile.php' );
 
 if( !isset( $_SERVER['PATH_INFO'] ) ) {
        wfForbidden();
@@ -40,142 +41,7 @@ if( is_dir( $filename ) ) {
 }
 
 # Write file
-$type = wfGetType( $filename );
-if ( $type ) {
-       header('Content-type: '.$type);
-} else {
-       header('Content-type: application/x-wiki');
-}
-
-readfile( $filename );
-
-function wfGetType( $filename ) {
-       # There's probably a better way to do this
-       $types = <<<END_STRING
-application/andrew-inset ez
-application/mac-binhex40 hqx
-application/mac-compactpro cpt
-application/mathml+xml mathml
-application/msword doc
-application/octet-stream bin dms lha lzh exe class so dll
-application/oda oda
-application/ogg ogg
-application/pdf pdf
-application/postscript ai eps ps
-application/rdf+xml rdf
-application/smil smi smil
-application/srgs gram
-application/srgs+xml grxml
-application/vnd.mif mif
-application/vnd.ms-excel xls
-application/vnd.ms-powerpoint ppt
-application/vnd.wap.wbxml wbxml
-application/vnd.wap.wmlc wmlc
-application/vnd.wap.wmlscriptc wmlsc
-application/voicexml+xml vxml
-application/x-bcpio bcpio
-application/x-cdlink vcd
-application/x-chess-pgn pgn
-application/x-cpio cpio
-application/x-csh csh
-application/x-director dcr dir dxr
-application/x-dvi dvi
-application/x-futuresplash spl
-application/x-gtar gtar
-application/x-hdf hdf
-application/x-javascript js
-application/x-koan skp skd skt skm
-application/x-latex latex
-application/x-netcdf nc cdf
-application/x-sh sh
-application/x-shar shar
-application/x-shockwave-flash swf
-application/x-stuffit sit
-application/x-sv4cpio sv4cpio
-application/x-sv4crc sv4crc
-application/x-tar tar
-application/x-tcl tcl
-application/x-tex tex
-application/x-texinfo texinfo texi
-application/x-troff t tr roff
-application/x-troff-man man
-application/x-troff-me me
-application/x-troff-ms ms
-application/x-ustar ustar
-application/x-wais-source src
-application/xhtml+xml xhtml xht
-application/xslt+xml xslt
-application/xml xml xsl
-application/xml-dtd dtd
-application/zip zip
-audio/basic au snd
-audio/midi mid midi kar
-audio/mpeg mpga mp2 mp3
-audio/x-aiff aif aiff aifc
-audio/x-mpegurl m3u
-audio/x-pn-realaudio ram rm
-audio/x-pn-realaudio-plugin rpm
-audio/x-realaudio ra
-audio/x-wav wav
-chemical/x-pdb pdb
-chemical/x-xyz xyz
-image/bmp bmp
-image/cgm cgm
-image/gif gif
-image/ief ief
-image/jpeg jpeg jpg jpe
-image/png png
-image/svg+xml svg
-image/tiff tiff tif
-image/vnd.djvu djvu djv
-image/vnd.wap.wbmp wbmp
-image/x-cmu-raster ras
-image/x-icon ico
-image/x-portable-anymap pnm
-image/x-portable-bitmap pbm
-image/x-portable-graymap pgm
-image/x-portable-pixmap ppm
-image/x-rgb rgb
-image/x-xbitmap xbm
-image/x-xpixmap xpm
-image/x-xwindowdump xwd
-model/iges igs iges
-model/mesh msh mesh silo
-model/vrml wrl vrml
-text/calendar ics ifb
-text/css css
-text/richtext rtx
-text/rtf rtf
-text/sgml sgml sgm
-text/tab-separated-values tsv
-text/vnd.wap.wml wml
-text/vnd.wap.wmlscript wmls
-text/x-setext etx
-video/mpeg mpeg mpg mpe
-video/quicktime qt mov
-video/vnd.mpegurl mxu
-video/x-msvideo avi
-video/x-sgi-movie movie
-x-conference/x-cooltalk ice
-END_STRING;
-       // Needed for windows servers who use \r\n not \n
-       $endl = "
-";
-       $types = explode( $endl, $types );
-       if ( !preg_match( "/\.([^.]*?)$/", $filename, $matches ) ) {
-               return false;
-       }
-
-       foreach( $types as $type ) {
-               $extensions = explode( " ", $type );
-               for ( $i=1; $i<count( $extensions ); $i++ ) {
-                       if ( $extensions[$i] == $matches[1] ) {
-                               return $extensions[0];
-                       }
-               }
-       }
-       return false;
-}
+wfStreamFile( $filename );
 
 function wfForbidden() {
        header( 'HTTP/1.0 403 Forbidden' );
index 62f8a50..e4a7067 100644 (file)
@@ -139,6 +139,18 @@ $wgSharedUploadDBname = false;
 /** Cache shared metadata in memcached. Don't do this if the commons wiki is in a different memcached domain */
 $wgCacheSharedUploads = true;
 
+/** 
+ * Give a path here to use thumb.php for thumbnail generation on client request, instead of 
+ * generating them on render and outputting a static URL. This is necessary if some of your 
+ * apache servers don't have read/write access to the thumbnail path.
+ * 
+ * Example: 
+ *   $wgThumnailScriptPath = "{$wgScriptPath}/thumb.php";
+ */
+$wgThumbnailScriptPath = false;
+$wgSharedThumbnailScriptPath = false;
+
+
 /**
  * Set the following to false especially if you have a set of files that need to
  * be accessible by all wikis, and you do not want to use the hash (path/a/aa/)
index fbc4786..8dd6c27 100644 (file)
@@ -48,11 +48,11 @@ class Image
        /** 
         * Obsolete factory function, use constructor
         */
-       function newFromTitle( &$title ) {
+       function newFromTitle( $title ) {
                return new Image( $title );
        }
        
-       function Image( &$title ) {
+       function Image( $title ) {
                $this->title =& $title;
                $this->name = $title->getDBkey();
 
@@ -105,6 +105,7 @@ class Image
                                                $this->height = $commonsCachedValues['height'];
                                                $this->bits = $commonsCachedValues['bits'];
                                                $this->type = $commonsCachedValues['type'];
+                                               $this->size = $commonsCachedValues['size'];
                                                $this->fromSharedDirectory = true;
                                                $this->dataLoaded = true;
                                        }
@@ -118,6 +119,7 @@ class Image
                                $this->height = $cachedValues['height'];
                                $this->bits = $cachedValues['bits'];
                                $this->type = $cachedValues['type'];
+                               $this->size = $cachedValues['size'];
                                $this->fromSharedDirectory = false;
                                $this->dataLoaded = true;
                        }
@@ -145,7 +147,8 @@ class Image
                                                                  'width' => $this->width,
                                                                  'height' => $this->height,
                                                                  'bits' => $this->bits,
-                                                                 'type' => $this->type);
+                                                                 'type' => $this->type,
+                                                                 'size' => $this->size);
 
                        $wgMemc->set( $keys[0], $cachedValues );
                }
@@ -155,23 +158,25 @@ class Image
         * Load metadata from the file itself
         */
        function loadFromFile() {
-               global $wgUseSharedUploads, $wgSharedUploadDirectory;
+               global $wgUseSharedUploads, $wgSharedUploadDirectory, $wgLang;
                $fname = 'Image::loadFromFile';
                wfProfileIn( $fname );
                $this->imagePath = $this->getFullPath();
                $this->fileExists = file_exists( $this->imagePath );
                $gis = false;
 
-               # If the file is not found, and a non-wiki shared upload directory is used, look for it there.
+               # If the file is not found, and a shared upload directory is used, look for it there.
                if (!$this->fileExists && $wgUseSharedUploads && $wgSharedUploadDirectory) {                    
                        # In case we're on a wgCapitalLinks=false wiki, we 
                        # capitalize the first letter of the filename before 
                        # looking it up in the shared repository.
-                       $this->name = $wgLang->ucfirst($name);
-                       
-                       $this->imagePath = $this->getFullPath(true);
-                       $this->fileExists = file_exists( $this->imagePath);
-                       $this->fromSharedDirectory = true;
+                       $sharedImage = Image::newFromName( $wgLang->ucfirst($this->name) );
+                       $this->fileExists = file_exists( $sharedImage->getFullPath(true) );
+                       if ( $this->fileExists ) {
+                               $this->name = $sharedImage->name;
+                               $this->imagePath = $this->getFulPath(true);
+                               $this->fromSharedDirectory = true;
+                       }
                }
 
                if ( $this->fileExists ) {
@@ -505,6 +510,7 @@ class Image
                if ( $height == -1 ) {
                        return $this->renderThumb( $width );
                }
+               $this->load();
                if ( $width < $this->width ) {
                        $thumbheight = $this->height * $width / $this->width;
                        $thumbwidth = $width;
@@ -534,7 +540,7 @@ class Image
                        $path = '/common/images/' . $icon;
                        $filepath = $wgStyleDirectory . $path;
                        if( file_exists( $filepath ) ) {
-                               return new ThumbnailImage( $filepath, $wgStylePath . $path );
+                               return new ThumbnailImage( $wgStylePath . $path, 120, 120 );
                        }
                }
                return null;
@@ -552,13 +558,15 @@ class Image
         * @return ThumbnailImage
         * @access private
         */
-       function /* private */ renderThumb( $width ) {
+       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 );
@@ -577,7 +585,30 @@ class Image
 
                if( $width > $this->width && !$this->mustRender() ) {
                        # Don't make an image bigger than the source
-                       return new ThumbnailImage( $this->getImagePath(), $this->getViewURL() );
+                       return new ThumbnailImage( $this->getViewURL(), $this->getWidth(), $this->getHeight() );
+               }
+               
+               $height = floor( $this->height * ( $width/$this->width ) );
+               
+               // Generate thumb.php URL if possible
+               $thumbScript = false;
+               $scriptUrl = false;
+
+               if ( $this->fromSharedDirectory ) {
+                       if ( $wgSharedThumbnailScriptPath ) {
+                               $thumbScript = $wgSharedThumbnailScriptPath;
+                       }
+               } else {
+                       if ( $wgThumbnailScriptPath ) {
+                               $thumbScript = $wgThumbnailScriptPath;
+                       }
+               }
+               if ( $thumbScript ) {
+                       $scriptUrl = $thumbScript . '?f=' . urlencode( $this->name ) . '&w=' . urlencode( $width );
+                       if ( $useScript ) {
+                               // Use thumb.php to render the image
+                               return new ThumbnailImage( , $width, $height );
+                       }
                }
 
                if ( (! file_exists( $thumbPath ) ) || ( filemtime($thumbPath) < filemtime($this->imagePath) ) ) {
@@ -635,7 +666,6 @@ class Image
                                                return 'Image type not supported';
                                                break;
                                }
-                               $height = floor( $this->height * ( $width/$this->width ) );
                                if ( $truecolor ) {
                                        $dst_image = imagecreatetruecolor( $width, $height );
                                } else {
@@ -683,10 +713,13 @@ class Image
                                $urlArr = Array(
                                        $wgInternalServer.$thumbUrl
                                );
+                               if ( $scriptUrl ) {
+                                       $urlArr[] = $scriptUrl;
+                               }
                                wfPurgeSquidServers($urlArr);
                        }
                }
-               return new ThumbnailImage( $thumbPath, $thumbUrl );
+               return new ThumbnailImage( $thumbUrl, $width, $height );
        } // END OF function createThumb
 
        /**
@@ -889,6 +922,8 @@ class Image
  * Returns the image directory of an image
  * If the directory does not exist, it is created.
  * The result is an absolute path.
+ *
+ * This function is called from thumb.php before Setup.php is included
  * 
  * @param string $fname                file name of the image file
  * @access public
@@ -913,6 +948,8 @@ function wfImageDir( $fname ) {
  * Returns the image directory of an image's thubnail
  * If the directory does not exist, it is created.
  * The result is an absolute path.
+ *
+ * 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 $subdir       (optional) subdirectory of the image upload directory that should be used for storing the thumbnail. Default is 'thumb'
@@ -927,6 +964,8 @@ function wfImageThumbDir( $fname , $subdir='thumb', $shared=false) {
  * Returns the image directory of an image's old version
  * If the directory does not exist, it is created.
  * The result is an absolute path.
+ *
+ * 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 $subdir       (optional) subdirectory of the image upload directory that should be used for storing the old version. Default is 'archive'
@@ -1090,17 +1129,10 @@ class ThumbnailImage {
         * @param string $url URL path to the thumb
         * @access private
         */
-       function ThumbnailImage( $path, $url ) {
+       function ThumbnailImage( $url, $width, $height ) {
                $this->url = $url;
-               $this->path = $path;
-               $size = @getimagesize( $this->path );
-               if( $size ) {
-                       $this->width = $size[0];
-                       $this->height = $size[1];
-               } else {
-                       $this->width = 0;
-                       $this->height = 0;
-               }
+               $this->width = $width;
+               $this->height = $height;
        }
 
        /**
@@ -1136,18 +1168,5 @@ class ThumbnailImage {
                return $html;
        }
 
-    /**             
-     * Return the size of the thumbnail file, in bytes or false if the file
-     * can't be stat().
-     * @access public
-     */                     
-    function getSize() {         
-               $st = stat( $this->path );
-               if( $st ) {     
-                       return $st['size']; 
-               } else {        
-                       return false;
-               }                       
-       }
 }
 ?>
index 4cbec94..faea274 100644 (file)
@@ -51,7 +51,7 @@ class ImageGallery
 
 
        /**
-        * isEmpty() returns false iff the gallery doesn't contain any images
+        * isEmpty() returns true if the gallery contains no images
         */
        function isEmpty() {
                return empty( $this->mImages );
@@ -119,13 +119,13 @@ class ImageGallery
                                } else {
                                        $nb = wfMsg( 'filemissing' );
                                }
-                               $nb = htmlspecialchars( $nb ) . '<br />';
+                               $nb = htmlspecialchars( $nb ) . "<br />\n";
                        } else {
                                $nb = '';
                        }
                                
                        $textlink = $this->mShowFilename ?
-                               $sk->makeKnownLinkObj( $nt, htmlspecialchars( $wgLang->truncate( $nt->getText(), 20, '...' ) ) ) . '<br />' :
+                               $sk->makeKnownLinkObj( $nt, htmlspecialchars( $wgLang->truncate( $nt->getText(), 20, '...' ) ) ) . "<br />\n" :
                                '' ;
 
                        $s .= ($i%4==0) ? '<tr>' : '';
index 43a401c..27628d8 100644 (file)
@@ -88,9 +88,9 @@ function wfSpecialNewimages() {
                $img = Image::newFromTitle( $nt );
                $ul = $sk->makeLinkObj( Title::makeTitle( NS_USER, $ut ), $ut );
 
-               $gallery->add( $img, $ul.'<br /><i>'.$wgLang->timeanddate( $s->img_timestamp, true ).'</i><br />' );
+               $gallery->add( $img, "$ul<br />\n<i>".$wgLang->timeanddate( $s->img_timestamp, true )."</i><br />\n" );
                
-               $timestamp = wfTImestamp( TS_MW, $s->img_timestamp );
+               $timestamp = wfTimestamp( TS_MW, $s->img_timestamp );
                if( empty( $firstTimestamp ) ) {
                        $firstTimestamp = $timestamp;
                }
diff --git a/includes/StreamFile.php b/includes/StreamFile.php
new file mode 100644 (file)
index 0000000..07f374d
--- /dev/null
@@ -0,0 +1,157 @@
+<?php
+
+function wfStreamFile( $fname ) {
+       global $wgSquidMaxage;
+       $stat = stat( $fname );
+       if ( !$stat ) {
+               header( 'HTTP/1.0 404 Not Found' );
+               echo "<html><body>
+<h1>File not found</h1>
+<p>Although this PHP script ({$_SERVER['SCRIPT_NAME']}) exists, the file requested for output 
+does not.</p>
+</body></html>";
+               return;
+       }
+
+       
+       $type = wfGetType( $fname );
+       if ( $type ) {
+               header("Content-type: $type");
+       } else {
+               header('Content-type: application/x-wiki');
+       }
+       header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $stat['mtime'] ) );
+       header( "Cache-Control: s-maxage=$wgSquidMaxage, must-revalidate, max-age=0" );
+       readfile( $fname );
+       exit;
+}
+
+function wfGetType( $filename ) {
+       # There's probably a better way to do this
+       $types = <<<END_STRING
+application/andrew-inset ez
+application/mac-binhex40 hqx
+application/mac-compactpro cpt
+application/mathml+xml mathml
+application/msword doc
+application/octet-stream bin dms lha lzh exe class so dll
+application/oda oda
+application/ogg ogg
+application/pdf pdf
+application/postscript ai eps ps
+application/rdf+xml rdf
+application/smil smi smil
+application/srgs gram
+application/srgs+xml grxml
+application/vnd.mif mif
+application/vnd.ms-excel xls
+application/vnd.ms-powerpoint ppt
+application/vnd.wap.wbxml wbxml
+application/vnd.wap.wmlc wmlc
+application/vnd.wap.wmlscriptc wmlsc
+application/voicexml+xml vxml
+application/x-bcpio bcpio
+application/x-cdlink vcd
+application/x-chess-pgn pgn
+application/x-cpio cpio
+application/x-csh csh
+application/x-director dcr dir dxr
+application/x-dvi dvi
+application/x-futuresplash spl
+application/x-gtar gtar
+application/x-hdf hdf
+application/x-javascript js
+application/x-koan skp skd skt skm
+application/x-latex latex
+application/x-netcdf nc cdf
+application/x-sh sh
+application/x-shar shar
+application/x-shockwave-flash swf
+application/x-stuffit sit
+application/x-sv4cpio sv4cpio
+application/x-sv4crc sv4crc
+application/x-tar tar
+application/x-tcl tcl
+application/x-tex tex
+application/x-texinfo texinfo texi
+application/x-troff t tr roff
+application/x-troff-man man
+application/x-troff-me me
+application/x-troff-ms ms
+application/x-ustar ustar
+application/x-wais-source src
+application/xhtml+xml xhtml xht
+application/xslt+xml xslt
+application/xml xml xsl
+application/xml-dtd dtd
+application/zip zip
+audio/basic au snd
+audio/midi mid midi kar
+audio/mpeg mpga mp2 mp3
+audio/x-aiff aif aiff aifc
+audio/x-mpegurl m3u
+audio/x-pn-realaudio ram rm
+audio/x-pn-realaudio-plugin rpm
+audio/x-realaudio ra
+audio/x-wav wav
+chemical/x-pdb pdb
+chemical/x-xyz xyz
+image/bmp bmp
+image/cgm cgm
+image/gif gif
+image/ief ief
+image/jpeg jpeg jpg jpe
+image/png png
+image/svg+xml svg
+image/tiff tiff tif
+image/vnd.djvu djvu djv
+image/vnd.wap.wbmp wbmp
+image/x-cmu-raster ras
+image/x-icon ico
+image/x-portable-anymap pnm
+image/x-portable-bitmap pbm
+image/x-portable-graymap pgm
+image/x-portable-pixmap ppm
+image/x-rgb rgb
+image/x-xbitmap xbm
+image/x-xpixmap xpm
+image/x-xwindowdump xwd
+model/iges igs iges
+model/mesh msh mesh silo
+model/vrml wrl vrml
+text/calendar ics ifb
+text/css css
+text/richtext rtx
+text/rtf rtf
+text/sgml sgml sgm
+text/tab-separated-values tsv
+text/vnd.wap.wml wml
+text/vnd.wap.wmlscript wmls
+text/x-setext etx
+video/mpeg mpeg mpg mpe
+video/quicktime qt mov
+video/vnd.mpegurl mxu
+video/x-msvideo avi
+video/x-sgi-movie movie
+x-conference/x-cooltalk ice
+END_STRING;
+       // Needed for windows servers who use \r\n not \n
+       $endl = "
+";
+       $types = explode( $endl, $types );
+       if ( !preg_match( "/\.([^.]*?)$/", $filename, $matches ) ) {
+               return false;
+       }
+
+       foreach( $types as $type ) {
+               $extensions = explode( " ", $type );
+               for ( $i=1; $i<count( $extensions ); $i++ ) {
+                       if ( $extensions[$i] == $matches[1] ) {
+                               return $extensions[0];
+                       }
+               }
+       }
+       return false;
+}
+
+?>
diff --git a/thumb.php b/thumb.php
new file mode 100644 (file)
index 0000000..2b40c68
--- /dev/null
+++ b/thumb.php
@@ -0,0 +1,72 @@
+<?php
+
+/** 
+ * PHP script to stream out an image thumbnail. 
+ * If the file exists, we make do with abridged MediaWiki initialisation. 
+ */
+
+unset( $IP );
+define( 'MEDIAWIKI', true );
+require_once( './includes/Defines.php' );
+require_once( './LocalSettings.php' );
+require_once( 'Image.php' );
+require_once( 'StreamFile.php' );
+
+// Get input parameters
+
+if ( get_magic_quotes_gpc() ) {
+       $fileName = stripslashes( $_REQUEST['f'] );
+       $width = stripslashes( $_REQUEST['w'] );
+} else {
+       $fileName = $_REQUEST['f'];
+       $width = $_REQUEST['w'];
+}
+
+// Some basic input validation
+
+$width = intval( $width );
+$fileName = str_replace( '/', '_', $fileName );
+
+// Work out paths, carefully avoiding constructing an Image object because that won't work yet
+
+$imagePath = wfImageDir( $fileName ) . '/' . $fileName;
+$thumbName = "{$width}px-$fileName";
+if ( preg_match( '/\.svg$/', $fileName ) ) {
+       $thumbName .= '.png';
+}
+$thumbPath = wfImageThumbDir( $thumbName ) . '/' . $thumbName;
+
+if ( file_exists( $thumbPath ) && filemtime( $thumbPath ) >= filemtime( $imagePath ) ) {
+       wfStreamFile( $thumbPath );
+       exit;
+}
+
+// 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 );
+} else {
+       $thumb = false;
+}
+
+if ( $thumb ) {
+       wfStreamFile( $thumb->path );
+} else {
+       $badtitle = wfMsg( 'badtitle' );
+       $badtitletext = wfMsg( 'badtitletext' );
+       echo "<html><head>
+       <title>$badtitle</title>
+       <body>
+<h1>$badtitle</h1>
+<p>$badtitletext</p>
+</body></html>";
+}
+
+
+?>