* Update the version every time you make breaking or significant changes. */
const VERSION = "2.1";
- var $fileFactory = array( 'ForeignAPIFile', 'newFromTitle' );
- /* Check back with Commons after a day */
- var $apiThumbCacheExpiry = 86400; /* 24*60*60 */
- /* Redownload thumbnail files after a month */
- var $fileCacheExpiry = 2592000; /* 86400*30 */
+ /**
+ * List of iiprop values for the thumbnail fetch queries.
+ * @since 1.23
+ */
+ protected static $imageInfoProps = array(
+ 'url',
+ 'thumbnail',
+ 'timestamp',
+ );
+
+ protected $fileFactory = array( 'ForeignAPIFile', 'newFromTitle' );
+ /** @var int Check back with Commons after a day (24*60*60) */
+ protected $apiThumbCacheExpiry = 86400;
- protected $mQueryCache = array();
+ /** @var int Redownload thumbnail files after a month (86400*30) */
+ protected $fileCacheExpiry = 2592000;
+
+ /** @var array */
+ private $mQueryCache = array();
+
+ /** @var array */
protected $mFileExists = array();
/**
if ( $time ) {
return false;
}
+
return parent::newFile( $title, $time );
}
function fileExistsBatch( array $files ) {
$results = array();
foreach ( $files as $k => $f ) {
- if ( isset( $this->mFileExists[$k] ) ) {
- $results[$k] = true;
+ if ( isset( $this->mFileExists[$f] ) ) {
+ $results[$k] = $this->mFileExists[$f];
unset( $files[$k] );
} elseif ( self::isVirtualUrl( $f ) ) {
# @todo FIXME: We need to be able to handle virtual
}
}
- $data = $this->fetchImageQuery( array( 'titles' => implode( $files, '|' ),
- 'prop' => 'imageinfo' ) );
+ $data = $this->fetchImageQuery( array(
+ 'titles' => implode( $files, '|' ),
+ 'prop' => 'imageinfo' )
+ );
+
if ( isset( $data['query']['pages'] ) ) {
- $i = 0;
+ # First, get results from the query. Note we only care whether the image exists,
+ # not whether it has a description page.
+ foreach ( $data['query']['pages'] as $p ) {
+ $this->mFileExists[$p['title']] = ( $p['imagerepository'] !== '' );
+ }
+ # Second, copy the results to any redirects that were queried
+ if ( isset( $data['query']['redirects'] ) ) {
+ foreach ( $data['query']['redirects'] as $r ) {
+ $this->mFileExists[$r['from']] = $this->mFileExists[$r['to']];
+ }
+ }
+ # Third, copy the results to any non-normalized titles that were queried
+ if ( isset( $data['query']['normalized'] ) ) {
+ foreach ( $data['query']['normalized'] as $n ) {
+ $this->mFileExists[$n['from']] = $this->mFileExists[$n['to']];
+ }
+ }
+ # Finally, copy the results to the output
foreach ( $files as $key => $file ) {
- $results[$key] = $this->mFileExists[$key] = !isset( $data['query']['pages'][$i]['missing'] );
- $i++;
+ $results[$key] = $this->mFileExists[$file];
}
}
+
return $results;
}
* @return string
*/
function fetchImageQuery( $query ) {
- global $wgMemc, $wgLanguageCode;
+ global $wgLanguageCode;
$query = array_merge( $query,
array(
'action' => 'query',
'redirects' => 'true'
) );
+
if ( !isset( $query['uselang'] ) ) { // uselang is unset or null
$query['uselang'] = $wgLanguageCode;
}
- if ( $this->mApiBase ) {
- $url = wfAppendQuery( $this->mApiBase, $query );
- } else {
- $url = $this->makeUrl( $query, 'api' );
- }
- if ( !isset( $this->mQueryCache[$url] ) ) {
- $key = $this->getLocalCacheKey( 'ForeignAPIRepo', 'Metadata', md5( $url ) );
- $data = $wgMemc->get( $key );
- if ( !$data ) {
- $data = self::httpGet( $url );
- if ( !$data ) {
- return null;
- }
- $wgMemc->set( $key, $data, 3600 );
- }
+ $data = $this->httpGetCached( 'Metadata', $query );
- if ( count( $this->mQueryCache ) > 100 ) {
- // Keep the cache from growing infinitely
- $this->mQueryCache = array();
- }
- $this->mQueryCache[$url] = $data;
+ if ( $data ) {
+ return FormatJson::decode( $data, true );
+ } else {
+ return null;
}
- return FormatJson::decode( $this->mQueryCache[$url], true );
}
/**
}
}
}
+
return false;
}
$ret[] = new ForeignAPIFile( Title::makeTitle( NS_FILE, $img['name'] ), $this, $img );
}
}
+
return $ret;
}
function getThumbUrl( $name, $width = -1, $height = -1, &$result = null, $otherParams = '' ) {
$data = $this->fetchImageQuery( array(
'titles' => 'File:' . $name,
- 'iiprop' => 'url|timestamp',
+ 'iiprop' => self::getIIProps(),
'iiurlwidth' => $width,
'iiurlheight' => $height,
'iiurlparam' => $otherParams,
if ( $data && $info && isset( $info['thumburl'] ) ) {
wfDebug( __METHOD__ . " got remote thumb " . $info['thumburl'] . "\n" );
$result = $info;
+
return $info['thumburl'];
} else {
return false;
function getThumbError( $name, $width = -1, $height = -1, $otherParams = '', $lang = null ) {
$data = $this->fetchImageQuery( array(
'titles' => 'File:' . $name,
- 'iiprop' => 'url|timestamp',
+ 'iiprop' => self::getIIProps(),
'iiurlwidth' => $width,
'iiurlheight' => $height,
'iiurlparam' => $otherParams,
if ( $data && $info && isset( $info['thumberror'] ) ) {
wfDebug( __METHOD__ . " got remote thumb error " . $info['thumberror'] . "\n" );
+
return new MediaTransformError(
'thumbnail_error_remote',
$width,
* @param string $name is a dbkey form of a title
* @param $width
* @param $height
- * @param string $params Other rendering parameters (page number, etc) from handler's makeParamString.
+ * @param string $params Other rendering parameters (page number, etc)
+ * from handler's makeParamString.
* @return bool|string
*/
function getThumbUrlFromCache( $name, $width, $height, $params = "" ) {
if ( isset( $knownThumbUrls[$sizekey] ) ) {
wfDebug( __METHOD__ . ': Got thumburl from local cache: ' .
"{$knownThumbUrls[$sizekey]} \n" );
+
return $knownThumbUrls[$sizekey];
}
/* This size is not yet known */
if ( !$foreignUrl ) {
wfDebug( __METHOD__ . " Could not find thumburl\n" );
+
return false;
}
$fileName = rawurldecode( pathinfo( $foreignUrl, PATHINFO_BASENAME ) );
if ( !$this->validateFilename( $fileName ) ) {
wfDebug( __METHOD__ . " The deduced filename $fileName is not safe\n" );
+
return false;
}
$localPath = $this->getZonePath( 'thumb' ) . "/" . $this->getHashPath( $name ) . $name;
$localFilename = $localPath . "/" . $fileName;
- $localUrl = $this->getZoneUrl( 'thumb' ) . "/" . $this->getHashPath( $name ) . rawurlencode( $name ) . "/" . rawurlencode( $fileName );
+ $localUrl = $this->getZoneUrl( 'thumb' ) . "/" . $this->getHashPath( $name ) .
+ rawurlencode( $name ) . "/" . rawurlencode( $fileName );
if ( $backend->fileExists( array( 'src' => $localFilename ) )
- && isset( $metadata['timestamp'] ) ) {
+ && isset( $metadata['timestamp'] )
+ ) {
wfDebug( __METHOD__ . " Thumbnail was already downloaded before\n" );
$modified = $backend->getFileTimestamp( array( 'src' => $localFilename ) );
$remoteModified = strtotime( $metadata['timestamp'] );
/* Use our current and already downloaded thumbnail */
$knownThumbUrls[$sizekey] = $localUrl;
$wgMemc->set( $key, $knownThumbUrls, $this->apiThumbCacheExpiry );
+
return $localUrl;
}
/* There is a new Commons file, or existing thumbnail older than a month */
$thumb = self::httpGet( $foreignUrl );
if ( !$thumb ) {
wfDebug( __METHOD__ . " Could not download thumb\n" );
+
return false;
}
$params = array( 'dst' => $localFilename, 'content' => $thumb );
if ( !$backend->quickCreate( $params )->isOK() ) {
wfDebug( __METHOD__ . " could not write to thumb path '$localFilename'\n" );
+
return $foreignUrl;
}
$knownThumbUrls[$sizekey] = $localUrl;
$wgMemc->set( $key, $knownThumbUrls, $this->apiThumbCacheExpiry );
wfDebug( __METHOD__ . " got local thumb $localUrl, saving to cache \n" );
+
return $localUrl;
}
if ( in_array( $zone, $supported ) ) {
return parent::getZonePath( $zone );
}
+
return false;
}
function getInfo() {
$info = parent::getInfo();
$info['apiurl'] = $this->getApiUrl();
+
+ $query = array(
+ 'format' => 'json',
+ 'action' => 'query',
+ 'meta' => 'siteinfo',
+ 'siprop' => 'general',
+ );
+
+ $data = $this->httpGetCached( 'SiteInfo', $query, 7200 );
+
+ if ( $data ) {
+ $siteInfo = FormatJson::decode( $data, true );
+ $general = $siteInfo['query']['general'];
+
+ $info['articlepath'] = $general['articlepath'];
+ $info['server'] = $general['server'];
+ }
+
return $info;
}
}
}
+ /**
+ * @return string
+ * @since 1.23
+ */
+ protected static function getIIProps() {
+ return join( '|', self::$imageInfoProps );
+ }
+
+ /**
+ * HTTP GET request to a mediawiki API (with caching)
+ * @param $target string Used in cache key creation, mostly
+ * @param $query array The query parameters for the API request
+ * @param $cacheTTL int Time to live for the memcached caching
+ */
+ public function httpGetCached( $target, $query, $cacheTTL = 3600 ) {
+ if ( $this->mApiBase ) {
+ $url = wfAppendQuery( $this->mApiBase, $query );
+ } else {
+ $url = $this->makeUrl( $query, 'api' );
+ }
+
+ if ( !isset( $this->mQueryCache[$url] ) ) {
+ global $wgMemc;
+
+ $key = $this->getLocalCacheKey( get_class( $this ), $target, md5( $url ) );
+ $data = $wgMemc->get( $key );
+
+ if ( !$data ) {
+ $data = self::httpGet( $url );
+
+ if ( !$data ) {
+ return null;
+ }
+
+ $wgMemc->set( $key, $data, $cacheTTL );
+ }
+
+ if ( count( $this->mQueryCache ) > 100 ) {
+ // Keep the cache from growing infinitely
+ $this->mQueryCache = array();
+ }
+
+ $this->mQueryCache[$url] = $data;
+ }
+
+ return $this->mQueryCache[$url];
+ }
+
/**
* @param $callback Array|string
* @throws MWException