Merge "Fix typo in UploadFromUrlJob::initializeSessionData()"
[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 $suppressCache =
175 $params['disablepp'] ||
176 $params['disablelimitreport'] ||
177 $params['preview'] ||
178 $params['sectionpreview'] ||
179 $params['disabletidy'];
180
181 if ( $suppressCache ) {
182 $this->content = $this->getContent( $pageObj, $pageid );
183 $p_result = $this->content->getParserOutput( $titleObj, null, $popts );
184 } else {
185 // Potentially cached
186 $p_result = $this->getParsedContent( $pageObj, $popts, $pageid,
187 isset( $prop['wikitext'] ) );
188 }
189 }
190 } else { // Not $oldid, $pageid, $page. Hence based on $text
191 $titleObj = Title::newFromText( $title );
192 if ( !$titleObj || $titleObj->isExternal() ) {
193 $this->dieUsageMsg( array( 'invalidtitle', $title ) );
194 }
195 $wgTitle = $titleObj;
196 if ( $titleObj->canExist() ) {
197 $pageObj = WikiPage::factory( $titleObj );
198 } else {
199 // Do like MediaWiki::initializeArticle()
200 $article = Article::newFromTitle( $titleObj, $this->getContext() );
201 $pageObj = $article->getPage();
202 }
203
204 $popts = $this->makeParserOptions( $pageObj, $params );
205 $textProvided = !is_null( $text );
206
207 if ( !$textProvided ) {
208 if ( $titleProvided && ( $prop || $params['generatexml'] ) ) {
209 $this->setWarning(
210 "'title' used without 'text', and parsed page properties were requested " .
211 "(did you mean to use 'page' instead of 'title'?)"
212 );
213 }
214 // Prevent warning from ContentHandler::makeContent()
215 $text = '';
216 }
217
218 // If we are parsing text, do not use the content model of the default
219 // API title, but default to wikitext to keep BC.
220 if ( $textProvided && !$titleProvided && is_null( $model ) ) {
221 $model = CONTENT_MODEL_WIKITEXT;
222 $this->setWarning( "No 'title' or 'contentmodel' was given, assuming $model." );
223 }
224
225 try {
226 $this->content = ContentHandler::makeContent( $text, $titleObj, $model, $format );
227 } catch ( MWContentSerializationException $ex ) {
228 $this->dieUsage( $ex->getMessage(), 'parseerror' );
229 }
230
231 if ( $this->section !== false ) {
232 if ( $this->section === 'new' ) {
233 // Insert the section title above the content.
234 if ( !is_null( $params['sectiontitle'] ) && $params['sectiontitle'] !== '' ) {
235 $this->content = $this->content->addSectionHeader( $params['sectiontitle'] );
236 }
237 } else {
238 $this->content = $this->getSectionContent( $this->content, $titleObj->getPrefixedText() );
239 }
240 }
241
242 if ( $params['pst'] || $params['onlypst'] ) {
243 $this->pstContent = $this->content->preSaveTransform( $titleObj, $this->getUser(), $popts );
244 }
245 if ( $params['onlypst'] ) {
246 // Build a result and bail out
247 $result_array = array();
248 $result_array['text'] = $this->pstContent->serialize( $format );
249 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'text';
250 if ( isset( $prop['wikitext'] ) ) {
251 $result_array['wikitext'] = $this->content->serialize( $format );
252 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'wikitext';
253 }
254 if ( !is_null( $params['summary'] ) ||
255 ( !is_null( $params['sectiontitle'] ) && $this->section === 'new' )
256 ) {
257 $result_array['parsedsummary'] = $this->formatSummary( $titleObj, $params );
258 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'parsedsummary';
259 }
260
261 $result->addValue( null, $this->getModuleName(), $result_array );
262
263 return;
264 }
265
266 // Not cached (save or load)
267 if ( $params['pst'] ) {
268 $p_result = $this->pstContent->getParserOutput( $titleObj, null, $popts );
269 } else {
270 $p_result = $this->content->getParserOutput( $titleObj, null, $popts );
271 }
272 }
273
274 $result_array = array();
275
276 $result_array['title'] = $titleObj->getPrefixedText();
277 $result_array['pageid'] = $pageid ? $pageid : $pageObj->getId();
278
279 if ( !is_null( $oldid ) ) {
280 $result_array['revid'] = intval( $oldid );
281 }
282
283 if ( $params['redirects'] && !is_null( $redirValues ) ) {
284 $result_array['redirects'] = $redirValues;
285 }
286
287 if ( $params['disabletoc'] ) {
288 $p_result->setTOCEnabled( false );
289 }
290
291 if ( isset( $prop['text'] ) ) {
292 $result_array['text'] = $p_result->getText();
293 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'text';
294 }
295
296 if ( !is_null( $params['summary'] ) ||
297 ( !is_null( $params['sectiontitle'] ) && $this->section === 'new' )
298 ) {
299 $result_array['parsedsummary'] = $this->formatSummary( $titleObj, $params );
300 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'parsedsummary';
301 }
302
303 if ( isset( $prop['langlinks'] ) ) {
304 $langlinks = $p_result->getLanguageLinks();
305
306 if ( $params['effectivelanglinks'] ) {
307 // Link flags are ignored for now, but may in the future be
308 // included in the result.
309 $linkFlags = array();
310 Hooks::run( 'LanguageLinks', array( $titleObj, &$langlinks, &$linkFlags ) );
311 }
312 } else {
313 $langlinks = false;
314 }
315
316 if ( isset( $prop['langlinks'] ) ) {
317 $result_array['langlinks'] = $this->formatLangLinks( $langlinks );
318 }
319 if ( isset( $prop['categories'] ) ) {
320 $result_array['categories'] = $this->formatCategoryLinks( $p_result->getCategories() );
321 }
322 if ( isset( $prop['categorieshtml'] ) ) {
323 $result_array['categorieshtml'] = $this->categoriesHtml( $p_result->getCategories() );
324 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'categorieshtml';
325 }
326 if ( isset( $prop['links'] ) ) {
327 $result_array['links'] = $this->formatLinks( $p_result->getLinks() );
328 }
329 if ( isset( $prop['templates'] ) ) {
330 $result_array['templates'] = $this->formatLinks( $p_result->getTemplates() );
331 }
332 if ( isset( $prop['images'] ) ) {
333 $result_array['images'] = array_keys( $p_result->getImages() );
334 }
335 if ( isset( $prop['externallinks'] ) ) {
336 $result_array['externallinks'] = array_keys( $p_result->getExternalLinks() );
337 }
338 if ( isset( $prop['sections'] ) ) {
339 $result_array['sections'] = $p_result->getSections();
340 }
341
342 if ( isset( $prop['displaytitle'] ) ) {
343 $result_array['displaytitle'] = $p_result->getDisplayTitle() ?
344 $p_result->getDisplayTitle() :
345 $titleObj->getPrefixedText();
346 }
347
348 if ( isset( $prop['headitems'] ) || isset( $prop['headhtml'] ) ) {
349 $context = $this->getContext();
350 $context->setTitle( $titleObj );
351 $context->getOutput()->addParserOutputMetadata( $p_result );
352
353 if ( isset( $prop['headitems'] ) ) {
354 $headItems = $this->formatHeadItems( $p_result->getHeadItems() );
355
356 $css = $this->formatCss( $context->getOutput()->buildCssLinksArray() );
357
358 $scripts = array( $context->getOutput()->getHeadScripts() );
359
360 $result_array['headitems'] = array_merge( $headItems, $css, $scripts );
361 }
362
363 if ( isset( $prop['headhtml'] ) ) {
364 $result_array['headhtml'] = $context->getOutput()->headElement( $context->getSkin() );
365 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'headhtml';
366 }
367 }
368
369 if ( isset( $prop['modules'] ) ) {
370 $result_array['modules'] = array_values( array_unique( $p_result->getModules() ) );
371 $result_array['modulescripts'] = array_values( array_unique( $p_result->getModuleScripts() ) );
372 $result_array['modulestyles'] = array_values( array_unique( $p_result->getModuleStyles() ) );
373 // To be removed in 1.27
374 $result_array['modulemessages'] = array();
375 $this->setWarning( 'modulemessages is deprecated since MediaWiki 1.26' );
376 }
377
378 if ( isset( $prop['jsconfigvars'] ) ) {
379 $result_array['jsconfigvars'] =
380 ApiResult::addMetadataToResultVars( $p_result->getJsConfigVars() );
381 }
382
383 if ( isset( $prop['encodedjsconfigvars'] ) ) {
384 $result_array['encodedjsconfigvars'] = FormatJson::encode(
385 $p_result->getJsConfigVars(), false, FormatJson::ALL_OK
386 );
387 $result_array[ApiResult::META_SUBELEMENTS][] = 'encodedjsconfigvars';
388 }
389
390 if ( isset( $prop['modules'] ) &&
391 !isset( $prop['jsconfigvars'] ) && !isset( $prop['encodedjsconfigvars'] ) ) {
392 $this->setWarning( "Property 'modules' was set but not 'jsconfigvars' " .
393 "or 'encodedjsconfigvars'. Configuration variables are necessary " .
394 "for proper module usage." );
395 }
396
397 if ( isset( $prop['indicators'] ) ) {
398 $result_array['indicators'] = (array)$p_result->getIndicators();
399 ApiResult::setArrayType( $result_array['indicators'], 'BCkvp', 'name' );
400 }
401
402 if ( isset( $prop['iwlinks'] ) ) {
403 $result_array['iwlinks'] = $this->formatIWLinks( $p_result->getInterwikiLinks() );
404 }
405
406 if ( isset( $prop['wikitext'] ) ) {
407 $result_array['wikitext'] = $this->content->serialize( $format );
408 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'wikitext';
409 if ( !is_null( $this->pstContent ) ) {
410 $result_array['psttext'] = $this->pstContent->serialize( $format );
411 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'psttext';
412 }
413 }
414 if ( isset( $prop['properties'] ) ) {
415 $result_array['properties'] = (array)$p_result->getProperties();
416 ApiResult::setArrayType( $result_array['properties'], 'BCkvp', 'name' );
417 }
418
419 if ( isset( $prop['limitreportdata'] ) ) {
420 $result_array['limitreportdata'] =
421 $this->formatLimitReportData( $p_result->getLimitReportData() );
422 }
423 if ( isset( $prop['limitreporthtml'] ) ) {
424 $result_array['limitreporthtml'] = EditPage::getPreviewLimitReport( $p_result );
425 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'limitreporthtml';
426 }
427
428 if ( isset( $prop['parsetree'] ) || $params['generatexml'] ) {
429 if ( !isset( $prop['parsetree'] ) ) {
430 $this->logFeatureUsage( 'action=parse&generatexml' );
431 }
432 if ( $this->content->getModel() != CONTENT_MODEL_WIKITEXT ) {
433 $this->dieUsage( "parsetree is only supported for wikitext content", "notwikitext" );
434 }
435
436 $wgParser->startExternalParse( $titleObj, $popts, Parser::OT_PREPROCESS );
437 $dom = $wgParser->preprocessToDom( $this->content->getNativeData() );
438 if ( is_callable( array( $dom, 'saveXML' ) ) ) {
439 $xml = $dom->saveXML();
440 } else {
441 $xml = $dom->__toString();
442 }
443 $result_array['parsetree'] = $xml;
444 $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'parsetree';
445 }
446
447 $result_mapping = array(
448 'redirects' => 'r',
449 'langlinks' => 'll',
450 'categories' => 'cl',
451 'links' => 'pl',
452 'templates' => 'tl',
453 'images' => 'img',
454 'externallinks' => 'el',
455 'iwlinks' => 'iw',
456 'sections' => 's',
457 'headitems' => 'hi',
458 'modules' => 'm',
459 'indicators' => 'ind',
460 'modulescripts' => 'm',
461 'modulestyles' => 'm',
462 'modulemessages' => 'm',
463 'properties' => 'pp',
464 'limitreportdata' => 'lr',
465 );
466 $this->setIndexedTagNames( $result_array, $result_mapping );
467 $result->addValue( null, $this->getModuleName(), $result_array );
468 }
469
470 /**
471 * Constructs a ParserOptions object
472 *
473 * @param WikiPage $pageObj
474 * @param array $params
475 *
476 * @return ParserOptions
477 */
478 protected function makeParserOptions( WikiPage $pageObj, array $params ) {
479
480 $popts = $pageObj->makeParserOptions( $this->getContext() );
481 $popts->enableLimitReport( !$params['disablepp'] && !$params['disablelimitreport'] );
482 $popts->setIsPreview( $params['preview'] || $params['sectionpreview'] );
483 $popts->setIsSectionPreview( $params['sectionpreview'] );
484 $popts->setEditSection( !$params['disableeditsection'] );
485 if ( $params['disabletidy'] ) {
486 $popts->setTidy( false );
487 }
488
489 return $popts;
490 }
491
492 /**
493 * @param WikiPage $page
494 * @param ParserOptions $popts
495 * @param int $pageId
496 * @param bool $getWikitext
497 * @return ParserOutput
498 */
499 private function getParsedContent( WikiPage $page, $popts, $pageId = null, $getWikitext = false ) {
500 $this->content = $this->getContent( $page, $pageId );
501
502 if ( $this->section !== false && $this->content !== null ) {
503 // Not cached (save or load)
504 return $this->content->getParserOutput( $page->getTitle(), null, $popts );
505 }
506
507 // Try the parser cache first
508 // getParserOutput will save to Parser cache if able
509 $pout = $page->getParserOutput( $popts );
510 if ( !$pout ) {
511 $this->dieUsage( "There is no revision ID {$page->getLatest()}", 'missingrev' );
512 }
513 if ( $getWikitext ) {
514 $this->content = $page->getContent( Revision::RAW );
515 }
516
517 return $pout;
518 }
519
520 /**
521 * Get the content for the given page and the requested section.
522 *
523 * @param WikiPage $page
524 * @param int $pageId
525 * @return Content
526 */
527 private function getContent( WikiPage $page, $pageId = null ) {
528 $content = $page->getContent( Revision::RAW ); //XXX: really raw?
529
530 if ( $this->section !== false && $content !== null ) {
531 $content = $this->getSectionContent(
532 $content,
533 !is_null( $pageId ) ? 'page id ' . $pageId : $page->getTitle()->getPrefixedText()
534 );
535 }
536 return $content;
537 }
538
539 /**
540 * Extract the requested section from the given Content
541 *
542 * @param Content $content
543 * @param string $what Identifies the content in error messages, e.g. page title.
544 * @return Content|bool
545 */
546 private function getSectionContent( Content $content, $what ) {
547 // Not cached (save or load)
548 $section = $content->getSection( $this->section );
549 if ( $section === false ) {
550 $this->dieUsage( "There is no section {$this->section} in " . $what, 'nosuchsection' );
551 }
552 if ( $section === null ) {
553 $this->dieUsage( "Sections are not supported by " . $what, 'nosuchsection' );
554 $section = false;
555 }
556
557 return $section;
558 }
559
560 /**
561 * This mimicks the behavior of EditPage in formatting a summary
562 *
563 * @param Title $title of the page being parsed
564 * @param Array $params the API parameters of the request
565 * @return Content|bool
566 */
567 private function formatSummary( $title, $params ) {
568 global $wgParser;
569 $summary = !is_null( $params['summary'] ) ? $params['summary'] : '';
570 $sectionTitle = !is_null( $params['sectiontitle'] ) ? $params['sectiontitle'] : '';
571
572 if ( $this->section === 'new' && ( $sectionTitle === '' || $summary === '' ) ) {
573 if ( $sectionTitle !== '' ) {
574 $summary = $params['sectiontitle'];
575 }
576 if ( $summary !== '' ) {
577 $summary = wfMessage( 'newsectionsummary' )
578 ->rawParams( $wgParser->stripSectionName( $summary ) )
579 ->inContentLanguage()->text();
580 }
581 }
582 return Linker::formatComment( $summary, $title, $this->section === 'new' );
583 }
584
585 private function formatLangLinks( $links ) {
586 $result = array();
587 foreach ( $links as $link ) {
588 $entry = array();
589 $bits = explode( ':', $link, 2 );
590 $title = Title::newFromText( $link );
591
592 $entry['lang'] = $bits[0];
593 if ( $title ) {
594 $entry['url'] = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
595 // localised language name in 'uselang' language
596 $entry['langname'] = Language::fetchLanguageName(
597 $title->getInterwiki(),
598 $this->getLanguage()->getCode()
599 );
600
601 // native language name
602 $entry['autonym'] = Language::fetchLanguageName( $title->getInterwiki() );
603 }
604 ApiResult::setContentValue( $entry, 'title', $bits[1] );
605 $result[] = $entry;
606 }
607
608 return $result;
609 }
610
611 private function formatCategoryLinks( $links ) {
612 $result = array();
613
614 if ( !$links ) {
615 return $result;
616 }
617
618 // Fetch hiddencat property
619 $lb = new LinkBatch;
620 $lb->setArray( array( NS_CATEGORY => $links ) );
621 $db = $this->getDB();
622 $res = $db->select( array( 'page', 'page_props' ),
623 array( 'page_title', 'pp_propname' ),
624 $lb->constructSet( 'page', $db ),
625 __METHOD__,
626 array(),
627 array( 'page_props' => array(
628 'LEFT JOIN', array( 'pp_propname' => 'hiddencat', 'pp_page = page_id' )
629 ) )
630 );
631 $hiddencats = array();
632 foreach ( $res as $row ) {
633 $hiddencats[$row->page_title] = isset( $row->pp_propname );
634 }
635
636 foreach ( $links as $link => $sortkey ) {
637 $entry = array();
638 $entry['sortkey'] = $sortkey;
639 ApiResult::setContentValue( $entry, 'category', $link );
640 if ( !isset( $hiddencats[$link] ) ) {
641 $entry['missing'] = true;
642 } elseif ( $hiddencats[$link] ) {
643 $entry['hidden'] = true;
644 }
645 $result[] = $entry;
646 }
647
648 return $result;
649 }
650
651 private function categoriesHtml( $categories ) {
652 $context = $this->getContext();
653 $context->getOutput()->addCategoryLinks( $categories );
654
655 return $context->getSkin()->getCategories();
656 }
657
658 private function formatLinks( $links ) {
659 $result = array();
660 foreach ( $links as $ns => $nslinks ) {
661 foreach ( $nslinks as $title => $id ) {
662 $entry = array();
663 $entry['ns'] = $ns;
664 ApiResult::setContentValue( $entry, 'title', Title::makeTitle( $ns, $title )->getFullText() );
665 $entry['exists'] = $id != 0;
666 $result[] = $entry;
667 }
668 }
669
670 return $result;
671 }
672
673 private function formatIWLinks( $iw ) {
674 $result = array();
675 foreach ( $iw as $prefix => $titles ) {
676 foreach ( array_keys( $titles ) as $title ) {
677 $entry = array();
678 $entry['prefix'] = $prefix;
679
680 $title = Title::newFromText( "{$prefix}:{$title}" );
681 if ( $title ) {
682 $entry['url'] = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
683 }
684
685 ApiResult::setContentValue( $entry, 'title', $title->getFullText() );
686 $result[] = $entry;
687 }
688 }
689
690 return $result;
691 }
692
693 private function formatHeadItems( $headItems ) {
694 $result = array();
695 foreach ( $headItems as $tag => $content ) {
696 $entry = array();
697 $entry['tag'] = $tag;
698 ApiResult::setContentValue( $entry, 'content', $content );
699 $result[] = $entry;
700 }
701
702 return $result;
703 }
704
705 private function formatCss( $css ) {
706 $result = array();
707 foreach ( $css as $file => $link ) {
708 $entry = array();
709 $entry['file'] = $file;
710 ApiResult::setContentValue( $entry, 'link', $link );
711 $result[] = $entry;
712 }
713
714 return $result;
715 }
716
717 private function formatLimitReportData( $limitReportData ) {
718 $result = array();
719 $apiResult = $this->getResult();
720
721 foreach ( $limitReportData as $name => $value ) {
722 $entry = array();
723 $entry['name'] = $name;
724 if ( !is_array( $value ) ) {
725 $value = array( $value );
726 }
727 ApiResult::setIndexedTagNameRecursive( $value, 'param' );
728 $entry = array_merge( $entry, $value );
729 $result[] = $entry;
730 }
731
732 return $result;
733 }
734
735 private function setIndexedTagNames( &$array, $mapping ) {
736 foreach ( $mapping as $key => $name ) {
737 if ( isset( $array[$key] ) ) {
738 ApiResult::setIndexedTagName( $array[$key], $name );
739 }
740 }
741 }
742
743 public function getAllowedParams() {
744 return array(
745 'title' => null,
746 'text' => array(
747 ApiBase::PARAM_TYPE => 'text',
748 ),
749 'summary' => null,
750 'page' => null,
751 'pageid' => array(
752 ApiBase::PARAM_TYPE => 'integer',
753 ),
754 'redirects' => false,
755 'oldid' => array(
756 ApiBase::PARAM_TYPE => 'integer',
757 ),
758 'prop' => array(
759 ApiBase::PARAM_DFLT => 'text|langlinks|categories|links|templates|' .
760 'images|externallinks|sections|revid|displaytitle|iwlinks|properties',
761 ApiBase::PARAM_ISMULTI => true,
762 ApiBase::PARAM_TYPE => array(
763 'text',
764 'langlinks',
765 'categories',
766 'categorieshtml',
767 'links',
768 'templates',
769 'images',
770 'externallinks',
771 'sections',
772 'revid',
773 'displaytitle',
774 'headitems',
775 'headhtml',
776 'modules',
777 'jsconfigvars',
778 'encodedjsconfigvars',
779 'indicators',
780 'iwlinks',
781 'wikitext',
782 'properties',
783 'limitreportdata',
784 'limitreporthtml',
785 'parsetree',
786 ),
787 ApiBase::PARAM_HELP_MSG_PER_VALUE => array(
788 'parsetree' => array( 'apihelp-parse-paramvalue-prop-parsetree', CONTENT_MODEL_WIKITEXT ),
789 ),
790 ),
791 'pst' => false,
792 'onlypst' => false,
793 'effectivelanglinks' => false,
794 'section' => null,
795 'sectiontitle' => array(
796 ApiBase::PARAM_TYPE => 'string',
797 ),
798 'disablepp' => array(
799 ApiBase::PARAM_DFLT => false,
800 ApiBase::PARAM_DEPRECATED => true,
801 ),
802 'disablelimitreport' => false,
803 'disableeditsection' => false,
804 'disabletidy' => false,
805 'generatexml' => array(
806 ApiBase::PARAM_DFLT => false,
807 ApiBase::PARAM_HELP_MSG => array(
808 'apihelp-parse-param-generatexml', CONTENT_MODEL_WIKITEXT
809 ),
810 ApiBase::PARAM_DEPRECATED => true,
811 ),
812 'preview' => false,
813 'sectionpreview' => false,
814 'disabletoc' => false,
815 'contentformat' => array(
816 ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
817 ),
818 'contentmodel' => array(
819 ApiBase::PARAM_TYPE => ContentHandler::getContentModels(),
820 )
821 );
822 }
823
824 protected function getExamplesMessages() {
825 return array(
826 'action=parse&page=Project:Sandbox'
827 => 'apihelp-parse-example-page',
828 'action=parse&text={{Project:Sandbox}}&contentmodel=wikitext'
829 => 'apihelp-parse-example-text',
830 'action=parse&text={{PAGENAME}}&title=Test'
831 => 'apihelp-parse-example-texttitle',
832 'action=parse&summary=Some+[[link]]&prop='
833 => 'apihelp-parse-example-summary',
834 );
835 }
836
837 public function getHelpUrls() {
838 return 'https://www.mediawiki.org/wiki/API:Parsing_wikitext#parse';
839 }
840 }