Compare commits

..

11 Commits

Author SHA1 Message Date
libraryupgrader
168ee8918c build: Updating npm dependencies
* grunt: 1.6.1 → 1.6.2
* grunt-legacy-log: 3.0.0 → 3.0.1
  * https://github.com/advisories/GHSA-f23m-r3pf-42rh
  * https://github.com/advisories/GHSA-r5fr-rjxr-66jc
* grunt-legacy-log-utils: 2.1.0 → 2.1.3
  * https://github.com/advisories/GHSA-f23m-r3pf-42rh
  * https://github.com/advisories/GHSA-r5fr-rjxr-66jc
* grunt-legacy-util: 2.0.1 → 2.0.2
  * https://github.com/advisories/GHSA-f23m-r3pf-42rh
  * https://github.com/advisories/GHSA-r5fr-rjxr-66jc
* lodash: 4.17.23 → 4.18.1
  * https://github.com/advisories/GHSA-f23m-r3pf-42rh
  * https://github.com/advisories/GHSA-r5fr-rjxr-66jc

Change-Id: I8d0e22493c589cc1487126a534667b38ed3eb4c3
2026-04-16 22:44:02 +00:00
libraryupgrader
bdb4346fd0 build: Updating brace-expansion to 1.1.13, 2.0.3
* https://github.com/advisories/GHSA-f886-m6hf-6m8v

Change-Id: I0b9d005a833f976111321ef517bae9004dbca8c1
2026-03-31 06:07:27 +00:00
libraryupgrader
75aab46d46 build: Updating npm dependencies
* picomatch: 2.3.1, 4.0.3 → 2.3.2, 4.0.4
  * https://github.com/advisories/GHSA-3v7f-55p6-f55p
  * https://github.com/advisories/GHSA-c2c7-rcm5-vvqj
* yaml: 2.4.1 → 2.8.3
  * https://github.com/advisories/GHSA-48c2-rrv3-qjmp

Change-Id: Icad06a6ee65a572f93a27e7870e0a17eefea8171
2026-03-27 02:01:21 +00:00
libraryupgrader
61b91c602e build: Updating flatted to 3.4.2
* https://github.com/advisories/GHSA-rf6f-7fwh-wjgh

Change-Id: I2c644094eb514ddaffe8d61c2ef998034db32b9d
2026-03-23 04:48:38 +00:00
libraryupgrader
d81cda7856 build: Updating flatted to 3.4.1
* https://github.com/advisories/GHSA-25h7-pfq9-p65f

Change-Id: Icaa97bad2ed3d0e0ad785c39583ca59845dc2cd5
2026-03-15 19:31:23 +00:00
libraryupgrader
9ca5e0338b build: Updating ajv to 6.14.0
* https://github.com/advisories/GHSA-2g4f-4pwh-qvx6

Change-Id: I715439521f09081dca8d3e8b798a0908c5e1bbed
2026-02-23 05:48:27 +00:00
libraryupgrader
9387aaf518 build: Updating lodash to 4.17.23
* https://github.com/advisories/GHSA-xxjr-mmjv-4gpg

Change-Id: Ifa2ed4a9d0ca3ad5e4948425062848cee66fa4bd
2026-01-23 04:00:32 +00:00
libraryupgrader
2efa7a5548 build: Updating js-yaml to 3.14.2, 4.1.1
* https://github.com/advisories/GHSA-mh29-5h37-fv8m

Change-Id: I9a6613aa11b95df94963d3926fadf56e1cfb9adb
2025-11-21 06:11:45 +00:00
libraryupgrader
6ecb9475a4 build: Updating brace-expansion to 1.1.12, 2.0.2
* https://github.com/advisories/GHSA-v6h2-p8h4-qcjw

Change-Id: I1dc1be164ce2477deec330eb0de0498dc1dc8b73
2025-07-07 19:18:01 +00:00
Pskyechology
283cf86e62 Use namespaced imports
Also bumps MW requirement to 1.40 as the namespaces were introduced in that version.
Remove unnecessary GROUP BY, which causes errors in MySQL strict mode.

Change-Id: I9cfc32b13518da39b97662e424682d4d7a7cf862
2025-07-05 22:23:29 +00:00
libraryupgrader
49d63684c8 build: Updating cross-spawn to 7.0.6
* https://github.com/advisories/GHSA-3xgq-45jj-v275

Change-Id: Ied99fe7e6fc844f11cfdef7f122a259208b68d0b
2025-05-08 04:21:16 +00:00
8 changed files with 807 additions and 1198 deletions

