From: Tim Starling Date: Sun, 10 Apr 2005 18:29:30 +0000 (+0000) Subject: Moved image metadata to the database. Changed Image object to have lightweight constr... X-Git-Tag: 1.5.0alpha1~312 X-Git-Url: http://git.cyclocoop.org/%22%2C%20generer_url_ecrire%28?a=commitdiff_plain;h=efba9e34764023c3fc0b7ce76b9b61d22a96ace6;p=lhc%2Fweb%2Fwiklou.git Moved image metadata to the database. Changed Image object to have lightweight constructors, similar to the User object. --- diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index fb08b23230..1082492451 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -132,10 +132,12 @@ $wgActionPaths = array(); $wgUseSharedUploads = false; /** Full path on the web server where shared uploads can be found */ $wgSharedUploadPath = "http://commons.wikimedia.org/shared/images"; -/** Path on the file system where shared uploads can be found */ +/** Path on the file system where shared uploads can be found. */ $wgSharedUploadDirectory = "/var/www/wiki3/images"; -/** Path on the file system where shared uploads can be found */ -$wgSharedUploadDBname = "commons"; +/** DB name with metadata about shared directory. Set this to false if the uploads do not come from a wiki. */ +$wgSharedUploadDBname = false; +/** Cache shared metadata in memcached. Don't do this if the commons wiki is in a different memcached domain */ +$wgCacheSharedUploads = true; /** * Set the following to false especially if you have a set of files that need to diff --git a/includes/Image.php b/includes/Image.php index 7aa408afdd..cb94ca8793 100644 --- a/includes/Image.php +++ b/includes/Image.php @@ -15,167 +15,277 @@ class Image /**#@+ * @access 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? - $fromSharedDirectory, # load this image from $wgSharedUploadDirectory - $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; # / + var $name, # name of the image (constructor) + $imagePath, # Path of the image (loadFromXxx) + $url, # Image URL (accessor) + $title, # Title object for this image (constructor) + $fileExists, # does the image file exist on disk? (loadFromXxx) + $fromSharedDirectory, # load this image from $wgSharedUploadDirectory (loadFromXxx) + $historyLine, # Number of line to return by nextHistoryLine() (constructor) + $historyRes, # result of the query for the image's history (nextHistoryLine) + $width, # \ + $height, # | + $bits, # --- returned by getimagesize (loadFromXxx) + $type, # | + $attr, # / + $size, # Size in bytes (loadFromXxx) + $dataLoaded; # Whether or not all this has been loaded from the database (loadFromXxx) - /**#@-*/ + /**#@-*/ /** * Create an Image object from an image name * * @param string $name name of the image, used to create a title object using Title::makeTitleSafe - * @param bool $recache if true, ignores anything in memcached and sets the updated metadata * @access public */ - function Image( $name, $recache = false ) { + function newFromName( $name ) { + $title = Title::makeTitleSafe( NS_IMAGE, $name ); + return new Image( $title ); + } - global $wgUseSharedUploads, $wgLang, $wgMemc, $wgDBname, - $wgSharedUploadDBname; + /** + * Obsolete factory function, use constructor + */ + function newFromTitle( &$title ) { + return new Image( $title ); + } + + function Image( &$title ) { + $this->title =& $title; + $this->name = $title->getDBkey(); - $this->name = $name; - $this->title = Title::makeTitleSafe( NS_IMAGE, $this->name ); - $this->fromSharedDirectory = false; - $this->imagePath = $this->getFullPath(); + $n = strrpos( $this->name, '.' ); + $this->extension = strtolower( $n ? substr( $this->name, $n + 1 ) : '' ); + $this->historyLine = 0; - $n = strrpos( $name, '.' ); - $this->extension = strtolower( $n ? substr( $name, $n + 1 ) : '' ); - $gis = false; - $hashedName = md5($this->name); - $cacheKey = "$wgDBname:Image:".$hashedName; - $foundCached = false; + $this->dataLoaded = false; + } + + /** + * Get the memcached keys + * Returns an array, first element is the local cache key, second is the shared cache key, if there is one + */ + function getCacheKeys( $shared = false ) { + global $wgDBname, $wgUseSharedUploads, $wgSharedUploadDBname, $wgCacheSharedUploads; - if ( !$recache ) { - $cachedValues = $wgMemc->get( $cacheKey ); - - if (!empty($cachedValues) && is_array($cachedValues)) { - if ($wgUseSharedUploads && $cachedValues['fromShared']) { - # if this is shared file, we need to check if image - # in shared repository has not changed - $commonsCachedValues = $wgMemc->get( "$wgSharedUploadDBname:Image:".$hashedName ); - if (!empty($commonsCachedValues) && is_array($commonsCachedValues)) { + $foundCached = false; + $hashedName = md5($this->name); + $keys = array( "$wgDBname:Image:$hashedName" ); + if ( $wgUseSharedUploads && $wgSharedUploadDBname && $wgCacheSharedUploads ) { + $keys[] = "$wgSharedUploadDBname:Image:$hashedName"; + } + return $keys; + } + + /** + * Try to load image metadata from memcached. Returns true on success. + */ + function loadFromCache() { + global $wgUseSharedUploads, $wgMemc; + $fname = 'Image::loadFromMemcached'; + wfProfileIn( $fname ); + $this->dataLoaded = false; + $keys = $this->getCacheKeys(); + $cachedValues = $wgMemc->get( $keys[0] ); + + // Check if the key existed and belongs to this version of MediaWiki + if (!empty($cachedValues) && is_array($cachedValues) && isset($cachedValues['width']) && $cachedValues['fileExists']) { + if ( $wgUseSharedUploads && $cachedValues['fromShared']) { + # if this is shared file, we need to check if image + # in shared repository has not changed + if ( isset( $keys[1] ) ) { + $commonsCachedValues = $wgMemc->get( $keys[1] ); + if (!empty($commonsCachedValues) && is_array($commonsCachedValues) && isset($commonsCachedValues['width'])) { $this->name = $commonsCachedValues['name']; $this->imagePath = $commonsCachedValues['imagePath']; $this->fileExists = $commonsCachedValues['fileExists']; + $this->width = $commonsCachedValues['width']; + $this->height = $commonsCachedValues['height']; + $this->bits = $commonsCachedValues['bits']; + $this->type = $commonsCachedValues['type']; $this->fromSharedDirectory = true; - $gis = $commonsCachedValues['gis']; - $foundCached = true; + $this->dataLoaded = true; } } - else { - $this->name = $cachedValues['name']; - $this->imagePath = $cachedValues['imagePath']; - $this->fileExists = $cachedValues['fileExists']; - $this->fromSharedDirectory = false; - $gis = $cachedValues['gis']; - $foundCached = true; - } } - } - - if (!$foundCached) { - $this->fileExists = file_exists( $this->imagePath); - - # If the file is not found, and a shared upload directory - # like the Wikimedia Commons is used, look for it there. - if (!$this->fileExists && $wgUseSharedUploads) { - - # 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; - $name=$this->name; + else { + $this->name = $cachedValues['name']; + $this->imagePath = $cachedValues['imagePath']; + $this->fileExists = $cachedValues['fileExists']; + $this->width = $cachedValues['width']; + $this->height = $cachedValues['height']; + $this->bits = $cachedValues['bits']; + $this->type = $cachedValues['type']; + $this->fromSharedDirectory = false; + $this->dataLoaded = true; } + } - if ( $this->fileExists ) { - # Don't try to get the size of sound and video files, that's bad for performance - if ( !Image::isKnownImageExtension( $this->extension ) ) { - $gis = false; - } elseif( $this->extension == 'svg' ) { - wfSuppressWarnings(); - $gis = getSVGsize( $this->imagePath ); - wfRestoreWarnings(); - } else { - wfSuppressWarnings(); - $gis = getimagesize( $this->imagePath ); - wfRestoreWarnings(); - } - } + wfProfileOut( $fname ); + return $this->dataLoaded; + } + /** + * Save the image metadata to memcached + */ + function saveToCache() { + global $wgMemc; + $this->load(); + // We can't cache metadata for non-existent files, because if the file later appears + // in commons, the local keys won't be purged. + if ( $this->fileExists ) { + $keys = $this->getCacheKeys(); + $cachedValues = array('name' => $this->name, 'imagePath' => $this->imagePath, 'fileExists' => $this->fileExists, 'fromShared' => $this->fromSharedDirectory, - 'gis' => $gis); + 'width' => $this->width, + 'height' => $this->height, + 'bits' => $this->bits, + 'type' => $this->type); - $wgMemc->set( $cacheKey, $cachedValues ); + $wgMemc->set( $keys[0], $cachedValues ); + } + } + + /** + * Load metadata from the file itself + */ + function loadFromFile() { + global $wgUseSharedUploads, $wgSharedUploadDirectory; + $fname = 'Image::loadFromFile'; + wfProfileIn( $fname ); + $this->imagePath = $this->getFullPath(); + $this->fileExists = file_exists( $this->imagePath ); + $gis = false; - if ($wgUseSharedUploads && $this->fromSharedDirectory) { - $cachedValues['fromShared'] = false; - $wgMemc->set( "$wgSharedUploadDBname:Image:".$hashedName, $cachedValues ); - } + # If the file is not found, and a non-wiki 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; } - if( $gis !== false ) { + if ( $this->fileExists ) { + # Get size in bytes + $s = stat( $this->imagePath ); + $this->size = $s['size']; + + # Height and width + # Don't try to get the width and height of sound and video files, that's bad for performance + if ( !Image::isKnownImageExtension( $this->extension ) ) { + $gis = false; + } elseif( $this->extension == 'svg' ) { + wfSuppressWarnings(); + $gis = wfGetSVGsize( $this->imagePath ); + wfRestoreWarnings(); + } else { + wfSuppressWarnings(); + $gis = getimagesize( $this->imagePath ); + wfRestoreWarnings(); + } + } + if( $gis === false ) { + $this->width = 0; + $this->height = 0; + $this->bits = 0; + $this->type = 0; + } else { $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->dataLoaded = true; + wfProfileOut( $fname ); + } - if($this->fileExists) { - $this->url = $this->wfImageUrl( $this->name, $this->fromSharedDirectory ); - } else { - $this->url=''; + /** + * Load image metadata from the DB + */ + function loadFromDB() { + global $wgUseSharedUploads, $wgSharedUploadDBname, $wgLang; + $fname = 'Image::loadFromDB'; + wfProfileIn( $fname ); + + $dbr =& wfGetDB( DB_SLAVE ); + $row = $dbr->selectRow( 'image', + array( 'img_size', 'img_width', 'img_height', 'img_bits', 'img_type' ), + array( 'img_name' => $this->name ), $fname ); + if ( $row ) { + $this->fromSharedDirectory = false; + $this->fileExists = true; + $this->loadFromRow( $row ); + $this->imagePath = $this->getFullPath(); + } elseif ( $wgUseSharedUploads && $wgSharedUploadDBname ) { + # 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. + $name = $wgLang->ucfirst($this->name); + + $row = $dbr->selectRow( "`$wgSharedUploadDBname`.image", + array( 'img_size', 'img_width', 'img_height', 'img_bits', 'img_type' ), + array( 'img_name' => $name ), $fname ); + if ( $row ) { + $this->fromSharedDirectory = true; + $this->fileExists = true; + $this->imagePath = ''; + $this->name = $name; + $this->loadFromRow( $row ); + } } - $this->historyLine = 0; + + if ( !$row ) { + $this->size = 0; + $this->width = 0; + $this->height = 0; + $this->bits = 0; + $this->type = 0; + $this->fileExists = false; + $this->fromSharedDirectory = false; + } + + # Unconditionally set loaded=true, we don't want the accessors constantly rechecking + $this->dataLoaded = true; } - /** - * Remove image metadata from cache if any - * - * Don't call this, use the $recache parameter of Image::Image() instead - * - * @param string $name the title of an image - * @static + /* + * Load image metadata from a DB result row */ - function invalidateMetadataCache( $name ) { - global $wgMemc, $wgDBname; - $wgMemc->delete("$wgDBname:Image:".md5($name)); + function loadFromRow( &$row ) { + $this->size = $row->img_size; + $this->width = $row->img_width; + $this->height = $row->img_height; + $this->bits = $row->img_bits; + $this->type = $row->img_type; + $this->dataLoaded = true; } /** - * Factory function - * - * Create a new image object from a title object. - * - * @param Title $nt Title object. Must be from namespace "image" - * @access public + * Load image metadata from cache or DB, unless already loaded */ - function newFromTitle( $nt ) { - $img = new Image( $nt->getDBKey() ); - $img->title = $nt; - return $img; + function load() { + if ( !$this->dataLoaded ) { + if ( !$this->loadFromCache() ) { + $this->loadFromDB(); + if ( $this->fileExists ) { + $this->saveToCache(); + } + } + $this->dataLoaded = true; + } } /** @@ -199,6 +309,14 @@ class Image * @access public */ function getURL() { + if ( !$this->url ) { + $this->load(); + if($this->fileExists) { + $this->url = Image::imageUrl( $this->name, $this->fromSharedDirectory ); + } else { + $this->url = ''; + } + } return $this->url; } @@ -215,8 +333,8 @@ class Image * local file system as an absolute path * @access public */ - function getImagePath() - { + function getImagePath() { + $this->load(); return $this->imagePath; } @@ -227,6 +345,7 @@ class Image * @access public */ function getWidth() { + $this->load(); return $this->width; } @@ -237,6 +356,7 @@ class Image * @access public */ function getHeight() { + $this->load(); return $this->height; } @@ -245,12 +365,8 @@ class Image * @access public */ function getSize() { - $st = stat( $this->getImagePath() ); - if( $st ) { - return $st['size']; - } else { - return false; - } + $this->load(); + return $this->size; } /** @@ -263,6 +379,7 @@ class Image * - 16 XBM */ function getType() { + $this->load(); return $this->type; } @@ -271,6 +388,7 @@ class Image * @access public */ function getEscapeLocalURL() { + $this->getTitle(); return $this->title->escapeLocalURL(); } @@ -279,6 +397,7 @@ class Image * @access public */ function getEscapeFullURL() { + $this->getTitle(); return $this->title->escapeFullURL(); } @@ -288,8 +407,9 @@ class Image * @param string $name Name of the image, without the leading "Image:" * @param boolean $fromSharedDirectory Should this be in $wgSharedUploadPath? * @access public + * @static */ - function wfImageUrl( $name, $fromSharedDirectory = false ) { + function imageUrl( $name, $fromSharedDirectory = false ) { global $wgUploadPath,$wgUploadBaseUrl,$wgSharedUploadPath; if($fromSharedDirectory) { $base = ''; @@ -303,11 +423,12 @@ class Image } /** - * Returns true iff the image file exists on disk. + * Returns true if the image file exists on disk. * * @access public */ function exists() { + $this->load(); return $this->fileExists; } @@ -616,6 +737,7 @@ class Image * @return bool */ function mustRender() { + $this->load(); return ( $this->extension == 'svg' ); } @@ -652,6 +774,113 @@ class Image static $extensions = array( 'svg', 'png', 'jpg', 'jpeg', 'gif', 'bmp', 'xbm' ); return in_array( $ext, $extensions ); } + + /** + * Record an image upload in the upload log and the image table + */ + function recordUpload( $oldver, $desc, $copyStatus = '', $source = '' ) { + global $wgUser, $wgLang, $wgTitle, $wgOut, $wgDeferredUpdateList; + global $wgUseCopyrightUpload; + + $fname = 'Image::recordUpload'; + $dbw =& wfGetDB( DB_MASTER ); + + # img_name must be unique + if ( !$dbw->indexUnique( 'image', 'img_name' ) && !$dbw->indexExists('image','PRIMARY') ) { + 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(); + + // Fail now if the image isn't there + if ( !$this->fileExists || $this->fromSharedDirectory ) { + return false; + } + + if ( $wgUseCopyrightUpload ) { + $textdesc = '== ' . wfMsg ( 'filedesc' ) . " ==\n" . $desc . "\n" . + '== ' . wfMsg ( 'filestatus' ) . " ==\n" . $copyStatus . "\n" . + '== ' . wfMsg ( 'filesource' ) . " ==\n" . $source ; + } else { + $textdesc = $desc; + } + + $now = wfTimestampNow(); + + # 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' => $this->name, + 'img_size'=> $this->size, + 'img_width' => $this->width, + 'img_height' => $this->height, + 'img_bits' => $this->bits, + 'img_type' => $this->type, + 'img_timestamp' => $dbw->timestamp($now), + 'img_description' => $desc, + 'img_user' => $wgUser->getID(), + 'img_user_text' => $wgUser->getName(), + ), $fname, 'IGNORE' + ); + $descTitle = $this->getTitle(); + + if ( $dbw->affectedRows() ) { + # Successfully inserted, this is a new image + $id = $descTitle->getArticleID(); + + if ( $id == 0 ) { + $article = new Article( $descTitle ); + $article->insertNewArticle( $textdesc, $desc, false, false, true ); + } + } else { + # Collision, this is an update of an image + # Insert previous contents into oldimage + $dbw->insertSelect( 'oldimage', 'image', + array( + 'oi_name' => 'img_name', + 'oi_archive_name' => $dbw->addQuotes( $oldver ), + 'oi_size' => 'img_size', + 'oi_width' => 'img_width', + 'oi_height' => 'img_height', + 'oi_bits' => 'img_bits', + 'oi_type' => 'img_type', + 'oi_timestamp' => 'img_timestamp', + 'oi_description' => 'img_description', + 'oi_user' => 'img_user', + 'oi_user_text' => 'img_user_text', + ), array( 'img_name' => $this->name ), $fname + ); + + # Update the current image row + $dbw->update( 'image', + array( /* SET */ + 'img_size' => $this->size, + 'img_width' => $this->width, + 'img_height' => $this->height, + 'img_bits' => $this->bits, + 'img_type' => $this->type, + 'img_timestamp' => $dbw->timestamp(), + 'img_user' => $wgUser->getID(), + 'img_user_text' => $wgUser->getName(), + 'img_description' => $desc, + ), array( /* WHERE */ + 'img_name' => $this->name + ), $fname + ); + + # Invalidate the cache for the description page + $descTitle->invalidateCache(); + } + + $log = new LogPage( 'upload' ); + $log->addEntry( 'upload', $descTitle, $desc ); + + return true; + } } //class @@ -748,98 +977,6 @@ function wfGetHashPath ( $dbkey, $fromSharedDirectory = false ) { } } - -/** - * Record an image upload in the upload log. - */ -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' ) && !$dbw->indexExists('image','PRIMARY') ) { - wfDebugDieBacktrace( 'Database schema not up to date, please run maintenance/archives/patch-image_name_unique.sql' ); - } - - - $now = wfTimestampNow(); - $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(); - - # 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' => $dbw->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 ) { - $article = new Article( $descTitle ); - $article->insertNewArticle( $textdesc, $desc, false, false, true ); - } - } else { - # Collision, this is an update of an image - # Get current image row for update - $s = $dbw->selectRow( '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->insert( 'oldimage', - array( - 'oi_name' => $s->img_name, - 'oi_archive_name' => $oldver, - 'oi_size' => $s->img_size, - 'oi_timestamp' => $dbw->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->update( 'image', - array( /* SET */ - 'img_size' => $size, - 'img_timestamp' => $dbw->timestamp(), - '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 ); -} - /** * Returns the image URL of an image's old version * @@ -867,7 +1004,7 @@ function wfImageArchiveUrl( $name, $subdir='archive' ) { * @param string $length * @return int Length in pixels */ -function scaleSVGUnit( $length ) { +function wfScaleSVGUnit( $length ) { static $unitLength = array( 'px' => 1.0, 'pt' => 1.25, @@ -897,7 +1034,7 @@ function scaleSVGUnit( $length ) { * @param string $filename * @return array */ -function getSVGsize( $filename ) { +function wfGetSVGsize( $filename ) { $width = 256; $height = 256; @@ -913,10 +1050,10 @@ function getSVGsize( $filename ) { } $tag = $matches[1]; if( preg_match( '/\bwidth\s*=\s*("[^"]+"|\'[^\']+\')/s', $tag, $matches ) ) { - $width = scaleSVGUnit( trim( substr( $matches[1], 1, -1 ) ) ); + $width = wfScaleSVGUnit( trim( substr( $matches[1], 1, -1 ) ) ); } if( preg_match( '/\bheight\s*=\s*("[^"]+"|\'[^\']+\')/s', $tag, $matches ) ) { - $height = scaleSVGUnit( trim( substr( $matches[1], 1, -1 ) ) ); + $height = wfScaleSVGUnit( trim( substr( $matches[1], 1, -1 ) ) ); } return array( $width, $height, 'SVG', @@ -985,7 +1122,7 @@ class ThumbnailImage { * can't be stat(). * @access public */ - function getSize() { + function getSize() { $st = stat( $this->path ); if( $st ) { return $st['size']; diff --git a/includes/ImageGallery.php b/includes/ImageGallery.php index 8ee152a282..4cbec9419e 100644 --- a/includes/ImageGallery.php +++ b/includes/ImageGallery.php @@ -124,7 +124,6 @@ class ImageGallery $nb = ''; } - '' ; $textlink = $this->mShowFilename ? $sk->makeKnownLinkObj( $nt, htmlspecialchars( $wgLang->truncate( $nt->getText(), 20, '...' ) ) ) . '
' : '' ; diff --git a/includes/ImagePage.php b/includes/ImagePage.php index 7a9209ebe8..b117d42c17 100644 --- a/includes/ImagePage.php +++ b/includes/ImagePage.php @@ -17,7 +17,7 @@ require_once( 'Image.php' ); */ class ImagePage extends Article { - /* private */ var $img; // Image object this page is shown for. Initilaized in openShowImage, not + /* private */ var $img; // Image object this page is shown for. Initialized in openShowImage, not // available in doDelete etc. function view() { @@ -47,7 +47,7 @@ class ImagePage extends Article { global $wgOut, $wgUser, $wgImageLimits, $wgRequest, $wgUseImageResize, $wgRepositoryBaseUrl, $wgUseExternalEditor; - $this->img = Image::newFromTitle( $this->mTitle ); + $this->img = new Image( $this->mTitle ); $full_url = $this->img->getViewURL(); $anchoropen = ''; $anchorclose = ''; @@ -270,10 +270,15 @@ class ImagePage extends Article { $wgOut->unexpectedValueError( 'oldimage', htmlspecialchars($oldimage) ); return; } + + # Invalidate description page cache + $this->mTitle->invalidateCache(); + # Squid purging if ( $wgUseSquid ) { $urlArr = Array( - $wgInternalServer.wfImageArchiveUrl( $oldimage ) + $wgInternalServer.wfImageArchiveUrl( $oldimage ), + $wgInternalServer.$this->mTitle->getFullURL() ); wfPurgeSquidServers($urlArr); } @@ -299,7 +304,7 @@ class ImagePage extends Article { # Squid purging if ( $wgUseSquid ) { $urlArr = Array( - $wgInternalServer . Image::wfImageUrl( $image ) + $wgInternalServer . Image::imageUrl( $image ) ); wfPurgeSquidServers($urlArr); } @@ -323,12 +328,13 @@ class ImagePage extends Article { # Image itself is now gone, and database is cleaned. # Now we remove the image description page. - $nt = Title::makeTitleSafe( NS_IMAGE, $image ); - $article = new Article( $nt ); + $article = new Article( $this->mTitle ); $article->doDeleteArticle( $reason ); # ignore errors /* refresh image metadata cache */ - new Image( $image, true ); + $imgObj = new Image( $this->mTitle ); + $imgObj->loadFromFile(); + $imgObj->saveToCache(); $deleted = $image; } @@ -338,13 +344,13 @@ class ImagePage extends Article { $sk = $wgUser->getSkin(); $loglink = $sk->makeKnownLinkObj( - Title::makeTitle( NS_SPECIAL, 'Delete/log' ), + Title::makeTitle( NS_SPECIAL, 'Log/delete' ), wfMsg( 'deletionlog' ) ); $text = wfMsg( 'deletedtext', $deleted, $loglink ); $wgOut->addHTML( '

