00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 if ( !defined( 'MEDIAWIKI' ) ) {
00027
00028 require_once ( 'ApiQueryBase.php' );
00029 }
00030
00036 class ApiQueryContributions extends ApiQueryBase {
00037
00038 public function __construct( $query, $moduleName ) {
00039 parent :: __construct( $query, $moduleName, 'uc' );
00040 }
00041
00042 private $params, $username;
00043 private $fld_ids = false, $fld_title = false, $fld_timestamp = false,
00044 $fld_comment = false, $fld_parsedcomment = false, $fld_flags = false,
00045 $fld_patrolled = false, $fld_tags = false;
00046
00047 public function execute() {
00048
00049 $this->params = $this->extractRequestParams();
00050
00051 $prop = array_flip( $this->params['prop'] );
00052 $this->fld_ids = isset( $prop['ids'] );
00053 $this->fld_title = isset( $prop['title'] );
00054 $this->fld_comment = isset( $prop['comment'] );
00055 $this->fld_parsedcomment = isset ( $prop['parsedcomment'] );
00056 $this->fld_size = isset( $prop['size'] );
00057 $this->fld_flags = isset( $prop['flags'] );
00058 $this->fld_timestamp = isset( $prop['timestamp'] );
00059 $this->fld_patrolled = isset( $prop['patrolled'] );
00060 $this->fld_tags = isset( $prop['tags'] );
00061
00062
00063 $this->selectNamedDB( 'contributions', DB_SLAVE, 'contributions' );
00064 $db = $this->getDB();
00065
00066 if ( isset( $this->params['userprefix'] ) )
00067 {
00068 $this->prefixMode = true;
00069 $this->multiUserMode = true;
00070 $this->userprefix = $this->params['userprefix'];
00071 }
00072 else
00073 {
00074 $this->usernames = array();
00075 if ( !is_array( $this->params['user'] ) )
00076 $this->params['user'] = array( $this->params['user'] );
00077 if ( !count( $this->params['user'] ) )
00078 $this->dieUsage( 'User parameter may not be empty.', 'param_user' );
00079 foreach ( $this->params['user'] as $u )
00080 $this->prepareUsername( $u );
00081 $this->prefixMode = false;
00082 $this->multiUserMode = ( count( $this->params['user'] ) > 1 );
00083 }
00084 $this->prepareQuery();
00085
00086
00087 $res = $this->select( __METHOD__ );
00088
00089
00090 $count = 0;
00091 $limit = $this->params['limit'];
00092
00093
00094 while ( $row = $db->fetchObject( $res ) ) {
00095 if ( ++ $count > $limit ) {
00096
00097 if ( $this->multiUserMode )
00098 $this->setContinueEnumParameter( 'continue', $this->continueStr( $row ) );
00099 else
00100 $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->rev_timestamp ) );
00101 break;
00102 }
00103
00104 $vals = $this->extractRowInfo( $row );
00105 $fit = $this->getResult()->addValue( array( 'query', $this->getModuleName() ), null, $vals );
00106 if ( !$fit )
00107 {
00108 if ( $this->multiUserMode )
00109 $this->setContinueEnumParameter( 'continue', $this->continueStr( $row ) );
00110 else
00111 $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->rev_timestamp ) );
00112 break;
00113 }
00114 }
00115
00116
00117 $db->freeResult( $res );
00118
00119 $this->getResult()->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'item' );
00120 }
00121
00126 private function prepareUsername( $user ) {
00127 if ( !is_null( $user ) && $user !== '' ) {
00128 $name = User::isIP( $user )
00129 ? $user
00130 : User::getCanonicalName( $user, 'valid' );
00131 if ( $name === false ) {
00132 $this->dieUsage( "User name {$user} is not valid", 'param_user' );
00133 } else {
00134 $this->usernames[] = $name;
00135 }
00136 } else {
00137 $this->dieUsage( 'User parameter may not be empty', 'param_user' );
00138 }
00139 }
00140
00144 private function prepareQuery() {
00145
00146
00147
00148 global $wgUser;
00149 $tables = array( 'page', 'revision' );
00150 $this->addWhere( 'page_id=rev_page' );
00151
00152
00153 if ( $this->multiUserMode && !is_null( $this->params['continue'] ) )
00154 {
00155 $continue = explode( '|', $this->params['continue'] );
00156 if ( count( $continue ) != 2 )
00157 $this->dieUsage( "Invalid continue param. You should pass the original " .
00158 "value returned by the previous query", "_badcontinue" );
00159 $encUser = $this->getDB()->strencode( $continue[0] );
00160 $encTS = wfTimestamp( TS_MW, $continue[1] );
00161 $op = ( $this->params['dir'] == 'older' ? '<' : '>' );
00162 $this->addWhere( "rev_user_text $op '$encUser' OR " .
00163 "(rev_user_text = '$encUser' AND " .
00164 "rev_timestamp $op= '$encTS')" );
00165 }
00166
00167 if ( !$wgUser->isAllowed( 'hideuser' ) )
00168 $this->addWhere( $this->getDB()->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0' );
00169
00170 if ( $this->prefixMode )
00171 $this->addWhere( 'rev_user_text' . $this->getDB()->buildLike( $this->userprefix, $this->getDB()->anyString() ) );
00172 else
00173 $this->addWhereFld( 'rev_user_text', $this->usernames );
00174
00175
00176
00177 if ( $this->multiUserMode )
00178 $this->addWhereRange( 'rev_user_text', $this->params['dir'], null, null );
00179 $this->addWhereRange( 'rev_timestamp',
00180 $this->params['dir'], $this->params['start'], $this->params['end'] );
00181 $this->addWhereFld( 'page_namespace', $this->params['namespace'] );
00182
00183 $show = $this->params['show'];
00184 if ( !is_null( $show ) ) {
00185 $show = array_flip( $show );
00186 if ( ( isset( $show['minor'] ) && isset( $show['!minor'] ) )
00187 || ( isset( $show['patrolled'] ) && isset( $show['!patrolled'] ) ) )
00188 $this->dieUsageMsg( array( 'show' ) );
00189
00190 $this->addWhereIf( 'rev_minor_edit = 0', isset( $show['!minor'] ) );
00191 $this->addWhereIf( 'rev_minor_edit != 0', isset( $show['minor'] ) );
00192 $this->addWhereIf( 'rc_patrolled = 0', isset( $show['!patrolled'] ) );
00193 $this->addWhereIf( 'rc_patrolled != 0', isset( $show['patrolled'] ) );
00194 }
00195 $this->addOption( 'LIMIT', $this->params['limit'] + 1 );
00196 $index = array( 'revision' => 'usertext_timestamp' );
00197
00198
00199
00200
00201 $this->addFields( array(
00202 'rev_timestamp',
00203 'page_namespace',
00204 'page_title',
00205 'rev_user_text',
00206 'rev_deleted'
00207 ) );
00208
00209 if ( isset( $show['patrolled'] ) || isset( $show['!patrolled'] ) ||
00210 $this->fld_patrolled )
00211 {
00212 global $wgUser;
00213 if ( !$wgUser->useRCPatrol() && !$wgUser->useNPPatrol() )
00214 $this->dieUsage( "You need the patrol right to request the patrolled flag", 'permissiondenied' );
00215
00216
00217
00218 $index['recentchanges'] = 'rc_user_text';
00219 if ( isset( $show['patrolled'] ) || isset( $show['!patrolled'] ) )
00220 {
00221
00222
00223 $tables = array( 'revision', 'recentchanges', 'page' );
00224 $this->addOption( 'STRAIGHT_JOIN' );
00225 $this->addWhere( 'rc_user_text=rev_user_text' );
00226 $this->addWhere( 'rc_timestamp=rev_timestamp' );
00227 $this->addWhere( 'rc_this_oldid=rev_id' );
00228 }
00229 else
00230 {
00231 $tables[] = 'recentchanges';
00232 $this->addJoinConds( array( 'recentchanges' => array(
00233 'LEFT JOIN', array(
00234 'rc_user_text=rev_user_text',
00235 'rc_timestamp=rev_timestamp',
00236 'rc_this_oldid=rev_id' ) ) ) );
00237 }
00238 }
00239
00240 $this->addTables( $tables );
00241 $this->addFieldsIf( 'rev_page', $this->fld_ids );
00242 $this->addFieldsIf( 'rev_id', $this->fld_ids || $this->fld_flags );
00243 $this->addFieldsIf( 'page_latest', $this->fld_flags );
00244
00245 $this->addFieldsIf( 'rev_comment', $this->fld_comment || $this->fld_parsedcomment );
00246 $this->addFieldsIf( 'rev_len', $this->fld_size );
00247 $this->addFieldsIf( 'rev_minor_edit', $this->fld_flags );
00248 $this->addFieldsIf( 'rev_parent_id', $this->fld_flags );
00249 $this->addFieldsIf( 'rc_patrolled', $this->fld_patrolled );
00250
00251 if ( $this->fld_tags )
00252 {
00253 $this->addTables( 'tag_summary' );
00254 $this->addJoinConds( array( 'tag_summary' => array( 'LEFT JOIN', array( 'rev_id=ts_rev_id' ) ) ) );
00255 $this->addFields( 'ts_tags' );
00256 }
00257
00258 if ( isset( $this->params['tag'] ) ) {
00259 $this->addTables( 'change_tag' );
00260 $this->addJoinConds( array( 'change_tag' => array( 'INNER JOIN', array( 'rev_id=ct_rev_id' ) ) ) );
00261 $this->addWhereFld( 'ct_tag', $this->params['tag'] );
00262 global $wgOldChangeTagsIndex;
00263 $index['change_tag'] = $wgOldChangeTagsIndex ? 'ct_tag' : 'change_tag_tag_id';
00264 }
00265
00266 $this->addOption( 'USE INDEX', $index );
00267 }
00268
00272 private function extractRowInfo( $row ) {
00273
00274 $vals = array();
00275
00276 $vals['user'] = $row->rev_user_text;
00277 if ( $row->rev_deleted & Revision::DELETED_USER )
00278 $vals['userhidden'] = '';
00279 if ( $this->fld_ids ) {
00280 $vals['pageid'] = intval( $row->rev_page );
00281 $vals['revid'] = intval( $row->rev_id );
00282
00283 }
00284
00285 $title = Title::makeTitle( $row->page_namespace, $row->page_title );
00286
00287 if ( $this->fld_title )
00288 ApiQueryBase::addTitleInfo( $vals, $title );
00289
00290 if ( $this->fld_timestamp )
00291 $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $row->rev_timestamp );
00292
00293 if ( $this->fld_flags ) {
00294 if ( $row->rev_parent_id == 0 && !is_null( $row->rev_parent_id ) )
00295 $vals['new'] = '';
00296 if ( $row->rev_minor_edit )
00297 $vals['minor'] = '';
00298 if ( $row->page_latest == $row->rev_id )
00299 $vals['top'] = '';
00300 }
00301
00302 if ( ( $this->fld_comment || $this->fld_parsedcomment ) && isset( $row->rev_comment ) ) {
00303 if ( $row->rev_deleted & Revision::DELETED_COMMENT )
00304 $vals['commenthidden'] = '';
00305 else {
00306 if ( $this->fld_comment )
00307 $vals['comment'] = $row->rev_comment;
00308
00309 if ( $this->fld_parsedcomment ) {
00310 global $wgUser;
00311 $vals['parsedcomment'] = $wgUser->getSkin()->formatComment( $row->rev_comment, $title );
00312 }
00313 }
00314 }
00315
00316 if ( $this->fld_patrolled && $row->rc_patrolled )
00317 $vals['patrolled'] = '';
00318
00319 if ( $this->fld_size && !is_null( $row->rev_len ) )
00320 $vals['size'] = intval( $row->rev_len );
00321
00322 if ( $this->fld_tags ) {
00323 if ( $row->ts_tags ) {
00324 $tags = explode( ',', $row->ts_tags );
00325 $this->getResult()->setIndexedTagName( $tags, 'tag' );
00326 $vals['tags'] = $tags;
00327 } else {
00328 $vals['tags'] = array();
00329 }
00330 }
00331
00332 return $vals;
00333 }
00334
00335 private function continueStr( $row )
00336 {
00337 return $row->rev_user_text . '|' .
00338 wfTimestamp( TS_ISO_8601, $row->rev_timestamp );
00339 }
00340
00341 public function getCacheMode( $params ) {
00342
00343
00344 return 'anon-public-user-private';
00345 }
00346
00347 public function getAllowedParams() {
00348 return array (
00349 'limit' => array (
00350 ApiBase :: PARAM_DFLT => 10,
00351 ApiBase :: PARAM_TYPE => 'limit',
00352 ApiBase :: PARAM_MIN => 1,
00353 ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1,
00354 ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
00355 ),
00356 'start' => array (
00357 ApiBase :: PARAM_TYPE => 'timestamp'
00358 ),
00359 'end' => array (
00360 ApiBase :: PARAM_TYPE => 'timestamp'
00361 ),
00362 'continue' => null,
00363 'user' => array (
00364 ApiBase :: PARAM_ISMULTI => true
00365 ),
00366 'userprefix' => null,
00367 'dir' => array (
00368 ApiBase :: PARAM_DFLT => 'older',
00369 ApiBase :: PARAM_TYPE => array (
00370 'newer',
00371 'older'
00372 )
00373 ),
00374 'namespace' => array (
00375 ApiBase :: PARAM_ISMULTI => true,
00376 ApiBase :: PARAM_TYPE => 'namespace'
00377 ),
00378 'prop' => array (
00379 ApiBase :: PARAM_ISMULTI => true,
00380 ApiBase :: PARAM_DFLT => 'ids|title|timestamp|comment|size|flags',
00381 ApiBase :: PARAM_TYPE => array (
00382 'ids',
00383 'title',
00384 'timestamp',
00385 'comment',
00386 'parsedcomment',
00387 'size',
00388 'flags',
00389 'patrolled',
00390 'tags'
00391 )
00392 ),
00393 'show' => array (
00394 ApiBase :: PARAM_ISMULTI => true,
00395 ApiBase :: PARAM_TYPE => array (
00396 'minor',
00397 '!minor',
00398 'patrolled',
00399 '!patrolled',
00400 )
00401 ),
00402 'tag' => null,
00403 );
00404 }
00405
00406 public function getParamDescription() {
00407 return array (
00408 'limit' => 'The maximum number of contributions to return.',
00409 'start' => 'The start timestamp to return from.',
00410 'end' => 'The end timestamp to return to.',
00411 'continue' => 'When more results are available, use this to continue.',
00412 'user' => 'The user to retrieve contributions for.',
00413 'userprefix' => 'Retrieve contibutions for all users whose names begin with this value. Overrides ucuser.',
00414 'dir' => 'The direction to search (older or newer).',
00415 'namespace' => 'Only list contributions in these namespaces',
00416 'prop' => 'Include additional pieces of information',
00417 'show' => array( 'Show only items that meet this criteria, e.g. non minor edits only: show=!minor',
00418 'NOTE: if show=patrolled or show=!patrolled is set, revisions older than $wgRCMaxAge won\'t be shown', ),
00419 'tag' => 'Only list revisions tagged with this tag',
00420 );
00421 }
00422
00423 public function getDescription() {
00424 return 'Get all edits by a user';
00425 }
00426
00427 public function getPossibleErrors() {
00428 return array_merge( parent::getPossibleErrors(), array(
00429 array( 'code' => 'param_user', 'info' => 'User parameter may not be empty.' ),
00430 array( 'code' => 'param_user', 'info' => 'User name user is not valid' ),
00431 array( 'show' ),
00432 array( 'code' => 'permissiondenied', 'info' => 'You need the patrol right to request the patrolled flag' ),
00433 ) );
00434 }
00435
00436 protected function getExamples() {
00437 return array (
00438 'api.php?action=query&list=usercontribs&ucuser=YurikBot',
00439 'api.php?action=query&list=usercontribs&ucuserprefix=217.121.114.',
00440 );
00441 }
00442
00443 public function getVersion() {
00444 return __CLASS__ . ': $Id: ApiQueryUserContributions.php 69932 2010-07-26 08:03:21Z tstarling $';
00445 }
00446 }