View File

@@ -1,7 +1,7 @@
{
"root": true,
"extends": [
"wikimedia/client",
"wikimedia/client-es5",
"wikimedia/jquery",
"wikimedia/mediawiki"
]

View File

@@ -268,17 +268,12 @@ $specialPageAliases['uk'] = [
'ContributionScores' => [ 'Очки_внеску', 'Очки_вклада' ],
];
/** Chinese (中文) */
$specialPageAliases['zh'] = [
'ContributionScores' => [ 'ContributionScores' ],
];
/** Simplified Chinese (中文(简体)) */
/** Simplified Chinese (中文(简体)‎) */
$specialPageAliases['zh-hans'] = [
'ContributionScores' => [ '贡献得分' ],
];
/** Traditional Chinese (中文(繁體)) */
/** Traditional Chinese (中文(繁體)) */
$specialPageAliases['zh-hant'] = [
'ContributionScores' => [ '貢獻分數' ],
];

26
README
View File

@@ -1,28 +1,2 @@
The Contribution Scores extension polls the wiki database to locate contributors with the highest contribution volume.
The extension is intended to add a fun metric for contributors to see how much they are helping out.
--- TEAR HERE ---
This is a modified version of Extension:Contribution Scores, the original source
code for which can be found here: https://www.mediawiki.org/wiki/Extension:Contribution_Scores
The rest of this document discussed changes made to this copy of the extension.
This fork developed by Eunakria the config option $wgContribScoreIgnoreMinorEdits,
which is mutually exclusive with $wgContribScoreUseRoughEditCount.
In addition to providing the aforementioned bug fixes, this fork also fixes two
other unwanted behaviors exhibited by the original extension:
1. When $wgContribScoreUseRoughEditCount was enabled, the extension would query
the user_editcount field of the user table but not actually join on the user
table, in the parser function {{#cscore}}. This was presumably never noticed
since this parser function is not frequently used.
2. On some databases, there was a potential for integer underflow when
subtracting unsigned values in both the parser function and contribution
score data. This could occur if $wgContribScoreUseRoughEditCount was enabled
and edits went uncounted in user_editcount, and was resolved by clamping the
right-hand side preimage of subtraction.
There are probably more bugs I don't know about, both in the original upstream
code *and* my code! In other words, caveat emptor.

View File

@@ -1,7 +1,7 @@
{
"require-dev": {
"mediawiki/mediawiki-codesniffer": "51.0.0",
"mediawiki/minus-x": "2.0.1",
"mediawiki/mediawiki-codesniffer": "45.0.0",
"mediawiki/minus-x": "1.1.3",
"php-parallel-lint/php-console-highlighter": "1.0.0",
"php-parallel-lint/php-parallel-lint": "1.4.0"
},

View File

@@ -1,7 +1,7 @@
{
"name": "ContributionScores",
"author": "Tim Laqua (modified by Eunakria)",
"url": "https://git.eunakria.com/Eunakria/mediawiki-extensions-ContributionScores",
"author": "Tim Laqua",
"url": "https://www.mediawiki.org/wiki/Extension:Contribution_Scores",
"descriptionmsg": "contributionscores-desc",
"version": "1.26.1",
"type": "specialpage",
@@ -53,11 +53,7 @@
},
"ContribScoreUseRoughEditCount": {
"value": false,
"description": "Set to true to use the rough number of edits in user table, for performance issue. Mutually exclusive with $ContribScoreIgnoreMinorEdits; this option will be disabled if the other is enabled."
},
"ContribScoreIgnoreMinorEdits": {
"value": false,
"description": "Set to true to ignore minor edits. Mutually exclusive with $wgContribScoreUseRoughEditCount; if this is enabled, the other option will be disabled."
"description": "Set to true to use the rough number of edits in user table, for performance issue."
},
"ContribScoreCacheTTL": {
"value": 30,

1834
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@
"test": "grunt test"
},
"devDependencies": {
"eslint-config-wikimedia": "0.32.4",
"eslint-config-wikimedia": "0.28.2",
"grunt": "1.6.2",
"grunt-banana-checker": "0.13.0",
"grunt-eslint": "24.3.0"

View File

@@ -16,7 +16,6 @@ use MediaWiki\User\ActorMigration;
*
* @ingroup Extensions
* @author Tim Laqua <t.laqua@gmail.com>
* @author Eunakria <eunakria@gmail.com>
*/
class ContributionScores extends IncludableSpecialPage {
const CONTRIBUTIONSCORES_MAXINCLUDELIMIT = 50;
@@ -29,19 +28,8 @@ class ContributionScores extends IncludableSpecialPage {
$parser->setFunctionHook( 'cscore', [ self::class, 'efContributionScoresRender' ] );
}
/**
* See extension.json; this function returns whether or not to use the
* "rough edit count" (user_editcount, stored in the user table). Since
* ignoring minor edits is mutually exclusive with this and takes priority,
* we check here.
*/
private static function shouldUseRoughEditCount(): bool {
global $wgContribScoreIgnoreMinorEdits, $wgContribScoreUseRoughEditCount;
return $wgContribScoreUseRoughEditCount && !$wgContribScoreIgnoreMinorEdits;
}
public static function efContributionScoresRender( $parser, $usertext, $metric = 'score' ) {
global $wgContribScoreDisableCache, $wgContribScoreIgnoreMinorEdits;
global $wgContribScoreDisableCache, $wgContribScoreUseRoughEditCount;
if ( $wgContribScoreDisableCache ) {
$parser->getOutput()->updateCacheExpiry( 0 );
@@ -53,81 +41,37 @@ class ContributionScores extends IncludableSpecialPage {
if ( $user instanceof User && $user->isRegistered() ) {
global $wgLang;
$revVar = self::shouldUseRoughEditCount() ? 'user_editcount' : 'COUNT(rev_id)';
$revVar = $wgContribScoreUseRoughEditCount ? 'user_editcount' : 'COUNT(rev_id)';
$revWhere = ActorMigration::newMigration()->getWhere( $dbr, 'rev_user', $user );
$revConds = (array)$revWhere['conds'];
if ( $wgContribScoreIgnoreMinorEdits ) {
$revConds['rev_minor_edit'] = 0;
}
# HACK: The original ContributionScores doesn't actually join on
# user despite querying on user... again not our fault, but
# definitely our problem. What a mess...
$revTables = array_merge([ 'revision' ], $revWhere['tables']);
$revJoins = $revWhere['joins'];
if ( self::shouldUseRoughEditCount() && !in_array( 'user', $revTables ) ) {
# Not familiar enough with MediaWiki to know if this is the
# right way to do things, but it seems like it. The
# ActorMigration class is deprecated and I think I'm supposed
# to just join on the bare names?
$revTables[] = 'actor';
$revTables[] = 'user';
$revJoins['actor'] = [ 'JOIN', 'rev_actor = actor_id' ];
$revJoins['user'] = [ 'JOIN', 'actor_user = user_id' ];
}
# HACK: This is not our fault (this bug exists in the original
# ContributionScores as well!) but in cases where the rough edit
# count underestimates the number of revisions, on MariaDB the
# unsigned subtraction can underflow, which will throw an error.
# So we have to clamp the subtraction rhs.
#
# Confused by buildLeast's API? Yeah, me too. It eventually
# dispatches to buildSuperlative, which looks like this:
# https://doc.wikimedia.org/mediawiki-core/master/php/SQLPlatform_8php_source.html#l00127
# The way to smuggle in an existing expression is by passing it as
# an array element with a non-integer key to the $fields argument,
# which is what we do here.
#
# Of course the use of buildLeast is necessitated by the fact that
# MariaDB and Postgres call it "LEAST", but SQLite calls it "MIN"
# (a more intuitive name, but the same as the aggregate function).
#
$clampedRhsSql = $dbr->buildLeast (
/* $fields: */ [ 'lhs' => $revVar, 'rhs' => 'COUNT(DISTINCT rev_page)' ],
/* $values: */ []
);
$wikiRankSql = "COUNT(DISTINCT rev_page) + SQRT($revVar-$clampedRhsSql) * 2";
if ( $metric == 'score' ) {
$row = $dbr->selectRow(
$revTables,
[ 'wiki_rank' => $wikiRankSql ],
$revConds,
[ 'revision' ] + $revWhere['tables'],
[ 'wiki_rank' => "COUNT(DISTINCT rev_page)+SQRT($revVar-COUNT(DISTINCT rev_page))*2" ],
$revWhere['conds'],
__METHOD__,
[],
$revJoins
$revWhere['joins']
);
$output = $wgLang->formatNum( round( $row->wiki_rank, 0 ) );
} elseif ( $metric == 'changes' ) {
$row = $dbr->selectRow(
$revTables,
[ 'revision' ] + $revWhere['tables'],
[ 'rev_count' => $revVar ],
$revConds,
$revWhere['conds'],
__METHOD__,
[],
$revJoins
$revWhere['joins']
);
$output = $wgLang->formatNum( $row->rev_count );
} elseif ( $metric == 'pages' ) {
$row = $dbr->selectRow(
$revTables,
[ 'revision' ] + $revWhere['tables'],
[ 'page_count' => 'COUNT(DISTINCT rev_page)' ],
$revConds,
$revWhere['conds'],
__METHOD__,
[],
$revJoins
$revWhere['joins']
);
$output = $wgLang->formatNum( $row->page_count );
} else {
@@ -148,7 +92,7 @@ class ContributionScores extends IncludableSpecialPage {
*/
public static function getContributionScoreData( $days, $limit ) {
global $wgContribScoreIgnoreBots, $wgContribScoreIgnoreBlockedUsers, $wgContribScoreIgnoreUsernames,
$wgContribScoreIgnoreMinorEdits;
$wgContribScoreUseRoughEditCount;
$loadBalancer = MediaWikiServices::getInstance()->getDBLoadBalancer();
$dbr = $loadBalancer->getConnection( DB_REPLICA );
@@ -161,10 +105,6 @@ class ContributionScores extends IncludableSpecialPage {
$sqlWhere = [];
if ( $wgContribScoreIgnoreMinorEdits ) {
$sqlWhere[] = 'rev_minor_edit = 0';
}
if ( $days > 0 ) {
$date = time() - ( 60 * 60 * 24 * $days );
$sqlWhere[] = 'rev_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( $date ) );
@@ -174,7 +114,7 @@ class ContributionScores extends IncludableSpecialPage {
'rev_user' => $revUser,
'page_count' => 'COUNT(DISTINCT rev_page)'
];
if ( self::shouldUseRoughEditCount() ) {
if ( $wgContribScoreUseRoughEditCount ) {
$revQuery['tables'][] = 'user';
$revQuery['joins']['user'] = [ 'LEFT JOIN', [ "$revUser != 0", "user_id = $revUser" ] ];
$sqlVars['rev_count'] = 'user_editcount';
@@ -184,21 +124,7 @@ class ContributionScores extends IncludableSpecialPage {
if ( $wgContribScoreIgnoreBlockedUsers ) {
$sqlWhere[] = "{$revUser} NOT IN " .
$dbr->buildSelectSubquery(
[
'block',
'block_target'
],
'bt_user',
'bt_user <> 0',
__METHOD__,
[],
[
'block_target' => [ 'JOIN', [
'bl_target=bt_id'
] ]
]
);
$dbr->buildSelectSubquery( 'ipblocks', 'ipb_user', 'ipb_user <> 0', __METHOD__ );
}
if ( $wgContribScoreIgnoreBots ) {
@@ -246,14 +172,6 @@ class ContributionScores extends IncludableSpecialPage {
$revQuery['joins']
);
# HACK: See previous comment on the use of buildLeast. Not our fault,
# but still our problem...
$clampedRhsSql = $dbr->buildLeast(
/* $fields: */ [ 'rev_count', 'page_count' ],
/* $values: */ []
);
$wikiRankSql = "page_count+SQRT(rev_count-$clampedRhsSql)*2";
$sqlMostPagesOrRevs = $dbr->unionQueries( [ $sqlMostPages, $sqlMostRevs ], false );
$res = $dbr->select(
[
@@ -266,7 +184,7 @@ class ContributionScores extends IncludableSpecialPage {
'user_real_name',
'page_count',
'rev_count',
'wiki_rank' => $wikiRankSql,
'wiki_rank' => 'page_count+SQRT(rev_count-page_count)*2',
],
[],
__METHOD__,
@@ -447,7 +365,7 @@ class ContributionScores extends IncludableSpecialPage {
$reportTitle = $this->msg( 'contributionscores-allrevisions' )->text();
}
$reportTitle .= ' ' . $this->msg( 'contributionscores-top' )->numParams( $limit )->text();
$title = Html::element( 'h4',
$title = Xml::element( 'h4',
[ 'class' => 'contributionscores-title' ],
$reportTitle
) . "\n";
@@ -484,7 +402,7 @@ class ContributionScores extends IncludableSpecialPage {
$reportTitle = $this->msg( 'contributionscores-allrevisions' )->text();
}
$reportTitle .= ' ' . $this->msg( 'contributionscores-top' )->numParams( $revs )->text();
$title = Html::element( 'h2',
$title = Xml::element( 'h2',
[ 'class' => 'contributionscores-title' ],
$reportTitle
) . "\n";