2012-02-29 10:50:36 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
class PageImages {
|
2013-11-04 22:00:38 +00:00
|
|
|
/**
|
|
|
|
* Page property used to store the page image information
|
|
|
|
*/
|
|
|
|
const PROP_NAME = 'page_image';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns page image for a given title
|
|
|
|
*
|
|
|
|
* @param Title $title: Title to get page image for
|
|
|
|
*
|
|
|
|
* @return File|null
|
|
|
|
*/
|
|
|
|
public static function getPageImage( Title $title ) {
|
|
|
|
wfProfileIn( __METHOD__ );
|
|
|
|
$dbr = wfGetDB( DB_SLAVE );
|
|
|
|
$name = $dbr->selectField( 'page_props',
|
|
|
|
'pp_value',
|
|
|
|
array( 'pp_page' => $title->getArticleID(), 'pp_propname' => self::PROP_NAME ),
|
|
|
|
__METHOD__
|
|
|
|
);
|
|
|
|
$file = null;
|
|
|
|
if ( $name ) {
|
|
|
|
$file = wfFindFile( $name );
|
|
|
|
}
|
|
|
|
wfProfileOut( __METHOD__ );
|
|
|
|
return $file;
|
|
|
|
}
|
|
|
|
|
2012-05-08 21:42:07 +00:00
|
|
|
/**
|
|
|
|
* Returns true if data for this title should be saved
|
|
|
|
*
|
|
|
|
* @param Title $title
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
private static function processThisTitle( Title $title ) {
|
|
|
|
static $flipped = false;
|
|
|
|
if ( $flipped === false ) {
|
|
|
|
global $wgPageImagesNamespaces;
|
|
|
|
$flipped = array_flip( $wgPageImagesNamespaces );
|
|
|
|
}
|
|
|
|
return isset( $flipped[$title->getNamespace()] );
|
|
|
|
}
|
|
|
|
|
2012-03-01 13:29:30 +00:00
|
|
|
/**
|
2014-02-20 08:30:35 +00:00
|
|
|
* ParserMakeImageParams hook handler, saves extended information about images used on page
|
2012-03-01 14:33:28 +00:00
|
|
|
* @see https://www.mediawiki.org/wiki/Manual:Hooks/ParserMakeImageParams
|
2012-03-01 13:29:30 +00:00
|
|
|
* @param Title $title
|
|
|
|
* @param File|bool $file
|
|
|
|
* @param array $params
|
|
|
|
* @param Parser $parser
|
2012-03-02 16:45:07 +00:00
|
|
|
* @return bool
|
2012-03-01 13:29:30 +00:00
|
|
|
*/
|
2012-03-01 14:33:28 +00:00
|
|
|
public static function onParserMakeImageParams( Title $title, $file, array &$params, Parser $parser ) {
|
2012-05-08 21:42:07 +00:00
|
|
|
if ( !$file || !self::processThisTitle( $parser->getTitle() ) ) {
|
2012-03-01 13:29:30 +00:00
|
|
|
return true;
|
|
|
|
}
|
2012-02-29 10:50:36 +00:00
|
|
|
$out = $parser->getOutput();
|
|
|
|
if ( !isset( $out->pageImages ) ) {
|
|
|
|
$out->pageImages = array();
|
|
|
|
}
|
|
|
|
$myParams = $params;
|
|
|
|
if ( !isset( $myParams['handler']['width'] ) ) {
|
2012-12-27 19:40:13 +00:00
|
|
|
if ( isset( $myParams['frame']['thumbnail'] )
|
|
|
|
|| isset( $myParams['frame']['thumb'] )
|
|
|
|
|| isset( $myParams['frame']['frameless'] ) )
|
|
|
|
{
|
2012-03-01 12:25:57 +00:00
|
|
|
$myParams['handler']['width'] = 250;
|
2012-12-27 19:40:13 +00:00
|
|
|
} else {
|
|
|
|
$myParams['handler']['width'] = $file->getWidth();
|
2012-03-01 12:25:57 +00:00
|
|
|
}
|
2012-02-29 10:50:36 +00:00
|
|
|
}
|
2012-03-02 16:42:17 +00:00
|
|
|
$myParams['filename'] = $title->getDBkey();
|
2014-02-11 19:59:04 +00:00
|
|
|
$myParams['fullwidth'] = $file->getWidth();
|
|
|
|
$myParams['fullheight'] = $file->getHeight();
|
2012-03-01 12:25:57 +00:00
|
|
|
$out->pageImages[] = $myParams;
|
2012-02-29 10:50:36 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-03-01 14:33:28 +00:00
|
|
|
/**
|
2012-12-17 19:22:13 +00:00
|
|
|
* LinksUpdate hook handler, sets at most 2 page properties depending on images on page
|
2012-03-01 14:33:28 +00:00
|
|
|
* @see https://www.mediawiki.org/wiki/Manual:Hooks/LinksUpdate
|
|
|
|
* @param LinksUpdate $lu
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public static function onLinksUpdate( LinksUpdate $lu ) {
|
2012-12-17 19:22:13 +00:00
|
|
|
if ( !isset( $lu->getParserOutput()->pageImages ) ) {
|
2012-02-29 10:50:36 +00:00
|
|
|
return true;
|
|
|
|
}
|
2014-02-11 19:59:04 +00:00
|
|
|
wfProfileIn( __METHOD__ );
|
2012-02-29 10:50:36 +00:00
|
|
|
$images = $lu->getParserOutput()->pageImages;
|
2012-03-01 12:25:57 +00:00
|
|
|
$scores = array();
|
|
|
|
$counter = 0;
|
|
|
|
foreach ( $images as $image ) {
|
2012-03-02 16:42:17 +00:00
|
|
|
$fileName = $image['filename'];
|
2012-03-01 12:25:57 +00:00
|
|
|
if ( !isset( $scores[$fileName] ) ) {
|
|
|
|
$scores[$fileName] = -1;
|
|
|
|
}
|
|
|
|
$scores[$fileName] = max( $scores[$fileName], self::getScore( $image, $counter++ ) );
|
|
|
|
}
|
2012-12-17 19:22:13 +00:00
|
|
|
$image = false;
|
2012-03-01 12:25:57 +00:00
|
|
|
foreach ( $scores as $name => $score ) {
|
2012-12-17 19:22:13 +00:00
|
|
|
if ( $score > 0 && ( !$image || $score > $scores[$image] ) ) {
|
|
|
|
$image = $name;
|
2012-03-01 12:25:57 +00:00
|
|
|
}
|
|
|
|
}
|
2012-12-17 19:22:13 +00:00
|
|
|
if ( $image ) {
|
2013-11-04 22:00:38 +00:00
|
|
|
$lu->mProperties[self::PROP_NAME] = $image;
|
2012-03-01 12:25:57 +00:00
|
|
|
}
|
2014-02-11 19:59:04 +00:00
|
|
|
wfProfileOut( __METHOD__ );
|
2012-03-02 16:45:07 +00:00
|
|
|
|
2012-02-29 10:50:36 +00:00
|
|
|
return true;
|
|
|
|
}
|
2012-03-01 12:25:57 +00:00
|
|
|
|
2012-05-08 21:42:07 +00:00
|
|
|
/**
|
2013-11-07 18:55:02 +00:00
|
|
|
* OpenSearchXml hook handler, enhances Extension:OpenSearchXml results with this extension's data
|
2012-12-17 19:22:13 +00:00
|
|
|
* @param array $results
|
2012-05-08 21:42:07 +00:00
|
|
|
* @return bool
|
|
|
|
*/
|
2012-03-08 14:01:00 +00:00
|
|
|
public static function onOpenSearchXml( &$results ) {
|
|
|
|
global $wgPageImagesExpandOpenSearchXml;
|
|
|
|
if ( !$wgPageImagesExpandOpenSearchXml || !count( $results ) ) {
|
|
|
|
return true;
|
|
|
|
}
|
2012-05-08 21:42:07 +00:00
|
|
|
wfProfileIn( __METHOD__ );
|
2012-03-08 14:01:00 +00:00
|
|
|
$pageIds = array_keys( $results );
|
|
|
|
$api = new ApiMain(
|
|
|
|
new FauxRequest( array(
|
|
|
|
'action' => 'query',
|
|
|
|
'prop' => 'pageimages',
|
|
|
|
'piprop' => 'thumbnail',
|
|
|
|
'pageids' => implode( '|', $pageIds ),
|
2012-03-09 09:49:02 +00:00
|
|
|
'pilimit' => count( $results ),
|
2012-03-08 14:01:00 +00:00
|
|
|
) )
|
|
|
|
);
|
|
|
|
$api->execute();
|
|
|
|
$data = $api->getResultData();
|
|
|
|
foreach ( $pageIds as $id ) {
|
2013-11-07 18:55:02 +00:00
|
|
|
if ( isset( $data['query']['pages'][$id]['thumbnail'] ) ) {
|
|
|
|
$results[$id]['image'] = $data['query']['pages'][$id]['thumbnail'];
|
2012-03-08 14:01:00 +00:00
|
|
|
} else {
|
|
|
|
$results[$id]['image'] = null;
|
|
|
|
}
|
|
|
|
}
|
2012-05-08 21:42:07 +00:00
|
|
|
wfProfileOut( __METHOD__ );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-03-01 14:33:28 +00:00
|
|
|
/**
|
|
|
|
* Returns score for image, the more the better, if it is less than zero,
|
|
|
|
* the image shouldn't be used for anything
|
|
|
|
* @param array $image: Associative array describing an image
|
|
|
|
* @param int $position: Image order on page
|
|
|
|
* @return int
|
|
|
|
*/
|
2012-03-01 12:25:57 +00:00
|
|
|
private static function getScore( array $image, $position ) {
|
|
|
|
global $wgPageImagesScores;
|
|
|
|
|
2014-02-11 19:59:04 +00:00
|
|
|
$score = self::scoreFromTable( $image['handler']['width'], $wgPageImagesScores['width'] );
|
|
|
|
|
2012-03-01 12:25:57 +00:00
|
|
|
if ( isset( $wgPageImagesScores['position'][$position] ) ) {
|
|
|
|
$score += $wgPageImagesScores['position'][$position];
|
|
|
|
}
|
2014-02-11 19:59:04 +00:00
|
|
|
|
|
|
|
$ratio = intval( self::getRatio( $image ) * 10 );
|
|
|
|
$score += self::scoreFromTable( $ratio, $wgPageImagesScores['ratio'] );
|
|
|
|
|
2012-03-02 16:42:17 +00:00
|
|
|
$blacklist = self::getBlacklist();
|
|
|
|
if ( isset( $blacklist[$image['filename']] ) ) {
|
2014-02-11 19:59:04 +00:00
|
|
|
$score = -1000;
|
2012-03-02 16:42:17 +00:00
|
|
|
}
|
2012-03-01 12:25:57 +00:00
|
|
|
return $score;
|
|
|
|
}
|
2012-03-02 16:42:17 +00:00
|
|
|
|
2014-02-11 19:59:04 +00:00
|
|
|
/**
|
|
|
|
* Returns width/height ratio of an image as displayed or 0 is not available
|
|
|
|
*
|
|
|
|
* @param array $image
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
private static function getRatio( array $image ) {
|
|
|
|
$width = $image['fullwidth'];
|
|
|
|
$height = $image['fullheight'];
|
|
|
|
if ( !$width || !$height ) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return $width / $height;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns score based on table of ranges
|
|
|
|
*
|
|
|
|
* @param int|float $value
|
|
|
|
* @param array $scores
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
private static function scoreFromTable( $value, array $scores ) {
|
|
|
|
$lastScore = 0;
|
|
|
|
foreach ( $scores as $boundary => $score ) {
|
|
|
|
if ( $value <= $boundary ) {
|
|
|
|
return $score;
|
|
|
|
}
|
|
|
|
$lastScore = $score;
|
|
|
|
}
|
|
|
|
return $lastScore;
|
|
|
|
}
|
|
|
|
|
2012-03-02 16:42:17 +00:00
|
|
|
/**
|
|
|
|
* Returns a list of images blacklisted from influencing this extension's output
|
|
|
|
* @return array: Flipped associative array in format "image BDB key" => int
|
|
|
|
*/
|
|
|
|
private static function getBlacklist() {
|
|
|
|
global $wgPageImagesBlacklist, $wgPageImagesBlacklistExpiry, $wgMemc;
|
|
|
|
static $list = false;
|
|
|
|
if ( $list !== false ) {
|
|
|
|
return $list;
|
|
|
|
}
|
|
|
|
wfProfileIn( __METHOD__ );
|
|
|
|
$key = wfMemcKey( 'pageimages', 'blacklist' );
|
|
|
|
$list = $wgMemc->get( $key );
|
|
|
|
if ( $list !== false ) {
|
|
|
|
wfProfileOut( __METHOD__ );
|
|
|
|
return $list;
|
|
|
|
}
|
|
|
|
wfDebug( __METHOD__ . "(): cache miss\n" );
|
|
|
|
$list = array();
|
|
|
|
foreach ( $wgPageImagesBlacklist as $source ) {
|
|
|
|
switch ( $source['type'] ) {
|
|
|
|
case 'db':
|
|
|
|
$list = array_merge( $list, self::getDbBlacklist( $source['db'], $source['page'] ) );
|
|
|
|
break;
|
|
|
|
case 'url':
|
|
|
|
$list = array_merge( $list, self::getUrlBlacklist( $source['url'] ) );
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new MWException( __METHOD__ . "(): unrecognized image blacklist type '{$source['type']}'" );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$list = array_flip( $list );
|
|
|
|
$wgMemc->set( $key, $list, $wgPageImagesBlacklistExpiry );
|
|
|
|
wfProfileOut( __METHOD__ );
|
|
|
|
return $list;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns list of images linked by the given blacklist page
|
|
|
|
* @param string|int $dbName: Database name or false for current database
|
|
|
|
* @param string $page
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
private static function getDbBlacklist( $dbName, $page ) {
|
|
|
|
wfProfileIn( __METHOD__ );
|
|
|
|
$dbr = wfGetDB( DB_SLAVE, array(), $dbName );
|
|
|
|
$title = Title::newFromText( $page );
|
|
|
|
$list = array();
|
|
|
|
$id = $dbr->selectField( 'page',
|
|
|
|
'page_id',
|
|
|
|
array( 'page_namespace' => $title->getNamespace(), 'page_title' => $title->getDBkey() ),
|
|
|
|
__METHOD__
|
|
|
|
);
|
|
|
|
if ( $id ) {
|
|
|
|
$res = $dbr->select( 'pagelinks',
|
|
|
|
'pl_title',
|
|
|
|
array( 'pl_from' => $id, 'pl_namespace' => NS_FILE ),
|
|
|
|
__METHOD__
|
|
|
|
);
|
|
|
|
foreach ( $res as $row ) {
|
|
|
|
$list[] = $row->pl_title;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
wfProfileOut( __METHOD__ );
|
|
|
|
return $list;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns list of images on given remote blacklist page.
|
|
|
|
* Not quite 100% bulletproof due to localised namespaces and so on.
|
|
|
|
* Though if you beat people if they add bad entries to the list... :)
|
|
|
|
* @param string $url
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
private static function getUrlBlacklist( $url ) {
|
|
|
|
global $wgFileExtensions;
|
|
|
|
wfProfileIn( __METHOD__ );
|
|
|
|
$list = array();
|
|
|
|
$text = Http::get( $url, 3 );
|
|
|
|
$regex = '/\[\[:([^|\#]*?\.(?:' . implode( '|', $wgFileExtensions ) . '))/i';
|
|
|
|
if ( $text && preg_match_all( $regex, $text, $matches ) ) {
|
|
|
|
foreach ( $matches[1] as $s ) {
|
|
|
|
$t = Title::makeTitleSafe( NS_FILE, $s );
|
|
|
|
if ( $t ) {
|
|
|
|
$list[] = $t->getDBkey();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
wfProfileOut( __METHOD__ );
|
|
|
|
return $list;
|
|
|
|
}
|
2012-02-29 10:50:36 +00:00
|
|
|
}
|