Ladsgroup has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/403943 )

Change subject: Move helper methods of Hooks.php to Hooks\Helpers class
......................................................................

Move helper methods of Hooks.php to Hooks\Helpers class

This makes the monster of class named Hooks small enough
Also makes the code more reasonable and readable

Change-Id: Id73153c0f99f8961311a1bba167387bce2175d1a
---
M includes/ApiQueryORES.php
M includes/Hooks.php
M includes/Hooks/ApiHooksHandler.php
M includes/Hooks/ChangesListHooksHandler.php
M includes/Hooks/ContributionsHooksHandler.php
A includes/Hooks/Helpers.php
M includes/Hooks/PreferencesHookHandler.php
M includes/WatchedItemQueryServiceExtension.php
A tests/phpunit/includes/Hooks/HelpersTest.php
M tests/phpunit/includes/HooksTest.php
10 files changed, 457 insertions(+), 423 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/ORES 
refs/changes/43/403943/1

diff --git a/includes/ApiQueryORES.php b/includes/ApiQueryORES.php
index 10c8057..ffe03c8 100644
--- a/includes/ApiQueryORES.php
+++ b/includes/ApiQueryORES.php
@@ -20,6 +20,7 @@
 use ApiQuery;
 use ApiQueryBase;
 use MediaWiki\MediaWikiServices;
