Thiemo Mättig (WMDE) has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/252902

Change subject: Split PageImages class into HookHandler classes
......................................................................

Split PageImages class into HookHandler classes

This patch only moves existing code around, but does not change any
implementation detail. All static methods (except the ones that are
used from the outside, e.g. in the MobileFrontend extension) become
non-static. This is done in preparation to make this code testable
and being able to refactor it further.

I could try to split this patch into multiple smaller patches, but
I'm not sure how such a split should look like. I could add one
HookHandler class per patch, but I don't really thing this would help.

I'm not touching the original namespaces of the original classes,
but organizing everything new in proper namespaces.

I'm not adding any new tests in this patch. I want to do this in
later patches.

Change-Id: I6e15fcf28857390d95d2c7c136c32297d117ad03
---
M PageImages.php
A includes/ApiOpenSearchSuggestHookHandler.php
M includes/ApiQueryPageImages.php
A includes/InfoActionHookHandler.php
A includes/LinksUpdateHookHandler.php
M includes/PageImages.php
A includes/ParserFileProcessingHookHandler.php
A includes/SpecialMobileEditWatchlistHookHandler.php
M maintenance/initImageData.php
M tests/phpunit/ApiQueryPageImagesTest.php
A tests/phpunit/LinksUpdateHookHandlerTest.php
M tests/phpunit/PageImagesTest.php
12 files changed, 752 insertions(+), 436 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/PageImages 
refs/changes/02/252902/1

diff --git a/PageImages.php b/PageImages.php
index 3d6fb74..1bbf63a 100644
--- a/PageImages.php
+++ b/PageImages.php
@@ -14,19 +14,24 @@
        'url'            => 
'https://www.mediawiki.org/wiki/Extension:PageImages'
 );
 
+$wgAutoloadClasses['PageImages\Hooks\ApiOpenSearchSuggestHookHandler'] = 
__DIR__ . '/includes/ApiOpenSearchSuggestHookHandler.php';
 $wgAutoloadClasses['ApiQueryPageImages'] = __DIR__ . 
'/includes/ApiQueryPageImages.php';
+$wgAutoloadClasses['PageImages\Hooks\InfoActionHookHandler'] = __DIR__ . 
'/includes/InfoActionHookHandler.php';
+$wgAutoloadClasses['PageImages\Hooks\LinksUpdateHookHandler'] = __DIR__ . 
'/includes/LinksUpdateHookHandler.php';
 $wgAutoloadClasses['PageImages'] = __DIR__ . '/includes/PageImages.php';
+$wgAutoloadClasses['PageImages\Hooks\ParserFileProcessingHookHandlers'] = 
__DIR__ . '/includes/ParserFileProcessingHookHandlers.php';
+$wgAutoloadClasses['PageImages\Hooks\SpecialMobileEditWatchlistHookHandlers'] 
= __DIR__ . '/includes/SpecialMobileEditWatchlistHookHandlers.php';
 
 $wgMessagesDirs['PageImages'] = __DIR__ . '/i18n';
 $wgExtensionMessagesFiles['PageImages'] = __DIR__ . "/PageImages.i18n.php";
 
