MediaWiki  1.30.0
ApiBase.php
Go to the documentation of this file.
1 <?php
28 
41 abstract class ApiBase extends ContextSource {
42 
52  const PARAM_DFLT = 0;
53 
55  const PARAM_ISMULTI = 1;
56 
91  const PARAM_TYPE = 2;
92 
94  const PARAM_MAX = 3;
95 
100  const PARAM_MAX2 = 4;
101 
103  const PARAM_MIN = 5;
104 
107 
109  const PARAM_DEPRECATED = 7;
110 
115  const PARAM_REQUIRED = 8;
116 
122 
128  const PARAM_HELP_MSG = 10;
129 
136 
146 
152  const PARAM_VALUE_LINKS = 13;
153 
161 
169 
176 
183  const PARAM_ALL = 17;
184 
190 
196  const PARAM_SENSITIVE = 19;
197 
206 
212 
219 
222  const ALL_DEFAULT_STRING = '*';
223 
225  const LIMIT_BIG1 = 500;
227  const LIMIT_BIG2 = 5000;
229  const LIMIT_SML1 = 50;
231  const LIMIT_SML2 = 500;
232 
239 
241  private static $extensionInfo = null;
242 
244  private $mMainModule;
247  private $mSlaveDB = null;
248  private $mParamCache = [];
250  private $mModuleSource = false;
251 
257  public function __construct( ApiMain $mainModule, $moduleName, $modulePrefix = '' ) {
258  $this->mMainModule = $mainModule;
259  $this->mModuleName = $moduleName;
260  $this->mModulePrefix = $modulePrefix;
261 
262  if ( !$this->isMain() ) {
263  $this->setContext( $mainModule->getContext() );
264  }
265  }
266 
267  /************************************************************************/
288  abstract public function execute();
289 
295  public function getModuleManager() {
296  return null;
297  }
298 
308  public function getCustomPrinter() {
309  return null;
310  }
311 
323  protected function getExamplesMessages() {
324  // Fall back to old non-localised method
325  $ret = [];
326 
327  $examples = $this->getExamples();
328  if ( $examples ) {
329  if ( !is_array( $examples ) ) {
330  $examples = [ $examples ];
331  } elseif ( $examples && ( count( $examples ) & 1 ) == 0 &&
332  array_keys( $examples ) === range( 0, count( $examples ) - 1 ) &&
333  !preg_match( '/^\s*api\.php\?/', $examples[0] )
334  ) {
335  // Fix up the ugly "even numbered elements are description, odd
336  // numbered elemts are the link" format (see doc for self::getExamples)
337  $tmp = [];
338  $examplesCount = count( $examples );
339  for ( $i = 0; $i < $examplesCount; $i += 2 ) {
340  $tmp[$examples[$i + 1]] = $examples[$i];
341  }
342  $examples = $tmp;
343  }
344 
345  foreach ( $examples as $k => $v ) {
346  if ( is_numeric( $k ) ) {
347  $qs = $v;
348  $msg = '';
349  } else {
350  $qs = $k;
351  $msg = self::escapeWikiText( $v );
352  if ( is_array( $msg ) ) {
353  $msg = implode( ' ', $msg );
354  }
355  }
356 
357  $qs = preg_replace( '/^\s*api\.php\?/', '', $qs );
358  $ret[$qs] = $this->msg( 'api-help-fallback-example', [ $msg ] );
359  }
360  }
361 
362  return $ret;
363  }
364 
370  public function getHelpUrls() {
371  return [];
372  }
373 
386  protected function getAllowedParams( /* $flags = 0 */ ) {
387  // int $flags is not declared because it causes "Strict standards"
388  // warning. Most derived classes do not implement it.
389  return [];
390  }
391 
396  public function shouldCheckMaxlag() {
397  return true;
398  }
399 
404  public function isReadMode() {
405  return true;
406  }
407 
419  public function isWriteMode() {
420  return false;
421  }
422 
427  public function mustBePosted() {
428  return $this->needsToken() !== false;
429  }
430 
436  public function isDeprecated() {
437  return false;
438  }
439 
446  public function isInternal() {
447  return false;
448  }
449 
468  public function needsToken() {
469  return false;
470  }
471 
481  protected function getWebUITokenSalt( array $params ) {
482  return null;
483  }
484 
497  public function getConditionalRequestData( $condition ) {
498  return null;
499  }
500 
503  /************************************************************************/
512  public function getModuleName() {
513  return $this->mModuleName;
514  }
515 
520  public function getModulePrefix() {
521  return $this->mModulePrefix;
522  }
523 
528  public function getMain() {
529  return $this->mMainModule;
530  }
531 
537  public function isMain() {
538  return $this === $this->mMainModule;
539  }
540 
546  public function getParent() {
547  return $this->isMain() ? null : $this->getMain();
548  }
549 
560  public function lacksSameOriginSecurity() {
561  // Main module has this method overridden
562  // Safety - avoid infinite loop:
563  if ( $this->isMain() ) {
564  self::dieDebug( __METHOD__, 'base method was called on main module.' );
565  }
566 
567  return $this->getMain()->lacksSameOriginSecurity();
568  }
569 
576  public function getModulePath() {
577  if ( $this->isMain() ) {
578  return 'main';
579  } elseif ( $this->getParent()->isMain() ) {
580  return $this->getModuleName();
581  } else {
582  return $this->getParent()->getModulePath() . '+' . $this->getModuleName();
583  }
584  }
585 
594  public function getModuleFromPath( $path ) {
595  $module = $this->getMain();
596  if ( $path === 'main' ) {
597  return $module;
598  }
599 
600  $parts = explode( '+', $path );
601  if ( count( $parts ) === 1 ) {
602  // In case the '+' was typed into URL, it resolves as a space
603  $parts = explode( ' ', $path );
604  }
605 
606  $count = count( $parts );
607  for ( $i = 0; $i < $count; $i++ ) {
608  $parent = $module;
609  $manager = $parent->getModuleManager();
610  if ( $manager === null ) {
611  $errorPath = implode( '+', array_slice( $parts, 0, $i ) );
612  $this->dieWithError( [ 'apierror-badmodule-nosubmodules', $errorPath ], 'badmodule' );
613  }
614  $module = $manager->getModule( $parts[$i] );
615 
616  if ( $module === null ) {
617  $errorPath = $i ? implode( '+', array_slice( $parts, 0, $i ) ) : $parent->getModuleName();
618  $this->dieWithError(
619  [ 'apierror-badmodule-badsubmodule', $errorPath, wfEscapeWikiText( $parts[$i] ) ],
620  'badmodule'
621  );
622  }
623  }
624 
625  return $module;
626  }
627 
632  public function getResult() {
633  // Main module has getResult() method overridden
634  // Safety - avoid infinite loop:
635  if ( $this->isMain() ) {
636  self::dieDebug( __METHOD__, 'base method was called on main module. ' );
637  }
638 
639  return $this->getMain()->getResult();
640  }
641 
646  public function getErrorFormatter() {
647  // Main module has getErrorFormatter() method overridden
648  // Safety - avoid infinite loop:
649  if ( $this->isMain() ) {
650  self::dieDebug( __METHOD__, 'base method was called on main module. ' );
651  }
652 
653  return $this->getMain()->getErrorFormatter();
654  }
655 
660  protected function getDB() {
661  if ( !isset( $this->mSlaveDB ) ) {
662  $this->mSlaveDB = wfGetDB( DB_REPLICA, 'api' );
663  }
664 
665  return $this->mSlaveDB;
666  }
667 
672  public function getContinuationManager() {
673  // Main module has getContinuationManager() method overridden
674  // Safety - avoid infinite loop:
675  if ( $this->isMain() ) {
676  self::dieDebug( __METHOD__, 'base method was called on main module. ' );
677  }
678 
679  return $this->getMain()->getContinuationManager();
680  }
681 
686  public function setContinuationManager( $manager ) {
687  // Main module has setContinuationManager() method overridden
688  // Safety - avoid infinite loop:
689  if ( $this->isMain() ) {
690  self::dieDebug( __METHOD__, 'base method was called on main module. ' );
691  }
692 
693  $this->getMain()->setContinuationManager( $manager );
694  }
695 
698  /************************************************************************/
710  public function dynamicParameterDocumentation() {
711  return null;
712  }
713 
721  public function encodeParamName( $paramName ) {
722  if ( is_array( $paramName ) ) {
723  return array_map( function ( $name ) {
724  return $this->mModulePrefix . $name;
725  }, $paramName );
726  } else {
727  return $this->mModulePrefix . $paramName;
728  }
729  }
730 
740  public function extractRequestParams( $parseLimit = true ) {
741  // Cache parameters, for performance and to avoid T26564.
742  if ( !isset( $this->mParamCache[$parseLimit] ) ) {
743  $params = $this->getFinalParams();
744  $results = [];
745 
746  if ( $params ) { // getFinalParams() can return false
747  foreach ( $params as $paramName => $paramSettings ) {
748  $results[$paramName] = $this->getParameterFromSettings(
749  $paramName, $paramSettings, $parseLimit );
750  }
751  }
752  $this->mParamCache[$parseLimit] = $results;
753  }
754 
755  return $this->mParamCache[$parseLimit];
756  }
757 
764  protected function getParameter( $paramName, $parseLimit = true ) {
765  $paramSettings = $this->getFinalParams()[$paramName];
766 
767  return $this->getParameterFromSettings( $paramName, $paramSettings, $parseLimit );
768  }
769 
776  public function requireOnlyOneParameter( $params, $required /*...*/ ) {
777  $required = func_get_args();
778  array_shift( $required );
779 
780  $intersection = array_intersect( array_keys( array_filter( $params,
781  [ $this, 'parameterNotEmpty' ] ) ), $required );
782 
783  if ( count( $intersection ) > 1 ) {
784  $this->dieWithError( [
785  'apierror-invalidparammix',
786  Message::listParam( array_map(
787  function ( $p ) {
788  return '<var>' . $this->encodeParamName( $p ) . '</var>';
789  },
790  array_values( $intersection )
791  ) ),
792  count( $intersection ),
793  ] );
794  } elseif ( count( $intersection ) == 0 ) {
795  $this->dieWithError( [
796  'apierror-missingparam-one-of',
797  Message::listParam( array_map(
798  function ( $p ) {
799  return '<var>' . $this->encodeParamName( $p ) . '</var>';
800  },
801  array_values( $required )
802  ) ),
803  count( $required ),
804  ], 'missingparam' );
805  }
806  }
807 
814  public function requireMaxOneParameter( $params, $required /*...*/ ) {
815  $required = func_get_args();
816  array_shift( $required );
817 
818  $intersection = array_intersect( array_keys( array_filter( $params,
819  [ $this, 'parameterNotEmpty' ] ) ), $required );
820 
821  if ( count( $intersection ) > 1 ) {
822  $this->dieWithError( [
823  'apierror-invalidparammix',
824  Message::listParam( array_map(
825  function ( $p ) {
826  return '<var>' . $this->encodeParamName( $p ) . '</var>';
827  },
828  array_values( $intersection )
829  ) ),
830  count( $intersection ),
831  ] );
832  }
833  }
834 
842  public function requireAtLeastOneParameter( $params, $required /*...*/ ) {
843  $required = func_get_args();
844  array_shift( $required );
845 
846  $intersection = array_intersect(
847  array_keys( array_filter( $params, [ $this, 'parameterNotEmpty' ] ) ),
848  $required
849  );
850 
851  if ( count( $intersection ) == 0 ) {
852  $this->dieWithError( [
853  'apierror-missingparam-at-least-one-of',
854  Message::listParam( array_map(
855  function ( $p ) {
856  return '<var>' . $this->encodeParamName( $p ) . '</var>';
857  },
858  array_values( $required )
859  ) ),
860  count( $required ),
861  ], 'missingparam' );
862  }
863  }
864 
872  public function requirePostedParameters( $params, $prefix = 'prefix' ) {
873  // Skip if $wgDebugAPI is set or we're in internal mode
874  if ( $this->getConfig()->get( 'DebugAPI' ) || $this->getMain()->isInternalMode() ) {
875  return;
876  }
877 
878  $queryValues = $this->getRequest()->getQueryValues();
879  $badParams = [];
880  foreach ( $params as $param ) {
881  if ( $prefix !== 'noprefix' ) {
882  $param = $this->encodeParamName( $param );
883  }
884  if ( array_key_exists( $param, $queryValues ) ) {
885  $badParams[] = $param;
886  }
887  }
888 
889  if ( $badParams ) {
890  $this->dieWithError(
891  [ 'apierror-mustpostparams', join( ', ', $badParams ), count( $badParams ) ]
892  );
893  }
894  }
895 
902  private function parameterNotEmpty( $x ) {
903  return !is_null( $x ) && $x !== false;
904  }
905 
917  public function getTitleOrPageId( $params, $load = false ) {
918  $this->requireOnlyOneParameter( $params, 'title', 'pageid' );
919 
920  $pageObj = null;
921  if ( isset( $params['title'] ) ) {
922  $titleObj = Title::newFromText( $params['title'] );
923  if ( !$titleObj || $titleObj->isExternal() ) {
924  $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['title'] ) ] );
925  }
926  if ( !$titleObj->canExist() ) {
927  $this->dieWithError( 'apierror-pagecannotexist' );
928  }
929  $pageObj = WikiPage::factory( $titleObj );
930  if ( $load !== false ) {
931  $pageObj->loadPageData( $load );
932  }
933  } elseif ( isset( $params['pageid'] ) ) {
934  if ( $load === false ) {
935  $load = 'fromdb';
936  }
937  $pageObj = WikiPage::newFromID( $params['pageid'], $load );
938  if ( !$pageObj ) {
939  $this->dieWithError( [ 'apierror-nosuchpageid', $params['pageid'] ] );
940  }
941  }
942 
943  return $pageObj;
944  }
945 
954  public function getTitleFromTitleOrPageId( $params ) {
955  $this->requireOnlyOneParameter( $params, 'title', 'pageid' );
956 
957  $titleObj = null;
958  if ( isset( $params['title'] ) ) {
959  $titleObj = Title::newFromText( $params['title'] );
960  if ( !$titleObj || $titleObj->isExternal() ) {
961  $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['title'] ) ] );
962  }
963  return $titleObj;
964  } elseif ( isset( $params['pageid'] ) ) {
965  $titleObj = Title::newFromID( $params['pageid'] );
966  if ( !$titleObj ) {
967  $this->dieWithError( [ 'apierror-nosuchpageid', $params['pageid'] ] );
968  }
969  }
970 
971  return $titleObj;
972  }
973 
982  protected function getWatchlistValue( $watchlist, $titleObj, $userOption = null ) {
983  $userWatching = $this->getUser()->isWatched( $titleObj, User::IGNORE_USER_RIGHTS );
984 
985  switch ( $watchlist ) {
986  case 'watch':
987  return true;
988 
989  case 'unwatch':
990  return false;
991 
992  case 'preferences':
993  # If the user is already watching, don't bother checking
994  if ( $userWatching ) {
995  return true;
996  }
997  # If no user option was passed, use watchdefault and watchcreations
998  if ( is_null( $userOption ) ) {
999  return $this->getUser()->getBoolOption( 'watchdefault' ) ||
1000  $this->getUser()->getBoolOption( 'watchcreations' ) && !$titleObj->exists();
1001  }
1002 
1003  # Watch the article based on the user preference
1004  return $this->getUser()->getBoolOption( $userOption );
1005 
1006  case 'nochange':
1007  return $userWatching;
1008 
1009  default:
1010  return $userWatching;
1011  }
1012  }
1013 
1023  protected function getParameterFromSettings( $paramName, $paramSettings, $parseLimit ) {
1024  // Some classes may decide to change parameter names
1025  $encParamName = $this->encodeParamName( $paramName );
1026 
1027  // Shorthand
1028  if ( !is_array( $paramSettings ) ) {
1029  $paramSettings = [
1030  self::PARAM_DFLT => $paramSettings,
1031  ];
1032  }
1033 
1034  $default = isset( $paramSettings[self::PARAM_DFLT] )
1035  ? $paramSettings[self::PARAM_DFLT]
1036  : null;
1037  $multi = isset( $paramSettings[self::PARAM_ISMULTI] )
1038  ? $paramSettings[self::PARAM_ISMULTI]
1039  : false;
1040  $multiLimit1 = isset( $paramSettings[self::PARAM_ISMULTI_LIMIT1] )
1041  ? $paramSettings[self::PARAM_ISMULTI_LIMIT1]
1042  : null;
1043  $multiLimit2 = isset( $paramSettings[self::PARAM_ISMULTI_LIMIT2] )
1044  ? $paramSettings[self::PARAM_ISMULTI_LIMIT2]
1045  : null;
1046  $type = isset( $paramSettings[self::PARAM_TYPE] )
1047  ? $paramSettings[self::PARAM_TYPE]
1048  : null;
1049  $dupes = isset( $paramSettings[self::PARAM_ALLOW_DUPLICATES] )
1050  ? $paramSettings[self::PARAM_ALLOW_DUPLICATES]
1051  : false;
1052  $deprecated = isset( $paramSettings[self::PARAM_DEPRECATED] )
1053  ? $paramSettings[self::PARAM_DEPRECATED]
1054  : false;
1055  $deprecatedValues = isset( $paramSettings[self::PARAM_DEPRECATED_VALUES] )
1056  ? $paramSettings[self::PARAM_DEPRECATED_VALUES]
1057  : [];
1058  $required = isset( $paramSettings[self::PARAM_REQUIRED] )
1059  ? $paramSettings[self::PARAM_REQUIRED]
1060  : false;
1061  $allowAll = isset( $paramSettings[self::PARAM_ALL] )
1062  ? $paramSettings[self::PARAM_ALL]
1063  : false;
1064 
1065  // When type is not given, and no choices, the type is the same as $default
1066  if ( !isset( $type ) ) {
1067  if ( isset( $default ) ) {
1068  $type = gettype( $default );
1069  } else {
1070  $type = 'NULL'; // allow everything
1071  }
1072  }
1073 
1074  if ( $type == 'password' || !empty( $paramSettings[self::PARAM_SENSITIVE] ) ) {
1075  $this->getMain()->markParamsSensitive( $encParamName );
1076  }
1077 
1078  if ( $type == 'boolean' ) {
1079  if ( isset( $default ) && $default !== false ) {
1080  // Having a default value of anything other than 'false' is not allowed
1082  __METHOD__,
1083  "Boolean param $encParamName's default is set to '$default'. " .
1084  'Boolean parameters must default to false.'
1085  );
1086  }
1087 
1088  $value = $this->getMain()->getCheck( $encParamName );
1089  } elseif ( $type == 'upload' ) {
1090  if ( isset( $default ) ) {
1091  // Having a default value is not allowed
1093  __METHOD__,
1094  "File upload param $encParamName's default is set to " .
1095  "'$default'. File upload parameters may not have a default." );
1096  }
1097  if ( $multi ) {
1098  self::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
1099  }
1100  $value = $this->getMain()->getUpload( $encParamName );
1101  if ( !$value->exists() ) {
1102  // This will get the value without trying to normalize it
1103  // (because trying to normalize a large binary file
1104  // accidentally uploaded as a field fails spectacularly)
1105  $value = $this->getMain()->getRequest()->unsetVal( $encParamName );
1106  if ( $value !== null ) {
1107  $this->dieWithError(
1108  [ 'apierror-badupload', $encParamName ],
1109  "badupload_{$encParamName}"
1110  );
1111  }
1112  }
1113  } else {
1114  $value = $this->getMain()->getVal( $encParamName, $default );
1115 
1116  if ( isset( $value ) && $type == 'namespace' ) {
1118  if ( isset( $paramSettings[self::PARAM_EXTRA_NAMESPACES] ) &&
1119  is_array( $paramSettings[self::PARAM_EXTRA_NAMESPACES] )
1120  ) {
1121  $type = array_merge( $type, $paramSettings[self::PARAM_EXTRA_NAMESPACES] );
1122  }
1123  // By default, namespace parameters allow ALL_DEFAULT_STRING to be used to specify
1124  // all namespaces.
1125  $allowAll = true;
1126  }
1127  if ( isset( $value ) && $type == 'submodule' ) {
1128  if ( isset( $paramSettings[self::PARAM_SUBMODULE_MAP] ) ) {
1129  $type = array_keys( $paramSettings[self::PARAM_SUBMODULE_MAP] );
1130  } else {
1131  $type = $this->getModuleManager()->getNames( $paramName );
1132  }
1133  }
1134 
1135  $request = $this->getMain()->getRequest();
1136  $rawValue = $request->getRawVal( $encParamName );
1137  if ( $rawValue === null ) {
1138  $rawValue = $default;
1139  }
1140 
1141  // Preserve U+001F for self::parseMultiValue(), or error out if that won't be called
1142  if ( isset( $value ) && substr( $rawValue, 0, 1 ) === "\x1f" ) {
1143  if ( $multi ) {
1144  // This loses the potential $wgContLang->checkTitleEncoding() transformation
1145  // done by WebRequest for $_GET. Let's call that a feature.
1146  $value = join( "\x1f", $request->normalizeUnicode( explode( "\x1f", $rawValue ) ) );
1147  } else {
1148  $this->dieWithError( 'apierror-badvalue-notmultivalue', 'badvalue_notmultivalue' );
1149  }
1150  }
1151 
1152  // Check for NFC normalization, and warn
1153  if ( $rawValue !== $value ) {
1154  $this->handleParamNormalization( $paramName, $value, $rawValue );
1155  }
1156  }
1157 
1158  $allSpecifier = ( is_string( $allowAll ) ? $allowAll : self::ALL_DEFAULT_STRING );
1159  if ( $allowAll && $multi && is_array( $type ) && in_array( $allSpecifier, $type, true ) ) {
1161  __METHOD__,
1162  "For param $encParamName, PARAM_ALL collides with a possible value" );
1163  }
1164  if ( isset( $value ) && ( $multi || is_array( $type ) ) ) {
1165  $value = $this->parseMultiValue(
1166  $encParamName,
1167  $value,
1168  $multi,
1169  is_array( $type ) ? $type : null,
1170  $allowAll ? $allSpecifier : null,
1171  $multiLimit1,
1172  $multiLimit2
1173  );
1174  }
1175 
1176  // More validation only when choices were not given
1177  // choices were validated in parseMultiValue()
1178  if ( isset( $value ) ) {
1179  if ( !is_array( $type ) ) {
1180  switch ( $type ) {
1181  case 'NULL': // nothing to do
1182  break;
1183  case 'string':
1184  case 'text':
1185  case 'password':
1186  if ( $required && $value === '' ) {
1187  $this->dieWithError( [ 'apierror-missingparam', $paramName ] );
1188  }
1189  break;
1190  case 'integer': // Force everything using intval() and optionally validate limits
1191  $min = isset( $paramSettings[self::PARAM_MIN] ) ? $paramSettings[self::PARAM_MIN] : null;
1192  $max = isset( $paramSettings[self::PARAM_MAX] ) ? $paramSettings[self::PARAM_MAX] : null;
1193  $enforceLimits = isset( $paramSettings[self::PARAM_RANGE_ENFORCE] )
1194  ? $paramSettings[self::PARAM_RANGE_ENFORCE] : false;
1195 
1196  if ( is_array( $value ) ) {
1197  $value = array_map( 'intval', $value );
1198  if ( !is_null( $min ) || !is_null( $max ) ) {
1199  foreach ( $value as &$v ) {
1200  $this->validateLimit( $paramName, $v, $min, $max, null, $enforceLimits );
1201  }
1202  }
1203  } else {
1204  $value = intval( $value );
1205  if ( !is_null( $min ) || !is_null( $max ) ) {
1206  $this->validateLimit( $paramName, $value, $min, $max, null, $enforceLimits );
1207  }
1208  }
1209  break;
1210  case 'limit':
1211  if ( !$parseLimit ) {
1212  // Don't do any validation whatsoever
1213  break;
1214  }
1215  if ( !isset( $paramSettings[self::PARAM_MAX] )
1216  || !isset( $paramSettings[self::PARAM_MAX2] )
1217  ) {
1219  __METHOD__,
1220  "MAX1 or MAX2 are not defined for the limit $encParamName"
1221  );
1222  }
1223  if ( $multi ) {
1224  self::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
1225  }
1226  $min = isset( $paramSettings[self::PARAM_MIN] ) ? $paramSettings[self::PARAM_MIN] : 0;
1227  if ( $value == 'max' ) {
1228  $value = $this->getMain()->canApiHighLimits()
1229  ? $paramSettings[self::PARAM_MAX2]
1230  : $paramSettings[self::PARAM_MAX];
1231  $this->getResult()->addParsedLimit( $this->getModuleName(), $value );
1232  } else {
1233  $value = intval( $value );
1234  $this->validateLimit(
1235  $paramName,
1236  $value,
1237  $min,
1238  $paramSettings[self::PARAM_MAX],
1239  $paramSettings[self::PARAM_MAX2]
1240  );
1241  }
1242  break;
1243  case 'boolean':
1244  if ( $multi ) {
1245  self::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
1246  }
1247  break;
1248  case 'timestamp':
1249  if ( is_array( $value ) ) {
1250  foreach ( $value as $key => $val ) {
1251  $value[$key] = $this->validateTimestamp( $val, $encParamName );
1252  }
1253  } else {
1254  $value = $this->validateTimestamp( $value, $encParamName );
1255  }
1256  break;
1257  case 'user':
1258  if ( is_array( $value ) ) {
1259  foreach ( $value as $key => $val ) {
1260  $value[$key] = $this->validateUser( $val, $encParamName );
1261  }
1262  } else {
1263  $value = $this->validateUser( $value, $encParamName );
1264  }
1265  break;
1266  case 'upload': // nothing to do
1267  break;
1268  case 'tags':
1269  // If change tagging was requested, check that the tags are valid.
1270  if ( !is_array( $value ) && !$multi ) {
1271  $value = [ $value ];
1272  }
1274  if ( !$tagsStatus->isGood() ) {
1275  $this->dieStatus( $tagsStatus );
1276  }
1277  break;
1278  default:
1279  self::dieDebug( __METHOD__, "Param $encParamName's type is unknown - $type" );
1280  }
1281  }
1282 
1283  // Throw out duplicates if requested
1284  if ( !$dupes && is_array( $value ) ) {
1285  $value = array_unique( $value );
1286  }
1287 
1288  // Set a warning if a deprecated parameter has been passed
1289  if ( $deprecated && $value !== false ) {
1290  $feature = $encParamName;
1291  $m = $this;
1292  while ( !$m->isMain() ) {
1293  $p = $m->getParent();
1294  $name = $m->getModuleName();
1295  $param = $p->encodeParamName( $p->getModuleManager()->getModuleGroup( $name ) );
1296  $feature = "{$param}={$name}&{$feature}";
1297  $m = $p;
1298  }
1299  $this->addDeprecation( [ 'apiwarn-deprecation-parameter', $encParamName ], $feature );
1300  }
1301 
1302  // Set a warning if a deprecated parameter value has been passed
1303  $usedDeprecatedValues = $deprecatedValues && $value !== false
1304  ? array_intersect( array_keys( $deprecatedValues ), (array)$value )
1305  : [];
1306  if ( $usedDeprecatedValues ) {
1307  $feature = "$encParamName=";
1308  $m = $this;
1309  while ( !$m->isMain() ) {
1310  $p = $m->getParent();
1311  $name = $m->getModuleName();
1312  $param = $p->encodeParamName( $p->getModuleManager()->getModuleGroup( $name ) );
1313  $feature = "{$param}={$name}&{$feature}";
1314  $m = $p;
1315  }
1316  foreach ( $usedDeprecatedValues as $v ) {
1317  $msg = $deprecatedValues[$v];
1318  if ( $msg === true ) {
1319  $msg = [ 'apiwarn-deprecation-parameter', "$encParamName=$v" ];
1320  }
1321  $this->addDeprecation( $msg, "$feature$v" );
1322  }
1323  }
1324  } elseif ( $required ) {
1325  $this->dieWithError( [ 'apierror-missingparam', $paramName ] );
1326  }
1327 
1328  return $value;
1329  }
1330 
1338  protected function handleParamNormalization( $paramName, $value, $rawValue ) {
1339  $encParamName = $this->encodeParamName( $paramName );
1340  $this->addWarning( [ 'apiwarn-badutf8', $encParamName ] );
1341  }
1342 
1350  protected function explodeMultiValue( $value, $limit ) {
1351  if ( substr( $value, 0, 1 ) === "\x1f" ) {
1352  $sep = "\x1f";
1353  $value = substr( $value, 1 );
1354  } else {
1355  $sep = '|';
1356  }
1357 
1358  return explode( $sep, $value, $limit );
1359  }
1360 
1378  protected function parseMultiValue( $valueName, $value, $allowMultiple, $allowedValues,
1379  $allSpecifier = null, $limit1 = null, $limit2 = null
1380  ) {
1381  if ( ( trim( $value ) === '' || trim( $value ) === "\x1f" ) && $allowMultiple ) {
1382  return [];
1383  }
1384  $limit1 = $limit1 ?: self::LIMIT_SML1;
1385  $limit2 = $limit2 ?: self::LIMIT_SML2;
1386 
1387  // This is a bit awkward, but we want to avoid calling canApiHighLimits()
1388  // because it unstubs $wgUser
1389  $valuesList = $this->explodeMultiValue( $value, $limit2 + 1 );
1390  $sizeLimit = count( $valuesList ) > $limit1 && $this->mMainModule->canApiHighLimits()
1391  ? $limit2
1392  : $limit1;
1393 
1394  if ( $allowMultiple && is_array( $allowedValues ) && $allSpecifier &&
1395  count( $valuesList ) === 1 && $valuesList[0] === $allSpecifier
1396  ) {
1397  return $allowedValues;
1398  }
1399 
1400  if ( self::truncateArray( $valuesList, $sizeLimit ) ) {
1401  $this->addDeprecation(
1402  [ 'apiwarn-toomanyvalues', $valueName, $sizeLimit ],
1403  "too-many-$valueName-for-{$this->getModulePath()}"
1404  );
1405  }
1406 
1407  if ( !$allowMultiple && count( $valuesList ) != 1 ) {
1408  // T35482 - Allow entries with | in them for non-multiple values
1409  if ( in_array( $value, $allowedValues, true ) ) {
1410  return $value;
1411  }
1412 
1413  if ( is_array( $allowedValues ) ) {
1414  $values = array_map( function ( $v ) {
1415  return '<kbd>' . wfEscapeWikiText( $v ) . '</kbd>';
1416  }, $allowedValues );
1417  $this->dieWithError( [
1418  'apierror-multival-only-one-of',
1419  $valueName,
1420  Message::listParam( $values ),
1421  count( $values ),
1422  ], "multival_$valueName" );
1423  } else {
1424  $this->dieWithError( [
1425  'apierror-multival-only-one',
1426  $valueName,
1427  ], "multival_$valueName" );
1428  }
1429  }
1430 
1431  if ( is_array( $allowedValues ) ) {
1432  // Check for unknown values
1433  $unknown = array_map( 'wfEscapeWikiText', array_diff( $valuesList, $allowedValues ) );
1434  if ( count( $unknown ) ) {
1435  if ( $allowMultiple ) {
1436  $this->addWarning( [
1437  'apiwarn-unrecognizedvalues',
1438  $valueName,
1439  Message::listParam( $unknown, 'comma' ),
1440  count( $unknown ),
1441  ] );
1442  } else {
1443  $this->dieWithError(
1444  [ 'apierror-unrecognizedvalue', $valueName, wfEscapeWikiText( $valuesList[0] ) ],
1445  "unknown_$valueName"
1446  );
1447  }
1448  }
1449  // Now throw them out
1450  $valuesList = array_intersect( $valuesList, $allowedValues );
1451  }
1452 
1453  return $allowMultiple ? $valuesList : $valuesList[0];
1454  }
1455 
1466  protected function validateLimit( $paramName, &$value, $min, $max, $botMax = null,
1467  $enforceLimits = false
1468  ) {
1469  if ( !is_null( $min ) && $value < $min ) {
1470  $msg = ApiMessage::create(
1471  [ 'apierror-integeroutofrange-belowminimum',
1472  $this->encodeParamName( $paramName ), $min, $value ],
1473  'integeroutofrange',
1474  [ 'min' => $min, 'max' => $max, 'botMax' => $botMax ?: $max ]
1475  );
1476  $this->warnOrDie( $msg, $enforceLimits );
1477  $value = $min;
1478  }
1479 
1480  // Minimum is always validated, whereas maximum is checked only if not
1481  // running in internal call mode
1482  if ( $this->getMain()->isInternalMode() ) {
1483  return;
1484  }
1485 
1486  // Optimization: do not check user's bot status unless really needed -- skips db query
1487  // assumes $botMax >= $max
1488  if ( !is_null( $max ) && $value > $max ) {
1489  if ( !is_null( $botMax ) && $this->getMain()->canApiHighLimits() ) {
1490  if ( $value > $botMax ) {
1491  $msg = ApiMessage::create(
1492  [ 'apierror-integeroutofrange-abovebotmax',
1493  $this->encodeParamName( $paramName ), $botMax, $value ],
1494  'integeroutofrange',
1495  [ 'min' => $min, 'max' => $max, 'botMax' => $botMax ?: $max ]
1496  );
1497  $this->warnOrDie( $msg, $enforceLimits );
1498  $value = $botMax;
1499  }
1500  } else {
1501  $msg = ApiMessage::create(
1502  [ 'apierror-integeroutofrange-abovemax',
1503  $this->encodeParamName( $paramName ), $max, $value ],
1504  'integeroutofrange',
1505  [ 'min' => $min, 'max' => $max, 'botMax' => $botMax ?: $max ]
1506  );
1507  $this->warnOrDie( $msg, $enforceLimits );
1508  $value = $max;
1509  }
1510  }
1511  }
1512 
1519  protected function validateTimestamp( $value, $encParamName ) {
1520  // Confusing synonyms for the current time accepted by wfTimestamp()
1521  // (wfTimestamp() also accepts various non-strings and the string of 14
1522  // ASCII NUL bytes, but those can't get here)
1523  if ( !$value ) {
1524  $this->addDeprecation(
1525  [ 'apiwarn-unclearnowtimestamp', $encParamName, wfEscapeWikiText( $value ) ],
1526  'unclear-"now"-timestamp'
1527  );
1528  return wfTimestamp( TS_MW );
1529  }
1530 
1531  // Explicit synonym for the current time
1532  if ( $value === 'now' ) {
1533  return wfTimestamp( TS_MW );
1534  }
1535 
1536  $unixTimestamp = wfTimestamp( TS_UNIX, $value );
1537  if ( $unixTimestamp === false ) {
1538  $this->dieWithError(
1539  [ 'apierror-badtimestamp', $encParamName, wfEscapeWikiText( $value ) ],
1540  "badtimestamp_{$encParamName}"
1541  );
1542  }
1543 
1544  return wfTimestamp( TS_MW, $unixTimestamp );
1545  }
1546 
1556  final public function validateToken( $token, array $params ) {
1557  $tokenType = $this->needsToken();
1559  if ( !isset( $salts[$tokenType] ) ) {
1560  throw new MWException(
1561  "Module '{$this->getModuleName()}' tried to use token type '$tokenType' " .
1562  'without registering it'
1563  );
1564  }
1565 
1566  $tokenObj = ApiQueryTokens::getToken(
1567  $this->getUser(), $this->getRequest()->getSession(), $salts[$tokenType]
1568  );
1569  if ( $tokenObj->match( $token ) ) {
1570  return true;
1571  }
1572 
1573  $webUiSalt = $this->getWebUITokenSalt( $params );
1574  if ( $webUiSalt !== null && $this->getUser()->matchEditToken(
1575  $token,
1576  $webUiSalt,
1577  $this->getRequest()
1578  ) ) {
1579  return true;
1580  }
1581 
1582  return false;
1583  }
1584 
1591  private function validateUser( $value, $encParamName ) {
1593  if ( $title === null || $title->hasFragment() ) {
1594  $this->dieWithError(
1595  [ 'apierror-baduser', $encParamName, wfEscapeWikiText( $value ) ],
1596  "baduser_{$encParamName}"
1597  );
1598  }
1599 
1600  return $title->getText();
1601  }
1602 
1605  /************************************************************************/
1616  protected function setWatch( $watch, $titleObj, $userOption = null ) {
1617  $value = $this->getWatchlistValue( $watch, $titleObj, $userOption );
1618  if ( $value === null ) {
1619  return;
1620  }
1621 
1622  WatchAction::doWatchOrUnwatch( $value, $titleObj, $this->getUser() );
1623  }
1624 
1631  public static function truncateArray( &$arr, $limit ) {
1632  $modified = false;
1633  while ( count( $arr ) > $limit ) {
1634  array_pop( $arr );
1635  $modified = true;
1636  }
1637 
1638  return $modified;
1639  }
1640 
1647  public function getWatchlistUser( $params ) {
1648  if ( !is_null( $params['owner'] ) && !is_null( $params['token'] ) ) {
1649  $user = User::newFromName( $params['owner'], false );
1650  if ( !( $user && $user->getId() ) ) {
1651  $this->dieWithError(
1652  [ 'nosuchusershort', wfEscapeWikiText( $params['owner'] ) ], 'bad_wlowner'
1653  );
1654  }
1655  $token = $user->getOption( 'watchlisttoken' );
1656  if ( $token == '' || !hash_equals( $token, $params['token'] ) ) {
1657  $this->dieWithError( 'apierror-bad-watchlist-token', 'bad_wltoken' );
1658  }
1659  } else {
1660  if ( !$this->getUser()->isLoggedIn() ) {
1661  $this->dieWithError( 'watchlistanontext', 'notloggedin' );
1662  }
1663  $this->checkUserRightsAny( 'viewmywatchlist' );
1664  $user = $this->getUser();
1665  }
1666 
1667  return $user;
1668  }
1669 
1677  private static function escapeWikiText( $v ) {
1678  if ( is_array( $v ) ) {
1679  return array_map( 'self::escapeWikiText', $v );
1680  } else {
1681  return strtr( $v, [
1682  '__' => '_&#95;', '{' => '&#123;', '}' => '&#125;',
1683  '[[Category:' => '[[:Category:',
1684  '[[File:' => '[[:File:', '[[Image:' => '[[:Image:',
1685  ] );
1686  }
1687  }
1688 
1701  public static function makeMessage( $msg, IContextSource $context, array $params = null ) {
1702  if ( is_string( $msg ) ) {
1703  $msg = wfMessage( $msg );
1704  } elseif ( is_array( $msg ) ) {
1705  $msg = call_user_func_array( 'wfMessage', $msg );
1706  }
1707  if ( !$msg instanceof Message ) {
1708  return null;
1709  }
1710 
1711  $msg->setContext( $context );
1712  if ( $params ) {
1713  $msg->params( $params );
1714  }
1715 
1716  return $msg;
1717  }
1718 
1726  public function errorArrayToStatus( array $errors, User $user = null ) {
1727  if ( $user === null ) {
1728  $user = $this->getUser();
1729  }
1730 
1732  foreach ( $errors as $error ) {
1733  if ( is_array( $error ) && $error[0] === 'blockedtext' && $user->getBlock() ) {
1734  $status->fatal( ApiMessage::create(
1735  'apierror-blocked',
1736  'blocked',
1737  [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $user->getBlock() ) ]
1738  ) );
1739  } elseif ( is_array( $error ) && $error[0] === 'autoblockedtext' && $user->getBlock() ) {
1740  $status->fatal( ApiMessage::create(
1741  'apierror-autoblocked',
1742  'autoblocked',
1743  [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $user->getBlock() ) ]
1744  ) );
1745  } elseif ( is_array( $error ) && $error[0] === 'systemblockedtext' && $user->getBlock() ) {
1746  $status->fatal( ApiMessage::create(
1747  'apierror-systemblocked',
1748  'blocked',
1749  [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $user->getBlock() ) ]
1750  ) );
1751  } else {
1752  call_user_func_array( [ $status, 'fatal' ], (array)$error );
1753  }
1754  }
1755  return $status;
1756  }
1757 
1760  /************************************************************************/
1779  public function addWarning( $msg, $code = null, $data = null ) {
1780  $this->getErrorFormatter()->addWarning( $this->getModulePath(), $msg, $code, $data );
1781  }
1782 
1793  public function addDeprecation( $msg, $feature, $data = [] ) {
1794  $data = (array)$data;
1795  if ( $feature !== null ) {
1796  $data['feature'] = $feature;
1797  $this->logFeatureUsage( $feature );
1798  }
1799  $this->addWarning( $msg, 'deprecation', $data );
1800 
1801  // No real need to deduplicate here, ApiErrorFormatter does that for
1802  // us (assuming the hook is deterministic).
1803  $msgs = [ $this->msg( 'api-usage-mailinglist-ref' ) ];
1804  Hooks::run( 'ApiDeprecationHelp', [ &$msgs ] );
1805  if ( count( $msgs ) > 1 ) {
1806  $key = '$' . join( ' $', range( 1, count( $msgs ) ) );
1807  $msg = ( new RawMessage( $key ) )->params( $msgs );
1808  } else {
1809  $msg = reset( $msgs );
1810  }
1811  $this->getMain()->addWarning( $msg, 'deprecation-help' );
1812  }
1813 
1826  public function addError( $msg, $code = null, $data = null ) {
1827  $this->getErrorFormatter()->addError( $this->getModulePath(), $msg, $code, $data );
1828  }
1829 
1838  public function addMessagesFromStatus( StatusValue $status, $types = [ 'warning', 'error' ] ) {
1839  $this->getErrorFormatter()->addMessagesFromStatus( $this->getModulePath(), $status, $types );
1840  }
1841 
1855  public function dieWithError( $msg, $code = null, $data = null, $httpCode = null ) {
1856  throw ApiUsageException::newWithMessage( $this, $msg, $code, $data, $httpCode );
1857  }
1858 
1867  public function dieWithException( $exception, array $options = [] ) {
1868  $this->dieWithError(
1869  $this->getErrorFormatter()->getMessageFromException( $exception, $options )
1870  );
1871  }
1872 
1879  private function warnOrDie( ApiMessage $msg, $enforceLimits = false ) {
1880  if ( $enforceLimits ) {
1881  $this->dieWithError( $msg );
1882  } else {
1883  $this->addWarning( $msg );
1884  }
1885  }
1886 
1895  public function dieBlocked( Block $block ) {
1896  // Die using the appropriate message depending on block type
1897  if ( $block->getType() == Block::TYPE_AUTO ) {
1898  $this->dieWithError(
1899  'apierror-autoblocked',
1900  'autoblocked',
1901  [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) ]
1902  );
1903  } else {
1904  $this->dieWithError(
1905  'apierror-blocked',
1906  'blocked',
1907  [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) ]
1908  );
1909  }
1910  }
1911 
1920  public function dieStatus( StatusValue $status ) {
1921  if ( $status->isGood() ) {
1922  throw new MWException( 'Successful status passed to ApiBase::dieStatus' );
1923  }
1924 
1925  // ApiUsageException needs a fatal status, but this method has
1926  // historically accepted any non-good status. Convert it if necessary.
1927  $status->setOK( false );
1928  if ( !$status->getErrorsByType( 'error' ) ) {
1929  $newStatus = Status::newGood();
1930  foreach ( $status->getErrorsByType( 'warning' ) as $err ) {
1931  call_user_func_array(
1932  [ $newStatus, 'fatal' ],
1933  array_merge( [ $err['message'] ], $err['params'] )
1934  );
1935  }
1936  if ( !$newStatus->getErrorsByType( 'error' ) ) {
1937  $newStatus->fatal( 'unknownerror-nocode' );
1938  }
1939  $status = $newStatus;
1940  }
1941 
1942  throw new ApiUsageException( $this, $status );
1943  }
1944 
1950  public function dieReadOnly() {
1951  $this->dieWithError(
1952  'apierror-readonly',
1953  'readonly',
1954  [ 'readonlyreason' => wfReadOnlyReason() ]
1955  );
1956  }
1957 
1966  public function checkUserRightsAny( $rights, $user = null ) {
1967  if ( !$user ) {
1968  $user = $this->getUser();
1969  }
1970  $rights = (array)$rights;
1971  if ( !call_user_func_array( [ $user, 'isAllowedAny' ], $rights ) ) {
1972  $this->dieWithError( [ 'apierror-permissiondenied', $this->msg( "action-{$rights[0]}" ) ] );
1973  }
1974  }
1975 
1984  public function checkTitleUserPermissions( Title $title, $actions, $user = null ) {
1985  if ( !$user ) {
1986  $user = $this->getUser();
1987  }
1988 
1989  $errors = [];
1990  foreach ( (array)$actions as $action ) {
1991  $errors = array_merge( $errors, $title->getUserPermissionsErrors( $action, $user ) );
1992  }
1993  if ( $errors ) {
1994  $this->dieStatus( $this->errorArrayToStatus( $errors, $user ) );
1995  }
1996  }
1997 
2009  public function dieWithErrorOrDebug( $msg, $code = null, $data = null, $httpCode = null ) {
2010  if ( $this->getConfig()->get( 'DebugAPI' ) !== true ) {
2011  $this->dieWithError( $msg, $code, $data, $httpCode );
2012  } else {
2013  $this->addWarning( $msg, $code, $data );
2014  }
2015  }
2016 
2026  protected function dieContinueUsageIf( $condition ) {
2027  if ( $condition ) {
2028  $this->dieWithError( 'apierror-badcontinue' );
2029  }
2030  }
2031 
2038  protected static function dieDebug( $method, $message ) {
2039  throw new MWException( "Internal error in $method: $message" );
2040  }
2041 
2048  public function logFeatureUsage( $feature ) {
2049  $request = $this->getRequest();
2050  $s = '"' . addslashes( $feature ) . '"' .
2051  ' "' . wfUrlencode( str_replace( ' ', '_', $this->getUser()->getName() ) ) . '"' .
2052  ' "' . $request->getIP() . '"' .
2053  ' "' . addslashes( $request->getHeader( 'Referer' ) ) . '"' .
2054  ' "' . addslashes( $this->getMain()->getUserAgent() ) . '"';
2055  wfDebugLog( 'api-feature-usage', $s, 'private' );
2056  }
2057 
2060  /************************************************************************/
2074  protected function getSummaryMessage() {
2075  return "apihelp-{$this->getModulePath()}-summary";
2076  }
2077 
2087  protected function getExtendedDescription() {
2088  return [ [
2089  "apihelp-{$this->getModulePath()}-extended-description",
2090  'api-help-no-extended-description',
2091  ] ];
2092  }
2093 
2104  public function getFinalSummary() {
2105  $msg = self::makeMessage( $this->getSummaryMessage(), $this->getContext(), [
2106  $this->getModulePrefix(),
2107  $this->getModuleName(),
2108  $this->getModulePath(),
2109  ] );
2110  if ( !$msg->exists() ) {
2111  wfDeprecated( 'API help "description" messages', '1.30' );
2112  $msg = self::makeMessage( $this->getDescriptionMessage(), $this->getContext(), [
2113  $this->getModulePrefix(),
2114  $this->getModuleName(),
2115  $this->getModulePath(),
2116  ] );
2117  $msg = self::makeMessage( 'rawmessage', $this->getContext(), [
2118  preg_replace( '/\n.*/s', '', $msg->text() )
2119  ] );
2120  }
2121  return $msg;
2122  }
2123 
2131  public function getFinalDescription() {
2132  $desc = $this->getDescription();
2133 
2134  // Avoid PHP 7.1 warning of passing $this by reference
2135  $apiModule = $this;
2136  Hooks::run( 'APIGetDescription', [ &$apiModule, &$desc ] );
2137  $desc = self::escapeWikiText( $desc );
2138  if ( is_array( $desc ) ) {
2139  $desc = implode( "\n", $desc );
2140  } else {
2141  $desc = (string)$desc;
2142  }
2143 
2144  $summary = self::makeMessage( $this->getSummaryMessage(), $this->getContext(), [
2145  $this->getModulePrefix(),
2146  $this->getModuleName(),
2147  $this->getModulePath(),
2148  ] );
2149  $extendedDescription = self::makeMessage(
2150  $this->getExtendedDescription(), $this->getContext(), [
2151  $this->getModulePrefix(),
2152  $this->getModuleName(),
2153  $this->getModulePath(),
2154  ]
2155  );
2156 
2157  if ( $summary->exists() ) {
2158  $msgs = [ $summary, $extendedDescription ];
2159  } else {
2160  wfDeprecated( 'API help "description" messages', '1.30' );
2161  $description = self::makeMessage( $this->getDescriptionMessage(), $this->getContext(), [
2162  $this->getModulePrefix(),
2163  $this->getModuleName(),
2164  $this->getModulePath(),
2165  ] );
2166  if ( !$description->exists() ) {
2167  $description = $this->msg( 'api-help-fallback-description', $desc );
2168  }
2169  $msgs = [ $description ];
2170  }
2171 
2172  Hooks::run( 'APIGetDescriptionMessages', [ $this, &$msgs ] );
2173 
2174  return $msgs;
2175  }
2176 
2185  public function getFinalParams( $flags = 0 ) {
2186  $params = $this->getAllowedParams( $flags );
2187  if ( !$params ) {
2188  $params = [];
2189  }
2190 
2191  if ( $this->needsToken() ) {
2192  $params['token'] = [
2193  self::PARAM_TYPE => 'string',
2194  self::PARAM_REQUIRED => true,
2195  self::PARAM_SENSITIVE => true,
2196  self::PARAM_HELP_MSG => [
2197  'api-help-param-token',
2198  $this->needsToken(),
2199  ],
2200  ] + ( isset( $params['token'] ) ? $params['token'] : [] );
2201  }
2202 
2203  // Avoid PHP 7.1 warning of passing $this by reference
2204  $apiModule = $this;
2205  Hooks::run( 'APIGetAllowedParams', [ &$apiModule, &$params, $flags ] );
2206 
2207  return $params;
2208  }
2209 
2217  public function getFinalParamDescription() {
2218  $prefix = $this->getModulePrefix();
2219  $name = $this->getModuleName();
2220  $path = $this->getModulePath();
2221 
2222  $desc = $this->getParamDescription();
2223 
2224  // Avoid PHP 7.1 warning of passing $this by reference
2225  $apiModule = $this;
2226  Hooks::run( 'APIGetParamDescription', [ &$apiModule, &$desc ] );
2227 
2228  if ( !$desc ) {
2229  $desc = [];
2230  }
2231  $desc = self::escapeWikiText( $desc );
2232 
2233  $params = $this->getFinalParams( self::GET_VALUES_FOR_HELP );
2234  $msgs = [];
2235  foreach ( $params as $param => $settings ) {
2236  if ( !is_array( $settings ) ) {
2237  $settings = [];
2238  }
2239 
2240  $d = isset( $desc[$param] ) ? $desc[$param] : '';
2241  if ( is_array( $d ) ) {
2242  // Special handling for prop parameters
2243  $d = array_map( function ( $line ) {
2244  if ( preg_match( '/^\s+(\S+)\s+-\s+(.+)$/', $line, $m ) ) {
2245  $line = "\n;{$m[1]}:{$m[2]}";
2246  }
2247  return $line;
2248  }, $d );
2249  $d = implode( ' ', $d );
2250  }
2251 
2252  if ( isset( $settings[self::PARAM_HELP_MSG] ) ) {
2253  $msg = $settings[self::PARAM_HELP_MSG];
2254  } else {
2255  $msg = $this->msg( "apihelp-{$path}-param-{$param}" );
2256  if ( !$msg->exists() ) {
2257  $msg = $this->msg( 'api-help-fallback-parameter', $d );
2258  }
2259  }
2260  $msg = self::makeMessage( $msg, $this->getContext(),
2261  [ $prefix, $param, $name, $path ] );
2262  if ( !$msg ) {
2263  self::dieDebug( __METHOD__,
2264  'Value in ApiBase::PARAM_HELP_MSG is not valid' );
2265  }
2266  $msgs[$param] = [ $msg ];
2267 
2268  if ( isset( $settings[self::PARAM_TYPE] ) &&
2269  $settings[self::PARAM_TYPE] === 'submodule'
2270  ) {
2271  if ( isset( $settings[self::PARAM_SUBMODULE_MAP] ) ) {
2272  $map = $settings[self::PARAM_SUBMODULE_MAP];
2273  } else {
2274  $prefix = $this->isMain() ? '' : ( $this->getModulePath() . '+' );
2275  $map = [];
2276  foreach ( $this->getModuleManager()->getNames( $param ) as $submoduleName ) {
2277  $map[$submoduleName] = $prefix . $submoduleName;
2278  }
2279  }
2280  ksort( $map );
2281  $submodules = [];
2282  $deprecatedSubmodules = [];
2283  foreach ( $map as $v => $m ) {
2284  $arr = &$submodules;
2285  $isDeprecated = false;
2286  $summary = null;
2287  try {
2288  $submod = $this->getModuleFromPath( $m );
2289  if ( $submod ) {
2290  $summary = $submod->getFinalSummary();
2291  $isDeprecated = $submod->isDeprecated();
2292  if ( $isDeprecated ) {
2293  $arr = &$deprecatedSubmodules;
2294  }
2295  }
2296  } catch ( ApiUsageException $ex ) {
2297  // Ignore
2298  }
2299  if ( $summary ) {
2300  $key = $summary->getKey();
2301  $params = $summary->getParams();
2302  } else {
2303  $key = 'api-help-undocumented-module';
2304  $params = [ $m ];
2305  }
2306  $m = new ApiHelpParamValueMessage( "[[Special:ApiHelp/$m|$v]]", $key, $params, $isDeprecated );
2307  $arr[] = $m->setContext( $this->getContext() );
2308  }
2309  $msgs[$param] = array_merge( $msgs[$param], $submodules, $deprecatedSubmodules );
2310  } elseif ( isset( $settings[self::PARAM_HELP_MSG_PER_VALUE] ) ) {
2311  if ( !is_array( $settings[self::PARAM_HELP_MSG_PER_VALUE] ) ) {
2312  self::dieDebug( __METHOD__,
2313  'ApiBase::PARAM_HELP_MSG_PER_VALUE is not valid' );
2314  }
2315  if ( !is_array( $settings[self::PARAM_TYPE] ) ) {
2316  self::dieDebug( __METHOD__,
2317  'ApiBase::PARAM_HELP_MSG_PER_VALUE may only be used when ' .
2318  'ApiBase::PARAM_TYPE is an array' );
2319  }
2320 
2321  $valueMsgs = $settings[self::PARAM_HELP_MSG_PER_VALUE];
2322  $deprecatedValues = isset( $settings[self::PARAM_DEPRECATED_VALUES] )
2323  ? $settings[self::PARAM_DEPRECATED_VALUES]
2324  : [];
2325 
2326  foreach ( $settings[self::PARAM_TYPE] as $value ) {
2327  if ( isset( $valueMsgs[$value] ) ) {
2328  $msg = $valueMsgs[$value];
2329  } else {
2330  $msg = "apihelp-{$path}-paramvalue-{$param}-{$value}";
2331  }
2332  $m = self::makeMessage( $msg, $this->getContext(),
2333  [ $prefix, $param, $name, $path, $value ] );
2334  if ( $m ) {
2335  $m = new ApiHelpParamValueMessage(
2336  $value,
2337  [ $m->getKey(), 'api-help-param-no-description' ],
2338  $m->getParams(),
2339  isset( $deprecatedValues[$value] )
2340  );
2341  $msgs[$param][] = $m->setContext( $this->getContext() );
2342  } else {
2343  self::dieDebug( __METHOD__,
2344  "Value in ApiBase::PARAM_HELP_MSG_PER_VALUE for $value is not valid" );
2345  }
2346  }
2347  }
2348 
2349  if ( isset( $settings[self::PARAM_HELP_MSG_APPEND] ) ) {
2350  if ( !is_array( $settings[self::PARAM_HELP_MSG_APPEND] ) ) {
2351  self::dieDebug( __METHOD__,
2352  'Value for ApiBase::PARAM_HELP_MSG_APPEND is not an array' );
2353  }
2354  foreach ( $settings[self::PARAM_HELP_MSG_APPEND] as $m ) {
2355  $m = self::makeMessage( $m, $this->getContext(),
2356  [ $prefix, $param, $name, $path ] );
2357  if ( $m ) {
2358  $msgs[$param][] = $m;
2359  } else {
2360  self::dieDebug( __METHOD__,
2361  'Value in ApiBase::PARAM_HELP_MSG_APPEND is not valid' );
2362  }
2363  }
2364  }
2365  }
2366 
2367  Hooks::run( 'APIGetParamDescriptionMessages', [ $this, &$msgs ] );
2368 
2369  return $msgs;
2370  }
2371 
2381  protected function getHelpFlags() {
2382  $flags = [];
2383 
2384  if ( $this->isDeprecated() ) {
2385  $flags[] = 'deprecated';
2386  }
2387  if ( $this->isInternal() ) {
2388  $flags[] = 'internal';
2389  }
2390  if ( $this->isReadMode() ) {
2391  $flags[] = 'readrights';
2392  }
2393  if ( $this->isWriteMode() ) {
2394  $flags[] = 'writerights';
2395  }
2396  if ( $this->mustBePosted() ) {
2397  $flags[] = 'mustbeposted';
2398  }
2399 
2400  return $flags;
2401  }
2402 
2414  protected function getModuleSourceInfo() {
2415  global $IP;
2416 
2417  if ( $this->mModuleSource !== false ) {
2418  return $this->mModuleSource;
2419  }
2420 
2421  // First, try to find where the module comes from...
2422  $rClass = new ReflectionClass( $this );
2423  $path = $rClass->getFileName();
2424  if ( !$path ) {
2425  // No path known?
2426  $this->mModuleSource = null;
2427  return null;
2428  }
2429  $path = realpath( $path ) ?: $path;
2430 
2431  // Build map of extension directories to extension info
2432  if ( self::$extensionInfo === null ) {
2433  $extDir = $this->getConfig()->get( 'ExtensionDirectory' );
2434  self::$extensionInfo = [
2435  realpath( __DIR__ ) ?: __DIR__ => [
2436  'path' => $IP,
2437  'name' => 'MediaWiki',
2438  'license-name' => 'GPL-2.0+',
2439  ],
2440  realpath( "$IP/extensions" ) ?: "$IP/extensions" => null,
2441  realpath( $extDir ) ?: $extDir => null,
2442  ];
2443  $keep = [
2444  'path' => null,
2445  'name' => null,
2446  'namemsg' => null,
2447  'license-name' => null,
2448  ];
2449  foreach ( $this->getConfig()->get( 'ExtensionCredits' ) as $group ) {
2450  foreach ( $group as $ext ) {
2451  if ( !isset( $ext['path'] ) || !isset( $ext['name'] ) ) {
2452  // This shouldn't happen, but does anyway.
2453  continue;
2454  }
2455 
2456  $extpath = $ext['path'];
2457  if ( !is_dir( $extpath ) ) {
2458  $extpath = dirname( $extpath );
2459  }
2460  self::$extensionInfo[realpath( $extpath ) ?: $extpath] =
2461  array_intersect_key( $ext, $keep );
2462  }
2463  }
2464  foreach ( ExtensionRegistry::getInstance()->getAllThings() as $ext ) {
2465  $extpath = $ext['path'];
2466  if ( !is_dir( $extpath ) ) {
2467  $extpath = dirname( $extpath );
2468  }
2469  self::$extensionInfo[realpath( $extpath ) ?: $extpath] =
2470  array_intersect_key( $ext, $keep );
2471  }
2472  }
2473 
2474  // Now traverse parent directories until we find a match or run out of
2475  // parents.
2476  do {
2477  if ( array_key_exists( $path, self::$extensionInfo ) ) {
2478  // Found it!
2479  $this->mModuleSource = self::$extensionInfo[$path];
2480  return $this->mModuleSource;
2481  }
2482 
2483  $oldpath = $path;
2484  $path = dirname( $path );
2485  } while ( $path !== $oldpath );
2486 
2487  // No idea what extension this might be.
2488  $this->mModuleSource = null;
2489  return null;
2490  }
2491 
2503  public function modifyHelp( array &$help, array $options, array &$tocData ) {
2504  }
2505 
2508  /************************************************************************/
2522  protected function getDescription() {
2523  return false;
2524  }
2525 
2538  protected function getParamDescription() {
2539  return [];
2540  }
2541 
2558  protected function getExamples() {
2559  return false;
2560  }
2561 
2567  public function getModuleProfileName( $db = false ) {
2568  wfDeprecated( __METHOD__, '1.25' );
2569  return '';
2570  }
2571 
2575  public function profileIn() {
2576  // No wfDeprecated() yet because extensions call this and might need to
2577  // keep doing so for BC.
2578  }
2579 
2583  public function profileOut() {
2584  // No wfDeprecated() yet because extensions call this and might need to
2585  // keep doing so for BC.
2586  }
2587 
2591  public function safeProfileOut() {
2592  wfDeprecated( __METHOD__, '1.25' );
2593  }
2594 
2599  public function getProfileTime() {
2600  wfDeprecated( __METHOD__, '1.25' );
2601  return 0;
2602  }
2603 
2607  public function profileDBIn() {
2608  wfDeprecated( __METHOD__, '1.25' );
2609  }
2610 
2614  public function profileDBOut() {
2615  wfDeprecated( __METHOD__, '1.25' );
2616  }
2617 
2622  public function getProfileDBTime() {
2623  wfDeprecated( __METHOD__, '1.25' );
2624  return 0;
2625  }
2626 
2631  protected function useTransactionalTimeLimit() {
2632  if ( $this->getRequest()->wasPosted() ) {
2634  }
2635  }
2636 
2641  public function setWarning( $warning ) {
2642  wfDeprecated( __METHOD__, '1.29' );
2643  $msg = new ApiRawMessage( $warning, 'warning' );
2644  $this->getErrorFormatter()->addWarning( $this->getModulePath(), $msg );
2645  }
2646 
2660  public function dieUsage( $description, $errorCode, $httpRespCode = 0, $extradata = null ) {
2661  wfDeprecated( __METHOD__, '1.29' );
2662  $this->dieWithError(
2663  new RawMessage( '$1', [ $description ] ),
2664  $errorCode,
2665  $extradata,
2666  $httpRespCode
2667  );
2668  }
2669 
2680  public function getErrorFromStatus( $status, &$extraData = null ) {
2681  wfDeprecated( __METHOD__, '1.29' );
2682  if ( $status->isGood() ) {
2683  throw new MWException( 'Successful status passed to ApiBase::dieStatus' );
2684  }
2685 
2686  $errors = $status->getErrorsByType( 'error' );
2687  if ( !$errors ) {
2688  // No errors? Assume the warnings should be treated as errors
2689  $errors = $status->getErrorsByType( 'warning' );
2690  }
2691  if ( !$errors ) {
2692  // Still no errors? Punt
2693  $errors = [ [ 'message' => 'unknownerror-nocode', 'params' => [] ] ];
2694  }
2695 
2696  if ( $errors[0]['message'] instanceof MessageSpecifier ) {
2697  $msg = $errors[0]['message'];
2698  } else {
2699  $msg = new Message( $errors[0]['message'], $errors[0]['params'] );
2700  }
2701  if ( !$msg instanceof IApiMessage ) {
2702  $key = $msg->getKey();
2703  $params = $msg->getParams();
2704  array_unshift( $params, isset( self::$messageMap[$key] ) ? self::$messageMap[$key] : $key );
2705  $msg = ApiMessage::create( $params );
2706  }
2707 
2708  return [
2709  $msg->getApiCode(),
2710  ApiErrorFormatter::stripMarkup( $msg->inLanguage( 'en' )->useDatabase( false )->text() )
2711  ];
2712  }
2713 
2722  private static $messageMap = [
2723  'unknownerror' => 'apierror-unknownerror',
2724  'unknownerror-nocode' => 'apierror-unknownerror-nocode',
2725  'ns-specialprotected' => 'ns-specialprotected',
2726  'protectedinterface' => 'protectedinterface',
2727  'namespaceprotected' => 'namespaceprotected',
2728  'customcssprotected' => 'customcssprotected',
2729  'customjsprotected' => 'customjsprotected',
2730  'cascadeprotected' => 'cascadeprotected',
2731  'protectedpagetext' => 'protectedpagetext',
2732  'protect-cantedit' => 'protect-cantedit',
2733  'deleteprotected' => 'deleteprotected',
2734  'badaccess-group0' => 'badaccess-group0',
2735  'badaccess-groups' => 'badaccess-groups',
2736  'titleprotected' => 'titleprotected',
2737  'nocreate-loggedin' => 'nocreate-loggedin',
2738  'nocreatetext' => 'nocreatetext',
2739  'movenologintext' => 'movenologintext',
2740  'movenotallowed' => 'movenotallowed',
2741  'confirmedittext' => 'confirmedittext',
2742  'blockedtext' => 'apierror-blocked',
2743  'autoblockedtext' => 'apierror-autoblocked',
2744  'systemblockedtext' => 'apierror-systemblocked',
2745  'actionthrottledtext' => 'apierror-ratelimited',
2746  'alreadyrolled' => 'alreadyrolled',
2747  'cantrollback' => 'cantrollback',
2748  'readonlytext' => 'readonlytext',
2749  'sessionfailure' => 'sessionfailure',
2750  'cannotdelete' => 'cannotdelete',
2751  'notanarticle' => 'apierror-missingtitle',
2752  'selfmove' => 'selfmove',
2753  'immobile_namespace' => 'apierror-immobilenamespace',
2754  'articleexists' => 'articleexists',
2755  'hookaborted' => 'hookaborted',
2756  'cantmove-titleprotected' => 'cantmove-titleprotected',
2757  'imagenocrossnamespace' => 'imagenocrossnamespace',
2758  'imagetypemismatch' => 'imagetypemismatch',
2759  'ip_range_invalid' => 'ip_range_invalid',
2760  'range_block_disabled' => 'range_block_disabled',
2761  'nosuchusershort' => 'nosuchusershort',
2762  'badipaddress' => 'badipaddress',
2763  'ipb_expiry_invalid' => 'ipb_expiry_invalid',
2764  'ipb_already_blocked' => 'ipb_already_blocked',
2765  'ipb_blocked_as_range' => 'ipb_blocked_as_range',
2766  'ipb_cant_unblock' => 'ipb_cant_unblock',
2767  'mailnologin' => 'apierror-cantsend',
2768  'ipbblocked' => 'ipbblocked',
2769  'ipbnounblockself' => 'ipbnounblockself',
2770  'usermaildisabled' => 'usermaildisabled',
2771  'blockedemailuser' => 'apierror-blockedfrommail',
2772  'notarget' => 'apierror-notarget',
2773  'noemail' => 'noemail',
2774  'rcpatroldisabled' => 'rcpatroldisabled',
2775  'markedaspatrollederror-noautopatrol' => 'markedaspatrollederror-noautopatrol',
2776  'delete-toobig' => 'delete-toobig',
2777  'movenotallowedfile' => 'movenotallowedfile',
2778  'userrights-no-interwiki' => 'userrights-no-interwiki',
2779  'userrights-nodatabase' => 'userrights-nodatabase',
2780  'nouserspecified' => 'nouserspecified',
2781  'noname' => 'noname',
2782  'summaryrequired' => 'apierror-summaryrequired',
2783  'import-rootpage-invalid' => 'import-rootpage-invalid',
2784  'import-rootpage-nosubpage' => 'import-rootpage-nosubpage',
2785  'readrequired' => 'apierror-readapidenied',
2786  'writedisabled' => 'apierror-noapiwrite',
2787  'writerequired' => 'apierror-writeapidenied',
2788  'missingparam' => 'apierror-missingparam',
2789  'invalidtitle' => 'apierror-invalidtitle',
2790  'nosuchpageid' => 'apierror-nosuchpageid',
2791  'nosuchrevid' => 'apierror-nosuchrevid',
2792  'nosuchuser' => 'nosuchusershort',
2793  'invaliduser' => 'apierror-invaliduser',
2794  'invalidexpiry' => 'apierror-invalidexpiry',
2795  'pastexpiry' => 'apierror-pastexpiry',
2796  'create-titleexists' => 'apierror-create-titleexists',
2797  'missingtitle-createonly' => 'apierror-missingtitle-createonly',
2798  'cantblock' => 'apierror-cantblock',
2799  'canthide' => 'apierror-canthide',
2800  'cantblock-email' => 'apierror-cantblock-email',
2801  'cantunblock' => 'apierror-permissiondenied-generic',
2802  'cannotundelete' => 'cannotundelete',
2803  'permdenied-undelete' => 'apierror-permissiondenied-generic',
2804  'createonly-exists' => 'apierror-articleexists',
2805  'nocreate-missing' => 'apierror-missingtitle',
2806  'cantchangecontentmodel' => 'apierror-cantchangecontentmodel',
2807  'nosuchrcid' => 'apierror-nosuchrcid',
2808  'nosuchlogid' => 'apierror-nosuchlogid',
2809  'protect-invalidaction' => 'apierror-protect-invalidaction',
2810  'protect-invalidlevel' => 'apierror-protect-invalidlevel',
2811  'toofewexpiries' => 'apierror-toofewexpiries',
2812  'cantimport' => 'apierror-cantimport',
2813  'cantimport-upload' => 'apierror-cantimport-upload',
2814  'importnofile' => 'importnofile',
2815  'importuploaderrorsize' => 'importuploaderrorsize',
2816  'importuploaderrorpartial' => 'importuploaderrorpartial',
2817  'importuploaderrortemp' => 'importuploaderrortemp',
2818  'importcantopen' => 'importcantopen',
2819  'import-noarticle' => 'import-noarticle',
2820  'importbadinterwiki' => 'importbadinterwiki',
2821  'import-unknownerror' => 'apierror-import-unknownerror',
2822  'cantoverwrite-sharedfile' => 'apierror-cantoverwrite-sharedfile',
2823  'sharedfile-exists' => 'apierror-fileexists-sharedrepo-perm',
2824  'mustbeposted' => 'apierror-mustbeposted',
2825  'show' => 'apierror-show',
2826  'specialpage-cantexecute' => 'apierror-specialpage-cantexecute',
2827  'invalidoldimage' => 'apierror-invalidoldimage',
2828  'nodeleteablefile' => 'apierror-nodeleteablefile',
2829  'fileexists-forbidden' => 'fileexists-forbidden',
2830  'fileexists-shared-forbidden' => 'fileexists-shared-forbidden',
2831  'filerevert-badversion' => 'filerevert-badversion',
2832  'noimageredirect-anon' => 'apierror-noimageredirect-anon',
2833  'noimageredirect-logged' => 'apierror-noimageredirect',
2834  'spamdetected' => 'apierror-spamdetected',
2835  'contenttoobig' => 'apierror-contenttoobig',
2836  'noedit-anon' => 'apierror-noedit-anon',
2837  'noedit' => 'apierror-noedit',
2838  'wasdeleted' => 'apierror-pagedeleted',
2839  'blankpage' => 'apierror-emptypage',
2840  'editconflict' => 'editconflict',
2841  'hashcheckfailed' => 'apierror-badmd5',
2842  'missingtext' => 'apierror-notext',
2843  'emptynewsection' => 'apierror-emptynewsection',
2844  'revwrongpage' => 'apierror-revwrongpage',
2845  'undo-failure' => 'undo-failure',
2846  'content-not-allowed-here' => 'content-not-allowed-here',
2847  'edit-hook-aborted' => 'edit-hook-aborted',
2848  'edit-gone-missing' => 'edit-gone-missing',
2849  'edit-conflict' => 'edit-conflict',
2850  'edit-already-exists' => 'edit-already-exists',
2851  'invalid-file-key' => 'apierror-invalid-file-key',
2852  'nouploadmodule' => 'apierror-nouploadmodule',
2853  'uploaddisabled' => 'uploaddisabled',
2854  'copyuploaddisabled' => 'copyuploaddisabled',
2855  'copyuploadbaddomain' => 'apierror-copyuploadbaddomain',
2856  'copyuploadbadurl' => 'apierror-copyuploadbadurl',
2857  'filename-tooshort' => 'filename-tooshort',
2858  'filename-toolong' => 'filename-toolong',
2859  'illegal-filename' => 'illegal-filename',
2860  'filetype-missing' => 'filetype-missing',
2861  'mustbeloggedin' => 'apierror-mustbeloggedin',
2862  ];
2863 
2869  private function parseMsgInternal( $error ) {
2870  $msg = Message::newFromSpecifier( $error );
2871  if ( !$msg instanceof IApiMessage ) {
2872  $key = $msg->getKey();
2873  if ( isset( self::$messageMap[$key] ) ) {
2874  $params = $msg->getParams();
2875  array_unshift( $params, self::$messageMap[$key] );
2876  } else {
2877  $params = [ 'apierror-unknownerror', wfEscapeWikiText( $key ) ];
2878  }
2879  $msg = ApiMessage::create( $params );
2880  }
2881  return $msg;
2882  }
2883 
2890  public function parseMsg( $error ) {
2891  wfDeprecated( __METHOD__, '1.29' );
2892  // Check whether someone passed the whole array, instead of one element as
2893  // documented. This breaks if it's actually an array of fallback keys, but
2894  // that's long-standing misbehavior introduced in r87627 to incorrectly
2895  // fix T30797.
2896  if ( is_array( $error ) ) {
2897  $first = reset( $error );
2898  if ( is_array( $first ) ) {
2899  wfDebug( __METHOD__ . ' was passed an array of arrays. ' . wfGetAllCallers( 5 ) );
2900  $error = $first;
2901  }
2902  }
2903 
2904  $msg = $this->parseMsgInternal( $error );
2905  return [
2906  'code' => $msg->getApiCode(),
2908  $msg->inLanguage( 'en' )->useDatabase( false )->text()
2909  ),
2910  'data' => $msg->getApiData()
2911  ];
2912  }
2913 
2920  public function dieUsageMsg( $error ) {
2921  wfDeprecated( __METHOD__, '1.29' );
2922  $this->dieWithError( $this->parseMsgInternal( $error ) );
2923  }
2924 
2933  public function dieUsageMsgOrDebug( $error ) {
2934  wfDeprecated( __METHOD__, '1.29' );
2935  $this->dieWithErrorOrDebug( $this->parseMsgInternal( $error ) );
2936  }
2937 
2946  protected function getDescriptionMessage() {
2947  return [ [
2948  "apihelp-{$this->getModulePath()}-description",
2949  "apihelp-{$this->getModulePath()}-summary",
2950  ] ];
2951  }
2952 
2954 }
2955 
ApiBase\dieUsageMsgOrDebug
dieUsageMsgOrDebug( $error)
Will only set a warning instead of failing if the global $wgDebugAPI is set to true.
Definition: ApiBase.php:2933
ApiMain
This is the main API class, used for both external and internal processing.
Definition: ApiMain.php:45
ContextSource\$context
IContextSource $context
Definition: ContextSource.php:34
ContextSource\getConfig
getConfig()
Get the Config object.
Definition: ContextSource.php:68
ApiBase\PARAM_SUBMODULE_MAP
const PARAM_SUBMODULE_MAP
(string[]) When PARAM_TYPE is 'submodule', map parameter values to submodule paths.
Definition: ApiBase.php:168
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:244
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:268
ApiUsageException
Exception used to abort API execution with an error.
Definition: ApiUsageException.php:104
ContextSource\getContext
getContext()
Get the base IContextSource object.
Definition: ContextSource.php:41
ApiBase\$mMainModule
ApiMain $mMainModule
Definition: ApiBase.php:244
StatusValue
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: StatusValue.php:42
ApiBase\getParent
getParent()
Get the parent of this module.
Definition: ApiBase.php:546
ApiBase\addWarning
addWarning( $msg, $code=null, $data=null)
Add a warning for this module.
Definition: ApiBase.php:1779
Block\getType
getType()
Get the type of target for this particular block.
Definition: Block.php:1389
ApiBase\getSummaryMessage
getSummaryMessage()
Return the summary message.
Definition: ApiBase.php:2074
MWNamespace\getValidNamespaces
static getValidNamespaces()
Returns an array of the namespaces (by integer id) that exist on the wiki.
Definition: MWNamespace.php:264
ApiBase\getFinalParams
getFinalParams( $flags=0)
Get final list of parameters, after hooks have had a chance to tweak it as needed.
Definition: ApiBase.php:2185
ApiBase\PARAM_REQUIRED
const PARAM_REQUIRED
(boolean) Is the parameter required?
Definition: ApiBase.php:115
ApiBase\parseMsg
parseMsg( $error)
Return the error message related to a certain array.
Definition: ApiBase.php:2890
ApiBase\parameterNotEmpty
parameterNotEmpty( $x)
Callback function used in requireOnlyOneParameter to check whether required parameters are set.
Definition: ApiBase.php:902
ApiBase\$mModuleSource
array null bool $mModuleSource
Definition: ApiBase.php:250
captcha-old.count
count
Definition: captcha-old.py:249
ApiBase\getProfileDBTime
getProfileDBTime()
Definition: ApiBase.php:2622
ApiBase\validateToken
validateToken( $token, array $params)
Validate the supplied token.
Definition: ApiBase.php:1556
ApiBase\dieWithError
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
Definition: ApiBase.php:1855
ApiBase\PARAM_HELP_MSG
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition: ApiBase.php:128
ContextSource\msg
msg( $key)
Get a Message object with context set Parameters are the same as wfMessage()
Definition: ContextSource.php:189
ApiBase\getExamplesMessages
getExamplesMessages()
Returns usage examples for this module.
Definition: ApiBase.php:323
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:183
ApiBase\validateTimestamp
validateTimestamp( $value, $encParamName)
Validate and normalize of parameters of type 'timestamp'.
Definition: ApiBase.php:1519
ApiBase\dieUsageMsg
dieUsageMsg( $error)
Output the error message related to a certain array.
Definition: ApiBase.php:2920
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:2040
ApiBase\profileDBIn
profileDBIn()
Definition: ApiBase.php:2607
ApiBase\$extensionInfo
static array $extensionInfo
Maps extension paths to info arrays.
Definition: ApiBase.php:241
ApiBase\getTitleOrPageId
getTitleOrPageId( $params, $load=false)
Get a WikiPage object from a title or pageid param, if possible.
Definition: ApiBase.php:917
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:91
ApiBase\getResult
getResult()
Get the result object.
Definition: ApiBase.php:632
ApiBase\getDescriptionMessage
getDescriptionMessage()
Return the description message.
Definition: ApiBase.php:2946
MessageSpecifier
Definition: MessageSpecifier.php:21
$status
Status::newGood()` to allow deletion, and then `return false` from the hook function. Ensure you consume the 'ChangeTagAfterDelete' hook to carry out custom deletion actions. $tag:name of the tag $user:user initiating the action & $status:Status object. See above. 'ChangeTagsListActive':Allows you to nominate which of the tags your extension uses are in active use. & $tags:list of all active tags. Append to this array. 'ChangeTagsAfterUpdateTags':Called after tags have been updated with the ChangeTags::updateTags function. Params:$addedTags:tags effectively added in the update $removedTags:tags effectively removed in the update $prevTags:tags that were present prior to the update $rc_id:recentchanges table id $rev_id:revision table id $log_id:logging table id $params:tag params $rc:RecentChange being tagged when the tagging accompanies the action or null $user:User who performed the tagging when the tagging is subsequent to the action or null 'ChangeTagsAllowedAdd':Called when checking if a user can add tags to a change. & $allowedTags:List of all the tags the user is allowed to add. Any tags the user wants to add( $addTags) that are not in this array will cause it to fail. You may add or remove tags to this array as required. $addTags:List of tags user intends to add. $user:User who is adding the tags. 'ChangeUserGroups':Called before user groups are changed. $performer:The User who will perform the change $user:The User whose groups will be changed & $add:The groups that will be added & $remove:The groups that will be removed 'Collation::factory':Called if $wgCategoryCollation is an unknown collation. $collationName:Name of the collation in question & $collationObject:Null. Replace with a subclass of the Collation class that implements the collation given in $collationName. 'ConfirmEmailComplete':Called after a user 's email has been confirmed successfully. $user:user(object) whose email is being confirmed 'ContentAlterParserOutput':Modify parser output for a given content object. Called by Content::getParserOutput after parsing has finished. Can be used for changes that depend on the result of the parsing but have to be done before LinksUpdate is called(such as adding tracking categories based on the rendered HTML). $content:The Content to render $title:Title of the page, as context $parserOutput:ParserOutput to manipulate 'ContentGetParserOutput':Customize parser output for a given content object, called by AbstractContent::getParserOutput. May be used to override the normal model-specific rendering of page content. $content:The Content to render $title:Title of the page, as context $revId:The revision ID, as context $options:ParserOptions for rendering. To avoid confusing the parser cache, the output can only depend on parameters provided to this hook function, not on global state. $generateHtml:boolean, indicating whether full HTML should be generated. If false, generation of HTML may be skipped, but other information should still be present in the ParserOutput object. & $output:ParserOutput, to manipulate or replace 'ContentHandlerDefaultModelFor':Called when the default content model is determined for a given title. May be used to assign a different model for that title. $title:the Title in question & $model:the model name. Use with CONTENT_MODEL_XXX constants. 'ContentHandlerForModelID':Called when a ContentHandler is requested for a given content model name, but no entry for that model exists in $wgContentHandlers. Note:if your extension implements additional models via this hook, please use GetContentModels hook to make them known to core. $modeName:the requested content model name & $handler:set this to a ContentHandler object, if desired. 'ContentModelCanBeUsedOn':Called to determine whether that content model can be used on a given page. This is especially useful to prevent some content models to be used in some special location. $contentModel:ID of the content model in question $title:the Title in question. & $ok:Output parameter, whether it is OK to use $contentModel on $title. Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok. 'ContribsPager::getQueryInfo':Before the contributions query is about to run & $pager:Pager object for contributions & $queryInfo:The query for the contribs Pager 'ContribsPager::reallyDoQuery':Called before really executing the query for My Contributions & $data:an array of results of all contribs queries $pager:The ContribsPager object hooked into $offset:Index offset, inclusive $limit:Exact query limit $descending:Query direction, false for ascending, true for descending 'ContributionsLineEnding':Called before a contributions HTML line is finished $page:SpecialPage object for contributions & $ret:the HTML line $row:the DB row for this line & $classes:the classes to add to the surrounding< li > & $attribs:associative array of other HTML attributes for the< li > element. Currently only data attributes reserved to MediaWiki are allowed(see Sanitizer::isReservedDataAttribute). 'ContributionsToolLinks':Change tool links above Special:Contributions $id:User identifier $title:User page title & $tools:Array of tool links $specialPage:SpecialPage instance for context and services. Can be either SpecialContributions or DeletedContributionsPage. Extensions should type hint against a generic SpecialPage though. 'ConvertContent':Called by AbstractContent::convert when a conversion to another content model is requested. Handler functions that modify $result should generally return false to disable further attempts at conversion. $content:The Content object to be converted. $toModel:The ID of the content model to convert to. $lossy:boolean indicating whether lossy conversion is allowed. & $result:Output parameter, in case the handler function wants to provide a converted Content object. Note that $result->getContentModel() must return $toModel. 'CustomEditor':When invoking the page editor Return true to allow the normal editor to be used, or false if implementing a custom editor, e.g. for a special namespace, etc. $article:Article being edited $user:User performing the edit 'DatabaseOraclePostInit':Called after initialising an Oracle database $db:the DatabaseOracle object 'DeletedContribsPager::reallyDoQuery':Called before really executing the query for Special:DeletedContributions Similar to ContribsPager::reallyDoQuery & $data:an array of results of all contribs queries $pager:The DeletedContribsPager object hooked into $offset:Index offset, inclusive $limit:Exact query limit $descending:Query direction, false for ascending, true for descending 'DeletedContributionsLineEnding':Called before a DeletedContributions HTML line is finished. Similar to ContributionsLineEnding $page:SpecialPage object for DeletedContributions & $ret:the HTML line $row:the DB row for this line & $classes:the classes to add to the surrounding< li > & $attribs:associative array of other HTML attributes for the< li > element. Currently only data attributes reserved to MediaWiki are allowed(see Sanitizer::isReservedDataAttribute). 'DifferenceEngineAfterLoadNewText':called in DifferenceEngine::loadNewText() after the new revision 's content has been loaded into the class member variable $differenceEngine->mNewContent but before returning true from this function. $differenceEngine:DifferenceEngine object 'DifferenceEngineLoadTextAfterNewContentIsLoaded':called in DifferenceEngine::loadText() after the new revision 's content has been loaded into the class member variable $differenceEngine->mNewContent but before checking if the variable 's value is null. This hook can be used to inject content into said class member variable. $differenceEngine:DifferenceEngine object 'DifferenceEngineMarkPatrolledLink':Allows extensions to change the "mark as patrolled" link which is shown both on the diff header as well as on the bottom of a page, usually wrapped in a span element which has class="patrollink". $differenceEngine:DifferenceEngine object & $markAsPatrolledLink:The "mark as patrolled" link HTML(string) $rcid:Recent change ID(rc_id) for this change(int) 'DifferenceEngineMarkPatrolledRCID':Allows extensions to possibly change the rcid parameter. For example the rcid might be set to zero due to the user being the same as the performer of the change but an extension might still want to show it under certain conditions. & $rcid:rc_id(int) of the change or 0 $differenceEngine:DifferenceEngine object $change:RecentChange object $user:User object representing the current user 'DifferenceEngineNewHeader':Allows extensions to change the $newHeader variable, which contains information about the new revision, such as the revision 's author, whether the revision was marked as a minor edit or not, etc. $differenceEngine:DifferenceEngine object & $newHeader:The string containing the various #mw-diff-otitle[1-5] divs, which include things like revision author info, revision comment, RevisionDelete link and more $formattedRevisionTools:Array containing revision tools, some of which may have been injected with the DiffRevisionTools hook $nextlink:String containing the link to the next revision(if any) $status
Definition: hooks.txt:1245
ApiBase\__construct
__construct(ApiMain $mainModule, $moduleName, $modulePrefix='')
Definition: ApiBase.php:257
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
ApiBase\profileDBOut
profileDBOut()
Definition: ApiBase.php:2614
wfUrlencode
wfUrlencode( $s)
We want some things to be included as literal characters in our title URLs for prettiness,...
Definition: GlobalFunctions.php:405
ApiBase\mustBePosted
mustBePosted()
Indicates whether this module must be called with a POST request.
Definition: ApiBase.php:427
ApiBase\logFeatureUsage
logFeatureUsage( $feature)
Write logging information for API features to a debug log, for usage analysis.
Definition: ApiBase.php:2048
ApiBase\checkUserRightsAny
checkUserRightsAny( $rights, $user=null)
Helper function for permission-denied errors.
Definition: ApiBase.php:1966
ApiBase\shouldCheckMaxlag
shouldCheckMaxlag()
Indicates if this module needs maxlag to be checked.
Definition: ApiBase.php:396
ApiBase\dieWithErrorOrDebug
dieWithErrorOrDebug( $msg, $code=null, $data=null, $httpCode=null)
Will only set a warning instead of failing if the global $wgDebugAPI is set to true.
Definition: ApiBase.php:2009
$params
$params
Definition: styleTest.css.php:40
ApiBase\getExtendedDescription
getExtendedDescription()
Return the extended help text message.
Definition: ApiBase.php:2087
ApiBase\PARAM_ISMULTI_LIMIT1
const PARAM_ISMULTI_LIMIT1
(integer) Maximum number of values, for normal users.
Definition: ApiBase.php:211
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:550
ApiBase\escapeWikiText
static escapeWikiText( $v)
A subset of wfEscapeWikiText for BC texts.
Definition: ApiBase.php:1677
$s
$s
Definition: mergeMessageFileList.php:188
ApiBase\getDB
getDB()
Gets a default replica DB connection object.
Definition: ApiBase.php:660
ApiBase\makeMessage
static makeMessage( $msg, IContextSource $context, array $params=null)
Create a Message from a string or array.
Definition: ApiBase.php:1701
ApiBase\addMessagesFromStatus
addMessagesFromStatus(StatusValue $status, $types=[ 'warning', 'error'])
Add warnings and/or errors from a Status.
Definition: ApiBase.php:1838
ApiBase\$mSlaveDB
$mSlaveDB
Definition: ApiBase.php:247
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:302
ContextSource\getRequest
getRequest()
Get the WebRequest object.
Definition: ContextSource.php:78
ApiBase\dynamicParameterDocumentation
dynamicParameterDocumentation()
Indicate if the module supports dynamically-determined parameters that cannot be included in self::ge...
Definition: ApiBase.php:710
ApiBase\modifyHelp
modifyHelp(array &$help, array $options, array &$tocData)
Called from ApiHelp before the pieces are joined together and returned.
Definition: ApiBase.php:2503
ApiBase\PARAM_ALLOW_DUPLICATES
const PARAM_ALLOW_DUPLICATES
(boolean) Allow the same value to be set more than once when PARAM_ISMULTI is true?
Definition: ApiBase.php:106
ContextSource\getUser
getUser()
Get the User object.
Definition: ContextSource.php:133
IApiMessage
Interface for messages with machine-readable data for use by the API.
Definition: ApiMessage.php:35
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:205
ApiBase\getModuleProfileName
getModuleProfileName( $db=false)
Definition: ApiBase.php:2567
ApiBase\lacksSameOriginSecurity
lacksSameOriginSecurity()
Returns true if the current request breaks the same-origin policy.
Definition: ApiBase.php:560
ApiBase\PARAM_HELP_MSG_APPEND
const PARAM_HELP_MSG_APPEND
((string|array|Message)[]) Specify additional i18n messages to append to the normal message for this ...
Definition: ApiBase.php:135
ApiUsageException\newWithMessage
static newWithMessage(ApiBase $module=null, $msg, $code=null, $data=null, $httpCode=0)
Definition: ApiUsageException.php:144
wfDebugLog
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
Definition: GlobalFunctions.php:1140
ApiBase\getWebUITokenSalt
getWebUITokenSalt(array $params)
Fetch the salt used in the Web UI corresponding to this module.
Definition: ApiBase.php:481
ApiBase\isMain
isMain()
Returns true if this module is the main module ($this === $this->mMainModule), false otherwise.
Definition: ApiBase.php:537
ApiBase\isReadMode
isReadMode()
Indicates whether this module requires read rights.
Definition: ApiBase.php:404
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
ApiBase
This abstract class implements many basic API functions, and is the base of all API classes.
Definition: ApiBase.php:41
ApiRawMessage
Extension of RawMessage implementing IApiMessage.
Definition: ApiMessage.php:268
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:40
ApiBase\getModuleFromPath
getModuleFromPath( $path)
Get a module from its module path.
Definition: ApiBase.php:594
ApiBase\getFinalParamDescription
getFinalParamDescription()
Get final parameter descriptions, after hooks have had a chance to tweak it as needed.
Definition: ApiBase.php:2217
ApiMessage
Extension of Message implementing IApiMessage.
Definition: ApiMessage.php:198
ApiBase\PARAM_SENSITIVE
const PARAM_SENSITIVE
(boolean) Is the parameter sensitive? Note 'password'-type fields are always sensitive regardless of ...
Definition: ApiBase.php:196
ApiBase\dieBlocked
dieBlocked(Block $block)
Throw an ApiUsageException, which will (if uncaught) call the main module's error handler and die wit...
Definition: ApiBase.php:1895
ApiBase\PARAM_ISMULTI_LIMIT2
const PARAM_ISMULTI_LIMIT2
(integer) Maximum number of values, for users with the apihighimits right.
Definition: ApiBase.php:218
ApiBase\$messageMap
static $messageMap
Definition: ApiBase.php:2722
ApiBase\getParamDescription
getParamDescription()
Returns an array of parameter descriptions.
Definition: ApiBase.php:2538
ExtensionRegistry\getInstance
static getInstance()
Definition: ExtensionRegistry.php:80
ApiBase\getFinalSummary
getFinalSummary()
Get final module summary.
Definition: ApiBase.php:2104
ApiBase\profileOut
profileOut()
Definition: ApiBase.php:2583
ApiBase\PARAM_DEPRECATED
const PARAM_DEPRECATED
(boolean) Is the parameter deprecated (will show a warning)?
Definition: ApiBase.php:109
ApiBase\explodeMultiValue
explodeMultiValue( $value, $limit)
Split a multi-valued parameter string, like explode()
Definition: ApiBase.php:1350
ApiBase\PARAM_MIN
const PARAM_MIN
(integer) Lowest value allowed for the parameter, for PARAM_TYPE 'integer' and 'limit'.
Definition: ApiBase.php:103
MWException
MediaWiki exception.
Definition: MWException.php:26
$title
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:932
WikiPage\factory
static factory(Title $title)
Create a WikiPage object of the appropriate class for the given title.
Definition: WikiPage.php:121
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Definition: GlobalFunctions.php:1176
ApiErrorFormatter\stripMarkup
static stripMarkup( $text)
Turn wikitext into something resembling plaintext.
Definition: ApiErrorFormatter.php:252
ApiBase\warnOrDie
warnOrDie(ApiMessage $msg, $enforceLimits=false)
Adds a warning to the output, else dies.
Definition: ApiBase.php:1879
ApiBase\getCustomPrinter
getCustomPrinter()
If the module may only be used with a certain format module, it should override this method to return...
Definition: ApiBase.php:308
ApiBase\getFinalDescription
getFinalDescription()
Get final module description, after hooks have had a chance to tweak it as needed.
Definition: ApiBase.php:2131
wfTransactionalTimeLimit
wfTransactionalTimeLimit()
Set PHP's time limit to the larger of php.ini or $wgTransactionalTimeLimit.
Definition: GlobalFunctions.php:3114
ApiBase\getModulePath
getModulePath()
Get the path to this module.
Definition: ApiBase.php:576
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2856
ApiBase\LIMIT_BIG1
const LIMIT_BIG1
Fast query, standard limit.
Definition: ApiBase.php:225
ApiBase\getDescription
getDescription()
Returns the description string for this module.
Definition: ApiBase.php:2522
ContextSource
The simplest way of implementing IContextSource is to hold a RequestContext as a member variable and ...
Definition: ContextSource.php:30
ApiQueryTokens\getTokenTypeSalts
static getTokenTypeSalts()
Get the salts for known token types.
Definition: ApiQueryTokens.php:65
$IP
$IP
Definition: update.php:3
ApiBase\PARAM_MAX
const PARAM_MAX
(integer) Max value allowed for the parameter, for PARAM_TYPE 'integer' and 'limit'.
Definition: ApiBase.php:94
ApiBase\dieWithException
dieWithException( $exception, array $options=[])
Abort execution with an error derived from an exception.
Definition: ApiBase.php:1867
WatchAction\doWatchOrUnwatch
static doWatchOrUnwatch( $watch, Title $title, User $user)
Watch or unwatch a page.
Definition: WatchAction.php:93
ApiBase\parseMsgInternal
parseMsgInternal( $error)
Definition: ApiBase.php:2869
ApiBase\handleParamNormalization
handleParamNormalization( $paramName, $value, $rawValue)
Handle when a parameter was Unicode-normalized.
Definition: ApiBase.php:1338
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
ApiBase\isDeprecated
isDeprecated()
Indicates whether this module is deprecated.
Definition: ApiBase.php:436
ApiMessage\create
static create( $msg, $code=null, array $data=null)
Create an IApiMessage for the message.
Definition: ApiMessage.php:212
ApiBase\PARAM_EXTRA_NAMESPACES
const PARAM_EXTRA_NAMESPACES
(int[]) When PARAM_TYPE is 'namespace', include these as additional possible values.
Definition: ApiBase.php:189
ApiBase\ALL_DEFAULT_STRING
const ALL_DEFAULT_STRING
Definition: ApiBase.php:222
wfDebug
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:1047
string
This code would result in ircNotify being run twice when an article is and once for brion Hooks can return three possible true was required This is the default since MediaWiki *some string
Definition: hooks.txt:175
ContextSource\setContext
setContext(IContextSource $context)
Set the IContextSource object.
Definition: ContextSource.php:58
ApiBase\getHelpUrls
getHelpUrls()
Return links to more detailed help pages about the module.
Definition: ApiBase.php:370
ApiBase\validateUser
validateUser( $value, $encParamName)
Validate and normalize of parameters of type 'user'.
Definition: ApiBase.php:1591
ApiBase\getWatchlistValue
getWatchlistValue( $watchlist, $titleObj, $userOption=null)
Return true if we're to watch the page, false if not, null if no change.
Definition: ApiBase.php:982
ApiBase\needsToken
needsToken()
Returns the token type this module requires in order to execute.
Definition: ApiBase.php:468
ApiBase\getModulePrefix
getModulePrefix()
Get parameter prefix (usually two letters or an empty string).
Definition: ApiBase.php:520
ApiBase\$mModuleName
string $mModuleName
Definition: ApiBase.php:246
$request
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g. with the RejectParserCacheValue hook) because MediaWiki won 't do it for you. & $defaults also a ContextSource after deleting those rows but within the same transaction you ll probably need to make sure the header is varied on $request
Definition: hooks.txt:2581
ApiBase\setWatch
setWatch( $watch, $titleObj, $userOption=null)
Set a watch (or unwatch) based the based on a watchlist parameter.
Definition: ApiBase.php:1616
ApiBase\getContinuationManager
getContinuationManager()
Get the continuation manager.
Definition: ApiBase.php:672
$line
$line
Definition: cdb.php:58
ApiBase\addError
addError( $msg, $code=null, $data=null)
Add an error for this module without aborting.
Definition: ApiBase.php:1826
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:557
ApiBase\extractRequestParams
extractRequestParams( $parseLimit=true)
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:740
ApiBase\getWatchlistUser
getWatchlistUser( $params)
Gets the user for whom to get the watchlist.
Definition: ApiBase.php:1647
$value
$value
Definition: styleTest.css.php:45
ApiBase\dieContinueUsageIf
dieContinueUsageIf( $condition)
Die with the 'badcontinue' error.
Definition: ApiBase.php:2026
ApiBase\addDeprecation
addDeprecation( $msg, $feature, $data=[])
Add a deprecation warning for this module.
Definition: ApiBase.php:1793
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:81
ApiBase\LIMIT_SML2
const LIMIT_SML2
Slow query, apihighlimits limit.
Definition: ApiBase.php:231
ApiBase\encodeParamName
encodeParamName( $paramName)
This method mangles parameter name based on the prefix supplied to the constructor.
Definition: ApiBase.php:721
ApiBase\dieUsage
dieUsage( $description, $errorCode, $httpRespCode=0, $extradata=null)
Throw an ApiUsageException, which will (if uncaught) call the main module's error handler and die wit...
Definition: ApiBase.php:2660
ApiBase\dieReadOnly
dieReadOnly()
Helper function for readonly errors.
Definition: ApiBase.php:1950
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:238
ApiBase\checkTitleUserPermissions
checkTitleUserPermissions(Title $title, $actions, $user=null)
Helper function for permission-denied errors.
Definition: ApiBase.php:1984
WikiPage\newFromID
static newFromID( $id, $from='fromdb')
Constructor from a page id.
Definition: WikiPage.php:159
ApiBase\useTransactionalTimeLimit
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
Definition: ApiBase.php:2631
ApiBase\getConditionalRequestData
getConditionalRequestData( $condition)
Returns data for HTTP conditional request mechanisms.
Definition: ApiBase.php:497
ApiBase\isWriteMode
isWriteMode()
Indicates whether this module requires write mode.
Definition: ApiBase.php:419
wfEscapeWikiText
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Definition: GlobalFunctions.php:1703
$ret
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
Definition: hooks.txt:1965
ApiBase\requireMaxOneParameter
requireMaxOneParameter( $params, $required)
Die if more than one of a certain set of parameters is set and not false.
Definition: ApiBase.php:814
ApiBase\requireOnlyOneParameter
requireOnlyOneParameter( $params, $required)
Die if none or more than one of a certain set of parameters is set and not false.
Definition: ApiBase.php:776
ApiBase\PARAM_HELP_MSG_INFO
const PARAM_HELP_MSG_INFO
(array) Specify additional information tags for the parameter.
Definition: ApiBase.php:145
ApiBase\$mParamCache
$mParamCache
Definition: ApiBase.php:248
Block\TYPE_AUTO
const TYPE_AUTO
Definition: Block.php:86
ApiBase\truncateArray
static truncateArray(&$arr, $limit)
Truncate an array to a certain length.
Definition: ApiBase.php:1631
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:55
ApiBase\profileIn
profileIn()
Definition: ApiBase.php:2575
wfGetAllCallers
wfGetAllCallers( $limit=3)
Return a string consisting of callers in the stack.
Definition: GlobalFunctions.php:1623
ApiBase\PARAM_RANGE_ENFORCE
const PARAM_RANGE_ENFORCE
(boolean) For PARAM_TYPE 'integer', enforce PARAM_MIN and PARAM_MAX?
Definition: ApiBase.php:121
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:152
ChangeTags\canAddTagsAccompanyingChange
static canAddTagsAccompanyingChange(array $tags, User $user=null)
Is it OK to allow the user to apply all the specified tags at the same time as they edit/make the cha...
Definition: ChangeTags.php:421
ApiBase\requireAtLeastOneParameter
requireAtLeastOneParameter( $params, $required)
Die if none of a certain set of parameters is set and not false.
Definition: ApiBase.php:842
ApiBase\setWarning
setWarning( $warning)
Definition: ApiBase.php:2641
Title
Represents a title within MediaWiki.
Definition: Title.php:39
ApiBase\getProfileTime
getProfileTime()
Definition: ApiBase.php:2599
ApiBase\setContinuationManager
setContinuationManager( $manager)
Set the continuation manager.
Definition: ApiBase.php:686
ApiBase\LIMIT_BIG2
const LIMIT_BIG2
Fast query, apihighlimits limit.
Definition: ApiBase.php:227
wfReadOnlyReason
wfReadOnlyReason()
Check if the site is in read-only mode and return the message if so.
Definition: GlobalFunctions.php:1337
ApiQueryTokens\getToken
static getToken(User $user, MediaWiki\Session\Session $session, $salt)
Get a token from a salt.
Definition: ApiQueryTokens.php:96
$options
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:1965
ApiBase\getModuleManager
getModuleManager()
Get the module manager, or null if this module has no sub-modules.
Definition: ApiBase.php:295
$ext
$ext
Definition: NoLocalSettings.php:25
$code
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable & $code
Definition: hooks.txt:781
ApiBase\isInternal
isInternal()
Indicates whether this module is "internal" Internal API modules are not (yet) intended for 3rd party...
Definition: ApiBase.php:446
ApiBase\getModuleSourceInfo
getModuleSourceInfo()
Returns information about the source of this module, if known.
Definition: ApiBase.php:2414
ApiBase\validateLimit
validateLimit( $paramName, &$value, $min, $max, $botMax=null, $enforceLimits=false)
Validate the value against the minimum and user/bot maximum limits.
Definition: ApiBase.php:1466
$path
$path
Definition: NoLocalSettings.php:26
ApiBase\PARAM_DFLT
const PARAM_DFLT
(null|boolean|integer|string) Default value of the parameter.
Definition: ApiBase.php:52
ApiBase\getParameter
getParameter( $paramName, $parseLimit=true)
Get a value for the given parameter.
Definition: ApiBase.php:764
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
ApiBase\safeProfileOut
safeProfileOut()
Definition: ApiBase.php:2591
Block
Definition: Block.php:27
ApiBase\dieStatus
dieStatus(StatusValue $status)
Throw an ApiUsageException based on the Status object.
Definition: ApiBase.php:1920
ApiBase\getModuleName
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:512
ApiBase\PARAM_ISMULTI
const PARAM_ISMULTI
(boolean) Accept multiple pipe-separated values for this parameter (e.g.
Definition: ApiBase.php:55
NS_USER
const NS_USER
Definition: Defines.php:67
ApiBase\PARAM_MAX2
const PARAM_MAX2
(integer) Max value allowed for the parameter for users with the apihighlimits right,...
Definition: ApiBase.php:100
ApiBase\getAllowedParams
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
Definition: ApiBase.php:386
ApiBase\parseMultiValue
parseMultiValue( $valueName, $value, $allowMultiple, $allowedValues, $allSpecifier=null, $limit1=null, $limit2=null)
Return an array of values that were given in a 'a|b|c' notation, after it optionally validates them a...
Definition: ApiBase.php:1378
$help
$help
Definition: mcc.php:32
ApiBase\getMain
getMain()
Get the main module.
Definition: ApiBase.php:528
ApiBase\getParameterFromSettings
getParameterFromSettings( $paramName, $paramSettings, $parseLimit)
Using the settings determine the value for the given parameter.
Definition: ApiBase.php:1023
wfMessage
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "&lt
ApiBase\getTitleFromTitleOrPageId
getTitleFromTitleOrPageId( $params)
Get a Title object from a title or pageid param, if possible.
Definition: ApiBase.php:954
User\IGNORE_USER_RIGHTS
const IGNORE_USER_RIGHTS
Definition: User.php:88
ApiBase\getExamples
getExamples()
Returns usage examples for this module.
Definition: ApiBase.php:2558
ApiBase\$mModulePrefix
string $mModulePrefix
Definition: ApiBase.php:246
RawMessage
Variant of the Message class.
Definition: RawMessage.php:34
ApiBase\PARAM_HELP_MSG_PER_VALUE
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, this is an array mapping those values to $msg...
Definition: ApiBase.php:160
ApiBase\PARAM_SUBMODULE_PARAM_PREFIX
const PARAM_SUBMODULE_PARAM_PREFIX
(string) When PARAM_TYPE is 'submodule', used to indicate the 'g' prefix added by ApiQueryGeneratorBa...
Definition: ApiBase.php:175
ApiHelpParamValueMessage
Message subclass that prepends wikitext for API help.
Definition: ApiHelpParamValueMessage.php:36
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:51
Title\newFromID
static newFromID( $id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:411
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:203
ApiBase\dieDebug
static dieDebug( $method, $message)
Internal code errors should be reported with this method.
Definition: ApiBase.php:2038
ApiBase\execute
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
ApiBase\errorArrayToStatus
errorArrayToStatus(array $errors, User $user=null)
Turn an array of message keys or key+param arrays into a Status.
Definition: ApiBase.php:1726
ApiBase\getErrorFormatter
getErrorFormatter()
Get the error formatter.
Definition: ApiBase.php:646
ApiBase\requirePostedParameters
requirePostedParameters( $params, $prefix='prefix')
Die if any of the specified parameters were found in the query part of the URL rather than the post b...
Definition: ApiBase.php:872
$flags
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2801
ApiQueryUserInfo\getBlockInfo
static getBlockInfo(Block $block)
Get basic info about a given block.
Definition: ApiQueryUserInfo.php:69
ApiBase\getErrorFromStatus
getErrorFromStatus( $status, &$extraData=null)
Get error (as code, string) from a Status object.
Definition: ApiBase.php:2680
ApiBase\LIMIT_SML1
const LIMIT_SML1
Slow query, standard limit.
Definition: ApiBase.php:229
array
the array() calling protocol came about after MediaWiki 1.4rc1.
ApiBase\getHelpFlags
getHelpFlags()
Generates the list of flags for the help screen and for action=paraminfo.
Definition: ApiBase.php:2381
$type
$type
Definition: testCompression.php:48