[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |