702825a4dafaf77b06615ea75229ad14b42a0dd6
[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 __construct( $main, $action ) {
40 parent::__construct( $main, $action );
41 }
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 $text = $params['text'];
50 $title = $params['title'];
51 $page = $params['page'];
52 $pageid = $params['pageid'];
53 $oldid = $params['oldid'];
54
55 $model = $params['contentmodel'];
56 $format = $params['contentformat'];
57
58 if ( !is_null( $page ) && ( !is_null( $text ) || $title != 'API' ) ) {
59 $this->dieUsage( 'The page parameter cannot be used together with the text and title parameters', 'params' );
60 }
61
62 $prop = array_flip( $params['prop'] );
63
64 if ( isset( $params['section'] ) ) {
65 $this->section = $params['section'];
66 } else {
67 $this->section = false;
68 }
69
70 // The parser needs $wgTitle to be set, apparently the
71 // $title parameter in Parser::parse isn't enough *sigh*
72 // TODO: Does this still need $wgTitle?
73 global $wgParser, $wgTitle;
74
75 // Currently unnecessary, code to act as a safeguard against any change in current behaviour of uselang breaks
76 $oldLang = null;
77 if ( isset( $params['uselang'] ) && $params['uselang'] != $this->getContext()->getLanguage()->getCode() ) {
78 $oldLang = $this->getContext()->getLanguage(); // Backup language
79 $this->getContext()->setLanguage( Language::factory( $params['uselang'] ) );
80 }
81
82 $popts = ParserOptions::newFromContext( $this->getContext() );
83 $popts->setTidy( true );
84 $popts->enableLimitReport( !$params['disablepp'] );
85
86 $redirValues = null;
87
88 // Return result
89 $result = $this->getResult();
90
91 if ( !is_null( $oldid ) || !is_null( $pageid ) || !is_null( $page ) ) {
92 if ( !is_null( $oldid ) ) {
93 // Don't use the parser cache
94 $rev = Revision::newFromID( $oldid );
95 if ( !$rev ) {
96 $this->dieUsage( "There is no revision ID $oldid", 'missingrev' );
97 }
98 if ( !$rev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) {
99 $this->dieUsage( "You don't have permission to view deleted revisions", 'permissiondenied' );
100 }
101
102 $titleObj = $rev->getTitle();
103
104 $wgTitle = $titleObj;
105
106 // If for some reason the "oldid" is actually the current revision, it may be cached
107 if ( $titleObj->getLatestRevID() === intval( $oldid ) ) {
108 // May get from/save to parser cache
109 $pageObj = WikiPage::factory( $titleObj );
110 $p_result = $this->getParsedContent( $pageObj, $popts, $pageid, isset( $prop['wikitext'] ) ) ;
111 } else { // This is an old revision, so get the text differently
112 $this->content = $rev->getContent( Revision::FOR_THIS_USER, $this->getUser() );
113
114 if ( $this->section !== false ) {
115 $this->content = $this->getSectionContent( $this->content, 'r' . $rev->getId() );
116 }
117
118 // Should we save old revision parses to the parser cache?
119 $p_result = $this->content->getParserOutput( $titleObj, $popts );
120 }
121 } else { // Not $oldid, but $pageid or $page
122 if ( $params['redirects'] ) {
123 $reqParams = array(
124 'action' => 'query',
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 $main->execute();
135 $data = $main->getResultData();
136 $redirValues = isset( $data['query']['redirects'] )
137 ? $data['query']['redirects']
138 : array();
139 $to = $page;
140 foreach ( (array)$redirValues as $r ) {
141 $to = $r['to'];
142 }
143 $titleObj = Title::newFromText( $to );
144 } else {
145 if ( !is_null ( $pageid ) ) {
146 $reqParams['pageids'] = $pageid;
147 $titleObj = Title::newFromID( $pageid );
148 } else { // $page
149 $to = $page;
150 $titleObj = Title::newFromText( $to );
151 }
152 }
153 if ( !is_null ( $pageid ) ) {
154 if ( !$titleObj ) {
155 // Still throw nosuchpageid error if pageid was provided
156 $this->dieUsageMsg( array( 'nosuchpageid', $pageid ) );
157 }
158 } elseif ( !$titleObj || !$titleObj->exists() ) {
159 $this->dieUsage( "The page you specified doesn't exist", 'missingtitle' );
160 }
161 $wgTitle = $titleObj;
162
163 if ( isset( $prop['revid'] ) ) {
164 $oldid = $titleObj->getLatestRevID();
165 }
166
167 $pageObj = WikiPage::factory( $titleObj );
168
169 // Potentially cached
170 $p_result = $this->getParsedContent( $pageObj, $popts, $pageid, isset( $prop['wikitext'] ) ) ;
171 }
172 } else { // Not $oldid, $pageid, $page. Hence based on $text
173 $titleObj = Title::newFromText( $title );
174 if ( !$titleObj ) {
175 $this->dieUsageMsg( array( 'invalidtitle', $title ) );
176 }
177 $wgTitle = $titleObj;
178
179 if ( is_null( $text ) ) {
180 $this->dieUsage( 'The text parameter should be passed with the title parameter. Should you be using the "page" parameter instead?', 'params' );
181 }
182
183 try {
184 $this->content = ContentHandler::makeContent( $text, $titleObj, $model, $format );
185 } catch ( MWContentSerializationException $ex ) {
186 $this->dieUsage( $ex->getMessage(), 'parseerror' );
187 }
188
189 if ( $this->section !== false ) {
190 $this->content = $this->getSectionContent( $this->content, $titleObj->getText() );
191 }
192
193 if ( $params['pst'] || $params['onlypst'] ) {
194 $this->pstContent = $this->content->preSaveTransform( $titleObj, $this->getUser(), $popts );
195 }
196 if ( $params['onlypst'] ) {
197 // Build a result and bail out
198 $result_array = array();
199 $result_array['text'] = array();
200 $result->setContent( $result_array['text'], $this->pstContent->serialize( $format ) );
201 if ( isset( $prop['wikitext'] ) ) {
202 $result_array['wikitext'] = array();
203 $result->setContent( $result_array['wikitext'], $this->content->serialize( $format ) );
204 }
205 $result->addValue( null, $this->getModuleName(), $result_array );
206 return;
207 }
208
209 // Not cached (save or load)
210 if ( $params['pst'] ) {
211 $p_result = $this->pstContent->getParserOutput( $titleObj, $popts );
212 } else {
213 $p_result = $this->content->getParserOutput( $titleObj, $popts );
214 }
215 }
216
217 $result_array = array();
218
219 $result_array['title'] = $titleObj->getPrefixedText();
220
221 if ( !is_null( $oldid ) ) {
222 $result_array['revid'] = intval( $oldid );
223 }
224
225 if ( $params['redirects'] && !is_null( $redirValues ) ) {
226 $result_array['redirects'] = $redirValues;
227 }
228
229 if ( isset( $prop['text'] ) ) {
230 $result_array['text'] = array();
231 $result->setContent( $result_array['text'], $p_result->getText() );
232 }
233
234 if ( !is_null( $params['summary'] ) ) {
235 $result_array['parsedsummary'] = array();
236 $result->setContent( $result_array['parsedsummary'], Linker::formatComment( $params['summary'], $titleObj ) );
237 }
238
239 if ( isset( $prop['langlinks'] ) ) {
240 $result_array['langlinks'] = $this->formatLangLinks( $p_result->getLanguageLinks() );
241 }
242 if ( isset( $prop['languageshtml'] ) ) {
243 $languagesHtml = $this->languagesHtml( $p_result->getLanguageLinks() );
244 $result_array['languageshtml'] = array();
245 $result->setContent( $result_array['languageshtml'], $languagesHtml );
246 }
247 if ( isset( $prop['categories'] ) ) {
248 $result_array['categories'] = $this->formatCategoryLinks( $p_result->getCategories() );
249 }
250 if ( isset( $prop['categorieshtml'] ) ) {
251 $categoriesHtml = $this->categoriesHtml( $p_result->getCategories() );
252 $result_array['categorieshtml'] = array();
253 $result->setContent( $result_array['categorieshtml'], $categoriesHtml );
254 }
255 if ( isset( $prop['links'] ) ) {
256 $result_array['links'] = $this->formatLinks( $p_result->getLinks() );
257 }
258 if ( isset( $prop['templates'] ) ) {
259 $result_array['templates'] = $this->formatLinks( $p_result->getTemplates() );
260 }
261 if ( isset( $prop['images'] ) ) {
262 $result_array['images'] = array_keys( $p_result->getImages() );
263 }
264 if ( isset( $prop['externallinks'] ) ) {
265 $result_array['externallinks'] = array_keys( $p_result->getExternalLinks() );
266 }
267 if ( isset( $prop['sections'] ) ) {
268 $result_array['sections'] = $p_result->getSections();
269 }
270
271 if ( isset( $prop['displaytitle'] ) ) {
272 $result_array['displaytitle'] = $p_result->getDisplayTitle() ?
273 $p_result->getDisplayTitle() :
274 $titleObj->getPrefixedText();
275 }
276
277 if ( isset( $prop['headitems'] ) || isset( $prop['headhtml'] ) ) {
278 $context = $this->getContext();
279 $context->setTitle( $titleObj );
280 $context->getOutput()->addParserOutputNoText( $p_result );
281
282 if ( isset( $prop['headitems'] ) ) {
283 $headItems = $this->formatHeadItems( $p_result->getHeadItems() );
284
285 $css = $this->formatCss( $context->getOutput()->buildCssLinksArray() );
286
287 $scripts = array( $context->getOutput()->getHeadScripts() );
288
289 $result_array['headitems'] = array_merge( $headItems, $css, $scripts );
290 }
291
292 if ( isset( $prop['headhtml'] ) ) {
293 $result_array['headhtml'] = array();
294 $result->setContent( $result_array['headhtml'], $context->getOutput()->headElement( $context->getSkin() ) );
295 }
296 }
297
298 if ( isset( $prop['iwlinks'] ) ) {
299 $result_array['iwlinks'] = $this->formatIWLinks( $p_result->getInterwikiLinks() );
300 }
301
302 if ( isset( $prop['wikitext'] ) ) {
303 $result_array['wikitext'] = array();
304 $result->setContent( $result_array['wikitext'], $this->content->serialize( $format ) );
305 if ( !is_null( $this->pstContent ) ) {
306 $result_array['psttext'] = array();
307 $result->setContent( $result_array['psttext'], $this->pstContent->serialize( $format ) );
308 }
309 }
310 if ( isset( $prop['properties'] ) ) {
311 $result_array['properties'] = $this->formatProperties( $p_result->getProperties() );
312 }
313
314 if ( $params['generatexml'] ) {
315 if ( $this->content->getModel() != CONTENT_MODEL_WIKITEXT ) {
316 $this->dieUsage( "generatexml is only supported for wikitext content", "notwikitext" );
317 }
318
319 $wgParser->startExternalParse( $titleObj, $popts, OT_PREPROCESS );
320 $dom = $wgParser->preprocessToDom( $this->content->getNativeData() );
321 if ( is_callable( array( $dom, 'saveXML' ) ) ) {
322 $xml = $dom->saveXML();
323 } else {
324 $xml = $dom->__toString();
325 }
326 $result_array['parsetree'] = array();
327 $result->setContent( $result_array['parsetree'], $xml );
328 }
329
330 $result_mapping = array(
331 'redirects' => 'r',
332 'langlinks' => 'll',
333 'categories' => 'cl',
334 'links' => 'pl',
335 'templates' => 'tl',
336 'images' => 'img',
337 'externallinks' => 'el',
338 'iwlinks' => 'iw',
339 'sections' => 's',
340 'headitems' => 'hi',
341 'properties' => 'pp',
342 );
343 $this->setIndexedTagNames( $result_array, $result_mapping );
344 $result->addValue( null, $this->getModuleName(), $result_array );
345
346 if ( !is_null( $oldLang ) ) {
347 $this->getContext()->setLanguage( $oldLang ); // Reset language to $oldLang
348 }
349 }
350
351 /**
352 * @param $page WikiPage
353 * @param $popts ParserOptions
354 * @param $pageId Int
355 * @param $getWikitext Bool
356 * @return ParserOutput
357 */
358 private function getParsedContent( WikiPage $page, $popts, $pageId = null, $getWikitext = false ) {
359 $this->content = $page->getContent( Revision::RAW ); //XXX: really raw?
360
361 if ( $this->section !== false ) {
362 $this->content = $this->getSectionContent( $this->content, !is_null( $pageId )
363 ? 'page id ' . $pageId : $page->getTitle()->getText() );
364
365 // Not cached (save or load)
366 return $this->content->getParserOutput( $page->getTitle(), $popts );
367 } else {
368 // Try the parser cache first
369 // getParserOutput will save to Parser cache if able
370 $pout = $page->getParserOutput( $popts );
371 if ( !$pout ) {
372 $this->dieUsage( "There is no revision ID {$page->getLatest()}", 'missingrev' );
373 }
374 if ( $getWikitext ) {
375 $this->content = $page->getContent( Revision::RAW );
376 }
377 return $pout;
378 }
379 }
380
381 private function getSectionContent( Content $content, $what ) {
382 // Not cached (save or load)
383 $section = $content->getSection( $this->section );
384 if ( $section === false ) {
385 $this->dieUsage( "There is no section {$this->section} in " . $what, 'nosuchsection' );
386 }
387 if ( $section === null ) {
388 $this->dieUsage( "Sections are not supported by " . $what, 'nosuchsection' );
389 $section = false;
390 }
391 return $section;
392 }
393
394 private function formatLangLinks( $links ) {
395 $result = array();
396 foreach ( $links as $link ) {
397 $entry = array();
398 $bits = explode( ':', $link, 2 );
399 $title = Title::newFromText( $link );
400
401 $entry['lang'] = $bits[0];
402 if ( $title ) {
403 $entry['url'] = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
404 }
405 $this->getResult()->setContent( $entry, $bits[1] );
406 $result[] = $entry;
407 }
408 return $result;
409 }
410
411 private function formatCategoryLinks( $links ) {
412 $result = array();
413 foreach ( $links as $link => $sortkey ) {
414 $entry = array();
415 $entry['sortkey'] = $sortkey;
416 $this->getResult()->setContent( $entry, $link );
417 $result[] = $entry;
418 }
419 return $result;
420 }
421
422 private function categoriesHtml( $categories ) {
423 $context = $this->getContext();
424 $context->getOutput()->addCategoryLinks( $categories );
425 return $context->getSkin()->getCategories();
426 }
427
428 /**
429 * @deprecated since 1.18 No modern skin generates language links this way, please use language links
430 * data to generate your own HTML.
431 * @param $languages array
432 * @return string
433 */
434 private function languagesHtml( $languages ) {
435 wfDeprecated( __METHOD__, '1.18' );
436
437 global $wgContLang, $wgHideInterlanguageLinks;
438
439 if ( $wgHideInterlanguageLinks || count( $languages ) == 0 ) {
440 return '';
441 }
442
443 $s = htmlspecialchars( wfMessage( 'otherlanguages' )->text() . wfMessage( 'colon-separator' )->text() );
444
445 $langs = array();
446 foreach ( $languages as $l ) {
447 $nt = Title::newFromText( $l );
448 $text = Language::fetchLanguageName( $nt->getInterwiki() );
449
450 $langs[] = Html::element( 'a',
451 array( 'href' => $nt->getFullURL(), 'title' => $nt->getText(), 'class' => "external" ),
452 $text == '' ? $l : $text );
453 }
454
455 $s .= implode( htmlspecialchars( wfMsgExt( 'pipe-separator', 'escapenoentities' ) ), $langs );
456
457 if ( $wgContLang->isRTL() ) {
458 $s = Html::rawElement( 'span', array( 'dir' => "LTR" ), $s );
459 }
460
461 return $s;
462 }
463
464 private function formatLinks( $links ) {
465 $result = array();
466 foreach ( $links as $ns => $nslinks ) {
467 foreach ( $nslinks as $title => $id ) {
468 $entry = array();
469 $entry['ns'] = $ns;
470 $this->getResult()->setContent( $entry, Title::makeTitle( $ns, $title )->getFullText() );
471 if ( $id != 0 ) {
472 $entry['exists'] = '';
473 }
474 $result[] = $entry;
475 }
476 }
477 return $result;
478 }
479
480 private function formatIWLinks( $iw ) {
481 $result = array();
482 foreach ( $iw as $prefix => $titles ) {
483 foreach ( array_keys( $titles ) as $title ) {
484 $entry = array();
485 $entry['prefix'] = $prefix;
486
487 $title = Title::newFromText( "{$prefix}:{$title}" );
488 if ( $title ) {
489 $entry['url'] = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
490 }
491
492 $this->getResult()->setContent( $entry, $title->getFullText() );
493 $result[] = $entry;
494 }
495 }
496 return $result;
497 }
498
499 private function formatHeadItems( $headItems ) {
500 $result = array();
501 foreach ( $headItems as $tag => $content ) {
502 $entry = array();
503 $entry['tag'] = $tag;
504 $this->getResult()->setContent( $entry, $content );
505 $result[] = $entry;
506 }
507 return $result;
508 }
509
510 private function formatProperties( $properties ) {
511 $result = array();
512 foreach ( $properties as $name => $value ) {
513 $entry = array();
514 $entry['name'] = $name;
515 $this->getResult()->setContent( $entry, $value );
516 $result[] = $entry;
517 }
518 return $result;
519 }
520
521 private function formatCss( $css ) {
522 $result = array();
523 foreach ( $css as $file => $link ) {
524 $entry = array();
525 $entry['file'] = $file;
526 $this->getResult()->setContent( $entry, $link );
527 $result[] = $entry;
528 }
529 return $result;
530 }
531
532 private function setIndexedTagNames( &$array, $mapping ) {
533 foreach ( $mapping as $key => $name ) {
534 if ( isset( $array[$key] ) ) {
535 $this->getResult()->setIndexedTagName( $array[$key], $name );
536 }
537 }
538 }
539
540 public function getAllowedParams() {
541 return array(
542 'title' => array(
543 ApiBase::PARAM_DFLT => 'API',
544 ),
545 'text' => null,
546 'summary' => null,
547 'page' => null,
548 'pageid' => array(
549 ApiBase::PARAM_TYPE => 'integer',
550 ),
551 'redirects' => false,
552 'oldid' => array(
553 ApiBase::PARAM_TYPE => 'integer',
554 ),
555 'prop' => array(
556 ApiBase::PARAM_DFLT => 'text|langlinks|categories|links|templates|images|externallinks|sections|revid|displaytitle|iwlinks|properties',
557 ApiBase::PARAM_ISMULTI => true,
558 ApiBase::PARAM_TYPE => array(
559 'text',
560 'langlinks',
561 'languageshtml',
562 'categories',
563 'categorieshtml',
564 'links',
565 'templates',
566 'images',
567 'externallinks',
568 'sections',
569 'revid',
570 'displaytitle',
571 'headitems',
572 'headhtml',
573 'iwlinks',
574 'wikitext',
575 'properties',
576 )
577 ),
578 'pst' => false,
579 'onlypst' => false,
580 'uselang' => null,
581 'section' => null,
582 'disablepp' => false,
583 'generatexml' => false,
584 'contentformat' => array(
585 ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
586 ),
587 'contentmodel' => array(
588 ApiBase::PARAM_TYPE => ContentHandler::getContentModels(),
589 )
590 );
591 }
592
593 public function getParamDescription() {
594 $p = $this->getModulePrefix();
595 return array(
596 'text' => 'Wikitext to parse',
597 'summary' => 'Summary to parse',
598 'redirects' => "If the {$p}page or the {$p}pageid parameter is set to a redirect, resolve it",
599 'title' => 'Title of page the text belongs to',
600 'page' => "Parse the content of this page. Cannot be used together with {$p}text and {$p}title",
601 'pageid' => "Parse the content of this page. Overrides {$p}page",
602 'oldid' => "Parse the content of this revision. Overrides {$p}page and {$p}pageid",
603 'prop' => array(
604 'Which pieces of information to get',
605 ' text - Gives the parsed text of the wikitext',
606 ' langlinks - Gives the language links in the parsed wikitext',
607 ' categories - Gives the categories in the parsed wikitext',
608 ' categorieshtml - Gives the HTML version of the categories',
609 ' languageshtml - Gives the HTML version of the language links',
610 ' links - Gives the internal links in the parsed wikitext',
611 ' templates - Gives the templates in the parsed wikitext',
612 ' images - Gives the images in the parsed wikitext',
613 ' externallinks - Gives the external links in the parsed wikitext',
614 ' sections - Gives the sections in the parsed wikitext',
615 ' revid - Adds the revision ID of the parsed page',
616 ' displaytitle - Adds the title of the parsed wikitext',
617 ' headitems - Gives items to put in the <head> of the page',
618 ' headhtml - Gives parsed <head> of the page',
619 ' iwlinks - Gives interwiki links in the parsed wikitext',
620 ' wikitext - Gives the original wikitext that was parsed',
621 ' properties - Gives various properties defined in the parsed wikitext',
622 ),
623 'pst' => array(
624 'Do a pre-save transform on the input before parsing it',
625 'Ignored if page, pageid or oldid is used'
626 ),
627 'onlypst' => array(
628 'Do a pre-save transform (PST) on the input, but don\'t parse it',
629 'Returns the same wikitext, after a PST has been applied. Ignored if page, pageid or oldid is used'
630 ),
631 'uselang' => 'Which language to parse the request in',
632 'section' => 'Only retrieve the content of this section number',
633 'disablepp' => 'Disable the PP Report from the parser output',
634 'generatexml' => 'Generate XML parse tree',
635 'contentformat' => 'Content serialization format used for the input text',
636 'contentmodel' => 'Content model of the new content',
637 );
638 }
639
640 public function getDescription() {
641 return array(
642 'Parses wikitext and returns parser output',
643 'See the various prop-Modules of action=query to get information from the current version of a page',
644 );
645 }
646
647 public function getPossibleErrors() {
648 return array_merge( parent::getPossibleErrors(), array(
649 array( 'code' => 'params', 'info' => 'The page parameter cannot be used together with the text and title parameters' ),
650 array( 'code' => 'params', 'info' => 'The text parameter should be passed with the title parameter. Should you be using the "page" parameter instead?' ),
651 array( 'code' => 'missingrev', 'info' => 'There is no revision ID oldid' ),
652 array( 'code' => 'permissiondenied', 'info' => 'You don\'t have permission to view deleted revisions' ),
653 array( 'code' => 'missingtitle', 'info' => 'The page you specified doesn\'t exist' ),
654 array( 'code' => 'nosuchsection', 'info' => 'There is no section sectionnumber in page' ),
655 array( 'nosuchpageid' ),
656 array( 'invalidtitle', 'title' ),
657 array( 'code' => 'parseerror', 'info' => 'Failed to parse the given text.' ),
658 array( 'code' => 'notwikitext', 'info' => 'The requested operation is only supported on wikitext content.' ),
659 ) );
660 }
661
662 public function getExamples() {
663 return array(
664 'api.php?action=parse&text={{Project:Sandbox}}'
665 );
666 }
667
668 public function getHelpUrls() {
669 return 'https://www.mediawiki.org/wiki/API:Parsing_wikitext#parse';
670 }
671
672 public function getVersion() {
673 return __CLASS__ . ': $Id$';
674 }
675 }