' . $text . "

\n" ); - $wgOut->returnToMain( false ); + $wgOut->returnToMain( false, $this->mTitle->getPrefixedText() ); } function doDeleteOldImage( $oldimage ) @@ -416,8 +422,7 @@ class ImagePage extends Article { $oldver = wfTimestampNow() . "!{$name}"; $dbr =& wfGetDB( DB_SLAVE ); - $size = $dbr->selectField( 'oldimage', 'oi_size', 'oi_archive_name=\'' . - $dbr->strencode( $oldimage ) . "'" ); + $size = $dbr->selectField( 'oldimage', 'oi_size', array( 'oi_archive_name' => $oldimage ) ); if ( ! rename( $curfile, "${archive}/{$oldver}" ) ) { $wgOut->fileRenameError( $curfile, "${archive}/{$oldver}" ); @@ -426,16 +431,16 @@ class ImagePage extends Article { if ( ! copy( "{$archive}/{$oldimage}", $curfile ) ) { $wgOut->fileCopyError( "${archive}/{$oldimage}", $curfile ); } - wfRecordUpload( $name, $oldver, $size, wfMsg( "reverted" ) ); - /* refresh image metadata cache */ - new Image( $name, true ); + # Record upload and update metadata cache + $img = Image::newFromName( $name ); + $img->recordUpload( $oldver, wfMsg( "reverted" ) ); # Squid purging if ( $wgUseSquid ) { $urlArr = Array( $wgInternalServer.wfImageArchiveUrl( $name ), - $wgInternalServer . Image::wfImageUrl( $name ) + $wgInternalServer . Image::imageUrl( $name ), ); wfPurgeSquidServers($urlArr); } @@ -443,7 +448,9 @@ class ImagePage extends Article { $wgOut->setPagetitle( wfMsg( 'actioncomplete' ) ); $wgOut->setRobotpolicy( 'noindex,nofollow' ); $wgOut->addHTML( wfMsg( 'imagereverted' ) ); - $wgOut->returnToMain( false ); + + $descTitle = $img->getTitle(); + $wgOut->returnToMain( false, $descTitle->getPrefixedText() ); } } @@ -476,7 +483,7 @@ class ImageHistoryList { $cur = wfMsg( 'cur' ); if ( $iscur ) { - $url = Image::wfImageUrl( $img ); + $url = Image::imageUrl( $img ); $rlink = $cur; if ( $wgUser->isAllowed('delete') ) { $link = $wgTitle->escapeLocalURL( 'image=' . $wgTitle->getPartialURL() . diff --git a/includes/Linker.php b/includes/Linker.php index f830819c8e..a035471d37 100644 --- a/includes/Linker.php +++ b/includes/Linker.php @@ -396,7 +396,7 @@ class Linker { /** @todo document */ function makeImageLinkObj( $nt, $alt = '' ) { global $wgContLang, $wgUseImageResize; - $img = Image::newFromTitle( $nt ); + $img = new Image( $nt ); $url = $img->getViewURL(); $align = ''; @@ -567,7 +567,7 @@ class Linker { if ( $manual_thumb != '' ) # Use manually specified thumbnail { $manual_title = Title::makeTitleSafe( NS_IMAGE, $manual_thumb ); #new Title ( $manual_thumb ) ; - $manual_img = Image::newFromTitle( $manual_title ); + $manual_img = new Image( $manual_title ); $thumbUrl = $manual_img->getViewURL(); if ( $manual_img->exists() ) { @@ -631,7 +631,7 @@ class Linker { return $text; } else { $name = $title->getDBKey(); - $img = Image::newFromTitle( $title ); + $img = new Image( $title ); $url = $img->getURL(); if( $nourl ) { $url = str_replace( "http://", "http-noparse://", $url ); diff --git a/includes/Parser.php b/includes/Parser.php index 7fe8bce954..e45f745f0e 100644 --- a/includes/Parser.php +++ b/includes/Parser.php @@ -2906,7 +2906,7 @@ class Parser $html = $wgParser->parse( $label , $wgTitle, $parserOptions ); $html = $html->mText; - $ig->add( Image::newFromTitle( $nt ), $html ); + $ig->add( new Image( $nt ), $html ); $wgLinkCache->addImageLinkObj( $nt ); } return $ig->toHTML(); diff --git a/includes/Skin.php b/includes/Skin.php index daf3ba4248..03d2cd095a 100644 --- a/includes/Skin.php +++ b/includes/Skin.php @@ -519,7 +519,7 @@ END; if ( $wgOut->isArticleRelated() ) { if ( $wgTitle->getNamespace() == NS_IMAGE ) { $name = $wgTitle->getDBkey(); - $image = new Image( $wgTitle->getDBkey() ); + $image = new Image( $wgTitle ); if( $image->exists() ) { $link = htmlspecialchars( $image->getURL() ); $style = $this->getInternalLinkAttributes( $link, $name ); diff --git a/includes/SpecialImagelist.php b/includes/SpecialImagelist.php index 277ed15a3c..b889cf753f 100644 --- a/includes/SpecialImagelist.php +++ b/includes/SpecialImagelist.php @@ -115,7 +115,7 @@ function wfSpecialImagelist() { $ul = $sk->makeLinkObj( Title::makeTitle( NS_USER, $ut ), $ut ); } - $ilink = "" . htmlspecialchars( $name ) . ""; $nb = wfMsg( "nbytes", $wgLang->formatNum( $s->img_size ) ); diff --git a/includes/SpecialUnusedimages.php b/includes/SpecialUnusedimages.php index 6d6cb2fcbd..3b7459f71f 100644 --- a/includes/SpecialUnusedimages.php +++ b/includes/SpecialUnusedimages.php @@ -35,7 +35,7 @@ class UnusedimagesPage extends QueryPage { global $wgLang, $wgContLang; $title = Title::makeTitle( NS_IMAGE, $result->title ); - $imageUrl = htmlspecialchars( Image::wfImageUrl( $result->title ) ); + $imageUrl = htmlspecialchars( Image::imageUrl( $result->title ) ); $return = # The 'desc' linking to the image page '('.$skin->makeKnownLinkObj( $title, wfMsg('imgdesc') ).') ' diff --git a/includes/SpecialUpload.php b/includes/SpecialUpload.php index 89509de668..de2199acf2 100644 --- a/includes/SpecialUpload.php +++ b/includes/SpecialUpload.php @@ -266,17 +266,19 @@ class UploadForm { * Update the upload log and create the description page * if it's a new file. */ - wfRecordUpload( $this->mUploadSaveName, - $this->mUploadOldVersion, - $this->mUploadSize, - $this->mUploadDescription, - $this->mUploadCopyStatus, - $this->mUploadSource ); - - /* refresh image metadata cache */ - new Image( $this->mUploadSaveName, true ); - - $this->showSuccess(); + $img = Image::newFromName( $this->mUploadSaveName ); + $success = $img->recordUpload( $this->mUploadOldVersion, + $this->mUploadDescription, + $this->mUploadCopyStatus, + $this->mUploadSource ); + + if ( $success ) { + $this->showSuccess(); + } else { + // Image::recordUpload() fails if the image went missing, which is + // unlikely, hence the lack of a specialised message + $wgOut->fileNotFoundError( $this->mUploadSaveName ); + } } } @@ -411,7 +413,7 @@ class UploadForm { global $wgUser, $wgOut, $wgContLang; $sk = $wgUser->getSkin(); - $ilink = $sk->makeMediaLink( $this->mUploadSaveName, Image::wfImageUrl( $this->mUploadSaveName ) ); + $ilink = $sk->makeMediaLink( $this->mUploadSaveName, Image::imageUrl( $this->mUploadSaveName ) ); $dname = $wgContLang->getNsText( NS_IMAGE ) . ':'.$this->mUploadSaveName; $dlink = $sk->makeKnownLink( $dname, $dname ); diff --git a/maintenance/archives/patch-img_width.sql b/maintenance/archives/patch-img_width.sql new file mode 100644 index 0000000000..b622a8f604 --- /dev/null +++ b/maintenance/archives/patch-img_width.sql @@ -0,0 +1,17 @@ +-- Extra image metadata, added for 1.5 + +ALTER TABLE /*$wgDBprefix*/image ADD ( + img_width int(5) NOT NULL default '0', + img_height int(5) NOT NULL default '0', + img_bits int(5) NOT NULL default '0', + img_type int(5) NOT NULL default '0' +); + +ALTER TABLE /*$wgDBprefix*/oldimage ADD ( + oi_width int(5) NOT NULL default 0, + oi_height int(5) NOT NULL default 0, + oi_bits int(3) NOT NULL default 0, + oi_type int(3) NOT NULL default 0, +); + + diff --git a/maintenance/tables.sql b/maintenance/tables.sql index f5fdae3207..b02d7612a0 100644 --- a/maintenance/tables.sql +++ b/maintenance/tables.sql @@ -198,10 +198,15 @@ CREATE TABLE /*$wgDBprefix*/ipblocks ( CREATE TABLE /*$wgDBprefix*/image ( img_name varchar(255) binary NOT NULL default '', img_size int(8) unsigned NOT NULL default '0', + img_width int(5) NOT NULL default '0', + img_height int(5) NOT NULL default '0', + img_bits int(3) NOT NULL default '0', + img_type int(3) NOT NULL default '0', img_description tinyblob NOT NULL default '', img_user int(5) unsigned NOT NULL default '0', img_user_text varchar(255) binary NOT NULL default '', img_timestamp char(14) binary NOT NULL default '', + PRIMARY KEY img_name (img_name), INDEX img_size (img_size), INDEX img_timestamp (img_timestamp) @@ -211,10 +216,15 @@ CREATE TABLE /*$wgDBprefix*/oldimage ( oi_name varchar(255) binary NOT NULL default '', oi_archive_name varchar(255) binary NOT NULL default '', oi_size int(8) unsigned NOT NULL default 0, + oi_width int(5) NOT NULL default 0, + oi_height int(5) NOT NULL default 0, + oi_bits int(3) NOT NULL default 0, + oi_type int(3) NOT NULL default 0, oi_description tinyblob NOT NULL default '', oi_user int(5) unsigned NOT NULL default '0', oi_user_text varchar(255) binary NOT NULL default '', oi_timestamp char(14) binary NOT NULL default '', + INDEX oi_name (oi_name(10)) ); diff --git a/maintenance/updaters.inc b/maintenance/updaters.inc index d8e967642d..c6eefb3ac5 100644 --- a/maintenance/updaters.inc +++ b/maintenance/updaters.inc @@ -37,6 +37,7 @@ $wgNewFields = array( array( 'archive', 'ar_rev_id', 'patch-archive-rev_id.sql' ), array( 'page', 'page_len', 'patch-page_len.sql' ), array( 'revision', 'rev_deleted', 'patch-rev_deleted.sql' ), + array( 'image', 'img_width', 'patch-img_width.sql' ), ); function add_table( $name, $patch ) {