+use ORES\Hooks\Helpers;
 
 /**
  * A query action to return meta information about ORES models and
@@ -43,7 +44,7 @@
                        'wikiid' => $wgOresWikiId ?: wfWikiID(),
                        'models' => [],
                        'excludebots' => (bool)$wgOresExcludeBots,
-                       'damagingthresholds' => Hooks::getDamagingThresholds(),
+                       'damagingthresholds' => 
Helpers::getDamagingThresholds(),
                        'namespaces' => $wgOresEnabledNamespaces
                                ? array_keys( array_filter( 
$wgOresEnabledNamespaces ) )
                                : \MWNamespace::getValidNamespaces(),
diff --git a/includes/Hooks.php b/includes/Hooks.php
index ffe69db..73822a1 100644
--- a/includes/Hooks.php
+++ b/includes/Hooks.php
@@ -17,28 +17,16 @@
 namespace ORES;
 
 use DatabaseUpdater;
-use Exception;
 use JobQueueGroup;
-use IContextSource;
 use MediaWiki\Logger\LoggerFactory;
 use MediaWiki\MediaWikiServices;
+use ORES\Hooks\Helpers;
 use OutputPage;
 use RecentChange;
 use RequestContext;
 use Skin;
-use SpecialRecentChanges;
-use SpecialWatchlist;
-use User;
-use Title;
 
 class Hooks {
-       // The oresDamagingPref preference uses these names for historical 
reasons
-       // TODO: Move to a better place
-       public static $damagingPrefMap = [
-               'hard' => 'maybebad',
-               'soft' => 'likelybad',
-               'softest' => 'verylikelybad',
-       ];
 
        /**
         * @param DatabaseUpdater $updater
@@ -124,52 +112,13 @@
        }
 
        /**
-        * Internal helper to get damaging level preference
-        * with backward compatibility for old level names
-        * @param User $user
-        * @param Title $title
-        * @return string 'maybebad', 'likelybad', or 'verylikelybad'
-        */
-       public static function getDamagingLevelPreference( User $user, Title 
$title = null ) {
-               $option = !$title || self::isWLPage( $title ) ?
-                               'oresDamagingPref' :
-                               'rcOresDamagingPref';
-
-               $pref = $user->getOption( $option );
-               if ( isset( self::$damagingPrefMap[ $pref ] ) ) {
-                       $pref = self::$damagingPrefMap[ $pref ];
-               }
-               return $pref;
-       }
-
-       /**
-        * Internal helper to get threshold
-        * @param string $type
-        * @param User $user
-        * @param Title|null $title
-        * @return float|null Threshold, or null if not set
-        * @throws Exception When $type is not recognized
-        */
-       public static function getThreshold( $type, User $user, Title $title = 
null ) {
-               if ( $type === 'damaging' ) {
-                       $pref = self::getDamagingLevelPreference( $user, $title 
);
-                       $thresholds = self::getDamagingThresholds();
-                       if ( isset( $thresholds[ $pref ] ) ) {
-                               return $thresholds[ $pref ];
-                       }
-                       return null;
-               }
-               throw new Exception( "Unknown ORES test: '$type'" );
-       }
-
-       /**
         * Add CSS styles to output page
         *
         * @param OutputPage &$out
         * @param Skin &$skin
         */
        public static function onBeforePageDisplay( OutputPage &$out, Skin 
&$skin ) {
-               if ( !self::oresUiEnabled( $out->getUser() ) ) {
+               if ( !Helpers::oresUiEnabled( $out->getUser() ) ) {
                        return;
                }
 
@@ -179,179 +128,12 @@
                        $out->addJsConfigVars( 'oresData', $oresData );
                        $out->addJsConfigVars(
                                'oresThresholds',
-                               [ 'damaging' => self::getDamagingThresholds() ]
+                               [ 'damaging' => 
Helpers::getDamagingThresholds() ]
                        );
                        $out->addModuleStyles( 'ext.ores.styles' );
-                       if ( self::isHighlightEnabled( $out ) ) {
+                       if ( Helpers::isHighlightEnabled( $out ) ) {
                                $out->addModules( 'ext.ores.highlighter' );
                        }
-               }
-       }
-
-       public static function getDamagingThresholds() {
-               $stats = MediaWikiServices::getInstance()->getService( 
'ORESThresholdLookup' );
-               $thresholds = [];
-               foreach ( $stats->getThresholds( 'damaging' ) as $name => 
$bounds ) {
-                       $thresholds[ $name ] = $bounds[ 'min' ];
-               }
-               unset( $thresholds[ 'likelygood' ] );
-               return $thresholds;
-       }
-
-       /**
-        * Check whether ores is enabled
-        *
-        * @param User $user
-        * @return bool
-        */
-       public static function oresUiEnabled( User $user ) {
-               global $wgOresUiEnabled;
-
-               // Is the UI enabled or not?  If not, we've been deployed in
-               // infrastructure-only mode, so hide all the UI elements.
-               if ( !$wgOresUiEnabled ) {
-                       return false;
-               }
-
-               return true;
-       }
-
-       /**
-        * @param Title $title
-        * @return bool Whether $title is a RecentChanges page
-        */
-       private static function isRCPage( Title $title ) {
-               return $title->isSpecial( 'Recentchanges' ) ||
-                       $title->isSpecial( 'Recentchangeslinked' );
-       }
-
-       /**
-        * @param Title $title
-        * @return bool Whether $title is the Watchlist page
-        */
-       private static function isWLPage( Title $title ) {
-               return $title->isSpecial( 'Watchlist' );
-       }
-
-       public static function isRCStructuredUiEnabled( IContextSource $context 
) {
-               $page = new SpecialRecentChanges();
-               $page->setContext( $context );
-               return $page->isStructuredFilterUiEnabled();
-       }
-
-       public static function isWLStructuredUiEnabled( IContextSource $context 
) {
-               $page = new SpecialWatchlist();
-               $page->setContext( $context );
-               return $page->isStructuredFilterUiEnabled();
-       }
-
-       /**
-        * @param IContextSource $context
-        * @return bool Whether highlights should be shown
-        */
-       public static function isHighlightEnabled( IContextSource $context ) {
-               // Was previously controlled by different preferences than the 
"r", but they're currently
-               // the same.
-               return self::isDamagingFlagEnabled( $context );
-       }
-
-       /**
-        * @param IContextSource $context
-        * @return bool Whether the damaging flag ("r") should be shown
-        */
-       public static function isDamagingFlagEnabled( IContextSource $context ) 
{
-               $user = $context->getUser();
-
-               if ( !self::oresUiEnabled( $user ) ) {
-                       return false;
-               }
-
-               if ( self::isRCPage( $context->getTitle() ) ) {
-                       return !self::isRCStructuredUiEnabled( $context ) &&
-                               $user->getBoolOption( 'ores-damaging-flag-rc' );
-               }
-
-               if ( self::isWLPage( $context->getTitle() ) ) {
-                       return !self::isWLStructuredUiEnabled( $context ) &&
-                               $user->getBoolOption( 'oresHighlight' );
-               }
-
-               return $user->getBoolOption( 'oresHighlight' );
-       }
-
-       /**
-        * Check whether a given model is enabled in the config
-        * @param string $model
-        * @return bool
-        */
-       public static function isModelEnabled( $model ) {
-               global $wgOresModels;
-               return isset( $wgOresModels[$model] ) && $wgOresModels[$model];
-       }
-
-       /**
-        * @param IContextSource $context
-        * @param int $revisionId
-        * @param float $score
-        * @param string $model
-        */
-       public static function addRowData( IContextSource $context, 
$revisionId, $score, $model ) {
-               $out = $context->getOutput();
-               $data = $out->getProperty( 'oresData' );
-               if ( !isset( $data[$revisionId] ) ) {
-                       $data[$revisionId] = [];
-               }
-               $data[$revisionId][$model] = $score;
-               $out->setProperty( 'oresData', $data );
-       }
-
-       public static function joinWithOresTables(
-               $type,
-               $revIdField,
-               array &$tables,
-               array &$fields,
-               array &$join_conds
-       ) {
-               if ( !ctype_lower( $type ) ) {
-                       throw new Exception(
-                               "Invalid value for parameter 'type': '$type'. " 
.
-                               'Restricted to one lower case word to prevent 
accidental injection.'
-                       );
-               }
-
-               $modelId = MediaWikiServices::getInstance()->getService( 
'ORESModelLookup' )->getModelId(
-                       $type
-               );
-               $tables["ores_${type}_cls"] = 'ores_classification';
-
-               $fields["ores_${type}_score"] = 
"ores_${type}_cls.oresc_probability";
-
-               $join_conds["ores_${type}_cls"] = [ 'LEFT JOIN', [
-                       "ores_${type}_cls.oresc_model" => $modelId,
-                       "ores_${type}_cls.oresc_rev=$revIdField",
-                       "ores_${type}_cls.oresc_class" => 1
-               ] ];
-       }
-
-       public static function hideNonDamagingFilter(
-               array &$fields,
-               array &$conds,
-               $hidenondamaging,
-               User $user,
-               Title $title = null
-       ) {
-               $dbr = \wfGetDB( DB_REPLICA );
-               // Add user-based threshold
-               $threshold = self::getThreshold( 'damaging', $user, $title );
-               if ( $threshold === null ) {
-                       return;
-               }
-               // FIXME: This is not a "filter" but an undocumented side 
effect of this function.
-               $fields['ores_damaging_threshold'] = $threshold;
-
-               if ( $hidenondamaging ) {
-                       // Filter out non-damaging edits.
-                       $conds[] = 'ores_damaging_cls.oresc_probability > ' . 
$dbr->addQuotes( $threshold );
                }
        }
 
diff --git a/includes/Hooks/ApiHooksHandler.php 
b/includes/Hooks/ApiHooksHandler.php
index bbfb472..02f3b33 100644
--- a/includes/Hooks/ApiHooksHandler.php
+++ b/includes/Hooks/ApiHooksHandler.php
@@ -33,7 +33,6 @@
 use MediaWiki\Logger\LoggerFactory;
 use MediaWiki\MediaWikiServices;
 use ORES\FetchScoreJob;
-use ORES\Hooks;
 use ORES\Parser\ScoreParser;
 use ORES\ScoreFetcher;
 use ORES\WatchedItemQueryServiceExtension;
@@ -70,7 +69,7 @@
                        $params['prop'][ApiBase::PARAM_TYPE][] = 'oresscores';
                }
 
