Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 138 |
|
0.00% |
0 / 5 |
CRAP | |
0.00% |
0 / 1 |
ApiExpandTemplates | |
0.00% |
0 / 138 |
|
0.00% |
0 / 5 |
1406 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 99 |
|
0.00% |
0 / 1 |
1122 | |||
getAllowedParams | |
0.00% |
0 / 31 |
|
0.00% |
0 / 1 |
2 | |||
getExamplesMessages | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
getHelpUrls | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * Copyright © 2007 Yuri Astrakhan "<Firstname><Lastname>@gmail.com" |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by |
7 | * the Free Software Foundation; either version 2 of the License, or |
8 | * (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License along |
16 | * with this program; if not, write to the Free Software Foundation, Inc., |
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
18 | * http://www.gnu.org/copyleft/gpl.html |
19 | * |
20 | * @file |
21 | */ |
22 | |
23 | use MediaWiki\Parser\Parser; |
24 | use MediaWiki\Revision\RevisionStore; |
25 | use MediaWiki\Title\Title; |
26 | use Wikimedia\ParamValidator\ParamValidator; |
27 | |
28 | /** |
29 | * API module that functions as a shortcut to the wikitext preprocessor. Expands |
30 | * any templates in a provided string, and returns the result of this expansion |
31 | * to the caller. |
32 | * |
33 | * @ingroup API |
34 | */ |
35 | class ApiExpandTemplates extends ApiBase { |
36 | private RevisionStore $revisionStore; |
37 | private ParserFactory $parserFactory; |
38 | |
39 | /** |
40 | * @param ApiMain $main |
41 | * @param string $action |
42 | * @param RevisionStore $revisionStore |
43 | * @param ParserFactory $parserFactory |
44 | */ |
45 | public function __construct( |
46 | ApiMain $main, |
47 | $action, |
48 | RevisionStore $revisionStore, |
49 | ParserFactory $parserFactory |
50 | ) { |
51 | parent::__construct( $main, $action ); |
52 | $this->revisionStore = $revisionStore; |
53 | $this->parserFactory = $parserFactory; |
54 | } |
55 | |
56 | public function execute() { |
57 | // Cache may vary on the user because ParserOptions gets data from it |
58 | $this->getMain()->setCacheMode( 'anon-public-user-private' ); |
59 | |
60 | // Get parameters |
61 | $params = $this->extractRequestParams(); |
62 | $this->requireMaxOneParameter( $params, 'prop', 'generatexml' ); |
63 | |
64 | $title = $params['title']; |
65 | if ( $title === null ) { |
66 | $titleProvided = false; |
67 | // A title is needed for parsing, so arbitrarily choose one |
68 | $title = 'API'; |
69 | } else { |
70 | $titleProvided = true; |
71 | } |
72 | |
73 | if ( $params['prop'] === null ) { |
74 | $this->addDeprecation( |
75 | [ 'apiwarn-deprecation-missingparam', 'prop' ], 'action=expandtemplates&!prop' |
76 | ); |
77 | $prop = []; |
78 | } else { |
79 | $prop = array_fill_keys( $params['prop'], true ); |
80 | } |
81 | |
82 | $titleObj = Title::newFromText( $title ); |
83 | if ( !$titleObj || $titleObj->isExternal() ) { |
84 | $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['title'] ) ] ); |
85 | } |
86 | |
87 | // Get title and revision ID for parser |
88 | $revid = $params['revid']; |
89 | if ( $revid !== null ) { |
90 | $rev = $this->revisionStore->getRevisionById( $revid ); |
91 | if ( !$rev ) { |
92 | $this->dieWithError( [ 'apierror-nosuchrevid', $revid ] ); |
93 | } |
94 | $pTitleObj = $titleObj; |
95 | $titleObj = Title::newFromLinkTarget( $rev->getPageAsLinkTarget() ); |
96 | if ( $titleProvided && !$titleObj->equals( $pTitleObj ) ) { |
97 | $this->addWarning( [ 'apierror-revwrongpage', $rev->getId(), |
98 | wfEscapeWikiText( $pTitleObj->getPrefixedText() ) ] ); |
99 | } |
100 | } |
101 | |
102 | $result = $this->getResult(); |
103 | |
104 | // Parse text |
105 | $options = ParserOptions::newFromContext( $this->getContext() ); |
106 | |
107 | if ( $params['includecomments'] ) { |
108 | $options->setRemoveComments( false ); |
109 | } |
110 | |
111 | $reset = null; |
112 | $suppressCache = false; |
113 | $this->getHookRunner()->onApiMakeParserOptions( |
114 | $options, $titleObj, $params, $this, $reset, $suppressCache ); |
115 | |
116 | $retval = []; |
117 | |
118 | if ( isset( $prop['parsetree'] ) || $params['generatexml'] ) { |
119 | $parser = $this->parserFactory->getInstance(); |
120 | $parser->startExternalParse( $titleObj, $options, Parser::OT_PREPROCESS ); |
121 | $dom = $parser->preprocessToDom( $params['text'] ); |
122 | // @phan-suppress-next-line PhanUndeclaredMethodInCallable |
123 | if ( is_callable( [ $dom, 'saveXML' ] ) ) { |
124 | // @phan-suppress-next-line PhanUndeclaredMethod |
125 | $xml = $dom->saveXML(); |
126 | } else { |
127 | // @phan-suppress-next-line PhanUndeclaredMethod |
128 | $xml = $dom->__toString(); |
129 | } |
130 | if ( isset( $prop['parsetree'] ) ) { |
131 | unset( $prop['parsetree'] ); |
132 | $retval['parsetree'] = $xml; |
133 | } else { |
134 | // the old way |
135 | $result->addValue( null, 'parsetree', $xml ); |
136 | $result->addValue( null, ApiResult::META_BC_SUBELEMENTS, [ 'parsetree' ] ); |
137 | } |
138 | } |
139 | |
140 | // if they didn't want any output except (probably) the parse tree, |
141 | // then don't bother actually fully expanding it |
142 | if ( $prop || $params['prop'] === null ) { |
143 | $parser = $this->parserFactory->getInstance(); |
144 | $parser->startExternalParse( $titleObj, $options, Parser::OT_PREPROCESS ); |
145 | $frame = $parser->getPreprocessor()->newFrame(); |
146 | $wikitext = $parser->preprocess( $params['text'], $titleObj, $options, $revid, $frame ); |
147 | if ( $params['prop'] === null ) { |
148 | // the old way |
149 | ApiResult::setContentValue( $retval, 'wikitext', $wikitext ); |
150 | } else { |
151 | $p_output = $parser->getOutput(); |
152 | if ( isset( $prop['categories'] ) ) { |
153 | $categories = $p_output->getCategoryNames(); |
154 | if ( $categories ) { |
155 | $defaultSortKey = $p_output->getPageProperty( 'defaultsort' ) ?? ''; |
156 | $categories_result = []; |
157 | foreach ( $categories as $category ) { |
158 | $entry = [ |
159 | // Note that ::getCategorySortKey() returns |
160 | // the empty string '' to mean |
161 | // "use the default sort key" |
162 | 'sortkey' => $p_output->getCategorySortKey( $category ) ?: $defaultSortKey, |
163 | ]; |
164 | ApiResult::setContentValue( $entry, 'category', $category ); |
165 | $categories_result[] = $entry; |
166 | } |
167 | ApiResult::setIndexedTagName( $categories_result, 'category' ); |
168 | $retval['categories'] = $categories_result; |
169 | } |
170 | } |
171 | if ( isset( $prop['properties'] ) ) { |
172 | $properties = $p_output->getPageProperties(); |
173 | if ( $properties ) { |
174 | ApiResult::setArrayType( $properties, 'BCkvp', 'name' ); |
175 | ApiResult::setIndexedTagName( $properties, 'property' ); |
176 | $retval['properties'] = $properties; |
177 | } |
178 | } |
179 | if ( isset( $prop['volatile'] ) ) { |
180 | $retval['volatile'] = $frame->isVolatile(); |
181 | } |
182 | if ( isset( $prop['ttl'] ) && $frame->getTTL() !== null ) { |
183 | $retval['ttl'] = $frame->getTTL(); |
184 | } |
185 | if ( isset( $prop['wikitext'] ) ) { |
186 | $retval['wikitext'] = $wikitext; |
187 | } |
188 | if ( isset( $prop['modules'] ) ) { |
189 | $retval['modules'] = array_values( array_unique( $p_output->getModules() ) ); |
190 | // Deprecated since 1.32 (T188689) |
191 | $retval['modulescripts'] = []; |
192 | $retval['modulestyles'] = array_values( array_unique( $p_output->getModuleStyles() ) ); |
193 | } |
194 | if ( isset( $prop['jsconfigvars'] ) ) { |
195 | $showStrategyKeys = (bool)( $params['showstrategykeys'] ); |
196 | $retval['jsconfigvars'] = |
197 | ApiResult::addMetadataToResultVars( $p_output->getJsConfigVars( $showStrategyKeys ) ); |
198 | } |
199 | if ( isset( $prop['encodedjsconfigvars'] ) ) { |
200 | $retval['encodedjsconfigvars'] = FormatJson::encode( |
201 | $p_output->getJsConfigVars(), false, FormatJson::ALL_OK |
202 | ); |
203 | $retval[ApiResult::META_SUBELEMENTS][] = 'encodedjsconfigvars'; |
204 | } |
205 | if ( isset( $prop['modules'] ) && |
206 | !isset( $prop['jsconfigvars'] ) && !isset( $prop['encodedjsconfigvars'] ) ) { |
207 | $this->addWarning( 'apiwarn-moduleswithoutvars' ); |
208 | } |
209 | } |
210 | } |
211 | ApiResult::setSubelementsList( $retval, [ 'wikitext', 'parsetree' ] ); |
212 | $result->addValue( null, $this->getModuleName(), $retval ); |
213 | } |
214 | |
215 | public function getAllowedParams() { |
216 | return [ |
217 | 'title' => null, |
218 | 'text' => [ |
219 | ParamValidator::PARAM_TYPE => 'text', |
220 | ParamValidator::PARAM_REQUIRED => true, |
221 | ], |
222 | 'revid' => [ |
223 | ParamValidator::PARAM_TYPE => 'integer', |
224 | ], |
225 | 'prop' => [ |
226 | ParamValidator::PARAM_TYPE => [ |
227 | 'wikitext', |
228 | 'categories', |
229 | 'properties', |
230 | 'volatile', |
231 | 'ttl', |
232 | 'modules', |
233 | 'jsconfigvars', |
234 | 'encodedjsconfigvars', |
235 | 'parsetree', |
236 | ], |
237 | ParamValidator::PARAM_ISMULTI => true, |
238 | ApiBase::PARAM_HELP_MSG_PER_VALUE => [], |
239 | ], |
240 | 'includecomments' => false, |
241 | 'showstrategykeys' => false, |
242 | 'generatexml' => [ |
243 | ParamValidator::PARAM_TYPE => 'boolean', |
244 | ParamValidator::PARAM_DEPRECATED => true, |
245 | ], |
246 | ]; |
247 | } |
248 | |
249 | protected function getExamplesMessages() { |
250 | return [ |
251 | 'action=expandtemplates&text={{Project:Sandbox}}' |
252 | => 'apihelp-expandtemplates-example-simple', |
253 | ]; |
254 | } |
255 | |
256 | public function getHelpUrls() { |
257 | return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Expandtemplates'; |
258 | } |
259 | } |