[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

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

   1  <?php
   2  /**
   3   *
   4   *
   5   * Created on Sep 7, 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   * A query action to enumerate revisions of a given page, or show top revisions
  29   * of multiple pages. Various pieces of information may be shown - flags,
  30   * comments, and the actual wiki markup of the rev. In the enumeration mode,
  31   * ranges of revisions may be requested and filtered.
  32   *
  33   * @ingroup API
  34   */
  35  class ApiQueryRevisions extends ApiQueryBase {
  36  
  37      private $diffto, $difftotext, $expandTemplates, $generateXML, $section,
  38          $token, $parseContent, $contentFormat;
  39  
  40  	public function __construct( ApiQuery $query, $moduleName ) {
  41          parent::__construct( $query, $moduleName, 'rv' );
  42      }
  43  
  44      private $fld_ids = false, $fld_flags = false, $fld_timestamp = false,
  45          $fld_size = false, $fld_sha1 = false, $fld_comment = false,
  46          $fld_parsedcomment = false, $fld_user = false, $fld_userid = false,
  47          $fld_content = false, $fld_tags = false, $fld_contentmodel = false;
  48  
  49      private $tokenFunctions;
  50  
  51      /** @deprecated since 1.24 */
  52  	protected function getTokenFunctions() {
  53          // tokenname => function
  54          // function prototype is func($pageid, $title, $rev)
  55          // should return token or false
  56  
  57          // Don't call the hooks twice
  58          if ( isset( $this->tokenFunctions ) ) {
  59              return $this->tokenFunctions;
  60          }
  61  
  62          // If we're in JSON callback mode, no tokens can be obtained
  63          if ( !is_null( $this->getMain()->getRequest()->getVal( 'callback' ) ) ) {
  64              return array();
  65          }
  66  
  67          $this->tokenFunctions = array(
  68              'rollback' => array( 'ApiQueryRevisions', 'getRollbackToken' )
  69          );
  70          wfRunHooks( 'APIQueryRevisionsTokens', array( &$this->tokenFunctions ) );
  71  
  72          return $this->tokenFunctions;
  73      }
  74  
  75      /**
  76       * @deprecated since 1.24
  77       * @param int $pageid
  78       * @param Title $title
  79       * @param Revision $rev
  80       * @return bool|string
  81       */
  82  	public static function getRollbackToken( $pageid, $title, $rev ) {
  83          global $wgUser;
  84          if ( !$wgUser->isAllowed( 'rollback' ) ) {
  85              return false;
  86          }
  87  
  88          return $wgUser->getEditToken(
  89              array( $title->getPrefixedText(), $rev->getUserText() ) );
  90      }
  91  
  92  	public function execute() {
  93          $params = $this->extractRequestParams( false );
  94  
  95          // If any of those parameters are used, work in 'enumeration' mode.
  96          // Enum mode can only be used when exactly one page is provided.
  97          // Enumerating revisions on multiple pages make it extremely
  98          // difficult to manage continuations and require additional SQL indexes
  99          $enumRevMode = ( !is_null( $params['user'] ) || !is_null( $params['excludeuser'] ) ||
 100              !is_null( $params['limit'] ) || !is_null( $params['startid'] ) ||
 101              !is_null( $params['endid'] ) || $params['dir'] === 'newer' ||
 102              !is_null( $params['start'] ) || !is_null( $params['end'] ) );
 103  
 104          $pageSet = $this->getPageSet();
 105          $pageCount = $pageSet->getGoodTitleCount();
 106          $revCount = $pageSet->getRevisionCount();
 107  
 108          // Optimization -- nothing to do
 109          if ( $revCount === 0 && $pageCount === 0 ) {
 110              return;
 111          }
 112  
 113          if ( $revCount > 0 && $enumRevMode ) {
 114              $this->dieUsage(
 115                  'The revids= parameter may not be used with the list options ' .
 116                      '(limit, startid, endid, dirNewer, start, end).',
 117                  'revids'
 118              );
 119          }
 120  
 121          if ( $pageCount > 1 && $enumRevMode ) {
 122              $this->dieUsage(
 123                  'titles, pageids or a generator was used to supply multiple pages, ' .
 124                      'but the limit, startid, endid, dirNewer, user, excludeuser, start ' .
 125                      'and end parameters may only be used on a single page.',
 126                  'multpages'
 127              );
 128          }
 129  
 130          if ( !is_null( $params['difftotext'] ) ) {
 131              $this->difftotext = $params['difftotext'];
 132          } elseif ( !is_null( $params['diffto'] ) ) {
 133              if ( $params['diffto'] == 'cur' ) {
 134                  $params['diffto'] = 0;
 135              }
 136              if ( ( !ctype_digit( $params['diffto'] ) || $params['diffto'] < 0 )
 137                  && $params['diffto'] != 'prev' && $params['diffto'] != 'next'
 138              ) {
 139                  $this->dieUsage(
 140                      'rvdiffto must be set to a non-negative number, "prev", "next" or "cur"',
 141                      'diffto'
 142                  );
 143              }
 144              // Check whether the revision exists and is readable,
 145              // DifferenceEngine returns a rather ambiguous empty
 146              // string if that's not the case
 147              if ( $params['diffto'] != 0 ) {
 148                  $difftoRev = Revision::newFromID( $params['diffto'] );
 149                  if ( !$difftoRev ) {
 150                      $this->dieUsageMsg( array( 'nosuchrevid', $params['diffto'] ) );
 151                  }
 152                  if ( !$difftoRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) {
 153                      $this->setWarning( "Couldn't diff to r{$difftoRev->getID()}: content is hidden" );
 154                      $params['diffto'] = null;
 155                  }
 156              }
 157              $this->diffto = $params['diffto'];
 158          }
 159  
 160          $db = $this->getDB();
 161          $this->addTables( 'page' );
 162          $this->addFields( Revision::selectFields() );
 163          $this->addWhere( 'page_id = rev_page' );
 164  
 165          $prop = array_flip( $params['prop'] );
 166  
 167          // Optional fields
 168          $this->fld_ids = isset( $prop['ids'] );
 169          // $this->addFieldsIf('rev_text_id', $this->fld_ids); // should this be exposed?
 170          $this->fld_flags = isset( $prop['flags'] );
 171          $this->fld_timestamp = isset( $prop['timestamp'] );
 172          $this->fld_comment = isset( $prop['comment'] );
 173          $this->fld_parsedcomment = isset( $prop['parsedcomment'] );
 174          $this->fld_size = isset( $prop['size'] );
 175          $this->fld_sha1 = isset( $prop['sha1'] );
 176          $this->fld_contentmodel = isset( $prop['contentmodel'] );
 177          $this->fld_userid = isset( $prop['userid'] );
 178          $this->fld_user = isset( $prop['user'] );
 179          $this->token = $params['token'];
 180  
 181          if ( !empty( $params['contentformat'] ) ) {
 182              $this->contentFormat = $params['contentformat'];
 183          }
 184  
 185          $userMax = ( $this->fld_content ? ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1 );
 186          $botMax = ( $this->fld_content ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2 );
 187          $limit = $params['limit'];
 188          if ( $limit == 'max' ) {
 189              $limit = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
 190              $this->getResult()->setParsedLimit( $this->getModuleName(), $limit );
 191          }
 192  
 193          if ( !is_null( $this->token ) || $pageCount > 0 ) {
 194              $this->addFields( Revision::selectPageFields() );
 195          }
 196  
 197          if ( isset( $prop['tags'] ) ) {
 198              $this->fld_tags = true;
 199              $this->addTables( 'tag_summary' );
 200              $this->addJoinConds(
 201                  array( 'tag_summary' => array( 'LEFT JOIN', array( 'rev_id=ts_rev_id' ) ) )
 202              );
 203              $this->addFields( 'ts_tags' );
 204          }
 205  
 206          if ( !is_null( $params['tag'] ) ) {
 207              $this->addTables( 'change_tag' );
 208              $this->addJoinConds(
 209                  array( 'change_tag' => array( 'INNER JOIN', array( 'rev_id=ct_rev_id' ) ) )
 210              );
 211              $this->addWhereFld( 'ct_tag', $params['tag'] );
 212          }
 213  
 214          if ( isset( $prop['content'] ) || !is_null( $this->diffto ) || !is_null( $this->difftotext ) ) {
 215              // For each page we will request, the user must have read rights for that page
 216              $user = $this->getUser();
 217              /** @var $title Title */
 218              foreach ( $pageSet->getGoodTitles() as $title ) {
 219                  if ( !$title->userCan( 'read', $user ) ) {
 220                      $this->dieUsage(
 221                          'The current user is not allowed to read ' . $title->getPrefixedText(),
 222                          'accessdenied' );
 223                  }
 224              }
 225  
 226              $this->addTables( 'text' );
 227              $this->addWhere( 'rev_text_id=old_id' );
 228              $this->addFields( 'old_id' );
 229              $this->addFields( Revision::selectTextFields() );
 230  
 231              $this->fld_content = isset( $prop['content'] );
 232  
 233              $this->expandTemplates = $params['expandtemplates'];
 234              $this->generateXML = $params['generatexml'];
 235              $this->parseContent = $params['parse'];
 236              if ( $this->parseContent ) {
 237                  // Must manually initialize unset limit
 238                  if ( is_null( $limit ) ) {
 239                      $limit = 1;
 240                  }
 241                  // We are only going to parse 1 revision per request
 242                  $this->validateLimit( 'limit', $limit, 1, 1, 1 );
 243              }
 244              if ( isset( $params['section'] ) ) {
 245                  $this->section = $params['section'];
 246              } else {
 247                  $this->section = false;
 248              }
 249          }
 250  
 251          // add user name, if needed
 252          if ( $this->fld_user ) {
 253              $this->addTables( 'user' );
 254              $this->addJoinConds( array( 'user' => Revision::userJoinCond() ) );
 255              $this->addFields( Revision::selectUserFields() );
 256          }
 257  
 258          // Bug 24166 - API error when using rvprop=tags
 259          $this->addTables( 'revision' );
 260  
 261          if ( $enumRevMode ) {
 262              // This is mostly to prevent parameter errors (and optimize SQL?)
 263              if ( !is_null( $params['startid'] ) && !is_null( $params['start'] ) ) {
 264                  $this->dieUsage( 'start and startid cannot be used together', 'badparams' );
 265              }
 266  
 267              if ( !is_null( $params['endid'] ) && !is_null( $params['end'] ) ) {
 268                  $this->dieUsage( 'end and endid cannot be used together', 'badparams' );
 269              }
 270  
 271              if ( !is_null( $params['user'] ) && !is_null( $params['excludeuser'] ) ) {
 272                  $this->dieUsage( 'user and excludeuser cannot be used together', 'badparams' );
 273              }
 274  
 275              // Continuing effectively uses startid. But we can't use rvstartid
 276              // directly, because there is no way to tell the client to ''not''
 277              // send rvstart if it sent it in the original query. So instead we
 278              // send the continuation startid as rvcontinue, and ignore both
 279              // rvstart and rvstartid when that is supplied.
 280              if ( !is_null( $params['continue'] ) ) {
 281                  $params['startid'] = $params['continue'];
 282                  $params['start'] = null;
 283              }
 284  
 285              // This code makes an assumption that sorting by rev_id and rev_timestamp produces
 286              // the same result. This way users may request revisions starting at a given time,
 287              // but to page through results use the rev_id returned after each page.
 288              // Switching to rev_id removes the potential problem of having more than
 289              // one row with the same timestamp for the same page.
 290              // The order needs to be the same as start parameter to avoid SQL filesort.
 291              if ( is_null( $params['startid'] ) && is_null( $params['endid'] ) ) {
 292                  $this->addTimestampWhereRange( 'rev_timestamp', $params['dir'],
 293                      $params['start'], $params['end'] );
 294              } else {
 295                  $this->addWhereRange( 'rev_id', $params['dir'],
 296                      $params['startid'], $params['endid'] );
 297                  // One of start and end can be set
 298                  // If neither is set, this does nothing
 299                  $this->addTimestampWhereRange( 'rev_timestamp', $params['dir'],
 300                      $params['start'], $params['end'], false );
 301              }
 302  
 303              // must manually initialize unset limit
 304              if ( is_null( $limit ) ) {
 305                  $limit = 10;
 306              }
 307              $this->validateLimit( 'limit', $limit, 1, $userMax, $botMax );
 308  
 309              // There is only one ID, use it
 310              $ids = array_keys( $pageSet->getGoodTitles() );
 311              $this->addWhereFld( 'rev_page', reset( $ids ) );
 312  
 313              if ( !is_null( $params['user'] ) ) {
 314                  $this->addWhereFld( 'rev_user_text', $params['user'] );
 315              } elseif ( !is_null( $params['excludeuser'] ) ) {
 316                  $this->addWhere( 'rev_user_text != ' .
 317                      $db->addQuotes( $params['excludeuser'] ) );
 318              }
 319              if ( !is_null( $params['user'] ) || !is_null( $params['excludeuser'] ) ) {
 320                  // Paranoia: avoid brute force searches (bug 17342)
 321                  if ( !$this->getUser()->isAllowed( 'deletedhistory' ) ) {
 322                      $bitmask = Revision::DELETED_USER;
 323                  } elseif ( !$this->getUser()->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
 324                      $bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED;
 325                  } else {
 326                      $bitmask = 0;
 327                  }
 328                  if ( $bitmask ) {
 329                      $this->addWhere( $db->bitAnd( 'rev_deleted', $bitmask ) . " != $bitmask" );
 330                  }
 331              }
 332          } elseif ( $revCount > 0 ) {
 333              $max = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
 334              $revs = $pageSet->getRevisionIDs();
 335              if ( self::truncateArray( $revs, $max ) ) {
 336                  $this->setWarning( "Too many values supplied for parameter 'revids': the limit is $max" );
 337              }
 338  
 339              // Get all revision IDs
 340              $this->addWhereFld( 'rev_id', array_keys( $revs ) );
 341  
 342              if ( !is_null( $params['continue'] ) ) {
 343                  $this->addWhere( 'rev_id >= ' . intval( $params['continue'] ) );
 344              }
 345              $this->addOption( 'ORDER BY', 'rev_id' );
 346  
 347              // assumption testing -- we should never get more then $revCount rows.
 348              $limit = $revCount;
 349          } elseif ( $pageCount > 0 ) {
 350              $max = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
 351              $titles = $pageSet->getGoodTitles();
 352              if ( self::truncateArray( $titles, $max ) ) {
 353                  $this->setWarning( "Too many values supplied for parameter 'titles': the limit is $max" );
 354              }
 355  
 356              // When working in multi-page non-enumeration mode,
 357              // limit to the latest revision only
 358              $this->addWhere( 'page_id=rev_page' );
 359              $this->addWhere( 'page_latest=rev_id' );
 360  
 361              // Get all page IDs
 362              $this->addWhereFld( 'page_id', array_keys( $titles ) );
 363              // Every time someone relies on equality propagation, god kills a kitten :)
 364              $this->addWhereFld( 'rev_page', array_keys( $titles ) );
 365  
 366              if ( !is_null( $params['continue'] ) ) {
 367                  $cont = explode( '|', $params['continue'] );
 368                  $this->dieContinueUsageIf( count( $cont ) != 2 );
 369                  $pageid = intval( $cont[0] );
 370                  $revid = intval( $cont[1] );
 371                  $this->addWhere(
 372                      "rev_page > $pageid OR " .
 373                      "(rev_page = $pageid AND " .
 374                      "rev_id >= $revid)"
 375                  );
 376              }
 377              $this->addOption( 'ORDER BY', array(
 378                  'rev_page',
 379                  'rev_id'
 380              ) );
 381  
 382              // assumption testing -- we should never get more then $pageCount rows.
 383              $limit = $pageCount;
 384          } else {
 385              ApiBase::dieDebug( __METHOD__, 'param validation?' );
 386          }
 387  
 388          $this->addOption( 'LIMIT', $limit + 1 );
 389  
 390          $count = 0;
 391          $res = $this->select( __METHOD__ );
 392  
 393          foreach ( $res as $row ) {
 394              if ( ++$count > $limit ) {
 395                  // We've reached the one extra which shows that there are
 396                  // additional pages to be had. Stop here...
 397                  if ( !$enumRevMode ) {
 398                      ApiBase::dieDebug( __METHOD__, 'Got more rows then expected' ); // bug report
 399                  }
 400                  $this->setContinueEnumParameter( 'continue', intval( $row->rev_id ) );
 401                  break;
 402              }
 403  
 404              $fit = $this->addPageSubItem( $row->rev_page, $this->extractRowInfo( $row ), 'rev' );
 405              if ( !$fit ) {
 406                  if ( $enumRevMode ) {
 407                      $this->setContinueEnumParameter( 'continue', intval( $row->rev_id ) );
 408                  } elseif ( $revCount > 0 ) {
 409                      $this->setContinueEnumParameter( 'continue', intval( $row->rev_id ) );
 410                  } else {
 411                      $this->setContinueEnumParameter( 'continue', intval( $row->rev_page ) .
 412                          '|' . intval( $row->rev_id ) );
 413                  }
 414                  break;
 415              }
 416          }
 417      }
 418  
 419  	private function extractRowInfo( $row ) {
 420          $revision = new Revision( $row );
 421          $title = $revision->getTitle();
 422          $user = $this->getUser();
 423          $vals = array();
 424          $anyHidden = false;
 425  
 426          if ( $this->fld_ids ) {
 427              $vals['revid'] = intval( $revision->getId() );
 428              // $vals['oldid'] = intval( $row->rev_text_id ); // todo: should this be exposed?
 429              if ( !is_null( $revision->getParentId() ) ) {
 430                  $vals['parentid'] = intval( $revision->getParentId() );
 431              }
 432          }
 433  
 434          if ( $this->fld_flags && $revision->isMinor() ) {
 435              $vals['minor'] = '';
 436          }
 437  
 438          if ( $this->fld_user || $this->fld_userid ) {
 439              if ( $revision->isDeleted( Revision::DELETED_USER ) ) {
 440                  $vals['userhidden'] = '';
 441                  $anyHidden = true;
 442              }
 443              if ( $revision->userCan( Revision::DELETED_USER, $user ) ) {
 444                  if ( $this->fld_user ) {
 445                      $vals['user'] = $revision->getRawUserText();
 446                  }
 447                  $userid = $revision->getRawUser();
 448                  if ( !$userid ) {
 449                      $vals['anon'] = '';
 450                  }
 451  
 452                  if ( $this->fld_userid ) {
 453                      $vals['userid'] = $userid;
 454                  }
 455              }
 456          }
 457  
 458          if ( $this->fld_timestamp ) {
 459              $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $revision->getTimestamp() );
 460          }
 461  
 462          if ( $this->fld_size ) {
 463              if ( !is_null( $revision->getSize() ) ) {
 464                  $vals['size'] = intval( $revision->getSize() );
 465              } else {
 466                  $vals['size'] = 0;
 467              }
 468          }
 469  
 470          if ( $this->fld_sha1 ) {
 471              if ( $revision->isDeleted( Revision::DELETED_TEXT ) ) {
 472                  $vals['sha1hidden'] = '';
 473                  $anyHidden = true;
 474              }
 475              if ( $revision->userCan( Revision::DELETED_TEXT, $user ) ) {
 476                  if ( $revision->getSha1() != '' ) {
 477                      $vals['sha1'] = wfBaseConvert( $revision->getSha1(), 36, 16, 40 );
 478                  } else {
 479                      $vals['sha1'] = '';
 480                  }
 481              }
 482          }
 483  
 484          if ( $this->fld_contentmodel ) {
 485              $vals['contentmodel'] = $revision->getContentModel();
 486          }
 487  
 488          if ( $this->fld_comment || $this->fld_parsedcomment ) {
 489              if ( $revision->isDeleted( Revision::DELETED_COMMENT ) ) {
 490                  $vals['commenthidden'] = '';
 491                  $anyHidden = true;
 492              }
 493              if ( $revision->userCan( Revision::DELETED_COMMENT, $user ) ) {
 494                  $comment = $revision->getRawComment();
 495  
 496                  if ( $this->fld_comment ) {
 497                      $vals['comment'] = $comment;
 498                  }
 499  
 500                  if ( $this->fld_parsedcomment ) {
 501                      $vals['parsedcomment'] = Linker::formatComment( $comment, $title );
 502                  }
 503              }
 504          }
 505  
 506          if ( $this->fld_tags ) {
 507              if ( $row->ts_tags ) {
 508                  $tags = explode( ',', $row->ts_tags );
 509                  $this->getResult()->setIndexedTagName( $tags, 'tag' );
 510                  $vals['tags'] = $tags;
 511              } else {
 512                  $vals['tags'] = array();
 513              }
 514          }
 515  
 516          if ( !is_null( $this->token ) ) {
 517              $tokenFunctions = $this->getTokenFunctions();
 518              foreach ( $this->token as $t ) {
 519                  $val = call_user_func( $tokenFunctions[$t], $title->getArticleID(), $title, $revision );
 520                  if ( $val === false ) {
 521                      $this->setWarning( "Action '$t' is not allowed for the current user" );
 522                  } else {
 523                      $vals[$t . 'token'] = $val;
 524                  }
 525              }
 526          }
 527  
 528          $content = null;
 529          global $wgParser;
 530          if ( $this->fld_content || !is_null( $this->diffto ) || !is_null( $this->difftotext ) ) {
 531              $content = $revision->getContent( Revision::FOR_THIS_USER, $this->getUser() );
 532              // Expand templates after getting section content because
 533              // template-added sections don't count and Parser::preprocess()
 534              // will have less input
 535              if ( $content && $this->section !== false ) {
 536                  $content = $content->getSection( $this->section, false );
 537                  if ( !$content ) {
 538                      $this->dieUsage(
 539                          "There is no section {$this->section} in r" . $revision->getId(),
 540                          'nosuchsection'
 541                      );
 542                  }
 543              }
 544              if ( $revision->isDeleted( Revision::DELETED_TEXT ) ) {
 545                  $vals['texthidden'] = '';
 546                  $anyHidden = true;
 547              } elseif ( !$content ) {
 548                  $vals['textmissing'] = '';
 549              }
 550          }
 551          if ( $this->fld_content && $content ) {
 552              $text = null;
 553  
 554              if ( $this->generateXML ) {
 555                  if ( $content->getModel() === CONTENT_MODEL_WIKITEXT ) {
 556                      $t = $content->getNativeData(); # note: don't set $text
 557  
 558                      $wgParser->startExternalParse(
 559                          $title,
 560                          ParserOptions::newFromContext( $this->getContext() ),
 561                          OT_PREPROCESS
 562                      );
 563                      $dom = $wgParser->preprocessToDom( $t );
 564                      if ( is_callable( array( $dom, 'saveXML' ) ) ) {
 565                          $xml = $dom->saveXML();
 566                      } else {
 567                          $xml = $dom->__toString();
 568                      }
 569                      $vals['parsetree'] = $xml;
 570                  } else {
 571                      $this->setWarning( "Conversion to XML is supported for wikitext only, " .
 572                          $title->getPrefixedDBkey() .
 573                          " uses content model " . $content->getModel() );
 574                  }
 575              }
 576  
 577              if ( $this->expandTemplates && !$this->parseContent ) {
 578                  #XXX: implement template expansion for all content types in ContentHandler?
 579                  if ( $content->getModel() === CONTENT_MODEL_WIKITEXT ) {
 580                      $text = $content->getNativeData();
 581  
 582                      $text = $wgParser->preprocess(
 583                          $text,
 584                          $title,
 585                          ParserOptions::newFromContext( $this->getContext() )
 586                      );
 587                  } else {
 588                      $this->setWarning( "Template expansion is supported for wikitext only, " .
 589                          $title->getPrefixedDBkey() .
 590                          " uses content model " . $content->getModel() );
 591  
 592                      $text = false;
 593                  }
 594              }
 595              if ( $this->parseContent ) {
 596                  $po = $content->getParserOutput(
 597                      $title,
 598                      $revision->getId(),
 599                      ParserOptions::newFromContext( $this->getContext() )
 600                  );
 601                  $text = $po->getText();
 602              }
 603  
 604              if ( $text === null ) {
 605                  $format = $this->contentFormat ? $this->contentFormat : $content->getDefaultFormat();
 606                  $model = $content->getModel();
 607  
 608                  if ( !$content->isSupportedFormat( $format ) ) {
 609                      $name = $title->getPrefixedDBkey();
 610  
 611                      $this->dieUsage( "The requested format {$this->contentFormat} is not supported " .
 612                          "for content model $model used by $name", 'badformat' );
 613                  }
 614  
 615                  $text = $content->serialize( $format );
 616  
 617                  // always include format and model.
 618                  // Format is needed to deserialize, model is needed to interpret.
 619                  $vals['contentformat'] = $format;
 620                  $vals['contentmodel'] = $model;
 621              }
 622  
 623              if ( $text !== false ) {
 624                  ApiResult::setContent( $vals, $text );
 625              }
 626          }
 627  
 628          if ( $content && ( !is_null( $this->diffto ) || !is_null( $this->difftotext ) ) ) {
 629              static $n = 0; // Number of uncached diffs we've had
 630  
 631              if ( $n < $this->getConfig()->get( 'APIMaxUncachedDiffs' ) ) {
 632                  $vals['diff'] = array();
 633                  $context = new DerivativeContext( $this->getContext() );
 634                  $context->setTitle( $title );
 635                  $handler = $revision->getContentHandler();
 636  
 637                  if ( !is_null( $this->difftotext ) ) {
 638                      $model = $title->getContentModel();
 639  
 640                      if ( $this->contentFormat
 641                          && !ContentHandler::getForModelID( $model )->isSupportedFormat( $this->contentFormat )
 642                      ) {
 643  
 644                          $name = $title->getPrefixedDBkey();
 645  
 646                          $this->dieUsage( "The requested format {$this->contentFormat} is not supported for " .
 647                              "content model $model used by $name", 'badformat' );
 648                      }
 649  
 650                      $difftocontent = ContentHandler::makeContent(
 651                          $this->difftotext,
 652                          $title,
 653                          $model,
 654                          $this->contentFormat
 655                      );
 656  
 657                      $engine = $handler->createDifferenceEngine( $context );
 658                      $engine->setContent( $content, $difftocontent );
 659                  } else {
 660                      $engine = $handler->createDifferenceEngine( $context, $revision->getID(), $this->diffto );
 661                      $vals['diff']['from'] = $engine->getOldid();
 662                      $vals['diff']['to'] = $engine->getNewid();
 663                  }
 664                  $difftext = $engine->getDiffBody();
 665                  ApiResult::setContent( $vals['diff'], $difftext );
 666                  if ( !$engine->wasCacheHit() ) {
 667                      $n++;
 668                  }
 669              } else {
 670                  $vals['diff']['notcached'] = '';
 671              }
 672          }
 673  
 674          if ( $anyHidden && $revision->isDeleted( Revision::DELETED_RESTRICTED ) ) {
 675              $vals['suppressed'] = '';
 676          }
 677  
 678          return $vals;
 679      }
 680  
 681  	public function getCacheMode( $params ) {
 682          if ( isset( $params['token'] ) ) {
 683              return 'private';
 684          }
 685          if ( $this->userCanSeeRevDel() ) {
 686              return 'private';
 687          }
 688          if ( !is_null( $params['prop'] ) && in_array( 'parsedcomment', $params['prop'] ) ) {
 689              // formatComment() calls wfMessage() among other things
 690              return 'anon-public-user-private';
 691          }
 692  
 693          return 'public';
 694      }
 695  
 696  	public function getAllowedParams() {
 697          return array(
 698              'prop' => array(
 699                  ApiBase::PARAM_ISMULTI => true,
 700                  ApiBase::PARAM_DFLT => 'ids|timestamp|flags|comment|user',
 701                  ApiBase::PARAM_TYPE => array(
 702                      'ids',
 703                      'flags',
 704                      'timestamp',
 705                      'user',
 706                      'userid',
 707                      'size',
 708                      'sha1',
 709                      'contentmodel',
 710                      'comment',
 711                      'parsedcomment',
 712                      'content',
 713                      'tags'
 714                  )
 715              ),
 716              'limit' => array(
 717                  ApiBase::PARAM_TYPE => 'limit',
 718                  ApiBase::PARAM_MIN => 1,
 719                  ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
 720                  ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2
 721              ),
 722              'startid' => array(
 723                  ApiBase::PARAM_TYPE => 'integer'
 724              ),
 725              'endid' => array(
 726                  ApiBase::PARAM_TYPE => 'integer'
 727              ),
 728              'start' => array(
 729                  ApiBase::PARAM_TYPE => 'timestamp'
 730              ),
 731              'end' => array(
 732                  ApiBase::PARAM_TYPE => 'timestamp'
 733              ),
 734              'dir' => array(
 735                  ApiBase::PARAM_DFLT => 'older',
 736                  ApiBase::PARAM_TYPE => array(
 737                      'newer',
 738                      'older'
 739                  )
 740              ),
 741              'user' => array(
 742                  ApiBase::PARAM_TYPE => 'user'
 743              ),
 744              'excludeuser' => array(
 745                  ApiBase::PARAM_TYPE => 'user'
 746              ),
 747              'tag' => null,
 748              'expandtemplates' => false,
 749              'generatexml' => false,
 750              'parse' => false,
 751              'section' => null,
 752              'token' => array(
 753                  ApiBase::PARAM_DEPRECATED => true,
 754                  ApiBase::PARAM_TYPE => array_keys( $this->getTokenFunctions() ),
 755                  ApiBase::PARAM_ISMULTI => true
 756              ),
 757              'continue' => null,
 758              'diffto' => null,
 759              'difftotext' => null,
 760              'contentformat' => array(
 761                  ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
 762                  ApiBase::PARAM_DFLT => null
 763              ),
 764          );
 765      }
 766  
 767  	public function getParamDescription() {
 768          $p = $this->getModulePrefix();
 769  
 770          return array(
 771              'prop' => array(
 772                  'Which properties to get for each revision:',
 773                  ' ids            - The ID of the revision',
 774                  ' flags          - Revision flags (minor)',
 775                  ' timestamp      - The timestamp of the revision',
 776                  ' user           - User that made the revision',
 777                  ' userid         - User id of revision creator',
 778                  ' size           - Length (bytes) of the revision',
 779                  ' sha1           - SHA-1 (base 16) of the revision',
 780                  ' contentmodel   - Content model id',
 781                  ' comment        - Comment by the user for revision',
 782                  ' parsedcomment  - Parsed comment by the user for the revision',
 783                  ' content        - Text of the revision',
 784                  ' tags           - Tags for the revision',
 785              ),
 786              'limit' => 'Limit how many revisions will be returned (enum)',
 787              'startid' => 'From which revision id to start enumeration (enum)',
 788              'endid' => 'Stop revision enumeration on this revid (enum)',
 789              'start' => 'From which revision timestamp to start enumeration (enum)',
 790              'end' => 'Enumerate up to this timestamp (enum)',
 791              'dir' => $this->getDirectionDescription( $p, ' (enum)' ),
 792              'user' => 'Only include revisions made by user (enum)',
 793              'excludeuser' => 'Exclude revisions made by user (enum)',
 794              'expandtemplates' => "Expand templates in revision content (requires {$p}prop=content)",
 795              'generatexml' => "Generate XML parse tree for revision content (requires {$p}prop=content)",
 796              'parse' => array( "Parse revision content (requires {$p}prop=content).",
 797                  'For performance reasons if this option is used, rvlimit is enforced to 1.' ),
 798              'section' => 'Only retrieve the content of this section number',
 799              'token' => 'Which tokens to obtain for each revision',
 800              'continue' => 'When more results are available, use this to continue',
 801              'diffto' => array( 'Revision ID to diff each revision to.',
 802                  'Use "prev", "next" and "cur" for the previous, next and current revision respectively' ),
 803              'difftotext' => array(
 804                  'Text to diff each revision to. Only diffs a limited number of revisions.',
 805                  "Overrides {$p}diffto. If {$p}section is set, only that section will be",
 806                  'diffed against this text',
 807              ),
 808              'tag' => 'Only list revisions tagged with this tag',
 809              'contentformat' => 'Serialization format used for difftotext and expected for output of content',
 810          );
 811      }
 812  
 813  	public function getDescription() {
 814          return array(
 815              'Get revision information.',
 816              'May be used in several ways:',
 817              ' 1) Get data about a set of pages (last revision), by setting titles or pageids parameter.',
 818              ' 2) Get revisions for one given page, by using titles/pageids with start/end/limit params.',
 819              ' 3) Get data about a set of revisions by setting their IDs with revids parameter.',
 820              'All parameters marked as (enum) may only be used with a single page (#2).'
 821          );
 822      }
 823  
 824  	public function getExamples() {
 825          return array(
 826              'Get data with content for the last revision of titles "API" and "Main Page"',
 827              '  api.php?action=query&prop=revisions&titles=API|Main%20Page&' .
 828                  'rvprop=timestamp|user|comment|content',
 829              'Get last 5 revisions of the "Main Page"',
 830              '  api.php?action=query&prop=revisions&titles=Main%20Page&rvlimit=5&' .
 831                  'rvprop=timestamp|user|comment',
 832              'Get first 5 revisions of the "Main Page"',
 833              '  api.php?action=query&prop=revisions&titles=Main%20Page&rvlimit=5&' .
 834                  'rvprop=timestamp|user|comment&rvdir=newer',
 835              'Get first 5 revisions of the "Main Page" made after 2006-05-01',
 836              '  api.php?action=query&prop=revisions&titles=Main%20Page&rvlimit=5&' .
 837                  'rvprop=timestamp|user|comment&rvdir=newer&rvstart=20060501000000',
 838              'Get first 5 revisions of the "Main Page" that were not made made by anonymous user "127.0.0.1"',
 839              '  api.php?action=query&prop=revisions&titles=Main%20Page&rvlimit=5&' .
 840                  'rvprop=timestamp|user|comment&rvexcludeuser=127.0.0.1',
 841              'Get first 5 revisions of the "Main Page" that were made by the user "MediaWiki default"',
 842              '  api.php?action=query&prop=revisions&titles=Main%20Page&rvlimit=5&' .
 843                  'rvprop=timestamp|user|comment&rvuser=MediaWiki%20default',
 844          );
 845      }
 846  
 847  	public function getHelpUrls() {
 848          return 'https://www.mediawiki.org/wiki/API:Properties#revisions_.2F_rv';
 849      }
 850  }


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