]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - includes/api/ApiParse.php
MediaWiki 1.17.4
[autoinstalls/mediawiki.git] / includes / api / ApiParse.php
1 <?php
2 /**
3  * API for MediaWiki 1.8+
4  *
5  * Created on Dec 01, 2007
6  *
7  * Copyright © 2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22  * http://www.gnu.org/copyleft/gpl.html
23  *
24  * @file
25  */
26
27 if ( !defined( 'MEDIAWIKI' ) ) {
28         // Eclipse helper - will be ignored in production
29         require_once( "ApiBase.php" );
30 }
31
32 /**
33  * @ingroup API
34  */
35 class ApiParse extends ApiBase {
36
37         private $section, $text, $pstText = 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                 if ( !is_null( $page ) && ( !is_null( $text ) || $title != 'API' ) ) {
56                         $this->dieUsage( 'The page parameter cannot be used together with the text and title parameters', 'params' );
57                 }
58                 $prop = array_flip( $params['prop'] );
59
60                 if ( isset( $params['section'] ) ) {
61                         $this->section = $params['section'];
62                 } else {
63                         $this->section = false;
64                 }
65
66                 // The parser needs $wgTitle to be set, apparently the
67                 // $title parameter in Parser::parse isn't enough *sigh*
68                 global $wgParser, $wgUser, $wgTitle, $wgLang;
69
70                 // Currently unnecessary, code to act as a safeguard against any change in current behaviour of uselang breaks
71                 $oldLang = null;
72                 if ( isset( $params['uselang'] ) && $params['uselang'] != $wgLang->getCode() ) {
73                         $oldLang = $wgLang; // Backup wgLang
74                         $wgLang = Language::factory( $params['uselang'] );
75                 }
76
77                 $popts = new ParserOptions();
78                 $popts->setTidy( true );
79                 $popts->enableLimitReport( !$params['disablepp'] );
80
81                 $redirValues = null;
82
83                 if ( !is_null( $oldid ) || !is_null( $pageid ) || !is_null( $page ) ) {
84
85                         if ( !is_null( $oldid ) ) {
86                                 // Don't use the parser cache
87                                 $rev = Revision::newFromID( $oldid );
88                                 if ( !$rev ) {
89                                         $this->dieUsage( "There is no revision ID $oldid", 'missingrev' );
90                                 }
91                                 if ( !$rev->userCan( Revision::DELETED_TEXT ) ) {
92                                         $this->dieUsage( "You don't have permission to view deleted revisions", 'permissiondenied' );
93                                 }
94
95                                 $titleObj = $rev->getTitle();
96
97                                 $wgTitle = $titleObj;
98
99                                 //If for some reason the "oldid" is actually the current revision, it may be cached
100                                 if ( $titleObj->getLatestRevID() === $oldid ) {
101                                         $articleObj = new Article( $titleObj, 0 );
102
103                                         $p_result = $this->getParsedSectionOrText( $articleObj, $titleObj, $popts, $pageid,
104                                                  isset( $prop['wikitext'] ) ) ;
105
106                                 } else { // This is an old revision, so get the text differently
107                                         $this->text = $rev->getText( Revision::FOR_THIS_USER );
108
109                                         $wgTitle = $titleObj;
110
111                                         if ( $this->section !== false ) {
112                                                 $this->text = $this->getSectionText( $this->text, 'r' . $rev->getId() );
113                                         }
114
115                                         $p_result = $wgParser->parse( $this->text, $titleObj, $popts );
116                                 }
117
118                         } else { // Not $oldid
119
120                                 if ( !is_null ( $pageid ) ) {
121                                         $titleObj = Title::newFromID( $pageid );
122
123                                         if ( !$titleObj ) {
124                                                 $this->dieUsageMsg( array( 'nosuchpageid', $pageid ) );
125                                         }
126                                 } else { // $page
127
128                                         if ( $params['redirects'] ) {
129                                                 $req = new FauxRequest( array(
130                                                         'action' => 'query',
131                                                         'redirects' => '',
132                                                         'titles' => $page
133                                                 ) );
134                                                 $main = new ApiMain( $req );
135                                                 $main->execute();
136                                                 $data = $main->getResultData();
137                                                 $redirValues = @$data['query']['redirects'];
138                                                 $to = $page;
139                                                 foreach ( (array)$redirValues as $r ) {
140                                                         $to = $r['to'];
141                                                 }
142                                         } else {
143                                                 $to = $page;
144                                         }
145                                         $titleObj = Title::newFromText( $to );
146                                         if ( !$titleObj || !$titleObj->exists() ) {
147                                                 $this->dieUsage( "The page you specified doesn't exist", 'missingtitle' );
148                                         }
149                                 }
150                                 $wgTitle = $titleObj;
151
152                                 $articleObj = new Article( $titleObj, 0 );
153                                 if ( isset( $prop['revid'] ) ) {
154                                         $oldid = $articleObj->getRevIdFetched();
155                                 }
156
157                                 $p_result = $this->getParsedSectionOrText( $articleObj, $titleObj, $popts, $pageid,
158                                          isset( $prop['wikitext'] ) ) ;
159                         }
160
161                 } else { // Not $oldid, $pageid, $page. Hence based on $text
162
163                         $this->text = $text;
164                         $titleObj = Title::newFromText( $title );
165                         if ( !$titleObj ) {
166                                 $titleObj = Title::newFromText( 'API' );
167                         }
168                         $wgTitle = $titleObj;
169
170                         if ( $this->section !== false ) {
171                                 $this->text = $this->getSectionText( $this->text, $titleObj->getText() );
172                         }
173
174                         if ( $params['pst'] || $params['onlypst'] ) {
175                                 $this->pstText = $wgParser->preSaveTransform( $this->text, $titleObj, $wgUser, $popts );
176                         }
177                         if ( $params['onlypst'] ) {
178                                 // Build a result and bail out
179                                 $result_array['text'] = array();
180                                 $this->getResult()->setContent( $result_array['text'], $this->pstText );
181                                 if ( isset( $prop['wikitext'] ) ) {
182                                         $result_array['wikitext'] = array();
183                                         $this->getResult()->setContent( $result_array['wikitext'], $this->text );
184                                 }
185                                 $this->getResult()->addValue( null, $this->getModuleName(), $result_array );
186                                 return;
187                         }
188                         $p_result = $wgParser->parse( $params['pst'] ? $this->pstText : $this->text, $titleObj, $popts );
189                 }
190
191                 // Return result
192                 $result = $this->getResult();
193                 $result_array = array();
194                 if ( $params['redirects'] && !is_null( $redirValues ) ) {
195                         $result_array['redirects'] = $redirValues;
196                 }
197
198                 if ( isset( $prop['text'] ) ) {
199                         $result_array['text'] = array();
200                         $result->setContent( $result_array['text'], $p_result->getText() );
201                 }
202
203                 if ( !is_null( $params['summary'] ) ) {
204                         $result_array['parsedsummary'] = array();
205                         $result->setContent( $result_array['parsedsummary'], $wgUser->getSkin()->formatComment( $params['summary'], $titleObj ) );
206                 }
207
208                 if ( isset( $prop['langlinks'] ) ) {
209                         $result_array['langlinks'] = $this->formatLangLinks( $p_result->getLanguageLinks() );
210                 }
211                 if ( isset( $prop['languageshtml'] ) ) {
212                         $languagesHtml = $this->languagesHtml( $p_result->getLanguageLinks() );
213                         $result_array['languageshtml'] = array();
214                         $result->setContent( $result_array['languageshtml'], $languagesHtml );
215                 }
216                 if ( isset( $prop['categories'] ) ) {
217                         $result_array['categories'] = $this->formatCategoryLinks( $p_result->getCategories() );
218                 }
219                 if ( isset( $prop['categorieshtml'] ) ) {
220                         $categoriesHtml = $this->categoriesHtml( $p_result->getCategories() );
221                         $result_array['categorieshtml'] = array();
222                         $result->setContent( $result_array['categorieshtml'], $categoriesHtml );
223                 }
224                 if ( isset( $prop['links'] ) ) {
225                         $result_array['links'] = $this->formatLinks( $p_result->getLinks() );
226                 }
227                 if ( isset( $prop['templates'] ) ) {
228                         $result_array['templates'] = $this->formatLinks( $p_result->getTemplates() );
229                 }
230                 if ( isset( $prop['images'] ) ) {
231                         $result_array['images'] = array_keys( $p_result->getImages() );
232                 }
233                 if ( isset( $prop['externallinks'] ) ) {
234                         $result_array['externallinks'] = array_keys( $p_result->getExternalLinks() );
235                 }
236                 if ( isset( $prop['sections'] ) ) {
237                         $result_array['sections'] = $p_result->getSections();
238                 }
239
240                 if ( isset( $prop['displaytitle'] ) ) {
241                         $result_array['displaytitle'] = $p_result->getDisplayTitle() ?
242                                                         $p_result->getDisplayTitle() :
243                                                         $titleObj->getPrefixedText();
244                 }
245
246                 if ( isset( $prop['headitems'] ) || isset( $prop['headhtml'] ) ) {
247                         $out = new OutputPage;
248                         $out->addParserOutputNoText( $p_result );
249                         $userSkin = $wgUser->getSkin();
250                 }
251
252                 if ( isset( $prop['headitems'] ) ) {
253                         $headItems = $this->formatHeadItems( $p_result->getHeadItems() );
254
255                         $userSkin->setupUserCss( $out );
256                         $css = $this->formatCss( $out->buildCssLinksArray() );
257
258                         $scripts = array( $out->getHeadScripts( $userSkin ) );
259
260                         $result_array['headitems'] = array_merge( $headItems, $css, $scripts );
261                 }
262
263                 if ( isset( $prop['headhtml'] ) ) {
264                         $result_array['headhtml'] = array();
265                         $result->setContent( $result_array['headhtml'], $out->headElement( $userSkin ) );
266                 }
267
268                 if ( isset( $prop['iwlinks'] ) ) {
269                         $result_array['iwlinks'] = $this->formatIWLinks( $p_result->getInterwikiLinks() );
270                 }
271                 
272                 if ( isset( $prop['wikitext'] ) ) {
273                         $result_array['wikitext'] = array();
274                         $result->setContent( $result_array['wikitext'], $this->text );
275                         if ( !is_null( $this->pstText ) ) {
276                                 $result_array['psttext'] = array();
277                                 $result->setContent( $result_array['psttext'], $this->pstText );
278                         }
279                 }
280
281                 if ( !is_null( $oldid ) ) {
282                         $result_array['revid'] = intval( $oldid );
283                 }
284
285                 $result_mapping = array(
286                         'redirects' => 'r',
287                         'langlinks' => 'll',
288                         'categories' => 'cl',
289                         'links' => 'pl',
290                         'templates' => 'tl',
291                         'images' => 'img',
292                         'externallinks' => 'el',
293                         'iwlinks' => 'iw',
294                         'sections' => 's',
295                         'headitems' => 'hi',
296                 );
297                 $this->setIndexedTagNames( $result_array, $result_mapping );
298                 $result->addValue( null, $this->getModuleName(), $result_array );
299
300                 if ( !is_null( $oldLang ) ) {
301                         $wgLang = $oldLang; // Reset $wgLang to $oldLang
302                 }
303         }
304
305         /**
306          * @param  $articleObj Article
307          * @param  $titleObj Title
308          * @param  $popts ParserOptions
309          * @param  $pageId Int
310          * @param  $getWikitext Bool
311          * @return ParserOutput
312          */
313         private function getParsedSectionOrText( $articleObj, $titleObj, $popts, $pageId = null, $getWikitext = false ) {
314                 if ( $this->section !== false ) {
315                         global $wgParser;
316
317                         $this->text = $this->getSectionText( $articleObj->getRawText(), !is_null ( $pageId )
318                                         ? 'page id ' . $pageId : $titleObj->getText() );
319
320                         return $wgParser->parse( $this->text, $titleObj, $popts );
321                 } else {
322                         // Try the parser cache first
323                         $pout = $articleObj->getParserOutput();
324                         if ( $getWikitext ) {
325                                 $rev = Revision::newFromTitle( $titleObj );
326                                 if ( $rev ) {
327                                         $this->text = $rev->getText();
328                                 }
329                         }
330                         return $pout;
331                 }
332         }
333
334         private function getSectionText( $text, $what ) {
335                 global $wgParser;
336                 $text = $wgParser->getSection( $text, $this->section, false );
337                 if ( $text === false ) {
338                         $this->dieUsage( "There is no section {$this->section} in " . $what, 'nosuchsection' );
339                 }
340                 return $text;
341         }
342
343         private function formatLangLinks( $links ) {
344                 $result = array();
345                 foreach ( $links as $link ) {
346                         $entry = array();
347                         $bits = explode( ':', $link, 2 );
348                         $title = Title::newFromText( $link );
349                         
350                         $entry['lang'] = $bits[0];
351                         if ( $title ) {
352                                 $entry['url'] = $title->getFullURL();
353                         }
354                         $this->getResult()->setContent( $entry, $bits[1] );
355                         $result[] = $entry;
356                 }
357                 return $result;
358         }
359
360         private function formatCategoryLinks( $links ) {
361                 $result = array();
362                 foreach ( $links as $link => $sortkey ) {
363                         $entry = array();
364                         $entry['sortkey'] = $sortkey;
365                         $this->getResult()->setContent( $entry, $link );
366                         $result[] = $entry;
367                 }
368                 return $result;
369         }
370
371         private function categoriesHtml( $categories ) {
372                 global $wgOut, $wgUser;
373                 $wgOut->addCategoryLinks( $categories );
374                 $sk = $wgUser->getSkin();
375                 return $sk->getCategories();
376         }
377
378         private function languagesHtml( $languages ) {
379                 global $wgOut, $wgUser;
380                 $wgOut->setLanguageLinks( $languages );
381                 $sk = $wgUser->getSkin();
382                 return $sk->otherLanguages();
383         }
384
385         private function formatLinks( $links ) {
386                 $result = array();
387                 foreach ( $links as $ns => $nslinks ) {
388                         foreach ( $nslinks as $title => $id ) {
389                                 $entry = array();
390                                 $entry['ns'] = $ns;
391                                 $this->getResult()->setContent( $entry, Title::makeTitle( $ns, $title )->getFullText() );
392                                 if ( $id != 0 ) {
393                                         $entry['exists'] = '';
394                                 }
395                                 $result[] = $entry;
396                         }
397                 }
398                 return $result;
399         }
400
401         private function formatIWLinks( $iw ) {
402                 $result = array();
403                 foreach ( $iw as $prefix => $titles ) {
404                         foreach ( array_keys( $titles ) as $title ) {
405                                 $entry = array();
406                                 $entry['prefix'] = $prefix;
407
408                                 $title = Title::newFromText( "{$prefix}:{$title}" );
409                                 if ( $title ) {
410                                         $entry['url'] = $title->getFullURL();
411                                 }
412
413                                 $this->getResult()->setContent( $entry, $title->getFullText() );
414                                 $result[] = $entry;
415                         }
416                 }
417                 return $result;
418         }
419
420         private function formatHeadItems( $headItems ) {
421                 $result = array();
422                 foreach ( $headItems as $tag => $content ) {
423                         $entry = array();
424                         $entry['tag'] = $tag;
425                         $this->getResult()->setContent( $entry, $content );
426                         $result[] = $entry;
427                 }
428                 return $result;
429         }
430
431         private function formatCss( $css ) {
432                 $result = array();
433                 foreach ( $css as $file => $link ) {
434                         $entry = array();
435                         $entry['file'] = $file;
436                         $this->getResult()->setContent( $entry, $link );
437                         $result[] = $entry;
438                 }
439                 return $result;
440         }
441
442         private function setIndexedTagNames( &$array, $mapping ) {
443                 foreach ( $mapping as $key => $name ) {
444                         if ( isset( $array[$key] ) ) {
445                                 $this->getResult()->setIndexedTagName( $array[$key], $name );
446                         }
447                 }
448         }
449
450         public function getAllowedParams() {
451                 return array(
452                         'title' => array(
453                                 ApiBase::PARAM_DFLT => 'API',
454                         ),
455                         'text' => null,
456                         'summary' => null,
457                         'page' => null,
458                         'pageid' => null,
459                         'redirects' => false,
460                         'oldid' => null,
461                         'prop' => array(
462                                 ApiBase::PARAM_DFLT => 'text|langlinks|categories|links|templates|images|externallinks|sections|revid|displaytitle',
463                                 ApiBase::PARAM_ISMULTI => true,
464                                 ApiBase::PARAM_TYPE => array(
465                                         'text',
466                                         'langlinks',
467                                         'languageshtml',
468                                         'categories',
469                                         'categorieshtml',
470                                         'links',
471                                         'templates',
472                                         'images',
473                                         'externallinks',
474                                         'sections',
475                                         'revid',
476                                         'displaytitle',
477                                         'headitems',
478                                         'headhtml',
479                                         'iwlinks',
480                                         'wikitext',
481                                 )
482                         ),
483                         'pst' => false,
484                         'onlypst' => false,
485                         'uselang' => null,
486                         'section' => null,
487                         'disablepp' => false,
488                 );
489         }
490
491         public function getParamDescription() {
492                 $p = $this->getModulePrefix();
493                 return array(
494                         'text' => 'Wikitext to parse',
495                         'summary' => 'Summary to parse',
496                         'redirects' => "If the {$p}page parameter is set to a redirect, resolve it",
497                         'title' => 'Title of page the text belongs to',
498                         'page' => "Parse the content of this page. Cannot be used together with {$p}text and {$p}title",
499                         'pageid' => "Parse the content of this page. Overrides {$p}page",
500                         'oldid' => "Parse the content of this revision. Overrides {$p}page and {$p}pageid",
501                         'prop' => array(
502                                 'Which pieces of information to get',
503                                 ' text           - Gives the parsed text of the wikitext',
504                                 ' langlinks      - Gives the langlinks the parsed wikitext',
505                                 ' categories     - Gives the categories of the parsed wikitext',
506                                 ' categorieshtml - Gives the html version of the categories',
507                                 ' languageshtml  - Gives the html version of the languagelinks',
508                                 ' links          - Gives the internal links in the parsed wikitext',
509                                 ' templates      - Gives the templates in the parsed wikitext',
510                                 ' images         - Gives the images in the parsed wikitext',
511                                 ' externallinks  - Gives the external links in the parsed wikitext',
512                                 ' sections       - Gives the sections in the parsed wikitext',
513                                 ' revid          - Adds the revision id of the parsed page',
514                                 ' displaytitle   - Adds the title of the parsed wikitext',
515                                 ' headitems      - Gives items to put in the <head> of the page',
516                                 ' headhtml       - Gives parsed <head> of the page',
517                                 ' iwlinks        - Gives interwiki links in the parsed wikitext',
518                                 ' wikitext       - Gives the original wikitext that was parsed',
519                         ),
520                         'pst' => array(
521                                 'Do a pre-save transform on the input before parsing it',
522                                 'Ignored if page, pageid or oldid is used'
523                         ),
524                         'onlypst' => array(
525                                 'Do a pre-save transform (PST) on the input, but don\'t parse it',
526                                 'Returns the same wikitext, after a PST has been applied. Ignored if page, pageid or oldid is used'
527                         ),
528                         'uselang' => 'Which language to parse the request in',
529                         'section' => 'Only retrieve the content of this section number',
530                         'disablepp' => 'Disable the PP Report from the parser output',
531                 );
532         }
533
534         public function getDescription() {
535                 return 'This module parses wikitext and returns parser output';
536         }
537
538         public function getPossibleErrors() {
539                 return array_merge( parent::getPossibleErrors(), array(
540                         array( 'code' => 'params', 'info' => 'The page parameter cannot be used together with the text and title parameters' ),
541                         array( 'code' => 'missingrev', 'info' => 'There is no revision ID oldid' ),
542                         array( 'code' => 'permissiondenied', 'info' => 'You don\'t have permission to view deleted revisions' ),
543                         array( 'code' => 'missingtitle', 'info' => 'The page you specified doesn\'t exist' ),
544                         array( 'code' => 'nosuchsection', 'info' => 'There is no section sectionnumber in page' ),
545                         array( 'nosuchpageid' ),
546                 ) );
547         }
548
549         protected function getExamples() {
550                 return array(
551                         'api.php?action=parse&text={{Project:Sandbox}}'
552                 );
553         }
554
555         public function getVersion() {
556                 return __CLASS__ . ': $Id$';
557         }
558 }