Ladsgroup has uploaded a new change for review. (
https://gerrit.wikimedia.org/r/392415 )
Change subject: Move Contributions and ChangesList hook handlers to their
dedicated files
......................................................................
Move Contributions and ChangesList hook handlers to their dedicated files
It's just moving the code around and makes the Hook class bearably clean
Change-Id: I35247553444d595330f149c6c5b1f29165ee1e40
---
M extension.json
M includes/Hooks.php
A includes/Hooks/ChangesListHooksHandler.php
A includes/Hooks/ContributionsHooksHandler.php
A tests/phpunit/includes/Hooks/ChangesListHooksHandlerTest.php
A tests/phpunit/includes/Hooks/ContributionsHookHandlerTest.php
M tests/phpunit/includes/HooksTest.php
7 files changed, 1,336 insertions(+), 1,223 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/ORES
refs/changes/15/392415/1
diff --git a/extension.json b/extension.json
index c3ce04a..63f226b 100644
--- a/extension.json
+++ b/extension.json
@@ -13,6 +13,8 @@
"ORES\\Cache": "includes/Cache.php",
"ORES\\Hooks": "includes/Hooks.php",
"ORES\\Hooks\\ApiHooksHandler":
"includes/Hooks/ApiHooksHandler.php",
+ "ORES\\Hooks\\ChangesListHooksHandler":
"includes/Hooks/ChangesListHooksHandler.php",
+ "ORES\\Hooks\\ContributionsHooksHandler":
"includes/Hooks/ContributionsHooksHandler.php",
"ORES\\Hooks\\PreferencesHookHandler":
"includes/Hooks/PreferencesHookHandler.php",
"ORES\\FetchScoreJob": "includes/FetchScoreJob.php",
"ORES\\Range": "includes/Range.php",
@@ -50,19 +52,19 @@
"ORES\\Hooks::onBeforePageDisplay"
],
"ChangesListSpecialPageStructuredFilters": [
- "ORES\\Hooks::onChangesListSpecialPageStructuredFilters"
+
"ORES\\Hooks\\ChangesListHooksHandler::onChangesListSpecialPageStructuredFilters"
],
"ChangesListSpecialPageQuery": [
- "ORES\\Hooks::onChangesListSpecialPageQuery"
+
"ORES\\Hooks\\ChangesListHooksHandler::onChangesListSpecialPageQuery"
],
"ContribsPager::getQueryInfo": [
- "ORES\\Hooks::onContribsGetQueryInfo"
+
"ORES\\Hooks\\ContributionsHooksHandler::onContribsGetQueryInfo"
],
"EnhancedChangesListModifyBlockLineData": [
- "ORES\\Hooks::onEnhancedChangesListModifyBlockLineData"
+
"ORES\\Hooks\\ChangesListHooksHandler::onEnhancedChangesListModifyBlockLineData"
],
"EnhancedChangesListModifyLineData": [
- "ORES\\Hooks::onEnhancedChangesListModifyLineData"
+
"ORES\\Hooks\\ChangesListHooksHandler::onEnhancedChangesListModifyLineData"
],
"GetBetaFeaturePreferences": [
"ORES\\Hooks::onGetBetaFeaturePreferences"
@@ -74,7 +76,7 @@
"ORES\\Hooks::onLoadExtensionSchemaUpdates"
],
"OldChangesListRecentChangesLine": [
- "ORES\\Hooks::onOldChangesListRecentChangesLine"
+
"ORES\\Hooks\\ChangesListHooksHandler::onOldChangesListRecentChangesLine"
],
"RecentChange_save": [
"ORES\\Hooks::onRecentChange_save"
@@ -83,13 +85,13 @@
"ORES\\Hooks::onRecentChangesPurgeRows"
],
"SpecialContributions::formatRow::flags": [
- "ORES\\Hooks::onSpecialContributionsFormatRowFlags"
+
"ORES\\Hooks\\ContributionsHooksHandler::onSpecialContributionsFormatRowFlags"
],
"ContributionsLineEnding": [
- "ORES\\Hooks::onContributionsLineEnding"
+
"ORES\\Hooks\\ContributionsHooksHandler::onContributionsLineEnding"
],
"SpecialContributions::getForm::filters": [
- "ORES\\Hooks::onSpecialContributionsGetFormFilters"
+
"ORES\\Hooks\\ContributionsHooksHandler::onSpecialContributionsGetFormFilters"
]
},
"ResourceFileModulePaths": {
diff --git a/includes/Hooks.php b/includes/Hooks.php
index b0d53aa..78c71c3 100644
--- a/includes/Hooks.php
+++ b/includes/Hooks.php
@@ -3,29 +3,18 @@
namespace ORES;
use BetaFeatures;
-use ChangesList;
-use ChangesListBooleanFilterGroup;
-use ChangesListSpecialPage;
-use ChangesListStringOptionsFilterGroup;
-use ContribsPager;
use DatabaseUpdater;
-use EnhancedChangesList;
use Exception;
-use FormOptions;
use JobQueueGroup;
-use Html;
use IContextSource;
use MediaWiki\Logger\LoggerFactory;
use OutputPage;
-use RCCacheEntry;
use RecentChange;
use RequestContext;
use Skin;
-use SpecialContributions;
use SpecialRecentChanges;
use SpecialWatchlist;
use User;
-use Xml;
class Hooks {
// The oresDamagingPref preference uses these names for historical
reasons
@@ -117,601 +106,6 @@
$revIds[] = $row->rc_this_oldid;
}
Cache::instance()->purgeRows( $revIds );
- }
-
- public static function onChangesListSpecialPageStructuredFilters(
- ChangesListSpecialPage $clsp
- ) {
- // ORES is disabled on Recentchangeslinked: T163063
- if ( !self::oresUiEnabled( $clsp->getUser() ) ||
$clsp->getName() === 'Recentchangeslinked' ) {
- return;
- }
-
- $stats = Stats::newFromGlobalState();
-
- $changeTypeGroup = $clsp->getFilterGroup( 'changeType' );
- $logFilter = $changeTypeGroup->getFilter( 'hidelog' );
-
- if ( self::isModelEnabled( 'damaging' ) ) {
- if ( $clsp instanceof SpecialRecentChanges ) {
- $damagingDefault = $clsp->getUser()->getOption(
'oresRCHideNonDamaging' );
- } elseif ( $clsp instanceof SpecialWatchlist ) {
- $damagingDefault = $clsp->getUser()->getOption(
'oresWatchlistHideNonDamaging' );
- } else {
- $damagingDefault = false;
- }
-
- $damagingLevels = $stats->getThresholds( 'damaging' );
- $filters = [];
- if ( isset( $damagingLevels[ 'likelygood' ] ) ) {
- $filters[ 'likelygood' ] = [
- 'name' => 'likelygood',
- 'label' =>
'ores-rcfilters-damaging-likelygood-label',
- 'description' =>
'ores-rcfilters-damaging-likelygood-desc',
- 'cssClassSuffix' =>
'damaging-likelygood',
- 'isRowApplicableCallable' =>
self::makeApplicableCallback(
- 'damaging',
- $damagingLevels['likelygood']
- ),
- ];
- }
- if ( isset( $damagingLevels[ 'maybebad' ] ) ) {
- $filters[ 'maybebad' ] = [
- 'name' => 'maybebad',
- 'label' =>
'ores-rcfilters-damaging-maybebad-label',
- 'description' =>
'ores-rcfilters-damaging-maybebad-desc',
- 'cssClassSuffix' => 'damaging-maybebad',
- 'isRowApplicableCallable' =>
self::makeApplicableCallback(
- 'damaging',
- $damagingLevels['maybebad']
- ),
- ];
- }
- if ( isset( $damagingLevels[ 'likelybad' ] ) ) {
- $descMsg = isset( $filters[ 'maybebad' ] ) ?
-
'ores-rcfilters-damaging-likelybad-desc-low' :
-
'ores-rcfilters-damaging-likelybad-desc-high';
- $filters[ 'likelybad' ] = [
- 'name' => 'likelybad',
- 'label' =>
'ores-rcfilters-damaging-likelybad-label',
- 'description' => $descMsg,
- 'cssClassSuffix' =>
'damaging-likelybad',
- 'isRowApplicableCallable' =>
self::makeApplicableCallback(
- 'damaging',
- $damagingLevels['likelybad']
- ),
- ];
- }
- if ( isset( $damagingLevels[ 'verylikelybad' ] ) ) {
- $filters[ 'verylikelybad' ] = [
- 'name' => 'verylikelybad',
- 'label' =>
'ores-rcfilters-damaging-verylikelybad-label',
- 'description' =>
'ores-rcfilters-damaging-verylikelybad-desc',
- 'cssClassSuffix' =>
'damaging-verylikelybad',
- 'isRowApplicableCallable' =>
self::makeApplicableCallback(
- 'damaging',
- $damagingLevels['verylikelybad']
- ),
- ];
- }
-
- if ( $filters ) {
- $newDamagingGroup = new
ChangesListStringOptionsFilterGroup( [
- 'name' => 'damaging',
- 'title' =>
'ores-rcfilters-damaging-title',
- 'whatsThisHeader' =>
'ores-rcfilters-damaging-whats-this-header',
- 'whatsThisBody' =>
'ores-rcfilters-damaging-whats-this-body',
- 'whatsThisUrl' =>
'https://www.mediawiki.org/wiki/' .
-
'Special:MyLanguage/Help:New_filters_for_edit_review/Quality_and_Intent_Filters',
- 'whatsThisLinkText' =>
'ores-rcfilters-whats-this-link-text',
- 'priority' => 2,
- 'filters' => array_values( $filters ),
- 'default' =>
ChangesListStringOptionsFilterGroup::NONE,
- 'isFullCoverage' => false,
- 'queryCallable' => function (
$specialClassName, $ctx, $dbr, &$tables, &$fields,
- &$conds,
&$query_options, &$join_conds, $selectedValues ) {
- $condition =
self::buildRangeFilter( 'damaging', $selectedValues );
- if ( $condition ) {
- $conds[] = $condition;
-
- // Filter out
incompatible types; log actions and external rows are not scorable
- $conds[] = 'rc_type NOT
IN (' . $dbr->makeList( [ RC_LOG, RC_EXTERNAL ] ) . ')';
- // Make the joins INNER
JOINs instead of LEFT JOINs
-
$join_conds['ores_damaging_mdl'][0] = 'INNER JOIN';
-
$join_conds['ores_damaging_cls'][0] = 'INNER JOIN';
- // Performance hack:
add STRAIGHT_JOIN (T146111) but not for Watchlist (T176456 / T164796)
- if ( $specialClassName
!== 'SpecialWatchlist' ) {
-
$query_options[] = 'STRAIGHT_JOIN';
- }
- }
- },
- ] );
-
- $newDamagingGroup->conflictsWith(
- $logFilter,
-
'ores-rcfilters-ores-conflicts-logactions-global',
-
'ores-rcfilters-damaging-conflicts-logactions',
-
'ores-rcfilters-logactions-conflicts-ores'
- );
-
- if ( isset( $filters[ 'maybebad' ] ) && isset(
$filters[ 'likelybad' ] ) ) {
- $newDamagingGroup->getFilter(
'maybebad' )->setAsSupersetOf(
- $newDamagingGroup->getFilter(
'likelybad' )
- );
- }
-
- if ( isset( $filters[ 'likelybad' ] ) && isset(
$filters[ 'verylikelybad' ] ) ) {
- $newDamagingGroup->getFilter(
'likelybad' )->setAsSupersetOf(
- $newDamagingGroup->getFilter(
'verylikelybad' )
- );
- }
-
- // Transitive closure
- if ( isset( $filters[ 'maybebad' ] ) && isset(
$filters[ 'verylikelybad' ] ) ) {
- $newDamagingGroup->getFilter(
'maybebad' )->setAsSupersetOf(
- $newDamagingGroup->getFilter(
'verylikelybad' )
- );
- }
-
- if ( $damagingDefault ) {
- $newDamagingGroup->setDefault(
self::getDamagingLevelPreference( $clsp->getUser() ) );
- }
-
- if ( $clsp->getUser()->getBoolOption(
'oresHighlight' ) ) {
- $levelsColors = [
- 'maybebad' => 'c3',
- 'likelybad' => 'c4',
- 'verylikelybad' => 'c5',
- ];
-
- $prefLevel =
self::getDamagingLevelPreference( $clsp->getUser() );
- $allLevels = array_keys( $levelsColors
);
- $applicableLevels = array_slice(
$allLevels, array_search( $prefLevel, $allLevels ) );
- $applicableLevels = array_intersect(
$applicableLevels, array_keys( $filters ) );
-
- foreach ( $applicableLevels as $level )
{
- $newDamagingGroup
- ->getFilter( $level )
-
->setDefaultHighlightColor( $levelsColors[ $level ] );
- }
- }
-
- $clsp->registerFilterGroup( $newDamagingGroup );
- }
-
- // I don't think we need to register a conflict here,
since
- // if we're showing non-damaging, that won't conflict
with
- // anything.
- $legacyDamagingGroup = new
ChangesListBooleanFilterGroup( [
- 'name' => 'ores',
- 'filters' => [
- [
- 'name' => 'hidenondamaging',
- 'showHide' =>
'ores-damaging-filter',
- 'isReplacedInStructuredUi' =>
true,
- 'default' => $damagingDefault,
- 'queryCallable' => function (
$specialClassName, $ctx, $dbr, &$tables,
- &$fields,
&$conds, &$query_options, &$join_conds ) {
-
self::hideNonDamagingFilter( $fields, $conds, true, $ctx->getUser() );
- // 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
- $conds['rc_patrolled']
= 0;
- // Make the joins INNER
JOINs instead of LEFT JOINs
-
$join_conds['ores_damaging_mdl'][0] = 'INNER JOIN';
-
$join_conds['ores_damaging_cls'][0] = 'INNER JOIN';
- // Performance hack:
add STRAIGHT_JOIN (T146111) but not for Watchlist (T176456 / T164796)
- if ( $specialClassName
!== 'SpecialWatchlist' ) {
-
$query_options[] = 'STRAIGHT_JOIN';
- }
- },
- ]
- ],
-
- ] );
-
- $clsp->registerFilterGroup( $legacyDamagingGroup );
- }
- if ( self::isModelEnabled( 'goodfaith' ) ) {
- $goodfaithLevels = $stats->getThresholds( 'goodfaith' );
- $filters = [];
- if ( isset( $goodfaithLevels['likelygood'] ) ) {
- $filters[ 'likelygood' ] = [
- 'name' => 'likelygood',
- 'label' =>
'ores-rcfilters-goodfaith-good-label',
- 'description' =>
'ores-rcfilters-goodfaith-good-desc',
- 'cssClassSuffix' => 'goodfaith-good',
- 'isRowApplicableCallable' =>
self::makeApplicableCallback(
- 'goodfaith',
- $goodfaithLevels['likelygood']
- ),
- ];
- }
- if ( isset( $goodfaithLevels['maybebad'] ) ) {
- $filters[ 'maybebad' ] = [
- 'name' => 'maybebad',
- 'label' =>
'ores-rcfilters-goodfaith-maybebad-label',
- 'description' =>
'ores-rcfilters-goodfaith-maybebad-desc',
- 'cssClassSuffix' =>
'goodfaith-maybebad',
- 'isRowApplicableCallable' =>
self::makeApplicableCallback(
- 'goodfaith',
- $goodfaithLevels['maybebad']
- ),
- ];
- }
- if ( isset( $goodfaithLevels['likelybad'] ) ) {
- $descMsg = isset( $filters[ 'maybebad' ] ) ?
- 'ores-rcfilters-goodfaith-bad-desc-low'
:
-
'ores-rcfilters-goodfaith-bad-desc-high';
- $filters[ 'likelybad' ] = [
- 'name' => 'likelybad',
- 'label' =>
'ores-rcfilters-goodfaith-bad-label',
- 'description' => $descMsg,
- 'cssClassSuffix' => 'goodfaith-bad',
- 'isRowApplicableCallable' =>
self::makeApplicableCallback(
- 'goodfaith',
- $goodfaithLevels['likelybad']
- ),
- ];
- }
- if ( isset( $goodfaithLevels['verylikelybad'] ) ) {
- $filters[ 'verylikelybad' ] = [
- 'name' => 'verylikelybad',
- 'label' =>
'ores-rcfilters-goodfaith-verylikelybad-label',
- 'description' =>
'ores-rcfilters-goodfaith-verylikelybad-desc',
- 'cssClassSuffix' =>
'goodfaith-verylikelybad',
- 'isRowApplicableCallable' =>
self::makeApplicableCallback(
- 'goodfaith',
-
$goodfaithLevels['verylikelybad']
- ),
- ];
- }
-
- if ( $filters ) {
- $goodfaithGroup = new
ChangesListStringOptionsFilterGroup( [
- 'name' => 'goodfaith',
- 'title' =>
'ores-rcfilters-goodfaith-title',
- 'whatsThisHeader' =>
'ores-rcfilters-goodfaith-whats-this-header',
- 'whatsThisBody' =>
'ores-rcfilters-goodfaith-whats-this-body',
- 'whatsThisUrl' =>
'https://www.mediawiki.org/wiki/' .
-
'Special:MyLanguage/Help:New_filters_for_edit_review/Quality_and_Intent_Filters',
- 'whatsThisLinkText' =>
'ores-rcfilters-whats-this-link-text',
- 'priority' => 1,
- 'filters' => array_values( $filters ),
- 'default' =>
ChangesListStringOptionsFilterGroup::NONE,
- 'isFullCoverage' => false,
- 'queryCallable' => function (
$specialClassName, $ctx, $dbr, &$tables, &$fields,
- &$conds, &$query_options,
&$join_conds, $selectedValues ) {
- $condition =
self::buildRangeFilter( 'goodfaith', $selectedValues );
- if ( $condition ) {
- $conds[] = $condition;
-
- // Filter out
incompatible types; log actions and external rows are not scorable
- $conds[] = 'rc_type NOT
IN (' . $dbr->makeList( [ RC_LOG, RC_EXTERNAL ] ) . ')';
- // Make the joins INNER
JOINs instead of LEFT JOINs
-
$join_conds['ores_goodfaith_mdl'][0] = 'INNER JOIN';
-
$join_conds['ores_goodfaith_cls'][0] = 'INNER JOIN';
- // Performance hack:
add STRAIGHT_JOIN (T146111) but not for Watchlist (T176456 / T164796)
- if ( $specialClassName
!== 'SpecialWatchlist' ) {
-
$query_options[] = 'STRAIGHT_JOIN';
- }
- }
- },
- ] );
-
- if ( isset( $filters['maybebad'] ) && isset(
$filters['likelybad'] ) ) {
- $goodfaithGroup->getFilter( 'maybebad'
)->setAsSupersetOf(
- $goodfaithGroup->getFilter(
'likelybad' )
- );
- }
-
- if ( isset( $filters['likelybad'] ) && isset(
$filters['verylikelybad'] ) ) {
- $goodfaithGroup->getFilter( 'likelybad'
)->setAsSupersetOf(
- $goodfaithGroup->getFilter(
'verylikelybad' )
- );
- }
-
- if ( isset( $filters['maybebad'] ) && isset(
$filters['verylikelybad'] ) ) {
- $goodfaithGroup->getFilter( 'maybebad'
)->setAsSupersetOf(
- $goodfaithGroup->getFilter(
'verylikelybad' )
- );
- }
-
- $goodfaithGroup->conflictsWith(
- $logFilter,
-
'ores-rcfilters-ores-conflicts-logactions-global',
-
'ores-rcfilters-goodfaith-conflicts-logactions',
-
'ores-rcfilters-logactions-conflicts-ores'
- );
-
- $clsp->registerFilterGroup( $goodfaithGroup );
- }
- }
- }
-
- public static function onChangesListSpecialPageQuery(
- $name, array &$tables, array &$fields, array &$conds,
- array &$query_options, array &$join_conds, FormOptions $opts
- ) {
- global $wgUser;
-
- // ORES is disabled on Recentchangeslinked: T163063
- if ( !self::oresUiEnabled( $wgUser ) || $name ===
'Recentchangeslinked' ) {
- return;
- }
-
- if ( self::isModelEnabled( 'damaging' ) ) {
- self::joinWithOresTables(
- 'damaging',
- 'rc_this_oldid',
- $tables,
- $fields,
- $join_conds
- );
- }
- if ( self::isModelEnabled( 'goodfaith' ) ) {
- self::joinWithOresTables(
- 'goodfaith',
- 'rc_this_oldid',
- $tables,
- $fields,
- $join_conds
- );
- }
- }
-
- /**
- * Label recent changes with ORES scores (for each change in an
expanded group)
- *
- * @param EnhancedChangesList $ecl
- * @param array &$data
- * @param RCCacheEntry[] $block
- * @param RCCacheEntry $rcObj
- * @param string[] &$classes
- */
- public static function onEnhancedChangesListModifyLineData(
- EnhancedChangesList $ecl,
- array &$data,
- array $block,
- RCCacheEntry $rcObj,
- array &$classes
- ) {
- if ( !self::oresUiEnabled( $ecl->getUser() ) ) {
- return;
- }
-
- self::processRecentChangesList( $rcObj, $data, $classes,
$ecl->getContext() );
- }
-
- /**
- * Label recent changes with ORES scores (for top-level ungrouped lines)
- *
- * @param EnhancedChangesList $ecl
- * @param array &$data
- * @param RCCacheEntry $rcObj
- */
- public static function onEnhancedChangesListModifyBlockLineData(
- EnhancedChangesList $ecl,
- array &$data,
- RCCacheEntry $rcObj
- ) {
- if ( !self::oresUiEnabled( $ecl->getUser() ) ) {
- return;
- }
-
- $classes = [];
- self::processRecentChangesList( $rcObj, $data, $classes,
$ecl->getContext() );
- $data['attribs']['class'] = array_merge(
$data['attribs']['class'], $classes );
- }
-
- /**
- * Hook for formatting recent changes links
- * @see
https://www.mediawiki.org/wiki/Manual:Hooks/OldChangesListRecentChangesLine
- *
- * @param ChangesList &$changesList
- * @param string &$s
- * @param RecentChange $rc
- * @param string[] &$classes
- * @return bool|void
- */
- public static function onOldChangesListRecentChangesLine(
- ChangesList &$changesList,
- &$s,
- $rc,
- &$classes = []
- ) {
- if ( !self::oresUiEnabled( $changesList->getUser() ) ) {
- return;
- }
-
- $damaging = self::getScoreRecentChangesList( $rc,
$changesList->getContext() );
- if ( $damaging ) {
- // Add highlight class
- if ( self::isHighlightEnabled( $changesList ) ) {
- $classes[] = 'ores-highlight';
- }
-
- // Add damaging class and flag
- if ( self::isDamagingFlagEnabled( $changesList ) ) {
- $classes[] = 'damaging';
-
- $separator = ' <span
class="mw-changeslist-separator">. .</span> ';
- if ( strpos( $s, $separator ) === false ) {
- return;
- }
-
- $parts = explode( $separator, $s );
- $parts[1] = ChangesList::flag( 'damaging' ) .
$parts[1];
- $s = implode( $separator, $parts );
- }
- }
-
- return true;
- }
-
- /**
- * Filter out non-damaging changes from Special:Contributions
- *
- * @param ContribsPager $pager
- * @param array &$query
- */
- public static function onContribsGetQueryInfo(
- ContribsPager $pager,
- &$query
- ) {
- if ( !self::oresUiEnabled( $pager->getUser() ) ) {
- return;
- }
-
- if ( self::isModelEnabled( 'damaging' ) ) {
- $request = $pager->getContext()->getRequest();
-
- self::joinWithOresTables(
- 'damaging',
- 'rev_id',
- $query['tables'],
- $query['fields'],
- $query['join_conds']
- );
-
- self::hideNonDamagingFilter(
- $query['fields'],
- $query['conds'],
- $request->getVal( 'hidenondamaging' ),
- $pager->getUser()
- );
- }
- }
-
- public static function onSpecialContributionsFormatRowFlags(
- RequestContext $context,
- $row,
- array &$flags
- ) {
- if ( !self::oresUiEnabled( $context->getUser() ) ) {
- return;
- }
-
- // Doesn't have ores score, skipping.
- if ( !isset( $row->ores_damaging_score ) ) {
- return;
- }
-
- self::addRowData( $context, $row->rev_id,
(float)$row->ores_damaging_score, 'damaging' );
-
- if (
- self::isDamagingFlagEnabled( $context ) &&
- $row->ores_damaging_score >
$row->ores_damaging_threshold
- ) {
- // Prepend the "r" flag
- array_unshift( $flags, ChangesList::flag( 'damaging' )
);
- }
- }
-
- public static function onContributionsLineEnding(
- ContribsPager $pager,
- &$ret,
- $row,
- array &$classes
- ) {
- if ( !self::oresUiEnabled( $pager->getUser() ) ) {
- return;
- }
-
- // Doesn't have ores score or threshold is not set properly,
skipping.
- if ( !isset( $row->ores_damaging_score ) || !isset(
$row->ores_damaging_threshold ) ) {
- return;
- }
-
- if ( $row->ores_damaging_score > $row->ores_damaging_threshold
) {
- if ( self::isHighlightEnabled( $pager ) ) {
- $classes[] = 'ores-highlight';
- }
- if ( self::isDamagingFlagEnabled( $pager ) ) {
- $classes[] = 'damaging';
- }
- }
- }
-
- /**
- * Hook into Special:Contributions filters
- *
- * @param SpecialContributions $page
- * @param string[] &$filters HTML
- */
- public static function onSpecialContributionsGetFormFilters(
- SpecialContributions $page,
- array &$filters
- ) {
- if ( !self::oresUiEnabled( $page->getUser() ) ||
!self::isModelEnabled( 'damaging' ) ) {
- return;
- }
-
- $filters[] = Html::rawElement(
- 'span',
- [ 'class' => 'mw-input-with-label' ],
- Xml::checkLabel(
- $page->msg( 'ores-hide-nondamaging-filter'
)->text(),
- 'hidenondamaging',
- 'ores-hide-nondamaging',
- $page->getContext()->getRequest()->getVal(
'hidenondamaging' ),
- [ 'class' => 'mw-input' ]
- )
- );
- }
-
- /**
- * Internal helper to label matching rows
- *
- * @param RCCacheEntry $rcObj
- * @param string[] &$data
- * @param string[] &$classes
- * @param IContextSource $context
- */
- protected static function processRecentChangesList(
- RCCacheEntry $rcObj,
- array &$data,
- array &$classes = [],
- IContextSource $context
- ) {
- $damaging = self::getScoreRecentChangesList( $rcObj, $context );
-
- if ( $damaging && self::isDamagingFlagEnabled( $context ) ) {
- $classes[] = 'damaging';
- $data['recentChangesFlags']['damaging'] = true;
- }
- }
-
- /**
- * Check if we should flag a row. As a side effect, also adds score
data for this row.
- * @param RecentChange $rcObj
- * @param IContextSource $context
- * @return bool
- */
- public static function getScoreRecentChangesList( $rcObj,
IContextSource $context ) {
- global $wgUser;
- $threshold = $rcObj->getAttribute( 'ores_damaging_threshold' );
- if ( $threshold === null ) {
- $threshold = self::getThreshold( 'damaging', $wgUser );
- }
- $score = $rcObj->getAttribute( 'ores_damaging_score' );
- $patrolled = $rcObj->getAttribute( 'rc_patrolled' );
- $type = $rcObj->getAttribute( 'rc_type' );
-
- // Log actions and external rows are not scorable; if such a
row does have a score, ignore it
- if ( !$score || $threshold === null || in_array( $type, [
RC_LOG, RC_EXTERNAL ] ) ) {
- // Shorten out
- return false;
- }
-
- self::addRowData(
- $context,
- $rcObj->getAttribute( 'rc_this_oldid' ),
- (float)$score,
- 'damaging'
- );
-
- return $score && $score >= $threshold && !$patrolled;
}
/**
@@ -870,7 +264,7 @@
* @param IContextSource $context
* @return bool Whether highlights should be shown
*/
- private static function isHighlightEnabled( IContextSource $context ) {
+ 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 );
@@ -880,7 +274,7 @@
* @param IContextSource $context
* @return bool Whether the damaging flag ("r") should be shown
*/
- private static function isDamagingFlagEnabled( IContextSource $context
) {
+ public static function isDamagingFlagEnabled( IContextSource $context )
{
$user = $context->getUser();
if ( !self::oresUiEnabled( $user ) ) {
@@ -916,7 +310,7 @@
* @param float $score
* @param string $model
*/
- private static function addRowData( IContextSource $context,
$revisionId, $score, $model ) {
+ public static function addRowData( IContextSource $context,
$revisionId, $score, $model ) {
$out = $context->getOutput();
$data = $out->getProperty( 'oresData' );
if ( !isset( $data[$revisionId] ) ) {
@@ -926,7 +320,7 @@
$out->setProperty( 'oresData', $data );
}
- private static function joinWithOresTables(
+ public static function joinWithOresTables(
$type,
$revIdField,
array &$tables,
@@ -956,7 +350,7 @@
] ];
}
- private static function hideNonDamagingFilter(
+ public static function hideNonDamagingFilter(
array &$fields,
array &$conds,
$hidenondamaging,
@@ -975,64 +369,6 @@
// Filter out non-damaging edits.
$conds[] = 'ores_damaging_cls.oresc_probability > ' .
$dbr->addQuotes( $threshold );
}
- }
-
- private static function buildRangeFilter( $name, $filterValue ) {
- $stats = Stats::newFromGlobalState();
- $thresholds = $stats->getThresholds( $name );
-
- $selectedLevels = is_array( $filterValue ) ? $filterValue :
- explode( ',', strtolower( $filterValue ) );
- $selectedLevels = array_intersect(
- $selectedLevels,
- array_keys( $thresholds )
- );
-
- if ( $selectedLevels ) {
- $ranges = [];
- foreach ( $selectedLevels as $level ) {
- $range = new Range(
- $thresholds[$level]['min'],
- $thresholds[$level]['max']
- );
-
- $result = array_filter(
- $ranges,
- function ( Range $r ) use ( $range ) {
- return $r->overlaps( $range );
- }
- );
- $overlap = reset( $result );
- if ( $overlap ) {
- $overlap->combineWith( $range );
- } else {
- $ranges[] = $range;
- }
- }
-
- $betweenConditions = array_map(
- function ( Range $range ) use ( $name ) {
- $min = $range->getMin();
- $max = $range->getMax();
- return
"ores_{$name}_cls.oresc_probability BETWEEN $min AND $max";
- },
- $ranges
- );
-
- return \wfGetDB( DB_REPLICA )->makeList(
$betweenConditions, \IDatabase::LIST_OR );
- }
- }
-
- private static function makeApplicableCallback( $model, array
$levelData ) {
- return function ( $ctx, $rc ) use ( $model, $levelData ) {
- $score = $rc->getAttribute( "ores_{$model}_score" );
- $type = $rc->getAttribute( 'rc_type' );
- // Log actions and external rows are not scorable; if
such a row does have a score, ignore it
- if ( $score === null || in_array( $type, [ RC_LOG,
RC_EXTERNAL ] ) ) {
- return false;
- }
- return $levelData['min'] <= $score && $score <=
$levelData['max'];
- };
}
}
diff --git a/includes/Hooks/ChangesListHooksHandler.php
b/includes/Hooks/ChangesListHooksHandler.php
new file mode 100644
index 0000000..d211f18
--- /dev/null
+++ b/includes/Hooks/ChangesListHooksHandler.php
@@ -0,0 +1,564 @@
+<?php
+
+namespace ORES\Hooks;
+
+use ChangesList;
+use ChangesListBooleanFilterGroup;
+use ChangesListSpecialPage;
+use ChangesListStringOptionsFilterGroup;
+use EnhancedChangesList;
+use FormOptions;
+use IContextSource;
+use ORES\Hooks;
+use ORES\Range;
+use ORES\Stats;
+use RCCacheEntry;
+use RecentChange;
+use SpecialRecentChanges;
+use SpecialWatchlist;
+
+class ChangesListHooksHandler {
+
+ public static function onChangesListSpecialPageStructuredFilters(
+ ChangesListSpecialPage $clsp
+ ) {
+ // ORES is disabled on Recentchangeslinked: T163063
+ if ( !Hooks::oresUiEnabled( $clsp->getUser() ) ||
$clsp->getName() === 'Recentchangeslinked'
+ ) {
+ return;
+ }
+
+ $stats = Stats::newFromGlobalState();
+
+ $changeTypeGroup = $clsp->getFilterGroup( 'changeType' );
+ $logFilter = $changeTypeGroup->getFilter( 'hidelog' );
+
+ if ( Hooks::isModelEnabled( 'damaging' ) ) {
+ if ( $clsp instanceof SpecialRecentChanges ) {
+ $damagingDefault = $clsp->getUser()->getOption(
'oresRCHideNonDamaging' );
+ } elseif ( $clsp instanceof SpecialWatchlist ) {
+ $damagingDefault = $clsp->getUser()->getOption(
'oresWatchlistHideNonDamaging' );
+ } else {
+ $damagingDefault = false;
+ }
+
+ $damagingLevels = $stats->getThresholds( 'damaging' );
+ $filters = [];
+ if ( isset( $damagingLevels[ 'likelygood' ] ) ) {
+ $filters[ 'likelygood' ] = [
+ 'name' => 'likelygood',
+ 'label' =>
'ores-rcfilters-damaging-likelygood-label',
+ 'description' =>
'ores-rcfilters-damaging-likelygood-desc',
+ 'cssClassSuffix' =>
'damaging-likelygood',
+ 'isRowApplicableCallable' =>
self::makeApplicableCallback(
+ 'damaging',
+ $damagingLevels['likelygood']
+ ),
+ ];
+ }
+ if ( isset( $damagingLevels[ 'maybebad' ] ) ) {
+ $filters[ 'maybebad' ] = [
+ 'name' => 'maybebad',
+ 'label' =>
'ores-rcfilters-damaging-maybebad-label',
+ 'description' =>
'ores-rcfilters-damaging-maybebad-desc',
+ 'cssClassSuffix' => 'damaging-maybebad',
+ 'isRowApplicableCallable' =>
self::makeApplicableCallback(
+ 'damaging',
+ $damagingLevels['maybebad']
+ ),
+ ];
+ }
+ if ( isset( $damagingLevels[ 'likelybad' ] ) ) {
+ $descMsg = isset( $filters[ 'maybebad' ] ) ?
+
'ores-rcfilters-damaging-likelybad-desc-low' :
+
'ores-rcfilters-damaging-likelybad-desc-high';
+ $filters[ 'likelybad' ] = [
+ 'name' => 'likelybad',
+ 'label' =>
'ores-rcfilters-damaging-likelybad-label',
+ 'description' => $descMsg,
+ 'cssClassSuffix' =>
'damaging-likelybad',
+ 'isRowApplicableCallable' =>
self::makeApplicableCallback(
+ 'damaging',
+ $damagingLevels['likelybad']
+ ),
+ ];
+ }
+ if ( isset( $damagingLevels[ 'verylikelybad' ] ) ) {
+ $filters[ 'verylikelybad' ] = [
+ 'name' => 'verylikelybad',
+ 'label' =>
'ores-rcfilters-damaging-verylikelybad-label',
+ 'description' =>
'ores-rcfilters-damaging-verylikelybad-desc',
+ 'cssClassSuffix' =>
'damaging-verylikelybad',
+ 'isRowApplicableCallable' =>
self::makeApplicableCallback(
+ 'damaging',
+ $damagingLevels['verylikelybad']
+ ),
+ ];
+ }
+
+ if ( $filters ) {
+ $newDamagingGroup = new
ChangesListStringOptionsFilterGroup( [
+ 'name' => 'damaging',
+ 'title' =>
'ores-rcfilters-damaging-title',
+ 'whatsThisHeader' =>
'ores-rcfilters-damaging-whats-this-header',
+ 'whatsThisBody' =>
'ores-rcfilters-damaging-whats-this-body',
+ 'whatsThisUrl' =>
'https://www.mediawiki.org/wiki/' .
+
'Special:MyLanguage/Help:New_filters_for_edit_review/Quality_and_Intent_Filters',
+ 'whatsThisLinkText' =>
'ores-rcfilters-whats-this-link-text',
+ 'priority' => 2,
+ 'filters' => array_values( $filters ),
+ 'default' =>
ChangesListStringOptionsFilterGroup::NONE,
+ 'isFullCoverage' => false,
+ 'queryCallable' => function (
$specialClassName, $ctx, $dbr, &$tables,
+ &$fields, &$conds,
&$query_options, &$join_conds, $selectedValues ) {
+ $condition =
self::buildRangeFilter( 'damaging', $selectedValues );
+ if ( $condition ) {
+ $conds[] = $condition;
+
+ // Filter out
incompatible types; log actions and external rows are not scorable
+ $conds[] = 'rc_type NOT
IN (' . $dbr->makeList( [ RC_LOG, RC_EXTERNAL ] ) . ')';
+ // Make the joins INNER
JOINs instead of LEFT JOINs
+
$join_conds['ores_damaging_mdl'][0] = 'INNER JOIN';
+
$join_conds['ores_damaging_cls'][0] = 'INNER JOIN';
+ // Performance hack:
add STRAIGHT_JOIN (T146111) but not for Watchlist (T176456 / T164796)
+ if ( $specialClassName
!== 'SpecialWatchlist' ) {
+
$query_options[] = 'STRAIGHT_JOIN';
+ }
+ }
+ },
+ ] );
+
+ $newDamagingGroup->conflictsWith(
+ $logFilter,
+
'ores-rcfilters-ores-conflicts-logactions-global',
+
'ores-rcfilters-damaging-conflicts-logactions',
+
'ores-rcfilters-logactions-conflicts-ores'
+ );
+
+ if ( isset( $filters[ 'maybebad' ] ) && isset(
$filters[ 'likelybad' ] ) ) {
+ $newDamagingGroup->getFilter(
'maybebad' )->setAsSupersetOf(
+ $newDamagingGroup->getFilter(
'likelybad' )
+ );
+ }
+
+ if ( isset( $filters[ 'likelybad' ] ) && isset(
$filters[ 'verylikelybad' ] ) ) {
+ $newDamagingGroup->getFilter(
'likelybad' )->setAsSupersetOf(
+ $newDamagingGroup->getFilter(
'verylikelybad' )
+ );
+ }
+
+ // Transitive closure
+ if ( isset( $filters[ 'maybebad' ] ) && isset(
$filters[ 'verylikelybad' ] ) ) {
+ $newDamagingGroup->getFilter(
'maybebad' )->setAsSupersetOf(
+ $newDamagingGroup->getFilter(
'verylikelybad' )
+ );
+ }
+
+ if ( $damagingDefault ) {
+ $newDamagingGroup->setDefault(
Hooks::getDamagingLevelPreference( $clsp->getUser
+ () ) );
+ }
+
+ if ( $clsp->getUser()->getBoolOption(
'oresHighlight' ) ) {
+ $levelsColors = [
+ 'maybebad' => 'c3',
+ 'likelybad' => 'c4',
+ 'verylikelybad' => 'c5',
+ ];
+
+ $prefLevel =
Hooks::getDamagingLevelPreference( $clsp->getUser() );
+ $allLevels = array_keys( $levelsColors
);
+ $applicableLevels = array_slice(
$allLevels, array_search( $prefLevel, $allLevels ) );
+ $applicableLevels = array_intersect(
$applicableLevels, array_keys( $filters ) );
+
+ foreach ( $applicableLevels as $level )
{
+ $newDamagingGroup
+ ->getFilter( $level )
+
->setDefaultHighlightColor( $levelsColors[ $level ] );
+ }
+ }
+
+ $clsp->registerFilterGroup( $newDamagingGroup );
+ }
+
+ // I don't think we need to register a conflict here,
since
+ // if we're showing non-damaging, that won't conflict
with
+ // anything.
+ $legacyDamagingGroup = new
ChangesListBooleanFilterGroup( [
+ 'name' => 'ores',
+ 'filters' => [
+ [
+ 'name' => 'hidenondamaging',
+ 'showHide' =>
'ores-damaging-filter',
+ 'isReplacedInStructuredUi' =>
true,
+ 'default' => $damagingDefault,
+ 'queryCallable' => function (
$specialClassName, $ctx, $dbr, &$tables,
+ &$fields,
&$conds, &$query_options, &$join_conds ) {
+
Hooks::hideNonDamagingFilter( $fields, $conds, true, $ctx->getUser() );
+ // 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
+ $conds['rc_patrolled']
= 0;
+ // Make the joins INNER
JOINs instead of LEFT JOINs
+
$join_conds['ores_damaging_mdl'][0] = 'INNER JOIN';
+
$join_conds['ores_damaging_cls'][0] = 'INNER JOIN';
+ // Performance hack:
add STRAIGHT_JOIN (T146111) but not for Watchlist (T176456 / T164796)
+ if ( $specialClassName
!== 'SpecialWatchlist' ) {
+
$query_options[] = 'STRAIGHT_JOIN';
+ }
+ },
+ ]
+ ],
+
+ ] );
+
+ $clsp->registerFilterGroup( $legacyDamagingGroup );
+ }
+ if ( Hooks::isModelEnabled( 'goodfaith' ) ) {
+ $goodfaithLevels = $stats->getThresholds( 'goodfaith' );
+ $filters = [];
+ if ( isset( $goodfaithLevels['likelygood'] ) ) {
+ $filters[ 'likelygood' ] = [
+ 'name' => 'likelygood',
+ 'label' =>
'ores-rcfilters-goodfaith-good-label',
+ 'description' =>
'ores-rcfilters-goodfaith-good-desc',
+ 'cssClassSuffix' => 'goodfaith-good',
+ 'isRowApplicableCallable' =>
self::makeApplicableCallback(
+ 'goodfaith',
+ $goodfaithLevels['likelygood']
+ ),
+ ];
+ }
+ if ( isset( $goodfaithLevels['maybebad'] ) ) {
+ $filters[ 'maybebad' ] = [
+ 'name' => 'maybebad',
+ 'label' =>
'ores-rcfilters-goodfaith-maybebad-label',
+ 'description' =>
'ores-rcfilters-goodfaith-maybebad-desc',
+ 'cssClassSuffix' =>
'goodfaith-maybebad',
+ 'isRowApplicableCallable' =>
self::makeApplicableCallback(
+ 'goodfaith',
+ $goodfaithLevels['maybebad']
+ ),
+ ];
+ }
+ if ( isset( $goodfaithLevels['likelybad'] ) ) {
+ $descMsg = isset( $filters[ 'maybebad' ] ) ?
+ 'ores-rcfilters-goodfaith-bad-desc-low'
:
+
'ores-rcfilters-goodfaith-bad-desc-high';
+ $filters[ 'likelybad' ] = [
+ 'name' => 'likelybad',
+ 'label' =>
'ores-rcfilters-goodfaith-bad-label',
+ 'description' => $descMsg,
+ 'cssClassSuffix' => 'goodfaith-bad',
+ 'isRowApplicableCallable' =>
self::makeApplicableCallback(
+ 'goodfaith',
+ $goodfaithLevels['likelybad']
+ ),
+ ];
+ }
+ if ( isset( $goodfaithLevels['verylikelybad'] ) ) {
+ $filters[ 'verylikelybad' ] = [
+ 'name' => 'verylikelybad',
+ 'label' =>
'ores-rcfilters-goodfaith-verylikelybad-label',
+ 'description' =>
'ores-rcfilters-goodfaith-verylikelybad-desc',
+ 'cssClassSuffix' =>
'goodfaith-verylikelybad',
+ 'isRowApplicableCallable' =>
self::makeApplicableCallback(
+ 'goodfaith',
+
$goodfaithLevels['verylikelybad']
+ ),
+ ];
+ }
+
+ if ( $filters ) {
+ $goodfaithGroup = new
ChangesListStringOptionsFilterGroup( [
+ 'name' => 'goodfaith',
+ 'title' =>
'ores-rcfilters-goodfaith-title',
+ 'whatsThisHeader' =>
'ores-rcfilters-goodfaith-whats-this-header',
+ 'whatsThisBody' =>
'ores-rcfilters-goodfaith-whats-this-body',
+ 'whatsThisUrl' =>
'https://www.mediawiki.org/wiki/' .
+
'Special:MyLanguage/Help:New_filters_for_edit_review/Quality_and_Intent_Filters',
+ 'whatsThisLinkText' =>
'ores-rcfilters-whats-this-link-text',
+ 'priority' => 1,
+ 'filters' => array_values( $filters ),
+ 'default' =>
ChangesListStringOptionsFilterGroup::NONE,
+ 'isFullCoverage' => false,
+ 'queryCallable' => function (
$specialClassName, $ctx, $dbr, &$tables, &$fields,
+ &$conds,
&$query_options, &$join_conds, $selectedValues ) {
+ $condition =
self::buildRangeFilter( 'goodfaith', $selectedValues );
+ if ( $condition ) {
+ $conds[] = $condition;
+
+ // Filter out
incompatible types; log actions and external rows are not scorable
+ $conds[] = 'rc_type NOT
IN (' . $dbr->makeList( [ RC_LOG, RC_EXTERNAL ] ) . ')';
+ // Make the joins INNER
JOINs instead of LEFT JOINs
+
$join_conds['ores_goodfaith_mdl'][0] = 'INNER JOIN';
+
$join_conds['ores_goodfaith_cls'][0] = 'INNER JOIN';
+ // Performance hack:
add STRAIGHT_JOIN (T146111) but not for Watchlist (T176456 / T164796)
+ if ( $specialClassName
!== 'SpecialWatchlist' ) {
+
$query_options[] = 'STRAIGHT_JOIN';
+ }
+ }
+ },
+ ] );
+
+ if ( isset( $filters['maybebad'] ) && isset(
$filters['likelybad'] ) ) {
+ $goodfaithGroup->getFilter( 'maybebad'
)->setAsSupersetOf(
+ $goodfaithGroup->getFilter(
'likelybad' )
+ );
+ }
+
+ if ( isset( $filters['likelybad'] ) && isset(
$filters['verylikelybad'] ) ) {
+ $goodfaithGroup->getFilter( 'likelybad'
)->setAsSupersetOf(
+ $goodfaithGroup->getFilter(
'verylikelybad' )
+ );
+ }
+
+ if ( isset( $filters['maybebad'] ) && isset(
$filters['verylikelybad'] ) ) {
+ $goodfaithGroup->getFilter( 'maybebad'
)->setAsSupersetOf(
+ $goodfaithGroup->getFilter(
'verylikelybad' )
+ );
+ }
+
+ $goodfaithGroup->conflictsWith(
+ $logFilter,
+
'ores-rcfilters-ores-conflicts-logactions-global',
+
'ores-rcfilters-goodfaith-conflicts-logactions',
+
'ores-rcfilters-logactions-conflicts-ores'
+ );
+
+ $clsp->registerFilterGroup( $goodfaithGroup );
+ }
+ }
+ }
+
+ public static function onChangesListSpecialPageQuery(
+ $name, array &$tables, array &$fields, array &$conds,
+ array &$query_options, array &$join_conds, FormOptions $opts
+ ) {
+ global $wgUser;
+
+ // ORES is disabled on Recentchangeslinked: T163063
+ if ( !Hooks::oresUiEnabled( $wgUser ) || $name ===
'Recentchangeslinked' ) {
+ return;
+ }
+
+ if ( Hooks::isModelEnabled( 'damaging' ) ) {
+ Hooks::joinWithOresTables(
+ 'damaging',
+ 'rc_this_oldid',
+ $tables,
+ $fields,
+ $join_conds
+ );
+ }
+ if ( Hooks::isModelEnabled( 'goodfaith' ) ) {
+ Hooks::joinWithOresTables(
+ 'goodfaith',
+ 'rc_this_oldid',
+ $tables,
+ $fields,
+ $join_conds
+ );
+ }
+ }
+
+ /**
+ * Label recent changes with ORES scores (for each change in an
expanded group)
+ *
+ * @param EnhancedChangesList $ecl
+ * @param array &$data
+ * @param RCCacheEntry[] $block
+ * @param RCCacheEntry $rcObj
+ * @param string[] &$classes
+ */
+ public static function onEnhancedChangesListModifyLineData(
+ EnhancedChangesList $ecl,
+ array &$data,
+ array $block,
+ RCCacheEntry $rcObj,
+ array &$classes
+ ) {
+ if ( !Hooks::oresUiEnabled( $ecl->getUser() ) ) {
+ return;
+ }
+
+ self::processRecentChangesList( $rcObj, $data, $classes,
$ecl->getContext() );
+ }
+
+ /**
+ * Label recent changes with ORES scores (for top-level ungrouped lines)
+ *
+ * @param EnhancedChangesList $ecl
+ * @param array &$data
+ * @param RCCacheEntry $rcObj
+ */
+ public static function onEnhancedChangesListModifyBlockLineData(
+ EnhancedChangesList $ecl,
+ array &$data,
+ RCCacheEntry $rcObj
+ ) {
+ if ( !Hooks::oresUiEnabled( $ecl->getUser() ) ) {
+ return;
+ }
+
+ $classes = [];
+ self::processRecentChangesList( $rcObj, $data, $classes,
$ecl->getContext() );
+ $data['attribs']['class'] = array_merge(
$data['attribs']['class'], $classes );
+ }
+
+ /**
+ * Internal helper to label matching rows
+ *
+ * @param RCCacheEntry $rcObj
+ * @param string[] &$data
+ * @param string[] &$classes
+ * @param IContextSource $context
+ */
+ protected static function processRecentChangesList(
+ RCCacheEntry $rcObj,
+ array &$data,
+ array &$classes = [],
+ IContextSource $context
+ ) {
+ $damaging = self::getScoreRecentChangesList( $rcObj, $context );
+
+ if ( $damaging && Hooks::isDamagingFlagEnabled( $context ) ) {
+ $classes[] = 'damaging';
+ $data['recentChangesFlags']['damaging'] = true;
+ }
+ }
+
+ /**
+ * Hook for formatting recent changes links
+ * @see
https://www.mediawiki.org/wiki/Manual:Hooks/OldChangesListRecentChangesLine
+ *
+ * @param ChangesList &$changesList
+ * @param string &$s
+ * @param RecentChange $rc
+ * @param string[] &$classes
+ * @return bool|void
+ */
+ public static function onOldChangesListRecentChangesLine(
+ ChangesList &$changesList,
+ &$s,
+ $rc,
+ &$classes = []
+ ) {
+ if ( !Hooks::oresUiEnabled( $changesList->getUser() ) ) {
+ return;
+ }
+
+ $damaging = self::getScoreRecentChangesList( $rc,
$changesList->getContext() );
+ if ( $damaging ) {
+ // Add highlight class
+ if ( Hooks::isHighlightEnabled( $changesList ) ) {
+ $classes[] = 'ores-highlight';
+ }
+
+ // Add damaging class and flag
+ if ( Hooks::isDamagingFlagEnabled( $changesList ) ) {
+ $classes[] = 'damaging';
+
+ $separator = ' <span
class="mw-changeslist-separator">. .</span> ';
+ if ( strpos( $s, $separator ) === false ) {
+ return;
+ }
+
+ $parts = explode( $separator, $s );
+ $parts[1] = ChangesList::flag( 'damaging' ) .
$parts[1];
+ $s = implode( $separator, $parts );
+ }
+ }
+ }
+
+ /**
+ * Check if we should flag a row. As a side effect, also adds score
data for this row.
+ * @param RecentChange $rcObj
+ * @param IContextSource $context
+ * @return bool
+ */
+ public static function getScoreRecentChangesList( $rcObj,
IContextSource $context ) {
+ global $wgUser;
+ $threshold = $rcObj->getAttribute( 'ores_damaging_threshold' );
+ if ( $threshold === null ) {
+ $threshold = Hooks::getThreshold( 'damaging', $wgUser );
+ }
+ $score = $rcObj->getAttribute( 'ores_damaging_score' );
+ $patrolled = $rcObj->getAttribute( 'rc_patrolled' );
+ $type = $rcObj->getAttribute( 'rc_type' );
+
+ // Log actions and external rows are not scorable; if such a
row does have a score, ignore it
+ if ( !$score || $threshold === null || in_array( $type, [
RC_LOG, RC_EXTERNAL ] ) ) {
+ // Shorten out
+ return false;
+ }
+
+ Hooks::addRowData(
+ $context,
+ $rcObj->getAttribute( 'rc_this_oldid' ),
+ (float)$score,
+ 'damaging'
+ );
+
+ return $score && $score >= $threshold && !$patrolled;
+ }
+
+ private static function makeApplicableCallback( $model, array
$levelData ) {
+ return function ( $ctx, $rc ) use ( $model, $levelData ) {
+ $score = $rc->getAttribute( "ores_{$model}_score" );
+ $type = $rc->getAttribute( 'rc_type' );
+ // Log actions and external rows are not scorable; if
such a row does have a score, ignore it
+ if ( $score === null || in_array( $type, [ RC_LOG,
RC_EXTERNAL ] ) ) {
+ return false;
+ }
+ return $levelData['min'] <= $score && $score <=
$levelData['max'];
+ };
+ }
+
+ private static function buildRangeFilter( $name, $filterValue ) {
+ $stats = Stats::newFromGlobalState();
+ $thresholds = $stats->getThresholds( $name );
+
+ $selectedLevels = is_array( $filterValue ) ? $filterValue :
+ explode( ',', strtolower( $filterValue ) );
+ $selectedLevels = array_intersect(
+ $selectedLevels,
+ array_keys( $thresholds )
+ );
+
+ if ( $selectedLevels ) {
+ $ranges = [];
+ foreach ( $selectedLevels as $level ) {
+ $range = new Range(
+ $thresholds[$level]['min'],
+ $thresholds[$level]['max']
+ );
+
+ $result = array_filter(
+ $ranges,
+ function ( Range $r ) use ( $range ) {
+ return $r->overlaps( $range );
+ }
+ );
+ $overlap = reset( $result );
+ if ( $overlap ) {
+ $overlap->combineWith( $range );
+ } else {
+ $ranges[] = $range;
+ }
+ }
+
+ $betweenConditions = array_map(
+ function ( Range $range ) use ( $name ) {
+ $min = $range->getMin();
+ $max = $range->getMax();
+ return
"ores_{$name}_cls.oresc_probability BETWEEN $min AND $max";
+ },
+ $ranges
+ );
+
+ return \wfGetDB( DB_REPLICA )->makeList(
$betweenConditions, \IDatabase::LIST_OR );
+ }
+ }
+
+}
diff --git a/includes/Hooks/ContributionsHooksHandler.php
b/includes/Hooks/ContributionsHooksHandler.php
new file mode 100644
index 0000000..903db4a
--- /dev/null
+++ b/includes/Hooks/ContributionsHooksHandler.php
@@ -0,0 +1,126 @@
+<?php
+
+namespace ORES\Hooks;
+
+use ChangesList;
+use ContribsPager;
+use Html;
+use ORES\Hooks;
+use RequestContext;
+use SpecialContributions;
+use Xml;
+
+class ContributionsHooksHandler {
+
+ /**
+ * Filter out non-damaging changes from Special:Contributions
+ *
+ * @param ContribsPager $pager
+ * @param array &$query
+ */
+ public static function onContribsGetQueryInfo(
+ ContribsPager $pager,
+ &$query
+ ) {
+ if ( !Hooks::oresUiEnabled( $pager->getUser() ) ) {
+ return;
+ }
+
+ if ( Hooks::isModelEnabled( 'damaging' ) ) {
+ $request = $pager->getContext()->getRequest();
+
+ Hooks::joinWithOresTables(
+ 'damaging',
+ 'rev_id',
+ $query['tables'],
+ $query['fields'],
+ $query['join_conds']
+ );
+
+ Hooks::hideNonDamagingFilter(
+ $query['fields'],
+ $query['conds'],
+ $request->getVal( 'hidenondamaging' ),
+ $pager->getUser()
+ );
+ }
+ }
+
+ public static function onSpecialContributionsFormatRowFlags(
+ RequestContext $context,
+ $row,
+ array &$flags
+ ) {
+ if ( !Hooks::oresUiEnabled( $context->getUser() ) ) {
+ return;
+ }
+
+ // Doesn't have ores score, skipping.
+ if ( !isset( $row->ores_damaging_score ) ) {
+ return;
+ }
+
+ Hooks::addRowData( $context, $row->rev_id,
(float)$row->ores_damaging_score, 'damaging' );
+
+ if (
+ Hooks::isDamagingFlagEnabled( $context ) &&
+ $row->ores_damaging_score >
$row->ores_damaging_threshold
+ ) {
+ // Prepend the "r" flag
+ array_unshift( $flags, ChangesList::flag( 'damaging' )
);
+ }
+ }
+
+ public static function onContributionsLineEnding(
+ ContribsPager $pager,
+ &$ret,
+ $row,
+ array &$classes
+ ) {
+ if ( !Hooks::oresUiEnabled( $pager->getUser() ) ) {
+ return;
+ }
+
+ // Doesn't have ores score or threshold is not set properly,
skipping.
+ if ( !isset( $row->ores_damaging_score ) || !isset(
$row->ores_damaging_threshold ) ) {
+ return;
+ }
+
+ if ( $row->ores_damaging_score > $row->ores_damaging_threshold
) {
+ if ( Hooks::isHighlightEnabled( $pager ) ) {
+ $classes[] = 'ores-highlight';
+ }
+ if ( Hooks::isDamagingFlagEnabled( $pager ) ) {
+ $classes[] = 'damaging';
+ }
+ }
+ }
+
+ /**
+ * Hook into Special:Contributions filters
+ *
+ * @param SpecialContributions $page
+ * @param string[] &$filters HTML
+ */
+ public static function onSpecialContributionsGetFormFilters(
+ SpecialContributions $page,
+ array &$filters
+ ) {
+ if ( !Hooks::oresUiEnabled( $page->getUser() ) ||
!Hooks::isModelEnabled( 'damaging' ) ) {
+ return;
+ }
+
+ $filters[] = Html::rawElement(
+ 'span',
+ [ 'class' => 'mw-input-with-label' ],
+ Xml::checkLabel(
+ $page->msg( 'ores-hide-nondamaging-filter'
)->text(),
+ 'hidenondamaging',
+ 'ores-hide-nondamaging',
+ $page->getContext()->getRequest()->getVal(
'hidenondamaging' ),
+ [ 'class' => 'mw-input' ]
+ )
+ );
+ }
+
+}
diff --git a/tests/phpunit/includes/Hooks/ChangesListHooksHandlerTest.php
b/tests/phpunit/includes/Hooks/ChangesListHooksHandlerTest.php
new file mode 100644
index 0000000..c1a35b5
--- /dev/null
+++ b/tests/phpunit/includes/Hooks/ChangesListHooksHandlerTest.php
@@ -0,0 +1,442 @@
+<?php
+
+namespace ORES\Tests\Hooks;
+
+use ChangesList;
+use Config;
+use EnhancedChangesList;
+use FauxRequest;
+use FormOptions;
+use IContextSource;
+use ORES\Hooks\ChangesListHooksHandler;
+use RCCacheEntry;
+use RecentChange;
+use RequestContext;
+use SpecialPage;
+use User;
+
+/**
+ * @group ORES
+ * @covers ORES\Hooks\ChangesListHooksHandler
+ */
+class ChangesListHooksHandlerTest 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 );
+ }
+
+ public function testOresRCObj() {
+ $row = new \stdClass();
+ $row->ores_damaging_threshold = 0.2;
+ $row->ores_damaging_score = 0.3;
+ $row->rc_patrolled = 0;
+ $row->rc_timestamp = '20150921134808';
+ $row->rc_deleted = 0;
+ $row->rc_comment = '';
+ $row->rc_comment_text = '';
+ $row->rc_comment_data = null;
+
+ $rc = RecentChange::newFromRow( $row );
+ $this->assertTrue(
ChangesListHooksHandler::getScoreRecentChangesList( $rc, $this->context ) );
+
+ $row->ores_damaging_threshold = 0.4;
+ $rc = RecentChange::newFromRow( $row );
+ $this->assertFalse(
ChangesListHooksHandler::getScoreRecentChangesList( $rc, $this->context ) );
+ }
+
+ /**
+ * @dataProvider onChangesListSpecialPageQuery_provider
+ */
+ public function testOnChangesListSpecialPageQuery( $modelConfig,
$expectedQuery ) {
+ $this->setMwGlobals( [
+ 'wgUser' => $this->user,
+ 'wgOresModels' => $modelConfig
+ ] );
+ $tables = [];
+ $fields = [];
+ $conds = [];
+ $query_options = [];
+ $join_conds = [];
+ ChangesListHooksHandler::onChangesListSpecialPageQuery(
+ '',
+ $tables,
+ $fields,
+ $conds,
+ $query_options,
+ $join_conds,
+ new FormOptions()
+ );
+ $this->assertSame( $expectedQuery['tables'], $tables );
+ $this->assertSame( $expectedQuery['fields'], $fields );
+ $this->assertSame( $expectedQuery['join_conds'], $join_conds );
+ }
+
+ public function onChangesListSpecialPageQuery_provider() {
+ return [
+ [
+ [ 'damaging' => false, 'goodfaith' => false ],
+ [
+ 'tables' => [],
+ 'fields' => [],
+ 'join_conds' => []
+ ]
+ ],
+ [
+ [ 'damaging' => true, 'goodfaith' => false ],
+ [
+ 'tables' => [
+ 'ores_damaging_mdl' =>
'ores_model',
+ 'ores_damaging_cls' =>
'ores_classification'
+ ],
+ 'fields' => [
+ 'ores_damaging_score' =>
'ores_damaging_cls.oresc_probability',
+ ],
+ 'join_conds' => [
+ 'ores_damaging_mdl' => [ 'LEFT
JOIN',
+ [
+
'ores_damaging_mdl.oresm_is_current' => 1,
+
'ores_damaging_mdl.oresm_name' => 'damaging'
+ ]
+ ],
+ 'ores_damaging_cls' => [ 'LEFT
JOIN',
+ [
+
'ores_damaging_cls.oresc_model = ores_damaging_mdl.oresm_id',
+ 'rc_this_oldid
= ores_damaging_cls.oresc_rev',
+
'ores_damaging_cls.oresc_class' => 1
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ [ 'damaging' => false, 'goodfaith' => true ],
+ [
+ 'tables' => [
+ 'ores_goodfaith_mdl' =>
'ores_model',
+ 'ores_goodfaith_cls' =>
'ores_classification'
+ ],
+ 'fields' => [
+ 'ores_goodfaith_score' =>
'ores_goodfaith_cls.oresc_probability',
+ ],
+ 'join_conds' => [
+ 'ores_goodfaith_mdl' => [ 'LEFT
JOIN',
+ [
+
'ores_goodfaith_mdl.oresm_is_current' => 1,
+
'ores_goodfaith_mdl.oresm_name' => 'goodfaith'
+ ]
+ ],
+ 'ores_goodfaith_cls' => [ 'LEFT
JOIN',
+ [
+
'ores_goodfaith_cls.oresc_model = ores_goodfaith_mdl.oresm_id',
+ 'rc_this_oldid
= ores_goodfaith_cls.oresc_rev',
+
'ores_goodfaith_cls.oresc_class' => 1
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ [ 'damaging' => true, 'goodfaith' => true ],
+ [
+ 'tables' => [
+ 'ores_damaging_mdl' =>
'ores_model',
+ 'ores_damaging_cls' =>
'ores_classification',
+ 'ores_goodfaith_mdl' =>
'ores_model',
+ 'ores_goodfaith_cls' =>
'ores_classification'
+ ],
+ 'fields' => [
+ 'ores_damaging_score' =>
'ores_damaging_cls.oresc_probability',
+ 'ores_goodfaith_score' =>
'ores_goodfaith_cls.oresc_probability',
+ ],
+ 'join_conds' => [
+ 'ores_damaging_mdl' => [ 'LEFT
JOIN',
+ [
+
'ores_damaging_mdl.oresm_is_current' => 1,
+
'ores_damaging_mdl.oresm_name' => 'damaging'
+ ]
+ ],
+ 'ores_damaging_cls' => [ 'LEFT
JOIN',
+ [
+
'ores_damaging_cls.oresc_model = ores_damaging_mdl.oresm_id',
+ 'rc_this_oldid
= ores_damaging_cls.oresc_rev',
+
'ores_damaging_cls.oresc_class' => 1
+ ]
+ ],
+ 'ores_goodfaith_mdl' => [ 'LEFT
JOIN',
+ [
+
'ores_goodfaith_mdl.oresm_is_current' => 1,
+
'ores_goodfaith_mdl.oresm_name' => 'goodfaith'
+ ]
+ ],
+ 'ores_goodfaith_cls' => [ 'LEFT
JOIN',
+ [
+
'ores_goodfaith_cls.oresc_model = ores_goodfaith_mdl.oresm_id',
+ 'rc_this_oldid
= ores_goodfaith_cls.oresc_rev',
+
'ores_goodfaith_cls.oresc_class' => 1
+ ]
+ ]
+ ]
+ ]
+ ]
+ ];
+ }
+
+ public function testOnEnhancedChangesListModifyLineDataDamaging() {
+ $row = new \stdClass();
+ $row->ores_damaging_threshold = 0.2;
+ $row->ores_damaging_score = 0.3;
+ $row->rc_patrolled = 0;
+ $row->rc_timestamp = '20150921134808';
+ $row->rc_deleted = 0;
+ $row->rc_comment = '';
+ $row->rc_comment_text = '';
+ $row->rc_comment_data = null;
+ $rc = RecentChange::newFromRow( $row );
+ $rc = RCCacheEntry::newFromParent( $rc );
+
+ $ecl = $this->getMockBuilder( EnhancedChangesList::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $ecl->expects( $this->any() )
+ ->method( 'getUser' )
+ ->will( $this->returnValue( $this->user ) );
+
+ $ecl->expects( $this->any() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( $this->context ) );
+
+ $data = [];
+ $block = [];
+ $classes = [];
+
+ ChangesListHooksHandler::onEnhancedChangesListModifyLineData(
+ $ecl,
+ $data,
+ $block,
+ $rc,
+ $classes
+ );
+
+ $this->assertSame( [ 'recentChangesFlags' => [ 'damaging' =>
true ] ], $data );
+ $this->assertSame( [], $block );
+ $this->assertSame( [ 'damaging' ], $classes );
+ }
+
+ public function testOnEnhancedChangesListModifyLineDataNonDamaging() {
+ $row = new \stdClass();
+ $row->ores_damaging_threshold = 0.4;
+ $row->ores_damaging_score = 0.3;
+ $row->rc_patrolled = 0;
+ $row->rc_timestamp = '20150921134808';
+ $row->rc_deleted = 0;
+ $row->rc_comment = '';
+ $row->rc_comment_text = '';
+ $row->rc_comment_data = null;
+ $rc = RecentChange::newFromRow( $row );
+ $rc = RCCacheEntry::newFromParent( $rc );
+
+ $ecl = $this->getMockBuilder( EnhancedChangesList::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $ecl->expects( $this->any() )
+ ->method( 'getUser' )
+ ->will( $this->returnValue( $this->user ) );
+
+ $ecl->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( SpecialPage::getTitleFor(
'Recentchanges' ) ) );
+
+ $ecl->expects( $this->any() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( $this->context ) );
+
+ $data = [];
+ $block = [];
+ $classes = [];
+
+ ChangesListHooksHandler::onEnhancedChangesListModifyLineData(
+ $ecl,
+ $data,
+ $block,
+ $rc,
+ $classes
+ );
+
+ $this->assertSame( [], $data );
+ $this->assertSame( [], $block );
+ $this->assertSame( [], $classes );
+ }
+
+ public function testOnOldChangesListModifyLineDataDamaging() {
+ $row = new \stdClass();
+ $row->ores_damaging_threshold = 0.2;
+ $row->ores_damaging_score = 0.3;
+ $row->rc_patrolled = 0;
+ $row->rc_timestamp = '20150921134808';
+ $row->rc_deleted = 0;
+ $row->rc_comment = '';
+ $row->rc_comment_text = '';
+ $row->rc_comment_data = null;
+ $rc = RecentChange::newFromRow( $row );
+ $rc = RCCacheEntry::newFromParent( $rc );
+
+ $config = $this->getMockBuilder( Config::class )->getMock();
+ $config->expects( $this->any() )
+ ->method( 'get' )
+ ->will( $this->returnValue( false ) );
+
+ $cl = $this->getMockBuilder( ChangesList::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $cl->expects( $this->any() )
+ ->method( 'getUser' )
+ ->will( $this->returnValue( $this->user ) );
+
+ $cl->expects( $this->any() )
+ ->method( 'getRequest' )
+ ->will( $this->returnValue( new FauxRequest() ) );
+
+ $cl->expects( $this->any() )
+ ->method( 'getConfig' )
+ ->will( $this->returnValue( $config ) );
+
+ $cl->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( SpecialPage::getTitleFor(
'Recentchanges' ) ) );
+
+ $cl->expects( $this->any() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( $this->context ) );
+
+ $classes = [];
+
+ $s = ' <span class="mw-changeslist-separator">. .</span> ';
+ ChangesListHooksHandler::onOldChangesListRecentChangesLine(
$cl, $s, $rc, $classes );
+
+ $this->assertSame(
+ ' <span class="mw-changeslist-separator">. .</span>' .
+ ' <abbr class="ores-damaging" title="This edit needs
review">r</abbr>',
+ $s
+ );
+ $this->assertSame( [ 'ores-highlight', 'damaging' ], $classes );
+ }
+
+ public function testOnOldChangesListModifyLineDataNonDamaging() {
+ $row = new \stdClass();
+ $row->ores_damaging_threshold = 0.4;
+ $row->ores_damaging_score = 0.3;
+ $row->rc_patrolled = 0;
+ $row->rc_timestamp = '20150921134808';
+ $row->rc_deleted = 0;
+ $row->rc_comment = '';
+ $row->rc_comment_text = '';
+ $row->rc_comment_data = null;
+ $rc = RecentChange::newFromRow( $row );
+ $rc = RCCacheEntry::newFromParent( $rc );
+
+ $cl = $this->getMockBuilder( ChangesList::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $cl->expects( $this->any() )
+ ->method( 'getUser' )
+ ->will( $this->returnValue( $this->user ) );
+
+ $cl->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( SpecialPage::getTitleFor(
'Recentchanges' ) ) );
+
+ $cl->expects( $this->any() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( $this->context ) );
+
+ $classes = [];
+
+ $s = ' <span class="mw-changeslist-separator">. .</span> ';
+ ChangesListHooksHandler::onOldChangesListRecentChangesLine(
$cl, $s, $rc, $classes );
+
+ $this->assertSame( ' <span class="mw-changeslist-separator">.
.</span> ', $s );
+ $this->assertSame( [], $classes );
+ }
+
+ public function provideOnContribsGetQueryInfo() {
+ $expected = [
+ 'tables' => [
+ 'ores_damaging_mdl' => 'ores_model',
+ 'ores_damaging_cls' => 'ores_classification'
+ ],
+ 'fields' => [
+ 'ores_damaging_score' =>
'ores_damaging_cls.oresc_probability',
+ 'ores_damaging_threshold' => "'0.16'"
+ ],
+ 'conds' => [],
+ 'join_conds' => [
+ 'ores_damaging_mdl' => [
+ 'LEFT JOIN',
+ [
+
'ores_damaging_mdl.oresm_is_current' => 1,
+ 'ores_damaging_mdl.oresm_name'
=> 'damaging'
+ ]
+ ],
+ 'ores_damaging_cls' => [
+ 'LEFT JOIN',
+ [
+ 'ores_damaging_cls.oresc_model
= ores_damaging_mdl.oresm_id',
+ 'rev_id =
ores_damaging_cls.oresc_rev',
+ 'ores_damaging_cls.oresc_class'
=> 1
+ ]
+ ]
+ ],
+ ];
+
+ $expectedDamaging = $expected;
+ $expectedDamaging['conds'] = [
'ores_damaging_cls.oresc_probability > \'0.16\'' ];
+
+ return [
+ 'all' => [ $expected, false ],
+ 'damaging only' => [ $expectedDamaging, true ]
+ ];
+ }
+
+ /**
+ * @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;
+ }
+
+}
diff --git a/tests/phpunit/includes/Hooks/ContributionsHookHandlerTest.php
b/tests/phpunit/includes/Hooks/ContributionsHookHandlerTest.php
new file mode 100644
index 0000000..a297019
--- /dev/null
+++ b/tests/phpunit/includes/Hooks/ContributionsHookHandlerTest.php
@@ -0,0 +1,182 @@
+<?php
+
+namespace ORES\Tests;
+
+use ContribsPager;
+use IContextSource;
+use ORES\Hooks\ContributionsHooksHandler;
+use RequestContext;
+use SpecialPage;
+use User;
+
+/**
+ * @group ORES
+ * @covers ORES\Hooks\ContributionsHooksHandler
+ */
+class ContributionsHookHandlerTest 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 );
+ }
+
+ /**
+ * @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;
+ }
+
+ public function testOnContribsGetQueryInfo( array $expected,
$nonDamaging ) {
+ $cp =
+ $this->getMockBuilder( ContribsPager::class
)->disableOriginalConstructor()->getMock();
+
+ $cp->expects( $this->any() )
+ ->method( 'getUser' )
+ ->will( $this->returnValue( $this->user ) );
+
+ $cp->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( SpecialPage::getTitleFor(
'Contributions' ) ) );
+
+ $cp->expects( $this->any() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( $this->context ) );
+
+ if ( $nonDamaging === true ) {
+ $this->context->getRequest()->setVal(
'hidenondamaging', true );
+ }
+
+ $query = [
+ 'tables' => [],
+ 'fields' => [],
+ 'conds' => [],
+ 'options' => [],
+ 'join_conds' => [],
+ ];
+ ContributionsHooksHandler::onContribsGetQueryInfo( $cp, $query
);
+
+ $this->assertSame( $expected['tables'], $query['tables'] );
+ $this->assertSame( $expected['fields'], $query['fields'] );
+ $this->assertSame( $expected['conds'], $query['conds'] );
+ $this->assertSame( $expected['join_conds'],
$query['join_conds'] );
+ }
+
+ public function testOnSpecialContributionsFormatRowFlagsDamaging() {
+ $row = new \stdClass();
+ $row->ores_damaging_threshold = 0.2;
+ $row->ores_damaging_score = 0.3;
+ $row->rev_id = 0;
+
+ $flags = [];
+
+
ContributionsHooksHandler::onSpecialContributionsFormatRowFlags(
$this->context, $row, $flags );
+
+ $this->assertSame( [ '<abbr class="ores-damaging" title="This
edit needs review">r</abbr>' ],
+ $flags );
+ }
+
+ public function testOnSpecialContributionsFormatRowFlagsNonDamaging() {
+ $row = new \stdClass();
+ $row->ores_damaging_threshold = 0.4;
+ $row->ores_damaging_score = 0.3;
+ $row->rev_id = 0;
+
+ $flags = [];
+
+
ContributionsHooksHandler::onSpecialContributionsFormatRowFlags(
$this->context, $row, $flags );
+
+ $this->assertSame( [], $flags );
+ }
+
+ public function testOnContributionsLineEndingDamaging() {
+ $cp =
+ $this->getMockBuilder( ContribsPager::class
)->disableOriginalConstructor()->getMock();
+
+ $cp->expects( $this->any() )
+ ->method( 'getUser' )
+ ->will( $this->returnValue( $this->user ) );
+
+ $cp->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( SpecialPage::getTitleFor(
'Contributions' ) ) );
+
+ $cp->expects( $this->any() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( $this->context ) );
+
+ $row = new \stdClass();
+ $row->ores_damaging_threshold = 0.2;
+ $row->ores_damaging_score = 0.3;
+ $row->rev_id = 0;
+
+ $ret = [];
+ $classes = [];
+
+ ContributionsHooksHandler::onContributionsLineEnding( $cp,
$ret, $row, $classes );
+
+ $this->assertSame( [ 'ores-highlight', 'damaging' ], $classes );
+ $this->assertSame( [], $ret );
+ }
+
+ public function testOnContributionsLineEndingNonDamaging() {
+ $cp =
+ $this->getMockBuilder( ContribsPager::class
)->disableOriginalConstructor()->getMock();
+
+ $cp->expects( $this->any() )
+ ->method( 'getUser' )
+ ->will( $this->returnValue( $this->user ) );
+
+ $cp->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( SpecialPage::getTitleFor(
'Contributions' ) ) );
+
+ $cp->expects( $this->any() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( $this->context ) );
+
+ $row = new \stdClass();
+ $row->ores_damaging_threshold = 0.4;
+ $row->ores_damaging_score = 0.3;
+ $row->rev_id = 0;
+
+ $ret = [];
+ $classes = [];
+
+ ContributionsHooksHandler::onContributionsLineEnding( $cp,
$ret, $row, $classes );
+
+ $this->assertSame( [], $classes );
+ $this->assertSame( [], $ret );
+ }
+
+}
diff --git a/tests/phpunit/includes/HooksTest.php
b/tests/phpunit/includes/HooksTest.php
index 7dff0a8..fd374a2 100644
--- a/tests/phpunit/includes/HooksTest.php
+++ b/tests/phpunit/includes/HooksTest.php
@@ -2,24 +2,12 @@
namespace ORES\Tests;
-use ChangesList;
-use Config;
-use ContribsPager;
-use EnhancedChangesList;
-use EventRelayerNull;
-use FauxRequest;
-use FormOptions;
-use HashBagOStuff;
use IContextSource;
-use MediaWiki\MediaWikiServices;
-use ORES;
+use ORES\Hooks;
use ORES\Hooks\PreferencesHookHandler;
-use RCCacheEntry;
-use RecentChange;
use RequestContext;
use SpecialPage;
use User;
-use WANObjectCache;
/**
* @group ORES
@@ -66,512 +54,15 @@
$this->user->setOption( 'oresDamagingPref', 'maybebad' );
$this->assertEquals(
0.16,
- ORES\Hooks::getThreshold( 'damaging', $this->user )
+ Hooks::getThreshold( 'damaging', $this->user )
);
// b/c
$this->user->setOption( 'oresDamagingPref', 'soft' );
$this->assertEquals(
0.56,
- ORES\Hooks::getThreshold( 'damaging', $this->user )
+ Hooks::getThreshold( 'damaging', $this->user )
);
- }
-
- public function testOresRCObj() {
- $row = new \stdClass();
- $row->ores_damaging_threshold = 0.2;
- $row->ores_damaging_score = 0.3;
- $row->rc_patrolled = 0;
- $row->rc_timestamp = '20150921134808';
- $row->rc_deleted = 0;
- $row->rc_comment = '';
- $row->rc_comment_text = '';
- $row->rc_comment_data = null;
-
- $rc = RecentChange::newFromRow( $row );
- $this->assertTrue( ORES\Hooks::getScoreRecentChangesList( $rc,
$this->context ) );
-
- $row->ores_damaging_threshold = 0.4;
- $rc = RecentChange::newFromRow( $row );
- $this->assertFalse( ORES\Hooks::getScoreRecentChangesList( $rc,
$this->context ) );
- }
-
- /**
- * @dataProvider onChangesListSpecialPageQuery_provider
- */
- public function testOnChangesListSpecialPageQuery( $modelConfig,
$expectedQuery ) {
- $this->setMwGlobals( [
- 'wgUser' => $this->user,
- 'wgOresModels' => $modelConfig
- ] );
- $tables = [];
- $fields = [];
- $conds = [];
- $query_options = [];
- $join_conds = [];
- ORES\Hooks::onChangesListSpecialPageQuery(
- '',
- $tables,
- $fields,
- $conds,
- $query_options,
- $join_conds,
- new FormOptions()
- );
- $this->assertSame( $expectedQuery['tables'], $tables );
- $this->assertSame( $expectedQuery['fields'], $fields );
- $this->assertSame( $expectedQuery['join_conds'], $join_conds );
- }
-
- public function onChangesListSpecialPageQuery_provider() {
- return [
- [
- [ 'damaging' => false, 'goodfaith' => false ],
- [
- 'tables' => [],
- 'fields' => [],
- 'join_conds' => []
- ]
- ],
- [
- [ 'damaging' => true, 'goodfaith' => false ],
- [
- 'tables' => [
- 'ores_damaging_mdl' =>
'ores_model',
- 'ores_damaging_cls' =>
'ores_classification'
- ],
- 'fields' => [
- 'ores_damaging_score' =>
'ores_damaging_cls.oresc_probability',
- ],
- 'join_conds' => [
- 'ores_damaging_mdl' => [ 'LEFT
JOIN',
- [
-
'ores_damaging_mdl.oresm_is_current' => 1,
-
'ores_damaging_mdl.oresm_name' => 'damaging'
- ]
- ],
- 'ores_damaging_cls' => [ 'LEFT
JOIN',
- [
-
'ores_damaging_cls.oresc_model = ores_damaging_mdl.oresm_id',
- 'rc_this_oldid
= ores_damaging_cls.oresc_rev',
-
'ores_damaging_cls.oresc_class' => 1
- ]
- ]
- ]
- ]
- ],
- [
- [ 'damaging' => false, 'goodfaith' => true ],
- [
- 'tables' => [
- 'ores_goodfaith_mdl' =>
'ores_model',
- 'ores_goodfaith_cls' =>
'ores_classification'
- ],
- 'fields' => [
- 'ores_goodfaith_score' =>
'ores_goodfaith_cls.oresc_probability',
- ],
- 'join_conds' => [
- 'ores_goodfaith_mdl' => [ 'LEFT
JOIN',
- [
-
'ores_goodfaith_mdl.oresm_is_current' => 1,
-
'ores_goodfaith_mdl.oresm_name' => 'goodfaith'
- ]
- ],
- 'ores_goodfaith_cls' => [ 'LEFT
JOIN',
- [
-
'ores_goodfaith_cls.oresc_model = ores_goodfaith_mdl.oresm_id',
- 'rc_this_oldid
= ores_goodfaith_cls.oresc_rev',
-
'ores_goodfaith_cls.oresc_class' => 1
- ]
- ]
- ]
- ]
- ],
- [
- [ 'damaging' => true, 'goodfaith' => true ],
- [
- 'tables' => [
- 'ores_damaging_mdl' =>
'ores_model',
- 'ores_damaging_cls' =>
'ores_classification',
- 'ores_goodfaith_mdl' =>
'ores_model',
- 'ores_goodfaith_cls' =>
'ores_classification'
- ],
- 'fields' => [
- 'ores_damaging_score' =>
'ores_damaging_cls.oresc_probability',
- 'ores_goodfaith_score' =>
'ores_goodfaith_cls.oresc_probability',
- ],
- 'join_conds' => [
- 'ores_damaging_mdl' => [ 'LEFT
JOIN',
- [
-
'ores_damaging_mdl.oresm_is_current' => 1,
-
'ores_damaging_mdl.oresm_name' => 'damaging'
- ]
- ],
- 'ores_damaging_cls' => [ 'LEFT
JOIN',
- [
-
'ores_damaging_cls.oresc_model = ores_damaging_mdl.oresm_id',
- 'rc_this_oldid
= ores_damaging_cls.oresc_rev',
-
'ores_damaging_cls.oresc_class' => 1
- ]
- ],
- 'ores_goodfaith_mdl' => [ 'LEFT
JOIN',
- [
-
'ores_goodfaith_mdl.oresm_is_current' => 1,
-
'ores_goodfaith_mdl.oresm_name' => 'goodfaith'
- ]
- ],
- 'ores_goodfaith_cls' => [ 'LEFT
JOIN',
- [
-
'ores_goodfaith_cls.oresc_model = ores_goodfaith_mdl.oresm_id',
- 'rc_this_oldid
= ores_goodfaith_cls.oresc_rev',
-
'ores_goodfaith_cls.oresc_class' => 1
- ]
- ]
- ]
- ]
- ]
- ];
- }
-
- public function testOnEnhancedChangesListModifyLineDataDamaging() {
- $row = new \stdClass();
- $row->ores_damaging_threshold = 0.2;
- $row->ores_damaging_score = 0.3;
- $row->rc_patrolled = 0;
- $row->rc_timestamp = '20150921134808';
- $row->rc_deleted = 0;
- $row->rc_comment = '';
- $row->rc_comment_text = '';
- $row->rc_comment_data = null;
- $rc = RecentChange::newFromRow( $row );
- $rc = RCCacheEntry::newFromParent( $rc );
-
- $ecl = $this->getMockBuilder( EnhancedChangesList::class )
- ->disableOriginalConstructor()
- ->getMock();
-
- $ecl->expects( $this->any() )
- ->method( 'getUser' )
- ->will( $this->returnValue( $this->user ) );
-
- $ecl->expects( $this->any() )
- ->method( 'getContext' )
- ->will( $this->returnValue( $this->context ) );
-
- $data = [];
- $block = [];
- $classes = [];
-
- ORES\Hooks::onEnhancedChangesListModifyLineData( $ecl, $data,
$block, $rc, $classes );
-
- $this->assertSame( [ 'recentChangesFlags' => [ 'damaging' =>
true ] ], $data );
- $this->assertSame( [], $block );
- $this->assertSame( [ 'damaging' ], $classes );
- }
-
- public function testOnEnhancedChangesListModifyLineDataNonDamaging() {
- $row = new \stdClass();
- $row->ores_damaging_threshold = 0.4;
- $row->ores_damaging_score = 0.3;
- $row->rc_patrolled = 0;
- $row->rc_timestamp = '20150921134808';
- $row->rc_deleted = 0;
- $row->rc_comment = '';
- $row->rc_comment_text = '';
- $row->rc_comment_data = null;
- $rc = RecentChange::newFromRow( $row );
- $rc = RCCacheEntry::newFromParent( $rc );
-
- $ecl = $this->getMockBuilder( EnhancedChangesList::class )
- ->disableOriginalConstructor()
- ->getMock();
-
- $ecl->expects( $this->any() )
- ->method( 'getUser' )
- ->will( $this->returnValue( $this->user ) );
-
- $ecl->expects( $this->any() )
- ->method( 'getTitle' )
- ->will( $this->returnValue( SpecialPage::getTitleFor(
'Recentchanges' ) ) );
-
- $ecl->expects( $this->any() )
- ->method( 'getContext' )
- ->will( $this->returnValue( $this->context ) );
-
- $data = [];
- $block = [];
- $classes = [];
-
- ORES\Hooks::onEnhancedChangesListModifyLineData( $ecl, $data,
$block, $rc, $classes );
-
- $this->assertSame( [], $data );
- $this->assertSame( [], $block );
- $this->assertSame( [], $classes );
- }
-
- public function testOnOldChangesListModifyLineDataDamaging() {
- $row = new \stdClass();
- $row->ores_damaging_threshold = 0.2;
- $row->ores_damaging_score = 0.3;
- $row->rc_patrolled = 0;
- $row->rc_timestamp = '20150921134808';
- $row->rc_deleted = 0;
- $row->rc_comment = '';
- $row->rc_comment_text = '';
- $row->rc_comment_data = null;
- $rc = RecentChange::newFromRow( $row );
- $rc = RCCacheEntry::newFromParent( $rc );
-
- $config = $this->getMockBuilder( Config::class )->getMock();
- $config->expects( $this->any() )
- ->method( 'get' )
- ->will( $this->returnValue( false ) );
-
- $cl = $this->getMockBuilder( ChangesList::class )
- ->disableOriginalConstructor()
- ->getMock();
-
- $cl->expects( $this->any() )
- ->method( 'getUser' )
- ->will( $this->returnValue( $this->user ) );
-
- $cl->expects( $this->any() )
- ->method( 'getRequest' )
- ->will( $this->returnValue( new FauxRequest() ) );
-
- $cl->expects( $this->any() )
- ->method( 'getConfig' )
- ->will( $this->returnValue( $config ) );
-
- $cl->expects( $this->any() )
- ->method( 'getTitle' )
- ->will( $this->returnValue( SpecialPage::getTitleFor(
'Recentchanges' ) ) );
-
- $cl->expects( $this->any() )
- ->method( 'getContext' )
- ->will( $this->returnValue( $this->context ) );
-
- $classes = [];
-
- $s = ' <span class="mw-changeslist-separator">. .</span> ';
- ORES\Hooks::onOldChangesListRecentChangesLine( $cl, $s, $rc,
$classes );
-
- $this->assertSame(
- ' <span class="mw-changeslist-separator">. .</span>' .
- ' <abbr class="ores-damaging" title="This edit needs
review">r</abbr>',
- $s
- );
- $this->assertSame( [ 'ores-highlight', 'damaging' ], $classes );
- }
-
- public function testOnOldChangesListModifyLineDataNonDamaging() {
- $row = new \stdClass();
- $row->ores_damaging_threshold = 0.4;
- $row->ores_damaging_score = 0.3;
- $row->rc_patrolled = 0;
- $row->rc_timestamp = '20150921134808';
- $row->rc_deleted = 0;
- $row->rc_comment = '';
- $row->rc_comment_text = '';
- $row->rc_comment_data = null;
- $rc = RecentChange::newFromRow( $row );
- $rc = RCCacheEntry::newFromParent( $rc );
-
- $cl = $this->getMockBuilder( ChangesList::class )
- ->disableOriginalConstructor()
- ->getMock();
-
- $cl->expects( $this->any() )
- ->method( 'getUser' )
- ->will( $this->returnValue( $this->user ) );
-
- $cl->expects( $this->any() )
- ->method( 'getTitle' )
- ->will( $this->returnValue( SpecialPage::getTitleFor(
'Recentchanges' ) ) );
-
- $cl->expects( $this->any() )
- ->method( 'getContext' )
- ->will( $this->returnValue( $this->context ) );
-
- $classes = [];
-
- $s = ' <span class="mw-changeslist-separator">. .</span> ';
- ORES\Hooks::onOldChangesListRecentChangesLine( $cl, $s, $rc,
$classes );
-
- $this->assertSame( ' <span class="mw-changeslist-separator">.
.</span> ', $s );
- $this->assertSame( [], $classes );
- }
-
- public function provideOnContribsGetQueryInfo() {
- $expected = [
- 'tables' => [
- 'ores_damaging_mdl' => 'ores_model',
- 'ores_damaging_cls' => 'ores_classification'
- ],
- 'fields' => [
- 'ores_damaging_score' =>
'ores_damaging_cls.oresc_probability',
- 'ores_damaging_threshold' => "'0.16'"
- ],
- 'conds' => [],
- 'join_conds' => [
- 'ores_damaging_mdl' => [
- 'LEFT JOIN',
- [
-
'ores_damaging_mdl.oresm_is_current' => 1,
- 'ores_damaging_mdl.oresm_name'
=> 'damaging'
- ]
- ],
- 'ores_damaging_cls' => [
- 'LEFT JOIN',
- [
- 'ores_damaging_cls.oresc_model
= ores_damaging_mdl.oresm_id',
- 'rev_id =
ores_damaging_cls.oresc_rev',
- 'ores_damaging_cls.oresc_class'
=> 1
- ]
- ]
- ],
- ];
-
- $expectedDamaging = $expected;
- $expectedDamaging['conds'] = [
'ores_damaging_cls.oresc_probability > \'0.16\'' ];
-
- return [
- 'all' => [ $expected, false ],
- 'damaging only' => [ $expectedDamaging, true ]
- ];
- }
-
- /**
- * @dataProvider provideOnContribsGetQueryInfo
- */
- public function testOnContribsGetQueryInfo( array $expected,
$nonDamaging ) {
- $cp = $this->getMockBuilder( ContribsPager::class )
- ->disableOriginalConstructor()
- ->getMock();
-
- $cp->expects( $this->any() )
- ->method( 'getUser' )
- ->will( $this->returnValue( $this->user ) );
-
- $cp->expects( $this->any() )
- ->method( 'getTitle' )
- ->will( $this->returnValue( SpecialPage::getTitleFor(
'Contributions' ) ) );
-
- $cp->expects( $this->any() )
- ->method( 'getContext' )
- ->will( $this->returnValue( $this->context ) );
-
- if ( $nonDamaging === true ) {
- $this->context->getRequest()->setVal(
'hidenondamaging', true );
- }
-
- $query = [
- 'tables' => [],
- 'fields' => [],
- 'conds' => [],
- 'options' => [],
- 'join_conds' => [],
- ];
- ORES\Hooks::onContribsGetQueryInfo(
- $cp,
- $query
- );
-
- $this->assertSame( $expected['tables'], $query['tables'] );
- $this->assertSame( $expected['fields'], $query['fields'] );
- $this->assertSame( $expected['conds'], $query['conds'] );
- $this->assertSame( $expected['join_conds'],
$query['join_conds'] );
- }
-
- public function testOnSpecialContributionsFormatRowFlagsDamaging() {
- $row = new \stdClass();
- $row->ores_damaging_threshold = 0.2;
- $row->ores_damaging_score = 0.3;
- $row->rev_id = 0;
-
- $flags = [];
-
- ORES\Hooks::onSpecialContributionsFormatRowFlags(
$this->context, $row, $flags );
-
- $this->assertSame(
- [ '<abbr class="ores-damaging" title="This edit needs
review">r</abbr>' ],
- $flags
- );
- }
-
- public function testOnSpecialContributionsFormatRowFlagsNonDamaging() {
- $row = new \stdClass();
- $row->ores_damaging_threshold = 0.4;
- $row->ores_damaging_score = 0.3;
- $row->rev_id = 0;
-
- $flags = [];
-
- ORES\Hooks::onSpecialContributionsFormatRowFlags(
$this->context, $row, $flags );
-
- $this->assertSame( [], $flags );
- }
-
- public function testOnContributionsLineEndingDamaging() {
- $cp = $this->getMockBuilder( ContribsPager::class )
- ->disableOriginalConstructor()
- ->getMock();
-
- $cp->expects( $this->any() )
- ->method( 'getUser' )
- ->will( $this->returnValue( $this->user ) );
-
- $cp->expects( $this->any() )
- ->method( 'getTitle' )
- ->will( $this->returnValue( SpecialPage::getTitleFor(
'Contributions' ) ) );
-
- $cp->expects( $this->any() )
- ->method( 'getContext' )
- ->will( $this->returnValue( $this->context ) );
-
- $row = new \stdClass();
- $row->ores_damaging_threshold = 0.2;
- $row->ores_damaging_score = 0.3;
- $row->rev_id = 0;
-
- $ret = [];
- $classes = [];
-
- ORES\Hooks::onContributionsLineEnding( $cp, $ret, $row,
$classes );
-
- $this->assertSame( [ 'ores-highlight', 'damaging' ], $classes );
- $this->assertSame( [], $ret );
- }
-
- public function testOnContributionsLineEndingNonDamaging() {
- $cp = $this->getMockBuilder( ContribsPager::class )
- ->disableOriginalConstructor()
- ->getMock();
-
- $cp->expects( $this->any() )
- ->method( 'getUser' )
- ->will( $this->returnValue( $this->user ) );
-
- $cp->expects( $this->any() )
- ->method( 'getTitle' )
- ->will( $this->returnValue( SpecialPage::getTitleFor(
'Contributions' ) ) );
-
- $cp->expects( $this->any() )
- ->method( 'getContext' )
- ->will( $this->returnValue( $this->context ) );
-
- $row = new \stdClass();
- $row->ores_damaging_threshold = 0.4;
- $row->ores_damaging_score = 0.3;
- $row->rev_id = 0;
-
- $ret = [];
- $classes = [];
-
- ORES\Hooks::onContributionsLineEnding( $cp, $ret, $row,
$classes );
-
- $this->assertSame( [], $classes );
- $this->assertSame( [], $ret );
}
public function testOnGetPreferencesEnabled() {
@@ -584,7 +75,7 @@
public function testOnGetBetaFeaturePreferences_on() {
$this->setMwGlobals( 'wgOresExtensionStatus', 'on' );
$prefs = [];
- ORES\Hooks::onGetBetaFeaturePreferences( $this->user, $prefs );
+ Hooks::onGetBetaFeaturePreferences( $this->user, $prefs );
$this->assertSame( 0, count( $prefs ) );
}
@@ -592,7 +83,7 @@
public function testOnGetBetaFeaturePreferences_off() {
$this->setMwGlobals( 'wgOresExtensionStatus', 'off' );
$prefs = [];
- ORES\Hooks::onGetBetaFeaturePreferences( $this->user, $prefs );
+ Hooks::onGetBetaFeaturePreferences( $this->user, $prefs );
$this->assertSame( 0, count( $prefs ) );
}
@@ -600,7 +91,7 @@
public function testOnGetBetaFeaturePreferences_beta() {
$this->setMwGlobals( 'wgOresExtensionStatus', 'beta' );
$prefs = [];
- ORES\Hooks::onGetBetaFeaturePreferences( $this->user, $prefs );
+ Hooks::onGetBetaFeaturePreferences( $this->user, $prefs );
$this->assertSame( 1, count( $prefs ) );
$this->assertArrayHasKey( 'ores-enabled', $prefs );
@@ -619,36 +110,6 @@
$context->setTitle( SpecialPage::getTitleFor( 'Recentchanges' )
);
return $context;
- }
-
- private function mockStatsInCache() {
- $cache = new WANObjectCache( [
- 'cache' => new HashBagOStuff(),
- 'pool' => 'testcache-hash',
- 'relayer' => new EventRelayerNull( [] )
- ] );
-
- $invalidStats = [ 'will trigger the use of' => 'default values'
];
-
- $cache->set(
- $cache->makeKey( 'ORES', 'test_stats', 'damaging' ),
- $invalidStats,
- \WANObjectCache::TTL_DAY
- );
-
- $cache->set(
- $cache->makeKey( 'ORES', 'test_stats', 'goodfaith' ),
- $invalidStats,
- \WANObjectCache::TTL_DAY
- );
-
- MediaWikiServices::getInstance()->resetServiceForTesting(
'MainWANObjectCache' );
- MediaWikiServices::getInstance()->redefineService(
- 'MainWANObjectCache',
- function () use ( $cache ) {
- return $cache;
- }
- );
}
}
--
To view, visit https://gerrit.wikimedia.org/r/392415
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I35247553444d595330f149c6c5b1f29165ee1e40
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