-$wgHooks['ParserMakeImageParams'][] = 'PageImages::onParserMakeImageParams';
-$wgHooks['LinksUpdate'][] = 'PageImages::onLinksUpdate';
-$wgHooks['OpenSearchXml'][] = 'PageImages::onApiOpenSearchSuggest';
-$wgHooks['ApiOpenSearchSuggest'][] = 'PageImages::onApiOpenSearchSuggest';
-$wgHooks['InfoAction'][] = 'PageImages::onInfoAction';
-$wgHooks['AfterParserFetchFileAndTitle'][] = 
'PageImages::onAfterParserFetchFileAndTitle';
-$wgHooks['SpecialMobileEditWatchlist::images'][] = 
'PageImages::onSpecialMobileEditWatchlist_images';
+$wgHooks['ParserMakeImageParams'][] = 
'PageImages\Hooks\ParserFileProcessingHookHandlers::onParserMakeImageParams';
+$wgHooks['LinksUpdate'][] = 
'PageImages\Hooks\LinksUpdateHookHandler::onLinksUpdate';
+$wgHooks['OpenSearchXml'][] = 
'PageImages\Hooks\ApiOpenSearchSuggestHookHandler::onApiOpenSearchSuggest';
+$wgHooks['ApiOpenSearchSuggest'][] = 
'PageImages\Hooks\ApiOpenSearchSuggestHookHandler::onApiOpenSearchSuggest';
+$wgHooks['InfoAction'][] = 
'PageImages\Hooks\InfoActionHookHandler::onInfoAction';
+$wgHooks['AfterParserFetchFileAndTitle'][] = 
'PageImages\Hooks\ParserFileProcessingHookHandlers::onAfterParserFetchFileAndTitle';
+$wgHooks['SpecialMobileEditWatchlist::images'][] = 
'PageImages\Hooks\SpecialMobileEditWatchlistHookHandlers::onSpecialMobileEditWatchlist_images';
 
 $wgHooks['UnitTestsList'][] = function( array &$paths ) {
        $paths[] = __DIR__ . '/tests/phpunit';
diff --git a/includes/ApiOpenSearchSuggestHookHandler.php 
b/includes/ApiOpenSearchSuggestHookHandler.php
new file mode 100644
index 0000000..9cac819
--- /dev/null
+++ b/includes/ApiOpenSearchSuggestHookHandler.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace PageImages\Hooks;
+
+use PageImages;
+
+/**
+ * Handler for the "ApiOpenSearchSuggest" and the identical but deprecated 
"OpenSearchXml" hook.
+ *
+ * @license WTFPL 2.0
+ * @author MaxSem
+ * @author Thiemo Mättig
+ */
+class ApiOpenSearchSuggestHookHandler {
+
+       /**
+        * @var PageImages
+        */
+       private $pageImages;
+
+       function __construct() {
+               $this->pageImages = new PageImages();
+       }
+
+       /**
+        * ApiOpenSearchSuggest hook handler, enhances ApiOpenSearch results 
with this extension's data
+        *
+        * @param array[] &$results
+        *
+        * @return bool
+        */
+       public static function onApiOpenSearchSuggest( array &$results ) {
+               $handler = new self();
+               return $handler->doApiOpenSearchSuggest( &$results );
+       }
+
+       /**
+        * @param array[] &$results
+        *
+        * @return bool Always true.
+        */
+       public function doApiOpenSearchSuggest( array &$results ) {
+               global $wgPageImagesExpandOpenSearchXml;
+
+               if ( !$wgPageImagesExpandOpenSearchXml || !count( $results ) ) {
+                       return true;
+               }
+
+               $pageIds = array_keys( $results );
+               $data = $this->pageImages->getImages( $pageIds, 50 );
+               foreach ( $pageIds as $id ) {
+                       if ( isset( $data[$id]['thumbnail'] ) ) {
+                               $results[$id]['image'] = 
$data[$id]['thumbnail'];
+                       } else {
+                               $results[$id]['image'] = null;
+                       }
+               }
+
+               return true;
+       }
+
+}
diff --git a/includes/ApiQueryPageImages.php b/includes/ApiQueryPageImages.php
index 12b8c49..714a060 100644
--- a/includes/ApiQueryPageImages.php
+++ b/includes/ApiQueryPageImages.php
@@ -1,9 +1,18 @@
 <?php
+
 /**
  * Expose image information for a page via a new prop=pageimages API.
- * See https://www.mediawiki.org/wiki/Extension:PageImages#API
+ *
+ * @see https://www.mediawiki.org/wiki/Extension:PageImages#API
+ *
+ * @license WTFPL 2.0
+ * @author MaxSem
+ * @author Ryan Kaldari
+ * @author Yuvi Panda
+ * @author Sam Smith
  */
 class ApiQueryPageImages extends ApiQueryBase {
+
        public function __construct( $query, $moduleName ) {
                parent::__construct( $query, $moduleName, 'pi' );
        }
diff --git a/includes/InfoActionHookHandler.php 
b/includes/InfoActionHookHandler.php
new file mode 100644
index 0000000..11b077d
--- /dev/null
+++ b/includes/InfoActionHookHandler.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace PageImages\Hooks;
+
+use IContextSource;
+use PageImages;
+
+/**
+ * Handler for the "InfoAction" hook.
+ *
+ * @license WTFPL 2.0
+ * @author MaxSem
+ * @author Wctaiwan
+ * @author Thiemo Mättig
+ */
+class InfoActionHookHandler {
+
+       /**
+        * @var PageImages
+        */
+       private $pageImages;
+
+       function __construct() {
+               $this->pageImages = new PageImages();
+       }
+
+       /**
+        * InfoAction hook handler, adds the page image to the info=action page
+        *
+        * @see https://www.mediawiki.org/wiki/Manual:Hooks/InfoAction
+        *
+        * @param IContextSource $context
+        * @param array[] &$pageInfo
+        *
+        * @return bool
+        */
+       public static function onInfoAction( IContextSource $context, 
&$pageInfo ) {
+               $handler = new self();
+               return $handler->doInfoAction( $context, &$pageInfo );
+       }
+
+       /**
+        * @param IContextSource $context
+        * @param array[] &$pageInfo
+        *
+        * @return bool Always true.
+        */
+       public function doInfoAction( IContextSource $context, &$pageInfo ) {
+               global $wgDefaultUserOptions, $wgThumbLimits;
+
+               $imageFile = $this->pageImages->getPageImageFileForTitle( 
$context->getTitle() );
+               if ( !$imageFile ) {
+                       // The page has no image
+                       return true;
+               }
+
+               $thumbSetting = $context->getUser()->getOption(
+                       'thumbsize',
+                       $wgDefaultUserOptions['thumbsize']
+               );
+               $thumbSize = $wgThumbLimits[$thumbSetting];
+
+               $thumb = $imageFile->transform( array( 'width' => $thumbSize ) 
);
+               if ( !$thumb ) {
+                       return true;
+               }
+
+               $imageHtml = $thumb->toHtml(
+                       array(
+                               'alt' => $imageFile->getTitle()->getText(),
+                               'desc-link' => true,
+                       )
+               );
+
+               $pageInfo['header-basic'][] = array(
+                       $context->msg( 'pageimages-info-label' ),
+                       $imageHtml
+               );
+
+               return true;
+       }
+
+}
diff --git a/includes/LinksUpdateHookHandler.php 
b/includes/LinksUpdateHookHandler.php
new file mode 100644
index 0000000..7d5ccea
--- /dev/null
+++ b/includes/LinksUpdateHookHandler.php
@@ -0,0 +1,253 @@
+<?php
+
+namespace PageImages\Hooks;
+
+use Exception;
+use Http;
+use LinksUpdate;
+use PageImages;
+use Title;
+
+/**
+ * Handler for the "LinksUpdate" hook.
+ *
+ * @license WTFPL 2.0
+ * @author MaxSem
+ * @author Thiemo Mättig
+ */
+class LinksUpdateHookHandler {
+
+       /**
+        * LinksUpdate hook handler, sets at most 2 page properties depending 
on images on page
+        *
+        * @see https://www.mediawiki.org/wiki/Manual:Hooks/LinksUpdate
+        *
+        * @param LinksUpdate $linksUpdate
+        *
+        * @return bool
+        */
+       public static function onLinksUpdate( LinksUpdate $linksUpdate ) {
+               $handler = new self();
+               return $handler->doLinksUpdate( $linksUpdate );
+       }
+
+       /**
+        * @param LinksUpdate $linksUpdate
+        *
+        * @return bool Always true.
+        */
+       public function doLinksUpdate( LinksUpdate $linksUpdate ) {
+               $images = $linksUpdate->getParserOutput()->getExtensionData( 
'pageImages' );
+
+               if ( $images === null ) {
+                       return true;
+               }
+
+               $scores = array();
+               $counter = 0;
+
+               foreach ( $images as $image ) {
+                       $fileName = $image['filename'];
+
+                       if ( !isset( $scores[$fileName] ) ) {
+                               $scores[$fileName] = -1;
+                       }
+
+                       $scores[$fileName] = max( $scores[$fileName], 
$this->getScore( $image, $counter++ ) );
+               }
+
+               $image = false;
+
+               foreach ( $scores as $name => $score ) {
+                       if ( $score > 0 && ( !$image || $score > 
$scores[$image] ) ) {
+                               $image = $name;
+                       }
+               }
+
+               if ( $image ) {
+                       $linksUpdate->mProperties[PageImages::PROP_NAME] = 
$image;
+               }
+
+               return true;
+       }
+
+       /**
+        * 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
+        */
+       private function getScore( array $image, $position ) {
+               global $wgPageImagesScores;
+
+               if ( isset( $image['handler'] ) ) {
+                       // Standalone image
+                       $score = $this->scoreFromTable( 
$image['handler']['width'], $wgPageImagesScores['width'] );
+               } else {
+                       // From gallery
+                       $score = $this->scoreFromTable( $image['fullwidth'], 
$wgPageImagesScores['galleryImageWidth'] );
+               }
+
+               if ( isset( $wgPageImagesScores['position'][$position] ) ) {
+                       $score += $wgPageImagesScores['position'][$position];
+               }
+
+               $ratio = intval( $this->getRatio( $image ) * 10 );
+               $score += $this->scoreFromTable( $ratio, 
$wgPageImagesScores['ratio'] );
+
+               $blacklist = $this->getBlacklist();
+               if ( isset( $blacklist[$image['filename']] ) ) {
+                       $score = -1000;
+               }
+
+               return $score;
+       }
+
+       /**
+        * Returns score based on table of ranges
+        *
+        * @param int $value
+        * @param int[] $scores
+        *
+        * @return int
+        */
+       private function scoreFromTable( $value, array $scores ) {
+               $lastScore = 0;
+
+               foreach ( $scores as $boundary => $score ) {
+                       if ( $value <= $boundary ) {
+                               return $score;
+                       }
+
+                       $lastScore = $score;
+               }
+
+               return $lastScore;
+       }
+
+       /**
+        * Returns width/height ratio of an image as displayed or 0 is not 
available
+        *
+        * @param array $image
+        *
+        * @return float|int
+        */
+       private function getRatio( array $image ) {
+               $width = $image['fullwidth'];
+               $height = $image['fullheight'];
+
+               if ( !$width || !$height ) {
+                       return 0;
+               }
+
+               return $width / $height;
+       }
+
+       /**
+        * Returns a list of images blacklisted from influencing this 
extension's output
+        *
+        * @throws Exception
+        * @return int[] Flipped associative array in format "image BDB key" => 
int
+        */
+       private function getBlacklist() {
+               global $wgPageImagesBlacklist, $wgPageImagesBlacklistExpiry, 
$wgMemc;
+               static $list = false;
+
+               if ( $list !== false ) {
+                       return $list;
+               }
+
+               $key = wfMemcKey( 'pageimages', 'blacklist' );
+               $list = $wgMemc->get( $key );
+               if ( $list !== false ) {
+                       return $list;
+               }
+
+               wfDebug( __METHOD__ . "(): cache miss\n" );
+               $list = array();
+
+               foreach ( $wgPageImagesBlacklist as $source ) {
+                       switch ( $source['type'] ) {
+                               case 'db':
+                                       $list = array_merge( $list, 
$this->getDbBlacklist( $source['db'], $source['page'] ) );
+                                       break;
+                               case 'url':
+                                       $list = array_merge( $list, 
$this->getUrlBlacklist( $source['url'] ) );
+                                       break;
+                               default:
+                                       throw new Exception( __METHOD__ . "(): 
unrecognized image blacklist type '{$source['type']}'" );
+                       }
+               }
+
+               $list = array_flip( $list );
+               $wgMemc->set( $key, $list, $wgPageImagesBlacklistExpiry );
+               return $list;
+       }
+
+       /**
+        * Returns list of images linked by the given blacklist page
+        *
+        * @param string|bool $dbName Database name or false for current 
database
+        * @param string $page
+        *
+        * @return string[]
+        */
+       private function getDbBlacklist( $dbName, $page ) {
+               $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;
+                       }
+               }
+
+               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 string[]
+        */
+       private function getUrlBlacklist( $url ) {
+               global $wgFileExtensions;
+
+               $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();
+                               }
+                       }
+               }
+
+               return $list;
+       }
+
+}
diff --git a/includes/PageImages.php b/includes/PageImages.php
index 4e16f52..b606e11 100644
--- a/includes/PageImages.php
+++ b/includes/PageImages.php
@@ -1,10 +1,31 @@
 <?php
 
