[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/api/ -> ApiQueryLogEvents.php (source)

   1  <?php
   2  /**
   3   *
   4   *
   5   * Created on Oct 16, 2006
   6   *
   7   * Copyright © 2006 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 action to List the log events, with optional filtering by various parameters.
  29   *
  30   * @ingroup API
  31   */
  32  class ApiQueryLogEvents extends ApiQueryBase {
  33  
  34  	public function __construct( ApiQuery $query, $moduleName ) {
  35          parent::__construct( $query, $moduleName, 'le' );
  36      }
  37  
  38      private $fld_ids = false, $fld_title = false, $fld_type = false,
  39          $fld_action = false, $fld_user = false, $fld_userid = false,
  40          $fld_timestamp = false, $fld_comment = false, $fld_parsedcomment = false,
  41          $fld_details = false, $fld_tags = false;
  42  
  43  	public function execute() {
  44          $params = $this->extractRequestParams();
  45          $db = $this->getDB();
  46          $this->requireMaxOneParameter( $params, 'title', 'prefix', 'namespace' );
  47  
  48          $prop = array_flip( $params['prop'] );
  49  
  50          $this->fld_ids = isset( $prop['ids'] );
  51          $this->fld_title = isset( $prop['title'] );
  52          $this->fld_type = isset( $prop['type'] );
  53          $this->fld_action = isset( $prop['action'] );
  54          $this->fld_user = isset( $prop['user'] );
  55          $this->fld_userid = isset( $prop['userid'] );
  56          $this->fld_timestamp = isset( $prop['timestamp'] );
  57          $this->fld_comment = isset( $prop['comment'] );
  58          $this->fld_parsedcomment = isset( $prop['parsedcomment'] );
  59          $this->fld_details = isset( $prop['details'] );
  60          $this->fld_tags = isset( $prop['tags'] );
  61  
  62          $hideLogs = LogEventsList::getExcludeClause( $db, 'user', $this->getUser() );
  63          if ( $hideLogs !== false ) {
  64              $this->addWhere( $hideLogs );
  65          }
  66  
  67          // Order is significant here
  68          $this->addTables( array( 'logging', 'user', 'page' ) );
  69          $this->addJoinConds( array(
  70              'user' => array( 'LEFT JOIN',
  71                  'user_id=log_user' ),
  72              'page' => array( 'LEFT JOIN',
  73                  array( 'log_namespace=page_namespace',
  74                      'log_title=page_title' ) ) ) );
  75  
  76          $this->addFields( array(
  77              'log_id',
  78              'log_type',
  79              'log_action',
  80              'log_timestamp',
  81              'log_deleted',
  82          ) );
  83  
  84          $this->addFieldsIf( 'page_id', $this->fld_ids );
  85          // log_page is the page_id saved at log time, whereas page_id is from a
  86          // join at query time.  This leads to different results in various
  87          // scenarios, e.g. deletion, recreation.
  88          $this->addFieldsIf( 'log_page', $this->fld_ids );
  89          $this->addFieldsIf( array( 'log_user', 'log_user_text', 'user_name' ), $this->fld_user );
  90          $this->addFieldsIf( 'log_user', $this->fld_userid );
  91          $this->addFieldsIf(
  92              array( 'log_namespace', 'log_title' ),
  93              $this->fld_title || $this->fld_parsedcomment
  94          );
  95          $this->addFieldsIf( 'log_comment', $this->fld_comment || $this->fld_parsedcomment );
  96          $this->addFieldsIf( 'log_params', $this->fld_details );
  97  
  98          if ( $this->fld_tags ) {
  99              $this->addTables( 'tag_summary' );
 100              $this->addJoinConds( array( 'tag_summary' => array( 'LEFT JOIN', 'log_id=ts_log_id' ) ) );
 101              $this->addFields( 'ts_tags' );
 102          }
 103  
 104          if ( !is_null( $params['tag'] ) ) {
 105              $this->addTables( 'change_tag' );
 106              $this->addJoinConds( array( 'change_tag' => array( 'INNER JOIN',
 107                  array( 'log_id=ct_log_id' ) ) ) );
 108              $this->addWhereFld( 'ct_tag', $params['tag'] );
 109          }
 110  
 111          if ( !is_null( $params['action'] ) ) {
 112              // Do validation of action param, list of allowed actions can contains wildcards
 113              // Allow the param, when the actions is in the list or a wildcard version is listed.
 114              $logAction = $params['action'];
 115              if ( strpos( $logAction, '/' ) === false ) {
 116                  // all items in the list have a slash
 117                  $valid = false;
 118              } else {
 119                  $logActions = array_flip( $this->getAllowedLogActions() );
 120                  list( $type, $action ) = explode( '/', $logAction, 2 );
 121                  $valid = isset( $logActions[$logAction] ) || isset( $logActions[$type . '/*'] );
 122              }
 123  
 124              if ( !$valid ) {
 125                  $valueName = $this->encodeParamName( 'action' );
 126                  $this->dieUsage(
 127                      "Unrecognized value for parameter '$valueName': {$logAction}",
 128                      "unknown_$valueName"
 129                  );
 130              }
 131  
 132              $this->addWhereFld( 'log_type', $type );
 133              $this->addWhereFld( 'log_action', $action );
 134          } elseif ( !is_null( $params['type'] ) ) {
 135              $this->addWhereFld( 'log_type', $params['type'] );
 136          }
 137  
 138          $this->addTimestampWhereRange(
 139              'log_timestamp',
 140              $params['dir'],
 141              $params['start'],
 142              $params['end']
 143          );
 144          // Include in ORDER BY for uniqueness
 145          $this->addWhereRange( 'log_id', $params['dir'], null, null );
 146  
 147          if ( !is_null( $params['continue'] ) ) {
 148              $cont = explode( '|', $params['continue'] );
 149              $this->dieContinueUsageIf( count( $cont ) != 2 );
 150              $op = ( $params['dir'] === 'newer' ? '>' : '<' );
 151              $continueTimestamp = $db->addQuotes( $db->timestamp( $cont[0] ) );
 152              $continueId = (int)$cont[1];
 153              $this->dieContinueUsageIf( $continueId != $cont[1] );
 154              $this->addWhere( "log_timestamp $op $continueTimestamp OR " .
 155                  "(log_timestamp = $continueTimestamp AND " .
 156                  "log_id $op= $continueId)"
 157              );
 158          }
 159  
 160          $limit = $params['limit'];
 161          $this->addOption( 'LIMIT', $limit + 1 );
 162  
 163          $user = $params['user'];
 164          if ( !is_null( $user ) ) {
 165              $userid = User::idFromName( $user );
 166              if ( $userid ) {
 167                  $this->addWhereFld( 'log_user', $userid );
 168              } else {
 169                  $this->addWhereFld( 'log_user_text', IP::sanitizeIP( $user ) );
 170              }
 171          }
 172  
 173          $title = $params['title'];
 174          if ( !is_null( $title ) ) {
 175              $titleObj = Title::newFromText( $title );
 176              if ( is_null( $titleObj ) ) {
 177                  $this->dieUsage( "Bad title value '$title'", 'param_title' );
 178              }
 179              $this->addWhereFld( 'log_namespace', $titleObj->getNamespace() );
 180              $this->addWhereFld( 'log_title', $titleObj->getDBkey() );
 181          }
 182  
 183          if ( $params['namespace'] !== null ) {
 184              $this->addWhereFld( 'log_namespace', $params['namespace'] );
 185          }
 186  
 187          $prefix = $params['prefix'];
 188  
 189          if ( !is_null( $prefix ) ) {
 190              if ( $this->getConfig()->get( 'MiserMode' ) ) {
 191                  $this->dieUsage( 'Prefix search disabled in Miser Mode', 'prefixsearchdisabled' );
 192              }
 193  
 194              $title = Title::newFromText( $prefix );
 195              if ( is_null( $title ) ) {
 196                  $this->dieUsage( "Bad title value '$prefix'", 'param_prefix' );
 197              }
 198              $this->addWhereFld( 'log_namespace', $title->getNamespace() );
 199              $this->addWhere( 'log_title ' . $db->buildLike( $title->getDBkey(), $db->anyString() ) );
 200          }
 201  
 202          // Paranoia: avoid brute force searches (bug 17342)
 203          $hideActions = $params['namespace'] !== null || !is_null( $title ) || !is_null( $params['action'] );
 204          if ( $hideActions || !is_null( $user ) ) {
 205              if ( !$this->getUser()->isAllowed( 'deletedhistory' ) ) {
 206                  $titleBits = LogPage::DELETED_ACTION;
 207                  $userBits = LogPage::DELETED_USER;
 208              } elseif ( !$this->getUser()->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
 209                  $titleBits = LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED;
 210                  $userBits = LogPage::DELETED_USER | LogPage::DELETED_RESTRICTED;
 211              } else {
 212                  $titleBits = 0;
 213                  $userBits = 0;
 214              }
 215              if ( $hideActions && $titleBits ) {
 216                  $this->addWhere( $db->bitAnd( 'log_deleted', $titleBits ) . " != $titleBits" );
 217              }
 218              if ( !is_null( $user ) && $userBits ) {
 219                  $this->addWhere( $db->bitAnd( 'log_deleted', $userBits ) . " != $userBits" );
 220              }
 221          }
 222  
 223          $count = 0;
 224          $res = $this->select( __METHOD__ );
 225          $result = $this->getResult();
 226          foreach ( $res as $row ) {
 227              if ( ++$count > $limit ) {
 228                  // We've reached the one extra which shows that there are
 229                  // additional pages to be had. Stop here...
 230                  $this->setContinueEnumParameter( 'continue', "$row->log_timestamp|$row->log_id" );
 231                  break;
 232              }
 233  
 234              $vals = $this->extractRowInfo( $row );
 235              if ( !$vals ) {
 236                  continue;
 237              }
 238              $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $vals );
 239              if ( !$fit ) {
 240                  $this->setContinueEnumParameter( 'continue', "$row->log_timestamp|$row->log_id" );
 241                  break;
 242              }
 243          }
 244          $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'item' );
 245      }
 246  
 247      /**
 248       * @param ApiResult $result
 249       * @param array $vals
 250       * @param string $params
 251       * @param string $type
 252       * @param string $action
 253       * @param string $ts
 254       * @param bool $legacy
 255       * @return array
 256       */
 257  	public static function addLogParams( $result, &$vals, $params, $type,
 258          $action, $ts, $legacy = false
 259      ) {
 260          switch ( $type ) {
 261              case 'move':
 262                  if ( $legacy ) {
 263                      $targetKey = 0;
 264                      $noredirKey = 1;
 265                  } else {
 266                      $targetKey = '4::target';
 267                      $noredirKey = '5::noredir';
 268                  }
 269  
 270                  if ( isset( $params[$targetKey] ) ) {
 271                      $title = Title::newFromText( $params[$targetKey] );
 272                      if ( $title ) {
 273                          $vals2 = array();
 274                          ApiQueryBase::addTitleInfo( $vals2, $title, 'new_' );
 275                          $vals[$type] = $vals2;
 276                      }
 277                  }
 278                  if ( isset( $params[$noredirKey] ) && $params[$noredirKey] ) {
 279                      $vals[$type]['suppressedredirect'] = '';
 280                  }
 281                  $params = null;
 282                  break;
 283              case 'patrol':
 284                  if ( $legacy ) {
 285                      $cur = 0;
 286                      $prev = 1;
 287                      $auto = 2;
 288                  } else {
 289                      $cur = '4::curid';
 290                      $prev = '5::previd';
 291                      $auto = '6::auto';
 292                  }
 293                  $vals2 = array();
 294                  $vals2['cur'] = $params[$cur];
 295                  $vals2['prev'] = $params[$prev];
 296                  $vals2['auto'] = $params[$auto];
 297                  $vals[$type] = $vals2;
 298                  $params = null;
 299                  break;
 300              case 'rights':
 301                  $vals2 = array();
 302                  if ( $legacy ) {
 303                      list( $vals2['old'], $vals2['new'] ) = $params;
 304                  } else {
 305                      $vals2['new'] = implode( ', ', $params['5::newgroups'] );
 306                      $vals2['old'] = implode( ', ', $params['4::oldgroups'] );
 307                  }
 308                  $vals[$type] = $vals2;
 309                  $params = null;
 310                  break;
 311              case 'block':
 312                  if ( $action == 'unblock' ) {
 313                      break;
 314                  }
 315                  $vals2 = array();
 316                  list( $vals2['duration'], $vals2['flags'] ) = $params;
 317  
 318                  // Indefinite blocks have no expiry time
 319                  if ( SpecialBlock::parseExpiryInput( $params[0] ) !== wfGetDB( DB_SLAVE )->getInfinity() ) {
 320                      $vals2['expiry'] = wfTimestamp( TS_ISO_8601,
 321                          strtotime( $params[0], wfTimestamp( TS_UNIX, $ts ) ) );
 322                  }
 323                  $vals[$type] = $vals2;
 324                  $params = null;
 325                  break;
 326              case 'upload':
 327                  if ( isset( $params['img_timestamp'] ) ) {
 328                      $params['img_timestamp'] = wfTimestamp( TS_ISO_8601, $params['img_timestamp'] );
 329                  }
 330                  break;
 331          }
 332          if ( !is_null( $params ) ) {
 333              $logParams = array();
 334              // Keys like "4::paramname" can't be used for output so we change them to "paramname"
 335              foreach ( $params as $key => $value ) {
 336                  if ( strpos( $key, ':' ) === false ) {
 337                      $logParams[$key] = $value;
 338                      continue;
 339                  }
 340                  $logParam = explode( ':', $key, 3 );
 341                  $logParams[$logParam[2]] = $value;
 342              }
 343              $result->setIndexedTagName( $logParams, 'param' );
 344              $result->setIndexedTagName_recursive( $logParams, 'param' );
 345              $vals = array_merge( $vals, $logParams );
 346          }
 347  
 348          return $vals;
 349      }
 350  
 351  	private function extractRowInfo( $row ) {
 352          $logEntry = DatabaseLogEntry::newFromRow( $row );
 353          $vals = array();
 354          $anyHidden = false;
 355          $user = $this->getUser();
 356  
 357          if ( $this->fld_ids ) {
 358              $vals['logid'] = intval( $row->log_id );
 359          }
 360  
 361          if ( $this->fld_title || $this->fld_parsedcomment ) {
 362              $title = Title::makeTitle( $row->log_namespace, $row->log_title );
 363          }
 364  
 365          if ( $this->fld_title || $this->fld_ids || $this->fld_type
 366              || $this->fld_details && $row->log_params !== ''
 367          ) {
 368              if ( LogEventsList::isDeleted( $row, LogPage::DELETED_ACTION ) ) {
 369                  $vals['actionhidden'] = '';
 370                  $anyHidden = true;
 371              }
 372              if ( LogEventsList::userCan( $row, LogPage::DELETED_ACTION, $user ) ) {
 373  
 374                  if ( $this->fld_type ) {
 375                      $vals['action'] = $row->log_action;
 376                  }
 377                  if ( $this->fld_title ) {
 378                      ApiQueryBase::addTitleInfo( $vals, $title );
 379                  }
 380                  if ( $this->fld_ids ) {
 381                      $vals['pageid'] = intval( $row->page_id );
 382                      $vals['logpage'] = intval( $row->log_page );
 383                  }
 384                  if ( $this->fld_details && $row->log_params !== '' ) {
 385                      self::addLogParams(
 386                          $this->getResult(),
 387                          $vals,
 388                          $logEntry->getParameters(),
 389                          $logEntry->getType(),
 390                          $logEntry->getSubtype(),
 391                          $logEntry->getTimestamp(),
 392                          $logEntry->isLegacy()
 393                      );
 394                  }
 395              }
 396          }
 397  
 398          if ( $this->fld_type ) {
 399              $vals['type'] = $row->log_type;
 400          }
 401  
 402          if ( $this->fld_user || $this->fld_userid ) {
 403              if ( LogEventsList::isDeleted( $row, LogPage::DELETED_USER ) ) {
 404                  $vals['userhidden'] = '';
 405                  $anyHidden = true;
 406              }
 407              if ( LogEventsList::userCan( $row, LogPage::DELETED_USER, $user ) ) {
 408                  if ( $this->fld_user ) {
 409                      $vals['user'] = $row->user_name === null ? $row->log_user_text : $row->user_name;
 410                  }
 411                  if ( $this->fld_userid ) {
 412                      $vals['userid'] = intval( $row->log_user );
 413                  }
 414  
 415                  if ( !$row->log_user ) {
 416                      $vals['anon'] = '';
 417                  }
 418              }
 419          }
 420          if ( $this->fld_timestamp ) {
 421              $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $row->log_timestamp );
 422          }
 423  
 424          if ( ( $this->fld_comment || $this->fld_parsedcomment ) && isset( $row->log_comment ) ) {
 425              if ( LogEventsList::isDeleted( $row, LogPage::DELETED_COMMENT ) ) {
 426                  $vals['commenthidden'] = '';
 427                  $anyHidden = true;
 428              }
 429              if ( LogEventsList::userCan( $row, LogPage::DELETED_COMMENT, $user ) ) {
 430                  if ( $this->fld_comment ) {
 431                      $vals['comment'] = $row->log_comment;
 432                  }
 433  
 434                  if ( $this->fld_parsedcomment ) {
 435                      $vals['parsedcomment'] = Linker::formatComment( $row->log_comment, $title );
 436                  }
 437              }
 438          }
 439  
 440          if ( $this->fld_tags ) {
 441              if ( $row->ts_tags ) {
 442                  $tags = explode( ',', $row->ts_tags );
 443                  $this->getResult()->setIndexedTagName( $tags, 'tag' );
 444                  $vals['tags'] = $tags;
 445              } else {
 446                  $vals['tags'] = array();
 447              }
 448          }
 449  
 450          if ( $anyHidden && LogEventsList::isDeleted( $row, LogPage::DELETED_RESTRICTED ) ) {
 451              $vals['suppressed'] = '';
 452          }
 453  
 454          return $vals;
 455      }
 456  
 457      /**
 458       * @return array
 459       */
 460  	private function getAllowedLogActions() {
 461          $config = $this->getConfig();
 462          return array_keys( array_merge( $config->get( 'LogActions' ), $config->get( 'LogActionsHandlers' ) ) );
 463      }
 464  
 465  	public function getCacheMode( $params ) {
 466          if ( $this->userCanSeeRevDel() ) {
 467              return 'private';
 468          }
 469          if ( !is_null( $params['prop'] ) && in_array( 'parsedcomment', $params['prop'] ) ) {
 470              // formatComment() calls wfMessage() among other things
 471              return 'anon-public-user-private';
 472          } elseif ( LogEventsList::getExcludeClause( $this->getDB(), 'user', $this->getUser() )
 473              === LogEventsList::getExcludeClause( $this->getDB(), 'public' )
 474          ) { // Output can only contain public data.
 475              return 'public';
 476          } else {
 477              return 'anon-public-user-private';
 478          }
 479      }
 480  
 481  	public function getAllowedParams( $flags = 0 ) {
 482          $config = $this->getConfig();
 483          return array(
 484              'prop' => array(
 485                  ApiBase::PARAM_ISMULTI => true,
 486                  ApiBase::PARAM_DFLT => 'ids|title|type|user|timestamp|comment|details',
 487                  ApiBase::PARAM_TYPE => array(
 488                      'ids',
 489                      'title',
 490                      'type',
 491                      'user',
 492                      'userid',
 493                      'timestamp',
 494                      'comment',
 495                      'parsedcomment',
 496                      'details',
 497                      'tags'
 498                  )
 499              ),
 500              'type' => array(
 501                  ApiBase::PARAM_TYPE => $config->get( 'LogTypes' )
 502              ),
 503              'action' => array(
 504                  // validation on request is done in execute()
 505                  ApiBase::PARAM_TYPE => ( $flags & ApiBase::GET_VALUES_FOR_HELP )
 506                      ? $this->getAllowedLogActions()
 507                      : null
 508              ),
 509              'start' => array(
 510                  ApiBase::PARAM_TYPE => 'timestamp'
 511              ),
 512              'end' => array(
 513                  ApiBase::PARAM_TYPE => 'timestamp'
 514              ),
 515              'dir' => array(
 516                  ApiBase::PARAM_DFLT => 'older',
 517                  ApiBase::PARAM_TYPE => array(
 518                      'newer',
 519                      'older'
 520                  )
 521              ),
 522              'user' => null,
 523              'title' => null,
 524              'namespace' => array(
 525                  ApiBase::PARAM_TYPE => 'namespace'
 526              ),
 527              'prefix' => null,
 528              'tag' => null,
 529              'limit' => array(
 530                  ApiBase::PARAM_DFLT => 10,
 531                  ApiBase::PARAM_TYPE => 'limit',
 532                  ApiBase::PARAM_MIN => 1,
 533                  ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
 534                  ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2
 535              ),
 536              'continue' => null,
 537          );
 538      }
 539  
 540  	public function getParamDescription() {
 541          $p = $this->getModulePrefix();
 542  
 543          return array(
 544              'prop' => array(
 545                  'Which properties to get',
 546                  ' ids            - Adds the ID of the log event',
 547                  ' title          - Adds the title of the page for the log event',
 548                  ' type           - Adds the type of log event',
 549                  ' user           - Adds the user responsible for the log event',
 550                  ' userid         - Adds the user ID who was responsible for the log event',
 551                  ' timestamp      - Adds the timestamp for the event',
 552                  ' comment        - Adds the comment of the event',
 553                  ' parsedcomment  - Adds the parsed comment of the event',
 554                  ' details        - Lists additional details about the event',
 555                  ' tags           - Lists tags for the event',
 556              ),
 557              'type' => 'Filter log entries to only this type',
 558              'action' => array(
 559                  "Filter log actions to only this action. Overrides {$p}type",
 560                  "Wildcard actions like 'action/*' allows to specify any string for the asterisk"
 561              ),
 562              'start' => 'The timestamp to start enumerating from',
 563              'end' => 'The timestamp to end enumerating',
 564              'dir' => $this->getDirectionDescription( $p ),
 565              'user' => 'Filter entries to those made by the given user',
 566              'title' => 'Filter entries to those related to a page',
 567              'namespace' => 'Filter entries to those in the given namespace',
 568              'prefix' => 'Filter entries that start with this prefix. Disabled in Miser Mode',
 569              'limit' => 'How many total event entries to return',
 570              'tag' => 'Only list event entries tagged with this tag',
 571              'continue' => 'When more results are available, use this to continue',
 572          );
 573      }
 574  
 575      public function getDescription() {
 576          return 'Get events from logs.';
 577      }
 578  
 579      public function getExamples() {
 580          return array(
 581              'api.php?action=query&list=logevents'
 582          );
 583      }
 584  
 585      public function getHelpUrls() {
 586          return 'https://www.mediawiki.org/wiki/API:Logevents';
 587      }
 588  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1