-               if ( Hooks::isModelEnabled( 'damaging' ) && (
+               if ( Helpers::isModelEnabled( 'damaging' ) && (
                        $module instanceof ApiQueryRecentChanges ||
                        $module instanceof ApiQueryWatchlist ||
                        $module instanceof ApiQueryContributions
@@ -124,7 +123,7 @@
                        return;
                }
 
-               $show = Hooks::isModelEnabled( 'damaging' ) && isset( 
$params['show'] )
+               $show = Helpers::isModelEnabled( 'damaging' ) && isset( 
$params['show'] )
                        ? array_flip( $params['show'] )
                        : [];
                if ( isset( $show['oresreview'] ) || isset( 
$show['!oresreview'] ) ) {
@@ -136,7 +135,8 @@
                                }
                        }
 
-                       $threshold = Hooks::getThreshold( 'damaging', 
$module->getUser(), $module->getTitle() );
+                       $threshold =
+                               Helpers::getThreshold( 'damaging', 
$module->getUser(), $module->getTitle() );
                        $dbr = \wfGetDB( DB_REPLICA );
 
                        $tables[] = 'ores_classification';
diff --git a/includes/Hooks/ChangesListHooksHandler.php 
b/includes/Hooks/ChangesListHooksHandler.php
index 8814119..f98b17d 100644
--- a/includes/Hooks/ChangesListHooksHandler.php
+++ b/includes/Hooks/ChangesListHooksHandler.php
@@ -24,7 +24,6 @@
 use FormOptions;
 use IContextSource;
 use MediaWiki\MediaWikiServices;
-use ORES\Hooks;
 use ORES\Range;
 use RCCacheEntry;
 use RecentChange;
@@ -37,7 +36,7 @@
        public static function onChangesListSpecialPageStructuredFilters(
                ChangesListSpecialPage $clsp
        ) {
-               if ( !Hooks::oresUiEnabled( $clsp->getUser() ) ) {
+               if ( !Helpers::oresUiEnabled( $clsp->getUser() ) ) {
                        return;
                }
 
@@ -50,7 +49,7 @@
                $changeTypeGroup = $clsp->getFilterGroup( 'changeType' );
                $logFilter = $changeTypeGroup->getFilter( 'hidelog' );
 
-               if ( Hooks::isModelEnabled( 'damaging' ) ) {
+               if ( Helpers::isModelEnabled( 'damaging' ) ) {
                        if ( $clsp instanceof SpecialRecentChanges ) {
                                $damagingDefault = $clsp->getUser()->getOption( 
'oresRCHideNonDamaging' );
                                $highlightDefault = 
$clsp->getUser()->getBoolOption( 'ores-damaging-flag-rc' );
@@ -124,10 +123,8 @@
                                }
 
                                if ( $damagingDefault ) {
-                                       $newDamagingGroup->setDefault( 
Hooks::getDamagingLevelPreference(
-                                               $clsp->getUser(),
-                                               $clsp->getPageTitle()
-                                       ) );
+                                       $newDamagingGroup->setDefault( 
Helpers::getDamagingLevelPreference( $clsp->getUser(),
+                                               $clsp->getPageTitle() ) );
                                }
 
                                if ( $highlightDefault ) {
@@ -137,10 +134,9 @@
                                                'verylikelybad' => 'c5',
                                        ];
 
-                                       $prefLevel = 
Hooks::getDamagingLevelPreference(
-                                               $clsp->getUser(),
-                                               $clsp->getPageTitle()
-                                       );
+                                       $prefLevel =
+                                               
Helpers::getDamagingLevelPreference( $clsp->getUser(),
+                                                       $clsp->getPageTitle() );
                                        $allLevels = array_keys( $levelsColors 
);
                                        $applicableLevels = array_slice( 
$allLevels, array_search( $prefLevel, $allLevels ) );
                                        $applicableLevels = array_intersect( 
$applicableLevels, array_keys( $filters ) );
@@ -168,7 +164,8 @@
                                                'default' => $damagingDefault,
                                                'queryCallable' => function ( 
$specialClassName, $ctx, $dbr, &$tables,
                                                                &$fields, 
&$conds, &$query_options, &$join_conds ) {
-                                                       
Hooks::hideNonDamagingFilter( $fields, $conds, true, $ctx->getUser(), 
$ctx->getTitle() );
+                                                       
Helpers::hideNonDamagingFilter( $fields, $conds, true, $ctx->getUser(),
+                                                               
$ctx->getTitle() );
                                                        // Filter out 
incompatible types; log actions and external rows are not scorable
                                                        $conds[] = 'rc_type NOT 
IN (' . $dbr->makeList( [ RC_LOG, RC_EXTERNAL ] ) . ')';
                                                        // Filter out patrolled 
edits: the 'r' doesn't appear for them
@@ -187,7 +184,7 @@
 
                        $clsp->registerFilterGroup( $legacyDamagingGroup );
                }
-               if ( Hooks::isModelEnabled( 'goodfaith' ) ) {
+               if ( Helpers::isModelEnabled( 'goodfaith' ) ) {
                        $filters = 
self::getGoodFaithStructuredFiltersOnChangesList(
                                $stats->getThresholds( 'goodfaith' )
                        );
@@ -379,7 +376,7 @@
        ) {
                global $wgUser, $wgRequest;
 
-               if ( !Hooks::oresUiEnabled( $wgUser ) ) {
+               if ( !Helpers::oresUiEnabled( $wgUser ) ) {
                        return;
                }
 
@@ -387,23 +384,13 @@
                        return;
                }
 
-               if ( Hooks::isModelEnabled( 'damaging' ) ) {
-                       Hooks::joinWithOresTables(
-                               'damaging',
-                               'rc_this_oldid',
-                               $tables,
-                               $fields,
-                               $join_conds
-                       );
+               if ( Helpers::isModelEnabled( 'damaging' ) ) {
+                       Helpers::joinWithOresTables( 'damaging', 
'rc_this_oldid', $tables, $fields,
+                               $join_conds );
                }
-               if ( Hooks::isModelEnabled( 'goodfaith' ) ) {
-                       Hooks::joinWithOresTables(
-                               'goodfaith',
-                               'rc_this_oldid',
-                               $tables,
-                               $fields,
-                               $join_conds
-                       );
+               if ( Helpers::isModelEnabled( 'goodfaith' ) ) {
+                       Helpers::joinWithOresTables( 'goodfaith', 
'rc_this_oldid', $tables, $fields,
+                               $join_conds );
                }
        }
 
@@ -423,7 +410,7 @@
                RCCacheEntry $rcObj,
                array &$classes
        ) {
-               if ( !Hooks::oresUiEnabled( $ecl->getUser() ) ) {
+               if ( !Helpers::oresUiEnabled( $ecl->getUser() ) ) {
                        return;
                }
 
@@ -442,7 +429,7 @@
                array &$data,
                RCCacheEntry $rcObj
        ) {
-               if ( !Hooks::oresUiEnabled( $ecl->getUser() ) ) {
+               if ( !Helpers::oresUiEnabled( $ecl->getUser() ) ) {
                        return;
                }
 
@@ -467,7 +454,7 @@
        ) {
                $damaging = self::getScoreRecentChangesList( $rcObj, $context );
 
-               if ( $damaging && Hooks::isDamagingFlagEnabled( $context ) ) {
+               if ( $damaging && Helpers::isDamagingFlagEnabled( $context ) ) {
                        $classes[] = 'damaging';
                        $data['recentChangesFlags']['damaging'] = true;
                }
@@ -489,19 +476,19 @@
                RecentChange $rc,
                array &$classes = []
        ) {
-               if ( !Hooks::oresUiEnabled( $changesList->getUser() ) ) {
+               if ( !Helpers::oresUiEnabled( $changesList->getUser() ) ) {
                        return;
                }
 
                $damaging = self::getScoreRecentChangesList( $rc, 
$changesList->getContext() );
                if ( $damaging ) {
                        // Add highlight class
-                       if ( Hooks::isHighlightEnabled( $changesList ) ) {
+                       if ( Helpers::isHighlightEnabled( $changesList ) ) {
                                $classes[] = 'ores-highlight';
                        }
 
                        // Add damaging class and flag
-                       if ( Hooks::isDamagingFlagEnabled( $changesList ) ) {
+                       if ( Helpers::isDamagingFlagEnabled( $changesList ) ) {
                                $classes[] = 'damaging';
 
                                $separator = ' <span 
class="mw-changeslist-separator">. .</span> ';
@@ -525,7 +512,8 @@
        public static function getScoreRecentChangesList( RecentChange $rcObj, 
IContextSource $context ) {
                $threshold = $rcObj->getAttribute( 'ores_damaging_threshold' );
                if ( $threshold === null ) {
-                       $threshold = Hooks::getThreshold( 'damaging', 
$context->getUser(), $context->getTitle() );
+                       $threshold =
+                               Helpers::getThreshold( 'damaging', 
$context->getUser(), $context->getTitle() );
                }
                $score = $rcObj->getAttribute( 'ores_damaging_score' );
                $patrolled = $rcObj->getAttribute( 'rc_patrolled' );
@@ -537,12 +525,8 @@
                        return false;
                }
 
-               Hooks::addRowData(
-                       $context,
-                       $rcObj->getAttribute( 'rc_this_oldid' ),
-                       (float)$score,
-                       'damaging'
-               );
+               Helpers::addRowData( $context, $rcObj->getAttribute( 
'rc_this_oldid' ), (float)$score,
+                       'damaging' );
 
                return $score && $score >= $threshold && !$patrolled;
        }
diff --git a/includes/Hooks/ContributionsHooksHandler.php 
b/includes/Hooks/ContributionsHooksHandler.php
index 338108c..3b1f989 100644
--- a/includes/Hooks/ContributionsHooksHandler.php
+++ b/includes/Hooks/ContributionsHooksHandler.php
@@ -19,7 +19,6 @@
 use ChangesList;
 use ContribsPager;
 use Html;
-use ORES\Hooks;
 use RequestContext;
 use SpecialContributions;
 use IContextSource;
@@ -37,26 +36,17 @@
                ContribsPager $pager,
                &$query
        ) {
-               if ( !Hooks::oresUiEnabled( $pager->getUser() ) ) {
+               if ( !Helpers::oresUiEnabled( $pager->getUser() ) ) {
                        return;
                }
 
-               if ( Hooks::isModelEnabled( 'damaging' ) ) {
-                       Hooks::joinWithOresTables(
-                               'damaging',
-                               'rev_id',
-                               $query['tables'],
-                               $query['fields'],
-                               $query['join_conds']
-                       );
+               if ( Helpers::isModelEnabled( 'damaging' ) ) {
+                       Helpers::joinWithOresTables( 'damaging', 'rev_id', 
$query['tables'], $query['fields'],
+                               $query['join_conds'] );
 
-                       Hooks::hideNonDamagingFilter(
-                               $query['fields'],
-                               $query['conds'],
-                               self::hideNonDamagingPreference( 
$pager->getContext() ),
-                               $pager->getUser(),
-                               $pager->getTitle()
-                       );
+                       Helpers::hideNonDamagingFilter( $query['fields'], 
$query['conds'],
+                               self::hideNonDamagingPreference( 
$pager->getContext() ), $pager->getUser(),
+                               $pager->getTitle() );
                }
        }
 
@@ -65,7 +55,7 @@
                $row,
                array &$flags
        ) {
-               if ( !Hooks::oresUiEnabled( $context->getUser() ) ) {
+               if ( !Helpers::oresUiEnabled( $context->getUser() ) ) {
                        return;
                }
 
@@ -77,10 +67,10 @@
                        return;
                }
 
-               Hooks::addRowData( $context, $row->rev_id, 
(float)$row->ores_damaging_score, 'damaging' );
+               Helpers::addRowData( $context, $row->rev_id, 
(float)$row->ores_damaging_score, 'damaging' );
 
                if (
-                       Hooks::isDamagingFlagEnabled( $context ) &&
+                       Helpers::isDamagingFlagEnabled( $context ) &&
                        $row->ores_damaging_score > 
$row->ores_damaging_threshold
                ) {
                        // Prepend the "r" flag
@@ -94,7 +84,7 @@
                $row,
                array &$classes
        ) {
-               if ( !Hooks::oresUiEnabled( $pager->getUser() ) ) {
+               if ( !Helpers::oresUiEnabled( $pager->getUser() ) ) {
                        return;
                }
 
@@ -104,10 +94,10 @@
                }
 
                if ( $row->ores_damaging_score > $row->ores_damaging_threshold 
) {
-                       if ( Hooks::isHighlightEnabled( $pager ) ) {
+                       if ( Helpers::isHighlightEnabled( $pager ) ) {
                                $classes[] = 'ores-highlight';
                        }
-                       if ( Hooks::isDamagingFlagEnabled( $pager ) ) {
+                       if ( Helpers::isDamagingFlagEnabled( $pager ) ) {
                                $classes[] = 'damaging';
                        }
                }
@@ -123,7 +113,7 @@
                SpecialContributions $page,
                array &$filters
        ) {
-               if ( !Hooks::oresUiEnabled( $page->getUser() ) || 
!Hooks::isModelEnabled( 'damaging' ) ) {
+               if ( !Helpers::oresUiEnabled( $page->getUser() ) || 
!Helpers::isModelEnabled( 'damaging' ) ) {
                        return;
                }
 
diff --git a/includes/Hooks/Helpers.php b/includes/Hooks/Helpers.php
new file mode 100644
index 0000000..fba1106
--- /dev/null
+++ b/includes/Hooks/Helpers.php
@@ -0,0 +1,238 @@
+<?php
+/**
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace ORES\Hooks;
+
+use Exception;
+use IContextSource;
+use MediaWiki\MediaWikiServices;
+use SpecialRecentChanges;
+use SpecialWatchlist;
+use Title;
+use User;
+
+class Helpers {
+
+       // The oresDamagingPref preference uses these names for historical 
reasons
+       public static $damagingPrefMap = [
+               'hard' => 'maybebad',
+               'soft' => 'likelybad',
+               'softest' => 'verylikelybad',
+       ];
+
+       public static function hideNonDamagingFilter(
+               array &$fields, array &$conds, $hidenondamaging, User $user, 
Title $title = null
+       ) {
+               $dbr = \wfGetDB( DB_REPLICA );
+               // Add user-based threshold
+               $threshold = self::getThreshold( 'damaging', $user, $title );
+               if ( $threshold === null ) {
+                       return;
+               }
+               // FIXME: This is not a "filter" but an undocumented side 
effect of this function.
+               $fields['ores_damaging_threshold'] = $threshold;
+
+               if ( $hidenondamaging ) {
+                       // Filter out non-damaging edits.
+                       $conds[] = 'ores_damaging_cls.oresc_probability > ' . 
$dbr->addQuotes( $threshold );
+               }
+       }
+
+       public static function joinWithOresTables(
+               $type, $revIdField, array &$tables, array &$fields, array 
&$join_conds
+       ) {
+               if ( !ctype_lower( $type ) ) {
+                       throw new Exception( "Invalid value for parameter 
'type': '$type'. " .
+                               'Restricted to one lower case word to prevent 
accidental injection.' );
+               }
+
+               $modelId =
+                       MediaWikiServices::getInstance()->getService( 
'ORESModelLookup' )->getModelId( $type );
+               $tables["ores_${type}_cls"] = 'ores_classification';
+
+               $fields["ores_${type}_score"] = 
"ores_${type}_cls.oresc_probability";
+
+               $join_conds["ores_${type}_cls"] = [
+                       'LEFT JOIN',
+                       [
+                               "ores_${type}_cls.oresc_model" => $modelId,
+                               "ores_${type}_cls.oresc_rev=$revIdField",
+                               "ores_${type}_cls.oresc_class" => 1
+                       ]
+               ];
+       }
+
+       /**
+        * @param IContextSource $context
+        * @param int $revisionId
+        * @param float $score
+        * @param string $model
+        */
+       public static function addRowData( IContextSource $context, 
$revisionId, $score, $model ) {
+               $out = $context->getOutput();
+               $data = $out->getProperty( 'oresData' );
+               if ( !isset( $data[$revisionId] ) ) {
+                       $data[$revisionId] = [];
+               }
+               $data[$revisionId][$model] = $score;
+               $out->setProperty( 'oresData', $data );
+       }
+
+       /**
+        * Check whether a given model is enabled in the config
+        * @param string $model
+        * @return bool
+        */
+       public static function isModelEnabled( $model ) {
+               global $wgOresModels;
+
+               return isset( $wgOresModels[$model] ) && $wgOresModels[$model];
+       }
+
+       /**
+        * @param IContextSource $context
+        * @return bool Whether the damaging flag ("r") should be shown
+        */
+       public static function isDamagingFlagEnabled( IContextSource $context ) 
{
+               $user = $context->getUser();
+
+               if ( !self::oresUiEnabled( $user ) ) {
+                       return false;
+               }
+
+               if ( self::isRCPage( $context->getTitle() ) ) {
+                       return !self::isRCStructuredUiEnabled( $context ) &&
+                               $user->getBoolOption( 'ores-damaging-flag-rc' );
+               }
+
+               if ( self::isWLPage( $context->getTitle() ) ) {
+                       return !self::isWLStructuredUiEnabled( $context ) &&
+                               $user->getBoolOption( 'oresHighlight' );
+               }
+
+               return $user->getBoolOption( 'oresHighlight' );
+       }
+
+       /**
+        * @param IContextSource $context
+        * @return bool Whether highlights should be shown
+        */
+       public static function isHighlightEnabled( IContextSource $context ) {
+               // Was previously controlled by different preferences than the 
"r", but they're currently
+               // the same.
+               return self::isDamagingFlagEnabled( $context );
+       }
+
+       /**
+        * @param Title $title
+        * @return bool Whether $title is a RecentChanges page
+        */
+       private static function isRCPage( Title $title ) {
+               return $title->isSpecial( 'Recentchanges' ) ||
+                       $title->isSpecial( 'Recentchangeslinked' );
+       }
+
+       /**
+        * Check whether ores is enabled
+        *
+        * @param User $user
+        * @return bool
+        */
+       public static function oresUiEnabled( User $user ) {
+               global $wgOresUiEnabled;
+
+               // Is the UI enabled or not?  If not, we've been deployed in
+               // infrastructure-only mode, so hide all the UI elements.
+               if ( !$wgOresUiEnabled ) {
+                       return false;
+               }
+
+               return true;
+       }
+
+       /**
+        * Internal helper to get threshold
+        * @param string $type
+        * @param User $user
+        * @param Title|null $title
+        * @return float|null Threshold, or null if not set
+        * @throws Exception When $type is not recognized
+        */
+       public static function getThreshold( $type, User $user, Title $title = 
null ) {
+               if ( $type === 'damaging' ) {
+                       $pref = self::getDamagingLevelPreference( $user, $title 
);
+                       $thresholds = self::getDamagingThresholds();
+                       if ( isset( $thresholds[$pref] ) ) {
+                               return $thresholds[$pref];
+                       }
+
+                       return null;
+               }
+               throw new Exception( "Unknown ORES test: '$type'" );
+       }
+
+       /**
+        * Internal helper to get damaging level preference
+        * with backward compatibility for old level names
+        * @param User $user
+        * @param Title $title
+        * @return string 'maybebad', 'likelybad', or 'verylikelybad'
+        */
+       public static function getDamagingLevelPreference( User $user, Title 
$title = null ) {
+               $option = !$title || self::isWLPage( $title ) ? 
'oresDamagingPref' : 'rcOresDamagingPref';
+
+               $pref = $user->getOption( $option );
+               if ( isset( self::$damagingPrefMap[$pref] ) ) {
+                       $pref = self::$damagingPrefMap[$pref];
+               }
+
+               return $pref;
+       }
+
+       /**
+        * @param Title $title
+        * @return bool Whether $title is the Watchlist page
+        */
+       private static function isWLPage( Title $title ) {
+               return $title->isSpecial( 'Watchlist' );
+       }
+
+       public static function getDamagingThresholds() {
+               $stats = MediaWikiServices::getInstance()->getService( 
'ORESThresholdLookup' );
+               $thresholds = [];
+               foreach ( $stats->getThresholds( 'damaging' ) as $name => 
$bounds ) {
+                       $thresholds[$name] = $bounds['min'];
+               }
+               unset( $thresholds['likelygood'] );
+
+               return $thresholds;
+       }
+
+       public static function isRCStructuredUiEnabled( IContextSource $context 
) {
+               $page = new SpecialRecentChanges();
+               $page->setContext( $context );
+
+               return $page->isStructuredFilterUiEnabled();
+       }
+
+       public static function isWLStructuredUiEnabled( IContextSource $context 
) {
+               $page = new SpecialWatchlist();
+               $page->setContext( $context );
+
+               return $page->isStructuredFilterUiEnabled();
+       }
+
+}
diff --git a/includes/Hooks/PreferencesHookHandler.php 
b/includes/Hooks/PreferencesHookHandler.php
index 2f3de26..61d6655 100644
--- a/includes/Hooks/PreferencesHookHandler.php
+++ b/includes/Hooks/PreferencesHookHandler.php
@@ -16,7 +16,6 @@
 
 namespace ORES\Hooks;
 
-use ORES\Hooks;
 use User;
 
 class PreferencesHookHandler {
@@ -31,12 +30,12 @@
        public static function onGetPreferences( User $user, array 
&$preferences ) {
                global $wgOresFiltersThresholds;
 
-               if ( !Hooks::oresUiEnabled( $user ) || !Hooks::isModelEnabled( 
'damaging' ) ) {
+               if ( !Helpers::oresUiEnabled( $user ) || 
!Helpers::isModelEnabled( 'damaging' ) ) {
                        return;
                }
 
                $options = [];
-               foreach ( Hooks::$damagingPrefMap as $prefName => $level ) {
+               foreach ( Helpers::$damagingPrefMap as $prefName => $level ) {
                        // In other places, we look at the keys of 
getDamagingThresholds() to determine which
                        // damaging levels exist, but it can drop levels from 
its output if the ORES API
                        // has issues. We don't want preference definitions to 
be potentially unstable.
diff --git a/includes/WatchedItemQueryServiceExtension.php 
b/includes/WatchedItemQueryServiceExtension.php
index 48e5889..dd80b2d 100644
--- a/includes/WatchedItemQueryServiceExtension.php
+++ b/includes/WatchedItemQueryServiceExtension.php
@@ -20,6 +20,7 @@
 
 use MediaWiki\MediaWikiServices;
 use ORES\Hooks\ApiHooksHandler;
+use ORES\Hooks\Helpers;
 use User;
 use Wikimedia\Rdbms\IDatabase;
 use Wikimedia\Rdbms\ResultWrapper;
@@ -57,11 +58,11 @@
                        }
                }
 
-               $show = Hooks::isModelEnabled( 'damaging' ) && isset( 
$options['filters'] )
+               $show = Helpers::isModelEnabled( 'damaging' ) && isset( 
$options['filters'] )
                        ? array_flip( $options['filters'] )
                        : [];
                if ( isset( $show['oresreview'] ) || isset( 
$show['!oresreview'] ) ) {
-                       $threshold = Hooks::getThreshold( 'damaging', $user );
+                       $threshold = Helpers::getThreshold( 'damaging', $user );
 
                        $tables[] = 'ores_model';
                        $tables[] = 'ores_classification';
diff --git a/tests/phpunit/includes/Hooks/HelpersTest.php 
b/tests/phpunit/includes/Hooks/HelpersTest.php
new file mode 100644
index 0000000..d61be2f
--- /dev/null
+++ b/tests/phpunit/includes/Hooks/HelpersTest.php
@@ -0,0 +1,162 @@
+<?php
+
+namespace ORES\Tests;
+
+use IContextSource;
+use ORES\Hooks\Helpers;
+use ORES\Storage\HashModelLookup;
+use ORES\ThresholdLookup;
+use RequestContext;
+use SpecialPage;
+use User;
+use Title;
+
+/**
+ * @group ORES
+ * @covers ORES\Hooks\Helpers
+ */
+class HelpersTest extends \MediaWikiTestCase {
+
+       protected $user;
+
+       protected $context;
+
+       protected function setUp() {
+               parent::setUp();
+
+               $this->setMwGlobals( [
+                       'wgOresFiltersThresholds' => [
+                               'damaging' => [
+                                       'maybebad' => [ 'min' => 0.16, 'max' => 
1 ],
+                                       'likelybad' => [ 'min' => 0.56, 'max' 
=> 1 ],
+                               ]
+                       ],
+                       'wgOresWikiId' => 'testwiki',
+               ] );
+
+               $this->user = static::getTestUser()->getUser();
+               $this->user->setOption( 'ores-enabled', 1 );
+               $this->user->setOption( 'oresDamagingPref', 'maybebad' );
+               $this->user->setOption( 'oresHighlight', 1 );
+               $this->user->setOption( 'ores-damaging-flag-rc', 1 );
+               $this->user->saveSettings();
+
+               $this->context = self::getContext( $this->user );
+       }
+
+       /**
+        * @covers ORES\Hooks::getDamagingLevelPreference
+        */
+       public function testGetDamagingLevelPreference_Watchlist() {
+               $level =
+                       Helpers::getDamagingLevelPreference( $this->user,
+                               Title::newFromText( 'Watchlist', NS_SPECIAL ) );
+
+               $this->assertEquals( 'maybebad', $level );
+       }
+
+       /**
+        * @covers ORES\Hooks::getThreshold
+        */
+       public function testGetThreshold_null() {
+               $mock = $this->createMock( ThresholdLookup::class );
+               $mock->method( 'getThresholds' )
+                       ->willReturn( [] );
+
+               $this->setService( 'ORESThresholdLookup', $mock );
+               $threshold = Helpers::getThreshold( 'damaging', $this->user );
+
+               $this->assertNull( $threshold );
+       }
+
+       /**
+        * @covers ORES\Hooks::getThreshold
+        *
+        * @expectedException \Exception
+        * @expectedExceptionMessageRegExp "Unknown ORES test: 'not_a_thing'"
+        */
+       public function testGetThreshold_invalid() {
+               Helpers::getThreshold( 'not_a_thing', $this->user );
+       }
+
+       /**
+        * @covers ORES\Hooks::getThreshold
+        */
+       public function testGetThreshold() {
+               $modelData = [ 'damaging' => [ 'id' => 5, 'version' => '0.0.2' 
] ];
+               $this->setService( 'ORESModelLookup', new HashModelLookup( 
$modelData ) );
+
+               $this->user->setOption( 'rcOresDamagingPref', 'maybebad' );
+               $this->assertEquals(
+                       0.16, Helpers::getThreshold( 'damaging', $this->user, 
$this->context->getTitle() )
+               );
+
+               // b/c
+               $this->user->setOption( 'rcOresDamagingPref', 'soft' );
+               $this->assertEquals(
+                       0.56, Helpers::getThreshold( 'damaging', $this->user, 
$this->context->getTitle() )
+               );
+       }
+
+       /**
+        * @param User $user
+        *
+        * @return IContextSource
+        */
+       public static function getContext( User $user ) {
+               $context = new RequestContext();
+
+               $context->setLanguage( 'en' );
+               $context->setUser( $user );
+               $context->setTitle( SpecialPage::getTitleFor( 'Recentchanges' ) 
);
+
+               return $context;
+       }
+
+       /**
+        * @covers ORES\Hooks::joinWithOresTables
+        */
+       public function testJoinWithOresTables() {
+               $modelData = [ 'damaging' => [ 'id' => 5, 'version' => '0.0.2' 
] ];
+               $this->setService( 'ORESModelLookup', new HashModelLookup( 
$modelData ) );
+
+               $tables = [];
+               $fields = [];
+               $join_conds = [];
+               Helpers::joinWithOresTables( 'damaging', 'rc_this_oldid', 
$tables, $fields, $join_conds );
+
+               $this->assertEquals( [
+                       'ores_damaging_cls' => 'ores_classification',
+               ], $tables );
+               $this->assertEquals( [
+                       'ores_damaging_score' => 
'ores_damaging_cls.oresc_probability',
+               ], $fields );
+               $this->assertEquals( [
+                       'ores_damaging_cls' => [ 'LEFT JOIN', [
+                               'ores_damaging_cls.oresc_model' => 5,
+                               'ores_damaging_cls.oresc_rev=rc_this_oldid',
+                               'ores_damaging_cls.oresc_class' => 1,
+                       ], ],
+               ], $join_conds );
+       }
+
+       /**
+        * @covers ORES\Hooks::hideNonDamagingFilter
+        */
+       public function testHideNonDamagingFilter() {
+               $modelData = [ 'damaging' => [ 'id' => 5, 'version' => '0.0.2' 
] ];
+               $this->setService( 'ORESModelLookup', new HashModelLookup( 
$modelData ) );
+
+               $fields = [];
+               $conds = [];
+               Helpers::hideNonDamagingFilter( $fields, $conds, true, 
$this->user );
+
+               $this->assertEquals( [
+                       'ores_damaging_threshold' => 0.16,
+               ], $fields );
+               $this->assertEquals( [
+                       'ores_damaging_cls.oresc_probability > \'0.16\'',
+               ], $conds );
+       }
+
+}
diff --git a/tests/phpunit/includes/HooksTest.php 
b/tests/phpunit/includes/HooksTest.php
index 290ff11..21f8a7e 100644
--- a/tests/phpunit/includes/HooksTest.php
+++ b/tests/phpunit/includes/HooksTest.php
@@ -2,20 +2,14 @@
 
 namespace ORES\Tests;
 
-use IContextSource;
 use JobQueueGroup;
 use ORES\Hooks;
 use ORES\Hooks\PreferencesHookHandler;
 use ORES\Storage\HashModelLookup;
 use ORES\Storage\ScoreStorage;
-use ORES\ThresholdLookup;
 use OutputPage;
 use RecentChange;
-use RequestContext;
 use SkinFactory;
-use SpecialPage;
-use User;
-use Title;
 
 /**
  * @group ORES
@@ -47,7 +41,7 @@
                $this->user->setOption( 'ores-damaging-flag-rc', 1 );
                $this->user->saveSettings();
 
-               $this->context = self::getContext( $this->user );
+               $this->context = HelpersTest::getContext( $this->user );
        }
 
        /**
@@ -90,40 +84,6 @@
                $this->setService( 'ORESScoreStorage', $mock );
 
                Hooks::onRecentChangesPurgeRows( $rows );
-       }
-
-       /**
-        * @covers ORES\Hooks::getDamagingLevelPreference
-        */
-       public function testGetDamagingLevelPreference_Watchlist() {
-               $level = Hooks::getDamagingLevelPreference( $this->user,
-                       Title::newFromText( 'Watchlist', NS_SPECIAL ) );
-
-               $this->assertEquals( 'maybebad', $level );
-       }
-
-       /**
-        * @covers ORES\Hooks::getThreshold
-        */
-       public function testGetThreshold_null() {
-               $mock = $this->createMock( ThresholdLookup::class );
-               $mock->method( 'getThresholds' )
-                       ->willReturn( [] );
-
-               $this->setService( 'ORESThresholdLookup', $mock );
-               $threshold = Hooks::getThreshold( 'damaging', $this->user );
-
-               $this->assertNull( $threshold );
-       }
-
-       /**
-        * @covers ORES\Hooks::getThreshold
-        *
-        * @expectedException Exception
-        * @expectedExceptionMessageRegExp "Unknown ORES test: 'not_a_thing'"
-        */
-       public function testGetThreshold_invalid() {
-               $threshold = Hooks::getThreshold( 'not_a_thing', $this->user );
        }
 
        /**
@@ -180,27 +140,6 @@
        }
 
        /**
-        * @covers ORES\Hooks::getThreshold
-        */
-       public function testGetThreshold() {
-               $modelData = [ 'damaging' => [ 'id' => 5, 'version' => '0.0.2' 
] ];
-               $this->setService( 'ORESModelLookup', new HashModelLookup( 
$modelData ) );
-
-               $this->user->setOption( 'rcOresDamagingPref', 'maybebad' );
-               $this->assertEquals(
-                       0.16,
-                       Hooks::getThreshold( 'damaging', $this->user, 
$this->context->getTitle() )
-               );
-
-               // b/c
-               $this->user->setOption( 'rcOresDamagingPref', 'soft' );
-               $this->assertEquals(
-                       0.56,
-                       Hooks::getThreshold( 'damaging', $this->user, 
$this->context->getTitle() )
-               );
-       }
-
-       /**
         * @covers ORES\Hooks\PreferencesHookHandler::onGetPreferences
         * @todo Move to a dedicated file
         */
@@ -209,68 +148,6 @@
                PreferencesHookHandler::onGetPreferences( $this->user, $prefs );
 
                $this->assertSame( 6, count( $prefs ) );
-       }
-
-       /**
-        * @param User $user
-        *
-        * @return IContextSource
-        */
-       private static function getContext( User $user ) {
-               $context = new RequestContext();
-
-               $context->setLanguage( 'en' );
-               $context->setUser( $user );
-               $context->setTitle( SpecialPage::getTitleFor( 'Recentchanges' ) 
);
-
-               return $context;
-       }
-
-       /**
-        * @covers ORES\Hooks::joinWithOresTables
-        */
-       public function testJoinWithOresTables() {
-               $modelData = [ 'damaging' => [ 'id' => 5, 'version' => '0.0.2' 
] ];
-               $this->setService( 'ORESModelLookup', new HashModelLookup( 
$modelData ) );
-
-               $tables = [];
-               $fields = [];
-               $join_conds = [];
-               Hooks::joinWithOresTables( 'damaging', 'rc_this_oldid',
-                       $tables, $fields, $join_conds );
-
-               $this->assertEquals( [
-                       'ores_damaging_cls' => 'ores_classification',
-               ], $tables );
-               $this->assertEquals( [
-                       'ores_damaging_score' => 
'ores_damaging_cls.oresc_probability',
-               ], $fields );
-               $this->assertEquals( [
-                       'ores_damaging_cls' => [ 'LEFT JOIN', [
-                               'ores_damaging_cls.oresc_model' => 5,
-                               'ores_damaging_cls.oresc_rev=rc_this_oldid',
-                               'ores_damaging_cls.oresc_class' => 1,
-                       ], ],
-               ], $join_conds );
-       }
-
-       /**
-        * @covers ORES\Hooks::hideNonDamagingFilter
-        */
-       public function testHideNonDamagingFilter() {
-               $modelData = [ 'damaging' => [ 'id' => 5, 'version' => '0.0.2' 
] ];
-               $this->setService( 'ORESModelLookup', new HashModelLookup( 
$modelData ) );
-
-               $fields = [];
-               $conds = [];
-               Hooks::hideNonDamagingFilter( $fields, $conds, true, 
$this->user );
-
-               $this->assertEquals( [
-                       'ores_damaging_threshold' => 0.16,
-               ], $fields );
-               $this->assertEquals( [
-                       'ores_damaging_cls.oresc_probability > \'0.16\'',
-               ], $conds );
        }
 
 }

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: Id73153c0f99f8961311a1bba167387bce2175d1a
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/ORES
Gerrit-Branch: master
Gerrit-Owner: Ladsgroup <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to