MediaWiki
master
|
00001 <?php 00034 class ApiQueryRevisions extends ApiQueryBase { 00035 00036 private $diffto, $difftotext, $expandTemplates, $generateXML, $section, 00037 $token, $parseContent, $contentFormat; 00038 00039 public function __construct( $query, $moduleName ) { 00040 parent::__construct( $query, $moduleName, 'rv' ); 00041 } 00042 00043 private $fld_ids = false, $fld_flags = false, $fld_timestamp = false, $fld_size = false, $fld_sha1 = false, 00044 $fld_comment = false, $fld_parsedcomment = false, $fld_user = false, $fld_userid = false, 00045 $fld_content = false, $fld_tags = false, $fld_contentmodel = false; 00046 00047 private $tokenFunctions; 00048 00049 protected function getTokenFunctions() { 00050 // tokenname => function 00051 // function prototype is func($pageid, $title, $rev) 00052 // should return token or false 00053 00054 // Don't call the hooks twice 00055 if ( isset( $this->tokenFunctions ) ) { 00056 return $this->tokenFunctions; 00057 } 00058 00059 // If we're in JSON callback mode, no tokens can be obtained 00060 if ( !is_null( $this->getMain()->getRequest()->getVal( 'callback' ) ) ) { 00061 return array(); 00062 } 00063 00064 $this->tokenFunctions = array( 00065 'rollback' => array( 'ApiQueryRevisions', 'getRollbackToken' ) 00066 ); 00067 wfRunHooks( 'APIQueryRevisionsTokens', array( &$this->tokenFunctions ) ); 00068 return $this->tokenFunctions; 00069 } 00070 00077 public static function getRollbackToken( $pageid, $title, $rev ) { 00078 global $wgUser; 00079 if ( !$wgUser->isAllowed( 'rollback' ) ) { 00080 return false; 00081 } 00082 return $wgUser->getEditToken( 00083 array( $title->getPrefixedText(), $rev->getUserText() ) ); 00084 } 00085 00086 public function execute() { 00087 $params = $this->extractRequestParams( false ); 00088 00089 // If any of those parameters are used, work in 'enumeration' mode. 00090 // Enum mode can only be used when exactly one page is provided. 00091 // Enumerating revisions on multiple pages make it extremely 00092 // difficult to manage continuations and require additional SQL indexes 00093 $enumRevMode = ( !is_null( $params['user'] ) || !is_null( $params['excludeuser'] ) || 00094 !is_null( $params['limit'] ) || !is_null( $params['startid'] ) || 00095 !is_null( $params['endid'] ) || $params['dir'] === 'newer' || 00096 !is_null( $params['start'] ) || !is_null( $params['end'] ) ); 00097 00098 00099 $pageSet = $this->getPageSet(); 00100 $pageCount = $pageSet->getGoodTitleCount(); 00101 $revCount = $pageSet->getRevisionCount(); 00102 00103 // Optimization -- nothing to do 00104 if ( $revCount === 0 && $pageCount === 0 ) { 00105 return; 00106 } 00107 00108 if ( $revCount > 0 && $enumRevMode ) { 00109 $this->dieUsage( 'The revids= parameter may not be used with the list options (limit, startid, endid, dirNewer, start, end).', 'revids' ); 00110 } 00111 00112 if ( $pageCount > 1 && $enumRevMode ) { 00113 $this->dieUsage( 'titles, pageids or a generator was used to supply multiple pages, but the limit, startid, endid, dirNewer, user, excludeuser, start and end parameters may only be used on a single page.', 'multpages' ); 00114 } 00115 00116 if ( !is_null( $params['difftotext'] ) ) { 00117 $this->difftotext = $params['difftotext']; 00118 } elseif ( !is_null( $params['diffto'] ) ) { 00119 if ( $params['diffto'] == 'cur' ) { 00120 $params['diffto'] = 0; 00121 } 00122 if ( ( !ctype_digit( $params['diffto'] ) || $params['diffto'] < 0 ) 00123 && $params['diffto'] != 'prev' && $params['diffto'] != 'next' ) { 00124 $this->dieUsage( 'rvdiffto must be set to a non-negative number, "prev", "next" or "cur"', 'diffto' ); 00125 } 00126 // Check whether the revision exists and is readable, 00127 // DifferenceEngine returns a rather ambiguous empty 00128 // string if that's not the case 00129 if ( $params['diffto'] != 0 ) { 00130 $difftoRev = Revision::newFromID( $params['diffto'] ); 00131 if ( !$difftoRev ) { 00132 $this->dieUsageMsg( array( 'nosuchrevid', $params['diffto'] ) ); 00133 } 00134 if ( $difftoRev->isDeleted( Revision::DELETED_TEXT ) ) { 00135 $this->setWarning( "Couldn't diff to r{$difftoRev->getID()}: content is hidden" ); 00136 $params['diffto'] = null; 00137 } 00138 } 00139 $this->diffto = $params['diffto']; 00140 } 00141 00142 $db = $this->getDB(); 00143 $this->addTables( 'page' ); 00144 $this->addFields( Revision::selectFields() ); 00145 $this->addWhere( 'page_id = rev_page' ); 00146 00147 $prop = array_flip( $params['prop'] ); 00148 00149 // Optional fields 00150 $this->fld_ids = isset ( $prop['ids'] ); 00151 // $this->addFieldsIf('rev_text_id', $this->fld_ids); // should this be exposed? 00152 $this->fld_flags = isset ( $prop['flags'] ); 00153 $this->fld_timestamp = isset ( $prop['timestamp'] ); 00154 $this->fld_comment = isset ( $prop['comment'] ); 00155 $this->fld_parsedcomment = isset ( $prop['parsedcomment'] ); 00156 $this->fld_size = isset ( $prop['size'] ); 00157 $this->fld_sha1 = isset ( $prop['sha1'] ); 00158 $this->fld_contentmodel = isset ( $prop['contentmodel'] ); 00159 $this->fld_userid = isset( $prop['userid'] ); 00160 $this->fld_user = isset ( $prop['user'] ); 00161 $this->token = $params['token']; 00162 00163 if ( !empty( $params['contentformat'] ) ) { 00164 $this->contentFormat = $params['contentformat']; 00165 } 00166 00167 // Possible indexes used 00168 $index = array(); 00169 00170 $userMax = ( $this->fld_content ? ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1 ); 00171 $botMax = ( $this->fld_content ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2 ); 00172 $limit = $params['limit']; 00173 if ( $limit == 'max' ) { 00174 $limit = $this->getMain()->canApiHighLimits() ? $botMax : $userMax; 00175 $this->getResult()->setParsedLimit( $this->getModuleName(), $limit ); 00176 } 00177 00178 if ( !is_null( $this->token ) || $pageCount > 0 ) { 00179 $this->addFields( Revision::selectPageFields() ); 00180 } 00181 00182 if ( isset( $prop['tags'] ) ) { 00183 $this->fld_tags = true; 00184 $this->addTables( 'tag_summary' ); 00185 $this->addJoinConds( array( 'tag_summary' => array( 'LEFT JOIN', array( 'rev_id=ts_rev_id' ) ) ) ); 00186 $this->addFields( 'ts_tags' ); 00187 } 00188 00189 if ( !is_null( $params['tag'] ) ) { 00190 $this->addTables( 'change_tag' ); 00191 $this->addJoinConds( array( 'change_tag' => array( 'INNER JOIN', array( 'rev_id=ct_rev_id' ) ) ) ); 00192 $this->addWhereFld( 'ct_tag' , $params['tag'] ); 00193 global $wgOldChangeTagsIndex; 00194 $index['change_tag'] = $wgOldChangeTagsIndex ? 'ct_tag' : 'change_tag_tag_id'; 00195 } 00196 00197 if ( isset( $prop['content'] ) || !is_null( $this->difftotext ) ) { 00198 // For each page we will request, the user must have read rights for that page 00199 $user = $this->getUser(); 00200 foreach ( $pageSet->getGoodTitles() as $title ) { 00201 if ( !$title->userCan( 'read', $user ) ) { 00202 $this->dieUsage( 00203 'The current user is not allowed to read ' . $title->getPrefixedText(), 00204 'accessdenied' ); 00205 } 00206 } 00207 00208 $this->addTables( 'text' ); 00209 $this->addWhere( 'rev_text_id=old_id' ); 00210 $this->addFields( 'old_id' ); 00211 $this->addFields( Revision::selectTextFields() ); 00212 00213 $this->fld_content = isset( $prop['content'] ); 00214 00215 $this->expandTemplates = $params['expandtemplates']; 00216 $this->generateXML = $params['generatexml']; 00217 $this->parseContent = $params['parse']; 00218 if ( $this->parseContent ) { 00219 // Must manually initialize unset limit 00220 if ( is_null( $limit ) ) { 00221 $limit = 1; 00222 } 00223 // We are only going to parse 1 revision per request 00224 $this->validateLimit( 'limit', $limit, 1, 1, 1 ); 00225 } 00226 if ( isset( $params['section'] ) ) { 00227 $this->section = $params['section']; 00228 } else { 00229 $this->section = false; 00230 } 00231 } 00232 00233 // add user name, if needed 00234 if ( $this->fld_user ) { 00235 $this->addTables( 'user' ); 00236 $this->addJoinConds( array( 'user' => Revision::userJoinCond() ) ); 00237 $this->addFields( Revision::selectUserFields() ); 00238 } 00239 00240 // Bug 24166 - API error when using rvprop=tags 00241 $this->addTables( 'revision' ); 00242 00243 if ( $enumRevMode ) { 00244 // This is mostly to prevent parameter errors (and optimize SQL?) 00245 if ( !is_null( $params['startid'] ) && !is_null( $params['start'] ) ) { 00246 $this->dieUsage( 'start and startid cannot be used together', 'badparams' ); 00247 } 00248 00249 if ( !is_null( $params['endid'] ) && !is_null( $params['end'] ) ) { 00250 $this->dieUsage( 'end and endid cannot be used together', 'badparams' ); 00251 } 00252 00253 if ( !is_null( $params['user'] ) && !is_null( $params['excludeuser'] ) ) { 00254 $this->dieUsage( 'user and excludeuser cannot be used together', 'badparams' ); 00255 } 00256 00257 // Continuing effectively uses startid. But we can't use rvstartid 00258 // directly, because there is no way to tell the client to ''not'' 00259 // send rvstart if it sent it in the original query. So instead we 00260 // send the continuation startid as rvcontinue, and ignore both 00261 // rvstart and rvstartid when that is supplied. 00262 if ( !is_null( $params['continue'] ) ) { 00263 $params['startid'] = $params['continue']; 00264 $params['start'] = null; 00265 } 00266 00267 // This code makes an assumption that sorting by rev_id and rev_timestamp produces 00268 // the same result. This way users may request revisions starting at a given time, 00269 // but to page through results use the rev_id returned after each page. 00270 // Switching to rev_id removes the potential problem of having more than 00271 // one row with the same timestamp for the same page. 00272 // The order needs to be the same as start parameter to avoid SQL filesort. 00273 if ( is_null( $params['startid'] ) && is_null( $params['endid'] ) ) { 00274 $this->addTimestampWhereRange( 'rev_timestamp', $params['dir'], 00275 $params['start'], $params['end'] ); 00276 } else { 00277 $this->addWhereRange( 'rev_id', $params['dir'], 00278 $params['startid'], $params['endid'] ); 00279 // One of start and end can be set 00280 // If neither is set, this does nothing 00281 $this->addTimestampWhereRange( 'rev_timestamp', $params['dir'], 00282 $params['start'], $params['end'], false ); 00283 } 00284 00285 // must manually initialize unset limit 00286 if ( is_null( $limit ) ) { 00287 $limit = 10; 00288 } 00289 $this->validateLimit( 'limit', $limit, 1, $userMax, $botMax ); 00290 00291 // There is only one ID, use it 00292 $ids = array_keys( $pageSet->getGoodTitles() ); 00293 $this->addWhereFld( 'rev_page', reset( $ids ) ); 00294 00295 if ( !is_null( $params['user'] ) ) { 00296 $this->addWhereFld( 'rev_user_text', $params['user'] ); 00297 } elseif ( !is_null( $params['excludeuser'] ) ) { 00298 $this->addWhere( 'rev_user_text != ' . 00299 $db->addQuotes( $params['excludeuser'] ) ); 00300 } 00301 if ( !is_null( $params['user'] ) || !is_null( $params['excludeuser'] ) ) { 00302 // Paranoia: avoid brute force searches (bug 17342) 00303 $this->addWhere( $db->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0' ); 00304 } 00305 } elseif ( $revCount > 0 ) { 00306 $max = $this->getMain()->canApiHighLimits() ? $botMax : $userMax; 00307 $revs = $pageSet->getRevisionIDs(); 00308 if ( self::truncateArray( $revs, $max ) ) { 00309 $this->setWarning( "Too many values supplied for parameter 'revids': the limit is $max" ); 00310 } 00311 00312 // Get all revision IDs 00313 $this->addWhereFld( 'rev_id', array_keys( $revs ) ); 00314 00315 if ( !is_null( $params['continue'] ) ) { 00316 $this->addWhere( 'rev_id >= ' . intval( $params['continue'] ) ); 00317 } 00318 $this->addOption( 'ORDER BY', 'rev_id' ); 00319 00320 // assumption testing -- we should never get more then $revCount rows. 00321 $limit = $revCount; 00322 } elseif ( $pageCount > 0 ) { 00323 $max = $this->getMain()->canApiHighLimits() ? $botMax : $userMax; 00324 $titles = $pageSet->getGoodTitles(); 00325 if ( self::truncateArray( $titles, $max ) ) { 00326 $this->setWarning( "Too many values supplied for parameter 'titles': the limit is $max" ); 00327 } 00328 00329 // When working in multi-page non-enumeration mode, 00330 // limit to the latest revision only 00331 $this->addWhere( 'page_id=rev_page' ); 00332 $this->addWhere( 'page_latest=rev_id' ); 00333 00334 // Get all page IDs 00335 $this->addWhereFld( 'page_id', array_keys( $titles ) ); 00336 // Every time someone relies on equality propagation, god kills a kitten :) 00337 $this->addWhereFld( 'rev_page', array_keys( $titles ) ); 00338 00339 if ( !is_null( $params['continue'] ) ) { 00340 $cont = explode( '|', $params['continue'] ); 00341 if ( count( $cont ) != 2 ) { 00342 $this->dieUsage( 'Invalid continue param. You should pass the original ' . 00343 'value returned by the previous query', '_badcontinue' ); 00344 } 00345 $pageid = intval( $cont[0] ); 00346 $revid = intval( $cont[1] ); 00347 $this->addWhere( 00348 "rev_page > $pageid OR " . 00349 "(rev_page = $pageid AND " . 00350 "rev_id >= $revid)" 00351 ); 00352 } 00353 $this->addOption( 'ORDER BY', array( 00354 'rev_page', 00355 'rev_id' 00356 )); 00357 00358 // assumption testing -- we should never get more then $pageCount rows. 00359 $limit = $pageCount; 00360 } else { 00361 ApiBase::dieDebug( __METHOD__, 'param validation?' ); 00362 } 00363 00364 $this->addOption( 'LIMIT', $limit + 1 ); 00365 $this->addOption( 'USE INDEX', $index ); 00366 00367 $count = 0; 00368 $res = $this->select( __METHOD__ ); 00369 00370 foreach ( $res as $row ) { 00371 if ( ++ $count > $limit ) { 00372 // We've reached the one extra which shows that there are additional pages to be had. Stop here... 00373 if ( !$enumRevMode ) { 00374 ApiBase::dieDebug( __METHOD__, 'Got more rows then expected' ); // bug report 00375 } 00376 $this->setContinueEnumParameter( 'continue', intval( $row->rev_id ) ); 00377 break; 00378 } 00379 00380 $fit = $this->addPageSubItem( $row->rev_page, $this->extractRowInfo( $row ), 'rev' ); 00381 if ( !$fit ) { 00382 if ( $enumRevMode ) { 00383 $this->setContinueEnumParameter( 'continue', intval( $row->rev_id ) ); 00384 } elseif ( $revCount > 0 ) { 00385 $this->setContinueEnumParameter( 'continue', intval( $row->rev_id ) ); 00386 } else { 00387 $this->setContinueEnumParameter( 'continue', intval( $row->rev_page ) . 00388 '|' . intval( $row->rev_id ) ); 00389 } 00390 break; 00391 } 00392 } 00393 } 00394 00395 private function extractRowInfo( $row ) { 00396 $revision = new Revision( $row ); 00397 $title = $revision->getTitle(); 00398 $vals = array(); 00399 00400 if ( $this->fld_ids ) { 00401 $vals['revid'] = intval( $revision->getId() ); 00402 // $vals['oldid'] = intval( $row->rev_text_id ); // todo: should this be exposed? 00403 if ( !is_null( $revision->getParentId() ) ) { 00404 $vals['parentid'] = intval( $revision->getParentId() ); 00405 } 00406 } 00407 00408 if ( $this->fld_flags && $revision->isMinor() ) { 00409 $vals['minor'] = ''; 00410 } 00411 00412 if ( $this->fld_user || $this->fld_userid ) { 00413 if ( $revision->isDeleted( Revision::DELETED_USER ) ) { 00414 $vals['userhidden'] = ''; 00415 } else { 00416 if ( $this->fld_user ) { 00417 $vals['user'] = $revision->getUserText(); 00418 } 00419 $userid = $revision->getUser(); 00420 if ( !$userid ) { 00421 $vals['anon'] = ''; 00422 } 00423 00424 if ( $this->fld_userid ) { 00425 $vals['userid'] = $userid; 00426 } 00427 } 00428 } 00429 00430 if ( $this->fld_timestamp ) { 00431 $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $revision->getTimestamp() ); 00432 } 00433 00434 if ( $this->fld_size ) { 00435 if ( !is_null( $revision->getSize() ) ) { 00436 $vals['size'] = intval( $revision->getSize() ); 00437 } else { 00438 $vals['size'] = 0; 00439 } 00440 } 00441 00442 if ( $this->fld_sha1 ) { 00443 if ( $revision->getSha1() != '' ) { 00444 $vals['sha1'] = wfBaseConvert( $revision->getSha1(), 36, 16, 40 ); 00445 } else { 00446 $vals['sha1'] = ''; 00447 } 00448 } 00449 00450 if ( $this->fld_contentmodel ) { 00451 $vals['contentmodel'] = $revision->getContentModel(); 00452 } 00453 00454 if ( $this->fld_comment || $this->fld_parsedcomment ) { 00455 if ( $revision->isDeleted( Revision::DELETED_COMMENT ) ) { 00456 $vals['commenthidden'] = ''; 00457 } else { 00458 $comment = $revision->getComment(); 00459 00460 if ( $this->fld_comment ) { 00461 $vals['comment'] = $comment; 00462 } 00463 00464 if ( $this->fld_parsedcomment ) { 00465 $vals['parsedcomment'] = Linker::formatComment( $comment, $title ); 00466 } 00467 } 00468 } 00469 00470 if ( $this->fld_tags ) { 00471 if ( $row->ts_tags ) { 00472 $tags = explode( ',', $row->ts_tags ); 00473 $this->getResult()->setIndexedTagName( $tags, 'tag' ); 00474 $vals['tags'] = $tags; 00475 } else { 00476 $vals['tags'] = array(); 00477 } 00478 } 00479 00480 if ( !is_null( $this->token ) ) { 00481 $tokenFunctions = $this->getTokenFunctions(); 00482 foreach ( $this->token as $t ) { 00483 $val = call_user_func( $tokenFunctions[$t], $title->getArticleID(), $title, $revision ); 00484 if ( $val === false ) { 00485 $this->setWarning( "Action '$t' is not allowed for the current user" ); 00486 } else { 00487 $vals[$t . 'token'] = $val; 00488 } 00489 } 00490 } 00491 00492 $content = null; 00493 global $wgParser; 00494 if ( $this->fld_content || !is_null( $this->difftotext ) ) { 00495 $content = $revision->getContent(); 00496 // Expand templates after getting section content because 00497 // template-added sections don't count and Parser::preprocess() 00498 // will have less input 00499 if ( $this->section !== false ) { 00500 $content = $content->getSection( $this->section, false ); 00501 if ( !$content ) { 00502 $this->dieUsage( "There is no section {$this->section} in r" . $revision->getId(), 'nosuchsection' ); 00503 } 00504 } 00505 } 00506 if ( $this->fld_content && !$revision->isDeleted( Revision::DELETED_TEXT ) ) { 00507 $text = null; 00508 00509 if ( $this->generateXML ) { 00510 if ( $content->getModel() === CONTENT_MODEL_WIKITEXT ) { 00511 $t = $content->getNativeData(); # note: don't set $text 00512 00513 $wgParser->startExternalParse( $title, ParserOptions::newFromContext( $this->getContext() ), OT_PREPROCESS ); 00514 $dom = $wgParser->preprocessToDom( $t ); 00515 if ( is_callable( array( $dom, 'saveXML' ) ) ) { 00516 $xml = $dom->saveXML(); 00517 } else { 00518 $xml = $dom->__toString(); 00519 } 00520 $vals['parsetree'] = $xml; 00521 } else { 00522 $this->setWarning( "Conversion to XML is supported for wikitext only, " . 00523 $title->getPrefixedDBkey() . 00524 " uses content model " . $content->getModel() . ")" ); 00525 } 00526 } 00527 00528 if ( $this->expandTemplates && !$this->parseContent ) { 00529 #XXX: implement template expansion for all content types in ContentHandler? 00530 if ( $content->getModel() === CONTENT_MODEL_WIKITEXT ) { 00531 $text = $content->getNativeData(); 00532 00533 $text = $wgParser->preprocess( $text, $title, ParserOptions::newFromContext( $this->getContext() ) ); 00534 } else { 00535 $this->setWarning( "Template expansion is supported for wikitext only, " . 00536 $title->getPrefixedDBkey() . 00537 " uses content model " . $content->getModel() . ")" ); 00538 00539 $text = false; 00540 } 00541 } 00542 if ( $this->parseContent ) { 00543 $po = $content->getParserOutput( $title, $revision->getId(), ParserOptions::newFromContext( $this->getContext() ) ); 00544 $text = $po->getText(); 00545 } 00546 00547 if ( $text === null ) { 00548 $format = $this->contentFormat ? $this->contentFormat : $content->getDefaultFormat(); 00549 00550 if ( !$content->isSupportedFormat( $format ) ) { 00551 $model = $content->getModel(); 00552 $name = $title->getPrefixedDBkey(); 00553 00554 $this->dieUsage( "The requested format {$this->contentFormat} is not supported ". 00555 "for content model $model used by $name", 'badformat' ); 00556 } 00557 00558 $text = $content->serialize( $format ); 00559 $vals['contentformat'] = $format; 00560 } 00561 00562 if ( $text !== false ) { 00563 ApiResult::setContent( $vals, $text ); 00564 } 00565 } elseif ( $this->fld_content ) { 00566 $vals['texthidden'] = ''; 00567 } 00568 00569 if ( !is_null( $this->diffto ) || !is_null( $this->difftotext ) ) { 00570 global $wgAPIMaxUncachedDiffs; 00571 static $n = 0; // Number of uncached diffs we've had 00572 if ( $n < $wgAPIMaxUncachedDiffs ) { 00573 $vals['diff'] = array(); 00574 $context = new DerivativeContext( $this->getContext() ); 00575 $context->setTitle( $title ); 00576 $handler = $revision->getContentHandler(); 00577 00578 if ( !is_null( $this->difftotext ) ) { 00579 $model = $title->getContentModel(); 00580 00581 if ( $this->contentFormat 00582 && !ContentHandler::getForModelID( $model )->isSupportedFormat( $this->contentFormat ) ) { 00583 00584 $name = $title->getPrefixedDBkey(); 00585 00586 $this->dieUsage( "The requested format {$this->contentFormat} is not supported for ". 00587 "content model $model used by $name", 'badformat' ); 00588 } 00589 00590 $difftocontent = ContentHandler::makeContent( $this->difftotext, $title, $model, $this->contentFormat ); 00591 00592 $engine = $handler->createDifferenceEngine( $context ); 00593 $engine->setContent( $content, $difftocontent ); 00594 } else { 00595 $engine = $handler->createDifferenceEngine( $context, $revision->getID(), $this->diffto ); 00596 $vals['diff']['from'] = $engine->getOldid(); 00597 $vals['diff']['to'] = $engine->getNewid(); 00598 } 00599 $difftext = $engine->getDiffBody(); 00600 ApiResult::setContent( $vals['diff'], $difftext ); 00601 if ( !$engine->wasCacheHit() ) { 00602 $n++; 00603 } 00604 } else { 00605 $vals['diff']['notcached'] = ''; 00606 } 00607 } 00608 return $vals; 00609 } 00610 00611 public function getCacheMode( $params ) { 00612 if ( isset( $params['token'] ) ) { 00613 return 'private'; 00614 } 00615 if ( !is_null( $params['prop'] ) && in_array( 'parsedcomment', $params['prop'] ) ) { 00616 // formatComment() calls wfMessage() among other things 00617 return 'anon-public-user-private'; 00618 } 00619 return 'public'; 00620 } 00621 00622 public function getAllowedParams() { 00623 return array( 00624 'prop' => array( 00625 ApiBase::PARAM_ISMULTI => true, 00626 ApiBase::PARAM_DFLT => 'ids|timestamp|flags|comment|user', 00627 ApiBase::PARAM_TYPE => array( 00628 'ids', 00629 'flags', 00630 'timestamp', 00631 'user', 00632 'userid', 00633 'size', 00634 'sha1', 00635 'contentmodel', 00636 'comment', 00637 'parsedcomment', 00638 'content', 00639 'tags' 00640 ) 00641 ), 00642 'limit' => array( 00643 ApiBase::PARAM_TYPE => 'limit', 00644 ApiBase::PARAM_MIN => 1, 00645 ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, 00646 ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 00647 ), 00648 'startid' => array( 00649 ApiBase::PARAM_TYPE => 'integer' 00650 ), 00651 'endid' => array( 00652 ApiBase::PARAM_TYPE => 'integer' 00653 ), 00654 'start' => array( 00655 ApiBase::PARAM_TYPE => 'timestamp' 00656 ), 00657 'end' => array( 00658 ApiBase::PARAM_TYPE => 'timestamp' 00659 ), 00660 'dir' => array( 00661 ApiBase::PARAM_DFLT => 'older', 00662 ApiBase::PARAM_TYPE => array( 00663 'newer', 00664 'older' 00665 ) 00666 ), 00667 'user' => array( 00668 ApiBase::PARAM_TYPE => 'user' 00669 ), 00670 'excludeuser' => array( 00671 ApiBase::PARAM_TYPE => 'user' 00672 ), 00673 'tag' => null, 00674 'expandtemplates' => false, 00675 'generatexml' => false, 00676 'parse' => false, 00677 'section' => null, 00678 'token' => array( 00679 ApiBase::PARAM_TYPE => array_keys( $this->getTokenFunctions() ), 00680 ApiBase::PARAM_ISMULTI => true 00681 ), 00682 'continue' => null, 00683 'diffto' => null, 00684 'difftotext' => null, 00685 'contentformat' => array( 00686 ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(), 00687 ApiBase::PARAM_DFLT => null 00688 ), 00689 ); 00690 } 00691 00692 public function getParamDescription() { 00693 $p = $this->getModulePrefix(); 00694 return array( 00695 'prop' => array( 00696 'Which properties to get for each revision:', 00697 ' ids - The ID of the revision', 00698 ' flags - Revision flags (minor)', 00699 ' timestamp - The timestamp of the revision', 00700 ' user - User that made the revision', 00701 ' userid - User id of revision creator', 00702 ' size - Length (bytes) of the revision', 00703 ' sha1 - SHA-1 (base 16) of the revision', 00704 ' contentmodel - Content model id', 00705 ' comment - Comment by the user for revision', 00706 ' parsedcomment - Parsed comment by the user for the revision', 00707 ' content - Text of the revision', 00708 ' tags - Tags for the revision', 00709 ), 00710 'limit' => 'Limit how many revisions will be returned (enum)', 00711 'startid' => 'From which revision id to start enumeration (enum)', 00712 'endid' => 'Stop revision enumeration on this revid (enum)', 00713 'start' => 'From which revision timestamp to start enumeration (enum)', 00714 'end' => 'Enumerate up to this timestamp (enum)', 00715 'dir' => $this->getDirectionDescription( $p, ' (enum)' ), 00716 'user' => 'Only include revisions made by user (enum)', 00717 'excludeuser' => 'Exclude revisions made by user (enum)', 00718 'expandtemplates' => "Expand templates in revision content (requires {$p}prop=content)", 00719 'generatexml' => "Generate XML parse tree for revision content (requires {$p}prop=content)", 00720 'parse' => array( "Parse revision content (requires {$p}prop=content).", 00721 'For performance reasons if this option is used, rvlimit is enforced to 1.' ), 00722 'section' => 'Only retrieve the content of this section number', 00723 'token' => 'Which tokens to obtain for each revision', 00724 'continue' => 'When more results are available, use this to continue', 00725 'diffto' => array( 'Revision ID to diff each revision to.', 00726 'Use "prev", "next" and "cur" for the previous, next and current revision respectively' ), 00727 'difftotext' => array( 'Text to diff each revision to. Only diffs a limited number of revisions.', 00728 "Overrides {$p}diffto. If {$p}section is set, only that section will be diffed against this text" ), 00729 'tag' => 'Only list revisions tagged with this tag', 00730 'contentformat' => 'Serialization format used for difftotext and expected for output of content', 00731 ); 00732 } 00733 00734 public function getResultProperties() { 00735 $props = array( 00736 '' => array(), 00737 'ids' => array( 00738 'revid' => 'integer', 00739 'parentid' => array( 00740 ApiBase::PROP_TYPE => 'integer', 00741 ApiBase::PROP_NULLABLE => true 00742 ) 00743 ), 00744 'flags' => array( 00745 'minor' => 'boolean' 00746 ), 00747 'user' => array( 00748 'userhidden' => 'boolean', 00749 'user' => 'string', 00750 'anon' => 'boolean' 00751 ), 00752 'userid' => array( 00753 'userhidden' => 'boolean', 00754 'userid' => 'integer', 00755 'anon' => 'boolean' 00756 ), 00757 'timestamp' => array( 00758 'timestamp' => 'timestamp' 00759 ), 00760 'size' => array( 00761 'size' => 'integer' 00762 ), 00763 'sha1' => array( 00764 'sha1' => 'string' 00765 ), 00766 'comment' => array( 00767 'commenthidden' => 'boolean', 00768 'comment' => array( 00769 ApiBase::PROP_TYPE => 'string', 00770 ApiBase::PROP_NULLABLE => true 00771 ) 00772 ), 00773 'parsedcomment' => array( 00774 'commenthidden' => 'boolean', 00775 'parsedcomment' => array( 00776 ApiBase::PROP_TYPE => 'string', 00777 ApiBase::PROP_NULLABLE => true 00778 ) 00779 ), 00780 'content' => array( 00781 '*' => array( 00782 ApiBase::PROP_TYPE => 'string', 00783 ApiBase::PROP_NULLABLE => true 00784 ), 00785 'texthidden' => 'boolean' 00786 ) 00787 ); 00788 00789 self::addTokenProperties( $props, $this->getTokenFunctions() ); 00790 00791 return $props; 00792 } 00793 00794 public function getDescription() { 00795 return array( 00796 'Get revision information', 00797 'May be used in several ways:', 00798 ' 1) Get data about a set of pages (last revision), by setting titles or pageids parameter', 00799 ' 2) Get revisions for one given page, by using titles/pageids with start/end/limit params', 00800 ' 3) Get data about a set of revisions by setting their IDs with revids parameter', 00801 'All parameters marked as (enum) may only be used with a single page (#2)' 00802 ); 00803 } 00804 00805 public function getPossibleErrors() { 00806 return array_merge( parent::getPossibleErrors(), array( 00807 array( 'nosuchrevid', 'diffto' ), 00808 array( 'code' => 'revids', 'info' => 'The revids= parameter may not be used with the list options ' 00809 . '(limit, startid, endid, dirNewer, start, end).' ), 00810 array( 'code' => 'multpages', 'info' => 'titles, pageids or a generator was used to supply multiple pages, ' 00811 . ' but the limit, startid, endid, dirNewer, user, excludeuser, ' 00812 . 'start and end parameters may only be used on a single page.' ), 00813 array( 'code' => 'diffto', 'info' => 'rvdiffto must be set to a non-negative number, "prev", "next" or "cur"' ), 00814 array( 'code' => 'badparams', 'info' => 'start and startid cannot be used together' ), 00815 array( 'code' => 'badparams', 'info' => 'end and endid cannot be used together' ), 00816 array( 'code' => 'badparams', 'info' => 'user and excludeuser cannot be used together' ), 00817 array( 'code' => 'nosuchsection', 'info' => 'There is no section section in rID' ), 00818 array( 'code' => 'badformat', 'info' => 'The requested serialization format can not be applied ' 00819 . ' to the page\'s content model' ), 00820 ) ); 00821 } 00822 00823 public function getExamples() { 00824 return array( 00825 'Get data with content for the last revision of titles "API" and "Main Page"', 00826 ' api.php?action=query&prop=revisions&titles=API|Main%20Page&rvprop=timestamp|user|comment|content', 00827 'Get last 5 revisions of the "Main Page"', 00828 ' api.php?action=query&prop=revisions&titles=Main%20Page&rvlimit=5&rvprop=timestamp|user|comment', 00829 'Get first 5 revisions of the "Main Page"', 00830 ' api.php?action=query&prop=revisions&titles=Main%20Page&rvlimit=5&rvprop=timestamp|user|comment&rvdir=newer', 00831 'Get first 5 revisions of the "Main Page" made after 2006-05-01', 00832 ' api.php?action=query&prop=revisions&titles=Main%20Page&rvlimit=5&rvprop=timestamp|user|comment&rvdir=newer&rvstart=20060501000000', 00833 'Get first 5 revisions of the "Main Page" that were not made made by anonymous user "127.0.0.1"', 00834 ' api.php?action=query&prop=revisions&titles=Main%20Page&rvlimit=5&rvprop=timestamp|user|comment&rvexcludeuser=127.0.0.1', 00835 'Get first 5 revisions of the "Main Page" that were made by the user "MediaWiki default"', 00836 ' api.php?action=query&prop=revisions&titles=Main%20Page&rvlimit=5&rvprop=timestamp|user|comment&rvuser=MediaWiki%20default', 00837 ); 00838 } 00839 00840 public function getHelpUrls() { 00841 return 'https://www.mediawiki.org/wiki/API:Properties#revisions_.2F_rv'; 00842 } 00843 00844 public function getVersion() { 00845 return __CLASS__ . ': $Id$'; 00846 } 00847 }