MediaWiki REL1_34
ApiParse.php
Go to the documentation of this file.
1<?php
25
29class ApiParse extends ApiBase {
30
32 private $section = null;
33
35 private $content = null;
36
38 private $pstContent = null;
39
41 private $contentIsDeleted = false, $contentIsSuppressed = false;
42
43 public function execute() {
44 // The data is hot but user-dependent, like page views, so we set vary cookies
45 $this->getMain()->setCacheMode( 'anon-public-user-private' );
46
47 // Get parameters
48 $params = $this->extractRequestParams();
49
50 // No easy way to say that text and title or revid are allowed together
51 // while the rest aren't, so just do it in three calls.
52 $this->requireMaxOneParameter( $params, 'page', 'pageid', 'oldid', 'text' );
53 $this->requireMaxOneParameter( $params, 'page', 'pageid', 'oldid', 'title' );
54 $this->requireMaxOneParameter( $params, 'page', 'pageid', 'oldid', 'revid' );
55
56 $text = $params['text'];
57 $title = $params['title'];
58 if ( $title === null ) {
59 $titleProvided = false;
60 // A title is needed for parsing, so arbitrarily choose one
61 $title = 'API';
62 } else {
63 $titleProvided = true;
64 }
65
66 $page = $params['page'];
67 $pageid = $params['pageid'];
68 $oldid = $params['oldid'];
69
70 $model = $params['contentmodel'];
71 $format = $params['contentformat'];
72
73 $prop = array_flip( $params['prop'] );
74
75 if ( isset( $params['section'] ) ) {
76 $this->section = $params['section'];
77 if ( !preg_match( '/^((T-)?\d+|new)$/', $this->section ) ) {
78 $this->dieWithError( 'apierror-invalidsection' );
79 }
80 } else {
81 $this->section = false;
82 }
83
84 // The parser needs $wgTitle to be set, apparently the
85 // $title parameter in Parser::parse isn't enough *sigh*
86 // TODO: Does this still need $wgTitle?
87 global $wgTitle;
88
89 $redirValues = null;
90
91 $needContent = isset( $prop['wikitext'] ) ||
92 isset( $prop['parsetree'] ) || $params['generatexml'];
93
94 // Return result
95 $result = $this->getResult();
96
97 if ( !is_null( $oldid ) || !is_null( $pageid ) || !is_null( $page ) ) {
98 if ( $this->section === 'new' ) {
99 $this->dieWithError( 'apierror-invalidparammix-parse-new-section', 'invalidparammix' );
100 }
101 if ( !is_null( $oldid ) ) {
102 // Don't use the parser cache
103 $rev = Revision::newFromId( $oldid );
104 if ( !$rev ) {
105 $this->dieWithError( [ 'apierror-nosuchrevid', $oldid ] );
106 }
107
108 $this->checkTitleUserPermissions( $rev->getTitle(), 'read' );
109 if ( !$rev->userCan( RevisionRecord::DELETED_TEXT, $this->getUser() ) ) {
110 $this->dieWithError(
111 [ 'apierror-permissiondenied', $this->msg( 'action-deletedtext' ) ]
112 );
113 }
114
115 $titleObj = $rev->getTitle();
116 $wgTitle = $titleObj;
117 $pageObj = WikiPage::factory( $titleObj );
118 list( $popts, $reset, $suppressCache ) = $this->makeParserOptions( $pageObj, $params );
119 $p_result = $this->getParsedContent(
120 $pageObj, $popts, $suppressCache, $pageid, $rev, $needContent
121 );
122 } else { // Not $oldid, but $pageid or $page
123 if ( $params['redirects'] ) {
124 $reqParams = [
125 'redirects' => '',
126 ];
127 if ( !is_null( $pageid ) ) {
128 $reqParams['pageids'] = $pageid;
129 } else { // $page
130 $reqParams['titles'] = $page;
131 }
132 $req = new FauxRequest( $reqParams );
133 $main = new ApiMain( $req );
134 $pageSet = new ApiPageSet( $main );
135 $pageSet->execute();
136 $redirValues = $pageSet->getRedirectTitlesAsResult( $this->getResult() );
137
138 $to = $page;
139 foreach ( $pageSet->getRedirectTitles() as $title ) {
140 $to = $title->getFullText();
141 }
142 $pageParams = [ 'title' => $to ];
143 } elseif ( !is_null( $pageid ) ) {
144 $pageParams = [ 'pageid' => $pageid ];
145 } else { // $page
146 $pageParams = [ 'title' => $page ];
147 }
148
149 $pageObj = $this->getTitleOrPageId( $pageParams, 'fromdb' );
150 $titleObj = $pageObj->getTitle();
151 if ( !$titleObj || !$titleObj->exists() ) {
152 $this->dieWithError( 'apierror-missingtitle' );
153 }
154
155 $this->checkTitleUserPermissions( $titleObj, 'read' );
156 $wgTitle = $titleObj;
157
158 if ( isset( $prop['revid'] ) ) {
159 $oldid = $pageObj->getLatest();
160 }
161
162 list( $popts, $reset, $suppressCache ) = $this->makeParserOptions( $pageObj, $params );
163 $p_result = $this->getParsedContent(
164 $pageObj, $popts, $suppressCache, $pageid, null, $needContent
165 );
166 }
167 } else { // Not $oldid, $pageid, $page. Hence based on $text
168 $titleObj = Title::newFromText( $title );
169 if ( !$titleObj || $titleObj->isExternal() ) {
170 $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $title ) ] );
171 }
172 $revid = $params['revid'];
173 if ( $revid !== null ) {
174 $rev = Revision::newFromId( $revid );
175 if ( !$rev ) {
176 $this->dieWithError( [ 'apierror-nosuchrevid', $revid ] );
177 }
178 $pTitleObj = $titleObj;
179 $titleObj = $rev->getTitle();
180 if ( $titleProvided ) {
181 if ( !$titleObj->equals( $pTitleObj ) ) {
182 $this->addWarning( [ 'apierror-revwrongpage', $rev->getId(),
183 wfEscapeWikiText( $pTitleObj->getPrefixedText() ) ] );
184 }
185 } else {
186 // Consider the title derived from the revid as having
187 // been provided.
188 $titleProvided = true;
189 }
190 }
191 $wgTitle = $titleObj;
192 if ( $titleObj->canExist() ) {
193 $pageObj = WikiPage::factory( $titleObj );
194 } else {
195 // Do like MediaWiki::initializeArticle()
196 $article = Article::newFromTitle( $titleObj, $this->getContext() );
197 $pageObj = $article->getPage();
198 }
199
200 list( $popts, $reset ) = $this->makeParserOptions( $pageObj, $params );
201 $textProvided = !is_null( $text );
202
203 if ( !$textProvided ) {
204 if ( $titleProvided && ( $prop || $params['generatexml'] ) ) {
205 if ( $revid !== null ) {
206 $this->addWarning( 'apiwarn-parse-revidwithouttext' );
207 } else {
208 $this->addWarning( 'apiwarn-parse-titlewithouttext' );
209 }
210 }
211 // Prevent warning from ContentHandler::makeContent()
212 $text = '';
213 }
214
215 // If we are parsing text, do not use the content model of the default
216 // API title, but default to wikitext to keep BC.
217 if ( $textProvided && !$titleProvided && is_null( $model ) ) {
218 $model = CONTENT_MODEL_WIKITEXT;
219 $this->addWarning( [ 'apiwarn-parse-nocontentmodel', $model ] );
220 }
221
222 try {
223 $this->content = ContentHandler::makeContent( $text, $titleObj, $model, $format );
224 } catch ( MWContentSerializationException $ex ) {
225 $this->dieWithException( $ex, [
226 'wrap' => ApiMessage::create( 'apierror-contentserializationexception', 'parseerror' )
227 ] );
228 }
229
230 if ( $this->section !== false ) {
231 if ( $this->section === 'new' ) {
232 // Insert the section title above the content.
233 if ( !is_null( $params['sectiontitle'] ) && $params['sectiontitle'] !== '' ) {
234 $this->content = $this->content->addSectionHeader( $params['sectiontitle'] );
235 }
236 } else {
237 $this->content = $this->getSectionContent( $this->content, $titleObj->getPrefixedText() );
238 }
239 }
240
241 if ( $params['pst'] || $params['onlypst'] ) {
242 $this->pstContent = $this->content->preSaveTransform( $titleObj, $this->getUser(), $popts );
243 }
244 if ( $params['onlypst'] ) {
245 // Build a result and bail out
246 $result_array = [];
247 $result_array['text'] = $this->pstContent->serialize( $format );
248 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'text';
249 if ( isset( $prop['wikitext'] ) ) {
250 $result_array['wikitext'] = $this->content->serialize( $format );
251 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'wikitext';
252 }
253 if ( !is_null( $params['summary'] ) ||
254 ( !is_null( $params['sectiontitle'] ) && $this->section === 'new' )
255 ) {
256 $result_array['parsedsummary'] = $this->formatSummary( $titleObj, $params );
257 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'parsedsummary';
258 }
259
260 $result->addValue( null, $this->getModuleName(), $result_array );
261
262 return;
263 }
264
265 // Not cached (save or load)
266 if ( $params['pst'] ) {
267 $p_result = $this->pstContent->getParserOutput( $titleObj, $revid, $popts );
268 } else {
269 $p_result = $this->content->getParserOutput( $titleObj, $revid, $popts );
270 }
271 }
272
273 $result_array = [];
274
275 $result_array['title'] = $titleObj->getPrefixedText();
276 $result_array['pageid'] = $pageid ?: $pageObj->getId();
277 if ( $this->contentIsDeleted ) {
278 $result_array['textdeleted'] = true;
279 }
280 if ( $this->contentIsSuppressed ) {
281 $result_array['textsuppressed'] = true;
282 }
283
284 if ( isset( $params['useskin'] ) ) {
285 $factory = MediaWikiServices::getInstance()->getSkinFactory();
286 $skin = $factory->makeSkin( Skin::normalizeKey( $params['useskin'] ) );
287 } else {
288 $skin = null;
289 }
290
291 $outputPage = null;
292 if ( $skin || isset( $prop['headhtml'] ) || isset( $prop['categorieshtml'] ) ) {
293 // Enabling the skin via 'useskin', 'headhtml', or 'categorieshtml'
294 // gets OutputPage and Skin involved, which (among others) applies
295 // these hooks:
296 // - ParserOutputHooks
297 // - Hook: LanguageLinks
298 // - Hook: OutputPageParserOutput
299 // - Hook: OutputPageMakeCategoryLinks
300 $context = new DerivativeContext( $this->getContext() );
301 $context->setTitle( $titleObj );
302 $context->setWikiPage( $pageObj );
303
304 if ( $skin ) {
305 // Use the skin specified by 'useskin'
306 $context->setSkin( $skin );
307 // Context clones the skin, refetch to stay in sync. (T166022)
308 $skin = $context->getSkin();
309 } else {
310 // Make sure the context's skin refers to the context. Without this,
311 // $outputPage->getSkin()->getOutput() !== $outputPage which
312 // confuses some of the output.
313 $context->setSkin( $context->getSkin() );
314 }
315
316 $outputPage = new OutputPage( $context );
317 $outputPage->addParserOutputMetadata( $p_result );
318 if ( $this->content ) {
319 $outputPage->addContentOverride( $titleObj, $this->content );
320 }
321 $context->setOutput( $outputPage );
322
323 if ( $skin ) {
324 // Based on OutputPage::headElement()
325 $skin->setupSkinUserCss( $outputPage );
326 // Based on OutputPage::output()
327 $outputPage->loadSkinModules( $skin );
328 }
329
330 Hooks::run( 'ApiParseMakeOutputPage', [ $this, $outputPage ] );
331 }
332
333 if ( !is_null( $oldid ) ) {
334 $result_array['revid'] = (int)$oldid;
335 }
336
337 if ( $params['redirects'] && !is_null( $redirValues ) ) {
338 $result_array['redirects'] = $redirValues;
339 }
340
341 if ( isset( $prop['text'] ) ) {
342 $result_array['text'] = $p_result->getText( [
343 'allowTOC' => !$params['disabletoc'],
344 'enableSectionEditLinks' => !$params['disableeditsection'],
345 'wrapperDivClass' => $params['wrapoutputclass'],
346 'deduplicateStyles' => !$params['disablestylededuplication'],
347 ] );
348 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'text';
349 }
350
351 if ( !is_null( $params['summary'] ) ||
352 ( !is_null( $params['sectiontitle'] ) && $this->section === 'new' )
353 ) {
354 $result_array['parsedsummary'] = $this->formatSummary( $titleObj, $params );
355 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'parsedsummary';
356 }
357
358 if ( isset( $prop['langlinks'] ) ) {
359 if ( $skin ) {
360 $langlinks = $outputPage->getLanguageLinks();
361 } else {
362 $langlinks = $p_result->getLanguageLinks();
363 // The deprecated 'effectivelanglinks' option depredates OutputPage
364 // support via 'useskin'. If not already applied, then run just this
365 // one hook of OutputPage::addParserOutputMetadata here.
366 if ( $params['effectivelanglinks'] ) {
367 $linkFlags = [];
368 Hooks::run( 'LanguageLinks', [ $titleObj, &$langlinks, &$linkFlags ] );
369 }
370 }
371
372 $result_array['langlinks'] = $this->formatLangLinks( $langlinks );
373 }
374 if ( isset( $prop['categories'] ) ) {
375 $result_array['categories'] = $this->formatCategoryLinks( $p_result->getCategories() );
376 }
377 if ( isset( $prop['categorieshtml'] ) ) {
378 $result_array['categorieshtml'] = $outputPage->getSkin()->getCategories();
379 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'categorieshtml';
380 }
381 if ( isset( $prop['links'] ) ) {
382 $result_array['links'] = $this->formatLinks( $p_result->getLinks() );
383 }
384 if ( isset( $prop['templates'] ) ) {
385 $result_array['templates'] = $this->formatLinks( $p_result->getTemplates() );
386 }
387 if ( isset( $prop['images'] ) ) {
388 $result_array['images'] = array_keys( $p_result->getImages() );
389 }
390 if ( isset( $prop['externallinks'] ) ) {
391 $result_array['externallinks'] = array_keys( $p_result->getExternalLinks() );
392 }
393 if ( isset( $prop['sections'] ) ) {
394 $result_array['sections'] = $p_result->getSections();
395 }
396 if ( isset( $prop['parsewarnings'] ) ) {
397 $result_array['parsewarnings'] = $p_result->getWarnings();
398 }
399
400 if ( isset( $prop['displaytitle'] ) ) {
401 $result_array['displaytitle'] = $p_result->getDisplayTitle() !== false
402 ? $p_result->getDisplayTitle() : $titleObj->getPrefixedText();
403 }
404
405 if ( isset( $prop['headitems'] ) ) {
406 if ( $skin ) {
407 $result_array['headitems'] = $this->formatHeadItems( $outputPage->getHeadItemsArray() );
408 } else {
409 $result_array['headitems'] = $this->formatHeadItems( $p_result->getHeadItems() );
410 }
411 }
412
413 if ( isset( $prop['headhtml'] ) ) {
414 $result_array['headhtml'] = $outputPage->headElement( $context->getSkin() );
415 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'headhtml';
416 }
417
418 if ( isset( $prop['modules'] ) ) {
419 if ( $skin ) {
420 $result_array['modules'] = $outputPage->getModules();
421 // Deprecated since 1.32 (T188689)
422 $result_array['modulescripts'] = [];
423 $result_array['modulestyles'] = $outputPage->getModuleStyles();
424 } else {
425 $result_array['modules'] = array_values( array_unique( $p_result->getModules() ) );
426 // Deprecated since 1.32 (T188689)
427 $result_array['modulescripts'] = [];
428 $result_array['modulestyles'] = array_values( array_unique( $p_result->getModuleStyles() ) );
429 }
430 }
431
432 if ( isset( $prop['jsconfigvars'] ) ) {
433 $jsconfigvars = $skin ? $outputPage->getJsConfigVars() : $p_result->getJsConfigVars();
434 $result_array['jsconfigvars'] = ApiResult::addMetadataToResultVars( $jsconfigvars );
435 }
436
437 if ( isset( $prop['encodedjsconfigvars'] ) ) {
438 $jsconfigvars = $skin ? $outputPage->getJsConfigVars() : $p_result->getJsConfigVars();
439 $result_array['encodedjsconfigvars'] = FormatJson::encode(
440 $jsconfigvars,
441 false,
442 FormatJson::ALL_OK
443 );
444 $result_array[ApiResult::META_SUBELEMENTS][] = 'encodedjsconfigvars';
445 }
446
447 if ( isset( $prop['modules'] ) &&
448 !isset( $prop['jsconfigvars'] ) && !isset( $prop['encodedjsconfigvars'] ) ) {
449 $this->addWarning( 'apiwarn-moduleswithoutvars' );
450 }
451
452 if ( isset( $prop['indicators'] ) ) {
453 if ( $skin ) {
454 $result_array['indicators'] = (array)$outputPage->getIndicators();
455 } else {
456 $result_array['indicators'] = (array)$p_result->getIndicators();
457 }
458 ApiResult::setArrayType( $result_array['indicators'], 'BCkvp', 'name' );
459 }
460
461 if ( isset( $prop['iwlinks'] ) ) {
462 $result_array['iwlinks'] = $this->formatIWLinks( $p_result->getInterwikiLinks() );
463 }
464
465 if ( isset( $prop['wikitext'] ) ) {
466 $result_array['wikitext'] = $this->content->serialize( $format );
467 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'wikitext';
468 if ( !is_null( $this->pstContent ) ) {
469 $result_array['psttext'] = $this->pstContent->serialize( $format );
470 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'psttext';
471 }
472 }
473 if ( isset( $prop['properties'] ) ) {
474 $result_array['properties'] = (array)$p_result->getProperties();
475 ApiResult::setArrayType( $result_array['properties'], 'BCkvp', 'name' );
476 }
477
478 if ( isset( $prop['limitreportdata'] ) ) {
479 $result_array['limitreportdata'] =
480 $this->formatLimitReportData( $p_result->getLimitReportData() );
481 }
482 if ( isset( $prop['limitreporthtml'] ) ) {
483 $result_array['limitreporthtml'] = EditPage::getPreviewLimitReport( $p_result );
484 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'limitreporthtml';
485 }
486
487 if ( isset( $prop['parsetree'] ) || $params['generatexml'] ) {
488 if ( $this->content->getModel() != CONTENT_MODEL_WIKITEXT ) {
489 $this->dieWithError( 'apierror-parsetree-notwikitext', 'notwikitext' );
490 }
491
492 $parser = MediaWikiServices::getInstance()->getParser();
493 $parser->startExternalParse( $titleObj, $popts, Parser::OT_PREPROCESS );
494 // @phan-suppress-next-line PhanUndeclaredMethod
495 $xml = $parser->preprocessToDom( $this->content->getText() )->__toString();
496 $result_array['parsetree'] = $xml;
497 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'parsetree';
498 }
499
500 $result_mapping = [
501 'redirects' => 'r',
502 'langlinks' => 'll',
503 'categories' => 'cl',
504 'links' => 'pl',
505 'templates' => 'tl',
506 'images' => 'img',
507 'externallinks' => 'el',
508 'iwlinks' => 'iw',
509 'sections' => 's',
510 'headitems' => 'hi',
511 'modules' => 'm',
512 'indicators' => 'ind',
513 'modulescripts' => 'm',
514 'modulestyles' => 'm',
515 'properties' => 'pp',
516 'limitreportdata' => 'lr',
517 'parsewarnings' => 'pw'
518 ];
519 $this->setIndexedTagNames( $result_array, $result_mapping );
520 $result->addValue( null, $this->getModuleName(), $result_array );
521 }
522
531 protected function makeParserOptions( WikiPage $pageObj, array $params ) {
532 $popts = $pageObj->makeParserOptions( $this->getContext() );
533 $popts->enableLimitReport( !$params['disablepp'] && !$params['disablelimitreport'] );
534 $popts->setIsPreview( $params['preview'] || $params['sectionpreview'] );
535 $popts->setIsSectionPreview( $params['sectionpreview'] );
536 if ( $params['disabletidy'] ) {
537 $popts->setTidy( false );
538 }
539 if ( $params['wrapoutputclass'] !== '' ) {
540 $popts->setWrapOutputClass( $params['wrapoutputclass'] );
541 }
542
543 $reset = null;
544 $suppressCache = false;
545 Hooks::run( 'ApiMakeParserOptions',
546 [ $popts, $pageObj->getTitle(), $params, $this, &$reset, &$suppressCache ] );
547
548 // Force cache suppression when $popts aren't cacheable.
549 $suppressCache = $suppressCache || !$popts->isSafeToCache();
550
551 return [ $popts, $reset, $suppressCache ];
552 }
553
563 private function getParsedContent(
564 WikiPage $page, $popts, $suppressCache, $pageId, $rev, $getContent
565 ) {
566 $revId = $rev ? $rev->getId() : null;
567 $isDeleted = $rev && $rev->isDeleted( RevisionRecord::DELETED_TEXT );
568
569 if ( $getContent || $this->section !== false || $isDeleted ) {
570 if ( $rev ) {
571 $this->content = $rev->getContent( RevisionRecord::FOR_THIS_USER, $this->getUser() );
572 if ( !$this->content ) {
573 $this->dieWithError( [ 'apierror-missingcontent-revid', $revId ] );
574 }
575 } else {
576 $this->content = $page->getContent( RevisionRecord::FOR_THIS_USER, $this->getUser() );
577 if ( !$this->content ) {
578 $this->dieWithError( [ 'apierror-missingcontent-pageid', $page->getId() ] );
579 }
580 }
581 $this->contentIsDeleted = $isDeleted;
582 $this->contentIsSuppressed = $rev &&
583 $rev->isDeleted( RevisionRecord::DELETED_TEXT | RevisionRecord::DELETED_RESTRICTED );
584 }
585
586 if ( $this->section !== false ) {
587 $this->content = $this->getSectionContent(
588 $this->content,
589 $pageId === null ? $page->getTitle()->getPrefixedText() : $this->msg( 'pageid', $pageId )
590 );
591 return $this->content->getParserOutput( $page->getTitle(), $revId, $popts );
592 }
593
594 if ( $isDeleted ) {
595 // getParserOutput can't do revdeled revisions
596 $pout = $this->content->getParserOutput( $page->getTitle(), $revId, $popts );
597 } else {
598 // getParserOutput will save to Parser cache if able
599 $pout = $page->getParserOutput( $popts, $revId, $suppressCache );
600 }
601 if ( !$pout ) {
602 // @codeCoverageIgnoreStart
603 $this->dieWithError( [ 'apierror-nosuchrevid', $revId ?: $page->getLatest() ] );
604 // @codeCoverageIgnoreEnd
605 }
606
607 return $pout;
608 }
609
617 private function getSectionContent( Content $content, $what ) {
618 // Not cached (save or load)
619 $section = $content->getSection( $this->section );
620 if ( $section === false ) {
621 $this->dieWithError( [ 'apierror-nosuchsection-what', $this->section, $what ], 'nosuchsection' );
622 }
623 if ( $section === null ) {
624 $this->dieWithError( [ 'apierror-sectionsnotsupported-what', $what ], 'nosuchsection' );
625 $section = false;
626 }
627
628 return $section;
629 }
630
638 private function formatSummary( $title, $params ) {
639 $summary = $params['summary'] ?? '';
640 $sectionTitle = $params['sectiontitle'] ?? '';
641
642 if ( $this->section === 'new' && ( $sectionTitle === '' || $summary === '' ) ) {
643 if ( $sectionTitle !== '' ) {
644 $summary = $params['sectiontitle'];
645 }
646 if ( $summary !== '' ) {
647 $summary = wfMessage( 'newsectionsummary' )
648 ->rawParams( MediaWikiServices::getInstance()->getParser()
649 ->stripSectionName( $summary ) )
650 ->inContentLanguage()->text();
651 }
652 }
653 return Linker::formatComment( $summary, $title, $this->section === 'new' );
654 }
655
656 private function formatLangLinks( $links ) {
657 $result = [];
658 foreach ( $links as $link ) {
659 $entry = [];
660 $bits = explode( ':', $link, 2 );
661 $title = Title::newFromText( $link );
662
663 $entry['lang'] = $bits[0];
664 if ( $title ) {
665 $entry['url'] = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
666 // localised language name in 'uselang' language
667 $entry['langname'] = Language::fetchLanguageName(
668 $title->getInterwiki(),
669 $this->getLanguage()->getCode()
670 );
671
672 // native language name
673 $entry['autonym'] = Language::fetchLanguageName( $title->getInterwiki() );
674 }
675 ApiResult::setContentValue( $entry, 'title', $bits[1] );
676 $result[] = $entry;
677 }
678
679 return $result;
680 }
681
682 private function formatCategoryLinks( $links ) {
683 $result = [];
684
685 if ( !$links ) {
686 return $result;
687 }
688
689 // Fetch hiddencat property
690 $lb = new LinkBatch;
691 $lb->setArray( [ NS_CATEGORY => $links ] );
692 $db = $this->getDB();
693 $res = $db->select( [ 'page', 'page_props' ],
694 [ 'page_title', 'pp_propname' ],
695 $lb->constructSet( 'page', $db ),
696 __METHOD__,
697 [],
698 [ 'page_props' => [
699 'LEFT JOIN', [ 'pp_propname' => 'hiddencat', 'pp_page = page_id' ]
700 ] ]
701 );
702 $hiddencats = [];
703 foreach ( $res as $row ) {
704 $hiddencats[$row->page_title] = isset( $row->pp_propname );
705 }
706
707 $linkCache = MediaWikiServices::getInstance()->getLinkCache();
708
709 foreach ( $links as $link => $sortkey ) {
710 $entry = [];
711 $entry['sortkey'] = $sortkey;
712 // array keys will cast numeric category names to ints, so cast back to string
713 ApiResult::setContentValue( $entry, 'category', (string)$link );
714 if ( !isset( $hiddencats[$link] ) ) {
715 $entry['missing'] = true;
716
717 // We already know the link doesn't exist in the database, so
718 // tell LinkCache that before calling $title->isKnown().
719 $title = Title::makeTitle( NS_CATEGORY, $link );
720 $linkCache->addBadLinkObj( $title );
721 if ( $title->isKnown() ) {
722 $entry['known'] = true;
723 }
724 } elseif ( $hiddencats[$link] ) {
725 $entry['hidden'] = true;
726 }
727 $result[] = $entry;
728 }
729
730 return $result;
731 }
732
733 private function formatLinks( $links ) {
734 $result = [];
735 foreach ( $links as $ns => $nslinks ) {
736 foreach ( $nslinks as $title => $id ) {
737 $entry = [];
738 $entry['ns'] = $ns;
739 ApiResult::setContentValue( $entry, 'title', Title::makeTitle( $ns, $title )->getFullText() );
740 $entry['exists'] = $id != 0;
741 $result[] = $entry;
742 }
743 }
744
745 return $result;
746 }
747
748 private function formatIWLinks( $iw ) {
749 $result = [];
750 foreach ( $iw as $prefix => $titles ) {
751 foreach ( array_keys( $titles ) as $title ) {
752 $entry = [];
753 $entry['prefix'] = $prefix;
754
755 $title = Title::newFromText( "{$prefix}:{$title}" );
756 if ( $title ) {
757 $entry['url'] = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
758 }
759
760 ApiResult::setContentValue( $entry, 'title', $title->getFullText() );
761 $result[] = $entry;
762 }
763 }
764
765 return $result;
766 }
767
768 private function formatHeadItems( $headItems ) {
769 $result = [];
770 foreach ( $headItems as $tag => $content ) {
771 $entry = [];
772 $entry['tag'] = $tag;
773 ApiResult::setContentValue( $entry, 'content', $content );
774 $result[] = $entry;
775 }
776
777 return $result;
778 }
779
780 private function formatLimitReportData( $limitReportData ) {
781 $result = [];
782
783 foreach ( $limitReportData as $name => $value ) {
784 $entry = [];
785 $entry['name'] = $name;
786 if ( !is_array( $value ) ) {
787 $value = [ $value ];
788 }
789 ApiResult::setIndexedTagNameRecursive( $value, 'param' );
790 $entry = array_merge( $entry, $value );
791 $result[] = $entry;
792 }
793
794 return $result;
795 }
796
797 private function setIndexedTagNames( &$array, $mapping ) {
798 foreach ( $mapping as $key => $name ) {
799 if ( isset( $array[$key] ) ) {
800 ApiResult::setIndexedTagName( $array[$key], $name );
801 }
802 }
803 }
804
805 public function getAllowedParams() {
806 return [
807 'title' => null,
808 'text' => [
809 ApiBase::PARAM_TYPE => 'text',
810 ],
811 'revid' => [
812 ApiBase::PARAM_TYPE => 'integer',
813 ],
814 'summary' => null,
815 'page' => null,
816 'pageid' => [
817 ApiBase::PARAM_TYPE => 'integer',
818 ],
819 'redirects' => false,
820 'oldid' => [
821 ApiBase::PARAM_TYPE => 'integer',
822 ],
823 'prop' => [
824 ApiBase::PARAM_DFLT => 'text|langlinks|categories|links|templates|' .
825 'images|externallinks|sections|revid|displaytitle|iwlinks|' .
826 'properties|parsewarnings',
829 'text',
830 'langlinks',
831 'categories',
832 'categorieshtml',
833 'links',
834 'templates',
835 'images',
836 'externallinks',
837 'sections',
838 'revid',
839 'displaytitle',
840 'headhtml',
841 'modules',
842 'jsconfigvars',
843 'encodedjsconfigvars',
844 'indicators',
845 'iwlinks',
846 'wikitext',
847 'properties',
848 'limitreportdata',
849 'limitreporthtml',
850 'parsetree',
851 'parsewarnings',
852 'headitems',
853 ],
855 'parsetree' => [ 'apihelp-parse-paramvalue-prop-parsetree', CONTENT_MODEL_WIKITEXT ],
856 ],
858 'headitems' => 'apiwarn-deprecation-parse-headitems',
859 ],
860 ],
861 'wrapoutputclass' => 'mw-parser-output',
862 'pst' => false,
863 'onlypst' => false,
864 'effectivelanglinks' => [
865 ApiBase::PARAM_DFLT => false,
867 ],
868 'section' => null,
869 'sectiontitle' => [
870 ApiBase::PARAM_TYPE => 'string',
871 ],
872 'disablepp' => [
873 ApiBase::PARAM_DFLT => false,
875 ],
876 'disablelimitreport' => false,
877 'disableeditsection' => false,
878 'disabletidy' => [
879 ApiBase::PARAM_DFLT => false,
880 ApiBase::PARAM_DEPRECATED => true, // Since 1.32
881 ],
882 'disablestylededuplication' => false,
883 'generatexml' => [
884 ApiBase::PARAM_DFLT => false,
886 'apihelp-parse-param-generatexml', CONTENT_MODEL_WIKITEXT
887 ],
889 ],
890 'preview' => false,
891 'sectionpreview' => false,
892 'disabletoc' => false,
893 'useskin' => [
894 ApiBase::PARAM_TYPE => array_keys( Skin::getAllowedSkins() ),
895 ],
896 'contentformat' => [
897 ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
898 ],
899 'contentmodel' => [
900 ApiBase::PARAM_TYPE => ContentHandler::getContentModels(),
901 ]
902 ];
903 }
904
905 protected function getExamplesMessages() {
906 return [
907 'action=parse&page=Project:Sandbox'
908 => 'apihelp-parse-example-page',
909 'action=parse&text={{Project:Sandbox}}&contentmodel=wikitext'
910 => 'apihelp-parse-example-text',
911 'action=parse&text={{PAGENAME}}&title=Test'
912 => 'apihelp-parse-example-texttitle',
913 'action=parse&summary=Some+[[link]]&prop='
914 => 'apihelp-parse-example-summary',
915 ];
916 }
917
918 public function getHelpUrls() {
919 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Parsing_wikitext#parse';
920 }
921}
wfExpandUrl( $url, $defaultProto=PROTO_CURRENT)
Expand a potentially local URL to a fully-qualified URL.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
if(! $wgRequest->checkUrlExtension()) if(isset( $_SERVER['PATH_INFO']) && $_SERVER['PATH_INFO'] !='') $wgTitle
Definition api.php:58
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:42
const PARAM_DEPRECATED
(boolean) Is the parameter deprecated (will show a warning)?
Definition ApiBase.php:112
getDB()
Gets a default replica DB connection object.
Definition ApiBase.php:668
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
Definition ApiBase.php:2014
checkTitleUserPermissions(LinkTarget $linkTarget, $actions, $options=[])
Helper function for permission-denied errors.
Definition ApiBase.php:2156
const PARAM_DEPRECATED_VALUES
(array) When PARAM_TYPE is an array, this indicates which of the values are deprecated.
Definition ApiBase.php:209
getMain()
Get the main module.
Definition ApiBase.php:536
const PARAM_TYPE
(string|string[]) Either an array of allowed value strings, or a string type as described below.
Definition ApiBase.php:94
const PARAM_DFLT
(null|boolean|integer|string) Default value of the parameter.
Definition ApiBase.php:55
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, this is an array mapping those values to $msg...
Definition ApiBase.php:164
getResult()
Get the result object.
Definition ApiBase.php:640
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:761
requireMaxOneParameter( $params, $required)
Die if more than one of a certain set of parameters is set and not false.
Definition ApiBase.php:931
dieWithException( $exception, array $options=[])
Abort execution with an error derived from an exception.
Definition ApiBase.php:2026
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition ApiBase.php:131
addWarning( $msg, $code=null, $data=null)
Add a warning for this module.
Definition ApiBase.php:1933
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:520
getTitleOrPageId( $params, $load=false)
Get a WikiPage object from a title or pageid param, if possible.
Definition ApiBase.php:1034
const PARAM_ISMULTI
(boolean) Accept multiple pipe-separated values for this parameter (e.g.
Definition ApiBase.php:58
This is the main API class, used for both external and internal processing.
Definition ApiMain.php:41
static create( $msg, $code=null, array $data=null)
Create an IApiMessage for the message.
This class contains a list of pages that the client has requested.
formatSummary( $title, $params)
This mimicks the behavior of EditPage in formatting a summary.
Definition ApiParse.php:638
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
Definition ApiParse.php:805
formatLinks( $links)
Definition ApiParse.php:733
getParsedContent(WikiPage $page, $popts, $suppressCache, $pageId, $rev, $getContent)
Definition ApiParse.php:563
formatHeadItems( $headItems)
Definition ApiParse.php:768
formatIWLinks( $iw)
Definition ApiParse.php:748
formatLangLinks( $links)
Definition ApiParse.php:656
getExamplesMessages()
Returns usage examples for this module.
Definition ApiParse.php:905
bool $contentIsDeleted
Definition ApiParse.php:41
formatCategoryLinks( $links)
Definition ApiParse.php:682
formatLimitReportData( $limitReportData)
Definition ApiParse.php:780
makeParserOptions(WikiPage $pageObj, array $params)
Constructs a ParserOptions object.
Definition ApiParse.php:531
bool $contentIsSuppressed
Definition ApiParse.php:41
getSectionContent(Content $content, $what)
Extract the requested section from the given Content.
Definition ApiParse.php:617
getHelpUrls()
Return links to more detailed help pages about the module.
Definition ApiParse.php:918
setIndexedTagNames(&$array, $mapping)
Definition ApiParse.php:797
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
Definition ApiParse.php:43
static setArrayType(array &$arr, $type, $kvpKeyName=null)
Set the array data type.
static addMetadataToResultVars( $vars, $forceHash=true)
Add the correct metadata to an array of vars we want to export through the API.
const META_SUBELEMENTS
Key for the 'subelements' metadata item.
Definition ApiResult.php:78
const META_BC_SUBELEMENTS
Key for the 'BC subelements' metadata item.
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
static setContentValue(array &$arr, $name, $value, $flags=0)
Add an output value to the array by name and mark as META_CONTENT.
static setIndexedTagNameRecursive(array &$arr, $tag)
Set indexed tag name on $arr and all subarrays.
static newFromTitle( $title, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition Article.php:160
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
IContextSource $context
getContext()
Get the base IContextSource object.
An IContextSource implementation which will inherit context from another source but allow individual ...
static getPreviewLimitReport(ParserOutput $output=null)
Get the Limit report for page previews.
WebRequest clone which takes values from a provided array.
Class representing a list of titles The execute() method checks them all for existence and adds them ...
Definition LinkBatch.php:34
setArray( $array)
Set the link list to a given 2-d array First key is the namespace, second is the DB key,...
static formatComment( $comment, $title=null, $local=false, $wikiId=null)
This function is called by all recent changes variants, by the page history, and by the user contribu...
Definition Linker.php:1165
Exception representing a failure to serialize or unserialize a content object.
MediaWikiServices is the service locator for the application scope of MediaWiki.
Page revision base class.
This is one of the Core classes and should be read at least once by any new developers.
static newFromId( $id, $flags=0)
Load a page revision from a given revision ID number.
Definition Revision.php:119
Class representing a MediaWiki article and history.
Definition WikiPage.php:47
getContent( $audience=RevisionRecord::FOR_PUBLIC, User $user=null)
Get the content of the current revision.
Definition WikiPage.php:820
getLatest()
Get the page_latest field.
Definition WikiPage.php:714
makeParserOptions( $context)
Get parser options suitable for rendering the primary article wikitext.
getParserOutput(ParserOptions $parserOptions, $oldid=null, $forceParse=false)
Get a ParserOutput for the given ParserOptions and revision ID.
getTitle()
Get the title object of the article.
Definition WikiPage.php:298
const PROTO_CURRENT
Definition Defines.php:211
const CONTENT_MODEL_WIKITEXT
Definition Defines.php:224
const NS_CATEGORY
Definition Defines.php:83
Base interface for content objects.
Definition Content.php:34