MediaWiki
REL1_19
|
00001 <?php 00028 class ApiParse extends ApiBase { 00029 private $section, $text, $pstText = null; 00030 00031 public function __construct( $main, $action ) { 00032 parent::__construct( $main, $action ); 00033 } 00034 00035 public function execute() { 00036 // The data is hot but user-dependent, like page views, so we set vary cookies 00037 $this->getMain()->setCacheMode( 'anon-public-user-private' ); 00038 00039 // Get parameters 00040 $params = $this->extractRequestParams(); 00041 $text = $params['text']; 00042 $title = $params['title']; 00043 $page = $params['page']; 00044 $pageid = $params['pageid']; 00045 $oldid = $params['oldid']; 00046 00047 if ( !is_null( $page ) && ( !is_null( $text ) || $title != 'API' ) ) { 00048 $this->dieUsage( 'The page parameter cannot be used together with the text and title parameters', 'params' ); 00049 } 00050 00051 $prop = array_flip( $params['prop'] ); 00052 00053 if ( isset( $params['section'] ) ) { 00054 $this->section = $params['section']; 00055 } else { 00056 $this->section = false; 00057 } 00058 00059 // The parser needs $wgTitle to be set, apparently the 00060 // $title parameter in Parser::parse isn't enough *sigh* 00061 // TODO: Does this still need $wgTitle? 00062 global $wgParser, $wgTitle, $wgLang; 00063 00064 // Currently unnecessary, code to act as a safeguard against any change in current behaviour of uselang breaks 00065 $oldLang = null; 00066 if ( isset( $params['uselang'] ) && $params['uselang'] != $wgLang->getCode() ) { 00067 $oldLang = $wgLang; // Backup wgLang 00068 $wgLang = Language::factory( $params['uselang'] ); 00069 } 00070 00071 $popts = ParserOptions::newFromContext( $this->getContext() ); 00072 $popts->setTidy( true ); 00073 $popts->enableLimitReport( !$params['disablepp'] ); 00074 00075 $redirValues = null; 00076 00077 // Return result 00078 $result = $this->getResult(); 00079 00080 if ( !is_null( $oldid ) || !is_null( $pageid ) || !is_null( $page ) ) { 00081 if ( !is_null( $oldid ) ) { 00082 // Don't use the parser cache 00083 $rev = Revision::newFromID( $oldid ); 00084 if ( !$rev ) { 00085 $this->dieUsage( "There is no revision ID $oldid", 'missingrev' ); 00086 } 00087 if ( !$rev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) { 00088 $this->dieUsage( "You don't have permission to view deleted revisions", 'permissiondenied' ); 00089 } 00090 00091 $titleObj = $rev->getTitle(); 00092 00093 $wgTitle = $titleObj; 00094 00095 // If for some reason the "oldid" is actually the current revision, it may be cached 00096 if ( $titleObj->getLatestRevID() === intval( $oldid ) ) { 00097 // May get from/save to parser cache 00098 $p_result = $this->getParsedSectionOrText( $titleObj, $popts, $pageid, 00099 isset( $prop['wikitext'] ) ) ; 00100 } else { // This is an old revision, so get the text differently 00101 $this->text = $rev->getText( Revision::FOR_THIS_USER, $this->getUser() ); 00102 00103 if ( $this->section !== false ) { 00104 $this->text = $this->getSectionText( $this->text, 'r' . $rev->getId() ); 00105 } 00106 00107 // Should we save old revision parses to the parser cache? 00108 $p_result = $wgParser->parse( $this->text, $titleObj, $popts ); 00109 } 00110 } else { // Not $oldid, but $pageid or $page 00111 if ( $params['redirects'] ) { 00112 $reqParams = array( 00113 'action' => 'query', 00114 'redirects' => '', 00115 ); 00116 if ( !is_null ( $pageid ) ) { 00117 $reqParams['pageids'] = $pageid; 00118 } else { // $page 00119 $reqParams['titles'] = $page; 00120 } 00121 $req = new FauxRequest( $reqParams ); 00122 $main = new ApiMain( $req ); 00123 $main->execute(); 00124 $data = $main->getResultData(); 00125 $redirValues = isset( $data['query']['redirects'] ) 00126 ? $data['query']['redirects'] 00127 : array(); 00128 $to = $page; 00129 foreach ( (array)$redirValues as $r ) { 00130 $to = $r['to']; 00131 } 00132 $titleObj = Title::newFromText( $to ); 00133 } else { 00134 if ( !is_null ( $pageid ) ) { 00135 $reqParams['pageids'] = $pageid; 00136 $titleObj = Title::newFromID( $pageid ); 00137 } else { // $page 00138 $to = $page; 00139 $titleObj = Title::newFromText( $to ); 00140 } 00141 } 00142 if ( !is_null ( $pageid ) ) { 00143 if ( !$titleObj ) { 00144 // Still throw nosuchpageid error if pageid was provided 00145 $this->dieUsageMsg( array( 'nosuchpageid', $pageid ) ); 00146 } 00147 } elseif ( !$titleObj || !$titleObj->exists() ) { 00148 $this->dieUsage( "The page you specified doesn't exist", 'missingtitle' ); 00149 } 00150 $wgTitle = $titleObj; 00151 00152 if ( isset( $prop['revid'] ) ) { 00153 $oldid = $titleObj->getLatestRevID(); 00154 } 00155 00156 // Potentially cached 00157 $p_result = $this->getParsedSectionOrText( $titleObj, $popts, $pageid, 00158 isset( $prop['wikitext'] ) ) ; 00159 } 00160 } else { // Not $oldid, $pageid, $page. Hence based on $text 00161 00162 if ( is_null( $text ) ) { 00163 $this->dieUsage( 'The text parameter should be passed with the title parameter. Should you be using the "page" parameter instead?', 'params' ); 00164 } 00165 $this->text = $text; 00166 $titleObj = Title::newFromText( $title ); 00167 if ( !$titleObj ) { 00168 $this->dieUsageMsg( array( 'invalidtitle', $title ) ); 00169 } 00170 $wgTitle = $titleObj; 00171 00172 if ( $this->section !== false ) { 00173 $this->text = $this->getSectionText( $this->text, $titleObj->getText() ); 00174 } 00175 00176 if ( $params['pst'] || $params['onlypst'] ) { 00177 $this->pstText = $wgParser->preSaveTransform( $this->text, $titleObj, $this->getUser(), $popts ); 00178 } 00179 if ( $params['onlypst'] ) { 00180 // Build a result and bail out 00181 $result_array = array(); 00182 $result_array['text'] = array(); 00183 $result->setContent( $result_array['text'], $this->pstText ); 00184 if ( isset( $prop['wikitext'] ) ) { 00185 $result_array['wikitext'] = array(); 00186 $result->setContent( $result_array['wikitext'], $this->text ); 00187 } 00188 $result->addValue( null, $this->getModuleName(), $result_array ); 00189 return; 00190 } 00191 // Not cached (save or load) 00192 $p_result = $wgParser->parse( $params['pst'] ? $this->pstText : $this->text, $titleObj, $popts ); 00193 } 00194 00195 $result_array = array(); 00196 00197 $result_array['title'] = $titleObj->getPrefixedText(); 00198 00199 if ( !is_null( $oldid ) ) { 00200 $result_array['revid'] = intval( $oldid ); 00201 } 00202 00203 if ( $params['redirects'] && !is_null( $redirValues ) ) { 00204 $result_array['redirects'] = $redirValues; 00205 } 00206 00207 if ( isset( $prop['text'] ) ) { 00208 $result_array['text'] = array(); 00209 $result->setContent( $result_array['text'], $p_result->getText() ); 00210 } 00211 00212 if ( !is_null( $params['summary'] ) ) { 00213 $result_array['parsedsummary'] = array(); 00214 $result->setContent( $result_array['parsedsummary'], Linker::formatComment( $params['summary'], $titleObj ) ); 00215 } 00216 00217 if ( isset( $prop['langlinks'] ) ) { 00218 $result_array['langlinks'] = $this->formatLangLinks( $p_result->getLanguageLinks() ); 00219 } 00220 if ( isset( $prop['languageshtml'] ) ) { 00221 $languagesHtml = $this->languagesHtml( $p_result->getLanguageLinks() ); 00222 $result_array['languageshtml'] = array(); 00223 $result->setContent( $result_array['languageshtml'], $languagesHtml ); 00224 } 00225 if ( isset( $prop['categories'] ) ) { 00226 $result_array['categories'] = $this->formatCategoryLinks( $p_result->getCategories() ); 00227 } 00228 if ( isset( $prop['categorieshtml'] ) ) { 00229 $categoriesHtml = $this->categoriesHtml( $p_result->getCategories() ); 00230 $result_array['categorieshtml'] = array(); 00231 $result->setContent( $result_array['categorieshtml'], $categoriesHtml ); 00232 } 00233 if ( isset( $prop['links'] ) ) { 00234 $result_array['links'] = $this->formatLinks( $p_result->getLinks() ); 00235 } 00236 if ( isset( $prop['templates'] ) ) { 00237 $result_array['templates'] = $this->formatLinks( $p_result->getTemplates() ); 00238 } 00239 if ( isset( $prop['images'] ) ) { 00240 $result_array['images'] = array_keys( $p_result->getImages() ); 00241 } 00242 if ( isset( $prop['externallinks'] ) ) { 00243 $result_array['externallinks'] = array_keys( $p_result->getExternalLinks() ); 00244 } 00245 if ( isset( $prop['sections'] ) ) { 00246 $result_array['sections'] = $p_result->getSections(); 00247 } 00248 00249 if ( isset( $prop['displaytitle'] ) ) { 00250 $result_array['displaytitle'] = $p_result->getDisplayTitle() ? 00251 $p_result->getDisplayTitle() : 00252 $titleObj->getPrefixedText(); 00253 } 00254 00255 if ( isset( $prop['headitems'] ) || isset( $prop['headhtml'] ) ) { 00256 $context = $this->getContext(); 00257 $context->setTitle( $titleObj ); 00258 $context->getOutput()->addParserOutputNoText( $p_result ); 00259 00260 if ( isset( $prop['headitems'] ) ) { 00261 $headItems = $this->formatHeadItems( $p_result->getHeadItems() ); 00262 00263 $css = $this->formatCss( $context->getOutput()->buildCssLinksArray() ); 00264 00265 $scripts = array( $context->getOutput()->getHeadScripts() ); 00266 00267 $result_array['headitems'] = array_merge( $headItems, $css, $scripts ); 00268 } 00269 00270 if ( isset( $prop['headhtml'] ) ) { 00271 $result_array['headhtml'] = array(); 00272 $result->setContent( $result_array['headhtml'], $context->getOutput()->headElement( $context->getSkin() ) ); 00273 } 00274 } 00275 00276 if ( isset( $prop['iwlinks'] ) ) { 00277 $result_array['iwlinks'] = $this->formatIWLinks( $p_result->getInterwikiLinks() ); 00278 } 00279 00280 if ( isset( $prop['wikitext'] ) ) { 00281 $result_array['wikitext'] = array(); 00282 $result->setContent( $result_array['wikitext'], $this->text ); 00283 if ( !is_null( $this->pstText ) ) { 00284 $result_array['psttext'] = array(); 00285 $result->setContent( $result_array['psttext'], $this->pstText ); 00286 } 00287 } 00288 00289 $result_mapping = array( 00290 'redirects' => 'r', 00291 'langlinks' => 'll', 00292 'categories' => 'cl', 00293 'links' => 'pl', 00294 'templates' => 'tl', 00295 'images' => 'img', 00296 'externallinks' => 'el', 00297 'iwlinks' => 'iw', 00298 'sections' => 's', 00299 'headitems' => 'hi', 00300 ); 00301 $this->setIndexedTagNames( $result_array, $result_mapping ); 00302 $result->addValue( null, $this->getModuleName(), $result_array ); 00303 00304 if ( !is_null( $oldLang ) ) { 00305 $wgLang = $oldLang; // Reset $wgLang to $oldLang 00306 } 00307 } 00308 00316 private function getParsedSectionOrText( $titleObj, $popts, $pageId = null, $getWikitext = false ) { 00317 global $wgParser; 00318 00319 $page = WikiPage::factory( $titleObj ); 00320 00321 if ( $this->section !== false ) { 00322 $this->text = $this->getSectionText( $page->getRawText(), !is_null( $pageId ) 00323 ? 'page id ' . $pageId : $titleObj->getText() ); 00324 00325 // Not cached (save or load) 00326 return $wgParser->parse( $this->text, $titleObj, $popts ); 00327 } else { 00328 // Try the parser cache first 00329 // getParserOutput will save to Parser cache if able 00330 $pout = $page->getParserOutput( $popts ); 00331 if ( $getWikitext ) { 00332 $this->text = $page->getRawText(); 00333 } 00334 return $pout; 00335 } 00336 } 00337 00338 private function getSectionText( $text, $what ) { 00339 global $wgParser; 00340 // Not cached (save or load) 00341 $text = $wgParser->getSection( $text, $this->section, false ); 00342 if ( $text === false ) { 00343 $this->dieUsage( "There is no section {$this->section} in " . $what, 'nosuchsection' ); 00344 } 00345 return $text; 00346 } 00347 00348 private function formatLangLinks( $links ) { 00349 $result = array(); 00350 foreach ( $links as $link ) { 00351 $entry = array(); 00352 $bits = explode( ':', $link, 2 ); 00353 $title = Title::newFromText( $link ); 00354 00355 $entry['lang'] = $bits[0]; 00356 if ( $title ) { 00357 $entry['url'] = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT ); 00358 } 00359 $this->getResult()->setContent( $entry, $bits[1] ); 00360 $result[] = $entry; 00361 } 00362 return $result; 00363 } 00364 00365 private function formatCategoryLinks( $links ) { 00366 $result = array(); 00367 foreach ( $links as $link => $sortkey ) { 00368 $entry = array(); 00369 $entry['sortkey'] = $sortkey; 00370 $this->getResult()->setContent( $entry, $link ); 00371 $result[] = $entry; 00372 } 00373 return $result; 00374 } 00375 00376 private function categoriesHtml( $categories ) { 00377 $context = $this->getContext(); 00378 $context->getOutput()->addCategoryLinks( $categories ); 00379 return $context->getSkin()->getCategories(); 00380 } 00381 00388 private function languagesHtml( $languages ) { 00389 wfDeprecated( __METHOD__, '1.18' ); 00390 00391 global $wgContLang, $wgHideInterlanguageLinks; 00392 00393 if ( $wgHideInterlanguageLinks || count( $languages ) == 0 ) { 00394 return ''; 00395 } 00396 00397 $s = htmlspecialchars( wfMsg( 'otherlanguages' ) . wfMsg( 'colon-separator' ) ); 00398 00399 $langs = array(); 00400 foreach ( $languages as $l ) { 00401 $nt = Title::newFromText( $l ); 00402 $text = $wgContLang->getLanguageName( $nt->getInterwiki() ); 00403 00404 $langs[] = Html::element( 'a', 00405 array( 'href' => $nt->getFullURL(), 'title' => $nt->getText(), 'class' => "external" ), 00406 $text == '' ? $l : $text ); 00407 } 00408 00409 $s .= implode( htmlspecialchars( wfMsgExt( 'pipe-separator', 'escapenoentities' ) ), $langs ); 00410 00411 if ( $wgContLang->isRTL() ) { 00412 $s = Html::rawElement( 'span', array( 'dir' => "LTR" ), $s ); 00413 } 00414 00415 return $s; 00416 } 00417 00418 private function formatLinks( $links ) { 00419 $result = array(); 00420 foreach ( $links as $ns => $nslinks ) { 00421 foreach ( $nslinks as $title => $id ) { 00422 $entry = array(); 00423 $entry['ns'] = $ns; 00424 $this->getResult()->setContent( $entry, Title::makeTitle( $ns, $title )->getFullText() ); 00425 if ( $id != 0 ) { 00426 $entry['exists'] = ''; 00427 } 00428 $result[] = $entry; 00429 } 00430 } 00431 return $result; 00432 } 00433 00434 private function formatIWLinks( $iw ) { 00435 $result = array(); 00436 foreach ( $iw as $prefix => $titles ) { 00437 foreach ( array_keys( $titles ) as $title ) { 00438 $entry = array(); 00439 $entry['prefix'] = $prefix; 00440 00441 $title = Title::newFromText( "{$prefix}:{$title}" ); 00442 if ( $title ) { 00443 $entry['url'] = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT ); 00444 } 00445 00446 $this->getResult()->setContent( $entry, $title->getFullText() ); 00447 $result[] = $entry; 00448 } 00449 } 00450 return $result; 00451 } 00452 00453 private function formatHeadItems( $headItems ) { 00454 $result = array(); 00455 foreach ( $headItems as $tag => $content ) { 00456 $entry = array(); 00457 $entry['tag'] = $tag; 00458 $this->getResult()->setContent( $entry, $content ); 00459 $result[] = $entry; 00460 } 00461 return $result; 00462 } 00463 00464 private function formatCss( $css ) { 00465 $result = array(); 00466 foreach ( $css as $file => $link ) { 00467 $entry = array(); 00468 $entry['file'] = $file; 00469 $this->getResult()->setContent( $entry, $link ); 00470 $result[] = $entry; 00471 } 00472 return $result; 00473 } 00474 00475 private function setIndexedTagNames( &$array, $mapping ) { 00476 foreach ( $mapping as $key => $name ) { 00477 if ( isset( $array[$key] ) ) { 00478 $this->getResult()->setIndexedTagName( $array[$key], $name ); 00479 } 00480 } 00481 } 00482 00483 public function getAllowedParams() { 00484 return array( 00485 'title' => array( 00486 ApiBase::PARAM_DFLT => 'API', 00487 ), 00488 'text' => null, 00489 'summary' => null, 00490 'page' => null, 00491 'pageid' => array( 00492 ApiBase::PARAM_TYPE => 'integer', 00493 ), 00494 'redirects' => false, 00495 'oldid' => array( 00496 ApiBase::PARAM_TYPE => 'integer', 00497 ), 00498 'prop' => array( 00499 ApiBase::PARAM_DFLT => 'text|langlinks|categories|links|templates|images|externallinks|sections|revid|displaytitle', 00500 ApiBase::PARAM_ISMULTI => true, 00501 ApiBase::PARAM_TYPE => array( 00502 'text', 00503 'langlinks', 00504 'languageshtml', 00505 'categories', 00506 'categorieshtml', 00507 'links', 00508 'templates', 00509 'images', 00510 'externallinks', 00511 'sections', 00512 'revid', 00513 'displaytitle', 00514 'headitems', 00515 'headhtml', 00516 'iwlinks', 00517 'wikitext', 00518 ) 00519 ), 00520 'pst' => false, 00521 'onlypst' => false, 00522 'uselang' => null, 00523 'section' => null, 00524 'disablepp' => false, 00525 ); 00526 } 00527 00528 public function getParamDescription() { 00529 $p = $this->getModulePrefix(); 00530 return array( 00531 'text' => 'Wikitext to parse', 00532 'summary' => 'Summary to parse', 00533 'redirects' => "If the {$p}page or the {$p}pageid parameter is set to a redirect, resolve it", 00534 'title' => 'Title of page the text belongs to', 00535 'page' => "Parse the content of this page. Cannot be used together with {$p}text and {$p}title", 00536 'pageid' => "Parse the content of this page. Overrides {$p}page", 00537 'oldid' => "Parse the content of this revision. Overrides {$p}page and {$p}pageid", 00538 'prop' => array( 00539 'Which pieces of information to get', 00540 ' text - Gives the parsed text of the wikitext', 00541 ' langlinks - Gives the language links in the parsed wikitext', 00542 ' categories - Gives the categories in the parsed wikitext', 00543 ' categorieshtml - Gives the HTML version of the categories', 00544 ' languageshtml - Gives the HTML version of the language links', 00545 ' links - Gives the internal links in the parsed wikitext', 00546 ' templates - Gives the templates in the parsed wikitext', 00547 ' images - Gives the images in the parsed wikitext', 00548 ' externallinks - Gives the external links in the parsed wikitext', 00549 ' sections - Gives the sections in the parsed wikitext', 00550 ' revid - Adds the revision ID of the parsed page', 00551 ' displaytitle - Adds the title of the parsed wikitext', 00552 ' headitems - Gives items to put in the <head> of the page', 00553 ' headhtml - Gives parsed <head> of the page', 00554 ' iwlinks - Gives interwiki links in the parsed wikitext', 00555 ' wikitext - Gives the original wikitext that was parsed', 00556 ), 00557 'pst' => array( 00558 'Do a pre-save transform on the input before parsing it', 00559 'Ignored if page, pageid or oldid is used' 00560 ), 00561 'onlypst' => array( 00562 'Do a pre-save transform (PST) on the input, but don\'t parse it', 00563 'Returns the same wikitext, after a PST has been applied. Ignored if page, pageid or oldid is used' 00564 ), 00565 'uselang' => 'Which language to parse the request in', 00566 'section' => 'Only retrieve the content of this section number', 00567 'disablepp' => 'Disable the PP Report from the parser output', 00568 ); 00569 } 00570 00571 public function getDescription() { 00572 return 'Parses wikitext and returns parser output'; 00573 } 00574 00575 public function getPossibleErrors() { 00576 return array_merge( parent::getPossibleErrors(), array( 00577 array( 'code' => 'params', 'info' => 'The page parameter cannot be used together with the text and title parameters' ), 00578 array( 'code' => 'params', 'info' => 'The text parameter should be passed with the title parameter. Should you be using the "page" parameter instead?' ), 00579 array( 'code' => 'missingrev', 'info' => 'There is no revision ID oldid' ), 00580 array( 'code' => 'permissiondenied', 'info' => 'You don\'t have permission to view deleted revisions' ), 00581 array( 'code' => 'missingtitle', 'info' => 'The page you specified doesn\'t exist' ), 00582 array( 'code' => 'nosuchsection', 'info' => 'There is no section sectionnumber in page' ), 00583 array( 'nosuchpageid' ), 00584 array( 'invalidtitle', 'title' ), 00585 ) ); 00586 } 00587 00588 public function getExamples() { 00589 return array( 00590 'api.php?action=parse&text={{Project:Sandbox}}' 00591 ); 00592 } 00593 00594 public function getHelpUrls() { 00595 return 'https://www.mediawiki.org/wiki/API:Parsing_wikitext#parse'; 00596 } 00597 00598 public function getVersion() { 00599 return __CLASS__ . ': $Id$'; 00600 } 00601 }