Fix missing commit() flag in postgres savepoint class
[mediawiki.git] / includes / api / ApiParse.php
blob0cad5dee9ce0e79a2c4fda5070551cbd171325bc
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 private function checkReadPermissions( Title $title ) {
40 if ( !$title->userCan( 'read', $this->getUser() ) ) {
41 $this->dieUsage( "You don't have permission to view this page", 'permissiondenied' );
45 public function execute() {
46 // The data is hot but user-dependent, like page views, so we set vary cookies
47 $this->getMain()->setCacheMode( 'anon-public-user-private' );
49 // Get parameters
50 $params = $this->extractRequestParams();
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 if ( !is_null( $page ) && ( !is_null( $text ) || $titleProvided ) ) {
69 $this->dieUsage(
70 'The page parameter cannot be used together with the text and title parameters',
71 'params'
75 $prop = array_flip( $params['prop'] );
77 if ( isset( $params['section'] ) ) {
78 $this->section = $params['section'];
79 if ( !preg_match( '/^((T-)?\d+|new)$/', $this->section ) ) {
80 $this->dieUsage(
81 'The section parameter must be a valid section id or "new"', 'invalidsection'
84 } else {
85 $this->section = false;
88 // The parser needs $wgTitle to be set, apparently the
89 // $title parameter in Parser::parse isn't enough *sigh*
90 // TODO: Does this still need $wgTitle?
91 global $wgParser, $wgTitle;
93 $redirValues = null;
95 // Return result
96 $result = $this->getResult();
98 if ( !is_null( $oldid ) || !is_null( $pageid ) || !is_null( $page ) ) {
99 if ( $this->section === 'new' ) {
100 $this->dieUsage(
101 'section=new cannot be combined with oldid, pageid or page parameters. ' .
102 'Please use text', 'params'
105 if ( !is_null( $oldid ) ) {
106 // Don't use the parser cache
107 $rev = Revision::newFromId( $oldid );
108 if ( !$rev ) {
109 $this->dieUsage( "There is no revision ID $oldid", 'missingrev' );
112 $this->checkReadPermissions( $rev->getTitle() );
113 if ( !$rev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) {
114 $this->dieUsage( "You don't have permission to view deleted revisions", 'permissiondenied' );
117 $titleObj = $rev->getTitle();
118 $wgTitle = $titleObj;
119 $pageObj = WikiPage::factory( $titleObj );
120 list( $popts, $reset, $suppressCache ) = $this->makeParserOptions( $pageObj, $params );
122 // If for some reason the "oldid" is actually the current revision, it may be cached
123 // Deliberately comparing $pageObj->getLatest() with $rev->getId(), rather than
124 // checking $rev->isCurrent(), because $pageObj is what actually ends up being used,
125 // and if its ->getLatest() is outdated, $rev->isCurrent() won't tell us that.
126 if ( !$suppressCache && $rev->getId() == $pageObj->getLatest() ) {
127 // May get from/save to parser cache
128 $p_result = $this->getParsedContent( $pageObj, $popts,
129 $pageid, isset( $prop['wikitext'] ) );
130 } else { // This is an old revision, so get the text differently
131 $this->content = $rev->getContent( Revision::FOR_THIS_USER, $this->getUser() );
133 if ( $this->section !== false ) {
134 $this->content = $this->getSectionContent( $this->content, 'r' . $rev->getId() );
137 // Should we save old revision parses to the parser cache?
138 $p_result = $this->content->getParserOutput( $titleObj, $rev->getId(), $popts );
140 } else { // Not $oldid, but $pageid or $page
141 if ( $params['redirects'] ) {
142 $reqParams = [
143 'redirects' => '',
145 if ( !is_null( $pageid ) ) {
146 $reqParams['pageids'] = $pageid;
147 } else { // $page
148 $reqParams['titles'] = $page;
150 $req = new FauxRequest( $reqParams );
151 $main = new ApiMain( $req );
152 $pageSet = new ApiPageSet( $main );
153 $pageSet->execute();
154 $redirValues = $pageSet->getRedirectTitlesAsResult( $this->getResult() );
156 $to = $page;
157 foreach ( $pageSet->getRedirectTitles() as $title ) {
158 $to = $title->getFullText();
160 $pageParams = [ 'title' => $to ];
161 } elseif ( !is_null( $pageid ) ) {
162 $pageParams = [ 'pageid' => $pageid ];
163 } else { // $page
164 $pageParams = [ 'title' => $page ];
167 $pageObj = $this->getTitleOrPageId( $pageParams, 'fromdb' );
168 $titleObj = $pageObj->getTitle();
169 if ( !$titleObj || !$titleObj->exists() ) {
170 $this->dieUsage( "The page you specified doesn't exist", 'missingtitle' );
173 $this->checkReadPermissions( $titleObj );
174 $wgTitle = $titleObj;
176 if ( isset( $prop['revid'] ) ) {
177 $oldid = $pageObj->getLatest();
180 list( $popts, $reset, $suppressCache ) = $this->makeParserOptions( $pageObj, $params );
182 // Don't pollute the parser cache when setting options that aren't
183 // in ParserOptions::optionsHash()
184 /// @todo: This should be handled closer to the actual cache instead of here, see T110269
185 $suppressCache = $suppressCache ||
186 $params['disablepp'] ||
187 $params['disablelimitreport'] ||
188 $params['preview'] ||
189 $params['sectionpreview'] ||
190 $params['disabletidy'];
192 if ( $suppressCache ) {
193 $this->content = $this->getContent( $pageObj, $pageid );
194 $p_result = $this->content->getParserOutput( $titleObj, null, $popts );
195 } else {
196 // Potentially cached
197 $p_result = $this->getParsedContent( $pageObj, $popts, $pageid,
198 isset( $prop['wikitext'] ) );
201 } else { // Not $oldid, $pageid, $page. Hence based on $text
202 $titleObj = Title::newFromText( $title );
203 if ( !$titleObj || $titleObj->isExternal() ) {
204 $this->dieUsageMsg( [ 'invalidtitle', $title ] );
206 $wgTitle = $titleObj;
207 if ( $titleObj->canExist() ) {
208 $pageObj = WikiPage::factory( $titleObj );
209 } else {
210 // Do like MediaWiki::initializeArticle()
211 $article = Article::newFromTitle( $titleObj, $this->getContext() );
212 $pageObj = $article->getPage();
215 list( $popts, $reset ) = $this->makeParserOptions( $pageObj, $params );
216 $textProvided = !is_null( $text );
218 if ( !$textProvided ) {
219 if ( $titleProvided && ( $prop || $params['generatexml'] ) ) {
220 $this->setWarning(
221 "'title' used without 'text', and parsed page properties were requested " .
222 "(did you mean to use 'page' instead of 'title'?)"
225 // Prevent warning from ContentHandler::makeContent()
226 $text = '';
229 // If we are parsing text, do not use the content model of the default
230 // API title, but default to wikitext to keep BC.
231 if ( $textProvided && !$titleProvided && is_null( $model ) ) {
232 $model = CONTENT_MODEL_WIKITEXT;
233 $this->setWarning( "No 'title' or 'contentmodel' was given, assuming $model." );
236 try {
237 $this->content = ContentHandler::makeContent( $text, $titleObj, $model, $format );
238 } catch ( MWContentSerializationException $ex ) {
239 $this->dieUsage( $ex->getMessage(), 'parseerror' );
242 if ( $this->section !== false ) {
243 if ( $this->section === 'new' ) {
244 // Insert the section title above the content.
245 if ( !is_null( $params['sectiontitle'] ) && $params['sectiontitle'] !== '' ) {
246 $this->content = $this->content->addSectionHeader( $params['sectiontitle'] );
248 } else {
249 $this->content = $this->getSectionContent( $this->content, $titleObj->getPrefixedText() );
253 if ( $params['pst'] || $params['onlypst'] ) {
254 $this->pstContent = $this->content->preSaveTransform( $titleObj, $this->getUser(), $popts );
256 if ( $params['onlypst'] ) {
257 // Build a result and bail out
258 $result_array = [];
259 $result_array['text'] = $this->pstContent->serialize( $format );
260 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'text';
261 if ( isset( $prop['wikitext'] ) ) {
262 $result_array['wikitext'] = $this->content->serialize( $format );
263 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'wikitext';
265 if ( !is_null( $params['summary'] ) ||
266 ( !is_null( $params['sectiontitle'] ) && $this->section === 'new' )
268 $result_array['parsedsummary'] = $this->formatSummary( $titleObj, $params );
269 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'parsedsummary';
272 $result->addValue( null, $this->getModuleName(), $result_array );
274 return;
277 // Not cached (save or load)
278 if ( $params['pst'] ) {
279 $p_result = $this->pstContent->getParserOutput( $titleObj, null, $popts );
280 } else {
281 $p_result = $this->content->getParserOutput( $titleObj, null, $popts );
285 $result_array = [];
287 $result_array['title'] = $titleObj->getPrefixedText();
288 $result_array['pageid'] = $pageid ?: $pageObj->getId();
290 if ( !is_null( $oldid ) ) {
291 $result_array['revid'] = intval( $oldid );
294 if ( $params['redirects'] && !is_null( $redirValues ) ) {
295 $result_array['redirects'] = $redirValues;
298 if ( $params['disabletoc'] ) {
299 $p_result->setTOCEnabled( false );
302 if ( isset( $prop['text'] ) ) {
303 $result_array['text'] = $p_result->getText();
304 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'text';
307 if ( !is_null( $params['summary'] ) ||
308 ( !is_null( $params['sectiontitle'] ) && $this->section === 'new' )
310 $result_array['parsedsummary'] = $this->formatSummary( $titleObj, $params );
311 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'parsedsummary';
314 if ( isset( $prop['langlinks'] ) ) {
315 $langlinks = $p_result->getLanguageLinks();
317 if ( $params['effectivelanglinks'] ) {
318 // Link flags are ignored for now, but may in the future be
319 // included in the result.
320 $linkFlags = [];
321 Hooks::run( 'LanguageLinks', [ $titleObj, &$langlinks, &$linkFlags ] );
323 } else {
324 $langlinks = false;
327 if ( isset( $prop['langlinks'] ) ) {
328 $result_array['langlinks'] = $this->formatLangLinks( $langlinks );
330 if ( isset( $prop['categories'] ) ) {
331 $result_array['categories'] = $this->formatCategoryLinks( $p_result->getCategories() );
333 if ( isset( $prop['categorieshtml'] ) ) {
334 $result_array['categorieshtml'] = $this->categoriesHtml( $p_result->getCategories() );
335 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'categorieshtml';
337 if ( isset( $prop['links'] ) ) {
338 $result_array['links'] = $this->formatLinks( $p_result->getLinks() );
340 if ( isset( $prop['templates'] ) ) {
341 $result_array['templates'] = $this->formatLinks( $p_result->getTemplates() );
343 if ( isset( $prop['images'] ) ) {
344 $result_array['images'] = array_keys( $p_result->getImages() );
346 if ( isset( $prop['externallinks'] ) ) {
347 $result_array['externallinks'] = array_keys( $p_result->getExternalLinks() );
349 if ( isset( $prop['sections'] ) ) {
350 $result_array['sections'] = $p_result->getSections();
353 if ( isset( $prop['displaytitle'] ) ) {
354 $result_array['displaytitle'] = $p_result->getDisplayTitle() ?:
355 $titleObj->getPrefixedText();
358 if ( isset( $prop['headitems'] ) ) {
359 $result_array['headitems'] = $this->formatHeadItems( $p_result->getHeadItems() );
360 $this->logFeatureUsage( 'action=parse&prop=headitems' );
361 $this->setWarning( 'headitems is deprecated since MediaWiki 1.28. '
362 . 'Use prop=headhtml when creating new HTML documents, or '
363 . 'prop=modules|jsconfigvars when updating a document client-side.' );
366 if ( isset( $prop['headhtml'] ) ) {
367 $context = new DerivativeContext( $this->getContext() );
368 $context->setTitle( $titleObj );
369 $context->setWikiPage( $pageObj );
371 // We need an OutputPage tied to $context, not to the
372 // RequestContext at the root of the stack.
373 $output = new OutputPage( $context );
374 $output->addParserOutputMetadata( $p_result );
376 $result_array['headhtml'] = $output->headElement( $context->getSkin() );
377 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'headhtml';
380 if ( isset( $prop['modules'] ) ) {
381 $result_array['modules'] = array_values( array_unique( $p_result->getModules() ) );
382 $result_array['modulescripts'] = array_values( array_unique( $p_result->getModuleScripts() ) );
383 $result_array['modulestyles'] = array_values( array_unique( $p_result->getModuleStyles() ) );
386 if ( isset( $prop['jsconfigvars'] ) ) {
387 $result_array['jsconfigvars'] =
388 ApiResult::addMetadataToResultVars( $p_result->getJsConfigVars() );
391 if ( isset( $prop['encodedjsconfigvars'] ) ) {
392 $result_array['encodedjsconfigvars'] = FormatJson::encode(
393 $p_result->getJsConfigVars(), false, FormatJson::ALL_OK
395 $result_array[ApiResult::META_SUBELEMENTS][] = 'encodedjsconfigvars';
398 if ( isset( $prop['modules'] ) &&
399 !isset( $prop['jsconfigvars'] ) && !isset( $prop['encodedjsconfigvars'] ) ) {
400 $this->setWarning( 'Property "modules" was set but not "jsconfigvars" ' .
401 'or "encodedjsconfigvars". Configuration variables are necessary ' .
402 'for proper module usage.' );
405 if ( isset( $prop['indicators'] ) ) {
406 $result_array['indicators'] = (array)$p_result->getIndicators();
407 ApiResult::setArrayType( $result_array['indicators'], 'BCkvp', 'name' );
410 if ( isset( $prop['iwlinks'] ) ) {
411 $result_array['iwlinks'] = $this->formatIWLinks( $p_result->getInterwikiLinks() );
414 if ( isset( $prop['wikitext'] ) ) {
415 $result_array['wikitext'] = $this->content->serialize( $format );
416 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'wikitext';
417 if ( !is_null( $this->pstContent ) ) {
418 $result_array['psttext'] = $this->pstContent->serialize( $format );
419 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'psttext';
422 if ( isset( $prop['properties'] ) ) {
423 $result_array['properties'] = (array)$p_result->getProperties();
424 ApiResult::setArrayType( $result_array['properties'], 'BCkvp', 'name' );
427 if ( isset( $prop['limitreportdata'] ) ) {
428 $result_array['limitreportdata'] =
429 $this->formatLimitReportData( $p_result->getLimitReportData() );
431 if ( isset( $prop['limitreporthtml'] ) ) {
432 $result_array['limitreporthtml'] = EditPage::getPreviewLimitReport( $p_result );
433 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'limitreporthtml';
436 if ( isset( $prop['parsetree'] ) || $params['generatexml'] ) {
437 if ( $this->content->getModel() != CONTENT_MODEL_WIKITEXT ) {
438 $this->dieUsage( 'parsetree is only supported for wikitext content', 'notwikitext' );
441 $wgParser->startExternalParse( $titleObj, $popts, Parser::OT_PREPROCESS );
442 $dom = $wgParser->preprocessToDom( $this->content->getNativeData() );
443 if ( is_callable( [ $dom, 'saveXML' ] ) ) {
444 $xml = $dom->saveXML();
445 } else {
446 $xml = $dom->__toString();
448 $result_array['parsetree'] = $xml;
449 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'parsetree';
452 $result_mapping = [
453 'redirects' => 'r',
454 'langlinks' => 'll',
455 'categories' => 'cl',
456 'links' => 'pl',
457 'templates' => 'tl',
458 'images' => 'img',
459 'externallinks' => 'el',
460 'iwlinks' => 'iw',
461 'sections' => 's',
462 'headitems' => 'hi',
463 'modules' => 'm',
464 'indicators' => 'ind',
465 'modulescripts' => 'm',
466 'modulestyles' => 'm',
467 'properties' => 'pp',
468 'limitreportdata' => 'lr',
470 $this->setIndexedTagNames( $result_array, $result_mapping );
471 $result->addValue( null, $this->getModuleName(), $result_array );
475 * Constructs a ParserOptions object
477 * @param WikiPage $pageObj
478 * @param array $params
480 * @return array [ ParserOptions, ScopedCallback, bool $suppressCache ]
482 protected function makeParserOptions( WikiPage $pageObj, array $params ) {
483 $popts = $pageObj->makeParserOptions( $this->getContext() );
484 $popts->enableLimitReport( !$params['disablepp'] && !$params['disablelimitreport'] );
485 $popts->setIsPreview( $params['preview'] || $params['sectionpreview'] );
486 $popts->setIsSectionPreview( $params['sectionpreview'] );
487 $popts->setEditSection( !$params['disableeditsection'] );
488 if ( $params['disabletidy'] ) {
489 $popts->setTidy( false );
492 $reset = null;
493 $suppressCache = false;
494 Hooks::run( 'ApiMakeParserOptions',
495 [ $popts, $pageObj->getTitle(), $params, $this, &$reset, &$suppressCache ] );
497 return [ $popts, $reset, $suppressCache ];
501 * @param WikiPage $page
502 * @param ParserOptions $popts
503 * @param int $pageId
504 * @param bool $getWikitext
505 * @return ParserOutput
507 private function getParsedContent( WikiPage $page, $popts, $pageId = null, $getWikitext = false ) {
508 $this->content = $this->getContent( $page, $pageId );
510 if ( $this->section !== false && $this->content !== null ) {
511 // Not cached (save or load)
512 return $this->content->getParserOutput( $page->getTitle(), null, $popts );
515 // Try the parser cache first
516 // getParserOutput will save to Parser cache if able
517 $pout = $page->getParserOutput( $popts );
518 if ( !$pout ) {
519 $this->dieUsage( "There is no revision ID {$page->getLatest()}", 'missingrev' );
521 if ( $getWikitext ) {
522 $this->content = $page->getContent( Revision::RAW );
525 return $pout;
529 * Get the content for the given page and the requested section.
531 * @param WikiPage $page
532 * @param int $pageId
533 * @return Content
535 private function getContent( WikiPage $page, $pageId = null ) {
536 $content = $page->getContent( Revision::RAW ); // XXX: really raw?
538 if ( $this->section !== false && $content !== null ) {
539 $content = $this->getSectionContent(
540 $content,
541 !is_null( $pageId ) ? 'page id ' . $pageId : $page->getTitle()->getPrefixedText()
544 return $content;
548 * Extract the requested section from the given Content
550 * @param Content $content
551 * @param string $what Identifies the content in error messages, e.g. page title.
552 * @return Content|bool
554 private function getSectionContent( Content $content, $what ) {
555 // Not cached (save or load)
556 $section = $content->getSection( $this->section );
557 if ( $section === false ) {
558 $this->dieUsage( "There is no section {$this->section} in $what", 'nosuchsection' );
560 if ( $section === null ) {
561 $this->dieUsage( "Sections are not supported by $what", 'nosuchsection' );
562 $section = false;
565 return $section;
569 * This mimicks the behavior of EditPage in formatting a summary
571 * @param Title $title of the page being parsed
572 * @param Array $params the API parameters of the request
573 * @return Content|bool
575 private function formatSummary( $title, $params ) {
576 global $wgParser;
577 $summary = !is_null( $params['summary'] ) ? $params['summary'] : '';
578 $sectionTitle = !is_null( $params['sectiontitle'] ) ? $params['sectiontitle'] : '';
580 if ( $this->section === 'new' && ( $sectionTitle === '' || $summary === '' ) ) {
581 if ( $sectionTitle !== '' ) {
582 $summary = $params['sectiontitle'];
584 if ( $summary !== '' ) {
585 $summary = wfMessage( 'newsectionsummary' )
586 ->rawParams( $wgParser->stripSectionName( $summary ) )
587 ->inContentLanguage()->text();
590 return Linker::formatComment( $summary, $title, $this->section === 'new' );
593 private function formatLangLinks( $links ) {
594 $result = [];
595 foreach ( $links as $link ) {
596 $entry = [];
597 $bits = explode( ':', $link, 2 );
598 $title = Title::newFromText( $link );
600 $entry['lang'] = $bits[0];
601 if ( $title ) {
602 $entry['url'] = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
603 // localised language name in 'uselang' language
604 $entry['langname'] = Language::fetchLanguageName(
605 $title->getInterwiki(),
606 $this->getLanguage()->getCode()
609 // native language name
610 $entry['autonym'] = Language::fetchLanguageName( $title->getInterwiki() );
612 ApiResult::setContentValue( $entry, 'title', $bits[1] );
613 $result[] = $entry;
616 return $result;
619 private function formatCategoryLinks( $links ) {
620 $result = [];
622 if ( !$links ) {
623 return $result;
626 // Fetch hiddencat property
627 $lb = new LinkBatch;
628 $lb->setArray( [ NS_CATEGORY => $links ] );
629 $db = $this->getDB();
630 $res = $db->select( [ 'page', 'page_props' ],
631 [ 'page_title', 'pp_propname' ],
632 $lb->constructSet( 'page', $db ),
633 __METHOD__,
635 [ 'page_props' => [
636 'LEFT JOIN', [ 'pp_propname' => 'hiddencat', 'pp_page = page_id' ]
639 $hiddencats = [];
640 foreach ( $res as $row ) {
641 $hiddencats[$row->page_title] = isset( $row->pp_propname );
644 $linkCache = LinkCache::singleton();
646 foreach ( $links as $link => $sortkey ) {
647 $entry = [];
648 $entry['sortkey'] = $sortkey;
649 // array keys will cast numeric category names to ints, so cast back to string
650 ApiResult::setContentValue( $entry, 'category', (string)$link );
651 if ( !isset( $hiddencats[$link] ) ) {
652 $entry['missing'] = true;
654 // We already know the link doesn't exist in the database, so
655 // tell LinkCache that before calling $title->isKnown().
656 $title = Title::makeTitle( NS_CATEGORY, $link );
657 $linkCache->addBadLinkObj( $title );
658 if ( $title->isKnown() ) {
659 $entry['known'] = true;
661 } elseif ( $hiddencats[$link] ) {
662 $entry['hidden'] = true;
664 $result[] = $entry;
667 return $result;
670 private function categoriesHtml( $categories ) {
671 $context = $this->getContext();
672 $context->getOutput()->addCategoryLinks( $categories );
674 return $context->getSkin()->getCategories();
677 private function formatLinks( $links ) {
678 $result = [];
679 foreach ( $links as $ns => $nslinks ) {
680 foreach ( $nslinks as $title => $id ) {
681 $entry = [];
682 $entry['ns'] = $ns;
683 ApiResult::setContentValue( $entry, 'title', Title::makeTitle( $ns, $title )->getFullText() );
684 $entry['exists'] = $id != 0;
685 $result[] = $entry;
689 return $result;
692 private function formatIWLinks( $iw ) {
693 $result = [];
694 foreach ( $iw as $prefix => $titles ) {
695 foreach ( array_keys( $titles ) as $title ) {
696 $entry = [];
697 $entry['prefix'] = $prefix;
699 $title = Title::newFromText( "{$prefix}:{$title}" );
700 if ( $title ) {
701 $entry['url'] = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
704 ApiResult::setContentValue( $entry, 'title', $title->getFullText() );
705 $result[] = $entry;
709 return $result;
712 private function formatHeadItems( $headItems ) {
713 $result = [];
714 foreach ( $headItems as $tag => $content ) {
715 $entry = [];
716 $entry['tag'] = $tag;
717 ApiResult::setContentValue( $entry, 'content', $content );
718 $result[] = $entry;
721 return $result;
724 private function formatLimitReportData( $limitReportData ) {
725 $result = [];
727 foreach ( $limitReportData as $name => $value ) {
728 $entry = [];
729 $entry['name'] = $name;
730 if ( !is_array( $value ) ) {
731 $value = [ $value ];
733 ApiResult::setIndexedTagNameRecursive( $value, 'param' );
734 $entry = array_merge( $entry, $value );
735 $result[] = $entry;
738 return $result;
741 private function setIndexedTagNames( &$array, $mapping ) {
742 foreach ( $mapping as $key => $name ) {
743 if ( isset( $array[$key] ) ) {
744 ApiResult::setIndexedTagName( $array[$key], $name );
749 public function getAllowedParams() {
750 return [
751 'title' => null,
752 'text' => [
753 ApiBase::PARAM_TYPE => 'text',
755 'summary' => null,
756 'page' => null,
757 'pageid' => [
758 ApiBase::PARAM_TYPE => 'integer',
760 'redirects' => false,
761 'oldid' => [
762 ApiBase::PARAM_TYPE => 'integer',
764 'prop' => [
765 ApiBase::PARAM_DFLT => 'text|langlinks|categories|links|templates|' .
766 'images|externallinks|sections|revid|displaytitle|iwlinks|properties',
767 ApiBase::PARAM_ISMULTI => true,
768 ApiBase::PARAM_TYPE => [
769 'text',
770 'langlinks',
771 'categories',
772 'categorieshtml',
773 'links',
774 'templates',
775 'images',
776 'externallinks',
777 'sections',
778 'revid',
779 'displaytitle',
780 'headitems',
781 'headhtml',
782 'modules',
783 'jsconfigvars',
784 'encodedjsconfigvars',
785 'indicators',
786 'iwlinks',
787 'wikitext',
788 'properties',
789 'limitreportdata',
790 'limitreporthtml',
791 'parsetree',
793 ApiBase::PARAM_HELP_MSG_PER_VALUE => [
794 'parsetree' => [ 'apihelp-parse-paramvalue-prop-parsetree', CONTENT_MODEL_WIKITEXT ],
797 'pst' => false,
798 'onlypst' => false,
799 'effectivelanglinks' => false,
800 'section' => null,
801 'sectiontitle' => [
802 ApiBase::PARAM_TYPE => 'string',
804 'disablepp' => [
805 ApiBase::PARAM_DFLT => false,
806 ApiBase::PARAM_DEPRECATED => true,
808 'disablelimitreport' => false,
809 'disableeditsection' => false,
810 'disabletidy' => false,
811 'generatexml' => [
812 ApiBase::PARAM_DFLT => false,
813 ApiBase::PARAM_HELP_MSG => [
814 'apihelp-parse-param-generatexml', CONTENT_MODEL_WIKITEXT
816 ApiBase::PARAM_DEPRECATED => true,
818 'preview' => false,
819 'sectionpreview' => false,
820 'disabletoc' => false,
821 'contentformat' => [
822 ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
824 'contentmodel' => [
825 ApiBase::PARAM_TYPE => ContentHandler::getContentModels(),
830 protected function getExamplesMessages() {
831 return [
832 'action=parse&page=Project:Sandbox'
833 => 'apihelp-parse-example-page',
834 'action=parse&text={{Project:Sandbox}}&contentmodel=wikitext'
835 => 'apihelp-parse-example-text',
836 'action=parse&text={{PAGENAME}}&title=Test'
837 => 'apihelp-parse-example-texttitle',
838 'action=parse&summary=Some+[[link]]&prop='
839 => 'apihelp-parse-example-summary',
843 public function getHelpUrls() {
844 return 'https://www.mediawiki.org/wiki/API:Parsing_wikitext#parse';