[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * 4 * 5 * Created on July 7, 2007 6 * 7 * Copyright © 2007 Yuri Astrakhan "<Firstname><Lastname>@gmail.com" 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License along 20 * with this program; if not, write to the Free Software Foundation, Inc., 21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 * http://www.gnu.org/copyleft/gpl.html 23 * 24 * @file 25 */ 26 27 /** 28 * Query module to enumerate all registered users. 29 * 30 * @ingroup API 31 */ 32 class ApiQueryAllUsers extends ApiQueryBase { 33 public function __construct( ApiQuery $query, $moduleName ) { 34 parent::__construct( $query, $moduleName, 'au' ); 35 } 36 37 /** 38 * This function converts the user name to a canonical form 39 * which is stored in the database. 40 * @param string $name 41 * @return string 42 */ 43 private function getCanonicalUserName( $name ) { 44 return str_replace( '_', ' ', $name ); 45 } 46 47 public function execute() { 48 $params = $this->extractRequestParams(); 49 $activeUserDays = $this->getConfig()->get( 'ActiveUserDays' ); 50 51 if ( $params['activeusers'] ) { 52 // Update active user cache 53 SpecialActiveUsers::mergeActiveUsers( 600, $activeUserDays ); 54 } 55 56 $db = $this->getDB(); 57 58 $prop = $params['prop']; 59 if ( !is_null( $prop ) ) { 60 $prop = array_flip( $prop ); 61 $fld_blockinfo = isset( $prop['blockinfo'] ); 62 $fld_editcount = isset( $prop['editcount'] ); 63 $fld_groups = isset( $prop['groups'] ); 64 $fld_rights = isset( $prop['rights'] ); 65 $fld_registration = isset( $prop['registration'] ); 66 $fld_implicitgroups = isset( $prop['implicitgroups'] ); 67 } else { 68 $fld_blockinfo = $fld_editcount = $fld_groups = $fld_registration = 69 $fld_rights = $fld_implicitgroups = false; 70 } 71 72 $limit = $params['limit']; 73 74 $this->addTables( 'user' ); 75 $useIndex = true; 76 77 $dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' ); 78 $from = is_null( $params['from'] ) ? null : $this->getCanonicalUserName( $params['from'] ); 79 $to = is_null( $params['to'] ) ? null : $this->getCanonicalUserName( $params['to'] ); 80 81 # MySQL can't figure out that 'user_name' and 'qcc_title' are the same 82 # despite the JOIN condition, so manually sort on the correct one. 83 $userFieldToSort = $params['activeusers'] ? 'qcc_title' : 'user_name'; 84 85 $this->addWhereRange( $userFieldToSort, $dir, $from, $to ); 86 87 if ( !is_null( $params['prefix'] ) ) { 88 $this->addWhere( $userFieldToSort . 89 $db->buildLike( $this->getCanonicalUserName( $params['prefix'] ), $db->anyString() ) ); 90 } 91 92 if ( !is_null( $params['rights'] ) && count( $params['rights'] ) ) { 93 $groups = array(); 94 foreach ( $params['rights'] as $r ) { 95 $groups = array_merge( $groups, User::getGroupsWithPermission( $r ) ); 96 } 97 98 // no group with the given right(s) exists, no need for a query 99 if ( !count( $groups ) ) { 100 $this->getResult()->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), '' ); 101 102 return; 103 } 104 105 $groups = array_unique( $groups ); 106 107 if ( is_null( $params['group'] ) ) { 108 $params['group'] = $groups; 109 } else { 110 $params['group'] = array_unique( array_merge( $params['group'], $groups ) ); 111 } 112 } 113 114 if ( !is_null( $params['group'] ) && !is_null( $params['excludegroup'] ) ) { 115 $this->dieUsage( 'group and excludegroup cannot be used together', 'group-excludegroup' ); 116 } 117 118 if ( !is_null( $params['group'] ) && count( $params['group'] ) ) { 119 $useIndex = false; 120 // Filter only users that belong to a given group 121 $this->addTables( 'user_groups', 'ug1' ); 122 $this->addJoinConds( array( 'ug1' => array( 'INNER JOIN', array( 'ug1.ug_user=user_id', 123 'ug1.ug_group' => $params['group'] ) ) ) ); 124 } 125 126 if ( !is_null( $params['excludegroup'] ) && count( $params['excludegroup'] ) ) { 127 $useIndex = false; 128 // Filter only users don't belong to a given group 129 $this->addTables( 'user_groups', 'ug1' ); 130 131 if ( count( $params['excludegroup'] ) == 1 ) { 132 $exclude = array( 'ug1.ug_group' => $params['excludegroup'][0] ); 133 } else { 134 $exclude = array( $db->makeList( 135 array( 'ug1.ug_group' => $params['excludegroup'] ), 136 LIST_OR 137 ) ); 138 } 139 $this->addJoinConds( array( 'ug1' => array( 'LEFT OUTER JOIN', 140 array_merge( array( 'ug1.ug_user=user_id' ), $exclude ) 141 ) ) ); 142 $this->addWhere( 'ug1.ug_user IS NULL' ); 143 } 144 145 if ( $params['witheditsonly'] ) { 146 $this->addWhere( 'user_editcount > 0' ); 147 } 148 149 $this->showHiddenUsersAddBlockInfo( $fld_blockinfo ); 150 151 if ( $fld_groups || $fld_rights ) { 152 // Show the groups the given users belong to 153 // request more than needed to avoid not getting all rows that belong to one user 154 $groupCount = count( User::getAllGroups() ); 155 $sqlLimit = $limit + $groupCount + 1; 156 157 $this->addTables( 'user_groups', 'ug2' ); 158 $this->addJoinConds( array( 'ug2' => array( 'LEFT JOIN', 'ug2.ug_user=user_id' ) ) ); 159 $this->addFields( array( 'ug_group2' => 'ug2.ug_group' ) ); 160 } else { 161 $sqlLimit = $limit + 1; 162 } 163 164 if ( $params['activeusers'] ) { 165 $activeUserSeconds = $activeUserDays * 86400; 166 167 // Filter query to only include users in the active users cache 168 $this->addTables( 'querycachetwo' ); 169 $this->addJoinConds( array( 'querycachetwo' => array( 170 'INNER JOIN', array( 171 'qcc_type' => 'activeusers', 172 'qcc_namespace' => NS_USER, 173 'qcc_title=user_name', 174 ), 175 ) ) ); 176 177 // Actually count the actions using a subquery (bug 64505 and bug 64507) 178 $timestamp = $db->timestamp( wfTimestamp( TS_UNIX ) - $activeUserSeconds ); 179 $this->addFields( array( 180 'recentactions' => '(' . $db->selectSQLText( 181 'recentchanges', 182 'COUNT(*)', 183 array( 184 'rc_user_text = user_name', 185 'rc_type != ' . $db->addQuotes( RC_EXTERNAL ), // no wikidata 186 'rc_log_type IS NULL OR rc_log_type != ' . $db->addQuotes( 'newusers' ), 187 'rc_timestamp >= ' . $db->addQuotes( $timestamp ), 188 ) 189 ) . ')' 190 ) ); 191 } 192 193 $this->addOption( 'LIMIT', $sqlLimit ); 194 195 $this->addFields( array( 196 'user_name', 197 'user_id' 198 ) ); 199 $this->addFieldsIf( 'user_editcount', $fld_editcount ); 200 $this->addFieldsIf( 'user_registration', $fld_registration ); 201 202 if ( $useIndex ) { 203 $this->addOption( 'USE INDEX', array( 'user' => 'user_name' ) ); 204 } 205 206 $res = $this->select( __METHOD__ ); 207 208 $count = 0; 209 $lastUserData = false; 210 $lastUser = false; 211 $result = $this->getResult(); 212 213 // This loop keeps track of the last entry. For each new row, if the 214 // new row is for different user then the last, the last entry is added 215 // to results. Otherwise, the group of the new row is appended to the 216 // last entry. The setContinue... is more complex because of this, and 217 // takes into account the higher sql limit to make sure all rows that 218 // belong to the same user are received. 219 220 foreach ( $res as $row ) { 221 $count++; 222 223 if ( $lastUser !== $row->user_name ) { 224 // Save the last pass's user data 225 if ( is_array( $lastUserData ) ) { 226 if ( $params['activeusers'] && $lastUserData['recentactions'] === 0 ) { 227 // activeusers cache was out of date 228 $fit = true; 229 } else { 230 $fit = $result->addValue( array( 'query', $this->getModuleName() ), 231 null, $lastUserData ); 232 } 233 234 $lastUserData = null; 235 236 if ( !$fit ) { 237 $this->setContinueEnumParameter( 'from', $lastUserData['name'] ); 238 break; 239 } 240 } 241 242 if ( $count > $limit ) { 243 // We've reached the one extra which shows that there are 244 // additional pages to be had. Stop here... 245 $this->setContinueEnumParameter( 'from', $row->user_name ); 246 break; 247 } 248 249 // Record new user's data 250 $lastUser = $row->user_name; 251 $lastUserData = array( 252 'userid' => $row->user_id, 253 'name' => $lastUser, 254 ); 255 if ( $fld_blockinfo && !is_null( $row->ipb_by_text ) ) { 256 $lastUserData['blockid'] = $row->ipb_id; 257 $lastUserData['blockedby'] = $row->ipb_by_text; 258 $lastUserData['blockedbyid'] = $row->ipb_by; 259 $lastUserData['blockedtimestamp'] = wfTimestamp( TS_ISO_8601, $row->ipb_timestamp ); 260 $lastUserData['blockreason'] = $row->ipb_reason; 261 $lastUserData['blockexpiry'] = $row->ipb_expiry; 262 } 263 if ( $row->ipb_deleted ) { 264 $lastUserData['hidden'] = ''; 265 } 266 if ( $fld_editcount ) { 267 $lastUserData['editcount'] = intval( $row->user_editcount ); 268 } 269 if ( $params['activeusers'] ) { 270 $lastUserData['recentactions'] = intval( $row->recentactions ); 271 // @todo 'recenteditcount' is set for BC, remove in 1.25 272 $lastUserData['recenteditcount'] = $lastUserData['recentactions']; 273 } 274 if ( $fld_registration ) { 275 $lastUserData['registration'] = $row->user_registration ? 276 wfTimestamp( TS_ISO_8601, $row->user_registration ) : ''; 277 } 278 } 279 280 if ( $sqlLimit == $count ) { 281 // @todo BUG! database contains group name that User::getAllGroups() does not return 282 // Should handle this more gracefully 283 ApiBase::dieDebug( 284 __METHOD__, 285 'MediaWiki configuration error: The database contains more ' . 286 'user groups than known to User::getAllGroups() function' 287 ); 288 } 289 290 $lastUserObj = User::newFromId( $row->user_id ); 291 292 // Add user's group info 293 if ( $fld_groups ) { 294 if ( !isset( $lastUserData['groups'] ) ) { 295 if ( $lastUserObj ) { 296 $lastUserData['groups'] = $lastUserObj->getAutomaticGroups(); 297 } else { 298 // This should not normally happen 299 $lastUserData['groups'] = array(); 300 } 301 } 302 303 if ( !is_null( $row->ug_group2 ) ) { 304 $lastUserData['groups'][] = $row->ug_group2; 305 } 306 307 $result->setIndexedTagName( $lastUserData['groups'], 'g' ); 308 } 309 310 if ( $fld_implicitgroups && !isset( $lastUserData['implicitgroups'] ) && $lastUserObj ) { 311 $lastUserData['implicitgroups'] = $lastUserObj->getAutomaticGroups(); 312 $result->setIndexedTagName( $lastUserData['implicitgroups'], 'g' ); 313 } 314 if ( $fld_rights ) { 315 if ( !isset( $lastUserData['rights'] ) ) { 316 if ( $lastUserObj ) { 317 $lastUserData['rights'] = User::getGroupPermissions( $lastUserObj->getAutomaticGroups() ); 318 } else { 319 // This should not normally happen 320 $lastUserData['rights'] = array(); 321 } 322 } 323 324 if ( !is_null( $row->ug_group2 ) ) { 325 $lastUserData['rights'] = array_unique( array_merge( $lastUserData['rights'], 326 User::getGroupPermissions( array( $row->ug_group2 ) ) ) ); 327 } 328 329 $result->setIndexedTagName( $lastUserData['rights'], 'r' ); 330 } 331 } 332 333 if ( is_array( $lastUserData ) && 334 !( $params['activeusers'] && $lastUserData['recentactions'] === 0 ) 335 ) { 336 $fit = $result->addValue( array( 'query', $this->getModuleName() ), 337 null, $lastUserData ); 338 if ( !$fit ) { 339 $this->setContinueEnumParameter( 'from', $lastUserData['name'] ); 340 } 341 } 342 343 $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'u' ); 344 } 345 346 public function getCacheMode( $params ) { 347 return 'anon-public-user-private'; 348 } 349 350 public function getAllowedParams() { 351 $userGroups = User::getAllGroups(); 352 353 return array( 354 'from' => null, 355 'to' => null, 356 'prefix' => null, 357 'dir' => array( 358 ApiBase::PARAM_DFLT => 'ascending', 359 ApiBase::PARAM_TYPE => array( 360 'ascending', 361 'descending' 362 ), 363 ), 364 'group' => array( 365 ApiBase::PARAM_TYPE => $userGroups, 366 ApiBase::PARAM_ISMULTI => true, 367 ), 368 'excludegroup' => array( 369 ApiBase::PARAM_TYPE => $userGroups, 370 ApiBase::PARAM_ISMULTI => true, 371 ), 372 'rights' => array( 373 ApiBase::PARAM_TYPE => User::getAllRights(), 374 ApiBase::PARAM_ISMULTI => true, 375 ), 376 'prop' => array( 377 ApiBase::PARAM_ISMULTI => true, 378 ApiBase::PARAM_TYPE => array( 379 'blockinfo', 380 'groups', 381 'implicitgroups', 382 'rights', 383 'editcount', 384 'registration' 385 ) 386 ), 387 'limit' => array( 388 ApiBase::PARAM_DFLT => 10, 389 ApiBase::PARAM_TYPE => 'limit', 390 ApiBase::PARAM_MIN => 1, 391 ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, 392 ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 393 ), 394 'witheditsonly' => false, 395 'activeusers' => false, 396 ); 397 } 398 399 public function getParamDescription() { 400 return array( 401 'from' => 'The user name to start enumerating from', 402 'to' => 'The user name to stop enumerating at', 403 'prefix' => 'Search for all users that begin with this value', 404 'dir' => 'Direction to sort in', 405 'group' => 'Limit users to given group name(s)', 406 'excludegroup' => 'Exclude users in given group name(s)', 407 'rights' => 'Limit users to given right(s) (does not include rights ' . 408 'granted by implicit or auto-promoted groups like *, user, or autoconfirmed)', 409 'prop' => array( 410 'What pieces of information to include.', 411 ' blockinfo - Adds the information about a current block on the user', 412 ' groups - Lists groups that the user is in. This uses ' . 413 'more server resources and may return fewer results than the limit', 414 ' implicitgroups - Lists all the groups the user is automatically in', 415 ' rights - Lists rights that the user has', 416 ' editcount - Adds the edit count of the user', 417 ' registration - Adds the timestamp of when the user registered if available (may be blank)', 418 ), 419 'limit' => 'How many total user names to return', 420 'witheditsonly' => 'Only list users who have made edits', 421 'activeusers' => "Only list users active in the last {$this->getConfig()->get( 'ActiveUserDays' )} days(s)" 422 ); 423 } 424 425 public function getDescription() { 426 return 'Enumerate all registered users.'; 427 } 428 429 public function getExamples() { 430 return array( 431 'api.php?action=query&list=allusers&aufrom=Y', 432 ); 433 } 434 435 public function getHelpUrls() { 436 return 'https://www.mediawiki.org/wiki/API:Allusers'; 437 } 438 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |