API: HTMLize and internationalize the help, add Special:ApiHelp
[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 } else {
74 $this->section = false;
75 }
76
77 // The parser needs $wgTitle to be set, apparently the
78 // $title parameter in Parser::parse isn't enough *sigh*
79 // TODO: Does this still need $wgTitle?
80 global $wgParser, $wgTitle;
81
82 $redirValues = null;
83
84 // Return result
85 $result = $this->getResult();
86
87 if ( !is_null( $oldid ) || !is_null( $pageid ) || !is_null( $page ) ) {
88 if ( !is_null( $oldid ) ) {
89 // Don't use the parser cache
90 $rev = Revision::newFromID( $oldid );
91 if ( !$rev ) {
92 $this->dieUsage( "There is no revision ID $oldid", 'missingrev' );
93 }
94 if ( !$rev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) {
95 $this->dieUsage( "You don't have permission to view deleted revisions", 'permissiondenied' );
96 }
97
98 $titleObj = $rev->getTitle();
99 $wgTitle = $titleObj;
100 $pageObj = WikiPage::factory( $titleObj );
101 $popts = $this->makeParserOptions( $pageObj, $params );
102
103 // If for some reason the "oldid" is actually the current revision, it may be cached
104 if ( $rev->isCurrent() ) {
105 // May get from/save to parser cache
106 $p_result = $this->getParsedContent( $pageObj, $popts,
107 $pageid, isset( $prop['wikitext'] ) );
108 } else { // This is an old revision, so get the text differently
109 $this->content = $rev->getContent( Revision::FOR_THIS_USER, $this->getUser() );
110
111 if ( $this->section !== false ) {
112 $this->content = $this->getSectionContent( $this->content, 'r' . $rev->getId() );
113 }
114
115 // Should we save old revision parses to the parser cache?
116 $p_result = $this->content->getParserOutput( $titleObj, $rev->getId(), $popts );
117 }
118 } else { // Not $oldid, but $pageid or $page
119 if ( $params['redirects'] ) {
120 $reqParams = array(
121 'action' => 'query',
122 'redirects' => '',
123 );
124 if ( !is_null( $pageid ) ) {
125 $reqParams['pageids'] = $pageid;
126 } else { // $page
127 $reqParams['titles'] = $page;
128 }
129 $req = new FauxRequest( $reqParams );
130 $main = new ApiMain( $req );
131 $main->execute();
132 $data = $main->getResultData();
133 $redirValues = isset( $data['query']['redirects'] )
134 ? $data['query']['redirects']
135 : array();
136 $to = $page;
137 foreach ( (array)$redirValues as $r ) {
138 $to = $r['to'];
139 }
140 $pageParams = array( 'title' => $to );
141 } elseif ( !is_null( $pageid ) ) {
142 $pageParams = array( 'pageid' => $pageid );
143 } else { // $page
144 $pageParams = array( 'title' => $page );
145 }
146
147 $pageObj = $this->getTitleOrPageId( $pageParams, 'fromdb' );
148 $titleObj = $pageObj->getTitle();
149 if ( !$titleObj || !$titleObj->exists() ) {
150 $this->dieUsage( "The page you specified doesn't exist", 'missingtitle' );
151 }
152 $wgTitle = $titleObj;
153
154 if ( isset( $prop['revid'] ) ) {
155 $oldid = $pageObj->getLatest();
156 }
157
158 $popts = $this->makeParserOptions( $pageObj, $params );
159
160 // Potentially cached
161 $p_result = $this->getParsedContent( $pageObj, $popts, $pageid,
162 isset( $prop['wikitext'] ) );
163 }
164 } else { // Not $oldid, $pageid, $page. Hence based on $text
165 $titleObj = Title::newFromText( $title );
166 if ( !$titleObj || $titleObj->isExternal() ) {
167 $this->dieUsageMsg( array( 'invalidtitle', $title ) );
168 }
169 $wgTitle = $titleObj;
170 if ( $titleObj->canExist() ) {
171 $pageObj = WikiPage::factory( $titleObj );
172 } else {
173 // Do like MediaWiki::initializeArticle()
174 $article = Article::newFromTitle( $titleObj, $this->getContext() );
175 $pageObj = $article->getPage();
176 }
177
178 $popts = $this->makeParserOptions( $pageObj, $params );
179 $textProvided = !is_null( $text );
180
181 if ( !$textProvided ) {
182 if ( $titleProvided && ( $prop || $params['generatexml'] ) ) {
183 $this->setWarning(
184 "'title' used without 'text', and parsed page properties were requested " .
185 "(did you mean to use 'page' instead of 'title'?)"
186 );
187 }
188 // Prevent warning from ContentHandler::makeContent()
189 $text = '';
190 }
191
192 // If we are parsing text, do not use the content model of the default
193 // API title, but default to wikitext to keep BC.
194 if ( $textProvided && !$titleProvided && is_null( $model ) ) {
195 $model = CONTENT_MODEL_WIKITEXT;
196 $this->setWarning( "No 'title' or 'contentmodel' was given, assuming $model." );
197 }
198
199 try {
200 $this->content = ContentHandler::makeContent( $text, $titleObj, $model, $format );
201 } catch ( MWContentSerializationException $ex ) {
202 $this->dieUsage( $ex->getMessage(), 'parseerror' );
203 }
204
205 if ( $this->section !== false ) {
206 $this->content = $this->getSectionContent( $this->content, $titleObj->getPrefixedText() );
207 }
208
209 if ( $params['pst'] || $params['onlypst'] ) {
210 $this->pstContent = $this->content->preSaveTransform( $titleObj, $this->getUser(), $popts );
211 }
212 if ( $params['onlypst'] ) {
213 // Build a result and bail out
214 $result_array = array();
215 $result_array['text'] = array();
216 ApiResult::setContent( $result_array['text'], $this->pstContent->serialize( $format ) );
217 if ( isset( $prop['wikitext'] ) ) {
218 $result_array['wikitext'] = array();
219 ApiResult::setContent( $result_array['wikitext'], $this->content->serialize( $format ) );
220 }
221 $result->addValue( null, $this->getModuleName(), $result_array );
222
223 return;
224 }
225
226 // Not cached (save or load)
227 if ( $params['pst'] ) {
228 $p_result = $this->pstContent->getParserOutput( $titleObj, null, $popts );
229 } else {
230 $p_result = $this->content->getParserOutput( $titleObj, null, $popts );
231 }
232 }
233
234 $result_array = array();
235
236 $result_array['title'] = $titleObj->getPrefixedText();
237
238 if ( !is_null( $oldid ) ) {
239 $result_array['revid'] = intval( $oldid );
240 }
241
242 if ( $params['redirects'] && !is_null( $redirValues ) ) {
243 $result_array['redirects'] = $redirValues;
244 }
245
246 if ( $params['disabletoc'] ) {
247 $p_result->setTOCEnabled( false );
248 }
249
250 if ( isset( $prop['text'] ) ) {
251 $result_array['text'] = array();
252 ApiResult::setContent( $result_array['text'], $p_result->getText() );
253 }
254
255 if ( !is_null( $params['summary'] ) ) {
256 $result_array['parsedsummary'] = array();
257 ApiResult::setContent(
258 $result_array['parsedsummary'],
259 Linker::formatComment( $params['summary'], $titleObj )
260 );
261 }
262
263 if ( isset( $prop['langlinks'] ) ) {
264 $langlinks = $p_result->getLanguageLinks();
265
266 if ( $params['effectivelanglinks'] ) {
267 // Link flags are ignored for now, but may in the future be
268 // included in the result.
269 $linkFlags = array();
270 wfRunHooks( 'LanguageLinks', array( $titleObj, &$langlinks, &$linkFlags ) );
271 }
272 } else {
273 $langlinks = false;
274 }
275
276 if ( isset( $prop['langlinks'] ) ) {
277 $result_array['langlinks'] = $this->formatLangLinks( $langlinks );
278 }
279 if ( isset( $prop['categories'] ) ) {
280 $result_array['categories'] = $this->formatCategoryLinks( $p_result->getCategories() );
281 }
282 if ( isset( $prop['categorieshtml'] ) ) {
283 $categoriesHtml = $this->categoriesHtml( $p_result->getCategories() );
284 $result_array['categorieshtml'] = array();
285 ApiResult::setContent( $result_array['categorieshtml'], $categoriesHtml );
286 }
287 if ( isset( $prop['links'] ) ) {
288 $result_array['links'] = $this->formatLinks( $p_result->getLinks() );
289 }
290 if ( isset( $prop['templates'] ) ) {
291 $result_array['templates'] = $this->formatLinks( $p_result->getTemplates() );
292 }
293 if ( isset( $prop['images'] ) ) {
294 $result_array['images'] = array_keys( $p_result->getImages() );
295 }
296 if ( isset( $prop['externallinks'] ) ) {
297 $result_array['externallinks'] = array_keys( $p_result->getExternalLinks() );
298 }
299 if ( isset( $prop['sections'] ) ) {
300 $result_array['sections'] = $p_result->getSections();
301 }
302
303 if ( isset( $prop['displaytitle'] ) ) {
304 $result_array['displaytitle'] = $p_result->getDisplayTitle() ?
305 $p_result->getDisplayTitle() :
306 $titleObj->getPrefixedText();
307 }
308
309 if ( isset( $prop['headitems'] ) || isset( $prop['headhtml'] ) ) {
310 $context = $this->getContext();
311 $context->setTitle( $titleObj );
312 $context->getOutput()->addParserOutputMetadata( $p_result );
313
314 if ( isset( $prop['headitems'] ) ) {
315 $headItems = $this->formatHeadItems( $p_result->getHeadItems() );
316
317 $css = $this->formatCss( $context->getOutput()->buildCssLinksArray() );
318
319 $scripts = array( $context->getOutput()->getHeadScripts() );
320
321 $result_array['headitems'] = array_merge( $headItems, $css, $scripts );
322 }
323
324 if ( isset( $prop['headhtml'] ) ) {
325 $result_array['headhtml'] = array();
326 ApiResult::setContent(
327 $result_array['headhtml'],
328 $context->getOutput()->headElement( $context->getSkin() )
329 );
330 }
331 }
332
333 if ( isset( $prop['modules'] ) ) {
334 $result_array['modules'] = array_values( array_unique( $p_result->getModules() ) );
335 $result_array['modulescripts'] = array_values( array_unique( $p_result->getModuleScripts() ) );
336 $result_array['modulestyles'] = array_values( array_unique( $p_result->getModuleStyles() ) );
337 $result_array['modulemessages'] = array_values( array_unique( $p_result->getModuleMessages() ) );
338 }
339
340 if ( isset( $prop['iwlinks'] ) ) {
341 $result_array['iwlinks'] = $this->formatIWLinks( $p_result->getInterwikiLinks() );
342 }
343
344 if ( isset( $prop['wikitext'] ) ) {
345 $result_array['wikitext'] = array();
346 ApiResult::setContent( $result_array['wikitext'], $this->content->serialize( $format ) );
347 if ( !is_null( $this->pstContent ) ) {
348 $result_array['psttext'] = array();
349 ApiResult::setContent( $result_array['psttext'], $this->pstContent->serialize( $format ) );
350 }
351 }
352 if ( isset( $prop['properties'] ) ) {
353 $result_array['properties'] = $this->formatProperties( $p_result->getProperties() );
354 }
355
356 if ( isset( $prop['limitreportdata'] ) ) {
357 $result_array['limitreportdata'] =
358 $this->formatLimitReportData( $p_result->getLimitReportData() );
359 }
360 if ( isset( $prop['limitreporthtml'] ) ) {
361 $limitreportHtml = EditPage::getPreviewLimitReport( $p_result );
362 $result_array['limitreporthtml'] = array();
363 ApiResult::setContent( $result_array['limitreporthtml'], $limitreportHtml );
364 }
365
366 if ( $params['generatexml'] ) {
367 if ( $this->content->getModel() != CONTENT_MODEL_WIKITEXT ) {
368 $this->dieUsage( "generatexml is only supported for wikitext content", "notwikitext" );
369 }
370
371 $wgParser->startExternalParse( $titleObj, $popts, OT_PREPROCESS );
372 $dom = $wgParser->preprocessToDom( $this->content->getNativeData() );
373 if ( is_callable( array( $dom, 'saveXML' ) ) ) {
374 $xml = $dom->saveXML();
375 } else {
376 $xml = $dom->__toString();
377 }
378 $result_array['parsetree'] = array();
379 ApiResult::setContent( $result_array['parsetree'], $xml );
380 }
381
382 $result_mapping = array(
383 'redirects' => 'r',
384 'langlinks' => 'll',
385 'categories' => 'cl',
386 'links' => 'pl',
387 'templates' => 'tl',
388 'images' => 'img',
389 'externallinks' => 'el',
390 'iwlinks' => 'iw',
391 'sections' => 's',
392 'headitems' => 'hi',
393 'modules' => 'm',
394 'modulescripts' => 'm',
395 'modulestyles' => 'm',
396 'modulemessages' => 'm',
397 'properties' => 'pp',
398 'limitreportdata' => 'lr',
399 );
400 $this->setIndexedTagNames( $result_array, $result_mapping );
401 $result->addValue( null, $this->getModuleName(), $result_array );
402 }
403
404 /**
405 * Constructs a ParserOptions object
406 *
407 * @param WikiPage $pageObj
408 * @param array $params
409 *
410 * @return ParserOptions
411 */
412 protected function makeParserOptions( WikiPage $pageObj, array $params ) {
413 wfProfileIn( __METHOD__ );
414
415 $popts = $pageObj->makeParserOptions( $this->getContext() );
416 $popts->enableLimitReport( !$params['disablepp'] );
417 $popts->setIsPreview( $params['preview'] || $params['sectionpreview'] );
418 $popts->setIsSectionPreview( $params['sectionpreview'] );
419 $popts->setEditSection( !$params['disableeditsection'] );
420
421 wfProfileOut( __METHOD__ );
422
423 return $popts;
424 }
425
426 /**
427 * @param WikiPage $page
428 * @param ParserOptions $popts
429 * @param int $pageId
430 * @param bool $getWikitext
431 * @return ParserOutput
432 */
433 private function getParsedContent( WikiPage $page, $popts, $pageId = null, $getWikitext = false ) {
434 $this->content = $page->getContent( Revision::RAW ); //XXX: really raw?
435
436 if ( $this->section !== false && $this->content !== null ) {
437 $this->content = $this->getSectionContent(
438 $this->content,
439 !is_null( $pageId ) ? 'page id ' . $pageId : $page->getTitle()->getPrefixedText()
440 );
441
442 // Not cached (save or load)
443 return $this->content->getParserOutput( $page->getTitle(), null, $popts );
444 }
445
446 // Try the parser cache first
447 // getParserOutput will save to Parser cache if able
448 $pout = $page->getParserOutput( $popts );
449 if ( !$pout ) {
450 $this->dieUsage( "There is no revision ID {$page->getLatest()}", 'missingrev' );
451 }
452 if ( $getWikitext ) {
453 $this->content = $page->getContent( Revision::RAW );
454 }
455
456 return $pout;
457 }
458
459 /**
460 * @param Content $content
461 * @param string $what Identifies the content in error messages, e.g. page title.
462 * @return Content|bool
463 */
464 private function getSectionContent( Content $content, $what ) {
465 // Not cached (save or load)
466 $section = $content->getSection( $this->section );
467 if ( $section === false ) {
468 $this->dieUsage( "There is no section {$this->section} in " . $what, 'nosuchsection' );
469 }
470 if ( $section === null ) {
471 $this->dieUsage( "Sections are not supported by " . $what, 'nosuchsection' );
472 $section = false;
473 }
474
475 return $section;
476 }
477
478 private function formatLangLinks( $links ) {
479 $result = array();
480 foreach ( $links as $link ) {
481 $entry = array();
482 $bits = explode( ':', $link, 2 );
483 $title = Title::newFromText( $link );
484
485 $entry['lang'] = $bits[0];
486 if ( $title ) {
487 $entry['url'] = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
488 // localised language name in 'uselang' language
489 $entry['langname'] = Language::fetchLanguageName(
490 $title->getInterwiki(),
491 $this->getLanguage()->getCode()
492 );
493
494 // native language name
495 $entry['autonym'] = Language::fetchLanguageName( $title->getInterwiki() );
496 }
497 ApiResult::setContent( $entry, $bits[1] );
498 $result[] = $entry;
499 }
500
501 return $result;
502 }
503
504 private function formatCategoryLinks( $links ) {
505 $result = array();
506
507 if ( !$links ) {
508 return $result;
509 }
510
511 // Fetch hiddencat property
512 $lb = new LinkBatch;
513 $lb->setArray( array( NS_CATEGORY => $links ) );
514 $db = $this->getDB();
515 $res = $db->select( array( 'page', 'page_props' ),
516 array( 'page_title', 'pp_propname' ),
517 $lb->constructSet( 'page', $db ),
518 __METHOD__,
519 array(),
520 array( 'page_props' => array(
521 'LEFT JOIN', array( 'pp_propname' => 'hiddencat', 'pp_page = page_id' )
522 ) )
523 );
524 $hiddencats = array();
525 foreach ( $res as $row ) {
526 $hiddencats[$row->page_title] = isset( $row->pp_propname );
527 }
528
529 foreach ( $links as $link => $sortkey ) {
530 $entry = array();
531 $entry['sortkey'] = $sortkey;
532 ApiResult::setContent( $entry, $link );
533 if ( !isset( $hiddencats[$link] ) ) {
534 $entry['missing'] = '';
535 } elseif ( $hiddencats[$link] ) {
536 $entry['hidden'] = '';
537 }
538 $result[] = $entry;
539 }
540
541 return $result;
542 }
543
544 private function categoriesHtml( $categories ) {
545 $context = $this->getContext();
546 $context->getOutput()->addCategoryLinks( $categories );
547
548 return $context->getSkin()->getCategories();
549 }
550
551 private function formatLinks( $links ) {
552 $result = array();
553 foreach ( $links as $ns => $nslinks ) {
554 foreach ( $nslinks as $title => $id ) {
555 $entry = array();
556 $entry['ns'] = $ns;
557 ApiResult::setContent( $entry, Title::makeTitle( $ns, $title )->getFullText() );
558 if ( $id != 0 ) {
559 $entry['exists'] = '';
560 }
561 $result[] = $entry;
562 }
563 }
564
565 return $result;
566 }
567
568 private function formatIWLinks( $iw ) {
569 $result = array();
570 foreach ( $iw as $prefix => $titles ) {
571 foreach ( array_keys( $titles ) as $title ) {
572 $entry = array();
573 $entry['prefix'] = $prefix;
574
575 $title = Title::newFromText( "{$prefix}:{$title}" );
576 if ( $title ) {
577 $entry['url'] = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
578 }
579
580 ApiResult::setContent( $entry, $title->getFullText() );
581 $result[] = $entry;
582 }
583 }
584
585 return $result;
586 }
587
588 private function formatHeadItems( $headItems ) {
589 $result = array();
590 foreach ( $headItems as $tag => $content ) {
591 $entry = array();
592 $entry['tag'] = $tag;
593 ApiResult::setContent( $entry, $content );
594 $result[] = $entry;
595 }
596
597 return $result;
598 }
599
600 private function formatProperties( $properties ) {
601 $result = array();
602 foreach ( $properties as $name => $value ) {
603 $entry = array();
604 $entry['name'] = $name;
605 ApiResult::setContent( $entry, $value );
606 $result[] = $entry;
607 }
608
609 return $result;
610 }
611
612 private function formatCss( $css ) {
613 $result = array();
614 foreach ( $css as $file => $link ) {
615 $entry = array();
616 $entry['file'] = $file;
617 ApiResult::setContent( $entry, $link );
618 $result[] = $entry;
619 }
620
621 return $result;
622 }
623
624 private function formatLimitReportData( $limitReportData ) {
625 $result = array();
626 $apiResult = $this->getResult();
627
628 foreach ( $limitReportData as $name => $value ) {
629 $entry = array();
630 $entry['name'] = $name;
631 if ( !is_array( $value ) ) {
632 $value = array( $value );
633 }
634 $apiResult->setIndexedTagName( $value, 'param' );
635 $apiResult->setIndexedTagName_recursive( $value, 'param' );
636 $entry = array_merge( $entry, $value );
637 $result[] = $entry;
638 }
639
640 return $result;
641 }
642
643 private function setIndexedTagNames( &$array, $mapping ) {
644 foreach ( $mapping as $key => $name ) {
645 if ( isset( $array[$key] ) ) {
646 $this->getResult()->setIndexedTagName( $array[$key], $name );
647 }
648 }
649 }
650
651 public function getAllowedParams() {
652 return array(
653 'title' => null,
654 'text' => null,
655 'summary' => null,
656 'page' => null,
657 'pageid' => array(
658 ApiBase::PARAM_TYPE => 'integer',
659 ),
660 'redirects' => false,
661 'oldid' => array(
662 ApiBase::PARAM_TYPE => 'integer',
663 ),
664 'prop' => array(
665 ApiBase::PARAM_DFLT => 'text|langlinks|categories|links|templates|' .
666 'images|externallinks|sections|revid|displaytitle|iwlinks|properties',
667 ApiBase::PARAM_ISMULTI => true,
668 ApiBase::PARAM_TYPE => array(
669 'text',
670 'langlinks',
671 'categories',
672 'categorieshtml',
673 'links',
674 'templates',
675 'images',
676 'externallinks',
677 'sections',
678 'revid',
679 'displaytitle',
680 'headitems',
681 'headhtml',
682 'modules',
683 'iwlinks',
684 'wikitext',
685 'properties',
686 'limitreportdata',
687 'limitreporthtml',
688 )
689 ),
690 'pst' => false,
691 'onlypst' => false,
692 'effectivelanglinks' => false,
693 'section' => null,
694 'disablepp' => false,
695 'disableeditsection' => false,
696 'generatexml' => false,
697 'preview' => false,
698 'sectionpreview' => false,
699 'disabletoc' => false,
700 'contentformat' => array(
701 ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
702 ),
703 'contentmodel' => array(
704 ApiBase::PARAM_TYPE => ContentHandler::getContentModels(),
705 )
706 );
707 }
708
709 public function getParamDescription() {
710 $p = $this->getModulePrefix();
711 $wikitext = CONTENT_MODEL_WIKITEXT;
712
713 return array(
714 'text' => "Text to parse. Use {$p}title or {$p}contentmodel to control the content model",
715 'summary' => 'Summary to parse',
716 'redirects' => "If the {$p}page or the {$p}pageid parameter is set to a redirect, resolve it",
717 'title' => "Title of page the text belongs to. " .
718 "If omitted, {$p}contentmodel must be specified, and \"API\" will be used as the title",
719 'page' => "Parse the content of this page. Cannot be used together with {$p}text and {$p}title",
720 'pageid' => "Parse the content of this page. Overrides {$p}page",
721 'oldid' => "Parse the content of this revision. Overrides {$p}page and {$p}pageid",
722 'prop' => array(
723 'Which pieces of information to get',
724 ' text - Gives the parsed text of the wikitext',
725 ' langlinks - Gives the language links in the parsed wikitext',
726 ' categories - Gives the categories in the parsed wikitext',
727 ' categorieshtml - Gives the HTML version of the categories',
728 ' links - Gives the internal links in the parsed wikitext',
729 ' templates - Gives the templates in the parsed wikitext',
730 ' images - Gives the images in the parsed wikitext',
731 ' externallinks - Gives the external links in the parsed wikitext',
732 ' sections - Gives the sections in the parsed wikitext',
733 ' revid - Adds the revision ID of the parsed page',
734 ' displaytitle - Adds the title of the parsed wikitext',
735 ' headitems - Gives items to put in the <head> of the page',
736 ' headhtml - Gives parsed <head> of the page',
737 ' modules - Gives the ResourceLoader modules used on the page',
738 ' iwlinks - Gives interwiki links in the parsed wikitext',
739 ' wikitext - Gives the original wikitext that was parsed',
740 ' properties - Gives various properties defined in the parsed wikitext',
741 ' limitreportdata - Gives the limit report in a structured way.',
742 " Gives no data, when {$p}disablepp is set.",
743 ' limitreporthtml - Gives the HTML version of the limit report.',
744 " Gives no data, when {$p}disablepp is set.",
745 ),
746 'effectivelanglinks' => array(
747 'Includes language links supplied by extensions',
748 '(for use with prop=langlinks)',
749 ),
750 'pst' => array(
751 'Do a pre-save transform on the input before parsing it',
752 "Only valid when used with {$p}text",
753 ),
754 'onlypst' => array(
755 'Do a pre-save transform (PST) on the input, but don\'t parse it',
756 'Returns the same wikitext, after a PST has been applied.',
757 "Only valid when used with {$p}text",
758 ),
759 'section' => 'Only retrieve the content of this section number',
760 'disablepp' => 'Disable the PP Report from the parser output',
761 'disableeditsection' => 'Disable edit section links from the parser output',
762 'generatexml' => "Generate XML parse tree (requires contentmodel=$wikitext)",
763 'preview' => 'Parse in preview mode',
764 'sectionpreview' => 'Parse in section preview mode (enables preview mode too)',
765 'disabletoc' => 'Disable table of contents in output',
766 'contentformat' => array(
767 'Content serialization format used for the input text',
768 "Only valid when used with {$p}text",
769 ),
770 'contentmodel' => array(
771 "Content model of the input text. If omitted, ${p}title must be specified, " .
772 "and default will be the model of the specified ${p}title",
773 "Only valid when used with {$p}text",
774 ),
775 );
776 }
777
778 public function getDescription() {
779 $p = $this->getModulePrefix();
780
781 return array(
782 'Parses content and returns parser output.',
783 'See the various prop-Modules of action=query to get information from the current' .
784 'version of a page.',
785 'There are several ways to specify the text to parse:',
786 "1) Specify a page or revision, using {$p}page, {$p}pageid, or {$p}oldid.",
787 "2) Specify content explicitly, using {$p}text, {$p}title, and {$p}contentmodel.",
788 "3) Specify only a summary to parse. {$p}prop should be given an empty value.",
789 );
790 }
791
792 public function getExamples() {
793 return array(
794 'api.php?action=parse&page=Project:Sandbox' => 'Parse a page',
795 'api.php?action=parse&text={{Project:Sandbox}}&contentmodel=wikitext' => 'Parse wikitext',
796 'api.php?action=parse&text={{PAGENAME}}&title=Test'
797 => 'Parse wikitext, specifying the page title',
798 'api.php?action=parse&summary=Some+[[link]]&prop=' => 'Parse a summary',
799 );
800 }
801
802 public function getHelpUrls() {
803 return 'https://www.mediawiki.org/wiki/API:Parsing_wikitext#parse';
804 }
805 }