Merge "Update docs/hooks.txt for ShowSearchHitTitle"
[mediawiki.git] / includes / api / ApiParse.php
blob287ffb7c0ebf10ca63a57b2305226e9a98c1ed79
1 <?php
2 /**
3 * Created on Dec 01, 2007
5 * Copyright © 2007 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
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.
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.
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
22 * @file
25 /**
26 * @ingroup API
28 class ApiParse extends ApiBase {
30 /** @var string $section */
31 private $section = null;
33 /** @var Content $content */
34 private $content = null;
36 /** @var Content $pstContent */
37 private $pstContent = null;
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' );
43 // Get parameters
44 $params = $this->extractRequestParams();
46 // No easy way to say that text & title are allowed together while the
47 // rest aren't, so just do it in two calls.
48 $this->requireMaxOneParameter( $params, 'page', 'pageid', 'oldid', 'text' );
49 $this->requireMaxOneParameter( $params, 'page', 'pageid', 'oldid', 'title' );
51 $text = $params['text'];
52 $title = $params['title'];
53 if ( $title === null ) {
54 $titleProvided = false;
55 // A title is needed for parsing, so arbitrarily choose one
56 $title = 'API';
57 } else {
58 $titleProvided = true;
61 $page = $params['page'];
62 $pageid = $params['pageid'];
63 $oldid = $params['oldid'];
65 $model = $params['contentmodel'];
66 $format = $params['contentformat'];
68 $prop = array_flip( $params['prop'] );
70 if ( isset( $params['section'] ) ) {
71 $this->section = $params['section'];
72 if ( !preg_match( '/^((T-)?\d+|new)$/', $this->section ) ) {
73 $this->dieWithError( 'apierror-invalidsection' );
75 } else {
76 $this->section = false;
79 // The parser needs $wgTitle to be set, apparently the
80 // $title parameter in Parser::parse isn't enough *sigh*
81 // TODO: Does this still need $wgTitle?
82 global $wgParser, $wgTitle;
84 $redirValues = null;
86 // Return result
87 $result = $this->getResult();
89 if ( !is_null( $oldid ) || !is_null( $pageid ) || !is_null( $page ) ) {
90 if ( $this->section === 'new' ) {
91 $this->dieWithError( 'apierror-invalidparammix-parse-new-section', 'invalidparammix' );
93 if ( !is_null( $oldid ) ) {
94 // Don't use the parser cache
95 $rev = Revision::newFromId( $oldid );
96 if ( !$rev ) {
97 $this->dieWithError( [ 'apierror-nosuchrevid', $oldid ] );
100 $this->checkTitleUserPermissions( $rev->getTitle(), 'read' );
101 if ( !$rev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) {
102 $this->dieWithError(
103 [ 'apierror-permissiondenied', $this->msg( 'action-deletedtext' ) ]
107 $titleObj = $rev->getTitle();
108 $wgTitle = $titleObj;
109 $pageObj = WikiPage::factory( $titleObj );
110 list( $popts, $reset, $suppressCache ) = $this->makeParserOptions( $pageObj, $params );
112 // If for some reason the "oldid" is actually the current revision, it may be cached
113 // Deliberately comparing $pageObj->getLatest() with $rev->getId(), rather than
114 // checking $rev->isCurrent(), because $pageObj is what actually ends up being used,
115 // and if its ->getLatest() is outdated, $rev->isCurrent() won't tell us that.
116 if ( !$suppressCache && $rev->getId() == $pageObj->getLatest() ) {
117 // May get from/save to parser cache
118 $p_result = $this->getParsedContent( $pageObj, $popts,
119 $pageid, isset( $prop['wikitext'] ) );
120 } else { // This is an old revision, so get the text differently
121 $this->content = $rev->getContent( Revision::FOR_THIS_USER, $this->getUser() );
123 if ( $this->section !== false ) {
124 $this->content = $this->getSectionContent(
125 $this->content, $this->msg( 'revid', $rev->getId() )
129 // Should we save old revision parses to the parser cache?
130 $p_result = $this->content->getParserOutput( $titleObj, $rev->getId(), $popts );
132 } else { // Not $oldid, but $pageid or $page
133 if ( $params['redirects'] ) {
134 $reqParams = [
135 'redirects' => '',
137 if ( !is_null( $pageid ) ) {
138 $reqParams['pageids'] = $pageid;
139 } else { // $page
140 $reqParams['titles'] = $page;
142 $req = new FauxRequest( $reqParams );
143 $main = new ApiMain( $req );
144 $pageSet = new ApiPageSet( $main );
145 $pageSet->execute();
146 $redirValues = $pageSet->getRedirectTitlesAsResult( $this->getResult() );
148 $to = $page;
149 foreach ( $pageSet->getRedirectTitles() as $title ) {
150 $to = $title->getFullText();
152 $pageParams = [ 'title' => $to ];
153 } elseif ( !is_null( $pageid ) ) {
154 $pageParams = [ 'pageid' => $pageid ];
155 } else { // $page
156 $pageParams = [ 'title' => $page ];
159 $pageObj = $this->getTitleOrPageId( $pageParams, 'fromdb' );
160 $titleObj = $pageObj->getTitle();
161 if ( !$titleObj || !$titleObj->exists() ) {
162 $this->dieWithError( 'apierror-missingtitle' );
165 $this->checkTitleUserPermissions( $titleObj, 'read' );
166 $wgTitle = $titleObj;
168 if ( isset( $prop['revid'] ) ) {
169 $oldid = $pageObj->getLatest();
172 list( $popts, $reset, $suppressCache ) = $this->makeParserOptions( $pageObj, $params );
174 // Don't pollute the parser cache when setting options that aren't
175 // in ParserOptions::optionsHash()
176 /// @todo: This should be handled closer to the actual cache instead of here, see T110269
177 $suppressCache = $suppressCache ||
178 $params['disablepp'] ||
179 $params['disablelimitreport'] ||
180 $params['preview'] ||
181 $params['sectionpreview'] ||
182 $params['disabletidy'];
184 if ( $suppressCache ) {
185 $this->content = $this->getContent( $pageObj, $pageid );
186 $p_result = $this->content->getParserOutput( $titleObj, null, $popts );
187 } else {
188 // Potentially cached
189 $p_result = $this->getParsedContent( $pageObj, $popts, $pageid,
190 isset( $prop['wikitext'] ) );
193 } else { // Not $oldid, $pageid, $page. Hence based on $text
194 $titleObj = Title::newFromText( $title );
195 if ( !$titleObj || $titleObj->isExternal() ) {
196 $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $title ) ] );
198 $wgTitle = $titleObj;
199 if ( $titleObj->canExist() ) {
200 $pageObj = WikiPage::factory( $titleObj );
201 } else {
202 // Do like MediaWiki::initializeArticle()
203 $article = Article::newFromTitle( $titleObj, $this->getContext() );
204 $pageObj = $article->getPage();
207 list( $popts, $reset ) = $this->makeParserOptions( $pageObj, $params );
208 $textProvided = !is_null( $text );
210 if ( !$textProvided ) {
211 if ( $titleProvided && ( $prop || $params['generatexml'] ) ) {
212 $this->addWarning( 'apiwarn-parse-titlewithouttext' );
214 // Prevent warning from ContentHandler::makeContent()
215 $text = '';
218 // If we are parsing text, do not use the content model of the default
219 // API title, but default to wikitext to keep BC.
220 if ( $textProvided && !$titleProvided && is_null( $model ) ) {
221 $model = CONTENT_MODEL_WIKITEXT;
222 $this->addWarning( [ 'apiwarn-parse-nocontentmodel', $model ] );
225 try {
226 $this->content = ContentHandler::makeContent( $text, $titleObj, $model, $format );
227 } catch ( MWContentSerializationException $ex ) {
228 $this->dieWithException( $ex, [
229 'wrap' => ApiMessage::create( 'apierror-contentserializationexception', 'parseerror' )
230 ] );
233 if ( $this->section !== false ) {
234 if ( $this->section === 'new' ) {
235 // Insert the section title above the content.
236 if ( !is_null( $params['sectiontitle'] ) && $params['sectiontitle'] !== '' ) {
237 $this->content = $this->content->addSectionHeader( $params['sectiontitle'] );
239 } else {
240 $this->content = $this->getSectionContent( $this->content, $titleObj->getPrefixedText() );
244 if ( $params['pst'] || $params['onlypst'] ) {
245 $this->pstContent = $this->content->preSaveTransform( $titleObj, $this->getUser(), $popts );
247 if ( $params['onlypst'] ) {
248 // Build a result and bail out
249 $result_array = [];
250 $result_array['text'] = $this->pstContent->serialize( $format );
251 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'text';
252 if ( isset( $prop['wikitext'] ) ) {
253 $result_array['wikitext'] = $this->content->serialize( $format );
254 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'wikitext';
256 if ( !is_null( $params['summary'] ) ||
257 ( !is_null( $params['sectiontitle'] ) && $this->section === 'new' )
259 $result_array['parsedsummary'] = $this->formatSummary( $titleObj, $params );
260 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'parsedsummary';
263 $result->addValue( null, $this->getModuleName(), $result_array );
265 return;
268 // Not cached (save or load)
269 if ( $params['pst'] ) {
270 $p_result = $this->pstContent->getParserOutput( $titleObj, null, $popts );
271 } else {
272 $p_result = $this->content->getParserOutput( $titleObj, null, $popts );
276 $result_array = [];
278 $result_array['title'] = $titleObj->getPrefixedText();
279 $result_array['pageid'] = $pageid ?: $pageObj->getId();
281 if ( !is_null( $oldid ) ) {
282 $result_array['revid'] = intval( $oldid );
285 if ( $params['redirects'] && !is_null( $redirValues ) ) {
286 $result_array['redirects'] = $redirValues;
289 if ( $params['disabletoc'] ) {
290 $p_result->setTOCEnabled( false );
293 if ( isset( $prop['text'] ) ) {
294 $result_array['text'] = $p_result->getText();
295 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'text';
298 if ( !is_null( $params['summary'] ) ||
299 ( !is_null( $params['sectiontitle'] ) && $this->section === 'new' )
301 $result_array['parsedsummary'] = $this->formatSummary( $titleObj, $params );
302 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'parsedsummary';
305 if ( isset( $prop['langlinks'] ) ) {
306 $langlinks = $p_result->getLanguageLinks();
308 if ( $params['effectivelanglinks'] ) {
309 // Link flags are ignored for now, but may in the future be
310 // included in the result.
311 $linkFlags = [];
312 Hooks::run( 'LanguageLinks', [ $titleObj, &$langlinks, &$linkFlags ] );
314 } else {
315 $langlinks = false;
318 if ( isset( $prop['langlinks'] ) ) {
319 $result_array['langlinks'] = $this->formatLangLinks( $langlinks );
321 if ( isset( $prop['categories'] ) ) {
322 $result_array['categories'] = $this->formatCategoryLinks( $p_result->getCategories() );
324 if ( isset( $prop['categorieshtml'] ) ) {
325 $result_array['categorieshtml'] = $this->categoriesHtml( $p_result->getCategories() );
326 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'categorieshtml';
328 if ( isset( $prop['links'] ) ) {
329 $result_array['links'] = $this->formatLinks( $p_result->getLinks() );
331 if ( isset( $prop['templates'] ) ) {
332 $result_array['templates'] = $this->formatLinks( $p_result->getTemplates() );
334 if ( isset( $prop['images'] ) ) {
335 $result_array['images'] = array_keys( $p_result->getImages() );
337 if ( isset( $prop['externallinks'] ) ) {
338 $result_array['externallinks'] = array_keys( $p_result->getExternalLinks() );
340 if ( isset( $prop['sections'] ) ) {
341 $result_array['sections'] = $p_result->getSections();
344 if ( isset( $prop['displaytitle'] ) ) {
345 $result_array['displaytitle'] = $p_result->getDisplayTitle() ?:
346 $titleObj->getPrefixedText();
349 if ( isset( $prop['headitems'] ) ) {
350 $result_array['headitems'] = $this->formatHeadItems( $p_result->getHeadItems() );
351 $this->addDeprecation( 'apiwarn-deprecation-parse-headitems', 'action=parse&prop=headitems' );
354 if ( isset( $prop['headhtml'] ) ) {
355 $context = new DerivativeContext( $this->getContext() );
356 $context->setTitle( $titleObj );
357 $context->setWikiPage( $pageObj );
359 // We need an OutputPage tied to $context, not to the
360 // RequestContext at the root of the stack.
361 $output = new OutputPage( $context );
362 $output->addParserOutputMetadata( $p_result );
364 $result_array['headhtml'] = $output->headElement( $context->getSkin() );
365 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'headhtml';
368 if ( isset( $prop['modules'] ) ) {
369 $result_array['modules'] = array_values( array_unique( $p_result->getModules() ) );
370 $result_array['modulescripts'] = array_values( array_unique( $p_result->getModuleScripts() ) );
371 $result_array['modulestyles'] = array_values( array_unique( $p_result->getModuleStyles() ) );
374 if ( isset( $prop['jsconfigvars'] ) ) {
375 $result_array['jsconfigvars'] =
376 ApiResult::addMetadataToResultVars( $p_result->getJsConfigVars() );
379 if ( isset( $prop['encodedjsconfigvars'] ) ) {
380 $result_array['encodedjsconfigvars'] = FormatJson::encode(
381 $p_result->getJsConfigVars(), false, FormatJson::ALL_OK
383 $result_array[ApiResult::META_SUBELEMENTS][] = 'encodedjsconfigvars';
386 if ( isset( $prop['modules'] ) &&
387 !isset( $prop['jsconfigvars'] ) && !isset( $prop['encodedjsconfigvars'] ) ) {
388 $this->addWarning( 'apiwarn-moduleswithoutvars' );
391 if ( isset( $prop['indicators'] ) ) {
392 $result_array['indicators'] = (array)$p_result->getIndicators();
393 ApiResult::setArrayType( $result_array['indicators'], 'BCkvp', 'name' );
396 if ( isset( $prop['iwlinks'] ) ) {
397 $result_array['iwlinks'] = $this->formatIWLinks( $p_result->getInterwikiLinks() );
400 if ( isset( $prop['wikitext'] ) ) {
401 $result_array['wikitext'] = $this->content->serialize( $format );
402 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'wikitext';
403 if ( !is_null( $this->pstContent ) ) {
404 $result_array['psttext'] = $this->pstContent->serialize( $format );
405 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'psttext';
408 if ( isset( $prop['properties'] ) ) {
409 $result_array['properties'] = (array)$p_result->getProperties();
410 ApiResult::setArrayType( $result_array['properties'], 'BCkvp', 'name' );
413 if ( isset( $prop['limitreportdata'] ) ) {
414 $result_array['limitreportdata'] =
415 $this->formatLimitReportData( $p_result->getLimitReportData() );
417 if ( isset( $prop['limitreporthtml'] ) ) {
418 $result_array['limitreporthtml'] = EditPage::getPreviewLimitReport( $p_result );
419 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'limitreporthtml';
422 if ( isset( $prop['parsetree'] ) || $params['generatexml'] ) {
423 if ( $this->content->getModel() != CONTENT_MODEL_WIKITEXT ) {
424 $this->dieWithError( 'apierror-parsetree-notwikitext', 'notwikitext' );
427 $wgParser->startExternalParse( $titleObj, $popts, Parser::OT_PREPROCESS );
428 $dom = $wgParser->preprocessToDom( $this->content->getNativeData() );
429 if ( is_callable( [ $dom, 'saveXML' ] ) ) {
430 $xml = $dom->saveXML();
431 } else {
432 $xml = $dom->__toString();
434 $result_array['parsetree'] = $xml;
435 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'parsetree';
438 $result_mapping = [
439 'redirects' => 'r',
440 'langlinks' => 'll',
441 'categories' => 'cl',
442 'links' => 'pl',
443 'templates' => 'tl',
444 'images' => 'img',
445 'externallinks' => 'el',
446 'iwlinks' => 'iw',
447 'sections' => 's',
448 'headitems' => 'hi',
449 'modules' => 'm',
450 'indicators' => 'ind',
451 'modulescripts' => 'm',
452 'modulestyles' => 'm',
453 'properties' => 'pp',
454 'limitreportdata' => 'lr',
456 $this->setIndexedTagNames( $result_array, $result_mapping );
457 $result->addValue( null, $this->getModuleName(), $result_array );
461 * Constructs a ParserOptions object
463 * @param WikiPage $pageObj
464 * @param array $params
466 * @return array [ ParserOptions, ScopedCallback, bool $suppressCache ]
468 protected function makeParserOptions( WikiPage $pageObj, array $params ) {
469 $popts = $pageObj->makeParserOptions( $this->getContext() );
470 $popts->enableLimitReport( !$params['disablepp'] && !$params['disablelimitreport'] );
471 $popts->setIsPreview( $params['preview'] || $params['sectionpreview'] );
472 $popts->setIsSectionPreview( $params['sectionpreview'] );
473 $popts->setEditSection( !$params['disableeditsection'] );
474 if ( $params['disabletidy'] ) {
475 $popts->setTidy( false );
478 $reset = null;
479 $suppressCache = false;
480 Hooks::run( 'ApiMakeParserOptions',
481 [ $popts, $pageObj->getTitle(), $params, $this, &$reset, &$suppressCache ] );
483 return [ $popts, $reset, $suppressCache ];
487 * @param WikiPage $page
488 * @param ParserOptions $popts
489 * @param int $pageId
490 * @param bool $getWikitext
491 * @return ParserOutput
493 private function getParsedContent( WikiPage $page, $popts, $pageId = null, $getWikitext = false ) {
494 $this->content = $this->getContent( $page, $pageId );
496 if ( $this->section !== false && $this->content !== null ) {
497 // Not cached (save or load)
498 return $this->content->getParserOutput( $page->getTitle(), null, $popts );
501 // Try the parser cache first
502 // getParserOutput will save to Parser cache if able
503 $pout = $page->getParserOutput( $popts );
504 if ( !$pout ) {
505 $this->dieWithError( [ 'apierror-nosuchrevid', $page->getLatest() ] );
507 if ( $getWikitext ) {
508 $this->content = $page->getContent( Revision::RAW );
511 return $pout;
515 * Get the content for the given page and the requested section.
517 * @param WikiPage $page
518 * @param int $pageId
519 * @return Content
521 private function getContent( WikiPage $page, $pageId = null ) {
522 $content = $page->getContent( Revision::RAW ); // XXX: really raw?
524 if ( $this->section !== false && $content !== null ) {
525 $content = $this->getSectionContent(
526 $content,
527 !is_null( $pageId )
528 ? $this->msg( 'pageid', $pageId )
529 : $page->getTitle()->getPrefixedText()
532 return $content;
536 * Extract the requested section from the given Content
538 * @param Content $content
539 * @param string|Message $what Identifies the content in error messages, e.g. page title.
540 * @return Content|bool
542 private function getSectionContent( Content $content, $what ) {
543 // Not cached (save or load)
544 $section = $content->getSection( $this->section );
545 if ( $section === false ) {
546 $this->dieWithError( [ 'apierror-nosuchsection-what', $this->section, $what ], 'nosuchsection' );
548 if ( $section === null ) {
549 $this->dieWithError( [ 'apierror-sectionsnotsupported-what', $what ], 'nosuchsection' );
550 $section = false;
553 return $section;
557 * This mimicks the behavior of EditPage in formatting a summary
559 * @param Title $title of the page being parsed
560 * @param Array $params the API parameters of the request
561 * @return Content|bool
563 private function formatSummary( $title, $params ) {
564 global $wgParser;
565 $summary = !is_null( $params['summary'] ) ? $params['summary'] : '';
566 $sectionTitle = !is_null( $params['sectiontitle'] ) ? $params['sectiontitle'] : '';
568 if ( $this->section === 'new' && ( $sectionTitle === '' || $summary === '' ) ) {
569 if ( $sectionTitle !== '' ) {
570 $summary = $params['sectiontitle'];
572 if ( $summary !== '' ) {
573 $summary = wfMessage( 'newsectionsummary' )
574 ->rawParams( $wgParser->stripSectionName( $summary ) )
575 ->inContentLanguage()->text();
578 return Linker::formatComment( $summary, $title, $this->section === 'new' );
581 private function formatLangLinks( $links ) {
582 $result = [];
583 foreach ( $links as $link ) {
584 $entry = [];
585 $bits = explode( ':', $link, 2 );
586 $title = Title::newFromText( $link );
588 $entry['lang'] = $bits[0];
589 if ( $title ) {
590 $entry['url'] = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
591 // localised language name in 'uselang' language
592 $entry['langname'] = Language::fetchLanguageName(
593 $title->getInterwiki(),
594 $this->getLanguage()->getCode()
597 // native language name
598 $entry['autonym'] = Language::fetchLanguageName( $title->getInterwiki() );
600 ApiResult::setContentValue( $entry, 'title', $bits[1] );
601 $result[] = $entry;
604 return $result;
607 private function formatCategoryLinks( $links ) {
608 $result = [];
610 if ( !$links ) {
611 return $result;
614 // Fetch hiddencat property
615 $lb = new LinkBatch;
616 $lb->setArray( [ NS_CATEGORY => $links ] );
617 $db = $this->getDB();
618 $res = $db->select( [ 'page', 'page_props' ],
619 [ 'page_title', 'pp_propname' ],
620 $lb->constructSet( 'page', $db ),
621 __METHOD__,
623 [ 'page_props' => [
624 'LEFT JOIN', [ 'pp_propname' => 'hiddencat', 'pp_page = page_id' ]
627 $hiddencats = [];
628 foreach ( $res as $row ) {
629 $hiddencats[$row->page_title] = isset( $row->pp_propname );
632 $linkCache = LinkCache::singleton();
634 foreach ( $links as $link => $sortkey ) {
635 $entry = [];
636 $entry['sortkey'] = $sortkey;
637 // array keys will cast numeric category names to ints, so cast back to string
638 ApiResult::setContentValue( $entry, 'category', (string)$link );
639 if ( !isset( $hiddencats[$link] ) ) {
640 $entry['missing'] = true;
642 // We already know the link doesn't exist in the database, so
643 // tell LinkCache that before calling $title->isKnown().
644 $title = Title::makeTitle( NS_CATEGORY, $link );
645 $linkCache->addBadLinkObj( $title );
646 if ( $title->isKnown() ) {
647 $entry['known'] = true;
649 } elseif ( $hiddencats[$link] ) {
650 $entry['hidden'] = true;
652 $result[] = $entry;
655 return $result;
658 private function categoriesHtml( $categories ) {
659 $context = $this->getContext();
660 $context->getOutput()->addCategoryLinks( $categories );
662 return $context->getSkin()->getCategories();
665 private function formatLinks( $links ) {
666 $result = [];
667 foreach ( $links as $ns => $nslinks ) {
668 foreach ( $nslinks as $title => $id ) {
669 $entry = [];
670 $entry['ns'] = $ns;
671 ApiResult::setContentValue( $entry, 'title', Title::makeTitle( $ns, $title )->getFullText() );
672 $entry['exists'] = $id != 0;
673 $result[] = $entry;
677 return $result;
680 private function formatIWLinks( $iw ) {
681 $result = [];
682 foreach ( $iw as $prefix => $titles ) {
683 foreach ( array_keys( $titles ) as $title ) {
684 $entry = [];
685 $entry['prefix'] = $prefix;
687 $title = Title::newFromText( "{$prefix}:{$title}" );
688 if ( $title ) {
689 $entry['url'] = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
692 ApiResult::setContentValue( $entry, 'title', $title->getFullText() );
693 $result[] = $entry;
697 return $result;
700 private function formatHeadItems( $headItems ) {
701 $result = [];
702 foreach ( $headItems as $tag => $content ) {
703 $entry = [];
704 $entry['tag'] = $tag;
705 ApiResult::setContentValue( $entry, 'content', $content );
706 $result[] = $entry;
709 return $result;
712 private function formatLimitReportData( $limitReportData ) {
713 $result = [];
715 foreach ( $limitReportData as $name => $value ) {
716 $entry = [];
717 $entry['name'] = $name;
718 if ( !is_array( $value ) ) {
719 $value = [ $value ];
721 ApiResult::setIndexedTagNameRecursive( $value, 'param' );
722 $entry = array_merge( $entry, $value );
723 $result[] = $entry;
726 return $result;
729 private function setIndexedTagNames( &$array, $mapping ) {
730 foreach ( $mapping as $key => $name ) {
731 if ( isset( $array[$key] ) ) {
732 ApiResult::setIndexedTagName( $array[$key], $name );
737 public function getAllowedParams() {
738 return [
739 'title' => null,
740 'text' => [
741 ApiBase::PARAM_TYPE => 'text',
743 'summary' => null,
744 'page' => null,
745 'pageid' => [
746 ApiBase::PARAM_TYPE => 'integer',
748 'redirects' => false,
749 'oldid' => [
750 ApiBase::PARAM_TYPE => 'integer',
752 'prop' => [
753 ApiBase::PARAM_DFLT => 'text|langlinks|categories|links|templates|' .
754 'images|externallinks|sections|revid|displaytitle|iwlinks|properties',
755 ApiBase::PARAM_ISMULTI => true,
756 ApiBase::PARAM_TYPE => [
757 'text',
758 'langlinks',
759 'categories',
760 'categorieshtml',
761 'links',
762 'templates',
763 'images',
764 'externallinks',
765 'sections',
766 'revid',
767 'displaytitle',
768 'headitems',
769 'headhtml',
770 'modules',
771 'jsconfigvars',
772 'encodedjsconfigvars',
773 'indicators',
774 'iwlinks',
775 'wikitext',
776 'properties',
777 'limitreportdata',
778 'limitreporthtml',
779 'parsetree',
781 ApiBase::PARAM_HELP_MSG_PER_VALUE => [
782 'parsetree' => [ 'apihelp-parse-paramvalue-prop-parsetree', CONTENT_MODEL_WIKITEXT ],
785 'pst' => false,
786 'onlypst' => false,
787 'effectivelanglinks' => false,
788 'section' => null,
789 'sectiontitle' => [
790 ApiBase::PARAM_TYPE => 'string',
792 'disablepp' => [
793 ApiBase::PARAM_DFLT => false,
794 ApiBase::PARAM_DEPRECATED => true,
796 'disablelimitreport' => false,
797 'disableeditsection' => false,
798 'disabletidy' => false,
799 'generatexml' => [
800 ApiBase::PARAM_DFLT => false,
801 ApiBase::PARAM_HELP_MSG => [
802 'apihelp-parse-param-generatexml', CONTENT_MODEL_WIKITEXT
804 ApiBase::PARAM_DEPRECATED => true,
806 'preview' => false,
807 'sectionpreview' => false,
808 'disabletoc' => false,
809 'contentformat' => [
810 ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
812 'contentmodel' => [
813 ApiBase::PARAM_TYPE => ContentHandler::getContentModels(),
818 protected function getExamplesMessages() {
819 return [
820 'action=parse&page=Project:Sandbox'
821 => 'apihelp-parse-example-page',
822 'action=parse&text={{Project:Sandbox}}&contentmodel=wikitext'
823 => 'apihelp-parse-example-text',
824 'action=parse&text={{PAGENAME}}&title=Test'
825 => 'apihelp-parse-example-texttitle',
826 'action=parse&summary=Some+[[link]]&prop='
827 => 'apihelp-parse-example-summary',
831 public function getHelpUrls() {
832 return 'https://www.mediawiki.org/wiki/API:Parsing_wikitext#parse';