*/ class ContributionScores extends IncludableSpecialPage { const CONTRIBUTIONSCORES_MAXINCLUDELIMIT = 50; public function __construct() { parent::__construct( 'ContributionScores' ); } public static function onParserFirstCallInit( Parser $parser ) { $parser->setFunctionHook( 'cscore', [ self::class, 'efContributionScoresRender' ] ); } public static function efContributionScoresRender( $parser, $usertext, $metric = 'score' ) { global $wgContribScoreDisableCache; if ( $wgContribScoreDisableCache ) { $parser->getOutput()->updateCacheExpiry( 0 ); } $user = User::newFromName( $usertext ); $dbr = wfGetDB( DB_REPLICA ); if ( $user instanceof User && $user->isRegistered() ) { global $wgLang; $revWhere = ActorMigration::newMigration()->getWhere( $dbr, 'rev_user', $user ); if ( $metric == 'score' ) { $res = $dbr->select( [ 'revision' ] + $revWhere['tables'], 'COUNT(DISTINCT rev_page)+SQRT(COUNT(rev_id)-COUNT(DISTINCT rev_page))*2 AS wiki_rank', $revWhere['conds'], __METHOD__, [], $revWhere['joins'] ); $row = $dbr->fetchObject( $res ); $output = $wgLang->formatNum( round( $row->wiki_rank, 0 ) ); } elseif ( $metric == 'changes' ) { $res = $dbr->select( [ 'revision' ] + $revWhere['tables'], 'COUNT(rev_id) AS rev_count', $revWhere['conds'], __METHOD__, [], $revWhere['joins'] ); $row = $dbr->fetchObject( $res ); $output = $wgLang->formatNum( $row->rev_count ); } elseif ( $metric == 'pages' ) { $res = $dbr->select( [ 'revision' ] + $revWhere['tables'], 'COUNT(DISTINCT rev_page) AS page_count', $revWhere['conds'], __METHOD__, [], $revWhere['joins'] ); $row = $dbr->fetchObject( $res ); $output = $wgLang->formatNum( $row->page_count ); } else { $output = wfMessage( 'contributionscores-invalidmetric' )->text(); } } else { $output = wfMessage( 'contributionscores-invalidusername' )->text(); } return $parser->insertStripItem( $output, $parser->mStripState ); } /// Generates a "Contribution Scores" table for a given LIMIT and date range /** * Function generates Contribution Scores tables in HTML format (not wikiText) * * @param int $days Days in the past to run report for * @param int $limit Maximum number of users to return (default 50) * @param string|null $title The title of the table * @param array $options array of options (default none; nosort/notools) * @return string Html Table representing the requested Contribution Scores. */ function genContributionScoreTable( $days, $limit, $title = null, $options = 'none' ) { global $wgContribScoreIgnoreBots, $wgContribScoreIgnoreBlockedUsers, $wgContribScoresUseRealName; $opts = explode( ',', strtolower( $options ) ); $dbr = wfGetDB( DB_REPLICA ); $revQuery = ActorMigration::newMigration()->getJoin( 'rev_user' ); $revQuery['tables'] = array_merge( [ 'revision' ], $revQuery['tables'] ); $revUser = $revQuery['fields']['rev_user']; $sqlWhere = []; if ( $days > 0 ) { $date = time() - ( 60 * 60 * 24 * $days ); $sqlWhere[] = 'rev_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( $date ) ); } if ( $wgContribScoreIgnoreBlockedUsers ) { $sqlWhere[] = "{$revUser} NOT IN " . $dbr->buildSelectSubquery( 'ipblocks', 'ipb_user', 'ipb_user <> 0', __METHOD__ ); } if ( $wgContribScoreIgnoreBots ) { $sqlWhere[] = "{$revUser} NOT IN " . $dbr->buildSelectSubquery( 'user_groups', 'ug_user', [ 'ug_group' => 'bot', 'ug_expiry IS NULL OR ug_expiry >= ' . $dbr->addQuotes( $dbr->timestamp() ) ], __METHOD__ ); } if ( $dbr->unionSupportsOrderAndLimit() ) { $order = [ 'GROUP BY' => 'rev_user', 'ORDER BY' => 'page_count DESC', 'LIMIT' => $limit ]; } else { $order = [ 'GROUP BY' => 'rev_user' ]; } $sqlMostPages = $dbr->selectSQLText( $revQuery['tables'], [ 'rev_user' => $revUser, 'page_count' => 'COUNT(DISTINCT rev_page)', 'rev_count' => 'COUNT(rev_id)', ], $sqlWhere, __METHOD__, $order, $revQuery['joins'] ); if ( $dbr->unionSupportsOrderAndLimit() ) { $order['ORDER BY'] = 'rev_count DESC'; } $sqlMostRevs = $dbr->selectSQLText( $revQuery['tables'], [ 'rev_user' => $revUser, 'page_count' => 'COUNT(DISTINCT rev_page)', 'rev_count' => 'COUNT(rev_id)', ], $sqlWhere, __METHOD__, $order, $revQuery['joins'] ); $sqlMostPagesOrRevs = $dbr->unionQueries( [ $sqlMostPages, $sqlMostRevs ], false ); $res = $dbr->select( [ 'u' => 'user', 's' => new Wikimedia\Rdbms\Subquery( $sqlMostPagesOrRevs ), ], [ 'user_id', 'user_name', 'user_real_name', 'page_count', 'rev_count', 'wiki_rank' => 'page_count+SQRT(rev_count-page_count)*2', ], [], __METHOD__, [ 'ORDER BY' => 'wiki_rank DESC', 'GROUP BY' => 'user_name', 'LIMIT' => $limit, ], [ 's' => [ 'JOIN', 'user_id=rev_user' ] ] ); $sortable = in_array( 'nosort', $opts ) ? '' : ' sortable'; $output = "
" . $lang->formatNum( $user_rank ) . "\n | " . $lang->formatNum( round( $row->wiki_rank, 0 ) ) . "\n | " . $lang->formatNum( $row->page_count ) . "\n | " . $lang->formatNum( $row->rev_count ) . "\n | " . $userLink; # Option to not display user tools if ( !in_array( 'notools', $opts ) ) { $output .= Linker::userToolLinks( $row->user_id, $row->user_name ); } $output .= Html::closeElement( 'td' ) . "\n"; if ( $altrow == '' && empty( $sortable ) ) { $altrow = 'odd '; } else { $altrow = ''; } $user_rank++; } $output .= Html::closeElement( 'tr' ); $output .= Html::closeElement( 'table' ); $dbr->freeResult( $res ); if ( !empty( $title ) ) { $output = Html::rawElement( 'table', [ 'style' => 'border-spacing: 0; padding: 0', 'class' => 'contributionscores-wrapper', 'lang' => htmlspecialchars( $lang->getCode() ), 'dir' => $lang->getDir() ], "\n" . " |
{$title} | \n" . "||||
{$output} | \n" . "