MediaWiki REL1_39
ApiFormatBase.php
Go to the documentation of this file.
1<?php
26
32abstract class ApiFormatBase extends ApiBase {
33 private $mIsHtml, $mFormat;
34 private $mBuffer, $mDisabled = false;
35 private $mIsWrappedHtml = false;
36 private $mHttpStatus = false;
37 protected $mForceDefaultParams = false;
38
44 public function __construct( ApiMain $main, $format ) {
45 parent::__construct( $main, $format );
46
47 $this->mIsHtml = ( substr( $format, -2, 2 ) === 'fm' ); // ends with 'fm'
48 if ( $this->mIsHtml ) {
49 $this->mFormat = substr( $format, 0, -2 ); // remove ending 'fm'
50 $this->mIsWrappedHtml = $this->getMain()->getCheck( 'wrappedhtml' );
51 } else {
52 $this->mFormat = $format;
53 }
54 $this->mFormat = strtoupper( $this->mFormat );
55 }
56
65 abstract public function getMimeType();
66
74 public function getFilename() {
75 if ( $this->getIsWrappedHtml() ) {
76 return 'api-result-wrapped.json';
77 } elseif ( $this->getIsHtml() ) {
78 return 'api-result.html';
79 } else {
80 $mimeAnalyzer = MediaWikiServices::getInstance()->getMimeAnalyzer();
81 $ext = $mimeAnalyzer->getExtensionFromMimeTypeOrNull( $this->getMimeType() )
82 ?? strtolower( $this->mFormat );
83 return "api-result.$ext";
84 }
85 }
86
91 public function getFormat() {
92 return $this->mFormat;
93 }
94
101 public function getIsHtml() {
102 return $this->mIsHtml;
103 }
104
110 protected function getIsWrappedHtml() {
111 return $this->mIsWrappedHtml;
112 }
113
119 public function disable() {
120 $this->mDisabled = true;
121 }
122
127 public function isDisabled() {
128 return $this->mDisabled;
129 }
130
139 public function canPrintErrors() {
140 return true;
141 }
142
149 public function forceDefaultParams() {
150 $this->mForceDefaultParams = true;
151 }
152
158 protected function getParameterFromSettings( $paramName, $paramSettings, $parseLimit ) {
159 if ( !$this->mForceDefaultParams ) {
160 return parent::getParameterFromSettings( $paramName, $paramSettings, $parseLimit );
161 }
162
163 if ( !is_array( $paramSettings ) ) {
164 return $paramSettings;
165 }
166
167 return $paramSettings[ParamValidator::PARAM_DEFAULT] ?? null;
168 }
169
175 public function setHttpStatus( $code ) {
176 if ( $this->mDisabled ) {
177 return;
178 }
179
180 if ( $this->getIsHtml() ) {
181 $this->mHttpStatus = $code;
182 } else {
183 $this->getMain()->getRequest()->response()->statusHeader( $code );
184 }
185 }
186
191 public function initPrinter( $unused = false ) {
192 if ( $this->mDisabled ) {
193 return;
194 }
195
196 $mime = $this->getIsWrappedHtml()
197 ? 'text/mediawiki-api-prettyprint-wrapped'
198 : ( $this->getIsHtml() ? 'text/html' : $this->getMimeType() );
199
200 // Some printers (ex. Feed) do their own header settings,
201 // in which case $mime will be set to null
202 if ( $mime === null ) {
203 return; // skip any initialization
204 }
205
206 $this->getMain()->getRequest()->response()->header( "Content-Type: $mime; charset=utf-8" );
207
208 // Set X-Frame-Options API results (T41180)
209 $apiFrameOptions = $this->getConfig()->get( MainConfigNames::ApiFrameOptions );
210 if ( $apiFrameOptions ) {
211 $this->getMain()->getRequest()->response()->header( "X-Frame-Options: $apiFrameOptions" );
212 }
213
214 // Set a Content-Disposition header so something downloading an API
215 // response uses a halfway-sensible filename (T128209).
216 $header = 'Content-Disposition: inline';
217 $filename = $this->getFilename();
218 $compatFilename = mb_convert_encoding( $filename, 'ISO-8859-1' );
219 if ( preg_match( '/^[0-9a-zA-Z!#$%&\'*+\-.^_`|~]+$/', $compatFilename ) ) {
220 $header .= '; filename=' . $compatFilename;
221 } else {
222 $header .= '; filename="'
223 . preg_replace( '/([\0-\x1f"\x5c\x7f])/', '\\\\$1', $compatFilename ) . '"';
224 }
225 if ( $compatFilename !== $filename ) {
226 $value = "UTF-8''" . rawurlencode( $filename );
227 // rawurlencode() encodes more characters than RFC 5987 specifies. Unescape the ones it allows.
228 $value = strtr( $value, [
229 '%21' => '!', '%23' => '#', '%24' => '$', '%26' => '&', '%2B' => '+', '%5E' => '^',
230 '%60' => '`', '%7C' => '|',
231 ] );
232 $header .= '; filename*=' . $value;
233 }
234 $this->getMain()->getRequest()->response()->header( $header );
235 }
236
240 public function closePrinter() {
241 if ( $this->mDisabled ) {
242 return;
243 }
244
245 $mime = $this->getMimeType();
246 if ( $this->getIsHtml() && $mime !== null ) {
247 $format = $this->getFormat();
248 $lcformat = strtolower( $format );
249 $result = $this->getBuffer();
250
251 $context = new DerivativeContext( $this->getMain() );
252 $skinFactory = MediaWikiServices::getInstance()->getSkinFactory();
253 $context->setSkin( $skinFactory->makeSkin( 'apioutput' ) );
254 $context->setTitle( SpecialPage::getTitleFor( 'ApiHelp' ) );
255 $out = new OutputPage( $context );
256 $context->setOutput( $out );
257
258 $out->setRobotPolicy( 'noindex,nofollow' );
259 $out->addModuleStyles( 'mediawiki.apipretty' );
260 $out->setPageTitle( $context->msg( 'api-format-title' ) );
261
262 if ( !$this->getIsWrappedHtml() ) {
263 // When the format without suffix 'fm' is defined, there is a non-html version
264 if ( $this->getMain()->getModuleManager()->isDefined( $lcformat, 'format' ) ) {
265 if ( !$this->getRequest()->wasPosted() ) {
266 $nonHtmlUrl = strtok( $this->getRequest()->getFullRequestURL(), '?' )
267 . '?' . $this->getRequest()->appendQueryValue( 'format', $lcformat );
268 $msg = $context->msg( 'api-format-prettyprint-header-hyperlinked' )
269 ->params( $format, $lcformat, $nonHtmlUrl );
270 } else {
271 $msg = $context->msg( 'api-format-prettyprint-header' )->params( $format, $lcformat );
272 }
273 } else {
274 $msg = $context->msg( 'api-format-prettyprint-header-only-html' )->params( $format );
275 }
276
277 $header = $msg->parseAsBlock();
278 $out->addHTML(
279 Html::rawElement( 'div', [ 'class' => 'api-pretty-header' ],
281 )
282 );
283
284 if ( $this->mHttpStatus && $this->mHttpStatus !== 200 ) {
285 $out->addHTML(
286 Html::rawElement( 'div', [ 'class' => [ 'api-pretty-header', 'api-pretty-status' ] ],
287 $this->msg(
288 'api-format-prettyprint-status',
289 $this->mHttpStatus,
290 HttpStatus::getMessage( $this->mHttpStatus )
291 )->parse()
292 )
293 );
294 }
295 }
296
297 if ( $this->getHookRunner()->onApiFormatHighlight( $context, $result, $mime, $format ) ) {
298 $out->addHTML(
299 Html::element( 'pre', [ 'class' => 'api-pretty-content' ], $result )
300 );
301 }
302
303 if ( $this->getIsWrappedHtml() ) {
304 // This is a special output mode mainly intended for ApiSandbox use
305 $time = $this->getMain()->getRequest()->getElapsedTime();
306 echo FormatJson::encode(
307 [
308 'status' => (int)( $this->mHttpStatus ?: 200 ),
309 'statustext' => HttpStatus::getMessage( $this->mHttpStatus ?: 200 ),
310 'html' => $out->getHTML(),
311 'modules' => array_values( array_unique( array_merge(
312 $out->getModules(),
313 $out->getModuleStyles()
314 ) ) ),
315 'continue' => $this->getResult()->getResultData( 'continue' ),
316 'time' => round( $time * 1000 ),
317 ],
318 false, FormatJson::ALL_OK
319 );
320 } else {
321 // API handles its own clickjacking protection.
322 // Note, that $wgBreakFrames will still override $wgApiFrameOptions for format mode.
323 $out->setPreventClickjacking( false );
324 $out->output();
325 }
326 } else {
327 // For non-HTML output, clear all errors that might have been
328 // displayed if display_errors=On
329 ob_clean();
330
331 echo $this->getBuffer();
332 }
333 }
334
339 public function printText( $text ) {
340 $this->mBuffer .= $text;
341 }
342
347 public function getBuffer() {
348 return $this->mBuffer;
349 }
350
351 public function getAllowedParams() {
352 $ret = [];
353 if ( $this->getIsHtml() ) {
354 $ret['wrappedhtml'] = [
355 ParamValidator::PARAM_DEFAULT => false,
356 ApiBase::PARAM_HELP_MSG => 'apihelp-format-param-wrappedhtml',
357 ];
358 }
359 return $ret;
360 }
361
362 protected function getExamplesMessages() {
363 return [
364 'action=query&meta=siteinfo&siprop=namespaces&format=' . $this->getModuleName()
365 => [ 'apihelp-format-example-generic', $this->getFormat() ]
366 ];
367 }
368
369 public function getHelpUrls() {
370 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Data_formats';
371 }
372
373}
374
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:56
getModuleManager()
Get the module manager, or null if this module has no sub-modules.
Definition ApiBase.php:307
getMain()
Get the main module.
Definition ApiBase.php:514
getResult()
Get the result object.
Definition ApiBase.php:629
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition ApiBase.php:163
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:498
getHookRunner()
Get an ApiHookRunner for running core API hooks.
Definition ApiBase.php:711
This is the abstract base class for API formatters.
getHelpUrls()
Return links to more detailed help pages about the module.
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
__construct(ApiMain $main, $format)
If $format ends with 'fm', pretty-print the output in HTML.
getMimeType()
Overriding class returns the MIME type that should be sent to the client.
getFormat()
Get the internal format name.
getFilename()
Return a filename for this module's output.
printText( $text)
Append text to the output buffer.
initPrinter( $unused=false)
Initialize the printer function and prepare the output headers.
disable()
Disable the formatter.
getBuffer()
Get the contents of the buffer.
getIsWrappedHtml()
Returns true when the special wrapped mode is enabled.
canPrintErrors()
Whether this formatter can handle printing API errors.
getExamplesMessages()
Returns usage examples for this module.
isDisabled()
Whether the printer is disabled.
forceDefaultParams()
Ignore request parameters, force a default.
getIsHtml()
Returns true when the HTML pretty-printer should be used.
getParameterFromSettings( $paramName, $paramSettings, $parseLimit)
Overridden to honor $this->forceDefaultParams(), if applicable Using the settings determine the value...
setHttpStatus( $code)
Set the HTTP status code to be used for the response.
closePrinter()
Finish printing and output buffered data.
static fixHelpLinks( $html, $helptitle=null, $localModules=[])
Replace Special:ApiHelp links with links to api.php.
Definition ApiHelp.php:208
This is the main API class, used for both external and internal processing.
Definition ApiMain.php:52
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
An IContextSource implementation which will inherit context from another source but allow individual ...
A class containing constants representing the names of configuration variables.
Service locator for MediaWiki core services.
This is one of the Core classes and should be read at least once by any new developers.
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
Service for formatting and validating API parameters.
$mime
Definition router.php:60
if(!is_readable( $file)) $ext
Definition router.php:48
$header