Merge "Don't check namespace in SpecialWantedtemplates"
[lhc/web/wiklou.git] / includes / api / ApiParse.php
1 <?php
2 /**
3 * Created on Dec 01, 2007
4 *
5 * Copyright © 2007 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * http://www.gnu.org/copyleft/gpl.html
21 *
22 * @file
23 */
24
25 /**
26 * @ingroup API
27 */
28 class ApiParse extends ApiBase {
29
30 /** @var string $section */
31 private $section = null;
32
33 /** @var Content $content */
34 private $content = null;
35
36 /** @var Content $pstContent */
37 private $pstContent = null;
38
39 public function execute() {
40 // The data is hot but user-dependent, like page views, so we set vary cookies
41 $this->getMain()->setCacheMode( 'anon-public-user-private' );
42
43 // Get parameters
44 $params = $this->extractRequestParams();
45 $text = $params['text'];
46 $title = $params['title'];
47 if ( $title === null ) {
48 $titleProvided = false;
49 // A title is needed for parsing, so arbitrarily choose one
50 $title = 'API';
51 } else {
52 $titleProvided = true;
53 }
54
55 $page = $params['page'];
56 $pageid = $params['pageid'];
57 $oldid = $params['oldid'];
58
59 $model = $params['contentmodel'];
60 $format = $params['contentformat'];
61
62 if ( !is_null( $page ) && ( !is_null( $text ) || $titleProvided ) ) {
63 $this->dieUsage(
64 'The page parameter cannot be used together with the text and title parameters',
65 'params'
66 );
67 }
68
69 $prop = array_flip( $params['prop'] );
70
71 if ( isset( $params['section'] ) ) {
72 $this->section = $params['section'];
73 if ( !preg_match( '/^((T-)?\d+|new)$/', $this->section ) ) {
74 $this->dieUsage(
75 "The section parameter must be a valid section id or 'new'", "invalidsection"
76 );
77 }
78 } else {
79 $this->section = false;
80 }
81
82 // The parser needs $wgTitle to be set, apparently the
83 // $title parameter in Parser::parse isn't enough *sigh*
84 // TODO: Does this still need $wgTitle?
85 global $wgParser, $wgTitle;
86
87 $redirValues = null;
88
89 // Return result
90 $result = $this->getResult();
91
92 if ( !is_null( $oldid ) || !is_null( $pageid ) || !is_null( $page ) ) {
93 if ( $this->section === 'new' ) {
94 $this->dieUsage(
95 'section=new cannot be combined with oldid, pageid or page parameters. ' .
96 'Please use text', 'params'
97 );
98 }
99 if ( !is_null( $oldid ) ) {
100 // Don't use the parser cache
101 $rev = Revision::newFromId( $oldid );
102 if ( !$rev ) {
103 $this->dieUsage( "There is no revision ID $oldid", 'missingrev' );
104 }
105 if ( !$rev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) {
106 $this->dieUsage( "You don't have permission to view deleted revisions", 'permissiondenied' );
107 }
108
109 $titleObj = $rev->getTitle();
110 $wgTitle = $titleObj;
111 $pageObj = WikiPage::factory( $titleObj );
112 $popts = $this->makeParserOptions( $pageObj, $params );
113
114 // If for some reason the "oldid" is actually the current revision, it may be cached
115 // Deliberately comparing $pageObj->getLatest() with $rev->getId(), rather than
116 // checking $rev->isCurrent(), because $pageObj is what actually ends up being used,
117 // and if its ->getLatest() is outdated, $rev->isCurrent() won't tell us that.
118 if ( $rev->getId() == $pageObj->getLatest() ) {
119 // May get from/save to parser cache
120 $p_result = $this->getParsedContent( $pageObj, $popts,
121 $pageid, isset( $prop['wikitext'] ) );
122 } else { // This is an old revision, so get the text differently
123 $this->content = $rev->getContent( Revision::FOR_THIS_USER, $this->getUser() );
124
125 if ( $this->section !== false ) {
126 $this->content = $this->getSectionContent( $this->content, 'r' . $rev->getId() );
127 }
128
129 // Should we save old revision parses to the parser cache?
130 $p_result = $this->content->getParserOutput( $titleObj, $rev->getId(), $popts );
131 }
132 } else { // Not $oldid, but $pageid or $page
133 if ( $params['redirects'] ) {
134 $reqParams = array(
135 'redirects' => '',
136 );
137 if ( !is_null( $pageid ) ) {
138 $reqParams['pageids'] = $pageid;
139 } else { // $page
140 $reqParams['titles'] = $page;
141 }
142 $req = new FauxRequest( $reqParams );
143 $main = new ApiMain( $req );
144 $pageSet = new ApiPageSet( $main );
145 $pageSet->execute();
146 $redirValues = $pageSet->getRedirectTitlesAsResult( $this->getResult() );
147
148 $to = $page;
149 foreach ( $pageSet->getRedirectTitles() as $title ) {
150 $to = $title->getFullText();
151 }
152 $pageParams = array( 'title' => $to );
153 } elseif ( !is_null( $pageid ) ) {
154 $pageParams = array( 'pageid' => $pageid );
155 } else { // $page
156 $pageParams = array( 'title' => $page );
157 }
158
159 $pageObj = $this->getTitleOrPageId( $pageParams, 'fromdb' );
160 $titleObj = $pageObj->getTitle();
161 if ( !$titleObj || !$titleObj->exists() ) {
162 $this->dieUsage( "The page you specified doesn't exist", 'missingtitle' );
163 }
164 $wgTitle = $titleObj;
165
166 if ( isset( $prop['revid'] ) ) {
167 $oldid = $pageObj->getLatest();
168 }
169
170 $popts = $this->makeParserOptions( $pageObj, $params );
171
172 // Don't pollute the parser cache when setting options that aren't
173 // in ParserOptions::optionsHash()
174 /// @todo: This should be handled closer to the actual cache instead of here, see T110269
175 $suppressCache =
176 $params['disablepp'] ||
177 $params['disablelimitreport'] ||
178 $params['preview'] ||
179 $params['sectionpreview'] ||
180 $params['disabletidy'];
181
182 if ( $suppressCache ) {
183 $this->content = $this->getContent( $pageObj, $pageid );
184 $p_result = $this->content->getParserOutput( $titleObj, null, $popts );
185 } else {
186 // Potentially cached
187 $p_result = $this->getParsedContent( $pageObj, $popts, $pageid,
188 isset( $prop['wikitext'] ) );
189 }
190 }
191 } else { // Not $oldid, $pageid, $page. Hence based on $text
192 $titleObj = Title::newFromText( $title );
193 if ( !$titleObj || $titleObj->isExternal() ) {
194 $this->dieUsageMsg( array( 'invalidtitle', $title ) );
195 }
196 $wgTitle = $titleObj;
197 if ( $titleObj->canExist() ) {
198 $pageObj = WikiPage::factory( $titleObj );
199 } else {
200 // Do like MediaWiki::initializeArticle()
201 $article = Article::newFromTitle( $titleObj, $this->getContext() );
202 $pageObj = $article->getPage();
203 }
204
205 $popts = $this->makeParserOptions( $pageObj, $params );
206 $textProvided = !is_null( $text );
207
208 if ( !$textProvided ) {
209 if ( $titleProvided && ( $prop || $params['generatexml'] ) ) {
210 $this->setWarning(
211 "'title' used without 'text', and parsed page properties were requested " .
212 "(did you mean to use 'page' instead of 'title'?)"
213 );
214 }
215 // Prevent warning from ContentHandler::makeContent()
216 $text = '';
217 }
218
219 // If we are parsing text, do not use the content model of the default
220 // API title, but default to wikitext to keep BC.
221 if ( $textProvided && !$titleProvided && is_null( $model ) ) {
222 $model = CONTENT_MODEL_WIKITEXT;
223 $this->setWarning( "No 'title' or 'contentmodel' was given, assuming $model." );
224 }
225
226 try {
227 $this->content = ContentHandler::makeContent( $text, $titleObj, $model, $format );
228 } catch ( MWContentSerializationException $ex ) {
229 $this->dieUsage( $ex->getMessage(), 'parseerror' );
230 }
231
232 if ( $this->section !== false ) {
233 if ( $this->section === 'new' ) {
234 // Insert the section title above the content.
235 if ( !is_null( $params['sectiontitle'] ) && $params['sectiontitle'] !== '' ) {
236 $this->content = $this->content->addSectionHeader( $params['sectiontitle'] );
237 }
238 } else {
239 $this->content = $this->getSectionContent( $this->content, $titleObj->getPrefixedText() );
240 }
241 }
242
243 if ( $params['pst'] || $params['onlypst'] ) {
244 $this->pstContent = $this->content->preSaveTransform( $titleObj, $this->getUser(), $popts );
245 }
246 if ( $params['onlypst'] ) {
247 // Build a result and bail out
248 $result_array = array();
249 $result_array['text'] = $this->pstContent->serialize( $format );
250 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'text';
251 if ( isset( $prop['wikitext'] ) ) {
252 $result_array['wikitext'] = $this->content->serialize( $format );
253 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'wikitext';
254 }
255 if ( !is_null( $params['summary'] ) ||
256 ( !is_null( $params['sectiontitle'] ) && $this->section === 'new' )
257 ) {
258 $result_array['parsedsummary'] = $this->formatSummary( $titleObj, $params );
259 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'parsedsummary';
260 }
261
262 $result->addValue( null, $this->getModuleName(), $result_array );
263
264 return;
265 }
266
267 // Not cached (save or load)
268 if ( $params['pst'] ) {
269 $p_result = $this->pstContent->getParserOutput( $titleObj, null, $popts );
270 } else {
271 $p_result = $this->content->getParserOutput( $titleObj, null, $popts );
272 }
273 }
274
275 $result_array = array();
276
277 $result_array['title'] = $titleObj->getPrefixedText();
278 $result_array['pageid'] = $pageid ? $pageid : $pageObj->getId();
279
280 if ( !is_null( $oldid ) ) {
281 $result_array['revid'] = intval( $oldid );
282 }
283
284 if ( $params['redirects'] && !is_null( $redirValues ) ) {
285 $result_array['redirects'] = $redirValues;
286 }
287
288 if ( $params['disabletoc'] ) {
289 $p_result->setTOCEnabled( false );
290 }
291
292 if ( isset( $prop['text'] ) ) {
293 $result_array['text'] = $p_result->getText();
294 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'text';
295 }
296
297 if ( !is_null( $params['summary'] ) ||
298 ( !is_null( $params['sectiontitle'] ) && $this->section === 'new' )
299 ) {
300 $result_array['parsedsummary'] = $this->formatSummary( $titleObj, $params );
301 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'parsedsummary';
302 }
303
304 if ( isset( $prop['langlinks'] ) ) {
305 $langlinks = $p_result->getLanguageLinks();
306
307 if ( $params['effectivelanglinks'] ) {
308 // Link flags are ignored for now, but may in the future be
309 // included in the result.
310 $linkFlags = array();
311 Hooks::run( 'LanguageLinks', array( $titleObj, &$langlinks, &$linkFlags ) );
312 }
313 } else {
314 $langlinks = false;
315 }
316
317 if ( isset( $prop['langlinks'] ) ) {
318 $result_array['langlinks'] = $this->formatLangLinks( $langlinks );
319 }
320 if ( isset( $prop['categories'] ) ) {
321 $result_array['categories'] = $this->formatCategoryLinks( $p_result->getCategories() );
322 }
323 if ( isset( $prop['categorieshtml'] ) ) {
324 $result_array['categorieshtml'] = $this->categoriesHtml( $p_result->getCategories() );
325 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'categorieshtml';
326 }
327 if ( isset( $prop['links'] ) ) {
328 $result_array['links'] = $this->formatLinks( $p_result->getLinks() );
329 }
330 if ( isset( $prop['templates'] ) ) {
331 $result_array['templates'] = $this->formatLinks( $p_result->getTemplates() );
332 }
333 if ( isset( $prop['images'] ) ) {
334 $result_array['images'] = array_keys( $p_result->getImages() );
335 }
336 if ( isset( $prop['externallinks'] ) ) {
337 $result_array['externallinks'] = array_keys( $p_result->getExternalLinks() );
338 }
339 if ( isset( $prop['sections'] ) ) {
340 $result_array['sections'] = $p_result->getSections();
341 }
342
343 if ( isset( $prop['displaytitle'] ) ) {
344 $result_array['displaytitle'] = $p_result->getDisplayTitle() ?
345 $p_result->getDisplayTitle() :
346 $titleObj->getPrefixedText();
347 }
348
349 if ( isset( $prop['headitems'] ) || isset( $prop['headhtml'] ) ) {
350 $context = $this->getContext();
351 $context->setTitle( $titleObj );
352 $context->getOutput()->addParserOutputMetadata( $p_result );
353
354 if ( isset( $prop['headitems'] ) ) {
355 $headItems = $this->formatHeadItems( $p_result->getHeadItems() );
356
357 $css = $this->formatCss( $context->getOutput()->buildCssLinksArray() );
358
359 $scripts = array( $context->getOutput()->getHeadScripts() );
360
361 $result_array['headitems'] = array_merge( $headItems, $css, $scripts );
362 }
363
364 if ( isset( $prop['headhtml'] ) ) {
365 $result_array['headhtml'] = $context->getOutput()->headElement( $context->getSkin() );
366 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'headhtml';
367 }
368 }
369
370 if ( isset( $prop['modules'] ) ) {
371 $result_array['modules'] = array_values( array_unique( $p_result->getModules() ) );
372 $result_array['modulescripts'] = array_values( array_unique( $p_result->getModuleScripts() ) );
373 $result_array['modulestyles'] = array_values( array_unique( $p_result->getModuleStyles() ) );
374 // To be removed in 1.27
375 $result_array['modulemessages'] = array();
376 $this->setWarning( 'modulemessages is deprecated since MediaWiki 1.26' );
377 }
378
379 if ( isset( $prop['jsconfigvars'] ) ) {
380 $result_array['jsconfigvars'] =
381 ApiResult::addMetadataToResultVars( $p_result->getJsConfigVars() );
382 }
383
384 if ( isset( $prop['encodedjsconfigvars'] ) ) {
385 $result_array['encodedjsconfigvars'] = FormatJson::encode(
386 $p_result->getJsConfigVars(), false, FormatJson::ALL_OK
387 );
388 $result_array[ApiResult::META_SUBELEMENTS][] = 'encodedjsconfigvars';
389 }
390
391 if ( isset( $prop['modules'] ) &&
392 !isset( $prop['jsconfigvars'] ) && !isset( $prop['encodedjsconfigvars'] ) ) {
393 $this->setWarning( "Property 'modules' was set but not 'jsconfigvars' " .
394 "or 'encodedjsconfigvars'. Configuration variables are necessary " .
395 "for proper module usage." );
396 }
397
398 if ( isset( $prop['indicators'] ) ) {
399 $result_array['indicators'] = (array)$p_result->getIndicators();
400 ApiResult::setArrayType( $result_array['indicators'], 'BCkvp', 'name' );
401 }
402
403 if ( isset( $prop['iwlinks'] ) ) {
404 $result_array['iwlinks'] = $this->formatIWLinks( $p_result->getInterwikiLinks() );
405 }
406
407 if ( isset( $prop['wikitext'] ) ) {
408 $result_array['wikitext'] = $this->content->serialize( $format );
409 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'wikitext';
410 if ( !is_null( $this->pstContent ) ) {
411 $result_array['psttext'] = $this->pstContent->serialize( $format );
412 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'psttext';
413 }
414 }
415 if ( isset( $prop['properties'] ) ) {
416 $result_array['properties'] = (array)$p_result->getProperties();
417 ApiResult::setArrayType( $result_array['properties'], 'BCkvp', 'name' );
418 }
419
420 if ( isset( $prop['limitreportdata'] ) ) {
421 $result_array['limitreportdata'] =
422 $this->formatLimitReportData( $p_result->getLimitReportData() );
423 }
424 if ( isset( $prop['limitreporthtml'] ) ) {
425 $result_array['limitreporthtml'] = EditPage::getPreviewLimitReport( $p_result );
426 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'limitreporthtml';
427 }
428
429 if ( isset( $prop['parsetree'] ) || $params['generatexml'] ) {
430 if ( !isset( $prop['parsetree'] ) ) {
431 $this->logFeatureUsage( 'action=parse&generatexml' );
432 }
433 if ( $this->content->getModel() != CONTENT_MODEL_WIKITEXT ) {
434 $this->dieUsage( "parsetree is only supported for wikitext content", "notwikitext" );
435 }
436
437 $wgParser->startExternalParse( $titleObj, $popts, Parser::OT_PREPROCESS );
438 $dom = $wgParser->preprocessToDom( $this->content->getNativeData() );
439 if ( is_callable( array( $dom, 'saveXML' ) ) ) {
440 $xml = $dom->saveXML();
441 } else {
442 $xml = $dom->__toString();
443 }
444 $result_array['parsetree'] = $xml;
445 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'parsetree';
446 }
447
448 $result_mapping = array(
449 'redirects' => 'r',
450 'langlinks' => 'll',
451 'categories' => 'cl',
452 'links' => 'pl',
453 'templates' => 'tl',
454 'images' => 'img',
455 'externallinks' => 'el',
456 'iwlinks' => 'iw',
457 'sections' => 's',
458 'headitems' => 'hi',
459 'modules' => 'm',
460 'indicators' => 'ind',
461 'modulescripts' => 'm',
462 'modulestyles' => 'm',
463 'modulemessages' => 'm',
464 'properties' => 'pp',
465 'limitreportdata' => 'lr',
466 );
467 $this->setIndexedTagNames( $result_array, $result_mapping );
468 $result->addValue( null, $this->getModuleName(), $result_array );
469 }
470
471 /**
472 * Constructs a ParserOptions object
473 *
474 * @param WikiPage $pageObj
475 * @param array $params
476 *
477 * @return ParserOptions
478 */
479 protected function makeParserOptions( WikiPage $pageObj, array $params ) {
480
481 $popts = $pageObj->makeParserOptions( $this->getContext() );
482 $popts->enableLimitReport( !$params['disablepp'] && !$params['disablelimitreport'] );
483 $popts->setIsPreview( $params['preview'] || $params['sectionpreview'] );
484 $popts->setIsSectionPreview( $params['sectionpreview'] );
485 $popts->setEditSection( !$params['disableeditsection'] );
486 if ( $params['disabletidy'] ) {
487 $popts->setTidy( false );
488 }
489
490 return $popts;
491 }
492
493 /**
494 * @param WikiPage $page
495 * @param ParserOptions $popts
496 * @param int $pageId
497 * @param bool $getWikitext
498 * @return ParserOutput
499 */
500 private function getParsedContent( WikiPage $page, $popts, $pageId = null, $getWikitext = false ) {
501 $this->content = $this->getContent( $page, $pageId );
502
503 if ( $this->section !== false && $this->content !== null ) {
504 // Not cached (save or load)
505 return $this->content->getParserOutput( $page->getTitle(), null, $popts );
506 }
507
508 // Try the parser cache first
509 // getParserOutput will save to Parser cache if able
510 $pout = $page->getParserOutput( $popts );
511 if ( !$pout ) {
512 $this->dieUsage( "There is no revision ID {$page->getLatest()}", 'missingrev' );
513 }
514 if ( $getWikitext ) {
515 $this->content = $page->getContent( Revision::RAW );
516 }
517
518 return $pout;
519 }
520
521 /**
522 * Get the content for the given page and the requested section.
523 *
524 * @param WikiPage $page
525 * @param int $pageId
526 * @return Content
527 */
528 private function getContent( WikiPage $page, $pageId = null ) {
529 $content = $page->getContent( Revision::RAW ); //XXX: really raw?
530
531 if ( $this->section !== false && $content !== null ) {
532 $content = $this->getSectionContent(
533 $content,
534 !is_null( $pageId ) ? 'page id ' . $pageId : $page->getTitle()->getPrefixedText()
535 );
536 }
537 return $content;
538 }
539
540 /**
541 * Extract the requested section from the given Content
542 *
543 * @param Content $content
544 * @param string $what Identifies the content in error messages, e.g. page title.
545 * @return Content|bool
546 */
547 private function getSectionContent( Content $content, $what ) {
548 // Not cached (save or load)
549 $section = $content->getSection( $this->section );
550 if ( $section === false ) {
551 $this->dieUsage( "There is no section {$this->section} in " . $what, 'nosuchsection' );
552 }
553 if ( $section === null ) {
554 $this->dieUsage( "Sections are not supported by " . $what, 'nosuchsection' );
555 $section = false;
556 }
557
558 return $section;
559 }
560
561 /**
562 * This mimicks the behavior of EditPage in formatting a summary
563 *
564 * @param Title $title of the page being parsed
565 * @param Array $params the API parameters of the request
566 * @return Content|bool
567 */
568 private function formatSummary( $title, $params ) {
569 global $wgParser;
570 $summary = !is_null( $params['summary'] ) ? $params['summary'] : '';
571 $sectionTitle = !is_null( $params['sectiontitle'] ) ? $params['sectiontitle'] : '';
572
573 if ( $this->section === 'new' && ( $sectionTitle === '' || $summary === '' ) ) {
574 if ( $sectionTitle !== '' ) {
575 $summary = $params['sectiontitle'];
576 }
577 if ( $summary !== '' ) {
578 $summary = wfMessage( 'newsectionsummary' )
579 ->rawParams( $wgParser->stripSectionName( $summary ) )
580 ->inContentLanguage()->text();
581 }
582 }
583 return Linker::formatComment( $summary, $title, $this->section === 'new' );
584 }
585
586 private function formatLangLinks( $links ) {
587 $result = array();
588 foreach ( $links as $link ) {
589 $entry = array();
590 $bits = explode( ':', $link, 2 );
591 $title = Title::newFromText( $link );
592
593 $entry['lang'] = $bits[0];
594 if ( $title ) {
595 $entry['url'] = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
596 // localised language name in 'uselang' language
597 $entry['langname'] = Language::fetchLanguageName(
598 $title->getInterwiki(),
599 $this->getLanguage()->getCode()
600 );
601
602 // native language name
603 $entry['autonym'] = Language::fetchLanguageName( $title->getInterwiki() );
604 }
605 ApiResult::setContentValue( $entry, 'title', $bits[1] );
606 $result[] = $entry;
607 }
608
609 return $result;
610 }
611
612 private function formatCategoryLinks( $links ) {
613 $result = array();
614
615 if ( !$links ) {
616 return $result;
617 }
618
619 // Fetch hiddencat property
620 $lb = new LinkBatch;
621 $lb->setArray( array( NS_CATEGORY => $links ) );
622 $db = $this->getDB();
623 $res = $db->select( array( 'page', 'page_props' ),
624 array( 'page_title', 'pp_propname' ),
625 $lb->constructSet( 'page', $db ),
626 __METHOD__,
627 array(),
628 array( 'page_props' => array(
629 'LEFT JOIN', array( 'pp_propname' => 'hiddencat', 'pp_page = page_id' )
630 ) )
631 );
632 $hiddencats = array();
633 foreach ( $res as $row ) {
634 $hiddencats[$row->page_title] = isset( $row->pp_propname );
635 }
636
637 foreach ( $links as $link => $sortkey ) {
638 $entry = array();
639 $entry['sortkey'] = $sortkey;
640 ApiResult::setContentValue( $entry, 'category', $link );
641 if ( !isset( $hiddencats[$link] ) ) {
642 $entry['missing'] = true;
643 } elseif ( $hiddencats[$link] ) {
644 $entry['hidden'] = true;
645 }
646 $result[] = $entry;
647 }
648
649 return $result;
650 }
651
652 private function categoriesHtml( $categories ) {
653 $context = $this->getContext();
654 $context->getOutput()->addCategoryLinks( $categories );
655
656 return $context->getSkin()->getCategories();
657 }
658
659 private function formatLinks( $links ) {
660 $result = array();
661 foreach ( $links as $ns => $nslinks ) {
662 foreach ( $nslinks as $title => $id ) {
663 $entry = array();
664 $entry['ns'] = $ns;
665 ApiResult::setContentValue( $entry, 'title', Title::makeTitle( $ns, $title )->getFullText() );
666 $entry['exists'] = $id != 0;
667 $result[] = $entry;
668 }
669 }
670
671 return $result;
672 }
673
674 private function formatIWLinks( $iw ) {
675 $result = array();
676 foreach ( $iw as $prefix => $titles ) {
677 foreach ( array_keys( $titles ) as $title ) {
678 $entry = array();
679 $entry['prefix'] = $prefix;
680
681 $title = Title::newFromText( "{$prefix}:{$title}" );
682 if ( $title ) {
683 $entry['url'] = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
684 }
685
686 ApiResult::setContentValue( $entry, 'title', $title->getFullText() );
687 $result[] = $entry;
688 }
689 }
690
691 return $result;
692 }
693
694 private function formatHeadItems( $headItems ) {
695 $result = array();
696 foreach ( $headItems as $tag => $content ) {
697 $entry = array();
698 $entry['tag'] = $tag;
699 ApiResult::setContentValue( $entry, 'content', $content );
700 $result[] = $entry;
701 }
702
703 return $result;
704 }
705
706 private function formatCss( $css ) {
707 $result = array();
708 foreach ( $css as $file => $link ) {
709 $entry = array();
710 $entry['file'] = $file;
711 ApiResult::setContentValue( $entry, 'link', $link );
712 $result[] = $entry;
713 }
714
715 return $result;
716 }
717
718 private function formatLimitReportData( $limitReportData ) {
719 $result = array();
720 $apiResult = $this->getResult();
721
722 foreach ( $limitReportData as $name => $value ) {
723 $entry = array();
724 $entry['name'] = $name;
725 if ( !is_array( $value ) ) {
726 $value = array( $value );
727 }
728 ApiResult::setIndexedTagNameRecursive( $value, 'param' );
729 $entry = array_merge( $entry, $value );
730 $result[] = $entry;
731 }
732
733 return $result;
734 }
735
736 private function setIndexedTagNames( &$array, $mapping ) {
737 foreach ( $mapping as $key => $name ) {
738 if ( isset( $array[$key] ) ) {
739 ApiResult::setIndexedTagName( $array[$key], $name );
740 }
741 }
742 }
743
744 public function getAllowedParams() {
745 return array(
746 'title' => null,
747 'text' => array(
748 ApiBase::PARAM_TYPE => 'text',
749 ),
750 'summary' => null,
751 'page' => null,
752 'pageid' => array(
753 ApiBase::PARAM_TYPE => 'integer',
754 ),
755 'redirects' => false,
756 'oldid' => array(
757 ApiBase::PARAM_TYPE => 'integer',
758 ),
759 'prop' => array(
760 ApiBase::PARAM_DFLT => 'text|langlinks|categories|links|templates|' .
761 'images|externallinks|sections|revid|displaytitle|iwlinks|properties',
762 ApiBase::PARAM_ISMULTI => true,
763 ApiBase::PARAM_TYPE => array(
764 'text',
765 'langlinks',
766 'categories',
767 'categorieshtml',
768 'links',
769 'templates',
770 'images',
771 'externallinks',
772 'sections',
773 'revid',
774 'displaytitle',
775 'headitems',
776 'headhtml',
777 'modules',
778 'jsconfigvars',
779 'encodedjsconfigvars',
780 'indicators',
781 'iwlinks',
782 'wikitext',
783 'properties',
784 'limitreportdata',
785 'limitreporthtml',
786 'parsetree',
787 ),
788 ApiBase::PARAM_HELP_MSG_PER_VALUE => array(
789 'parsetree' => array( 'apihelp-parse-paramvalue-prop-parsetree', CONTENT_MODEL_WIKITEXT ),
790 ),
791 ),
792 'pst' => false,
793 'onlypst' => false,
794 'effectivelanglinks' => false,
795 'section' => null,
796 'sectiontitle' => array(
797 ApiBase::PARAM_TYPE => 'string',
798 ),
799 'disablepp' => array(
800 ApiBase::PARAM_DFLT => false,
801 ApiBase::PARAM_DEPRECATED => true,
802 ),
803 'disablelimitreport' => false,
804 'disableeditsection' => false,
805 'disabletidy' => false,
806 'generatexml' => array(
807 ApiBase::PARAM_DFLT => false,
808 ApiBase::PARAM_HELP_MSG => array(
809 'apihelp-parse-param-generatexml', CONTENT_MODEL_WIKITEXT
810 ),
811 ApiBase::PARAM_DEPRECATED => true,
812 ),
813 'preview' => false,
814 'sectionpreview' => false,
815 'disabletoc' => false,
816 'contentformat' => array(
817 ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
818 ),
819 'contentmodel' => array(
820 ApiBase::PARAM_TYPE => ContentHandler::getContentModels(),
821 )
822 );
823 }
824
825 protected function getExamplesMessages() {
826 return array(
827 'action=parse&page=Project:Sandbox'
828 => 'apihelp-parse-example-page',
829 'action=parse&text={{Project:Sandbox}}&contentmodel=wikitext'
830 => 'apihelp-parse-example-text',
831 'action=parse&text={{PAGENAME}}&title=Test'
832 => 'apihelp-parse-example-texttitle',
833 'action=parse&summary=Some+[[link]]&prop='
834 => 'apihelp-parse-example-summary',
835 );
836 }
837
838 public function getHelpUrls() {
839 return 'https://www.mediawiki.org/wiki/API:Parsing_wikitext#parse';
840 }
841 }