+/**
+ * @license WTFPL 2.0
+ * @author MaxSem
+ * @author Brad Jorsch
+ * @author Thiemo Mättig
+ */
 class PageImages {
+
        /**
         * Page property used to store the page image information
         */
        const PROP_NAME = 'page_image';
+
+       /**
+        * @deprecated Instantiate the class and use 
self::getPageImageFileForTitle instead.
+        *
+        * Returns page image for a given title
+        *
+        * @param Title $title: Title to get page image for
+        *
+        * @return File|bool
+        */
+       public static function getPageImage( Title $title ) {
+               $pageImages = new self();
+               return $pageImages->getPageImageFileForTitle( $title );
+       }
 
        /**
         * Returns page image for a given title
@@ -13,266 +34,21 @@
         *
         * @return File|bool
         */
-       public static function getPageImage( Title $title ) {
+       public function getPageImageFileForTitle( Title $title ) {
                $dbr = wfGetDB( DB_SLAVE );
                $name = $dbr->selectField( 'page_props',
                        'pp_value',
                        array( 'pp_page' => $title->getArticleID(), 
'pp_propname' => self::PROP_NAME ),
                        __METHOD__
                );
+
                $file = false;
+
                if ( $name ) {
                        $file = wfFindFile( $name );
                }
+
                return $file;
-       }
-
-       /**
-        * 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()] );
-       }
-
-       /**
-        * ParserMakeImageParams hook handler, saves extended information about 
images used on page
-        *
-        * @see 
https://www.mediawiki.org/wiki/Manual:Hooks/ParserMakeImageParams
-        *
-        * @param Title $title
-        * @param File|bool $file
-        * @param array &$params
-        * @param Parser $parser
-        * @return bool
-        */
-       public static function onParserMakeImageParams( Title $title, $file, 
array &$params, Parser $parser ) {
-               self::processFile( $parser, $file, $params );
-               return true;
-       }
-
-       /**
-        * AfterParserFetchFileAndTitle hook handler, saves information about 
gallery images
-        *
-        * @param Parser $parser
-        * @param ImageGalleryBase $ig
-        * @return bool
-        */
-       public static function onAfterParserFetchFileAndTitle( Parser $parser, 
ImageGalleryBase $ig ) {
-               foreach ( $ig->getImages() as $image ) {
-                       self::processFile( $parser, $image[0], null );
-               }
-               return true;
-       }
-
-       /**
-        * @param Parser $parser
-        * @param File|Title|null $file
-        * @param array|null $handlerParams
-        */
-       private static function processFile( Parser $parser, $file, 
$handlerParams ) {
-               if ( !$file || !self::processThisTitle( $parser->getTitle() ) ) 
{
-                       return;
-               }
-
-               if ( !$file instanceof File ) {
-                       $file = wfFindFile( $file );
-                       if ( !$file ) {
-                               return;
-                       }
-               }
-
-               if ( is_array( $handlerParams ) ) {
-                       $myParams = $handlerParams;
-                       self::calcWidth( $myParams, $file );
-               } else {
-                       $myParams = array();
-               }
-
-               $myParams['filename'] = $file->getTitle()->getDBkey();
-               $myParams['fullwidth'] = $file->getWidth();
-               $myParams['fullheight'] = $file->getHeight();
-
-               $out = $parser->getOutput();
-               $pageImages = $out->getExtensionData( 'pageImages' ) ?: array();
-               $pageImages[] = $myParams;
-               $out->setExtensionData( 'pageImages', $pageImages );
-       }
-
-       /**
-        * Estimates image size as displayed if not explicitly provided.
-        * We don't follow the core size calculation algorithm precisely 
because it's not required and editor's
-        * intentions are more important than the precise number.
-        *
-        * @param array &$params
-        * @param File $file
-        */
-       private static function calcWidth( array &$params, File $file ) {
-               global $wgThumbLimits, $wgDefaultUserOptions;
-
-               if ( isset( $params['handler']['width'] ) ) {
-                       return;
-               }
-               if ( isset( $params['handler']['height'] ) && 
$file->getHeight() > 0 ) {
-                       $params['handler']['width'] =
-                               $file->getWidth() * ( 
$params['handler']['height'] / $file->getHeight() );
-               } elseif ( isset( $params['frame']['thumbnail'] )
-                       || isset( $params['frame']['thumb'] )
-                       || isset( $params['frame']['frameless'] ) )
-               {
-                       $params['handler']['width'] = isset( 
$wgThumbLimits[$wgDefaultUserOptions['thumbsize']] )
-                               ? 
$wgThumbLimits[$wgDefaultUserOptions['thumbsize']]
-                               : 250;
-               } else {
-                       $params['handler']['width'] = $file->getWidth();
-               }
-       }
-
-       /**
-        * LinksUpdate hook handler, sets at most 2 page properties depending 
on images on page
-        *
-        * @see https://www.mediawiki.org/wiki/Manual:Hooks/LinksUpdate
-        *
-        * @param LinksUpdate $linksUpdate
-        * @return bool
-        */
-       public static function onLinksUpdate( LinksUpdate $linksUpdate ) {
-               $images = $linksUpdate->getParserOutput()->getExtensionData( 
'pageImages' );
-               if ( $images === null ) {
-                       return true;
-               }
-
-               $scores = array();
-               $counter = 0;
-               foreach ( $images as $image ) {
-                       $fileName = $image['filename'];
-                       if ( !isset( $scores[$fileName] ) ) {
-                               $scores[$fileName] = -1;
-                       }
-                       $scores[$fileName] = max( $scores[$fileName], 
self::getScore( $image, $counter++ ) );
-               }
-
-               $image = false;
-               foreach ( $scores as $name => $score ) {
-                       if ( $score > 0 && ( !$image || $score > 
$scores[$image] ) ) {
-                               $image = $name;
-                       }
-               }
-               if ( $image ) {
-                       $linksUpdate->mProperties[self::PROP_NAME] = $image;
-               }
-
-               return true;
-       }
-
-       /**
-        * InfoAction hook handler, adds the page image to the info=action page
-        *
-        * @see https://www.mediawiki.org/wiki/Manual:Hooks/InfoAction
-        *
-        * @param IContextSource $context
-        * @param array[] &$pageInfo
-        * @return bool
-        */
-       public static function onInfoAction( IContextSource $context, 
&$pageInfo ) {
-               global $wgDefaultUserOptions, $wgThumbLimits;
-
-               $imageFile = self::getPageImage( $context->getTitle() );
-               if ( !$imageFile ) {
-                       // The page has no image
-                       return true;
-               }
-
-               $thumbSetting = $context->getUser()->getOption(
-                       'thumbsize',
-                       $wgDefaultUserOptions['thumbsize']
-               );
-               $thumbSize = $wgThumbLimits[$thumbSetting];
-
-               $thumb = $imageFile->transform( array( 'width' => $thumbSize ) 
);
-               if ( !$thumb ) {
-                       return true;
-               }
-               $imageHtml = $thumb->toHtml(
-                       array(
-                               'alt' => $imageFile->getTitle()->getText(),
-                               'desc-link' => true,
-                       )
-               );
-
-               $pageInfo['header-basic'][] = array(
-                       $context->msg( 'pageimages-info-label' ),
-                       $imageHtml
-               );
-
-               return true;
-       }
-
-       /**
-        * ApiOpenSearchSuggest hook handler, enhances ApiOpenSearch results 
with this extension's data
-        *
-        * @param array[] &$results
-        * @return bool
-        */
-       public static function onApiOpenSearchSuggest( array &$results ) {
-               global $wgPageImagesExpandOpenSearchXml;
-
-               if ( !$wgPageImagesExpandOpenSearchXml || !count( $results ) ) {
-                       return true;
-               }
-
-               $pageIds = array_keys( $results );
-               $data = self::getImages( $pageIds, 50 );
-               foreach ( $pageIds as $id ) {
-                       if ( isset( $data[$id]['thumbnail'] ) ) {
-                               $results[$id]['image'] = 
$data[$id]['thumbnail'];
-                       } else {
-                               $results[$id]['image'] = null;
-                       }
-               }
-
-               return true;
-       }
-
-       /**
-        * SpecialMobileEditWatchlist::images hook handler, adds images to 
mobile watchlist A-Z view
-        *
-        * @param IContextSource $context
-        * @param array[] $watchlist
-        * @param array[] &$images
-        * @return bool Always true
-        */
-       public static function onSpecialMobileEditWatchlist_images( 
IContextSource $context, array $watchlist,
-               array &$images
-       ) {
-               $ids = array();
-               foreach ( $watchlist as $ns => $pages ) {
-                       foreach ( array_keys( $pages ) as $dbKey ) {
-                               $title = Title::makeTitle( $ns, $dbKey );
-                               // Getting page ID here is safe because 
SpecialEditWatchlist::getWatchlistInfo()
-                               // uses LinkBatch
-                               $id = $title->getArticleID();
-                               if ( $id ) {
-                                       $ids[$id] = $dbKey;
-                               }
-                       }
-               }
-
-               $data = self::getImages( array_keys( $ids ) );
-               foreach ( $data as $id => $page ) {
-                       if ( isset( $page['pageimage'] ) ) {
-                               $images[ $page['ns'] ][ $ids[$id] ] = 
$page['pageimage'];
-                       }
-               }
-
-               return true;
        }
 
        /**
@@ -280,9 +56,10 @@
         *
         * @param int[] $pageIds
         * @param int $size
+        *
         * @return array[]
         */
-       private static function getImages( array $pageIds, $size = 0 ) {
+       public function getImages( array $pageIds, $size = 0 ) {
                $request = array(
                        'action' => 'query',
                        'prop' => 'pageimages',
@@ -290,12 +67,15 @@
                        'pageids' => implode( '|', $pageIds ),
                        'pilimit' => 'max',
                );
+
                if ( $size ) {
                        $request['piprop'] = 'thumbnail';
                        $request['pithumbsize'] = $size;
                }
+
                $api = new ApiMain( new FauxRequest( $request ) );
                $api->execute();
+
                if ( defined( 'ApiResult::META_CONTENT' ) ) {
                        return (array)$api->getResult()->getResultData( array( 
'query', 'pages' ),
                                array( 'Strip' => 'base' ) );
@@ -308,164 +88,4 @@
                }
        }
 
-       /**
-        * 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
-        */
-       private static function getScore( array $image, $position ) {
-               global $wgPageImagesScores;
-
-               if ( isset( $image['handler'] ) ) {
-                       // Standalone image
-                       $score = self::scoreFromTable( 
$image['handler']['width'], $wgPageImagesScores['width'] );
-               } else {
-                       // From gallery
-                       $score = self::scoreFromTable( $image['fullwidth'], 
$wgPageImagesScores['galleryImageWidth'] );
-               }
-
-               if ( isset( $wgPageImagesScores['position'][$position] ) ) {
-                       $score += $wgPageImagesScores['position'][$position];
-               }
-
-               $ratio = intval( self::getRatio( $image ) * 10 );
-               $score += self::scoreFromTable( $ratio, 
$wgPageImagesScores['ratio'] );
-
-               $blacklist = self::getBlacklist();
-               if ( isset( $blacklist[$image['filename']] ) ) {
-                       $score = -1000;
-               }
-
-               return $score;
-       }
-
-       /**
-        * Returns width/height ratio of an image as displayed or 0 is not 
available
-        *
-        * @param array $image
-        * @return float|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 $value
-        * @param int[] $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;
-       }
-
-       /**
-        * Returns a list of images blacklisted from influencing this 
extension's output
-        *
-        * @throws Exception
-        * @return int[] 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;
-               }
-
-               $key = wfMemcKey( 'pageimages', 'blacklist' );
-               $list = $wgMemc->get( $key );
-               if ( $list !== false ) {
-                       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 Exception( __METHOD__ . "(): 
unrecognized image blacklist type '{$source['type']}'" );
-                       }
-               }
-               $list = array_flip( $list );
-               $wgMemc->set( $key, $list, $wgPageImagesBlacklistExpiry );
-               return $list;
-       }
-
-       /**
-        * Returns list of images linked by the given blacklist page
-        *
-        * @param string|bool $dbName Database name or false for current 
database
-        * @param string $page
-        * @return string[]
-        */
-       private static function getDbBlacklist( $dbName, $page ) {
-               $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;
-                       }
-               }
-
-               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 string[]
-        */
-       private static function getUrlBlacklist( $url ) {
-               global $wgFileExtensions;
-
-               $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();
-                               }
-                       }
-               }
-
-               return $list;
-       }
 }
