diff options
Diffstat (limited to 'includes/api/ApiQueryAllUsers.php')
-rw-r--r-- | includes/api/ApiQueryAllUsers.php | 175 |
1 files changed, 83 insertions, 92 deletions
diff --git a/includes/api/ApiQueryAllUsers.php b/includes/api/ApiQueryAllUsers.php index 1948a51a..affddda7 100644 --- a/includes/api/ApiQueryAllUsers.php +++ b/includes/api/ApiQueryAllUsers.php @@ -30,7 +30,7 @@ * @ingroup API */ class ApiQueryAllUsers extends ApiQueryBase { - public function __construct( $query, $moduleName ) { + public function __construct( ApiQuery $query, $moduleName ) { parent::__construct( $query, $moduleName, 'au' ); } @@ -38,15 +38,22 @@ class ApiQueryAllUsers extends ApiQueryBase { * This function converts the user name to a canonical form * which is stored in the database. * @param string $name - * @return String + * @return string */ private function getCanonicalUserName( $name ) { return str_replace( '_', ' ', $name ); } public function execute() { - $db = $this->getDB(); $params = $this->extractRequestParams(); + $activeUserDays = $this->getConfig()->get( 'ActiveUserDays' ); + + if ( $params['activeusers'] ) { + // Update active user cache + SpecialActiveUsers::mergeActiveUsers( 600, $activeUserDays ); + } + + $db = $this->getDB(); $prop = $params['prop']; if ( !is_null( $prop ) ) { @@ -58,7 +65,8 @@ class ApiQueryAllUsers extends ApiQueryBase { $fld_registration = isset( $prop['registration'] ); $fld_implicitgroups = isset( $prop['implicitgroups'] ); } else { - $fld_blockinfo = $fld_editcount = $fld_groups = $fld_registration = $fld_rights = $fld_implicitgroups = false; + $fld_blockinfo = $fld_editcount = $fld_groups = $fld_registration = + $fld_rights = $fld_implicitgroups = false; } $limit = $params['limit']; @@ -70,9 +78,9 @@ class ApiQueryAllUsers extends ApiQueryBase { $from = is_null( $params['from'] ) ? null : $this->getCanonicalUserName( $params['from'] ); $to = is_null( $params['to'] ) ? null : $this->getCanonicalUserName( $params['to'] ); - # MySQL doesn't seem to use 'equality propagation' here, so like the - # ActiveUsers special page, we have to use rc_user_text for some cases. - $userFieldToSort = $params['activeusers'] ? 'rc_user_text' : 'user_name'; + # MySQL can't figure out that 'user_name' and 'qcc_title' are the same + # despite the JOIN condition, so manually sort on the correct one. + $userFieldToSort = $params['activeusers'] ? 'qcc_title' : 'user_name'; $this->addWhereRange( $userFieldToSort, $dir, $from, $to ); @@ -90,6 +98,7 @@ class ApiQueryAllUsers extends ApiQueryBase { // no group with the given right(s) exists, no need for a query if ( !count( $groups ) ) { $this->getResult()->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), '' ); + return; } @@ -111,7 +120,7 @@ class ApiQueryAllUsers extends ApiQueryBase { // Filter only users that belong to a given group $this->addTables( 'user_groups', 'ug1' ); $this->addJoinConds( array( 'ug1' => array( 'INNER JOIN', array( 'ug1.ug_user=user_id', - 'ug1.ug_group' => $params['group'] ) ) ) ); + 'ug1.ug_group' => $params['group'] ) ) ) ); } if ( !is_null( $params['excludegroup'] ) && count( $params['excludegroup'] ) ) { @@ -122,12 +131,14 @@ class ApiQueryAllUsers extends ApiQueryBase { if ( count( $params['excludegroup'] ) == 1 ) { $exclude = array( 'ug1.ug_group' => $params['excludegroup'][0] ); } else { - $exclude = array( $db->makeList( array( 'ug1.ug_group' => $params['excludegroup'] ), LIST_OR ) ); + $exclude = array( $db->makeList( + array( 'ug1.ug_group' => $params['excludegroup'] ), + LIST_OR + ) ); } $this->addJoinConds( array( 'ug1' => array( 'LEFT OUTER JOIN', array_merge( array( 'ug1.ug_user=user_id' ), $exclude ) - ) - ) ); + ) ) ); $this->addWhere( 'ug1.ug_user IS NULL' ); } @@ -145,26 +156,38 @@ class ApiQueryAllUsers extends ApiQueryBase { $this->addTables( 'user_groups', 'ug2' ); $this->addJoinConds( array( 'ug2' => array( 'LEFT JOIN', 'ug2.ug_user=user_id' ) ) ); - $this->addFields( 'ug2.ug_group ug_group2' ); + $this->addFields( array( 'ug_group2' => 'ug2.ug_group' ) ); } else { $sqlLimit = $limit + 1; } if ( $params['activeusers'] ) { - global $wgActiveUserDays; - $this->addTables( 'recentchanges' ); - - $this->addJoinConds( array( 'recentchanges' => array( - 'INNER JOIN', 'rc_user_text=user_name' + $activeUserSeconds = $activeUserDays * 86400; + + // Filter query to only include users in the active users cache + $this->addTables( 'querycachetwo' ); + $this->addJoinConds( array( 'querycachetwo' => array( + 'INNER JOIN', array( + 'qcc_type' => 'activeusers', + 'qcc_namespace' => NS_USER, + 'qcc_title=user_name', + ), ) ) ); - $this->addFields( array( 'recentedits' => 'COUNT(*)' ) ); - - $this->addWhere( 'rc_log_type IS NULL OR rc_log_type != ' . $db->addQuotes( 'newusers' ) ); - $timestamp = $db->timestamp( wfTimestamp( TS_UNIX ) - $wgActiveUserDays * 24 * 3600 ); - $this->addWhere( 'rc_timestamp >= ' . $db->addQuotes( $timestamp ) ); - - $this->addOption( 'GROUP BY', $userFieldToSort ); + // Actually count the actions using a subquery (bug 64505 and bug 64507) + $timestamp = $db->timestamp( wfTimestamp( TS_UNIX ) - $activeUserSeconds ); + $this->addFields( array( + 'recentactions' => '(' . $db->selectSQLText( + 'recentchanges', + 'COUNT(*)', + array( + 'rc_user_text = user_name', + 'rc_type != ' . $db->addQuotes( RC_EXTERNAL ), // no wikidata + 'rc_log_type IS NULL OR rc_log_type != ' . $db->addQuotes( 'newusers' ), + 'rc_timestamp >= ' . $db->addQuotes( $timestamp ), + ) + ) . ')' + ) ); } $this->addOption( 'LIMIT', $sqlLimit ); @@ -187,12 +210,12 @@ class ApiQueryAllUsers extends ApiQueryBase { $lastUser = false; $result = $this->getResult(); - // - // This loop keeps track of the last entry. - // For each new row, if the new row is for different user then the last, the last entry is added to results. - // Otherwise, the group of the new row is appended to the last entry. - // The setContinue... is more complex because of this, and takes into account the higher sql limit - // to make sure all rows that belong to the same user are received. + // This loop keeps track of the last entry. For each new row, if the + // new row is for different user then the last, the last entry is added + // to results. Otherwise, the group of the new row is appended to the + // last entry. The setContinue... is more complex because of this, and + // takes into account the higher sql limit to make sure all rows that + // belong to the same user are received. foreach ( $res as $row ) { $count++; @@ -200,8 +223,13 @@ class ApiQueryAllUsers extends ApiQueryBase { if ( $lastUser !== $row->user_name ) { // Save the last pass's user data if ( is_array( $lastUserData ) ) { - $fit = $result->addValue( array( 'query', $this->getModuleName() ), + if ( $params['activeusers'] && $lastUserData['recentactions'] === 0 ) { + // activeusers cache was out of date + $fit = true; + } else { + $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $lastUserData ); + } $lastUserData = null; @@ -212,7 +240,8 @@ class ApiQueryAllUsers extends ApiQueryBase { } if ( $count > $limit ) { - // We've reached the one extra which shows that there are additional pages to be had. Stop here... + // We've reached the one extra which shows that there are + // additional pages to be had. Stop here... $this->setContinueEnumParameter( 'from', $row->user_name ); break; } @@ -227,6 +256,7 @@ class ApiQueryAllUsers extends ApiQueryBase { $lastUserData['blockid'] = $row->ipb_id; $lastUserData['blockedby'] = $row->ipb_by_text; $lastUserData['blockedbyid'] = $row->ipb_by; + $lastUserData['blockedtimestamp'] = wfTimestamp( TS_ISO_8601, $row->ipb_timestamp ); $lastUserData['blockreason'] = $row->ipb_reason; $lastUserData['blockexpiry'] = $row->ipb_expiry; } @@ -237,7 +267,9 @@ class ApiQueryAllUsers extends ApiQueryBase { $lastUserData['editcount'] = intval( $row->user_editcount ); } if ( $params['activeusers'] ) { - $lastUserData['recenteditcount'] = intval( $row->recentedits ); + $lastUserData['recentactions'] = intval( $row->recentactions ); + // @todo 'recenteditcount' is set for BC, remove in 1.25 + $lastUserData['recenteditcount'] = $lastUserData['recentactions']; } if ( $fld_registration ) { $lastUserData['registration'] = $row->user_registration ? @@ -246,10 +278,13 @@ class ApiQueryAllUsers extends ApiQueryBase { } if ( $sqlLimit == $count ) { - // BUG! database contains group name that User::getAllGroups() does not return - // TODO: should handle this more gracefully - ApiBase::dieDebug( __METHOD__, - 'MediaWiki configuration error: the database contains more user groups than known to User::getAllGroups() function' ); + // @todo BUG! database contains group name that User::getAllGroups() does not return + // Should handle this more gracefully + ApiBase::dieDebug( + __METHOD__, + 'MediaWiki configuration error: The database contains more ' . + 'user groups than known to User::getAllGroups() function' + ); } $lastUserObj = User::newFromId( $row->user_id ); @@ -295,7 +330,9 @@ class ApiQueryAllUsers extends ApiQueryBase { } } - if ( is_array( $lastUserData ) ) { + if ( is_array( $lastUserData ) && + !( $params['activeusers'] && $lastUserData['recentactions'] === 0 ) + ) { $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $lastUserData ); if ( !$fit ) { @@ -312,6 +349,7 @@ class ApiQueryAllUsers extends ApiQueryBase { public function getAllowedParams() { $userGroups = User::getAllGroups(); + return array( 'from' => null, 'to' => null, @@ -359,7 +397,6 @@ class ApiQueryAllUsers extends ApiQueryBase { } public function getParamDescription() { - global $wgActiveUserDays; return array( 'from' => 'The user name to start enumerating from', 'to' => 'The user name to stop enumerating at', @@ -367,72 +404,26 @@ class ApiQueryAllUsers extends ApiQueryBase { 'dir' => 'Direction to sort in', 'group' => 'Limit users to given group name(s)', 'excludegroup' => 'Exclude users in given group name(s)', - 'rights' => 'Limit users to given right(s) (does not include rights granted by implicit or auto-promoted groups like *, user, or autoconfirmed)', + 'rights' => 'Limit users to given right(s) (does not include rights ' . + 'granted by implicit or auto-promoted groups like *, user, or autoconfirmed)', 'prop' => array( 'What pieces of information to include.', ' blockinfo - Adds the information about a current block on the user', - ' groups - Lists groups that the user is in. This uses more server resources and may return fewer results than the limit', + ' groups - Lists groups that the user is in. This uses ' . + 'more server resources and may return fewer results than the limit', ' implicitgroups - Lists all the groups the user is automatically in', ' rights - Lists rights that the user has', ' editcount - Adds the edit count of the user', ' registration - Adds the timestamp of when the user registered if available (may be blank)', - ), + ), 'limit' => 'How many total user names to return', 'witheditsonly' => 'Only list users who have made edits', - 'activeusers' => "Only list users active in the last {$wgActiveUserDays} days(s)" - ); - } - - public function getResultProperties() { - return array( - '' => array( - 'userid' => 'integer', - 'name' => 'string', - 'recenteditcount' => array( - ApiBase::PROP_TYPE => 'integer', - ApiBase::PROP_NULLABLE => true - ) - ), - 'blockinfo' => array( - 'blockid' => array( - ApiBase::PROP_TYPE => 'integer', - ApiBase::PROP_NULLABLE => true - ), - 'blockedby' => array( - ApiBase::PROP_TYPE => 'string', - ApiBase::PROP_NULLABLE => true - ), - 'blockedbyid' => array( - ApiBase::PROP_TYPE => 'integer', - ApiBase::PROP_NULLABLE => true - ), - 'blockedreason' => array( - ApiBase::PROP_TYPE => 'string', - ApiBase::PROP_NULLABLE => true - ), - 'blockedexpiry' => array( - ApiBase::PROP_TYPE => 'string', - ApiBase::PROP_NULLABLE => true - ), - 'hidden' => 'boolean' - ), - 'editcount' => array( - 'editcount' => 'integer' - ), - 'registration' => array( - 'registration' => 'string' - ) + 'activeusers' => "Only list users active in the last {$this->getConfig()->get( 'ActiveUserDays' )} days(s)" ); } public function getDescription() { - return 'Enumerate all registered users'; - } - - public function getPossibleErrors() { - return array_merge( parent::getPossibleErrors(), array( - array( 'code' => 'group-excludegroup', 'info' => 'group and excludegroup cannot be used together' ), - ) ); + return 'Enumerate all registered users.'; } public function getExamples() { |