Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 5 |
CRAP | |
0.00% |
0 / 88 |
ApiGraph | |
0.00% |
0 / 1 |
|
0.00% |
0 / 5 |
342 | |
0.00% |
0 / 88 |
execute | |
0.00% |
0 / 1 |
30 | |
0.00% |
0 / 21 |
|||
getAllowedParams | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 16 |
|||
getExamplesMessages | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 5 |
|||
preprocess | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 16 |
|||
getGraphSpec | |
0.00% |
0 / 1 |
56 | |
0.00% |
0 / 30 |
<?php | |
/** | |
* | |
* @license MIT | |
* @file | |
* | |
* @author Yuri Astrakhan | |
*/ | |
namespace Graph; | |
use ApiBase; | |
use FormatJson; | |
use MediaWiki\MediaWikiServices; | |
use ParserOptions; | |
use Title; | |
use WikiPage; | |
/** | |
* This class implements action=graph api, allowing client-side graphs to get the spec, | |
* regardless of how it is stored (page-props or other storage) | |
* @package Graph | |
*/ | |
class ApiGraph extends ApiBase { | |
public function execute() { | |
$params = $this->extractRequestParams(); | |
$this->requireOnlyOneParameter( $params, 'title', 'text' ); | |
if ( $params['title'] !== null ) { | |
if ( $params['hash'] === null ) { | |
$this->dieWithError( [ 'apierror-invalidparammix-mustusewith', 'title', 'hash' ], | |
'missingparam' ); | |
} | |
$graph = $this->getGraphSpec( $params['title'], $params['oldid'], $params['hash'] ); | |
} else { | |
if ( !$this->getRequest()->wasPosted() ) { | |
$this->dieWithError( 'apierror-graph-mustposttext', 'mustposttext' ); | |
} | |
if ( $params['hash'] !== null ) { | |
$this->dieWithError( [ 'apierror-invalidparammix-cannotusewith', 'hash', 'text' ], | |
'invalidparammix' ); | |
} | |
$graph = $this->preprocess( $params['text'] ); | |
} | |
$this->getMain()->setCacheMode( 'public' ); | |
$this->getResult()->addValue( null, $this->getModuleName(), $graph ); | |
} | |
/** | |
* @inheritDoc | |
*/ | |
public function getAllowedParams() { | |
return [ | |
'hash' => [ | |
ApiBase::PARAM_TYPE => 'string', | |
], | |
'title' => [ | |
ApiBase::PARAM_TYPE => 'string', | |
], | |
'text' => [ | |
ApiBase::PARAM_TYPE => 'string', | |
], | |
'oldid' => [ | |
ApiBase::PARAM_TYPE => 'integer', | |
ApiBase::PARAM_DFLT => 0 | |
], | |
]; | |
} | |
/** | |
* @inheritDoc | |
*/ | |
protected function getExamplesMessages() { | |
return [ | |
'formatversion=2&action=graph&title=Extension%3AGraph%2FDemo' . | |
'&hash=1533aaad45c733dcc7e07614b54cbae4119a6747' => 'apihelp-graph-example', | |
]; | |
} | |
/** | |
* Parse graph definition that may contain wiki markup into pure json | |
* @param string $text | |
* @return string | |
*/ | |
private function preprocess( $text ) { | |
$title = Title::makeTitle( NS_SPECIAL, Sandbox::PAGENAME )->fixSpecialName(); | |
$text = MediaWikiServices::getInstance()->getParser()->getFreshParser() | |
->preprocess( $text, $title, new ParserOptions( $this->getUser() ) ); | |
$st = FormatJson::parse( $text ); | |
if ( !$st->isOK() ) { | |
// Sometimes we get <graph ...> {...} </graph> as input. Try to strip <graph> tags | |
$count = 0; | |
$text = preg_replace( '/^\s*<graph[^>]*>(.*)<\/graph>\s*$/s', '$1', $text, 1, $count ); | |
if ( $count === 1 ) { | |
$st = FormatJson::parse( $text ); | |
} | |
if ( !$st->isOK() ) { | |
$this->dieWithError( 'apierror-graph-invalid', 'invalidtext' ); | |
} | |
} | |
return $st->getValue(); | |
} | |
/** | |
* Get graph definition with title and hash | |
* @param string $titleText | |
* @param int $revId | |
* @param string $hash | |
* @return mixed Decoded graph spec from the DB or the stash | |
*/ | |
private function getGraphSpec( $titleText, $revId, $hash ) { | |
$title = Title::newFromText( $titleText ); | |
if ( !$title ) { | |
$this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $titleText ) ] ); | |
} | |
// @phan-suppress-next-line PhanTypeMismatchArgumentNullable T240141 | |
$page = WikiPage::factory( $title ); | |
if ( !$page->exists() ) { | |
$this->dieWithError( 'apierror-missingtitle' ); | |
} | |
// @phan-suppress-next-line PhanTypeMismatchArgumentNullable T240141 | |
$this->checkTitleUserPermissions( $title, 'read' ); | |
// Use caching to avoid parses for old revisions and I/O for current revisions | |
$cache = MediaWikiServices::getInstance()->getMainWANObjectCache(); | |
$graph = $cache->getWithSetCallback( | |
$cache->makeKey( 'graph-data', $hash, $page->getTouched() ), | |
$cache::TTL_DAY, | |
static function ( $oldValue, &$ttl ) use ( $page, $revId, $hash ) { | |
$value = false; | |
$parserOptions = ParserOptions::newCanonical( 'canonical' ); | |
$parserOutput = $page->getParserOutput( $parserOptions, $revId ); | |
if ( $parserOutput !== false ) { | |
$allGraphs = $parserOutput->getExtensionData( 'graph_specs' ); | |
if ( is_array( $allGraphs ) && array_key_exists( $hash, $allGraphs ) ) { | |
$value = $allGraphs[$hash]; | |
} | |
} | |
return $value; | |
} | |
); | |
if ( !$graph ) { | |
$this->dieWithError( 'apierror-graph-missing', 'invalidhash' ); | |
} | |
return $graph; | |
} | |
} |