MediaWiki  REL1_22
ApiQueryLogEvents.php
Go to the documentation of this file.
00001 <?php
00032 class ApiQueryLogEvents extends ApiQueryBase {
00033 
00034     public function __construct( $query, $moduleName ) {
00035         parent::__construct( $query, $moduleName, 'le' );
00036     }
00037 
00038     private $fld_ids = false, $fld_title = false, $fld_type = false,
00039         $fld_action = false, $fld_user = false, $fld_userid = false,
00040         $fld_timestamp = false, $fld_comment = false, $fld_parsedcomment = false,
00041         $fld_details = false, $fld_tags = false;
00042 
00043     public function execute() {
00044         $params = $this->extractRequestParams();
00045         $db = $this->getDB();
00046 
00047         $prop = array_flip( $params['prop'] );
00048 
00049         $this->fld_ids = isset( $prop['ids'] );
00050         $this->fld_title = isset( $prop['title'] );
00051         $this->fld_type = isset( $prop['type'] );
00052         $this->fld_action = isset( $prop['action'] );
00053         $this->fld_user = isset( $prop['user'] );
00054         $this->fld_userid = isset( $prop['userid'] );
00055         $this->fld_timestamp = isset( $prop['timestamp'] );
00056         $this->fld_comment = isset( $prop['comment'] );
00057         $this->fld_parsedcomment = isset( $prop['parsedcomment'] );
00058         $this->fld_details = isset( $prop['details'] );
00059         $this->fld_tags = isset( $prop['tags'] );
00060 
00061         $hideLogs = LogEventsList::getExcludeClause( $db, 'user', $this->getUser() );
00062         if ( $hideLogs !== false ) {
00063             $this->addWhere( $hideLogs );
00064         }
00065 
00066         // Order is significant here
00067         $this->addTables( array( 'logging', 'user', 'page' ) );
00068         $this->addOption( 'STRAIGHT_JOIN' );
00069         $this->addJoinConds( array(
00070             'user' => array( 'LEFT JOIN',
00071                 'user_id=log_user' ),
00072             'page' => array( 'LEFT JOIN',
00073                 array( 'log_namespace=page_namespace',
00074                     'log_title=page_title' ) ) ) );
00075         $index = array( 'logging' => 'times' ); // default, may change
00076 
00077         $this->addFields( array(
00078             'log_type',
00079             'log_action',
00080             'log_timestamp',
00081             'log_deleted',
00082         ) );
00083 
00084         $this->addFieldsIf( array( 'log_id', 'page_id' ), $this->fld_ids );
00085         $this->addFieldsIf( array( 'log_user', 'log_user_text', 'user_name' ), $this->fld_user );
00086         $this->addFieldsIf( 'log_user', $this->fld_userid );
00087         $this->addFieldsIf( array( 'log_namespace', 'log_title' ), $this->fld_title || $this->fld_parsedcomment );
00088         $this->addFieldsIf( 'log_comment', $this->fld_comment || $this->fld_parsedcomment );
00089         $this->addFieldsIf( 'log_params', $this->fld_details );
00090 
00091         if ( $this->fld_tags ) {
00092             $this->addTables( 'tag_summary' );
00093             $this->addJoinConds( array( 'tag_summary' => array( 'LEFT JOIN', 'log_id=ts_log_id' ) ) );
00094             $this->addFields( 'ts_tags' );
00095         }
00096 
00097         if ( !is_null( $params['tag'] ) ) {
00098             $this->addTables( 'change_tag' );
00099             $this->addJoinConds( array( 'change_tag' => array( 'INNER JOIN', array( 'log_id=ct_log_id' ) ) ) );
00100             $this->addWhereFld( 'ct_tag', $params['tag'] );
00101             $index['change_tag'] = 'change_tag_tag_id';
00102         }
00103 
00104         if ( !is_null( $params['action'] ) ) {
00105             list( $type, $action ) = explode( '/', $params['action'] );
00106             $this->addWhereFld( 'log_type', $type );
00107             $this->addWhereFld( 'log_action', $action );
00108         } elseif ( !is_null( $params['type'] ) ) {
00109             $this->addWhereFld( 'log_type', $params['type'] );
00110             $index['logging'] = 'type_time';
00111         }
00112 
00113         $this->addTimestampWhereRange( 'log_timestamp', $params['dir'], $params['start'], $params['end'] );
00114 
00115         $limit = $params['limit'];
00116         $this->addOption( 'LIMIT', $limit + 1 );
00117 
00118         $user = $params['user'];
00119         if ( !is_null( $user ) ) {
00120             $userid = User::idFromName( $user );
00121             if ( !$userid ) {
00122                 $this->dieUsage( "User name $user not found", 'param_user' );
00123             }
00124             $this->addWhereFld( 'log_user', $userid );
00125             $index['logging'] = 'user_time';
00126         }
00127 
00128         $title = $params['title'];
00129         if ( !is_null( $title ) ) {
00130             $titleObj = Title::newFromText( $title );
00131             if ( is_null( $titleObj ) ) {
00132                 $this->dieUsage( "Bad title value '$title'", 'param_title' );
00133             }
00134             $this->addWhereFld( 'log_namespace', $titleObj->getNamespace() );
00135             $this->addWhereFld( 'log_title', $titleObj->getDBkey() );
00136 
00137             // Use the title index in preference to the user index if there is a conflict
00138             $index['logging'] = is_null( $user ) ? 'page_time' : array( 'page_time', 'user_time' );
00139         }
00140 
00141         $prefix = $params['prefix'];
00142 
00143         if ( !is_null( $prefix ) ) {
00144             global $wgMiserMode;
00145             if ( $wgMiserMode ) {
00146                 $this->dieUsage( 'Prefix search disabled in Miser Mode', 'prefixsearchdisabled' );
00147             }
00148 
00149             $title = Title::newFromText( $prefix );
00150             if ( is_null( $title ) ) {
00151                 $this->dieUsage( "Bad title value '$prefix'", 'param_prefix' );
00152             }
00153             $this->addWhereFld( 'log_namespace', $title->getNamespace() );
00154             $this->addWhere( 'log_title ' . $db->buildLike( $title->getDBkey(), $db->anyString() ) );
00155         }
00156 
00157         $this->addOption( 'USE INDEX', $index );
00158 
00159         // Paranoia: avoid brute force searches (bug 17342)
00160         if ( !is_null( $title ) ) {
00161             $this->addWhere( $db->bitAnd( 'log_deleted', LogPage::DELETED_ACTION ) . ' = 0' );
00162         }
00163         if ( !is_null( $user ) ) {
00164             $this->addWhere( $db->bitAnd( 'log_deleted', LogPage::DELETED_USER ) . ' = 0' );
00165         }
00166 
00167         $count = 0;
00168         $res = $this->select( __METHOD__ );
00169         $result = $this->getResult();
00170         foreach ( $res as $row ) {
00171             if ( ++ $count > $limit ) {
00172                 // We've reached the one extra which shows that there are additional pages to be had. Stop here...
00173                 $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->log_timestamp ) );
00174                 break;
00175             }
00176 
00177             $vals = $this->extractRowInfo( $row );
00178             if ( !$vals ) {
00179                 continue;
00180             }
00181             $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $vals );
00182             if ( !$fit ) {
00183                 $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->log_timestamp ) );
00184                 break;
00185             }
00186         }
00187         $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'item' );
00188     }
00189 
00200     public static function addLogParams( $result, &$vals, $params, $type, $action, $ts, $legacy = false ) {
00201         switch ( $type ) {
00202             case 'move':
00203                 if ( $legacy ) {
00204                     $targetKey = 0;
00205                     $noredirKey = 1;
00206                 } else {
00207                     $targetKey = '4::target';
00208                     $noredirKey = '5::noredir';
00209                 }
00210 
00211                 if ( isset( $params[$targetKey] ) ) {
00212                     $title = Title::newFromText( $params[$targetKey] );
00213                     if ( $title ) {
00214                         $vals2 = array();
00215                         ApiQueryBase::addTitleInfo( $vals2, $title, 'new_' );
00216                         $vals[$type] = $vals2;
00217                     }
00218                 }
00219                 if ( isset( $params[$noredirKey] ) && $params[$noredirKey] ) {
00220                     $vals[$type]['suppressedredirect'] = '';
00221                 }
00222                 $params = null;
00223                 break;
00224             case 'patrol':
00225                 if ( $legacy ) {
00226                     $cur = 0;
00227                     $prev = 1;
00228                     $auto = 2;
00229                 } else {
00230                     $cur = '4::curid';
00231                     $prev = '5::previd';
00232                     $auto = '6::auto';
00233                 }
00234                 $vals2 = array();
00235                 $vals2['cur'] = $params[$cur];
00236                 $vals2['prev'] = $params[$prev];
00237                 $vals2['auto'] = $params[$auto];
00238                 $vals[$type] = $vals2;
00239                 $params = null;
00240                 break;
00241             case 'rights':
00242                 $vals2 = array();
00243                 if ( $legacy ) {
00244                     list( $vals2['old'], $vals2['new'] ) = $params;
00245                 } else {
00246                     $vals2['new'] = implode( ', ', $params['5::newgroups'] );
00247                     $vals2['old'] = implode( ', ', $params['4::oldgroups'] );
00248                 }
00249                 $vals[$type] = $vals2;
00250                 $params = null;
00251                 break;
00252             case 'block':
00253                 if ( $action == 'unblock' ) {
00254                     break;
00255                 }
00256                 $vals2 = array();
00257                 list( $vals2['duration'], $vals2['flags'] ) = $params;
00258 
00259                 // Indefinite blocks have no expiry time
00260                 if ( SpecialBlock::parseExpiryInput( $params[0] ) !== wfGetDB( DB_SLAVE )->getInfinity() ) {
00261                     $vals2['expiry'] = wfTimestamp( TS_ISO_8601,
00262                         strtotime( $params[0], wfTimestamp( TS_UNIX, $ts ) ) );
00263                 }
00264                 $vals[$type] = $vals2;
00265                 $params = null;
00266                 break;
00267             case 'upload':
00268                 if ( isset( $params['img_timestamp'] ) ) {
00269                     $params['img_timestamp'] = wfTimestamp( TS_ISO_8601, $params['img_timestamp'] );
00270                 }
00271                 break;
00272         }
00273         if ( !is_null( $params ) ) {
00274             $logParams = array();
00275             // Keys like "4::paramname" can't be used for output so we change them to "paramname"
00276             foreach ( $params as $key => $value ) {
00277                 if ( strpos( $key, ':' ) === false ) {
00278                     $logParams[$key] = $value;
00279                     continue;
00280                 }
00281                 $logParam = explode( ':', $key, 3 );
00282                 $logParams[$logParam[2]] = $value;
00283             }
00284             $result->setIndexedTagName( $logParams, 'param' );
00285             $result->setIndexedTagName_recursive( $logParams, 'param' );
00286             $vals = array_merge( $vals, $logParams );
00287         }
00288         return $vals;
00289     }
00290 
00291     private function extractRowInfo( $row ) {
00292         $logEntry = DatabaseLogEntry::newFromRow( $row );
00293         $vals = array();
00294 
00295         if ( $this->fld_ids ) {
00296             $vals['logid'] = intval( $row->log_id );
00297         }
00298 
00299         if ( $this->fld_title || $this->fld_parsedcomment ) {
00300             $title = Title::makeTitle( $row->log_namespace, $row->log_title );
00301         }
00302 
00303         if ( $this->fld_title || $this->fld_ids ) {
00304             if ( LogEventsList::isDeleted( $row, LogPage::DELETED_ACTION ) ) {
00305                 $vals['actionhidden'] = '';
00306             } else {
00307                 if ( $this->fld_title ) {
00308                     ApiQueryBase::addTitleInfo( $vals, $title );
00309                 }
00310                 if ( $this->fld_ids ) {
00311                     $vals['pageid'] = intval( $row->page_id );
00312                 }
00313             }
00314         }
00315 
00316         if ( $this->fld_type || $this->fld_action ) {
00317             $vals['type'] = $row->log_type;
00318             $vals['action'] = $row->log_action;
00319         }
00320 
00321         if ( $this->fld_details && $row->log_params !== '' ) {
00322             if ( LogEventsList::isDeleted( $row, LogPage::DELETED_ACTION ) ) {
00323                 $vals['actionhidden'] = '';
00324             } else {
00325                 self::addLogParams(
00326                     $this->getResult(),
00327                     $vals,
00328                     $logEntry->getParameters(),
00329                     $logEntry->getType(),
00330                     $logEntry->getSubtype(),
00331                     $logEntry->getTimestamp(),
00332                     $logEntry->isLegacy()
00333                 );
00334             }
00335         }
00336 
00337         if ( $this->fld_user || $this->fld_userid ) {
00338             if ( LogEventsList::isDeleted( $row, LogPage::DELETED_USER ) ) {
00339                 $vals['userhidden'] = '';
00340             } else {
00341                 if ( $this->fld_user ) {
00342                     $vals['user'] = $row->user_name === null ? $row->log_user_text : $row->user_name;
00343                 }
00344                 if ( $this->fld_userid ) {
00345                     $vals['userid'] = $row->log_user;
00346                 }
00347 
00348                 if ( !$row->log_user ) {
00349                     $vals['anon'] = '';
00350                 }
00351             }
00352         }
00353         if ( $this->fld_timestamp ) {
00354             $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $row->log_timestamp );
00355         }
00356 
00357         if ( ( $this->fld_comment || $this->fld_parsedcomment ) && isset( $row->log_comment ) ) {
00358             if ( LogEventsList::isDeleted( $row, LogPage::DELETED_COMMENT ) ) {
00359                 $vals['commenthidden'] = '';
00360             } else {
00361                 if ( $this->fld_comment ) {
00362                     $vals['comment'] = $row->log_comment;
00363                 }
00364 
00365                 if ( $this->fld_parsedcomment ) {
00366                     $vals['parsedcomment'] = Linker::formatComment( $row->log_comment, $title );
00367                 }
00368             }
00369         }
00370 
00371         if ( $this->fld_tags ) {
00372             if ( $row->ts_tags ) {
00373                 $tags = explode( ',', $row->ts_tags );
00374                 $this->getResult()->setIndexedTagName( $tags, 'tag' );
00375                 $vals['tags'] = $tags;
00376             } else {
00377                 $vals['tags'] = array();
00378             }
00379         }
00380 
00381         return $vals;
00382     }
00383 
00384     public function getCacheMode( $params ) {
00385         if ( !is_null( $params['prop'] ) && in_array( 'parsedcomment', $params['prop'] ) ) {
00386             // formatComment() calls wfMessage() among other things
00387             return 'anon-public-user-private';
00388         } elseif ( LogEventsList::getExcludeClause( $this->getDB(), 'user', $this->getUser() )
00389             === LogEventsList::getExcludeClause( $this->getDB(), 'public' )
00390         ) { // Output can only contain public data.
00391             return 'public';
00392         } else {
00393             return 'anon-public-user-private';
00394         }
00395     }
00396 
00397     public function getAllowedParams() {
00398         global $wgLogTypes, $wgLogActions, $wgLogActionsHandlers;
00399         return array(
00400             'prop' => array(
00401                 ApiBase::PARAM_ISMULTI => true,
00402                 ApiBase::PARAM_DFLT => 'ids|title|type|user|timestamp|comment|details',
00403                 ApiBase::PARAM_TYPE => array(
00404                     'ids',
00405                     'title',
00406                     'type',
00407                     'user',
00408                     'userid',
00409                     'timestamp',
00410                     'comment',
00411                     'parsedcomment',
00412                     'details',
00413                     'tags'
00414                 )
00415             ),
00416             'type' => array(
00417                 ApiBase::PARAM_TYPE => $wgLogTypes
00418             ),
00419             'action' => array(
00420                 ApiBase::PARAM_TYPE => array_keys( array_merge( $wgLogActions, $wgLogActionsHandlers ) )
00421             ),
00422             'start' => array(
00423                 ApiBase::PARAM_TYPE => 'timestamp'
00424             ),
00425             'end' => array(
00426                 ApiBase::PARAM_TYPE => 'timestamp'
00427             ),
00428             'dir' => array(
00429                 ApiBase::PARAM_DFLT => 'older',
00430                 ApiBase::PARAM_TYPE => array(
00431                     'newer',
00432                     'older'
00433                 )
00434             ),
00435             'user' => null,
00436             'title' => null,
00437             'prefix' => null,
00438             'tag' => null,
00439             'limit' => array(
00440                 ApiBase::PARAM_DFLT => 10,
00441                 ApiBase::PARAM_TYPE => 'limit',
00442                 ApiBase::PARAM_MIN => 1,
00443                 ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
00444                 ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2
00445             )
00446         );
00447     }
00448 
00449     public function getParamDescription() {
00450         $p = $this->getModulePrefix();
00451         return array(
00452             'prop' => array(
00453                 'Which properties to get',
00454                 ' ids            - Adds the ID of the log event',
00455                 ' title          - Adds the title of the page for the log event',
00456                 ' type           - Adds the type of log event',
00457                 ' user           - Adds the user responsible for the log event',
00458                 ' userid         - Adds the user ID who was responsible for the log event',
00459                 ' timestamp      - Adds the timestamp for the event',
00460                 ' comment        - Adds the comment of the event',
00461                 ' parsedcomment  - Adds the parsed comment of the event',
00462                 ' details        - Lists additional details about the event',
00463                 ' tags           - Lists tags for the event',
00464             ),
00465             'type' => 'Filter log entries to only this type',
00466             'action' => "Filter log actions to only this type. Overrides {$p}type",
00467             'start' => 'The timestamp to start enumerating from',
00468             'end' => 'The timestamp to end enumerating',
00469             'dir' => $this->getDirectionDescription( $p ),
00470             'user' => 'Filter entries to those made by the given user',
00471             'title' => 'Filter entries to those related to a page',
00472             'prefix' => 'Filter entries that start with this prefix. Disabled in Miser Mode',
00473             'limit' => 'How many total event entries to return',
00474             'tag' => 'Only list event entries tagged with this tag',
00475         );
00476     }
00477 
00478     public function getResultProperties() {
00479         global $wgLogTypes;
00480         return array(
00481             'ids' => array(
00482                 'logid' => 'integer',
00483                 'pageid' => 'integer'
00484             ),
00485             'title' => array(
00486                 'ns' => 'namespace',
00487                 'title' => 'string'
00488             ),
00489             'type' => array(
00490                 'type' => array(
00491                     ApiBase::PROP_TYPE => $wgLogTypes
00492                 ),
00493                 'action' => 'string'
00494             ),
00495             'details' => array(
00496                 'actionhidden' => 'boolean'
00497             ),
00498             'user' => array(
00499                 'userhidden' => 'boolean',
00500                 'user' => array(
00501                     ApiBase::PROP_TYPE => 'string',
00502                     ApiBase::PROP_NULLABLE => true
00503                 ),
00504                 'anon' => 'boolean'
00505             ),
00506             'userid' => array(
00507                 'userhidden' => 'boolean',
00508                 'userid' => array(
00509                     ApiBase::PROP_TYPE => 'integer',
00510                     ApiBase::PROP_NULLABLE => true
00511                 ),
00512                 'anon' => 'boolean'
00513             ),
00514             'timestamp' => array(
00515                 'timestamp' => 'timestamp'
00516             ),
00517             'comment' => array(
00518                 'commenthidden' => 'boolean',
00519                 'comment' => array(
00520                     ApiBase::PROP_TYPE => 'string',
00521                     ApiBase::PROP_NULLABLE => true
00522                 )
00523             ),
00524             'parsedcomment' => array(
00525                 'commenthidden' => 'boolean',
00526                 'parsedcomment' => array(
00527                     ApiBase::PROP_TYPE => 'string',
00528                     ApiBase::PROP_NULLABLE => true
00529                 )
00530             )
00531         );
00532     }
00533 
00534     public function getDescription() {
00535         return 'Get events from logs';
00536     }
00537 
00538     public function getPossibleErrors() {
00539         return array_merge( parent::getPossibleErrors(), array(
00540             array( 'code' => 'param_user', 'info' => 'User name $user not found' ),
00541             array( 'code' => 'param_title', 'info' => 'Bad title value \'title\'' ),
00542             array( 'code' => 'param_prefix', 'info' => 'Bad title value \'prefix\'' ),
00543             array( 'code' => 'prefixsearchdisabled', 'info' => 'Prefix search disabled in Miser Mode' ),
00544         ) );
00545     }
00546 
00547     public function getExamples() {
00548         return array(
00549             'api.php?action=query&list=logevents'
00550         );
00551     }
00552 
00553     public function getHelpUrls() {
00554         return 'https://www.mediawiki.org/wiki/API:Logevents';
00555     }
00556 }