MediaWiki  1.34.0
ApiHelp.php
Go to the documentation of this file.
1 <?php
23 use HtmlFormatter\HtmlFormatter;
25 
32 class ApiHelp extends ApiBase {
33  public function execute() {
34  $params = $this->extractRequestParams();
35  $modules = [];
36 
37  foreach ( $params['modules'] as $path ) {
38  $modules[] = $this->getModuleFromPath( $path );
39  }
40 
41  // Get the help
42  $context = new DerivativeContext( $this->getMain()->getContext() );
43  $skinFactory = MediaWikiServices::getInstance()->getSkinFactory();
44  $context->setSkin( $skinFactory->makeSkin( 'apioutput' ) );
45  $context->setLanguage( $this->getMain()->getLanguage() );
46  $context->setTitle( SpecialPage::getTitleFor( 'ApiHelp' ) );
47  $out = new OutputPage( $context );
48  $out->setRobotPolicy( 'noindex,nofollow' );
49  $out->setCopyrightUrl( 'https://www.mediawiki.org/wiki/Special:MyLanguage/Copyright' );
50  $context->setOutput( $out );
51 
52  self::getHelp( $context, $modules, $params );
53 
54  // Grab the output from the skin
55  ob_start();
56  $context->getOutput()->output();
57  $html = ob_get_clean();
58 
59  $result = $this->getResult();
60  if ( $params['wrap'] ) {
61  $data = [
62  'mime' => 'text/html',
63  'filename' => 'api-help.html',
64  'help' => $html,
65  ];
66  ApiResult::setSubelementsList( $data, 'help' );
67  $result->addValue( null, $this->getModuleName(), $data );
68  } else {
69  // Show any errors at the top of the HTML
70  $transform = [
71  'Types' => [ 'AssocAsObject' => true ],
72  'Strip' => 'all',
73  ];
74  $errors = array_filter( [
75  'errors' => $this->getResult()->getResultData( [ 'errors' ], $transform ),
76  'warnings' => $this->getResult()->getResultData( [ 'warnings' ], $transform ),
77  ] );
78  if ( $errors ) {
79  $json = FormatJson::encode( $errors, true, FormatJson::UTF8_OK );
80  // Escape any "--", some parsers might interpret that as end-of-comment.
81  // The above already escaped any "<" and ">".
82  $json = str_replace( '--', '-\u002D', $json );
83  $html = "<!-- API warnings and errors:\n$json\n-->\n$html";
84  }
85 
86  $result->reset();
87  $result->addValue( null, 'text', $html, ApiResult::NO_SIZE_CHECK );
88  $result->addValue( null, 'mime', 'text/html', ApiResult::NO_SIZE_CHECK );
89  $result->addValue( null, 'filename', 'api-help.html', ApiResult::NO_SIZE_CHECK );
90  }
91  }
92 
112  public static function getHelp( IContextSource $context, $modules, array $options ) {
113  if ( !is_array( $modules ) ) {
114  $modules = [ $modules ];
115  }
116 
117  $out = $context->getOutput();
118  $out->addModuleStyles( [
119  'mediawiki.hlist',
120  'mediawiki.apipretty',
121  ] );
122  if ( !empty( $options['toc'] ) ) {
123  $out->addModuleStyles( 'mediawiki.toc.styles' );
124  }
125  $out->setPageTitle( $context->msg( 'api-help-title' ) );
126 
127  $services = MediaWikiServices::getInstance();
128  $cache = $services->getMainWANObjectCache();
129  $cacheKey = null;
130  if ( count( $modules ) == 1 && $modules[0] instanceof ApiMain &&
131  $options['recursivesubmodules'] &&
132  $context->getLanguage()->equals( $services->getContentLanguage() )
133  ) {
134  $cacheHelpTimeout = $context->getConfig()->get( 'APICacheHelpTimeout' );
135  if ( $cacheHelpTimeout > 0 ) {
136  // Get help text from cache if present
137  $cacheKey = $cache->makeKey( 'apihelp', $modules[0]->getModulePath(),
138  (int)!empty( $options['toc'] ),
139  str_replace( ' ', '_', SpecialVersion::getVersion( 'nodb' ) ) );
140  $cached = $cache->get( $cacheKey );
141  if ( $cached ) {
142  $out->addHTML( $cached );
143  return;
144  }
145  }
146  }
147  if ( $out->getHTML() !== '' ) {
148  // Don't save to cache, there's someone else's content in the page
149  // already
150  $cacheKey = null;
151  }
152 
153  $options['recursivesubmodules'] = !empty( $options['recursivesubmodules'] );
154  $options['submodules'] = $options['recursivesubmodules'] || !empty( $options['submodules'] );
155 
156  // Prepend lead
157  if ( empty( $options['nolead'] ) ) {
158  $msg = $context->msg( 'api-help-lead' );
159  if ( !$msg->isDisabled() ) {
160  $out->addHTML( $msg->parseAsBlock() );
161  }
162  }
163 
164  $haveModules = [];
165  $html = self::getHelpInternal( $context, $modules, $options, $haveModules );
166  if ( !empty( $options['toc'] ) && $haveModules ) {
167  $out->addHTML( Linker::generateTOC( $haveModules, $context->getLanguage() ) );
168  }
169  $out->addHTML( $html );
170 
171  $helptitle = $options['helptitle'] ?? null;
172  $html = self::fixHelpLinks( $out->getHTML(), $helptitle, $haveModules );
173  $out->clearHTML();
174  $out->addHTML( $html );
175 
176  if ( $cacheKey !== null ) {
177  $cache->set( $cacheKey, $out->getHTML(), $cacheHelpTimeout );
178  }
179  }
180 
189  public static function fixHelpLinks( $html, $helptitle = null, $localModules = [] ) {
190  $formatter = new HtmlFormatter( $html );
191  $doc = $formatter->getDoc();
192  $xpath = new DOMXPath( $doc );
193  $nodes = $xpath->query( '//a[@href][not(contains(@class,\'apihelp-linktrail\'))]' );
194  foreach ( $nodes as $node ) {
195  $href = $node->getAttribute( 'href' );
196  do {
197  $old = $href;
198  $href = rawurldecode( $href );
199  } while ( $old !== $href );
200  if ( preg_match( '!Special:ApiHelp/([^&/|#]+)((?:#.*)?)!', $href, $m ) ) {
201  if ( isset( $localModules[$m[1]] ) ) {
202  $href = $m[2] === '' ? '#' . $m[1] : $m[2];
203  } elseif ( $helptitle !== null ) {
204  $href = Title::newFromText( str_replace( '$1', $m[1], $helptitle ) . $m[2] )
205  ->getFullURL();
206  } else {
207  $href = wfAppendQuery( wfScript( 'api' ), [
208  'action' => 'help',
209  'modules' => $m[1],
210  ] ) . $m[2];
211  }
212  $node->setAttribute( 'href', $href );
213  $node->removeAttribute( 'title' );
214  }
215  }
216 
217  return $formatter->getText();
218  }
219 
228  private static function wrap( Message $msg, $class, $tag = 'span' ) {
229  return Html::rawElement( $tag, [ 'class' => $class ],
230  $msg->parse()
231  );
232  }
233 
243  private static function getHelpInternal( IContextSource $context, array $modules,
244  array $options, &$haveModules
245  ) {
246  $out = '';
247 
248  $level = empty( $options['headerlevel'] ) ? 2 : $options['headerlevel'];
249  if ( empty( $options['tocnumber'] ) ) {
250  $tocnumber = [ 2 => 0 ];
251  } else {
252  $tocnumber = &$options['tocnumber'];
253  }
254 
255  foreach ( $modules as $module ) {
256  $tocnumber[$level]++;
257  $path = $module->getModulePath();
258  $module->setContext( $context );
259  $help = [
260  'header' => '',
261  'flags' => '',
262  'description' => '',
263  'help-urls' => '',
264  'parameters' => '',
265  'examples' => '',
266  'submodules' => '',
267  ];
268 
269  if ( empty( $options['noheader'] ) || !empty( $options['toc'] ) ) {
270  $anchor = $path;
271  $i = 1;
272  while ( isset( $haveModules[$anchor] ) ) {
273  $anchor = $path . '|' . ++$i;
274  }
275 
276  if ( $module->isMain() ) {
277  $headerContent = $context->msg( 'api-help-main-header' )->parse();
278  $headerAttr = [
279  'class' => 'apihelp-header',
280  ];
281  } else {
282  $name = $module->getModuleName();
283  $headerContent = $module->getParent()->getModuleManager()->getModuleGroup( $name ) .
284  "=$name";
285  if ( $module->getModulePrefix() !== '' ) {
286  $headerContent .= ' ' .
287  $context->msg( 'parentheses', $module->getModulePrefix() )->parse();
288  }
289  // Module names are always in English and not localized,
290  // so English language and direction must be set explicitly,
291  // otherwise parentheses will get broken in RTL wikis
292  $headerAttr = [
293  'class' => 'apihelp-header apihelp-module-name',
294  'dir' => 'ltr',
295  'lang' => 'en',
296  ];
297  }
298 
299  $headerAttr['id'] = $anchor;
300 
301  $haveModules[$anchor] = [
302  'toclevel' => count( $tocnumber ),
303  'level' => $level,
304  'anchor' => $anchor,
305  'line' => $headerContent,
306  'number' => implode( '.', $tocnumber ),
307  'index' => false,
308  ];
309  if ( empty( $options['noheader'] ) ) {
310  $help['header'] .= Html::element(
311  'h' . min( 6, $level ),
312  $headerAttr,
313  $headerContent
314  );
315  }
316  } else {
317  $haveModules[$path] = true;
318  }
319 
320  $links = [];
321  $any = false;
322  for ( $m = $module; $m !== null; $m = $m->getParent() ) {
323  $name = $m->getModuleName();
324  if ( $name === 'main_int' ) {
325  $name = 'main';
326  }
327 
328  if ( count( $modules ) === 1 && $m === $modules[0] &&
329  !( !empty( $options['submodules'] ) && $m->getModuleManager() )
330  ) {
331  $link = Html::element( 'b', [ 'dir' => 'ltr', 'lang' => 'en' ], $name );
332  } else {
333  $link = SpecialPage::getTitleFor( 'ApiHelp', $m->getModulePath() )->getLocalURL();
334  $link = Html::element( 'a',
335  [ 'href' => $link, 'class' => 'apihelp-linktrail', 'dir' => 'ltr', 'lang' => 'en' ],
336  $name
337  );
338  $any = true;
339  }
340  array_unshift( $links, $link );
341  }
342  if ( $any ) {
343  $help['header'] .= self::wrap(
344  $context->msg( 'parentheses' )
345  ->rawParams( $context->getLanguage()->pipeList( $links ) ),
346  'apihelp-linktrail', 'div'
347  );
348  }
349 
350  $flags = $module->getHelpFlags();
351  $help['flags'] .= Html::openElement( 'div',
352  [ 'class' => 'apihelp-block apihelp-flags' ] );
353  $msg = $context->msg( 'api-help-flags' );
354  if ( !$msg->isDisabled() ) {
355  $help['flags'] .= self::wrap(
356  $msg->numParams( count( $flags ) ), 'apihelp-block-head', 'div'
357  );
358  }
359  $help['flags'] .= Html::openElement( 'ul' );
360  foreach ( $flags as $flag ) {
361  $help['flags'] .= Html::rawElement( 'li', null,
362  self::wrap( $context->msg( "api-help-flag-$flag" ), "apihelp-flag-$flag" )
363  );
364  }
365  $sourceInfo = $module->getModuleSourceInfo();
366  if ( $sourceInfo ) {
367  if ( isset( $sourceInfo['namemsg'] ) ) {
368  $extname = $context->msg( $sourceInfo['namemsg'] )->text();
369  } else {
370  // Probably English, so wrap it.
371  $extname = Html::element( 'span', [ 'dir' => 'ltr', 'lang' => 'en' ], $sourceInfo['name'] );
372  }
373  $help['flags'] .= Html::rawElement( 'li', null,
374  self::wrap(
375  $context->msg( 'api-help-source', $extname, $sourceInfo['name'] ),
376  'apihelp-source'
377  )
378  );
379 
380  $link = SpecialPage::getTitleFor( 'Version', 'License/' . $sourceInfo['name'] );
381  if ( isset( $sourceInfo['license-name'] ) ) {
382  $msg = $context->msg( 'api-help-license', $link,
383  Html::element( 'span', [ 'dir' => 'ltr', 'lang' => 'en' ], $sourceInfo['license-name'] )
384  );
385  } elseif ( SpecialVersion::getExtLicenseFileName( dirname( $sourceInfo['path'] ) ) ) {
386  $msg = $context->msg( 'api-help-license-noname', $link );
387  } else {
388  $msg = $context->msg( 'api-help-license-unknown' );
389  }
390  $help['flags'] .= Html::rawElement( 'li', null,
391  self::wrap( $msg, 'apihelp-license' )
392  );
393  } else {
394  $help['flags'] .= Html::rawElement( 'li', null,
395  self::wrap( $context->msg( 'api-help-source-unknown' ), 'apihelp-source' )
396  );
397  $help['flags'] .= Html::rawElement( 'li', null,
398  self::wrap( $context->msg( 'api-help-license-unknown' ), 'apihelp-license' )
399  );
400  }
401  $help['flags'] .= Html::closeElement( 'ul' );
402  $help['flags'] .= Html::closeElement( 'div' );
403 
404  foreach ( $module->getFinalDescription() as $msg ) {
405  $msg->setContext( $context );
406  $help['description'] .= $msg->parseAsBlock();
407  }
408 
409  $urls = $module->getHelpUrls();
410  if ( $urls ) {
411  $help['help-urls'] .= Html::openElement( 'div',
412  [ 'class' => 'apihelp-block apihelp-help-urls' ]
413  );
414  $msg = $context->msg( 'api-help-help-urls' );
415  if ( !$msg->isDisabled() ) {
416  $help['help-urls'] .= self::wrap(
417  $msg->numParams( count( $urls ) ), 'apihelp-block-head', 'div'
418  );
419  }
420  if ( !is_array( $urls ) ) {
421  $urls = [ $urls ];
422  }
423  $help['help-urls'] .= Html::openElement( 'ul' );
424  foreach ( $urls as $url ) {
425  $help['help-urls'] .= Html::rawElement( 'li', null,
426  Html::element( 'a', [ 'href' => $url, 'dir' => 'ltr' ], $url )
427  );
428  }
429  $help['help-urls'] .= Html::closeElement( 'ul' );
430  $help['help-urls'] .= Html::closeElement( 'div' );
431  }
432 
433  $params = $module->getFinalParams( ApiBase::GET_VALUES_FOR_HELP );
434  $dynamicParams = $module->dynamicParameterDocumentation();
435  $groups = [];
436  if ( $params || $dynamicParams !== null ) {
437  $help['parameters'] .= Html::openElement( 'div',
438  [ 'class' => 'apihelp-block apihelp-parameters' ]
439  );
440  $msg = $context->msg( 'api-help-parameters' );
441  if ( !$msg->isDisabled() ) {
442  $help['parameters'] .= self::wrap(
443  $msg->numParams( count( $params ) ), 'apihelp-block-head', 'div'
444  );
445  }
446  $help['parameters'] .= Html::openElement( 'dl' );
447 
448  $descriptions = $module->getFinalParamDescription();
449 
450  foreach ( $params as $name => $settings ) {
451  if ( !is_array( $settings ) ) {
452  $settings = [ ApiBase::PARAM_DFLT => $settings ];
453  }
454 
455  $help['parameters'] .= Html::rawElement( 'dt', null,
456  Html::element( 'span', [ 'dir' => 'ltr', 'lang' => 'en' ], $module->encodeParamName( $name ) )
457  );
458 
459  // Add description
460  $description = [];
461  if ( isset( $descriptions[$name] ) ) {
462  foreach ( $descriptions[$name] as $msg ) {
463  $msg->setContext( $context );
464  $description[] = $msg->parseAsBlock();
465  }
466  }
467 
468  // Add usage info
469  $info = [];
470 
471  // Required?
472  if ( !empty( $settings[ApiBase::PARAM_REQUIRED] ) ) {
473  $info[] = $context->msg( 'api-help-param-required' )->parse();
474  }
475 
476  // Custom info?
477  if ( !empty( $settings[ApiBase::PARAM_HELP_MSG_INFO] ) ) {
478  foreach ( $settings[ApiBase::PARAM_HELP_MSG_INFO] as $i ) {
479  $tag = array_shift( $i );
480  $info[] = $context->msg( "apihelp-{$path}-paraminfo-{$tag}" )
481  ->numParams( count( $i ) )
482  ->params( $context->getLanguage()->commaList( $i ) )
483  ->params( $module->getModulePrefix() )
484  ->parse();
485  }
486  }
487 
488  // Templated?
489  if ( !empty( $settings[ApiBase::PARAM_TEMPLATE_VARS] ) ) {
490  $vars = [];
491  $msg = 'api-help-param-templated-var-first';
492  foreach ( $settings[ApiBase::PARAM_TEMPLATE_VARS] as $k => $v ) {
493  $vars[] = $context->msg( $msg, $k, $module->encodeParamName( $v ) );
494  $msg = 'api-help-param-templated-var';
495  }
496  $info[] = $context->msg( 'api-help-param-templated' )
497  ->numParams( count( $vars ) )
498  ->params( Message::listParam( $vars ) )
499  ->parse();
500  }
501 
502  // Type documentation
503  if ( !isset( $settings[ApiBase::PARAM_TYPE] ) ) {
504  $dflt = $settings[ApiBase::PARAM_DFLT] ?? null;
505  if ( is_bool( $dflt ) ) {
506  $settings[ApiBase::PARAM_TYPE] = 'boolean';
507  } elseif ( is_string( $dflt ) || is_null( $dflt ) ) {
508  $settings[ApiBase::PARAM_TYPE] = 'string';
509  } elseif ( is_int( $dflt ) ) {
510  $settings[ApiBase::PARAM_TYPE] = 'integer';
511  }
512  }
513  if ( isset( $settings[ApiBase::PARAM_TYPE] ) ) {
514  $type = $settings[ApiBase::PARAM_TYPE];
515  $multi = !empty( $settings[ApiBase::PARAM_ISMULTI] );
516  $hintPipeSeparated = true;
517  $count = !empty( $settings[ApiBase::PARAM_ISMULTI_LIMIT2] )
518  ? $settings[ApiBase::PARAM_ISMULTI_LIMIT2] + 1
519  : ApiBase::LIMIT_SML2 + 1;
520 
521  if ( is_array( $type ) ) {
522  $count = count( $type );
523  $deprecatedValues = $settings[ApiBase::PARAM_DEPRECATED_VALUES] ?? [];
524  $links = $settings[ApiBase::PARAM_VALUE_LINKS] ?? [];
525  $values = array_map( function ( $v ) use ( $links, $deprecatedValues ) {
526  $attr = [];
527  if ( $v !== '' ) {
528  // We can't know whether this contains LTR or RTL text.
529  $attr['dir'] = 'auto';
530  }
531  if ( isset( $deprecatedValues[$v] ) ) {
532  $attr['class'] = 'apihelp-deprecated-value';
533  }
534  $ret = $attr ? Html::element( 'span', $attr, $v ) : $v;
535  if ( isset( $links[$v] ) ) {
536  $ret = "[[{$links[$v]}|$ret]]";
537  }
538  return $ret;
539  }, $type );
540  $i = array_search( '', $type, true );
541  if ( $i === false ) {
542  $values = $context->getLanguage()->commaList( $values );
543  } else {
544  unset( $values[$i] );
545  $values = $context->msg( 'api-help-param-list-can-be-empty' )
546  ->numParams( count( $values ) )
547  ->params( $context->getLanguage()->commaList( $values ) )
548  ->parse();
549  }
550  $info[] = $context->msg( 'api-help-param-list' )
551  ->params( $multi ? 2 : 1 )
552  ->params( $values )
553  ->parse();
554  $hintPipeSeparated = false;
555  } else {
556  switch ( $type ) {
557  case 'submodule':
558  $groups[] = $name;
559 
560  if ( isset( $settings[ApiBase::PARAM_SUBMODULE_MAP] ) ) {
561  $map = $settings[ApiBase::PARAM_SUBMODULE_MAP];
562  $defaultAttrs = [];
563  } else {
564  $prefix = $module->isMain() ? '' : ( $module->getModulePath() . '+' );
565  $map = [];
566  foreach ( $module->getModuleManager()->getNames( $name ) as $submoduleName ) {
567  $map[$submoduleName] = $prefix . $submoduleName;
568  }
569  $defaultAttrs = [ 'dir' => 'ltr', 'lang' => 'en' ];
570  }
571  ksort( $map );
572 
573  $submodules = [];
574  $deprecatedSubmodules = [];
575  foreach ( $map as $v => $m ) {
576  $attrs = $defaultAttrs;
577  $arr = &$submodules;
578  try {
579  $submod = $module->getModuleFromPath( $m );
580  if ( $submod && $submod->isDeprecated() ) {
581  $arr = &$deprecatedSubmodules;
582  $attrs['class'] = 'apihelp-deprecated-value';
583  }
584  } catch ( ApiUsageException $ex ) {
585  // Ignore
586  }
587  if ( $attrs ) {
588  $v = Html::element( 'span', $attrs, $v );
589  }
590  $arr[] = "[[Special:ApiHelp/{$m}|{$v}]]";
591  }
592  $submodules = array_merge( $submodules, $deprecatedSubmodules );
593  $count = count( $submodules );
594  $info[] = $context->msg( 'api-help-param-list' )
595  ->params( $multi ? 2 : 1 )
596  ->params( $context->getLanguage()->commaList( $submodules ) )
597  ->parse();
598  $hintPipeSeparated = false;
599  // No type message necessary, we have a list of values.
600  $type = null;
601  break;
602 
603  case 'namespace':
604  $namespaces = MediaWikiServices::getInstance()->
605  getNamespaceInfo()->getValidNamespaces();
606  if ( isset( $settings[ApiBase::PARAM_EXTRA_NAMESPACES] ) &&
607  is_array( $settings[ApiBase::PARAM_EXTRA_NAMESPACES] )
608  ) {
609  $namespaces = array_merge( $namespaces, $settings[ApiBase::PARAM_EXTRA_NAMESPACES] );
610  }
611  sort( $namespaces );
612  $count = count( $namespaces );
613  $info[] = $context->msg( 'api-help-param-list' )
614  ->params( $multi ? 2 : 1 )
615  ->params( $context->getLanguage()->commaList( $namespaces ) )
616  ->parse();
617  $hintPipeSeparated = false;
618  // No type message necessary, we have a list of values.
619  $type = null;
620  break;
621 
622  case 'tags':
624  $count = count( $tags );
625  $info[] = $context->msg( 'api-help-param-list' )
626  ->params( $multi ? 2 : 1 )
627  ->params( $context->getLanguage()->commaList( $tags ) )
628  ->parse();
629  $hintPipeSeparated = false;
630  $type = null;
631  break;
632 
633  case 'limit':
634  if ( isset( $settings[ApiBase::PARAM_MAX2] ) ) {
635  $info[] = $context->msg( 'api-help-param-limit2' )
636  ->numParams( $settings[ApiBase::PARAM_MAX] )
637  ->numParams( $settings[ApiBase::PARAM_MAX2] )
638  ->parse();
639  } else {
640  $info[] = $context->msg( 'api-help-param-limit' )
641  ->numParams( $settings[ApiBase::PARAM_MAX] )
642  ->parse();
643  }
644  break;
645 
646  case 'integer':
647  // Possible messages:
648  // api-help-param-integer-min,
649  // api-help-param-integer-max,
650  // api-help-param-integer-minmax
651  $suffix = '';
652  $min = $max = 0;
653  if ( isset( $settings[ApiBase::PARAM_MIN] ) ) {
654  $suffix .= 'min';
655  $min = $settings[ApiBase::PARAM_MIN];
656  }
657  if ( isset( $settings[ApiBase::PARAM_MAX] ) ) {
658  $suffix .= 'max';
659  $max = $settings[ApiBase::PARAM_MAX];
660  }
661  if ( $suffix !== '' ) {
662  $info[] =
663  $context->msg( "api-help-param-integer-$suffix" )
664  ->params( $multi ? 2 : 1 )
665  ->numParams( $min, $max )
666  ->parse();
667  }
668  break;
669 
670  case 'upload':
671  $info[] = $context->msg( 'api-help-param-upload' )
672  ->parse();
673  // No type message necessary, api-help-param-upload should handle it.
674  $type = null;
675  break;
676 
677  case 'string':
678  case 'text':
679  // Displaying a type message here would be useless.
680  $type = null;
681  break;
682  }
683  }
684 
685  // Add type. Messages for grep: api-help-param-type-limit
686  // api-help-param-type-integer api-help-param-type-boolean
687  // api-help-param-type-timestamp api-help-param-type-user
688  // api-help-param-type-password
689  if ( is_string( $type ) ) {
690  $msg = $context->msg( "api-help-param-type-$type" );
691  if ( !$msg->isDisabled() ) {
692  $info[] = $msg->params( $multi ? 2 : 1 )->parse();
693  }
694  }
695 
696  if ( $multi ) {
697  $extra = [];
698  $lowcount = !empty( $settings[ApiBase::PARAM_ISMULTI_LIMIT1] )
699  ? $settings[ApiBase::PARAM_ISMULTI_LIMIT1]
701  $highcount = !empty( $settings[ApiBase::PARAM_ISMULTI_LIMIT2] )
702  ? $settings[ApiBase::PARAM_ISMULTI_LIMIT2]
704 
705  if ( $hintPipeSeparated ) {
706  $extra[] = $context->msg( 'api-help-param-multi-separate' )->parse();
707  }
708  if ( $count > $lowcount ) {
709  if ( $lowcount === $highcount ) {
710  $msg = $context->msg( 'api-help-param-multi-max-simple' )
711  ->numParams( $lowcount );
712  } else {
713  $msg = $context->msg( 'api-help-param-multi-max' )
714  ->numParams( $lowcount, $highcount );
715  }
716  $extra[] = $msg->parse();
717  }
718  if ( $extra ) {
719  $info[] = implode( ' ', $extra );
720  }
721 
722  $allowAll = $settings[ApiBase::PARAM_ALL] ?? false;
723  if ( $allowAll || $settings[ApiBase::PARAM_TYPE] === 'namespace' ) {
724  if ( $settings[ApiBase::PARAM_TYPE] === 'namespace' ) {
725  $allSpecifier = ApiBase::ALL_DEFAULT_STRING;
726  } else {
727  $allSpecifier = ( is_string( $allowAll ) ? $allowAll : ApiBase::ALL_DEFAULT_STRING );
728  }
729  $info[] = $context->msg( 'api-help-param-multi-all' )
730  ->params( $allSpecifier )
731  ->parse();
732  }
733  }
734  }
735 
736  if ( isset( $settings[self::PARAM_MAX_BYTES] ) ) {
737  $info[] = $context->msg( 'api-help-param-maxbytes' )
738  ->numParams( $settings[self::PARAM_MAX_BYTES] );
739  }
740  if ( isset( $settings[self::PARAM_MAX_CHARS] ) ) {
741  $info[] = $context->msg( 'api-help-param-maxchars' )
742  ->numParams( $settings[self::PARAM_MAX_CHARS] );
743  }
744 
745  // Add default
746  $default = $settings[ApiBase::PARAM_DFLT] ?? null;
747  if ( $default === '' ) {
748  $info[] = $context->msg( 'api-help-param-default-empty' )
749  ->parse();
750  } elseif ( $default !== null && $default !== false ) {
751  // We can't know whether this contains LTR or RTL text.
752  $info[] = $context->msg( 'api-help-param-default' )
753  ->params( Html::element( 'span', [ 'dir' => 'auto' ], $default ) )
754  ->parse();
755  }
756 
757  if ( !array_filter( $description ) ) {
758  $description = [ self::wrap(
759  $context->msg( 'api-help-param-no-description' ),
760  'apihelp-empty'
761  ) ];
762  }
763 
764  // Add "deprecated" flag
765  if ( !empty( $settings[ApiBase::PARAM_DEPRECATED] ) ) {
766  $help['parameters'] .= Html::openElement( 'dd',
767  [ 'class' => 'info' ] );
768  $help['parameters'] .= self::wrap(
769  $context->msg( 'api-help-param-deprecated' ),
770  'apihelp-deprecated', 'strong'
771  );
772  $help['parameters'] .= Html::closeElement( 'dd' );
773  }
774 
775  if ( $description ) {
776  $description = implode( '', $description );
777  $description = preg_replace( '!\s*</([oud]l)>\s*<\1>\s*!', "\n", $description );
778  $help['parameters'] .= Html::rawElement( 'dd',
779  [ 'class' => 'description' ], $description );
780  }
781 
782  foreach ( $info as $i ) {
783  $help['parameters'] .= Html::rawElement( 'dd', [ 'class' => 'info' ], $i );
784  }
785  }
786 
787  if ( $dynamicParams !== null ) {
788  $dynamicParams = ApiBase::makeMessage( $dynamicParams, $context, [
789  $module->getModulePrefix(),
790  $module->getModuleName(),
791  $module->getModulePath()
792  ] );
793  $help['parameters'] .= Html::element( 'dt', null, '*' );
794  $help['parameters'] .= Html::rawElement( 'dd',
795  [ 'class' => 'description' ], $dynamicParams->parse() );
796  }
797 
798  $help['parameters'] .= Html::closeElement( 'dl' );
799  $help['parameters'] .= Html::closeElement( 'div' );
800  }
801 
802  $examples = $module->getExamplesMessages();
803  if ( $examples ) {
804  $help['examples'] .= Html::openElement( 'div',
805  [ 'class' => 'apihelp-block apihelp-examples' ] );
806  $msg = $context->msg( 'api-help-examples' );
807  if ( !$msg->isDisabled() ) {
808  $help['examples'] .= self::wrap(
809  $msg->numParams( count( $examples ) ), 'apihelp-block-head', 'div'
810  );
811  }
812 
813  $help['examples'] .= Html::openElement( 'dl' );
814  foreach ( $examples as $qs => $msg ) {
815  $msg = ApiBase::makeMessage( $msg, $context, [
816  $module->getModulePrefix(),
817  $module->getModuleName(),
818  $module->getModulePath()
819  ] );
820 
821  $link = wfAppendQuery( wfScript( 'api' ), $qs );
822  $sandbox = SpecialPage::getTitleFor( 'ApiSandbox' )->getLocalURL() . '#' . $qs;
823  $help['examples'] .= Html::rawElement( 'dt', null, $msg->parse() );
824  $help['examples'] .= Html::rawElement( 'dd', null,
825  Html::element( 'a', [ 'href' => $link, 'dir' => 'ltr' ], "api.php?$qs" ) . ' ' .
826  Html::rawElement( 'a', [ 'href' => $sandbox ],
827  $context->msg( 'api-help-open-in-apisandbox' )->parse() )
828  );
829  }
830  $help['examples'] .= Html::closeElement( 'dl' );
831  $help['examples'] .= Html::closeElement( 'div' );
832  }
833 
834  $subtocnumber = $tocnumber;
835  $subtocnumber[$level + 1] = 0;
836  $suboptions = [
837  'submodules' => $options['recursivesubmodules'],
838  'headerlevel' => $level + 1,
839  'tocnumber' => &$subtocnumber,
840  'noheader' => false,
841  ] + $options;
842 
843  if ( $options['submodules'] && $module->getModuleManager() ) {
844  $manager = $module->getModuleManager();
845  $submodules = [];
846  foreach ( $groups as $group ) {
847  $names = $manager->getNames( $group );
848  sort( $names );
849  foreach ( $names as $name ) {
850  $submodules[] = $manager->getModule( $name );
851  }
852  }
853  $help['submodules'] .= self::getHelpInternal(
854  $context,
855  $submodules,
856  $suboptions,
857  $haveModules
858  );
859  }
860 
861  $module->modifyHelp( $help, $suboptions, $haveModules );
862 
863  Hooks::run( 'APIHelpModifyOutput', [ $module, &$help, $suboptions, &$haveModules ] );
864 
865  $out .= implode( "\n", $help );
866  }
867 
868  return $out;
869  }
870 
871  public function shouldCheckMaxlag() {
872  return false;
873  }
874 
875  public function isReadMode() {
876  return false;
877  }
878 
879  public function getCustomPrinter() {
880  $params = $this->extractRequestParams();
881  if ( $params['wrap'] ) {
882  return null;
883  }
884 
885  $main = $this->getMain();
886  $errorPrinter = $main->createPrinterByName( $main->getParameter( 'format' ) );
887  return new ApiFormatRaw( $main, $errorPrinter );
888  }
889 
890  public function getAllowedParams() {
891  return [
892  'modules' => [
893  ApiBase::PARAM_DFLT => 'main',
894  ApiBase::PARAM_ISMULTI => true,
895  ],
896  'submodules' => false,
897  'recursivesubmodules' => false,
898  'wrap' => false,
899  'toc' => false,
900  ];
901  }
902 
903  protected function getExamplesMessages() {
904  return [
905  'action=help'
906  => 'apihelp-help-example-main',
907  'action=help&modules=query&submodules=1'
908  => 'apihelp-help-example-submodules',
909  'action=help&recursivesubmodules=1'
910  => 'apihelp-help-example-recursive',
911  'action=help&modules=help'
912  => 'apihelp-help-example-help',
913  'action=help&modules=query+info|query+categorymembers'
914  => 'apihelp-help-example-query',
915  ];
916  }
917 
918  public function getHelpUrls() {
919  return [
920  'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Main_page',
921  'https://www.mediawiki.org/wiki/Special:MyLanguage/API:FAQ',
922  'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Quick_start_guide',
923  ];
924  }
925 }
ApiMain
This is the main API class, used for both external and internal processing.
Definition: ApiMain.php:41
ContextSource\$context
IContextSource $context
Definition: ContextSource.php:33
ApiBase\PARAM_SUBMODULE_MAP
const PARAM_SUBMODULE_MAP
(string[]) When PARAM_TYPE is 'submodule', map parameter values to submodule paths.
Definition: ApiBase.php:172
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:316
ApiUsageException
Exception used to abort API execution with an error.
Definition: ApiUsageException.php:28
ContextSource\getContext
getContext()
Get the base IContextSource object.
Definition: ContextSource.php:40
Linker\generateTOC
static generateTOC( $tree, Language $lang=null)
Generate a table of contents from a section tree.
Definition: Linker.php:1716
ApiBase\PARAM_REQUIRED
const PARAM_REQUIRED
(boolean) Is the parameter required?
Definition: ApiBase.php:118
ApiHelp\getExamplesMessages
getExamplesMessages()
Returns usage examples for this module.
Definition: ApiHelp.php:903
ApiHelp\wrap
static wrap(Message $msg, $class, $tag='span')
Wrap a message in HTML with a class.
Definition: ApiHelp.php:228
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:117
ApiBase\PARAM_ALL
const PARAM_ALL
(boolean|string) When PARAM_TYPE has a defined set of values and PARAM_ISMULTI is true,...
Definition: ApiBase.php:187
true
return true
Definition: router.php:92
ApiHelp\isReadMode
isReadMode()
Indicates whether this module requires read rights.
Definition: ApiHelp.php:875
ApiBase\PARAM_TYPE
const PARAM_TYPE
(string|string[]) Either an array of allowed value strings, or a string type as described below.
Definition: ApiBase.php:94
ApiHelp\getHelpInternal
static getHelpInternal(IContextSource $context, array $modules, array $options, &$haveModules)
Recursively-called function to actually construct the help.
Definition: ApiHelp.php:243
ApiBase\getResult
getResult()
Get the result object.
Definition: ApiBase.php:640
ApiBase\PARAM_ISMULTI_LIMIT1
const PARAM_ISMULTI_LIMIT1
(integer) Maximum number of values, for normal users.
Definition: ApiBase.php:215
SpecialPage\getTitleFor
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,...
Definition: SpecialPage.php:83
ApiBase\makeMessage
static makeMessage( $msg, IContextSource $context, array $params=null)
Create a Message from a string or array.
Definition: ApiBase.php:1800
ApiResult\NO_SIZE_CHECK
const NO_SIZE_CHECK
For addValue() and similar functions, do not check size while adding a value Don't use this unless yo...
Definition: ApiResult.php:58
Message
ApiBase\PARAM_DEPRECATED_VALUES
const PARAM_DEPRECATED_VALUES
(array) When PARAM_TYPE is an array, this indicates which of the values are deprecated.
Definition: ApiBase.php:209
FormatJson\UTF8_OK
const UTF8_OK
Skip escaping most characters above U+007F for readability and compactness.
Definition: FormatJson.php:34
ApiBase
This abstract class implements many basic API functions, and is the base of all API classes.
Definition: ApiBase.php:42
ApiBase\getModuleFromPath
getModuleFromPath( $path)
Get a module from its module path.
Definition: ApiBase.php:602
ContextSource\getLanguage
getLanguage()
Definition: ContextSource.php:128
ApiBase\PARAM_ISMULTI_LIMIT2
const PARAM_ISMULTI_LIMIT2
(integer) Maximum number of values, for users with the apihighimits right.
Definition: ApiBase.php:222
wfAppendQuery
wfAppendQuery( $url, $query)
Append a query string to an existing URL, which may or may not already have query string parameters a...
Definition: GlobalFunctions.php:439
ApiBase\PARAM_DEPRECATED
const PARAM_DEPRECATED
(boolean) Is the parameter deprecated (will show a warning)?
Definition: ApiBase.php:112
ApiBase\PARAM_MIN
const PARAM_MIN
(integer) Lowest value allowed for the parameter, for PARAM_TYPE 'integer' and 'limit'.
Definition: ApiBase.php:106
DerivativeContext
An IContextSource implementation which will inherit context from another source but allow individual ...
Definition: DerivativeContext.php:30
FormatJson\encode
static encode( $value, $pretty=false, $escaping=0)
Returns the JSON representation of a value.
Definition: FormatJson.php:115
MessageLocalizer\msg
msg( $key,... $params)
This is the method for getting translated interface messages.
wfScript
wfScript( $script='index')
Get the path to a specified script file, respecting file extensions; this is a wrapper around $wgScri...
Definition: GlobalFunctions.php:2642
ApiBase\getModulePath
getModulePath()
Get the path to this module.
Definition: ApiBase.php:584
$modules
$modules
Definition: HTMLFormElement.php:13
ApiBase\PARAM_MAX
const PARAM_MAX
(integer) Max value allowed for the parameter, for PARAM_TYPE 'integer' and 'limit'.
Definition: ApiBase.php:97
ApiFormatRaw
Formatter that spits out anything you like with any desired MIME type.
Definition: ApiFormatRaw.php:27
ApiBase\extractRequestParams
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:761
ApiHelp\getHelpUrls
getHelpUrls()
Return links to more detailed help pages about the module.
Definition: ApiHelp.php:918
ApiBase\PARAM_EXTRA_NAMESPACES
const PARAM_EXTRA_NAMESPACES
(int[]) When PARAM_TYPE is 'namespace', include these as additional possible values.
Definition: ApiBase.php:193
ApiBase\ALL_DEFAULT_STRING
const ALL_DEFAULT_STRING
Definition: ApiBase.php:256
SpecialVersion\getExtLicenseFileName
static getExtLicenseFileName( $extDir)
Obtains the full path of an extensions copying or license file if one exists.
Definition: SpecialVersion.php:1084
ApiHelp\getAllowedParams
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
Definition: ApiHelp.php:890
SpecialVersion\getVersion
static getVersion( $flags='', $lang=null)
Return a string of the MediaWiki version with Git revision if available.
Definition: SpecialVersion.php:295
ChangeTags\listExplicitlyDefinedTags
static listExplicitlyDefinedTags()
Lists tags explicitly defined in the change_tag_def table of the database.
Definition: ChangeTags.php:1438
ApiHelp\fixHelpLinks
static fixHelpLinks( $html, $helptitle=null, $localModules=[])
Replace Special:ApiHelp links with links to api.php.
Definition: ApiHelp.php:189
ApiBase\LIMIT_SML2
const LIMIT_SML2
Slow query, apihighlimits limit.
Definition: ApiBase.php:265
ApiBase\GET_VALUES_FOR_HELP
const GET_VALUES_FOR_HELP
getAllowedParams() flag: When set, the result could take longer to generate, but should be more thoro...
Definition: ApiBase.php:272
ApiBase\PARAM_HELP_MSG_INFO
const PARAM_HELP_MSG_INFO
(array) Specify additional information tags for the parameter.
Definition: ApiBase.php:148
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:53
ApiBase\PARAM_VALUE_LINKS
const PARAM_VALUE_LINKS
(string[]) When PARAM_TYPE is an array, this may be an array mapping those values to page titles whic...
Definition: ApiBase.php:155
ApiBase\PARAM_TEMPLATE_VARS
const PARAM_TEMPLATE_VARS
(array) Indicate that this is a templated parameter, and specify replacements.
Definition: ApiBase.php:252
ApiHelp\execute
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
Definition: ApiHelp.php:33
$cache
$cache
Definition: mcc.php:33
ApiHelp\getHelp
static getHelp(IContextSource $context, $modules, array $options)
Generate help for the specified modules.
Definition: ApiHelp.php:112
IContextSource\getConfig
getConfig()
Get the site configuration.
$path
$path
Definition: NoLocalSettings.php:25
ApiBase\PARAM_DFLT
const PARAM_DFLT
(null|boolean|integer|string) Default value of the parameter.
Definition: ApiBase.php:55
ApiBase\getModuleName
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:520
ApiBase\PARAM_ISMULTI
const PARAM_ISMULTI
(boolean) Accept multiple pipe-separated values for this parameter (e.g.
Definition: ApiBase.php:58
ApiHelp\shouldCheckMaxlag
shouldCheckMaxlag()
Indicates if this module needs maxlag to be checked.
Definition: ApiHelp.php:871
ApiBase\PARAM_MAX2
const PARAM_MAX2
(integer) Max value allowed for the parameter for users with the apihighlimits right,...
Definition: ApiBase.php:103
$help
$help
Definition: mcc.php:32
ApiBase\getMain
getMain()
Get the main module.
Definition: ApiBase.php:536
ApiHelp\getCustomPrinter
getCustomPrinter()
If the module may only be used with a certain format module, it should override this method to return...
Definition: ApiHelp.php:879
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200
IContextSource\getOutput
getOutput()
ApiResult\setSubelementsList
static setSubelementsList(array &$arr, $names)
Causes the elements with the specified names to be output as subelements rather than attributes.
Definition: ApiResult.php:565
IContextSource\getLanguage
getLanguage()
ApiBase\LIMIT_SML1
const LIMIT_SML1
Slow query, standard limit.
Definition: ApiBase.php:263
$type
$type
Definition: testCompression.php:48
ApiHelp
Class to output help for an API module.
Definition: ApiHelp.php:32