ApiParse: Forbid section=new when using page, pageid, or oldid
[lhc/web/wiklou.git] / includes / api / ApiParse.php
1 <?php
2 /**
3 * Created on Dec 01, 2007
4 *
5 * Copyright © 2007 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * http://www.gnu.org/copyleft/gpl.html
21 *
22 * @file
23 */
24
25 /**
26 * @ingroup API
27 */
28 class ApiParse extends ApiBase {
29
30 /** @var string $section */
31 private $section = null;
32
33 /** @var Content $content */
34 private $content = null;
35
36 /** @var Content $pstContent */
37 private $pstContent = null;
38
39 public function execute() {
40 // The data is hot but user-dependent, like page views, so we set vary cookies
41 $this->getMain()->setCacheMode( 'anon-public-user-private' );
42
43 // Get parameters
44 $params = $this->extractRequestParams();
45 $text = $params['text'];
46 $title = $params['title'];
47 if ( $title === null ) {
48 $titleProvided = false;
49 // A title is needed for parsing, so arbitrarily choose one
50 $title = 'API';
51 } else {
52 $titleProvided = true;
53 }
54
55 $page = $params['page'];
56 $pageid = $params['pageid'];
57 $oldid = $params['oldid'];
58
59 $model = $params['contentmodel'];
60 $format = $params['contentformat'];
61
62 if ( !is_null( $page ) && ( !is_null( $text ) || $titleProvided ) ) {
63 $this->dieUsage(
64 'The page parameter cannot be used together with the text and title parameters',
65 'params'
66 );
67 }
68
69 $prop = array_flip( $params['prop'] );
70
71 if ( isset( $params['section'] ) ) {
72 $this->section = $params['section'];
73 if ( !preg_match( '/^((T-)?\d+|new)$/', $this->section ) ) {
74 $this->dieUsage( "The section parameter must be a valid section id or 'new'", "invalidsection" );
75 }
76 } else {
77 $this->section = false;
78 }
79
80 // The parser needs $wgTitle to be set, apparently the
81 // $title parameter in Parser::parse isn't enough *sigh*
82 // TODO: Does this still need $wgTitle?
83 global $wgParser, $wgTitle;
84
85 $redirValues = null;
86
87 // Return result
88 $result = $this->getResult();
89
90 if ( !is_null( $oldid ) || !is_null( $pageid ) || !is_null( $page ) ) {
91 if ( $this->section === 'new' ) {
92 $this->dieUsage( 'section=new cannot be combined with oldid, pageid or page parameters. Please use text', 'params' );
93 }
94 if ( !is_null( $oldid ) ) {
95 // Don't use the parser cache
96 $rev = Revision::newFromId( $oldid );
97 if ( !$rev ) {
98 $this->dieUsage( "There is no revision ID $oldid", 'missingrev' );
99 }
100 if ( !$rev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) {
101 $this->dieUsage( "You don't have permission to view deleted revisions", 'permissiondenied' );
102 }
103
104 $titleObj = $rev->getTitle();
105 $wgTitle = $titleObj;
106 $pageObj = WikiPage::factory( $titleObj );
107 $popts = $this->makeParserOptions( $pageObj, $params );
108
109 // If for some reason the "oldid" is actually the current revision, it may be cached
110 if ( $rev->isCurrent() ) {
111 // May get from/save to parser cache
112 $p_result = $this->getParsedContent( $pageObj, $popts,
113 $pageid, isset( $prop['wikitext'] ) );
114 } else { // This is an old revision, so get the text differently
115 $this->content = $rev->getContent( Revision::FOR_THIS_USER, $this->getUser() );
116
117 if ( $this->section !== false ) {
118 $this->content = $this->getSectionContent( $this->content, 'r' . $rev->getId() );
119 }
120
121 // Should we save old revision parses to the parser cache?
122 $p_result = $this->content->getParserOutput( $titleObj, $rev->getId(), $popts );
123 }
124 } else { // Not $oldid, but $pageid or $page
125 if ( $params['redirects'] ) {
126 $reqParams = array(
127 'action' => 'query',
128 'redirects' => '',
129 );
130 if ( !is_null( $pageid ) ) {
131 $reqParams['pageids'] = $pageid;
132 } else { // $page
133 $reqParams['titles'] = $page;
134 }
135 $req = new FauxRequest( $reqParams );
136 $main = new ApiMain( $req );
137 $main->execute();
138 $data = $main->getResultData();
139 $redirValues = isset( $data['query']['redirects'] )
140 ? $data['query']['redirects']
141 : array();
142 $to = $page;
143 foreach ( (array)$redirValues as $r ) {
144 $to = $r['to'];
145 }
146 $pageParams = array( 'title' => $to );
147 } elseif ( !is_null( $pageid ) ) {
148 $pageParams = array( 'pageid' => $pageid );
149 } else { // $page
150 $pageParams = array( 'title' => $page );
151 }
152
153 $pageObj = $this->getTitleOrPageId( $pageParams, 'fromdb' );
154 $titleObj = $pageObj->getTitle();
155 if ( !$titleObj || !$titleObj->exists() ) {
156 $this->dieUsage( "The page you specified doesn't exist", 'missingtitle' );
157 }
158 $wgTitle = $titleObj;
159
160 if ( isset( $prop['revid'] ) ) {
161 $oldid = $pageObj->getLatest();
162 }
163
164 $popts = $this->makeParserOptions( $pageObj, $params );
165
166 // Potentially cached
167 $p_result = $this->getParsedContent( $pageObj, $popts, $pageid,
168 isset( $prop['wikitext'] ) );
169 }
170 } else { // Not $oldid, $pageid, $page. Hence based on $text
171 $titleObj = Title::newFromText( $title );
172 if ( !$titleObj || $titleObj->isExternal() ) {
173 $this->dieUsageMsg( array( 'invalidtitle', $title ) );
174 }
175 $wgTitle = $titleObj;
176 if ( $titleObj->canExist() ) {
177 $pageObj = WikiPage::factory( $titleObj );
178 } else {
179 // Do like MediaWiki::initializeArticle()
180 $article = Article::newFromTitle( $titleObj, $this->getContext() );
181 $pageObj = $article->getPage();
182 }
183
184 $popts = $this->makeParserOptions( $pageObj, $params );
185 $textProvided = !is_null( $text );
186
187 if ( !$textProvided ) {
188 if ( $titleProvided && ( $prop || $params['generatexml'] ) ) {
189 $this->setWarning(
190 "'title' used without 'text', and parsed page properties were requested " .
191 "(did you mean to use 'page' instead of 'title'?)"
192 );
193 }
194 // Prevent warning from ContentHandler::makeContent()
195 $text = '';
196 }
197
198 // If we are parsing text, do not use the content model of the default
199 // API title, but default to wikitext to keep BC.
200 if ( $textProvided && !$titleProvided && is_null( $model ) ) {
201 $model = CONTENT_MODEL_WIKITEXT;
202 $this->setWarning( "No 'title' or 'contentmodel' was given, assuming $model." );
203 }
204
205 try {
206 $this->content = ContentHandler::makeContent( $text, $titleObj, $model, $format );
207 } catch ( MWContentSerializationException $ex ) {
208 $this->dieUsage( $ex->getMessage(), 'parseerror' );
209 }
210
211 if ( $this->section !== false ) {
212 if ( $this->section === 'new' ) {
213 // Insert the section title above the content.
214 if ( !is_null( $params['sectiontitle'] ) && $params['sectiontitle'] !== '' ) {
215 $this->content = $this->content->addSectionHeader( $params['sectiontitle'] );
216 }
217 } else {
218 $this->content = $this->getSectionContent( $this->content, $titleObj->getPrefixedText() );
219 }
220 }
221
222 if ( $params['pst'] || $params['onlypst'] ) {
223 $this->pstContent = $this->content->preSaveTransform( $titleObj, $this->getUser(), $popts );
224 }
225 if ( $params['onlypst'] ) {
226 // Build a result and bail out
227 $result_array = array();
228 $result_array['text'] = array();
229 ApiResult::setContent( $result_array['text'], $this->pstContent->serialize( $format ) );
230 if ( isset( $prop['wikitext'] ) ) {
231 $result_array['wikitext'] = array();
232 ApiResult::setContent( $result_array['wikitext'], $this->content->serialize( $format ) );
233 }
234 if ( !is_null( $params['summary'] ) ) {
235 $result_array['parsedsummary'] = array();
236 ApiResult::setContent(
237 $result_array['parsedsummary'],
238 Linker::formatComment( $params['summary'], $titleObj )
239 );
240 }
241
242 $result->addValue( null, $this->getModuleName(), $result_array );
243
244 return;
245 }
246
247 // Not cached (save or load)
248 if ( $params['pst'] ) {
249 $p_result = $this->pstContent->getParserOutput( $titleObj, null, $popts );
250 } else {
251 $p_result = $this->content->getParserOutput( $titleObj, null, $popts );
252 }
253 }
254
255 $result_array = array();
256
257 $result_array['title'] = $titleObj->getPrefixedText();
258
259 if ( !is_null( $oldid ) ) {
260 $result_array['revid'] = intval( $oldid );
261 }
262
263 if ( $params['redirects'] && !is_null( $redirValues ) ) {
264 $result_array['redirects'] = $redirValues;
265 }
266
267 if ( $params['disabletoc'] ) {
268 $p_result->setTOCEnabled( false );
269 }
270
271 if ( isset( $prop['text'] ) ) {
272 $result_array['text'] = array();
273 ApiResult::setContent( $result_array['text'], $p_result->getText() );
274 }
275
276 if ( !is_null( $params['summary'] ) ) {
277 $result_array['parsedsummary'] = array();
278 ApiResult::setContent(
279 $result_array['parsedsummary'],
280 Linker::formatComment( $params['summary'], $titleObj )
281 );
282 }
283
284 if ( isset( $prop['langlinks'] ) ) {
285 $langlinks = $p_result->getLanguageLinks();
286
287 if ( $params['effectivelanglinks'] ) {
288 // Link flags are ignored for now, but may in the future be
289 // included in the result.
290 $linkFlags = array();
291 Hooks::run( 'LanguageLinks', array( $titleObj, &$langlinks, &$linkFlags ) );
292 }
293 } else {
294 $langlinks = false;
295 }
296
297 if ( isset( $prop['langlinks'] ) ) {
298 $result_array['langlinks'] = $this->formatLangLinks( $langlinks );
299 }
300 if ( isset( $prop['categories'] ) ) {
301 $result_array['categories'] = $this->formatCategoryLinks( $p_result->getCategories() );
302 }
303 if ( isset( $prop['categorieshtml'] ) ) {
304 $categoriesHtml = $this->categoriesHtml( $p_result->getCategories() );
305 $result_array['categorieshtml'] = array();
306 ApiResult::setContent( $result_array['categorieshtml'], $categoriesHtml );
307 }
308 if ( isset( $prop['links'] ) ) {
309 $result_array['links'] = $this->formatLinks( $p_result->getLinks() );
310 }
311 if ( isset( $prop['templates'] ) ) {
312 $result_array['templates'] = $this->formatLinks( $p_result->getTemplates() );
313 }
314 if ( isset( $prop['images'] ) ) {
315 $result_array['images'] = array_keys( $p_result->getImages() );
316 }
317 if ( isset( $prop['externallinks'] ) ) {
318 $result_array['externallinks'] = array_keys( $p_result->getExternalLinks() );
319 }
320 if ( isset( $prop['sections'] ) ) {
321 $result_array['sections'] = $p_result->getSections();
322 }
323
324 if ( isset( $prop['displaytitle'] ) ) {
325 $result_array['displaytitle'] = $p_result->getDisplayTitle() ?
326 $p_result->getDisplayTitle() :
327 $titleObj->getPrefixedText();
328 }
329
330 if ( isset( $prop['headitems'] ) || isset( $prop['headhtml'] ) ) {
331 $context = $this->getContext();
332 $context->setTitle( $titleObj );
333 $context->getOutput()->addParserOutputMetadata( $p_result );
334
335 if ( isset( $prop['headitems'] ) ) {
336 $headItems = $this->formatHeadItems( $p_result->getHeadItems() );
337
338 $css = $this->formatCss( $context->getOutput()->buildCssLinksArray() );
339
340 $scripts = array( $context->getOutput()->getHeadScripts() );
341
342 $result_array['headitems'] = array_merge( $headItems, $css, $scripts );
343 }
344
345 if ( isset( $prop['headhtml'] ) ) {
346 $result_array['headhtml'] = array();
347 ApiResult::setContent(
348 $result_array['headhtml'],
349 $context->getOutput()->headElement( $context->getSkin() )
350 );
351 }
352 }
353
354 if ( isset( $prop['modules'] ) ) {
355 $result_array['modules'] = array_values( array_unique( $p_result->getModules() ) );
356 $result_array['modulescripts'] = array_values( array_unique( $p_result->getModuleScripts() ) );
357 $result_array['modulestyles'] = array_values( array_unique( $p_result->getModuleStyles() ) );
358 $result_array['modulemessages'] = array_values( array_unique( $p_result->getModuleMessages() ) );
359 }
360
361 if ( isset( $prop['indicators'] ) ) {
362 foreach ( $p_result->getIndicators() as $name => $content ) {
363 $indicator = array( 'name' => $name );
364 ApiResult::setContent( $indicator, $content );
365 $result_array['indicators'][] = $indicator;
366 }
367 }
368
369 if ( isset( $prop['iwlinks'] ) ) {
370 $result_array['iwlinks'] = $this->formatIWLinks( $p_result->getInterwikiLinks() );
371 }
372
373 if ( isset( $prop['wikitext'] ) ) {
374 $result_array['wikitext'] = array();
375 ApiResult::setContent( $result_array['wikitext'], $this->content->serialize( $format ) );
376 if ( !is_null( $this->pstContent ) ) {
377 $result_array['psttext'] = array();
378 ApiResult::setContent( $result_array['psttext'], $this->pstContent->serialize( $format ) );
379 }
380 }
381 if ( isset( $prop['properties'] ) ) {
382 $result_array['properties'] = $this->formatProperties( $p_result->getProperties() );
383 }
384
385 if ( isset( $prop['limitreportdata'] ) ) {
386 $result_array['limitreportdata'] =
387 $this->formatLimitReportData( $p_result->getLimitReportData() );
388 }
389 if ( isset( $prop['limitreporthtml'] ) ) {
390 $limitreportHtml = EditPage::getPreviewLimitReport( $p_result );
391 $result_array['limitreporthtml'] = array();
392 ApiResult::setContent( $result_array['limitreporthtml'], $limitreportHtml );
393 }
394
395 if ( $params['generatexml'] ) {
396 if ( $this->content->getModel() != CONTENT_MODEL_WIKITEXT ) {
397 $this->dieUsage( "generatexml is only supported for wikitext content", "notwikitext" );
398 }
399
400 $wgParser->startExternalParse( $titleObj, $popts, Parser::OT_PREPROCESS );
401 $dom = $wgParser->preprocessToDom( $this->content->getNativeData() );
402 if ( is_callable( array( $dom, 'saveXML' ) ) ) {
403 $xml = $dom->saveXML();
404 } else {
405 $xml = $dom->__toString();
406 }
407 $result_array['parsetree'] = array();
408 ApiResult::setContent( $result_array['parsetree'], $xml );
409 }
410
411 $result_mapping = array(
412 'redirects' => 'r',
413 'langlinks' => 'll',
414 'categories' => 'cl',
415 'links' => 'pl',
416 'templates' => 'tl',
417 'images' => 'img',
418 'externallinks' => 'el',
419 'iwlinks' => 'iw',
420 'sections' => 's',
421 'headitems' => 'hi',
422 'modules' => 'm',
423 'indicators' => 'ind',
424 'modulescripts' => 'm',
425 'modulestyles' => 'm',
426 'modulemessages' => 'm',
427 'properties' => 'pp',
428 'limitreportdata' => 'lr',
429 );
430 $this->setIndexedTagNames( $result_array, $result_mapping );
431 $result->addValue( null, $this->getModuleName(), $result_array );
432 }
433
434 /**
435 * Constructs a ParserOptions object
436 *
437 * @param WikiPage $pageObj
438 * @param array $params
439 *
440 * @return ParserOptions
441 */
442 protected function makeParserOptions( WikiPage $pageObj, array $params ) {
443
444 $popts = $pageObj->makeParserOptions( $this->getContext() );
445 $popts->enableLimitReport( !$params['disablepp'] );
446 $popts->setIsPreview( $params['preview'] || $params['sectionpreview'] );
447 $popts->setIsSectionPreview( $params['sectionpreview'] );
448 $popts->setEditSection( !$params['disableeditsection'] );
449
450 return $popts;
451 }
452
453 /**
454 * @param WikiPage $page
455 * @param ParserOptions $popts
456 * @param int $pageId
457 * @param bool $getWikitext
458 * @return ParserOutput
459 */
460 private function getParsedContent( WikiPage $page, $popts, $pageId = null, $getWikitext = false ) {
461 $this->content = $page->getContent( Revision::RAW ); //XXX: really raw?
462
463 if ( $this->section !== false && $this->content !== null ) {
464 $this->content = $this->getSectionContent(
465 $this->content,
466 !is_null( $pageId ) ? 'page id ' . $pageId : $page->getTitle()->getPrefixedText()
467 );
468
469 // Not cached (save or load)
470 return $this->content->getParserOutput( $page->getTitle(), null, $popts );
471 }
472
473 // Try the parser cache first
474 // getParserOutput will save to Parser cache if able
475 $pout = $page->getParserOutput( $popts );
476 if ( !$pout ) {
477 $this->dieUsage( "There is no revision ID {$page->getLatest()}", 'missingrev' );
478 }
479 if ( $getWikitext ) {
480 $this->content = $page->getContent( Revision::RAW );
481 }
482
483 return $pout;
484 }
485
486 /**
487 * @param Content $content
488 * @param string $what Identifies the content in error messages, e.g. page title.
489 * @return Content|bool
490 */
491 private function getSectionContent( Content $content, $what ) {
492 // Not cached (save or load)
493 $section = $content->getSection( $this->section );
494 if ( $section === false ) {
495 $this->dieUsage( "There is no section {$this->section} in " . $what, 'nosuchsection' );
496 }
497 if ( $section === null ) {
498 $this->dieUsage( "Sections are not supported by " . $what, 'nosuchsection' );
499 $section = false;
500 }
501
502 return $section;
503 }
504
505 private function formatLangLinks( $links ) {
506 $result = array();
507 foreach ( $links as $link ) {
508 $entry = array();
509 $bits = explode( ':', $link, 2 );
510 $title = Title::newFromText( $link );
511
512 $entry['lang'] = $bits[0];
513 if ( $title ) {
514 $entry['url'] = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
515 // localised language name in 'uselang' language
516 $entry['langname'] = Language::fetchLanguageName(
517 $title->getInterwiki(),
518 $this->getLanguage()->getCode()
519 );
520
521 // native language name
522 $entry['autonym'] = Language::fetchLanguageName( $title->getInterwiki() );
523 }
524 ApiResult::setContent( $entry, $bits[1] );
525 $result[] = $entry;
526 }
527
528 return $result;
529 }
530
531 private function formatCategoryLinks( $links ) {
532 $result = array();
533
534 if ( !$links ) {
535 return $result;
536 }
537
538 // Fetch hiddencat property
539 $lb = new LinkBatch;
540 $lb->setArray( array( NS_CATEGORY => $links ) );
541 $db = $this->getDB();
542 $res = $db->select( array( 'page', 'page_props' ),
543 array( 'page_title', 'pp_propname' ),
544 $lb->constructSet( 'page', $db ),
545 __METHOD__,
546 array(),
547 array( 'page_props' => array(
548 'LEFT JOIN', array( 'pp_propname' => 'hiddencat', 'pp_page = page_id' )
549 ) )
550 );
551 $hiddencats = array();
552 foreach ( $res as $row ) {
553 $hiddencats[$row->page_title] = isset( $row->pp_propname );
554 }
555
556 foreach ( $links as $link => $sortkey ) {
557 $entry = array();
558 $entry['sortkey'] = $sortkey;
559 ApiResult::setContent( $entry, $link );
560 if ( !isset( $hiddencats[$link] ) ) {
561 $entry['missing'] = '';
562 } elseif ( $hiddencats[$link] ) {
563 $entry['hidden'] = '';
564 }
565 $result[] = $entry;
566 }
567
568 return $result;
569 }
570
571 private function categoriesHtml( $categories ) {
572 $context = $this->getContext();
573 $context->getOutput()->addCategoryLinks( $categories );
574
575 return $context->getSkin()->getCategories();
576 }
577
578 private function formatLinks( $links ) {
579 $result = array();
580 foreach ( $links as $ns => $nslinks ) {
581 foreach ( $nslinks as $title => $id ) {
582 $entry = array();
583 $entry['ns'] = $ns;
584 ApiResult::setContent( $entry, Title::makeTitle( $ns, $title )->getFullText() );
585 if ( $id != 0 ) {
586 $entry['exists'] = '';
587 }
588 $result[] = $entry;
589 }
590 }
591
592 return $result;
593 }
594
595 private function formatIWLinks( $iw ) {
596 $result = array();
597 foreach ( $iw as $prefix => $titles ) {
598 foreach ( array_keys( $titles ) as $title ) {
599 $entry = array();
600 $entry['prefix'] = $prefix;
601
602 $title = Title::newFromText( "{$prefix}:{$title}" );
603 if ( $title ) {
604 $entry['url'] = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
605 }
606
607 ApiResult::setContent( $entry, $title->getFullText() );
608 $result[] = $entry;
609 }
610 }
611
612 return $result;
613 }
614
615 private function formatHeadItems( $headItems ) {
616 $result = array();
617 foreach ( $headItems as $tag => $content ) {
618 $entry = array();
619 $entry['tag'] = $tag;
620 ApiResult::setContent( $entry, $content );
621 $result[] = $entry;
622 }
623
624 return $result;
625 }
626
627 private function formatProperties( $properties ) {
628 $result = array();
629 foreach ( $properties as $name => $value ) {
630 $entry = array();
631 $entry['name'] = $name;
632 ApiResult::setContent( $entry, $value );
633 $result[] = $entry;
634 }
635
636 return $result;
637 }
638
639 private function formatCss( $css ) {
640 $result = array();
641 foreach ( $css as $file => $link ) {
642 $entry = array();
643 $entry['file'] = $file;
644 ApiResult::setContent( $entry, $link );
645 $result[] = $entry;
646 }
647
648 return $result;
649 }
650
651 private function formatLimitReportData( $limitReportData ) {
652 $result = array();
653 $apiResult = $this->getResult();
654
655 foreach ( $limitReportData as $name => $value ) {
656 $entry = array();
657 $entry['name'] = $name;
658 if ( !is_array( $value ) ) {
659 $value = array( $value );
660 }
661 $apiResult->setIndexedTagName( $value, 'param' );
662 $apiResult->setIndexedTagName_recursive( $value, 'param' );
663 $entry = array_merge( $entry, $value );
664 $result[] = $entry;
665 }
666
667 return $result;
668 }
669
670 private function setIndexedTagNames( &$array, $mapping ) {
671 foreach ( $mapping as $key => $name ) {
672 if ( isset( $array[$key] ) ) {
673 $this->getResult()->setIndexedTagName( $array[$key], $name );
674 }
675 }
676 }
677
678 public function getAllowedParams() {
679 return array(
680 'title' => null,
681 'text' => null,
682 'summary' => null,
683 'page' => null,
684 'pageid' => array(
685 ApiBase::PARAM_TYPE => 'integer',
686 ),
687 'redirects' => false,
688 'oldid' => array(
689 ApiBase::PARAM_TYPE => 'integer',
690 ),
691 'prop' => array(
692 ApiBase::PARAM_DFLT => 'text|langlinks|categories|links|templates|' .
693 'images|externallinks|sections|revid|displaytitle|iwlinks|properties',
694 ApiBase::PARAM_ISMULTI => true,
695 ApiBase::PARAM_TYPE => array(
696 'text',
697 'langlinks',
698 'categories',
699 'categorieshtml',
700 'links',
701 'templates',
702 'images',
703 'externallinks',
704 'sections',
705 'revid',
706 'displaytitle',
707 'headitems',
708 'headhtml',
709 'modules',
710 'indicators',
711 'iwlinks',
712 'wikitext',
713 'properties',
714 'limitreportdata',
715 'limitreporthtml',
716 )
717 ),
718 'pst' => false,
719 'onlypst' => false,
720 'effectivelanglinks' => false,
721 'section' => null,
722 'sectiontitle' => array(
723 ApiBase::PARAM_TYPE => 'string',
724 ),
725 'disablepp' => false,
726 'disableeditsection' => false,
727 'generatexml' => array(
728 ApiBase::PARAM_DFLT => false,
729 ApiBase::PARAM_HELP_MSG => array(
730 'apihelp-parse-param-generatexml', CONTENT_MODEL_WIKITEXT
731 ),
732 ),
733 'preview' => false,
734 'sectionpreview' => false,
735 'disabletoc' => false,
736 'contentformat' => array(
737 ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
738 ),
739 'contentmodel' => array(
740 ApiBase::PARAM_TYPE => ContentHandler::getContentModels(),
741 )
742 );
743 }
744
745 protected function getExamplesMessages() {
746 return array(
747 'action=parse&page=Project:Sandbox'
748 => 'apihelp-parse-example-page',
749 'action=parse&text={{Project:Sandbox}}&contentmodel=wikitext'
750 => 'apihelp-parse-example-text',
751 'action=parse&text={{PAGENAME}}&title=Test'
752 => 'apihelp-parse-example-texttitle',
753 'action=parse&summary=Some+[[link]]&prop='
754 => 'apihelp-parse-example-summary',
755 );
756 }
757
758 public function getHelpUrls() {
759 return 'https://www.mediawiki.org/wiki/API:Parsing_wikitext#parse';
760 }
761 }