47 if ( $this->
getUser()->isAnon() ) {
48 $poolKey .=
'a:' . $this->
getUser()->getName();
50 $poolKey .=
'u:' . $this->
getUser()->getId();
66 'error' =>
function () {
82 'doWork' =>
function () use ( $page, $revId, $popts, $suppressCache ) {
85 'error' =>
function () {
95 $this->
getMain()->setCacheMode(
'anon-public-user-private' );
106 $text = $params[
'text'];
107 $title = $params[
'title'];
109 $titleProvided =
false;
113 $titleProvided =
true;
116 $page = $params[
'page'];
117 $pageid = $params[
'pageid'];
118 $oldid = $params[
'oldid'];
120 $model = $params[
'contentmodel'];
121 $format = $params[
'contentformat'];
123 $prop = array_flip( $params[
'prop'] );
125 if ( isset( $params[
'section'] ) ) {
126 $this->section = $params[
'section'];
127 if ( !preg_match(
'/^((T-)?\d+|new)$/', $this->section ) ) {
131 $this->section =
false;
141 $needContent = isset( $prop[
'wikitext'] ) ||
142 isset( $prop[
'parsetree'] ) || $params[
'generatexml'];
147 $revisionLookup = MediaWikiServices::getInstance()->getRevisionLookup();
148 if ( $oldid !==
null || $pageid !==
null || $page !==
null ) {
149 if ( $this->section ===
'new' ) {
150 $this->
dieWithError(
'apierror-invalidparammix-parse-new-section',
'invalidparammix' );
152 if ( $oldid !==
null ) {
154 $rev = $revisionLookup->getRevisionById( $oldid );
156 $this->
dieWithError( [
'apierror-nosuchrevid', $oldid ] );
159 $revLinkTarget = $rev->getPageAsLinkTarget();
162 if ( !$rev->audienceCan(
163 RevisionRecord::DELETED_TEXT,
164 RevisionRecord::FOR_THIS_USER,
168 [
'apierror-permissiondenied', $this->
msg(
'action-deletedtext' ) ]
175 list( $popts, $reset, $suppressCache ) = $this->
makeParserOptions( $pageObj, $params );
177 $pageObj, $popts, $suppressCache, $pageid, $rev, $needContent
180 if ( $params[
'redirects'] ) {
185 if ( $pageid !==
null ) {
186 $reqParams[
'pageids'] = $pageid;
187 $pageParams[
'pageid'] = $pageid;
189 $reqParams[
'titles'] = $page;
190 $pageParams[
'title'] = $page;
196 $redirValues = $pageSet->getRedirectTitlesAsResult( $this->
getResult() );
198 foreach ( $pageSet->getRedirectTitles() as
$title ) {
199 $pageParams = [
'title' =>
$title->getFullText() ];
201 } elseif ( $pageid !==
null ) {
202 $pageParams = [
'pageid' => $pageid ];
204 $pageParams = [
'title' => $page ];
208 $titleObj = $pageObj->getTitle();
209 if ( !$titleObj || !$titleObj->exists() ) {
216 if ( isset( $prop[
'revid'] ) ) {
217 $oldid = $pageObj->getLatest();
220 list( $popts, $reset, $suppressCache ) = $this->
makeParserOptions( $pageObj, $params );
222 $pageObj, $popts, $suppressCache, $pageid,
null, $needContent
227 if ( !$titleObj || $titleObj->isExternal() ) {
230 $revid = $params[
'revid'];
231 if ( $revid !==
null ) {
232 $rev = $revisionLookup->getRevisionById( $revid );
234 $this->
dieWithError( [
'apierror-nosuchrevid', $revid ] );
236 $pTitleObj = $titleObj;
238 if ( $titleProvided ) {
239 if ( !$titleObj->equals( $pTitleObj ) ) {
240 $this->
addWarning( [
'apierror-revwrongpage', $rev->getId(),
246 $titleProvided =
true;
250 if ( $titleObj->canExist() ) {
260 $textProvided = $text !==
null;
262 if ( !$textProvided ) {
263 if ( $titleProvided && ( $prop || $params[
'generatexml'] ) ) {
264 if ( $revid !==
null ) {
265 $this->
addWarning(
'apiwarn-parse-revidwithouttext' );
267 $this->
addWarning(
'apiwarn-parse-titlewithouttext' );
276 if ( $textProvided && !$titleProvided && $model ===
null ) {
278 $this->
addWarning( [
'apiwarn-parse-nocontentmodel', $model ] );
285 'wrap' =>
ApiMessage::create(
'apierror-contentserializationexception',
'parseerror' )
289 if ( $this->section !==
false ) {
290 if ( $this->section ===
'new' ) {
292 if ( $params[
'sectiontitle'] !==
null && $params[
'sectiontitle'] !==
'' ) {
293 $this->content = $this->content->addSectionHeader( $params[
'sectiontitle'] );
296 $this->content = $this->
getSectionContent( $this->content, $titleObj->getPrefixedText() );
300 if ( $params[
'pst'] || $params[
'onlypst'] ) {
301 $this->pstContent = $this->content->preSaveTransform( $titleObj, $this->
getUser(), $popts );
303 if ( $params[
'onlypst'] ) {
306 $result_array[
'text'] = $this->pstContent->serialize( $format );
308 if ( isset( $prop[
'wikitext'] ) ) {
309 $result_array[
'wikitext'] = $this->content->serialize( $format );
312 if ( $params[
'summary'] !==
null ||
313 ( $params[
'sectiontitle'] !==
null && $this->section ===
'new' )
315 $result_array[
'parsedsummary'] = $this->
formatSummary( $titleObj, $params );
319 $result->addValue(
null, $this->
getModuleName(), $result_array );
325 if ( $params[
'pst'] ) {
334 $result_array[
'title'] = $titleObj->getPrefixedText();
335 $result_array[
'pageid'] = $pageid ?: $titleObj->getArticleID();
336 if ( $this->contentIsDeleted ) {
337 $result_array[
'textdeleted'] =
true;
339 if ( $this->contentIsSuppressed ) {
340 $result_array[
'textsuppressed'] =
true;
343 if ( isset( $params[
'useskin'] ) ) {
344 $factory = MediaWikiServices::getInstance()->getSkinFactory();
352 if ( $skin || isset( $prop[
'subtitle'] ) || isset( $prop[
'headhtml'] ) || isset( $prop[
'categorieshtml'] ) ) {
386 $outputPage->setArticleFlag(
true );
388 $outputPage->addParserOutputMetadata( $p_result );
389 if ( $this->content ) {
390 $outputPage->addContentOverride( $titleObj, $this->content );
396 $skin->doSetupSkinUserCss( $outputPage );
399 $outputPage->loadSkinModules( $skin );
402 $this->
getHookRunner()->onApiParseMakeOutputPage( $this, $outputPage );
405 if ( $oldid !==
null ) {
406 $result_array[
'revid'] = (int)$oldid;
409 if ( $params[
'redirects'] && $redirValues !==
null ) {
410 $result_array[
'redirects'] = $redirValues;
413 if ( isset( $prop[
'text'] ) ) {
414 $result_array[
'text'] = $p_result->getText( [
415 'allowTOC' => !$params[
'disabletoc'],
416 'enableSectionEditLinks' => !$params[
'disableeditsection'],
417 'wrapperDivClass' => $params[
'wrapoutputclass'],
418 'deduplicateStyles' => !$params[
'disablestylededuplication'],
427 if ( $params[
'summary'] !==
null ||
428 ( $params[
'sectiontitle'] !==
null && $this->section ===
'new' )
430 $result_array[
'parsedsummary'] = $this->
formatSummary( $titleObj, $params );
434 if ( isset( $prop[
'langlinks'] ) ) {
436 $langlinks = $outputPage->getLanguageLinks();
438 $langlinks = $p_result->getLanguageLinks();
442 if ( $params[
'effectivelanglinks'] ) {
444 $this->
getHookRunner()->onLanguageLinks( $titleObj, $langlinks, $linkFlags );
450 if ( isset( $prop[
'categories'] ) ) {
453 if ( isset( $prop[
'categorieshtml'] ) ) {
454 $result_array[
'categorieshtml'] = $outputPage->getSkin()->getCategories();
457 if ( isset( $prop[
'links'] ) ) {
458 $result_array[
'links'] = $this->
formatLinks( $p_result->getLinks() );
460 if ( isset( $prop[
'templates'] ) ) {
461 $result_array[
'templates'] = $this->
formatLinks( $p_result->getTemplates() );
463 if ( isset( $prop[
'images'] ) ) {
464 $result_array[
'images'] = array_keys( $p_result->getImages() );
466 if ( isset( $prop[
'externallinks'] ) ) {
467 $result_array[
'externallinks'] = array_keys( $p_result->getExternalLinks() );
469 if ( isset( $prop[
'sections'] ) ) {
470 $result_array[
'sections'] = $p_result->getSections();
472 if ( isset( $prop[
'parsewarnings'] ) ) {
473 $result_array[
'parsewarnings'] = $p_result->getWarnings();
476 if ( isset( $prop[
'displaytitle'] ) ) {
477 $result_array[
'displaytitle'] = $p_result->getDisplayTitle() !==
false
478 ? $p_result->getDisplayTitle() : $titleObj->getPrefixedText();
481 if ( isset( $prop[
'subtitle'] ) ) {
485 if ( isset( $prop[
'headitems'] ) ) {
487 $result_array[
'headitems'] = $this->
formatHeadItems( $outputPage->getHeadItemsArray() );
489 $result_array[
'headitems'] = $this->
formatHeadItems( $p_result->getHeadItems() );
493 if ( isset( $prop[
'headhtml'] ) ) {
494 $result_array[
'headhtml'] = $outputPage->headElement(
$context->
getSkin() );
498 if ( isset( $prop[
'modules'] ) ) {
500 $result_array[
'modules'] = $outputPage->getModules();
502 $result_array[
'modulescripts'] = [];
503 $result_array[
'modulestyles'] = $outputPage->getModuleStyles();
505 $result_array[
'modules'] = array_values( array_unique( $p_result->getModules() ) );
507 $result_array[
'modulescripts'] = [];
508 $result_array[
'modulestyles'] = array_values( array_unique( $p_result->getModuleStyles() ) );
512 if ( isset( $prop[
'jsconfigvars'] ) ) {
513 $jsconfigvars = $skin ? $outputPage->getJsConfigVars() : $p_result->getJsConfigVars();
517 if ( isset( $prop[
'encodedjsconfigvars'] ) ) {
518 $jsconfigvars = $skin ? $outputPage->getJsConfigVars() : $p_result->getJsConfigVars();
527 if ( isset( $prop[
'modules'] ) &&
528 !isset( $prop[
'jsconfigvars'] ) && !isset( $prop[
'encodedjsconfigvars'] ) ) {
529 $this->
addWarning(
'apiwarn-moduleswithoutvars' );
532 if ( isset( $prop[
'indicators'] ) ) {
534 $result_array[
'indicators'] = (array)$outputPage->getIndicators();
536 $result_array[
'indicators'] = (array)$p_result->getIndicators();
541 if ( isset( $prop[
'iwlinks'] ) ) {
542 $result_array[
'iwlinks'] = $this->
formatIWLinks( $p_result->getInterwikiLinks() );
545 if ( isset( $prop[
'wikitext'] ) ) {
546 $result_array[
'wikitext'] = $this->content->serialize( $format );
548 if ( $this->pstContent !==
null ) {
549 $result_array[
'psttext'] = $this->pstContent->serialize( $format );
553 if ( isset( $prop[
'properties'] ) ) {
554 $result_array[
'properties'] = (array)$p_result->getProperties();
558 if ( isset( $prop[
'limitreportdata'] ) ) {
559 $result_array[
'limitreportdata'] =
562 if ( isset( $prop[
'limitreporthtml'] ) ) {
567 if ( isset( $prop[
'parsetree'] ) || $params[
'generatexml'] ) {
569 $this->
dieWithError(
'apierror-parsetree-notwikitext',
'notwikitext' );
572 $parser = MediaWikiServices::getInstance()->getParser();
575 $xml = $parser->preprocessToDom( $this->content->getText() )->__toString();
576 $result_array[
'parsetree'] = $xml;
583 'categories' =>
'cl',
587 'externallinks' =>
'el',
592 'indicators' =>
'ind',
593 'modulescripts' =>
'm',
594 'modulestyles' =>
'm',
595 'properties' =>
'pp',
596 'limitreportdata' =>
'lr',
597 'parsewarnings' =>
'pw'
600 $result->addValue(
null, $this->
getModuleName(), $result_array );
626 $popts->
enableLimitReport( !$params[
'disablepp'] && !$params[
'disablelimitreport'] );
627 $popts->
setIsPreview( $params[
'preview'] || $params[
'sectionpreview'] );
630 if ( $params[
'wrapoutputclass'] !==
'' ) {
635 $suppressCache =
false;
637 $params, $this, $reset, $suppressCache );
639 return [ $popts, $reset, $suppressCache ];
652 WikiPage $page, $popts, $suppressCache, $pageId, $rev, $getContent
654 $revId = $rev ? $rev->getId() :
null;
655 $isDeleted = $rev && $rev->isDeleted( RevisionRecord::DELETED_TEXT );
657 if ( $getContent || $this->section !==
false || $isDeleted ) {
659 $this->content = $rev->getContent(
660 SlotRecord::MAIN, RevisionRecord::FOR_THIS_USER, $this->
getUser()
662 if ( !$this->content ) {
663 $this->
dieWithError( [
'apierror-missingcontent-revid', $revId ] );
666 $this->content = $page->
getContent( RevisionRecord::FOR_THIS_USER, $this->
getUser() );
667 if ( !$this->content ) {
671 $this->contentIsDeleted = $isDeleted;
672 $this->contentIsSuppressed = $rev &&
673 $rev->isDeleted( RevisionRecord::DELETED_TEXT | RevisionRecord::DELETED_RESTRICTED );
676 if ( $this->section !==
false ) {
679 $pageId ===
null ? $page->
getTitle()->getPrefixedText() : $this->msg(
'pageid', $pageId )
712 $this->
dieWithError( [
'apierror-nosuchsection-what', $this->section, $what ],
'nosuchsection' );
715 $this->
dieWithError( [
'apierror-sectionsnotsupported-what', $what ],
'nosuchsection' );
730 $summary = $params[
'summary'] ??
'';
731 $sectionTitle = $params[
'sectiontitle'] ??
'';
733 if ( $this->section ===
'new' && ( $sectionTitle ===
'' || $summary ===
'' ) ) {
734 if ( $sectionTitle !==
'' ) {
735 $summary = $params[
'sectiontitle'];
737 if ( $summary !==
'' ) {
738 $summary =
wfMessage(
'newsectionsummary' )
739 ->rawParams( MediaWikiServices::getInstance()->getParser()
740 ->stripSectionName( $summary ) )
741 ->inContentLanguage()->text();
748 $languageNameUtils = MediaWikiServices::getInstance()->getLanguageNameUtils();
750 foreach ( $links as $link ) {
752 $bits = explode(
':', $link, 2 );
755 $entry[
'lang'] = $bits[0];
759 $entry[
'langname'] = $languageNameUtils->getLanguageName(
761 $this->getLanguage()->getCode()
765 $entry[
'autonym'] = $languageNameUtils->getLanguageName(
$title->getInterwiki() );
782 $linkBatchFactory = MediaWikiServices::getInstance()->getLinkBatchFactory();
783 $lb = $linkBatchFactory->newLinkBatch();
785 $db = $this->
getDB();
786 $res = $db->select( [
'page',
'page_props' ],
787 [
'page_title',
'pp_propname' ],
788 $lb->constructSet(
'page', $db ),
792 'LEFT JOIN', [
'pp_propname' =>
'hiddencat',
'pp_page = page_id' ]
796 foreach (
$res as $row ) {
797 $hiddencats[$row->page_title] = isset( $row->pp_propname );
800 $linkCache = MediaWikiServices::getInstance()->getLinkCache();
802 foreach ( $links as $link => $sortkey ) {
804 $entry[
'sortkey'] = $sortkey;
807 if ( !isset( $hiddencats[$link] ) ) {
808 $entry[
'missing'] =
true;
813 $linkCache->addBadLinkObj(
$title );
814 if (
$title->isKnown() ) {
815 $entry[
'known'] =
true;
817 } elseif ( $hiddencats[$link] ) {
818 $entry[
'hidden'] =
true;
828 foreach ( $links as $ns => $nslinks ) {
829 foreach ( $nslinks as
$title => $id ) {
833 $entry[
'exists'] = $id != 0;
843 foreach ( $iw as $prefix => $titles ) {
844 foreach ( array_keys( $titles ) as
$title ) {
846 $entry[
'prefix'] = $prefix;
863 foreach ( $headItems as $tag =>
$content ) {
865 $entry[
'tag'] = $tag;
876 foreach ( $limitReportData as $name => $value ) {
878 $entry[
'name'] = $name;
879 if ( !is_array( $value ) ) {
883 $entry = array_merge( $entry, $value );
891 foreach ( $mapping as $key => $name ) {
892 if ( isset( $array[$key] ) ) {
899 $skinFactory = MediaWikiServices::getInstance()->getSkinFactory();
914 'redirects' =>
false,
920 'images|externallinks|sections|revid|displaytitle|iwlinks|' .
921 'properties|parsewarnings',
939 'encodedjsconfigvars',
954 'headitems' =>
'apiwarn-deprecation-parse-headitems',
957 'wrapoutputclass' =>
'mw-parser-output',
960 'effectivelanglinks' => [
972 'disablelimitreport' =>
false,
973 'disableeditsection' =>
false,
974 'disablestylededuplication' =>
false,
983 'sectionpreview' =>
false,
984 'disabletoc' =>
false,
999 'action=parse&page=Project:Sandbox'
1000 =>
'apihelp-parse-example-page',
1001 'action=parse&text={{Project:Sandbox}}&contentmodel=wikitext'
1002 =>
'apihelp-parse-example-text',
1003 'action=parse&text={{PAGENAME}}&title=Test'
1004 =>
'apihelp-parse-example-texttitle',
1005 'action=parse&summary=Some+[[link]]&prop='
1006 =>
'apihelp-parse-example-summary',
1011 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Parsing_wikitext#parse';
1015 return MediaWikiServices::getInstance()->getContentHandlerFactory();