From af3bd08fb8f8a563f49784a718b9598cacd72ccc Mon Sep 17 00:00:00 2001 From: Arlo Breault Date: Wed, 13 Sep 2017 21:13:15 -0400 Subject: [PATCH] API: Pass revid when parsing text * Provides consistency with expandtemplates * Necessary for properly rendering `text` with {{REVISIONID}} Change-Id: I310d0778d74c46f15385cd411d2462b541ed339a --- includes/api/ApiExpandTemplates.php | 42 ++++++++++++++++++++--------- includes/api/ApiParse.php | 37 +++++++++++++++++++++---- includes/api/i18n/en.json | 6 +++-- includes/api/i18n/qqq.json | 4 ++- 4 files changed, 69 insertions(+), 20 deletions(-) diff --git a/includes/api/ApiExpandTemplates.php b/includes/api/ApiExpandTemplates.php index e15d7da1e5..7c86e09d3d 100644 --- a/includes/api/ApiExpandTemplates.php +++ b/includes/api/ApiExpandTemplates.php @@ -41,6 +41,15 @@ class ApiExpandTemplates extends ApiBase { $params = $this->extractRequestParams(); $this->requireMaxOneParameter( $params, 'prop', 'generatexml' ); + $title = $params['title']; + if ( $title === null ) { + $titleProvided = false; + // A title is needed for parsing, so arbitrarily choose one + $title = 'API'; + } else { + $titleProvided = true; + } + if ( $params['prop'] === null ) { $this->addDeprecation( 'apiwarn-deprecation-expandtemplates-prop', 'action=expandtemplates&!prop' @@ -50,6 +59,11 @@ class ApiExpandTemplates extends ApiBase { $prop = array_flip( $params['prop'] ); } + $titleObj = Title::newFromText( $title ); + if ( !$titleObj || $titleObj->isExternal() ) { + $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['title'] ) ] ); + } + // Get title and revision ID for parser $revid = $params['revid']; if ( $revid !== null ) { @@ -57,11 +71,17 @@ class ApiExpandTemplates extends ApiBase { if ( !$rev ) { $this->dieWithError( [ 'apierror-nosuchrevid', $revid ] ); } - $title_obj = $rev->getTitle(); - } else { - $title_obj = Title::newFromText( $params['title'] ); - if ( !$title_obj || $title_obj->isExternal() ) { - $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['title'] ) ] ); + $pTitleObj = $titleObj; + $titleObj = $rev->getTitle(); + if ( $titleProvided ) { + if ( !$titleObj->equals( $pTitleObj ) ) { + $this->addWarning( [ 'apierror-revwrongpage', $rev->getId(), + wfEscapeWikiText( $pTitleObj->getPrefixedText() ) ] ); + } + } else { + // Consider the title derived from the revid as having + // been provided. + $titleProvided = true; } } @@ -78,12 +98,12 @@ class ApiExpandTemplates extends ApiBase { $reset = null; $suppressCache = false; Hooks::run( 'ApiMakeParserOptions', - [ $options, $title_obj, $params, $this, &$reset, &$suppressCache ] ); + [ $options, $titleObj, $params, $this, &$reset, &$suppressCache ] ); $retval = []; if ( isset( $prop['parsetree'] ) || $params['generatexml'] ) { - $wgParser->startExternalParse( $title_obj, $options, Parser::OT_PREPROCESS ); + $wgParser->startExternalParse( $titleObj, $options, Parser::OT_PREPROCESS ); $dom = $wgParser->preprocessToDom( $params['text'] ); if ( is_callable( [ $dom, 'saveXML' ] ) ) { $xml = $dom->saveXML(); @@ -103,9 +123,9 @@ class ApiExpandTemplates extends ApiBase { // if they didn't want any output except (probably) the parse tree, // then don't bother actually fully expanding it if ( $prop || $params['prop'] === null ) { - $wgParser->startExternalParse( $title_obj, $options, Parser::OT_PREPROCESS ); + $wgParser->startExternalParse( $titleObj, $options, Parser::OT_PREPROCESS ); $frame = $wgParser->getPreprocessor()->newFrame(); - $wikitext = $wgParser->preprocess( $params['text'], $title_obj, $options, $revid, $frame ); + $wikitext = $wgParser->preprocess( $params['text'], $titleObj, $options, $revid, $frame ); if ( $params['prop'] === null ) { // the old way ApiResult::setContentValue( $retval, 'wikitext', $wikitext ); @@ -169,9 +189,7 @@ class ApiExpandTemplates extends ApiBase { public function getAllowedParams() { return [ - 'title' => [ - ApiBase::PARAM_DFLT => 'API', - ], + 'title' => null, 'text' => [ ApiBase::PARAM_TYPE => 'text', ApiBase::PARAM_REQUIRED => true, diff --git a/includes/api/ApiParse.php b/includes/api/ApiParse.php index 031fbf76a7..7cbd35377e 100644 --- a/includes/api/ApiParse.php +++ b/includes/api/ApiParse.php @@ -48,10 +48,11 @@ class ApiParse extends ApiBase { // Get parameters $params = $this->extractRequestParams(); - // No easy way to say that text & title are allowed together while the - // rest aren't, so just do it in two calls. + // No easy way to say that text and title or revid are allowed together + // while the rest aren't, so just do it in three calls. $this->requireMaxOneParameter( $params, 'page', 'pageid', 'oldid', 'text' ); $this->requireMaxOneParameter( $params, 'page', 'pageid', 'oldid', 'title' ); + $this->requireMaxOneParameter( $params, 'page', 'pageid', 'oldid', 'revid' ); $text = $params['text']; $title = $params['title']; @@ -169,6 +170,25 @@ class ApiParse extends ApiBase { if ( !$titleObj || $titleObj->isExternal() ) { $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $title ) ] ); } + $revid = $params['revid']; + if ( $revid !== null ) { + $rev = Revision::newFromId( $revid ); + if ( !$rev ) { + $this->dieWithError( [ 'apierror-nosuchrevid', $revid ] ); + } + $pTitleObj = $titleObj; + $titleObj = $rev->getTitle(); + if ( $titleProvided ) { + if ( !$titleObj->equals( $pTitleObj ) ) { + $this->addWarning( [ 'apierror-revwrongpage', $rev->getId(), + wfEscapeWikiText( $pTitleObj->getPrefixedText() ) ] ); + } + } else { + // Consider the title derived from the revid as having + // been provided. + $titleProvided = true; + } + } $wgTitle = $titleObj; if ( $titleObj->canExist() ) { $pageObj = WikiPage::factory( $titleObj ); @@ -183,7 +203,11 @@ class ApiParse extends ApiBase { if ( !$textProvided ) { if ( $titleProvided && ( $prop || $params['generatexml'] ) ) { - $this->addWarning( 'apiwarn-parse-titlewithouttext' ); + if ( $revid !== null ) { + $this->addWarning( 'apiwarn-parse-revidwithouttext' ); + } else { + $this->addWarning( 'apiwarn-parse-titlewithouttext' ); + } } // Prevent warning from ContentHandler::makeContent() $text = ''; @@ -247,9 +271,9 @@ class ApiParse extends ApiBase { // Not cached (save or load) if ( $params['pst'] ) { - $p_result = $this->pstContent->getParserOutput( $titleObj, null, $popts ); + $p_result = $this->pstContent->getParserOutput( $titleObj, $revid, $popts ); } else { - $p_result = $this->content->getParserOutput( $titleObj, null, $popts ); + $p_result = $this->content->getParserOutput( $titleObj, $revid, $popts ); } } @@ -785,6 +809,9 @@ class ApiParse extends ApiBase { 'text' => [ ApiBase::PARAM_TYPE => 'text', ], + 'revid' => [ + ApiBase::PARAM_TYPE => 'integer', + ], 'summary' => null, 'page' => null, 'pageid' => [ diff --git a/includes/api/i18n/en.json b/includes/api/i18n/en.json index 9fbc0127fe..dbd5451409 100644 --- a/includes/api/i18n/en.json +++ b/includes/api/i18n/en.json @@ -162,7 +162,7 @@ "apihelp-expandtemplates-summary": "Expands all templates within wikitext.", "apihelp-expandtemplates-param-title": "Title of page.", "apihelp-expandtemplates-param-text": "Wikitext to convert.", - "apihelp-expandtemplates-param-revid": "Revision ID, for {{REVISIONID}} and similar variables.", + "apihelp-expandtemplates-param-revid": "Revision ID, for {{REVISIONID}} and similar variables.", "apihelp-expandtemplates-param-prop": "Which pieces of information to get.\n\nNote that if no values are selected, the result will contain the wikitext, but the output will be in a deprecated format.", "apihelp-expandtemplates-paramvalue-prop-wikitext": "The expanded wikitext.", "apihelp-expandtemplates-paramvalue-prop-categories": "Any categories present in the input that are not represented in the wikitext output.", @@ -343,9 +343,10 @@ "apihelp-paraminfo-example-2": "Show info for all submodules of [[Special:ApiHelp/query|action=query]].", "apihelp-parse-summary": "Parses content and returns parser output.", - "apihelp-parse-extended-description": "See the various prop-modules of [[Special:ApiHelp/query|action=query]] to get information from the current version of a page.\n\nThere are several ways to specify the text to parse:\n# Specify a page or revision, using $1page, $1pageid, or $1oldid.\n# Specify content explicitly, using $1text, $1title, and $1contentmodel.\n# Specify only a summary to parse. $1prop should be given an empty value.", + "apihelp-parse-extended-description": "See the various prop-modules of [[Special:ApiHelp/query|action=query]] to get information from the current version of a page.\n\nThere are several ways to specify the text to parse:\n# Specify a page or revision, using $1page, $1pageid, or $1oldid.\n# Specify content explicitly, using $1text, $1title, $1revid, and $1contentmodel.\n# Specify only a summary to parse. $1prop should be given an empty value.", "apihelp-parse-param-title": "Title of page the text belongs to. If omitted, $1contentmodel must be specified, and [[API]] will be used as the title.", "apihelp-parse-param-text": "Text to parse. Use $1title or $1contentmodel to control the content model.", + "apihelp-parse-param-revid": "Revision ID, for {{REVISIONID}} and similar variables.", "apihelp-parse-param-summary": "Summary to parse.", "apihelp-parse-param-page": "Parse the content of this page. Cannot be used together with $1text and $1title.", "apihelp-parse-param-pageid": "Parse the content of this page. Overrides $1page.", @@ -1847,6 +1848,7 @@ "apiwarn-notfile": "\"$1\" is not a file.", "apiwarn-nothumb-noimagehandler": "Could not create thumbnail because $1 does not have an associated image handler.", "apiwarn-parse-nocontentmodel": "No title or contentmodel was given, assuming $1.", + "apiwarn-parse-revidwithouttext": "revid used without text, and parsed page properties were requested. Did you mean to use oldid instead of revid?", "apiwarn-parse-titlewithouttext": "title used without text, and parsed page properties were requested. Did you mean to use page instead of title?", "apiwarn-redirectsandrevids": "Redirect resolution cannot be used together with the revids parameter. Any redirects the revids point to have not been resolved.", "apiwarn-tokennotallowed": "Action \"$1\" is not allowed for the current user.", diff --git a/includes/api/i18n/qqq.json b/includes/api/i18n/qqq.json index c878a539e6..6aaaac70f6 100644 --- a/includes/api/i18n/qqq.json +++ b/includes/api/i18n/qqq.json @@ -159,7 +159,7 @@ "apihelp-expandtemplates-summary": "{{doc-apihelp-summary|expandtemplates}}", "apihelp-expandtemplates-param-title": "{{doc-apihelp-param|expandtemplates|title}}", "apihelp-expandtemplates-param-text": "{{doc-apihelp-param|expandtemplates|text}}", - "apihelp-expandtemplates-param-revid": "{{doc-apihelp-param|expandtemplates|revid}}\n{{doc-important|Do not translate <nowiki>{{REVISIONID}}</nowiki>}}", + "apihelp-expandtemplates-param-revid": "{{doc-apihelp-param|expandtemplates|revid}}", "apihelp-expandtemplates-param-prop": "{{doc-apihelp-param|expandtemplates|prop|paramvalues=1}}", "apihelp-expandtemplates-paramvalue-prop-wikitext": "{{doc-apihelp-paramvalue|expandtemplates|prop|wikitext}}", "apihelp-expandtemplates-paramvalue-prop-categories": "{{doc-apihelp-paramvalue|expandtemplates|prop|categories}}", @@ -326,6 +326,7 @@ "apihelp-parse-extended-description": "{{doc-apihelp-extended-description|parse}}", "apihelp-parse-param-title": "{{doc-apihelp-param|parse|title}}", "apihelp-parse-param-text": "{{doc-apihelp-param|parse|text}}", + "apihelp-parse-param-revid": "{{doc-apihelp-param|parse|revid}}", "apihelp-parse-param-summary": "{{doc-apihelp-param|parse|summary}}", "apihelp-parse-param-page": "{{doc-apihelp-param|parse|page}}", "apihelp-parse-param-pageid": "{{doc-apihelp-param|parse|pageid}}", @@ -1735,6 +1736,7 @@ "apiwarn-notfile": "{{doc-apierror}}\n\nParameters:\n* $1 - Supplied file name.", "apiwarn-nothumb-noimagehandler": "{{doc-apierror}}\n\nParameters:\n* $1 - File name.", "apiwarn-parse-nocontentmodel": "{{doc-apierror}}\n\nParameters:\n* $1 - Content model being assumed.", + "apiwarn-parse-revidwithouttext": "{{doc-apierror}}", "apiwarn-parse-titlewithouttext": "{{doc-apierror}}", "apiwarn-redirectsandrevids": "{{doc-apierror}}", "apiwarn-tokennotallowed": "{{doc-apierror}}\n\nParameters:\n* $1 - Token type being requested, typically named after the action requiring the token.", -- 2.20.1