summaryrefslogtreecommitdiff
path: root/includes/filerepo
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2009-02-22 13:37:51 +0100
committerPierre Schmitz <pierre@archlinux.de>2009-02-22 13:37:51 +0100
commitb9b85843572bf283f48285001e276ba7e61b63f6 (patch)
tree4c6f4571552ada9ccfb4030481dcf77308f8b254 /includes/filerepo
parentd9a20acc4e789cca747ad360d87ee3f3e7aa58c1 (diff)
updated to MediaWiki 1.14.0
Diffstat (limited to 'includes/filerepo')
-rw-r--r--includes/filerepo/ArchivedFile.php44
-rw-r--r--includes/filerepo/FSRepo.php10
-rw-r--r--includes/filerepo/File.php26
-rw-r--r--includes/filerepo/FileCache.php156
-rw-r--r--includes/filerepo/FileRepo.php80
-rw-r--r--includes/filerepo/ForeignAPIFile.php83
-rw-r--r--includes/filerepo/ForeignAPIRepo.php77
-rw-r--r--includes/filerepo/ForeignDBFile.php2
-rw-r--r--includes/filerepo/Image.php2
-rw-r--r--includes/filerepo/LocalFile.php99
-rw-r--r--includes/filerepo/LocalRepo.php7
-rw-r--r--includes/filerepo/OldLocalFile.php3
-rw-r--r--includes/filerepo/RepoGroup.php24
-rw-r--r--includes/filerepo/UnregisteredLocalFile.php2
14 files changed, 482 insertions, 133 deletions
diff --git a/includes/filerepo/ArchivedFile.php b/includes/filerepo/ArchivedFile.php
index 646256bb..3919cfbc 100644
--- a/includes/filerepo/ArchivedFile.php
+++ b/includes/filerepo/ArchivedFile.php
@@ -30,12 +30,9 @@ class ArchivedFile
/**#@-*/
function ArchivedFile( $title, $id=0, $key='' ) {
- if( !is_object($title) ) {
- throw new MWException( 'ArchivedFile constructor given bogus title.' );
- }
$this->id = -1;
- $this->title = $title;
- $this->name = $title->getDBkey();
+ $this->title = false;
+ $this->name = false;
$this->group = '';
$this->key = '';
$this->size = 0;
@@ -51,6 +48,20 @@ class ArchivedFile
$this->timestamp = NULL;
$this->deleted = 0;
$this->dataLoaded = false;
+
+ if( is_object($title) ) {
+ $this->title = $title;
+ $this->name = $title->getDBkey();
+ }
+
+ if ($id)
+ $this->id = $id;
+
+ if ($key)
+ $this->key = $key;
+
+ if (!$id && !$key && !is_object($title))
+ throw new MWException( "No specifications provided to ArchivedFile constructor." );
}
/**
@@ -61,8 +72,19 @@ class ArchivedFile
if ( $this->dataLoaded ) {
return true;
}
- $conds = ($this->id) ? "fa_id = {$this->id}" : "fa_storage_key = '{$this->key}'";
- if( $this->title->getNamespace() == NS_IMAGE ) {
+ $conds = array();
+
+ if ($this->id>0)
+ $conds['fa_id'] = $this->id;
+ if ($this->key)
+ $conds['fa_storage_key'] = $this->key;
+ if ($this->title)
+ $conds['fa_name'] = $this->title->getDBkey();
+
+ if (!count($conds))
+ throw new MWException( "No specific information for retrieving archived file" );
+
+ if( !$this->title || $this->title->getNamespace() == NS_FILE ) {
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'filearchive',
array(
@@ -84,9 +106,7 @@ class ArchivedFile
'fa_user_text',
'fa_timestamp',
'fa_deleted' ),
- array(
- 'fa_name' => $this->title->getDBkey(),
- $conds ),
+ $conds,
__METHOD__,
array( 'ORDER BY' => 'fa_timestamp DESC' ) );
@@ -129,7 +149,7 @@ class ArchivedFile
* @return ResultWrapper
*/
public static function newFromRow( $row ) {
- $file = new ArchivedFile( Title::makeTitle( NS_IMAGE, $row->fa_name ) );
+ $file = new ArchivedFile( Title::makeTitle( NS_FILE, $row->fa_name ) );
$file->id = intval($row->fa_id);
$file->name = $row->fa_name;
@@ -251,7 +271,7 @@ class ArchivedFile
*/
public function getTimestamp() {
$this->load();
- return $this->timestamp;
+ return wfTimestamp( TS_MW, $this->timestamp );
}
/**
diff --git a/includes/filerepo/FSRepo.php b/includes/filerepo/FSRepo.php
index eb8df0f5..d561e61b 100644
--- a/includes/filerepo/FSRepo.php
+++ b/includes/filerepo/FSRepo.php
@@ -6,7 +6,7 @@
* @ingroup FileRepo
*/
class FSRepo extends FileRepo {
- var $directory, $deletedDir, $url, $hashLevels, $deletedHashLevels;
+ var $directory, $deletedDir, $url, $deletedHashLevels;
var $fileFactory = array( 'UnregisteredLocalFile', 'newFromTitle' );
var $oldFileFactory = false;
var $pathDisclosureProtection = 'simple';
@@ -452,14 +452,6 @@ class FSRepo extends FileRepo {
}
/**
- * Get a relative path including trailing slash, e.g. f/fa/
- * If the repo is not hashed, returns an empty string
- */
- function getHashPath( $name ) {
- return FileRepo::getHashPathForLevel( $name, $this->hashLevels );
- }
-
- /**
* Get a relative path for a deletion archive key,
* e.g. s/z/a/ for sza251lrxrc1jad41h5mgilp8nysje52.jpg
*/
diff --git a/includes/filerepo/File.php b/includes/filerepo/File.php
index 64b48e0a..4f0990af 100644
--- a/includes/filerepo/File.php
+++ b/includes/filerepo/File.php
@@ -264,7 +264,14 @@ abstract class File {
* Overridden by LocalFile, UnregisteredLocalFile
* STUB
*/
- function getMetadata() { return false; }
+ public function getMetadata() { return false; }
+
+ /**
+ * Return the bit depth of the file
+ * Overridden by LocalFile
+ * STUB
+ */
+ public function getBitDepth() { return 0; }
/**
* Return the size of the image file, in bytes
@@ -499,8 +506,7 @@ abstract class File {
*
* @param integer $width maximum width of the generated thumbnail
* @param integer $height maximum height of the image (optional)
- * @param boolean $render True to render the thumbnail if it doesn't exist,
- * false to just return the URL
+ * @param boolean $render Deprecated
*
* @return ThumbnailImage or null on failure
*
@@ -511,8 +517,7 @@ abstract class File {
if ( $height != -1 ) {
$params['height'] = $height;
}
- $flags = $render ? self::RENDER_NOW : 0;
- return $this->transform( $params, $flags );
+ return $this->transform( $params, 0 );
}
/**
@@ -575,7 +580,7 @@ abstract class File {
// Purge. Useful in the event of Core -> Squid connection failure or squid
// purge collisions from elsewhere during failure. Don't keep triggering for
// "thumbs" which have the main image URL though (bug 13776)
- if ( $wgUseSquid && ($thumb->isError() || $thumb->getUrl() != $this->getURL()) ) {
+ if ( $wgUseSquid && ( !$thumb || $thumb->isError() || $thumb->getUrl() != $this->getURL()) ) {
SquidUpdate::purge( array( $thumbUrl ) );
}
} while (false);
@@ -678,8 +683,9 @@ abstract class File {
* @param $limit integer Limit of rows to return
* @param $start timestamp Only revisions older than $start will be returned
* @param $end timestamp Only revisions newer than $end will be returned
+ * @param $inc bool Include the endpoints of the time range
*/
- function getHistory($limit = null, $start = null, $end = null) {
+ function getHistory($limit = null, $start = null, $end = null, $inc=true) {
return array();
}
@@ -1212,7 +1218,7 @@ abstract class File {
if ( $handler ) {
return $handler->getLongDesc( $this );
} else {
- return MediaHandler::getLongDesc( $this );
+ return MediaHandler::getGeneralLongDesc( $this );
}
}
@@ -1221,7 +1227,7 @@ abstract class File {
if ( $handler ) {
return $handler->getShortDesc( $this );
} else {
- return MediaHandler::getShortDesc( $this );
+ return MediaHandler::getGeneralShortDesc( $this );
}
}
@@ -1241,7 +1247,7 @@ abstract class File {
function getRedirectedTitle() {
if ( $this->redirected ) {
if ( !$this->redirectTitle )
- $this->redirectTitle = Title::makeTitle( NS_IMAGE, $this->redirected );
+ $this->redirectTitle = Title::makeTitle( NS_FILE, $this->redirected );
return $this->redirectTitle;
}
}
diff --git a/includes/filerepo/FileCache.php b/includes/filerepo/FileCache.php
new file mode 100644
index 00000000..7840d1a3
--- /dev/null
+++ b/includes/filerepo/FileCache.php
@@ -0,0 +1,156 @@
+<?php
+/**
+ * Cache of file objects, wrapping some RepoGroup functions to avoid redundant
+ * queries. Loosely inspired by the LinkCache / LinkBatch classes for titles.
+ *
+ * ISSUE: Merge with RepoGroup?
+ *
+ * @ingroup FileRepo
+ */
+class FileCache {
+ var $repoGroup;
+ var $cache = array(), $notFound = array();
+
+ protected static $instance;
+
+ /**
+ * Get a FileCache instance. Typically, only one instance of FileCache
+ * is needed in a MediaWiki invocation.
+ */
+ static function singleton() {
+ if ( self::$instance ) {
+ return self::$instance;
+ }
+ self::$instance = new FileCache( RepoGroup::singleton() );
+ return self::$instance;
+ }
+
+ /**
+ * Destroy the singleton instance, so that a new one will be created next
+ * time singleton() is called.
+ */
+ static function destroySingleton() {
+ self::$instance = null;
+ }
+
+ /**
+ * Set the singleton instance to a given object
+ */
+ static function setSingleton( $instance ) {
+ self::$instance = $instance;
+ }
+
+ /**
+ * Construct a group of file repositories.
+ * @param RepoGroup $repoGroup
+ */
+ function __construct( $repoGroup ) {
+ $this->repoGroup = $repoGroup;
+ }
+
+
+ /**
+ * Add some files to the cache. This is a fairly low-level function,
+ * which most users should not need to call. Note that any existing
+ * entries for the same keys will not be replaced. Call clearFiles()
+ * first if you need that.
+ * @param array $files array of File objects, indexed by DB key
+ */
+ function addFiles( $files ) {
+ wfDebug( "FileCache adding ".count( $files )." files\n" );
+ $this->cache += $files;
+ }
+
+ /**
+ * Remove some files from the cache, so that their existence will be
+ * rechecked. This is a fairly low-level function, which most users
+ * should not need to call.
+ * @param array $remove array indexed by DB keys to remove (the values are ignored)
+ */
+ function clearFiles( $remove ) {
+ wfDebug( "FileCache clearing data for ".count( $remove )." files\n" );
+ $this->cache = array_diff_keys( $this->cache, $remove );
+ $this->notFound = array_diff_keys( $this->notFound, $remove );
+ }
+
+ /**
+ * Mark some DB keys as nonexistent. This is a fairly low-level
+ * function, which most users should not need to call.
+ * @param array $dbkeys array of DB keys
+ */
+ function markNotFound( $dbkeys ) {
+ wfDebug( "FileCache marking ".count( $dbkeys )." files as not found\n" );
+ $this->notFound += array_fill_keys( $dbkeys, true );
+ }
+
+
+ /**
+ * Search the cache for a file.
+ * @param mixed $title Title object or string
+ * @return File object or false if it is not found
+ * @todo Implement searching for old file versions(?)
+ */
+ function findFile( $title ) {
+ if( !( $title instanceof Title ) ) {
+ $title = Title::makeTitleSafe( NS_FILE, $title );
+ }
+ if( !$title ) {
+ return false; // invalid title?
+ }
+
+ $dbkey = $title->getDBkey();
+ if( array_key_exists( $dbkey, $this->cache ) ) {
+ wfDebug( "FileCache HIT for $dbkey\n" );
+ return $this->cache[$dbkey];
+ }
+ if( array_key_exists( $dbkey, $this->notFound ) ) {
+ wfDebug( "FileCache negative HIT for $dbkey\n" );
+ return false;
+ }
+
+ // Not in cache, fall back to a direct query
+ $file = $this->repoGroup->findFile( $title );
+ if( $file ) {
+ wfDebug( "FileCache MISS for $dbkey\n" );
+ $this->cache[$dbkey] = $file;
+ } else {
+ wfDebug( "FileCache negative MISS for $dbkey\n" );
+ $this->notFound[$dbkey] = true;
+ }
+ return $file;
+ }
+
+ /**
+ * Search the cache for multiple files.
+ * @param array $titles Title objects or strings to search for
+ * @return array of File objects, indexed by DB key
+ */
+ function findFiles( $titles ) {
+ $titleObjs = array();
+ foreach ( $titles as $title ) {
+ if ( !( $title instanceof Title ) ) {
+ $title = Title::makeTitleSafe( NS_FILE, $title );
+ }
+ if ( $title ) {
+ $titleObjs[$title->getDBkey()] = $title;
+ }
+ }
+
+ $result = array_intersect_key( $this->cache, $titleObjs );
+
+ $unsure = array_diff_key( $titleObjs, $result, $this->notFound );
+ if( $unsure ) {
+ wfDebug( "FileCache MISS for ".count( $unsure )." files out of ".count( $titleObjs )."...\n" );
+ // XXX: We assume the array returned by findFiles() is
+ // indexed by DBkey; this appears to be true, but should
+ // be explicitly documented.
+ $found = $this->repoGroup->findFiles( $unsure );
+ $result += $found;
+ $this->addFiles( $found );
+ $this->markNotFound( array_keys( array_diff_key( $unsure, $found ) ) );
+ }
+
+ wfDebug( "FileCache found ".count( $result )." files out of ".count( $titleObjs )."\n" );
+ return $result;
+ }
+}
diff --git a/includes/filerepo/FileRepo.php b/includes/filerepo/FileRepo.php
index edfc2a99..5beac732 100644
--- a/includes/filerepo/FileRepo.php
+++ b/includes/filerepo/FileRepo.php
@@ -15,6 +15,7 @@ abstract class FileRepo {
var $thumbScriptUrl, $transformVia404;
var $descBaseUrl, $scriptDirUrl, $articleUrl, $fetchDescription, $initialCapital;
var $pathDisclosureProtection = 'paranoid';
+ var $descriptionCacheExpiry, $apiThumbCacheExpiry, $hashLevels;
/**
* Factory functions for creating new files
@@ -30,7 +31,8 @@ abstract class FileRepo {
// Optional settings
$this->initialCapital = true; // by default
foreach ( array( 'descBaseUrl', 'scriptDirUrl', 'articleUrl', 'fetchDescription',
- 'thumbScriptUrl', 'initialCapital', 'pathDisclosureProtection', 'descriptionCacheExpiry' ) as $var )
+ 'thumbScriptUrl', 'initialCapital', 'pathDisclosureProtection',
+ 'descriptionCacheExpiry', 'apiThumbCacheExpiry', 'hashLevels' ) as $var )
{
if ( isset( $info[$var] ) ) {
$this->$var = $info[$var];
@@ -57,7 +59,7 @@ abstract class FileRepo {
*/
function newFile( $title, $time = false ) {
if ( !($title instanceof Title) ) {
- $title = Title::makeTitleSafe( NS_IMAGE, $title );
+ $title = Title::makeTitleSafe( NS_FILE, $title );
if ( !is_object( $title ) ) {
return null;
}
@@ -83,7 +85,7 @@ abstract class FileRepo {
*/
function findFile( $title, $time = false, $flags = 0 ) {
if ( !($title instanceof Title) ) {
- $title = Title::makeTitleSafe( NS_IMAGE, $title );
+ $title = Title::makeTitleSafe( NS_FILE, $title );
if ( !is_object( $title ) ) {
return false;
}
@@ -99,7 +101,7 @@ abstract class FileRepo {
# Now try an old version of the file
if ( $time !== false ) {
$img = $this->newFile( $title, $time );
- if ( $img->exists() ) {
+ if ( $img && $img->exists() ) {
if ( !$img->isDeleted(File::DELETED_FILE) ) {
return $img;
} else if ( ($flags & FileRepo::FIND_PRIVATE) && $img->userCan(File::DELETED_FILE) ) {
@@ -113,7 +115,7 @@ abstract class FileRepo {
return false;
}
$redir = $this->checkRedirect( $title );
- if( $redir && $redir->getNamespace() == NS_IMAGE) {
+ if( $redir && $redir->getNamespace() == NS_FILE) {
$img = $this->newFile( $redir );
if( !$img ) {
return false;
@@ -129,12 +131,12 @@ abstract class FileRepo {
/*
* Find many files at once.
* @param array $titles, an array of titles
- * @param int $flags
+ * @todo Think of a good way to optionally pass timestamps to this function.
*/
- function findFiles( $titles, $flags ) {
+ function findFiles( $titles ) {
$result = array();
foreach ( $titles as $index => $title ) {
- $file = $this->findFile( $title, $flags );
+ $file = $this->findFile( $title );
if ( $file )
$result[$file->getTitle()->getDBkey()] = $file;
}
@@ -236,6 +238,14 @@ abstract class FileRepo {
return $path;
}
}
+
+ /**
+ * Get a relative path including trailing slash, e.g. f/fa/
+ * If the repo is not hashed, returns an empty string
+ */
+ function getHashPath( $name ) {
+ return self::getHashPathForLevel( $name, $this->hashLevels );
+ }
/**
* Get the name of this repository, as specified by $info['name]' to the constructor
@@ -245,25 +255,6 @@ abstract class FileRepo {
}
/**
- * 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( MWNamespace::getCanonicalName( NS_IMAGE ) ) . ':', $this->articleUrl );
- } elseif ( !is_null( $this->scriptDirUrl ) ) {
- $this->descBaseUrl = $this->scriptDirUrl . '/index.php?title=' .
- wfUrlencode( MWNamespace::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
@@ -273,12 +264,29 @@ abstract class FileRepo {
* constructor, whereas local repositories use the local Title functions.
*/
function getDescriptionUrl( $name ) {
- $base = $this->getDescBaseUrl();
- if ( $base ) {
- return $base . wfUrlencode( $name );
- } else {
- return false;
+ $encName = wfUrlencode( $name );
+ if ( !is_null( $this->descBaseUrl ) ) {
+ # "http://example.com/wiki/Image:"
+ return $this->descBaseUrl . $encName;
+ }
+ if ( !is_null( $this->articleUrl ) ) {
+ # "http://example.com/wiki/$1"
+ #
+ # We use "Image:" as the canonical namespace for
+ # compatibility across all MediaWiki versions.
+ return str_replace( '$1',
+ "Image:$encName", $this->articleUrl );
}
+ if ( !is_null( $this->scriptDirUrl ) ) {
+ # "http://example.com/w"
+ #
+ # We use "Image:" as the canonical namespace for
+ # compatibility across all MediaWiki versions,
+ # and just sort of hope index.php is right. ;)
+ return $this->scriptDirUrl .
+ "/index.php?title=Image:$encName";
+ }
+ return false;
}
/**
@@ -290,12 +298,12 @@ abstract class FileRepo {
function getDescriptionRenderUrl( $name ) {
if ( isset( $this->scriptDirUrl ) ) {
return $this->scriptDirUrl . '/index.php?title=' .
- wfUrlencode( MWNamespace::getCanonicalName( NS_IMAGE ) . ':' . $name ) .
+ wfUrlencode( 'Image:' . $name ) .
'&action=render';
} else {
- $descBase = $this->getDescBaseUrl();
- if ( $descBase ) {
- return wfAppendQuery( $descBase . wfUrlencode( $name ), 'action=render' );
+ $descUrl = $this->getDescriptionUrl( $name );
+ if ( $descUrl ) {
+ return wfAppendQuery( $descUrl, 'action=render' );
} else {
return false;
}
diff --git a/includes/filerepo/ForeignAPIFile.php b/includes/filerepo/ForeignAPIFile.php
index aaf92204..d9fb85d0 100644
--- a/includes/filerepo/ForeignAPIFile.php
+++ b/includes/filerepo/ForeignAPIFile.php
@@ -7,15 +7,19 @@
* @ingroup FileRepo
*/
class ForeignAPIFile extends File {
- function __construct( $title, $repo, $info ) {
+
+ private $mExists;
+
+ function __construct( $title, $repo, $info, $exists = false ) {
parent::__construct( $title, $repo );
$this->mInfo = $info;
+ $this->mExists = $exists;
}
static function newFromTitle( $title, $repo ) {
$info = $repo->getImageInfo( $title );
if( $info ) {
- return new ForeignAPIFile( $title, $repo, $info );
+ return new ForeignAPIFile( $title, $repo, $info, true );
} else {
return null;
}
@@ -23,7 +27,7 @@ class ForeignAPIFile extends File {
// Dummy functions...
public function exists() {
- return true;
+ return $this->mExists;
}
public function getPath() {
@@ -31,12 +35,15 @@ class ForeignAPIFile extends File {
}
function transform( $params, $flags = 0 ) {
- $thumbUrl = $this->repo->getThumbUrl(
- $this->getName(),
- isset( $params['width'] ) ? $params['width'] : -1,
- isset( $params['height'] ) ? $params['height'] : -1 );
+ if( !$this->canRender() ) {
+ // show icon
+ return parent::transform( $params, $flags );
+ }
+ $thumbUrl = $this->repo->getThumbUrlFromCache(
+ $this->getName(),
+ isset( $params['width'] ) ? $params['width'] : -1,
+ isset( $params['height'] ) ? $params['height'] : -1 );
if( $thumbUrl ) {
- wfDebug( __METHOD__ . " got remote thumb $thumbUrl\n" );
return $this->handler->getTransform( $this, 'bogus', $thumbUrl, $params );;
}
return false;
@@ -98,4 +105,64 @@ class ForeignAPIFile extends File {
? $this->mInfo['descriptionurl']
: false;
}
+
+ /**
+ * Only useful if we're locally caching thumbs anyway...
+ */
+ function getThumbPath( $suffix = '' ) {
+ if ( $this->repo->canCacheThumbs() ) {
+ global $wgUploadDirectory;
+ $path = $wgUploadDirectory . '/thumb/' . $this->getHashPath( $this->getName() );
+ if ( $suffix ) {
+ $path = $path . $suffix . '/';
+ }
+ return $path;
+ }
+ else {
+ return null;
+ }
+ }
+
+ function getThumbnails() {
+ $files = array();
+ $dir = $this->getThumbPath( $this->getName() );
+ if ( is_dir( $dir ) ) {
+ $handle = opendir( $dir );
+ if ( $handle ) {
+ while ( false !== ( $file = readdir($handle) ) ) {
+ if ( $file{0} != '.' ) {
+ $files[] = $file;
+ }
+ }
+ closedir( $handle );
+ }
+ }
+ return $files;
+ }
+
+ function purgeCache() {
+ $this->purgeThumbnails();
+ $this->purgeDescriptionPage();
+ }
+
+ function purgeDescriptionPage() {
+ global $wgMemc;
+ $url = $this->repo->getDescriptionRenderUrl( $this->getName() );
+ $key = wfMemcKey( 'RemoteFileDescription', 'url', md5($url) );
+ $wgMemc->delete( $key );
+ }
+
+ function purgeThumbnails() {
+ global $wgMemc;
+ $key = wfMemcKey( 'ForeignAPIRepo', 'ThumbUrl', $this->getName() );
+ $wgMemc->delete( $key );
+ $files = $this->getThumbnails();
+ $dir = $this->getThumbPath( $this->getName() );
+ foreach ( $files as $file ) {
+ unlink( $dir . $file );
+ }
+ if ( is_dir( $dir ) ) {
+ rmdir( $dir ); // Might have already gone away, spews errors if we don't.
+ }
+ }
}
diff --git a/includes/filerepo/ForeignAPIRepo.php b/includes/filerepo/ForeignAPIRepo.php
index 0dee699f..6fc9c465 100644
--- a/includes/filerepo/ForeignAPIRepo.php
+++ b/includes/filerepo/ForeignAPIRepo.php
@@ -19,6 +19,7 @@
*/
class ForeignAPIRepo extends FileRepo {
var $fileFactory = array( 'ForeignAPIFile', 'newFromTitle' );
+ var $apiThumbCacheExpiry = 0;
protected $mQueryCache = array();
function __construct( $info ) {
@@ -30,10 +31,12 @@ class ForeignAPIRepo extends FileRepo {
}
}
+/**
+ * No-ops
+ */
function storeBatch( $triplets, $flags = 0 ) {
return false;
}
-
function storeTemp( $originalName, $srcPath ) {
return false;
}
@@ -69,14 +72,16 @@ class ForeignAPIRepo extends FileRepo {
array_merge( $query,
array(
'format' => 'json',
- 'action' => 'query',
- 'prop' => 'imageinfo' ) ) );
+ 'action' => 'query' ) ) );
if( !isset( $this->mQueryCache[$url] ) ) {
- $key = wfMemcKey( 'ForeignAPIRepo', $url );
+ $key = wfMemcKey( 'ForeignAPIRepo', 'Metadata', md5( $url ) );
$data = $wgMemc->get( $key );
if( !$data ) {
$data = Http::get( $url );
+ if ( !$data ) {
+ return null;
+ }
$wgMemc->set( $key, $data, 3600 );
}
@@ -92,7 +97,22 @@ class ForeignAPIRepo extends FileRepo {
function getImageInfo( $title, $time = false ) {
return $this->queryImage( array(
'titles' => 'Image:' . $title->getText(),
- 'iiprop' => 'timestamp|user|comment|url|size|sha1|metadata|mime' ) );
+ 'iiprop' => 'timestamp|user|comment|url|size|sha1|metadata|mime',
+ 'prop' => 'imageinfo' ) );
+ }
+
+ function findBySha1( $hash ) {
+ $results = $this->fetchImageQuery( array(
+ 'aisha1base36' => $hash,
+ 'aiprop' => 'timestamp|user|comment|url|size|sha1|metadata|mime',
+ 'list' => 'allimages', ) );
+ $ret = array();
+ if ( isset( $results['query']['allimages'] ) ) {
+ foreach ( $results['query']['allimages'] as $img ) {
+ $ret[] = new ForeignAPIFile( Title::makeTitle( NS_IMAGE, $img['name'] ), $this, $img );
+ }
+ }
+ return $ret;
}
function getThumbUrl( $name, $width=-1, $height=-1 ) {
@@ -100,11 +120,56 @@ class ForeignAPIRepo extends FileRepo {
'titles' => 'Image:' . $name,
'iiprop' => 'url',
'iiurlwidth' => $width,
- 'iiurlheight' => $height ) );
+ 'iiurlheight' => $height,
+ 'prop' => 'imageinfo' ) );
if( $info ) {
+ wfDebug( __METHOD__ . " got remote thumb " . $info['thumburl'] . "\n" );
return $info['thumburl'];
} else {
return false;
}
}
+
+ function getThumbUrlFromCache( $name, $width, $height ) {
+ global $wgMemc, $wgUploadPath, $wgServer, $wgUploadDirectory;
+
+ if ( !$this->canCacheThumbs() ) {
+ return $this->getThumbUrl( $name, $width, $height );
+ }
+
+ $key = wfMemcKey( 'ForeignAPIRepo', 'ThumbUrl', $name );
+ if ( $thumbUrl = $wgMemc->get($key) ) {
+ wfDebug("Got thumb from local cache. $thumbUrl \n");
+ return $thumbUrl;
+ }
+ else {
+ $foreignUrl = $this->getThumbUrl( $name, $width, $height );
+
+ // We need the same filename as the remote one :)
+ $fileName = ltrim( substr( $foreignUrl, strrpos( $foreignUrl, '/' ) ), '/' );
+ $path = 'thumb/' . $this->getHashPath( $name ) . $name . "/";
+ if ( !is_dir($wgUploadDirectory . '/' . $path) ) {
+ wfMkdirParents($wgUploadDirectory . '/' . $path);
+ }
+ if ( !is_writable( $wgUploadDirectory . '/' . $path . $fileName ) ) {
+ wfDebug( __METHOD__ . " could not write to thumb path\n" );
+ return $foreignUrl;
+ }
+ $localUrl = $wgServer . $wgUploadPath . '/' . $path . $fileName;
+ $thumb = Http::get( $foreignUrl );
+ # FIXME: Delete old thumbs that aren't being used. Maintenance script?
+ file_put_contents($wgUploadDirectory . '/' . $path . $fileName, $thumb );
+ $wgMemc->set( $key, $localUrl, $this->apiThumbCacheExpiry );
+ wfDebug( __METHOD__ . " got local thumb $localUrl, saving to cache \n" );
+ return $localUrl;
+ }
+ }
+
+ /**
+ * Are we locally caching the thumbnails?
+ * @return bool
+ */
+ public function canCacheThumbs() {
+ return ( $this->apiThumbCacheExpiry > 0 );
+ }
}
diff --git a/includes/filerepo/ForeignDBFile.php b/includes/filerepo/ForeignDBFile.php
index eed26048..5fb432c8 100644
--- a/includes/filerepo/ForeignDBFile.php
+++ b/includes/filerepo/ForeignDBFile.php
@@ -13,7 +13,7 @@ class ForeignDBFile extends LocalFile {
* Do not call this except from inside a repo class.
*/
static function newFromRow( $row, $repo ) {
- $title = Title::makeTitle( NS_IMAGE, $row->img_name );
+ $title = Title::makeTitle( NS_FILE, $row->img_name );
$file = new self( $title, $repo );
$file->loadFromRow( $row );
return $file;
diff --git a/includes/filerepo/Image.php b/includes/filerepo/Image.php
index 665dd4bf..5207bb4b 100644
--- a/includes/filerepo/Image.php
+++ b/includes/filerepo/Image.php
@@ -36,7 +36,7 @@ class Image extends LocalFile {
*/
static function newFromName( $name ) {
wfDeprecated( __METHOD__ );
- $title = Title::makeTitleSafe( NS_IMAGE, $name );
+ $title = Title::makeTitleSafe( NS_FILE, $name );
if ( is_object( $title ) ) {
$img = wfFindFile( $title );
if ( !$img ) {
diff --git a/includes/filerepo/LocalFile.php b/includes/filerepo/LocalFile.php
index 57c0703d..6fd6de72 100644
--- a/includes/filerepo/LocalFile.php
+++ b/includes/filerepo/LocalFile.php
@@ -68,7 +68,7 @@ class LocalFile extends File
* Do not call this except from inside a repo class.
*/
static function newFromRow( $row, $repo ) {
- $title = Title::makeTitle( NS_IMAGE, $row->img_name );
+ $title = Title::makeTitle( NS_FILE, $row->img_name );
$file = new self( $title, $repo );
$file->loadFromRow( $row );
return $file;
@@ -453,6 +453,11 @@ class LocalFile extends File
return $this->metadata;
}
+ function getBitDepth() {
+ $this->load();
+ return $this->bits;
+ }
+
/**
* Return the size of the image file, in bytes
* @public
@@ -619,31 +624,38 @@ class LocalFile extends File
/** purgeDescription inherited */
/** purgeEverything inherited */
- function getHistory($limit = null, $start = null, $end = null) {
+ function getHistory($limit = null, $start = null, $end = null, $inc = true) {
$dbr = $this->repo->getSlaveDB();
$tables = array('oldimage');
- $join_conds = array();
$fields = OldLocalFile::selectFields();
- $conds = $opts = array();
+ $conds = $opts = $join_conds = array();
+ $eq = $inc ? "=" : "";
$conds[] = "oi_name = " . $dbr->addQuotes( $this->title->getDBKey() );
- if( $start !== null ) {
- $conds[] = "oi_timestamp <= " . $dbr->addQuotes( $dbr->timestamp( $start ) );
+ if( $start ) {
+ $conds[] = "oi_timestamp <$eq " . $dbr->addQuotes( $dbr->timestamp( $start ) );
}
- if( $end !== null ) {
- $conds[] = "oi_timestamp >= " . $dbr->addQuotes( $dbr->timestamp( $end ) );
+ if( $end ) {
+ $conds[] = "oi_timestamp >$eq " . $dbr->addQuotes( $dbr->timestamp( $end ) );
}
if( $limit ) {
$opts['LIMIT'] = $limit;
}
- $opts['ORDER BY'] = 'oi_timestamp DESC';
+ // Search backwards for time > x queries
+ $order = (!$start && $end !== null) ? "ASC" : "DESC";
+ $opts['ORDER BY'] = "oi_timestamp $order";
+ $opts['USE INDEX'] = array('oldimage' => 'oi_name_timestamp');
- wfRunHooks( 'LocalFile::getHistory', array( &$this, &$tables, &$fields, &$conds, &$opts, &$join_conds ) );
+ wfRunHooks( 'LocalFile::getHistory', array( &$this, &$tables, &$fields,
+ &$conds, &$opts, &$join_conds ) );
$res = $dbr->select( $tables, $fields, $conds, __METHOD__, $opts, $join_conds );
$r = array();
while( $row = $dbr->fetchObject($res) ) {
$r[] = OldLocalFile::newFromRow($row, $this->repo);
}
+ if( $order == "ASC" ) {
+ $r = array_reverse( $r ); // make sure it ends up descending
+ }
return $r;
}
@@ -732,11 +744,11 @@ class LocalFile extends File
* @return FileRepoStatus object. On success, the value member contains the
* archive name, or an empty string if it was a new file.
*/
- function upload( $srcPath, $comment, $pageText, $flags = 0, $props = false, $timestamp = false ) {
+ function upload( $srcPath, $comment, $pageText, $flags = 0, $props = false, $timestamp = false, $user = null ) {
$this->lock();
$status = $this->publish( $srcPath, $flags );
if ( $status->ok ) {
- if ( !$this->recordUpload2( $status->value, $comment, $pageText, $props, $timestamp ) ) {
+ if ( !$this->recordUpload2( $status->value, $comment, $pageText, $props, $timestamp, $user ) ) {
$status->fatal( 'filenotfound', $srcPath );
}
}
@@ -766,18 +778,22 @@ class LocalFile extends File
/**
* Record a file upload in the upload log and the image table
*/
- function recordUpload2( $oldver, $comment, $pageText, $props = false, $timestamp = false )
+ function recordUpload2( $oldver, $comment, $pageText, $props = false, $timestamp = false, $user = null )
{
- global $wgUser;
+ if( is_null( $user ) ) {
+ global $wgUser;
+ $user = $wgUser;
+ }
$dbw = $this->repo->getMasterDB();
+ $dbw->begin();
if ( !$props ) {
$props = $this->repo->getFileProps( $this->getVirtualUrl() );
}
$props['description'] = $comment;
- $props['user'] = $wgUser->getId();
- $props['user_text'] = $wgUser->getName();
+ $props['user'] = $user->getId();
+ $props['user_text'] = $user->getName();
$props['timestamp'] = wfTimestamp( TS_MW );
$this->setProps( $props );
@@ -812,8 +828,8 @@ class LocalFile extends File
'img_minor_mime' => $this->minor_mime,
'img_timestamp' => $timestamp,
'img_description' => $comment,
- 'img_user' => $wgUser->getId(),
- 'img_user_text' => $wgUser->getName(),
+ 'img_user' => $user->getId(),
+ 'img_user_text' => $user->getName(),
'img_metadata' => $this->metadata,
'img_sha1' => $this->sha1
),
@@ -858,8 +874,8 @@ class LocalFile extends File
'img_minor_mime' => $this->minor_mime,
'img_timestamp' => $timestamp,
'img_description' => $comment,
- 'img_user' => $wgUser->getId(),
- 'img_user_text' => $wgUser->getName(),
+ 'img_user' => $user->getId(),
+ 'img_user_text' => $user->getName(),
'img_metadata' => $this->metadata,
'img_sha1' => $this->sha1
), array( /* WHERE */
@@ -874,19 +890,22 @@ class LocalFile extends File
}
$descTitle = $this->getTitle();
- $article = new Article( $descTitle );
+ $article = new ImagePage( $descTitle );
+ $article->setFile( $this );
# Add the log entry
$log = new LogPage( 'upload' );
$action = $reupload ? 'overwrite' : 'upload';
- $log->addEntry( $action, $descTitle, $comment );
+ $log->addEntry( $action, $descTitle, $comment, array(), $user );
if( $descTitle->exists() ) {
# Create a null revision
- $nullRevision = Revision::newNullRevision( $dbw, $descTitle->getArticleId(), $log->getRcComment(), false );
+ $latest = $descTitle->getLatestRevID();
+ $nullRevision = Revision::newNullRevision( $dbw, $descTitle->getArticleId(),
+ $log->getRcComment(), false );
$nullRevision->insertOn( $dbw );
- wfRunHooks( 'NewRevisionFromEditComplete', array($article, $nullRevision, false) );
+ wfRunHooks( 'NewRevisionFromEditComplete', array($article, $nullRevision, $latest, $user) );
$article->updateRevisionOn( $dbw, $nullRevision );
# Invalidate the cache for the description page
@@ -1109,8 +1128,8 @@ class LocalFile extends File
if ( !$revision ) return false;
$text = $revision->getText();
if ( !$text ) return false;
- $html = $wgParser->parse( $text, new ParserOptions );
- return $html;
+ $pout = $wgParser->parse( $text, $this->title, new ParserOptions() );
+ return $pout->getText();
}
function getDescription() {
@@ -1128,7 +1147,7 @@ class LocalFile extends File
// Initialise now if necessary
if ( $this->sha1 == '' && $this->fileExists ) {
$this->sha1 = File::sha1Base36( $this->getPath() );
- if ( strval( $this->sha1 ) != '' ) {
+ if ( !wfReadOnly() && strval( $this->sha1 ) != '' ) {
$dbw = $this->repo->getMasterDB();
$dbw->update( 'image',
array( 'img_sha1' => $this->sha1 ),
@@ -1355,7 +1374,7 @@ class LocalFileDeleteBatch {
$dbw->delete( 'oldimage',
array(
'oi_name' => $this->file->getName(),
- 'oi_archive_name IN (' . $dbw->makeList( array_keys( $oldRels ) ) . ')'
+ 'oi_archive_name' => array_keys( $oldRels )
), __METHOD__ );
}
if ( $deleteCurrent ) {
@@ -1509,7 +1528,8 @@ class LocalFileRestoreBatch {
$result = $dbw->select( 'filearchive', '*',
$conditions,
__METHOD__,
- array( 'ORDER BY' => 'fa_timestamp DESC' ) );
+ array( 'ORDER BY' => 'fa_timestamp DESC' )
+ );
$idsPresent = array();
$storeBatch = array();
@@ -1554,15 +1574,11 @@ class LocalFileRestoreBatch {
'minor_mime' => $row->fa_minor_mime,
'major_mime' => $row->fa_major_mime,
'media_type' => $row->fa_media_type,
- 'metadata' => $row->fa_metadata );
+ 'metadata' => $row->fa_metadata
+ );
}
if ( $first && !$exists ) {
- // The live (current) version cannot be hidden!
- if( !$this->unsuppress && $row->fa_deleted ) {
- $this->file->unlock();
- return $status;
- }
// This revision will be published as the new current version
$destRel = $this->file->getRel();
$insertCurrent = array(
@@ -1579,7 +1595,13 @@ class LocalFileRestoreBatch {
'img_user' => $row->fa_user,
'img_user_text' => $row->fa_user_text,
'img_timestamp' => $row->fa_timestamp,
- 'img_sha1' => $sha1);
+ 'img_sha1' => $sha1
+ );
+ // The live (current) version cannot be hidden!
+ if( !$this->unsuppress && $row->fa_deleted ) {
+ $storeBatch[] = array( $deletedUrl, 'public', $destRel );
+ $this->cleanupBatch[] = $row->fa_storage_key;
+ }
} else {
$archiveName = $row->fa_archive_name;
if( $archiveName == '' ) {
@@ -1616,6 +1638,7 @@ class LocalFileRestoreBatch {
$deleteIds[] = $row->fa_id;
if( !$this->unsuppress && $row->fa_deleted & File::DELETED_FILE ) {
// private files can stay where they are
+ $status->successCount++;
} else {
$storeBatch[] = array( $deletedUrl, 'public', $destRel );
$this->cleanupBatch[] = $row->fa_storage_key;
@@ -1705,7 +1728,7 @@ class LocalFileMoveBatch {
$this->file = $file;
$this->target = $target;
$this->oldHash = $this->file->repo->getHashPath( $this->file->getName() );
- $this->newHash = $this->file->repo->getHashPath( $this->target->getDbKey() );
+ $this->newHash = $this->file->repo->getHashPath( $this->target->getDBKey() );
$this->oldName = $this->file->getName();
$this->newName = $this->file->repo->getNameFromTitle( $this->target );
$this->oldRel = $this->oldHash . $this->oldName;
@@ -1751,7 +1774,7 @@ class LocalFileMoveBatch {
continue;
}
$this->olds[] = array(
- "{$archiveBase}/{$this->oldHash}{$oldname}",
+ "{$archiveBase}/{$this->oldHash}{$oldName}",
"{$archiveBase}/{$this->newHash}{$timestamp}!{$this->newName}"
);
}
diff --git a/includes/filerepo/LocalRepo.php b/includes/filerepo/LocalRepo.php
index 90b198c8..5eb1a11c 100644
--- a/includes/filerepo/LocalRepo.php
+++ b/includes/filerepo/LocalRepo.php
@@ -94,7 +94,7 @@ class LocalRepo extends FSRepo {
'page_id', //Field
array( //Conditions
'page_namespace' => $title->getNamespace(),
- 'page_title' => $title->getDbKey(),
+ 'page_title' => $title->getDBKey(),
),
__METHOD__ //Function name
);
@@ -108,7 +108,7 @@ class LocalRepo extends FSRepo {
$title = Title::newFromTitle( $title );
}
if( $title instanceof Title && $title->getNamespace() == NS_MEDIA ) {
- $title = Title::makeTitle( NS_IMAGE, $title->getText() );
+ $title = Title::makeTitle( NS_FILE, $title->getText() );
}
$memcKey = $this->getMemcKey( "image_redirect:" . md5( $title->getPrefixedDBkey() ) );
@@ -164,8 +164,7 @@ class LocalRepo extends FSRepo {
/*
* Find many files using one query
*/
- function findFiles( $titles, $flags ) {
- // FIXME: Comply with $flags
+ function findFiles( $titles ) {
// FIXME: Only accepts a $titles array where the keys are the sanitized
// file names.
diff --git a/includes/filerepo/OldLocalFile.php b/includes/filerepo/OldLocalFile.php
index 89e49c4c..46c35bd9 100644
--- a/includes/filerepo/OldLocalFile.php
+++ b/includes/filerepo/OldLocalFile.php
@@ -23,7 +23,7 @@ class OldLocalFile extends LocalFile {
}
static function newFromRow( $row, $repo ) {
- $title = Title::makeTitle( NS_IMAGE, $row->oi_name );
+ $title = Title::makeTitle( NS_FILE, $row->oi_name );
$file = new self( $title, $repo, null, $row->oi_archive_name );
$file->loadFromRow( $row, 'oi_' );
return $file;
@@ -64,6 +64,7 @@ class OldLocalFile extends LocalFile {
'oi_user',
'oi_user_text',
'oi_timestamp',
+ 'oi_deleted',
'oi_sha1',
);
}
diff --git a/includes/filerepo/RepoGroup.php b/includes/filerepo/RepoGroup.php
index 7cb837b3..2303f581 100644
--- a/includes/filerepo/RepoGroup.php
+++ b/includes/filerepo/RepoGroup.php
@@ -82,7 +82,7 @@ class RepoGroup {
}
return false;
}
- function findFiles( $titles, $flags = 0 ) {
+ function findFiles( $titles ) {
if ( !$this->reposInitialised ) {
$this->initialiseRepos();
}
@@ -90,11 +90,12 @@ class RepoGroup {
$titleObjs = array();
foreach ( $titles as $title ) {
if ( !( $title instanceof Title ) )
- $title = Title::makeTitleSafe( NS_IMAGE, $title );
- $titleObjs[$title->getDBkey()] = $title;
+ $title = Title::makeTitleSafe( NS_FILE, $title );
+ if ( $title )
+ $titleObjs[$title->getDBkey()] = $title;
}
- $images = $this->localRepo->findFiles( $titleObjs, $flags );
+ $images = $this->localRepo->findFiles( $titleObjs );
foreach ( $this->foreignRepos as $repo ) {
// Remove found files from $titleObjs
@@ -102,7 +103,7 @@ class RepoGroup {
if ( isset( $titleObjs[$name] ) )
unset( $titleObjs[$name] );
- $images = array_merge( $images, $repo->findFiles( $titleObjs, $flags ) );
+ $images = array_merge( $images, $repo->findFiles( $titleObjs ) );
}
return $images;
}
@@ -176,6 +177,13 @@ class RepoGroup {
return $this->getRepo( 'local' );
}
+ /**
+ * Call a function for each foreign repo, with the repo object as the
+ * first parameter.
+ *
+ * @param $callback callback The function to call
+ * @param $params array Optional additional parameters to pass to the function
+ */
function forEachForeignRepo( $callback, $params = array() ) {
foreach( $this->foreignRepos as $repo ) {
$args = array_merge( array( $repo ), $params );
@@ -186,8 +194,12 @@ class RepoGroup {
return false;
}
+ /**
+ * Does the installation have any foreign repos set up?
+ * @return bool
+ */
function hasForeignRepos() {
- return !empty( $this->foreignRepos );
+ return (bool)$this->foreignRepos;
}
/**
diff --git a/includes/filerepo/UnregisteredLocalFile.php b/includes/filerepo/UnregisteredLocalFile.php
index c687ef6e..6f63cb0b 100644
--- a/includes/filerepo/UnregisteredLocalFile.php
+++ b/includes/filerepo/UnregisteredLocalFile.php
@@ -32,7 +32,7 @@ class UnregisteredLocalFile extends File {
$this->name = $repo->getNameFromTitle( $title );
} else {
$this->name = basename( $path );
- $this->title = Title::makeTitleSafe( NS_IMAGE, $this->name );
+ $this->title = Title::makeTitleSafe( NS_FILE, $this->name );
}
$this->repo = $repo;
if ( $path ) {