diff --git a/includes/ParserFileProcessingHookHandler.php 
b/includes/ParserFileProcessingHookHandler.php
new file mode 100644
index 0000000..978ca6c
--- /dev/null
+++ b/includes/ParserFileProcessingHookHandler.php
@@ -0,0 +1,168 @@
+<?php
+
+namespace PageImages\Hooks;
+
+use File;
+use ImageGalleryBase;
+use Parser;
+use Title;
+
+/**
+ * Handler for the "ParserMakeImageParams" and "AfterParserFetchFileAndTitle" 
hooks.
+ *
+ * @license WTFPL 2.0
+ * @author MaxSem
+ * @author Thiemo Mättig
+ */
+class ParserFileProcessingHookHandler {
+
+       /**
+        * ParserMakeImageParams hook handler, saves extended information about 
images used on page
+        *
+        * @see 
https://www.mediawiki.org/wiki/Manual:Hooks/ParserMakeImageParams
+        *
+        * @param Title $title
+        * @param File|bool $file
+        * @param array &$params
+        * @param Parser $parser
+        *
+        * @return bool
+        */
+       public static function onParserMakeImageParams(
+               Title $title,
+               $file,
+               array &$params,
+               Parser $parser
+       ) {
+               $handler = new self();
+               return $handler->doParserMakeImageParams( $title, $file, 
&$params, $parser );
+       }
+
+       /**
+        * AfterParserFetchFileAndTitle hook handler, saves information about 
gallery images
+        *
+        * @param Parser $parser
+        * @param ImageGalleryBase $gallery
+        *
+        * @return bool
+        */
+       public static function onAfterParserFetchFileAndTitle( Parser $parser, 
ImageGalleryBase $gallery ) {
+               $handler = new self();
+               return $handler->doAfterParserFetchFileAndTitle( $parser, 
$gallery );
+       }
+
+       /**
+        * @param Title $title
+        * @param File|bool $file
+        * @param array &$params
+        * @param Parser $parser
+        *
+        * @return bool Always true.
+        */
+       public function doParserMakeImageParams(
+               Title $title,
+               $file,
+               array &$params,
+               Parser $parser
+       ) {
+               $this->processFile( $parser, $file, $params );
+               return true;
+       }
+
+       /**
+        * @param Parser $parser
+        * @param ImageGalleryBase $gallery
+        *
+        * @return bool Always true.
+        */
+       public function doAfterParserFetchFileAndTitle( Parser $parser, 
ImageGalleryBase $gallery ) {
+               foreach ( $gallery->getImages() as $image ) {
+                       $this->processFile( $parser, $image[0], null );
+               }
+
+               return true;
+       }
+
+       /**
+        * @param Parser $parser
+        * @param File|Title|null $file
+        * @param array|null $handlerParams
+        */
+       private function processFile( Parser $parser, $file, $handlerParams ) {
+               if ( !$file || !$this->processThisTitle( $parser->getTitle() ) 
) {
+                       return;
+               }
+
+               if ( !( $file instanceof File ) ) {
+                       $file = wfFindFile( $file );
+                       if ( !$file ) {
+                               return;
+                       }
+               }
+
+               if ( is_array( $handlerParams ) ) {
+                       $myParams = $handlerParams;
+                       $this->calcWidth( $myParams, $file );
+               } else {
+                       $myParams = array();
+               }
+
+               $myParams['filename'] = $file->getTitle()->getDBkey();
+               $myParams['fullwidth'] = $file->getWidth();
+               $myParams['fullheight'] = $file->getHeight();
+
+               $out = $parser->getOutput();
+               $pageImages = $out->getExtensionData( 'pageImages' ) ?: array();
+               $pageImages[] = $myParams;
+               $out->setExtensionData( 'pageImages', $pageImages );
+       }
+
+       /**
+        * Returns true if data for this title should be saved
+        *
+        * @param Title $title
+        *
+        * @return bool
+        */
+       private function processThisTitle( Title $title ) {
+               global $wgPageImagesNamespaces;
+               static $flipped = false;
+
+               if ( $flipped === false ) {
+                       $flipped = array_flip( $wgPageImagesNamespaces );
+               }
+
+               return isset( $flipped[$title->getNamespace()] );
+       }
+
+       /**
+        * Estimates image size as displayed if not explicitly provided. We 
don't follow the core size
+        * calculation algorithm precisely because it's not required and 
editor's intentions are more
+        * important than the precise number.
+        *
+        * @param array &$params
+        * @param File $file
+        */
+       private function calcWidth( array &$params, File $file ) {
+               global $wgThumbLimits, $wgDefaultUserOptions;
+
+               if ( isset( $params['handler']['width'] ) ) {
+                       return;
+               }
+
+               if ( isset( $params['handler']['height'] ) && 
$file->getHeight() > 0 ) {
+                       $params['handler']['width'] =
+                               $file->getWidth() * ( 
$params['handler']['height'] / $file->getHeight() );
+               } elseif ( isset( $params['frame']['thumbnail'] )
+                       || isset( $params['frame']['thumb'] )
+                       || isset( $params['frame']['frameless'] ) )
+               {
+                       $params['handler']['width'] = isset( 
$wgThumbLimits[$wgDefaultUserOptions['thumbsize']] )
+                               ? 
$wgThumbLimits[$wgDefaultUserOptions['thumbsize']]
+                               : 250;
+               } else {
+                       $params['handler']['width'] = $file->getWidth();
+               }
+       }
+
+}
diff --git a/includes/SpecialMobileEditWatchlistHookHandler.php 
b/includes/SpecialMobileEditWatchlistHookHandler.php
new file mode 100644
index 0000000..c3f1efd
--- /dev/null
+++ b/includes/SpecialMobileEditWatchlistHookHandler.php
@@ -0,0 +1,84 @@
+<?php
+
+namespace PageImages\Hooks;
+
+use IContextSource;
+use PageImages;
+use Title;
+
+/**
+ * Handler for the "SpecialMobileEditWatchlist::images" hook.
+ *
+ * @license WTFPL 2.0
+ * @author MaxSem
+ * @author Thiemo Mättig
+ */
+class SpecialMobileEditWatchlistHookHandlers {
+
+       /**
+        * @var PageImages
+        */
+       private $pageImages;
+
+       function __construct() {
+               $this->pageImages = new PageImages();
+       }
+
+       /**
+        * SpecialMobileEditWatchlist::images hook handler, adds images to 
mobile watchlist A-Z view
+        *
+        * @param IContextSource $context
+        * @param array[] $watchlist
+        * @param array[] &$images
+        *
+        * @return bool
+        */
+       public static function onSpecialMobileEditWatchlist_images(
+               IContextSource $context,
+               array $watchlist,
+               array &$images
+       ) {
+               $handler = new self();
+               return $handler->doSpecialMobileEditWatchlist_images( $context, 
$watchlist, &$images);
+       }
+
+       /**
+        * SpecialMobileEditWatchlist::images hook handler, adds images to 
mobile watchlist A-Z view
+        *
+        * @param IContextSource $context
+        * @param array[] $watchlist
+        * @param array[] &$images
+        *
+        * @return bool Always true
+        */
+       public function doSpecialMobileEditWatchlist_images(
+               IContextSource $context,
+               array $watchlist,
+               array &$images
+       ) {
+               $ids = array();
+
+               foreach ( $watchlist as $ns => $pages ) {
+                       foreach ( array_keys( $pages ) as $dbKey ) {
+                               $title = Title::makeTitle( $ns, $dbKey );
+                               // Getting page ID here is safe because 
SpecialEditWatchlist::getWatchlistInfo()
+                               // uses LinkBatch
+                               $id = $title->getArticleID();
+                               if ( $id ) {
+                                       $ids[$id] = $dbKey;
+                               }
+                       }
+               }
+
+               $data = $this->pageImages->getImages( array_keys( $ids ) );
+
+               foreach ( $data as $id => $page ) {
+                       if ( isset( $page['pageimage'] ) ) {
+                               $images[ $page['ns'] ][ $ids[$id] ] = 
$page['pageimage'];
+                       }
+               }
+
+               return true;
+       }
+
+}
diff --git a/maintenance/initImageData.php b/maintenance/initImageData.php
index a0882dc..7ddecfb 100644
--- a/maintenance/initImageData.php
+++ b/maintenance/initImageData.php
@@ -6,6 +6,11 @@
 }
 require_once( "$IP/maintenance/Maintenance.php" );
 
