API: Pass revid when parsing text
authorArlo Breault <abreault@wikimedia.org>
Thu, 14 Sep 2017 01:13:15 +0000 (21:13 -0400)
committerArlo Breault <abreault@wikimedia.org>
Mon, 18 Sep 2017 18:33:47 +0000 (14:33 -0400)
 * Provides consistency with expandtemplates
 * Necessary for properly rendering `text` with {{REVISIONID}}

Change-Id: I310d0778d74c46f15385cd411d2462b541ed339a

includes/api/ApiExpandTemplates.php
includes/api/ApiParse.php
includes/api/i18n/en.json
includes/api/i18n/qqq.json

index e15d7da..7c86e09 100644 (file)
@@ -41,6 +41,15 @@ class ApiExpandTemplates extends ApiBase {
                $params = $this->extractRequestParams();
                $this->requireMaxOneParameter( $params, 'prop', 'generatexml' );
 
                $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'
                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'] );
                }
 
                        $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 ) {
                // 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 ] );
                        }
                        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',
                $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'] ) {
 
                $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();
                        $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 ) {
                // 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();
                        $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 );
                        if ( $params['prop'] === null ) {
                                // the old way
                                ApiResult::setContentValue( $retval, 'wikitext', $wikitext );
@@ -169,9 +189,7 @@ class ApiExpandTemplates extends ApiBase {
 
        public function getAllowedParams() {
                return [
 
        public function getAllowedParams() {
                return [
-                       'title' => [
-                               ApiBase::PARAM_DFLT => 'API',
-                       ],
+                       'title' => null,
                        'text' => [
                                ApiBase::PARAM_TYPE => 'text',
                                ApiBase::PARAM_REQUIRED => true,
                        'text' => [
                                ApiBase::PARAM_TYPE => 'text',
                                ApiBase::PARAM_REQUIRED => true,
index 031fbf7..7cbd353 100644 (file)
@@ -48,10 +48,11 @@ class ApiParse extends ApiBase {
                // Get parameters
                $params = $this->extractRequestParams();
 
                // 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', 'text' );
                $this->requireMaxOneParameter( $params, 'page', 'pageid', 'oldid', 'title' );
+               $this->requireMaxOneParameter( $params, 'page', 'pageid', 'oldid', 'revid' );
 
                $text = $params['text'];
                $title = $params['title'];
 
                $text = $params['text'];
                $title = $params['title'];
@@ -169,6 +170,25 @@ class ApiParse extends ApiBase {
                        if ( !$titleObj || $titleObj->isExternal() ) {
                                $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $title ) ] );
                        }
                        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 );
                        $wgTitle = $titleObj;
                        if ( $titleObj->canExist() ) {
                                $pageObj = WikiPage::factory( $titleObj );
@@ -183,7 +203,11 @@ class ApiParse extends ApiBase {
 
                        if ( !$textProvided ) {
                                if ( $titleProvided && ( $prop || $params['generatexml'] ) ) {
 
                        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 = '';
                                }
                                // Prevent warning from ContentHandler::makeContent()
                                $text = '';
@@ -247,9 +271,9 @@ class ApiParse extends ApiBase {
 
                        // Not cached (save or load)
                        if ( $params['pst'] ) {
 
                        // 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 {
                        } 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',
                        ],
                        'text' => [
                                ApiBase::PARAM_TYPE => 'text',
                        ],
+                       'revid' => [
+                               ApiBase::PARAM_TYPE => 'integer',
+                       ],
                        'summary' => null,
                        'page' => null,
                        'pageid' => [
                        'summary' => null,
                        'page' => null,
                        'pageid' => [
index 9fbc012..dbd5451 100644 (file)
        "apihelp-expandtemplates-summary": "Expands all templates within wikitext.",
        "apihelp-expandtemplates-param-title": "Title of page.",
        "apihelp-expandtemplates-param-text": "Wikitext to convert.",
        "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 <nowiki>{{REVISIONID}}</nowiki> and similar variables.",
+       "apihelp-expandtemplates-param-revid": "Revision ID, for <code><nowiki>{{REVISIONID}}</nowiki></code> 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.",
        "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.",
        "apihelp-paraminfo-example-2": "Show info for all submodules of <kbd>[[Special:ApiHelp/query|action=query]]</kbd>.",
 
        "apihelp-parse-summary": "Parses content and returns parser output.",
        "apihelp-paraminfo-example-2": "Show info for all submodules of <kbd>[[Special:ApiHelp/query|action=query]]</kbd>.",
 
        "apihelp-parse-summary": "Parses content and returns parser output.",
-       "apihelp-parse-extended-description": "See the various prop-modules of <kbd>[[Special:ApiHelp/query|action=query]]</kbd> 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 <var>$1page</var>, <var>$1pageid</var>, or <var>$1oldid</var>.\n# Specify content explicitly, using <var>$1text</var>, <var>$1title</var>, and <var>$1contentmodel</var>.\n# Specify only a summary to parse. <var>$1prop</var> should be given an empty value.",
+       "apihelp-parse-extended-description": "See the various prop-modules of <kbd>[[Special:ApiHelp/query|action=query]]</kbd> 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 <var>$1page</var>, <var>$1pageid</var>, or <var>$1oldid</var>.\n# Specify content explicitly, using <var>$1text</var>, <var>$1title</var>, <var>$1revid</var>, and <var>$1contentmodel</var>.\n# Specify only a summary to parse. <var>$1prop</var> should be given an empty value.",
        "apihelp-parse-param-title": "Title of page the text belongs to. If omitted, <var>$1contentmodel</var> must be specified, and [[API]] will be used as the title.",
        "apihelp-parse-param-text": "Text to parse. Use <var>$1title</var> or <var>$1contentmodel</var> to control the content model.",
        "apihelp-parse-param-title": "Title of page the text belongs to. If omitted, <var>$1contentmodel</var> must be specified, and [[API]] will be used as the title.",
        "apihelp-parse-param-text": "Text to parse. Use <var>$1title</var> or <var>$1contentmodel</var> to control the content model.",
+       "apihelp-parse-param-revid": "Revision ID, for <code><nowiki>{{REVISIONID}}</nowiki></code> 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 <var>$1text</var> and <var>$1title</var>.",
        "apihelp-parse-param-pageid": "Parse the content of this page. Overrides <var>$1page</var>.",
        "apihelp-parse-param-summary": "Summary to parse.",
        "apihelp-parse-param-page": "Parse the content of this page. Cannot be used together with <var>$1text</var> and <var>$1title</var>.",
        "apihelp-parse-param-pageid": "Parse the content of this page. Overrides <var>$1page</var>.",
        "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 <var>title</var> or <var>contentmodel</var> was given, assuming $1.",
        "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 <var>title</var> or <var>contentmodel</var> was given, assuming $1.",
+       "apiwarn-parse-revidwithouttext": "<var>revid</var> used without <var>text</var>, and parsed page properties were requested. Did you mean to use <var>oldid</var> instead of <var>revid</var>?",
        "apiwarn-parse-titlewithouttext": "<var>title</var> used without <var>text</var>, and parsed page properties were requested. Did you mean to use <var>page</var> instead of <var>title</var>?",
        "apiwarn-redirectsandrevids": "Redirect resolution cannot be used together with the <var>revids</var> parameter. Any redirects the <var>revids</var> point to have not been resolved.",
        "apiwarn-tokennotallowed": "Action \"$1\" is not allowed for the current user.",
        "apiwarn-parse-titlewithouttext": "<var>title</var> used without <var>text</var>, and parsed page properties were requested. Did you mean to use <var>page</var> instead of <var>title</var>?",
        "apiwarn-redirectsandrevids": "Redirect resolution cannot be used together with the <var>revids</var> parameter. Any redirects the <var>revids</var> point to have not been resolved.",
        "apiwarn-tokennotallowed": "Action \"$1\" is not allowed for the current user.",
index c878a53..6aaaac7 100644 (file)
        "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-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 <code><<nowiki />nowiki>{{<nowiki />REVISIONID}}<<nowiki />/nowiki></code>}}",
+       "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}}",
        "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}}",
        "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-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}}",
        "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}}",
        "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-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.",
        "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.",