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