diff options
Diffstat (limited to 'includes/api/ApiQueryRevisions.php')
-rw-r--r-- | includes/api/ApiQueryRevisions.php | 180 |
1 files changed, 134 insertions, 46 deletions
diff --git a/includes/api/ApiQueryRevisions.php b/includes/api/ApiQueryRevisions.php index b89a8ea9..192fe873 100644 --- a/includes/api/ApiQueryRevisions.php +++ b/includes/api/ApiQueryRevisions.php @@ -34,15 +34,15 @@ class ApiQueryRevisions extends ApiQueryBase { private $diffto, $difftotext, $expandTemplates, $generateXML, $section, - $token, $parseContent; + $token, $parseContent, $contentFormat; public function __construct( $query, $moduleName ) { parent::__construct( $query, $moduleName, 'rv' ); } - private $fld_ids = false, $fld_flags = false, $fld_timestamp = false, $fld_size = false, + private $fld_ids = false, $fld_flags = false, $fld_timestamp = false, $fld_size = false, $fld_sha1 = false, $fld_comment = false, $fld_parsedcomment = false, $fld_user = false, $fld_userid = false, - $fld_content = false, $fld_tags = false; + $fld_content = false, $fld_tags = false, $fld_contentmodel = false; private $tokenFunctions; @@ -95,7 +95,6 @@ class ApiQueryRevisions extends ApiQueryBase { !is_null( $params['endid'] ) || $params['dir'] === 'newer' || !is_null( $params['start'] ) || !is_null( $params['end'] ) ); - $pageSet = $this->getPageSet(); $pageCount = $pageSet->getGoodTitleCount(); $revCount = $pageSet->getRevisionCount(); @@ -155,15 +154,20 @@ class ApiQueryRevisions extends ApiQueryBase { $this->fld_parsedcomment = isset ( $prop['parsedcomment'] ); $this->fld_size = isset ( $prop['size'] ); $this->fld_sha1 = isset ( $prop['sha1'] ); + $this->fld_contentmodel = isset ( $prop['contentmodel'] ); $this->fld_userid = isset( $prop['userid'] ); $this->fld_user = isset ( $prop['user'] ); $this->token = $params['token']; + if ( !empty( $params['contentformat'] ) ) { + $this->contentFormat = $params['contentformat']; + } + // Possible indexes used $index = array(); $userMax = ( $this->fld_content ? ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1 ); - $botMax = ( $this->fld_content ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2 ); + $botMax = ( $this->fld_content ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2 ); $limit = $params['limit']; if ( $limit == 'max' ) { $limit = $this->getMain()->canApiHighLimits() ? $botMax : $userMax; @@ -184,15 +188,17 @@ class ApiQueryRevisions extends ApiQueryBase { if ( !is_null( $params['tag'] ) ) { $this->addTables( 'change_tag' ); $this->addJoinConds( array( 'change_tag' => array( 'INNER JOIN', array( 'rev_id=ct_rev_id' ) ) ) ); - $this->addWhereFld( 'ct_tag' , $params['tag'] ); + $this->addWhereFld( 'ct_tag', $params['tag'] ); global $wgOldChangeTagsIndex; $index['change_tag'] = $wgOldChangeTagsIndex ? 'ct_tag' : 'change_tag_tag_id'; } if ( isset( $prop['content'] ) || !is_null( $this->difftotext ) ) { // For each page we will request, the user must have read rights for that page + $user = $this->getUser(); + /** @var $title Title */ foreach ( $pageSet->getGoodTitles() as $title ) { - if ( !$title->userCan( 'read' ) ) { + if ( !$title->userCan( 'read', $user ) ) { $this->dieUsage( 'The current user is not allowed to read ' . $title->getPrefixedText(), 'accessdenied' ); @@ -255,7 +261,7 @@ class ApiQueryRevisions extends ApiQueryBase { // rvstart and rvstartid when that is supplied. if ( !is_null( $params['continue'] ) ) { $params['startid'] = $params['continue']; - unset( $params['start'] ); + $params['start'] = null; } // This code makes an assumption that sorting by rev_id and rev_timestamp produces @@ -332,10 +338,7 @@ class ApiQueryRevisions extends ApiQueryBase { if ( !is_null( $params['continue'] ) ) { $cont = explode( '|', $params['continue'] ); - if ( count( $cont ) != 2 ) { - $this->dieUsage( 'Invalid continue param. You should pass the original ' . - 'value returned by the previous query', '_badcontinue' ); - } + $this->dieContinueUsageIf( count( $cont ) != 2 ); $pageid = intval( $cont[0] ); $revid = intval( $cont[1] ); $this->addWhere( @@ -433,12 +436,18 @@ class ApiQueryRevisions extends ApiQueryBase { } } - if ( $this->fld_sha1 ) { + if ( $this->fld_sha1 && !$revision->isDeleted( Revision::DELETED_TEXT ) ) { if ( $revision->getSha1() != '' ) { $vals['sha1'] = wfBaseConvert( $revision->getSha1(), 36, 16, 40 ); } else { $vals['sha1'] = ''; } + } elseif ( $this->fld_sha1 ) { + $vals['sha1hidden'] = ''; + } + + if ( $this->fld_contentmodel ) { + $vals['contentmodel'] = $revision->getContentModel(); } if ( $this->fld_comment || $this->fld_parsedcomment ) { @@ -479,55 +488,121 @@ class ApiQueryRevisions extends ApiQueryBase { } } - $text = null; + $content = null; global $wgParser; - if ( $this->fld_content || !is_null( $this->difftotext ) ) { - $text = $revision->getText(); + if ( $this->fld_content || !is_null( $this->diffto ) || !is_null( $this->difftotext ) ) { + $content = $revision->getContent(); // Expand templates after getting section content because // template-added sections don't count and Parser::preprocess() // will have less input - if ( $this->section !== false ) { - $text = $wgParser->getSection( $text, $this->section, false ); - if ( $text === false ) { + if ( $content && $this->section !== false ) { + $content = $content->getSection( $this->section, false ); + if ( !$content ) { $this->dieUsage( "There is no section {$this->section} in r" . $revision->getId(), 'nosuchsection' ); } } } - if ( $this->fld_content && !$revision->isDeleted( Revision::DELETED_TEXT ) ) { + if ( $this->fld_content && $content && !$revision->isDeleted( Revision::DELETED_TEXT ) ) { + $text = null; + if ( $this->generateXML ) { - $wgParser->startExternalParse( $title, ParserOptions::newFromContext( $this->getContext() ), OT_PREPROCESS ); - $dom = $wgParser->preprocessToDom( $text ); - if ( is_callable( array( $dom, 'saveXML' ) ) ) { - $xml = $dom->saveXML(); + if ( $content->getModel() === CONTENT_MODEL_WIKITEXT ) { + $t = $content->getNativeData(); # note: don't set $text + + $wgParser->startExternalParse( $title, ParserOptions::newFromContext( $this->getContext() ), OT_PREPROCESS ); + $dom = $wgParser->preprocessToDom( $t ); + if ( is_callable( array( $dom, 'saveXML' ) ) ) { + $xml = $dom->saveXML(); + } else { + $xml = $dom->__toString(); + } + $vals['parsetree'] = $xml; } else { - $xml = $dom->__toString(); + $this->setWarning( "Conversion to XML is supported for wikitext only, " . + $title->getPrefixedDBkey() . + " uses content model " . $content->getModel() . ")" ); } - $vals['parsetree'] = $xml; - } + if ( $this->expandTemplates && !$this->parseContent ) { - $text = $wgParser->preprocess( $text, $title, ParserOptions::newFromContext( $this->getContext() ) ); + #XXX: implement template expansion for all content types in ContentHandler? + if ( $content->getModel() === CONTENT_MODEL_WIKITEXT ) { + $text = $content->getNativeData(); + + $text = $wgParser->preprocess( $text, $title, ParserOptions::newFromContext( $this->getContext() ) ); + } else { + $this->setWarning( "Template expansion is supported for wikitext only, " . + $title->getPrefixedDBkey() . + " uses content model " . $content->getModel() . ")" ); + + $text = false; + } } if ( $this->parseContent ) { - $text = $wgParser->parse( $text, $title, ParserOptions::newFromContext( $this->getContext() ) )->getText(); + $po = $content->getParserOutput( $title, $revision->getId(), ParserOptions::newFromContext( $this->getContext() ) ); + $text = $po->getText(); + } + + if ( $text === null ) { + $format = $this->contentFormat ? $this->contentFormat : $content->getDefaultFormat(); + $model = $content->getModel(); + + if ( !$content->isSupportedFormat( $format ) ) { + $name = $title->getPrefixedDBkey(); + + $this->dieUsage( "The requested format {$this->contentFormat} is not supported ". + "for content model $model used by $name", 'badformat' ); + } + + $text = $content->serialize( $format ); + + // always include format and model. + // Format is needed to deserialize, model is needed to interpret. + $vals['contentformat'] = $format; + $vals['contentmodel'] = $model; + } + + if ( $text !== false ) { + ApiResult::setContent( $vals, $text ); } - ApiResult::setContent( $vals, $text ); } elseif ( $this->fld_content ) { - $vals['texthidden'] = ''; + if ( $revision->isDeleted( Revision::DELETED_TEXT ) ) { + $vals['texthidden'] = ''; + } else { + $vals['textmissing'] = ''; + } } if ( !is_null( $this->diffto ) || !is_null( $this->difftotext ) ) { global $wgAPIMaxUncachedDiffs; static $n = 0; // Number of uncached diffs we've had - if ( $n < $wgAPIMaxUncachedDiffs ) { + + if ( is_null( $content ) ) { + $vals['textmissing'] = ''; + } elseif ( $n < $wgAPIMaxUncachedDiffs ) { $vals['diff'] = array(); $context = new DerivativeContext( $this->getContext() ); $context->setTitle( $title ); + $handler = $revision->getContentHandler(); + if ( !is_null( $this->difftotext ) ) { - $engine = new DifferenceEngine( $context ); - $engine->setText( $text, $this->difftotext ); + $model = $title->getContentModel(); + + if ( $this->contentFormat + && !ContentHandler::getForModelID( $model )->isSupportedFormat( $this->contentFormat ) ) { + + $name = $title->getPrefixedDBkey(); + + $this->dieUsage( "The requested format {$this->contentFormat} is not supported for ". + "content model $model used by $name", 'badformat' ); + } + + $difftocontent = ContentHandler::makeContent( $this->difftotext, $title, $model, $this->contentFormat ); + + $engine = $handler->createDifferenceEngine( $context ); + $engine->setContent( $content, $difftocontent ); } else { - $engine = new DifferenceEngine( $context, $revision->getID(), $this->diffto ); + $engine = $handler->createDifferenceEngine( $context, $revision->getID(), $this->diffto ); $vals['diff']['from'] = $engine->getOldid(); $vals['diff']['to'] = $engine->getNewid(); } @@ -567,6 +642,7 @@ class ApiQueryRevisions extends ApiQueryBase { 'userid', 'size', 'sha1', + 'contentmodel', 'comment', 'parsedcomment', 'content', @@ -616,6 +692,10 @@ class ApiQueryRevisions extends ApiQueryBase { 'continue' => null, 'diffto' => null, 'difftotext' => null, + 'contentformat' => array( + ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(), + ApiBase::PARAM_DFLT => null + ), ); } @@ -631,6 +711,7 @@ class ApiQueryRevisions extends ApiQueryBase { ' userid - User id of revision creator', ' size - Length (bytes) of the revision', ' sha1 - SHA-1 (base 16) of the revision', + ' contentmodel - Content model id', ' comment - Comment by the user for revision', ' parsedcomment - Parsed comment by the user for the revision', ' content - Text of the revision', @@ -644,9 +725,10 @@ class ApiQueryRevisions extends ApiQueryBase { 'dir' => $this->getDirectionDescription( $p, ' (enum)' ), 'user' => 'Only include revisions made by user (enum)', 'excludeuser' => 'Exclude revisions made by user (enum)', - 'expandtemplates' => 'Expand templates in revision content', - 'generatexml' => 'Generate XML parse tree for revision content', - 'parse' => 'Parse revision content. For performance reasons if this option is used, rvlimit is enforced to 1.', + 'expandtemplates' => "Expand templates in revision content (requires {$p}prop=content)", + 'generatexml' => "Generate XML parse tree for revision content (requires {$p}prop=content)", + 'parse' => array( "Parse revision content (requires {$p}prop=content).", + 'For performance reasons if this option is used, rvlimit is enforced to 1.' ), 'section' => 'Only retrieve the content of this section number', 'token' => 'Which tokens to obtain for each revision', 'continue' => 'When more results are available, use this to continue', @@ -655,6 +737,7 @@ class ApiQueryRevisions extends ApiQueryBase { 'difftotext' => array( 'Text to diff each revision to. Only diffs a limited number of revisions.', "Overrides {$p}diffto. If {$p}section is set, only that section will be diffed against this text" ), 'tag' => 'Only list revisions tagged with this tag', + 'contentformat' => 'Serialization format used for difftotext and expected for output of content', ); } @@ -709,8 +792,12 @@ class ApiQueryRevisions extends ApiQueryBase { ApiBase::PROP_TYPE => 'string', ApiBase::PROP_NULLABLE => true ), - 'texthidden' => 'boolean' - ) + 'texthidden' => 'boolean', + 'textmissing' => 'boolean', + ), + 'contentmodel' => array( + 'contentmodel' => 'string' + ), ); self::addTokenProperties( $props, $this->getTokenFunctions() ); @@ -732,13 +819,18 @@ class ApiQueryRevisions extends ApiQueryBase { public function getPossibleErrors() { return array_merge( parent::getPossibleErrors(), array( array( 'nosuchrevid', 'diffto' ), - array( 'code' => 'revids', 'info' => 'The revids= parameter may not be used with the list options (limit, startid, endid, dirNewer, start, end).' ), - array( 'code' => 'multpages', 'info' => '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.' ), + array( 'code' => 'revids', 'info' => 'The revids= parameter may not be used with the list options ' + . '(limit, startid, endid, dirNewer, start, end).' ), + array( 'code' => 'multpages', 'info' => '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.' ), array( 'code' => 'diffto', 'info' => 'rvdiffto must be set to a non-negative number, "prev", "next" or "cur"' ), array( 'code' => 'badparams', 'info' => 'start and startid cannot be used together' ), array( 'code' => 'badparams', 'info' => 'end and endid cannot be used together' ), array( 'code' => 'badparams', 'info' => 'user and excludeuser cannot be used together' ), array( 'code' => 'nosuchsection', 'info' => 'There is no section section in rID' ), + array( 'code' => 'badformat', 'info' => 'The requested serialization format can not be applied ' + . ' to the page\'s content model' ), ) ); } @@ -762,8 +854,4 @@ class ApiQueryRevisions extends ApiQueryBase { public function getHelpUrls() { return 'https://www.mediawiki.org/wiki/API:Properties#revisions_.2F_rv'; } - - public function getVersion() { - return __CLASS__ . ': $Id$'; - } } |