+/**
+ * @license WTFPL 2.0
+ * @author MaxSem
+ * @author Thiemo Mättig
+ */
 class InitImageData extends Maintenance {
        const BATCH_SIZE = 100;
 
diff --git a/tests/phpunit/ApiQueryPageImagesTest.php 
b/tests/phpunit/ApiQueryPageImagesTest.php
index e2434e1..fdba446 100644
--- a/tests/phpunit/ApiQueryPageImagesTest.php
+++ b/tests/phpunit/ApiQueryPageImagesTest.php
@@ -45,7 +45,7 @@
  *
  * @group PageImages
  *
- * @licence GNU GPL v2+
+ * @license WTFPL 2.0
  * @author Thiemo Mättig
  */
 class ApiQueryPageImagesTest extends PHPUnit_Framework_TestCase {
diff --git a/tests/phpunit/LinksUpdateHookHandlerTest.php 
b/tests/phpunit/LinksUpdateHookHandlerTest.php
new file mode 100644
index 0000000..4e6f080
--- /dev/null
+++ b/tests/phpunit/LinksUpdateHookHandlerTest.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace PageImages\Tests\Hooks;
+
+use PageImages\Hooks\LinksUpdateHookHandler;
+use PageImages;
+use ParserOutput;
+use PHPUnit_Framework_TestCase;
+
+/**
+ * @covers PageImages\Hooks\LinksUpdateHookHandler
+ *
+ * @group PageImages
+ *
+ * @license WTFPL 2.0
+ * @author Thiemo Mättig
+ */
+class LinksUpdateHookHandlerTest extends PHPUnit_Framework_TestCase {
+
+       public function testOnLinksUpdate() {
+               $parserOutput = new ParserOutput();
+               $parserOutput->setExtensionData( 'pageImages', array(
+                       array( 'filename' => 'A.jpg', 'fullwidth' => 100, 
'fullheight' => 50 ),
+               ) );
+
+               $linksUpdate = $this->getMockBuilder( 'LinksUpdate' )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+               $linksUpdate->expects( $this->any() )
+                       ->method( 'getParserOutput' )
+                       ->will( $this->returnValue( $parserOutput ) );
+
+               $this->assertTrue( LinksUpdateHookHandler::onLinksUpdate( 
$linksUpdate ) );
+               $this->assertTrue( property_exists( $linksUpdate, 'mProperties' 
), 'precondition' );
+               $this->assertSame( 'A.jpg', 
$linksUpdate->mProperties[PageImages::PROP_NAME] );
+       }
+
+}
diff --git a/tests/phpunit/PageImagesTest.php b/tests/phpunit/PageImagesTest.php
index e2b8df6..1029b04 100644
--- a/tests/phpunit/PageImagesTest.php
+++ b/tests/phpunit/PageImagesTest.php
@@ -4,7 +4,6 @@
 
 use MediaWikiTestCase;
 use PageImages;
-use ParserOutput;
 use Title;
 
 /**
@@ -13,7 +12,7 @@
  * @group PageImages
  * @group Database
  *
- * @licence GNU GPL v2+
+ * @license WTFPL 2.0
  * @author Thiemo Mättig
  */
 class PageImagesTest extends MediaWikiTestCase {
@@ -34,22 +33,12 @@
                $this->assertFalse( PageImages::getPageImage( $title ) );
        }
 
-       public function testOnLinksUpdate() {
-               $parserOutput = new ParserOutput();
-               $parserOutput->setExtensionData( 'pageImages', array(
-                       array( 'filename' => 'A.jpg', 'fullwidth' => 100, 
'fullheight' => 50 ),
-               ) );
+       public function 
testGivenNonExistingPage_getPageImageForTitleReturnsFalse() {
+               $title = Title::newFromText( wfRandomString() );
+               $title->resetArticleID( 0 );
 
-               $linksUpdate = $this->getMockBuilder( 'LinksUpdate' )
-                       ->disableOriginalConstructor()
-                       ->getMock();
-               $linksUpdate->expects( $this->any() )
-                       ->method( 'getParserOutput' )
-                       ->will( $this->returnValue( $parserOutput ) );
-
-               $this->assertTrue( PageImages::onLinksUpdate( $linksUpdate ) );
-               $this->assertTrue( property_exists( $linksUpdate, 'mProperties' 
), 'precondition' );
-               $this->assertSame( 'A.jpg', 
$linksUpdate->mProperties[PageImages::PROP_NAME] );
+               $pageImages = new PageImages();
+               $this->assertFalse( $pageImages->getPageImageFileForTitle( 
$title ) );
        }
 
 }

-- 
To view, visit https://gerrit.wikimedia.org/r/252902
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I6e15fcf28857390d95d2c7c136c32297d117ad03
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/PageImages
Gerrit-Branch: master
Gerrit-Owner: Thiemo Mättig (WMDE) <thiemo.maet...@wikimedia.de>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to