MediaWiki  REL1_23
ApiQueryAllUsers.php
Go to the documentation of this file.
00001 <?php
00032 class ApiQueryAllUsers extends ApiQueryBase {
00033     public function __construct( $query, $moduleName ) {
00034         parent::__construct( $query, $moduleName, 'au' );
00035     }
00036 
00043     private function getCanonicalUserName( $name ) {
00044         return str_replace( '_', ' ', $name );
00045     }
00046 
00047     public function execute() {
00048         $db = $this->getDB();
00049         $params = $this->extractRequestParams();
00050 
00051         $prop = $params['prop'];
00052         if ( !is_null( $prop ) ) {
00053             $prop = array_flip( $prop );
00054             $fld_blockinfo = isset( $prop['blockinfo'] );
00055             $fld_editcount = isset( $prop['editcount'] );
00056             $fld_groups = isset( $prop['groups'] );
00057             $fld_rights = isset( $prop['rights'] );
00058             $fld_registration = isset( $prop['registration'] );
00059             $fld_implicitgroups = isset( $prop['implicitgroups'] );
00060         } else {
00061             $fld_blockinfo = $fld_editcount = $fld_groups = $fld_registration =
00062                 $fld_rights = $fld_implicitgroups = false;
00063         }
00064 
00065         $limit = $params['limit'];
00066 
00067         $this->addTables( 'user' );
00068         $useIndex = true;
00069 
00070         $dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' );
00071         $from = is_null( $params['from'] ) ? null : $this->getCanonicalUserName( $params['from'] );
00072         $to = is_null( $params['to'] ) ? null : $this->getCanonicalUserName( $params['to'] );
00073 
00074         # MySQL doesn't seem to use 'equality propagation' here, so like the
00075         # ActiveUsers special page, we have to use rc_user_text for some cases.
00076         $userFieldToSort = $params['activeusers'] ? 'rc_user_text' : 'user_name';
00077 
00078         $this->addWhereRange( $userFieldToSort, $dir, $from, $to );
00079 
00080         if ( !is_null( $params['prefix'] ) ) {
00081             $this->addWhere( $userFieldToSort .
00082                 $db->buildLike( $this->getCanonicalUserName( $params['prefix'] ), $db->anyString() ) );
00083         }
00084 
00085         if ( !is_null( $params['rights'] ) && count( $params['rights'] ) ) {
00086             $groups = array();
00087             foreach ( $params['rights'] as $r ) {
00088                 $groups = array_merge( $groups, User::getGroupsWithPermission( $r ) );
00089             }
00090 
00091             // no group with the given right(s) exists, no need for a query
00092             if ( !count( $groups ) ) {
00093                 $this->getResult()->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), '' );
00094 
00095                 return;
00096             }
00097 
00098             $groups = array_unique( $groups );
00099 
00100             if ( is_null( $params['group'] ) ) {
00101                 $params['group'] = $groups;
00102             } else {
00103                 $params['group'] = array_unique( array_merge( $params['group'], $groups ) );
00104             }
00105         }
00106 
00107         if ( !is_null( $params['group'] ) && !is_null( $params['excludegroup'] ) ) {
00108             $this->dieUsage( 'group and excludegroup cannot be used together', 'group-excludegroup' );
00109         }
00110 
00111         if ( !is_null( $params['group'] ) && count( $params['group'] ) ) {
00112             $useIndex = false;
00113             // Filter only users that belong to a given group
00114             $this->addTables( 'user_groups', 'ug1' );
00115             $this->addJoinConds( array( 'ug1' => array( 'INNER JOIN', array( 'ug1.ug_user=user_id',
00116                 'ug1.ug_group' => $params['group'] ) ) ) );
00117         }
00118 
00119         if ( !is_null( $params['excludegroup'] ) && count( $params['excludegroup'] ) ) {
00120             $useIndex = false;
00121             // Filter only users don't belong to a given group
00122             $this->addTables( 'user_groups', 'ug1' );
00123 
00124             if ( count( $params['excludegroup'] ) == 1 ) {
00125                 $exclude = array( 'ug1.ug_group' => $params['excludegroup'][0] );
00126             } else {
00127                 $exclude = array( $db->makeList(
00128                     array( 'ug1.ug_group' => $params['excludegroup'] ),
00129                     LIST_OR
00130                 ) );
00131             }
00132             $this->addJoinConds( array( 'ug1' => array( 'LEFT OUTER JOIN',
00133                 array_merge( array( 'ug1.ug_user=user_id' ), $exclude )
00134             ) ) );
00135             $this->addWhere( 'ug1.ug_user IS NULL' );
00136         }
00137 
00138         if ( $params['witheditsonly'] ) {
00139             $this->addWhere( 'user_editcount > 0' );
00140         }
00141 
00142         $this->showHiddenUsersAddBlockInfo( $fld_blockinfo );
00143 
00144         if ( $fld_groups || $fld_rights ) {
00145             // Show the groups the given users belong to
00146             // request more than needed to avoid not getting all rows that belong to one user
00147             $groupCount = count( User::getAllGroups() );
00148             $sqlLimit = $limit + $groupCount + 1;
00149 
00150             $this->addTables( 'user_groups', 'ug2' );
00151             $this->addJoinConds( array( 'ug2' => array( 'LEFT JOIN', 'ug2.ug_user=user_id' ) ) );
00152             $this->addFields( 'ug2.ug_group ug_group2' );
00153         } else {
00154             $sqlLimit = $limit + 1;
00155         }
00156 
00157         if ( $params['activeusers'] ) {
00158             global $wgActiveUserDays;
00159             $this->addTables( 'recentchanges' );
00160 
00161             $this->addJoinConds( array( 'recentchanges' => array(
00162                 'INNER JOIN', 'rc_user_text=user_name'
00163             ) ) );
00164 
00165             $this->addFields( array( 'recentedits' => 'COUNT(*)' ) );
00166 
00167             $this->addWhere( 'rc_log_type IS NULL OR rc_log_type != ' . $db->addQuotes( 'newusers' ) );
00168             $timestamp = $db->timestamp( wfTimestamp( TS_UNIX ) - $wgActiveUserDays * 24 * 3600 );
00169             $this->addWhere( 'rc_timestamp >= ' . $db->addQuotes( $timestamp ) );
00170 
00171             $this->addOption( 'GROUP BY', $userFieldToSort );
00172         }
00173 
00174         $this->addOption( 'LIMIT', $sqlLimit );
00175 
00176         $this->addFields( array(
00177             'user_name',
00178             'user_id'
00179         ) );
00180         $this->addFieldsIf( 'user_editcount', $fld_editcount );
00181         $this->addFieldsIf( 'user_registration', $fld_registration );
00182 
00183         if ( $useIndex ) {
00184             $this->addOption( 'USE INDEX', array( 'user' => 'user_name' ) );
00185         }
00186 
00187         $res = $this->select( __METHOD__ );
00188 
00189         $count = 0;
00190         $lastUserData = false;
00191         $lastUser = false;
00192         $result = $this->getResult();
00193 
00194         // This loop keeps track of the last entry. For each new row, if the
00195         // new row is for different user then the last, the last entry is added
00196         // to results. Otherwise, the group of the new row is appended to the
00197         // last entry. The setContinue... is more complex because of this, and
00198         // takes into account the higher sql limit to make sure all rows that
00199         // belong to the same user are received.
00200 
00201         foreach ( $res as $row ) {
00202             $count++;
00203 
00204             if ( $lastUser !== $row->user_name ) {
00205                 // Save the last pass's user data
00206                 if ( is_array( $lastUserData ) ) {
00207                     $fit = $result->addValue( array( 'query', $this->getModuleName() ),
00208                         null, $lastUserData );
00209 
00210                     $lastUserData = null;
00211 
00212                     if ( !$fit ) {
00213                         $this->setContinueEnumParameter( 'from', $lastUserData['name'] );
00214                         break;
00215                     }
00216                 }
00217 
00218                 if ( $count > $limit ) {
00219                     // We've reached the one extra which shows that there are
00220                     // additional pages to be had. Stop here...
00221                     $this->setContinueEnumParameter( 'from', $row->user_name );
00222                     break;
00223                 }
00224 
00225                 // Record new user's data
00226                 $lastUser = $row->user_name;
00227                 $lastUserData = array(
00228                     'userid' => $row->user_id,
00229                     'name' => $lastUser,
00230                 );
00231                 if ( $fld_blockinfo && !is_null( $row->ipb_by_text ) ) {
00232                     $lastUserData['blockid'] = $row->ipb_id;
00233                     $lastUserData['blockedby'] = $row->ipb_by_text;
00234                     $lastUserData['blockedbyid'] = $row->ipb_by;
00235                     $lastUserData['blockreason'] = $row->ipb_reason;
00236                     $lastUserData['blockexpiry'] = $row->ipb_expiry;
00237                 }
00238                 if ( $row->ipb_deleted ) {
00239                     $lastUserData['hidden'] = '';
00240                 }
00241                 if ( $fld_editcount ) {
00242                     $lastUserData['editcount'] = intval( $row->user_editcount );
00243                 }
00244                 if ( $params['activeusers'] ) {
00245                     $lastUserData['recenteditcount'] = intval( $row->recentedits );
00246                 }
00247                 if ( $fld_registration ) {
00248                     $lastUserData['registration'] = $row->user_registration ?
00249                         wfTimestamp( TS_ISO_8601, $row->user_registration ) : '';
00250                 }
00251             }
00252 
00253             if ( $sqlLimit == $count ) {
00254                 // @todo BUG!  database contains group name that User::getAllGroups() does not return
00255                 // Should handle this more gracefully
00256                 ApiBase::dieDebug(
00257                     __METHOD__,
00258                     'MediaWiki configuration error: The database contains more ' .
00259                         'user groups than known to User::getAllGroups() function'
00260                 );
00261             }
00262 
00263             $lastUserObj = User::newFromId( $row->user_id );
00264 
00265             // Add user's group info
00266             if ( $fld_groups ) {
00267                 if ( !isset( $lastUserData['groups'] ) ) {
00268                     if ( $lastUserObj ) {
00269                         $lastUserData['groups'] = $lastUserObj->getAutomaticGroups();
00270                     } else {
00271                         // This should not normally happen
00272                         $lastUserData['groups'] = array();
00273                     }
00274                 }
00275 
00276                 if ( !is_null( $row->ug_group2 ) ) {
00277                     $lastUserData['groups'][] = $row->ug_group2;
00278                 }
00279 
00280                 $result->setIndexedTagName( $lastUserData['groups'], 'g' );
00281             }
00282 
00283             if ( $fld_implicitgroups && !isset( $lastUserData['implicitgroups'] ) && $lastUserObj ) {
00284                 $lastUserData['implicitgroups'] = $lastUserObj->getAutomaticGroups();
00285                 $result->setIndexedTagName( $lastUserData['implicitgroups'], 'g' );
00286             }
00287             if ( $fld_rights ) {
00288                 if ( !isset( $lastUserData['rights'] ) ) {
00289                     if ( $lastUserObj ) {
00290                         $lastUserData['rights'] = User::getGroupPermissions( $lastUserObj->getAutomaticGroups() );
00291                     } else {
00292                         // This should not normally happen
00293                         $lastUserData['rights'] = array();
00294                     }
00295                 }
00296 
00297                 if ( !is_null( $row->ug_group2 ) ) {
00298                     $lastUserData['rights'] = array_unique( array_merge( $lastUserData['rights'],
00299                         User::getGroupPermissions( array( $row->ug_group2 ) ) ) );
00300                 }
00301 
00302                 $result->setIndexedTagName( $lastUserData['rights'], 'r' );
00303             }
00304         }
00305 
00306         if ( is_array( $lastUserData ) ) {
00307             $fit = $result->addValue( array( 'query', $this->getModuleName() ),
00308                 null, $lastUserData );
00309             if ( !$fit ) {
00310                 $this->setContinueEnumParameter( 'from', $lastUserData['name'] );
00311             }
00312         }
00313 
00314         $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'u' );
00315     }
00316 
00317     public function getCacheMode( $params ) {
00318         return 'anon-public-user-private';
00319     }
00320 
00321     public function getAllowedParams() {
00322         $userGroups = User::getAllGroups();
00323 
00324         return array(
00325             'from' => null,
00326             'to' => null,
00327             'prefix' => null,
00328             'dir' => array(
00329                 ApiBase::PARAM_DFLT => 'ascending',
00330                 ApiBase::PARAM_TYPE => array(
00331                     'ascending',
00332                     'descending'
00333                 ),
00334             ),
00335             'group' => array(
00336                 ApiBase::PARAM_TYPE => $userGroups,
00337                 ApiBase::PARAM_ISMULTI => true,
00338             ),
00339             'excludegroup' => array(
00340                 ApiBase::PARAM_TYPE => $userGroups,
00341                 ApiBase::PARAM_ISMULTI => true,
00342             ),
00343             'rights' => array(
00344                 ApiBase::PARAM_TYPE => User::getAllRights(),
00345                 ApiBase::PARAM_ISMULTI => true,
00346             ),
00347             'prop' => array(
00348                 ApiBase::PARAM_ISMULTI => true,
00349                 ApiBase::PARAM_TYPE => array(
00350                     'blockinfo',
00351                     'groups',
00352                     'implicitgroups',
00353                     'rights',
00354                     'editcount',
00355                     'registration'
00356                 )
00357             ),
00358             'limit' => array(
00359                 ApiBase::PARAM_DFLT => 10,
00360                 ApiBase::PARAM_TYPE => 'limit',
00361                 ApiBase::PARAM_MIN => 1,
00362                 ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
00363                 ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2
00364             ),
00365             'witheditsonly' => false,
00366             'activeusers' => false,
00367         );
00368     }
00369 
00370     public function getParamDescription() {
00371         global $wgActiveUserDays;
00372 
00373         return array(
00374             'from' => 'The user name to start enumerating from',
00375             'to' => 'The user name to stop enumerating at',
00376             'prefix' => 'Search for all users that begin with this value',
00377             'dir' => 'Direction to sort in',
00378             'group' => 'Limit users to given group name(s)',
00379             'excludegroup' => 'Exclude users in given group name(s)',
00380             'rights' => 'Limit users to given right(s) (does not include rights ' .
00381                 'granted by implicit or auto-promoted groups like *, user, or autoconfirmed)',
00382             'prop' => array(
00383                 'What pieces of information to include.',
00384                 ' blockinfo      - Adds the information about a current block on the user',
00385                 ' groups         - Lists groups that the user is in. This uses ' .
00386                     'more server resources and may return fewer results than the limit',
00387                 ' implicitgroups - Lists all the groups the user is automatically in',
00388                 ' rights         - Lists rights that the user has',
00389                 ' editcount      - Adds the edit count of the user',
00390                 ' registration   - Adds the timestamp of when the user registered if available (may be blank)',
00391             ),
00392             'limit' => 'How many total user names to return',
00393             'witheditsonly' => 'Only list users who have made edits',
00394             'activeusers' => "Only list users active in the last {$wgActiveUserDays} days(s)"
00395         );
00396     }
00397 
00398     public function getResultProperties() {
00399         return array(
00400             '' => array(
00401                 'userid' => 'integer',
00402                 'name' => 'string',
00403                 'recenteditcount' => array(
00404                     ApiBase::PROP_TYPE => 'integer',
00405                     ApiBase::PROP_NULLABLE => true
00406                 )
00407             ),
00408             'blockinfo' => array(
00409                 'blockid' => array(
00410                     ApiBase::PROP_TYPE => 'integer',
00411                     ApiBase::PROP_NULLABLE => true
00412                 ),
00413                 'blockedby' => array(
00414                     ApiBase::PROP_TYPE => 'string',
00415                     ApiBase::PROP_NULLABLE => true
00416                 ),
00417                 'blockedbyid' => array(
00418                     ApiBase::PROP_TYPE => 'integer',
00419                     ApiBase::PROP_NULLABLE => true
00420                 ),
00421                 'blockedreason' => array(
00422                     ApiBase::PROP_TYPE => 'string',
00423                     ApiBase::PROP_NULLABLE => true
00424                 ),
00425                 'blockedexpiry' => array(
00426                     ApiBase::PROP_TYPE => 'string',
00427                     ApiBase::PROP_NULLABLE => true
00428                 ),
00429                 'hidden' => 'boolean'
00430             ),
00431             'editcount' => array(
00432                 'editcount' => 'integer'
00433             ),
00434             'registration' => array(
00435                 'registration' => 'string'
00436             )
00437         );
00438     }
00439 
00440     public function getDescription() {
00441         return 'Enumerate all registered users.';
00442     }
00443 
00444     public function getPossibleErrors() {
00445         return array_merge( parent::getPossibleErrors(), array(
00446             array(
00447                 'code' => 'group-excludegroup',
00448                 'info' => 'group and excludegroup cannot be used together'
00449             ),
00450         ) );
00451     }
00452 
00453     public function getExamples() {
00454         return array(
00455             'api.php?action=query&list=allusers&aufrom=Y',
00456         );
00457     }
00458 
00459     public function getHelpUrls() {
00460         return 'https://www.mediawiki.org/wiki/API:Allusers';
00461     }
00462 }