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