MediaWiki  1.31.0
ApiBase.php
Go to the documentation of this file.
1 <?php
24 
37 abstract class ApiBase extends ContextSource {
38 
48  const PARAM_DFLT = 0;
49 
51  const PARAM_ISMULTI = 1;
52 
87  const PARAM_TYPE = 2;
88 
90  const PARAM_MAX = 3;
91 
96  const PARAM_MAX2 = 4;
97 
99  const PARAM_MIN = 5;
100 
103 
105  const PARAM_DEPRECATED = 7;
106 
111  const PARAM_REQUIRED = 8;
112 
118 
124  const PARAM_HELP_MSG = 10;
125 
132 
142 
148  const PARAM_VALUE_LINKS = 13;
149 
158 
166 
173 
180  const PARAM_ALL = 17;
181 
187 
193  const PARAM_SENSITIVE = 19;
194 
203 
209 
216 
221  const PARAM_MAX_BYTES = 23;
222 
227  const PARAM_MAX_CHARS = 24;
228 
231  const ALL_DEFAULT_STRING = '*';
232 
234  const LIMIT_BIG1 = 500;
236  const LIMIT_BIG2 = 5000;
238  const LIMIT_SML1 = 50;
240  const LIMIT_SML2 = 500;
241 
248 
250  private static $extensionInfo = null;
251 
253  private $mMainModule;
256  private $mSlaveDB = null;
257  private $mParamCache = [];
259  private $mModuleSource = false;
260 
266  public function __construct( ApiMain $mainModule, $moduleName, $modulePrefix = '' ) {
267  $this->mMainModule = $mainModule;
268  $this->mModuleName = $moduleName;
269  $this->mModulePrefix = $modulePrefix;
270 
271  if ( !$this->isMain() ) {
272  $this->setContext( $mainModule->getContext() );
273  }
274  }
275 
276  /************************************************************************/
297  abstract public function execute();
298 
304  public function getModuleManager() {
305  return null;
306  }
307 
317  public function getCustomPrinter() {
318  return null;
319  }
320 
332  protected function getExamplesMessages() {
333  // Fall back to old non-localised method
334  $ret = [];
335 
336  $examples = $this->getExamples();
337  if ( $examples ) {
338  if ( !is_array( $examples ) ) {
339  $examples = [ $examples ];
340  } elseif ( $examples && ( count( $examples ) & 1 ) == 0 &&
341  array_keys( $examples ) === range( 0, count( $examples ) - 1 ) &&
342  !preg_match( '/^\s*api\.php\?/', $examples[0] )
343  ) {
344  // Fix up the ugly "even numbered elements are description, odd
345  // numbered elemts are the link" format (see doc for self::getExamples)
346  $tmp = [];
347  $examplesCount = count( $examples );
348  for ( $i = 0; $i < $examplesCount; $i += 2 ) {
349  $tmp[$examples[$i + 1]] = $examples[$i];
350  }
351  $examples = $tmp;
352  }
353 
354  foreach ( $examples as $k => $v ) {
355  if ( is_numeric( $k ) ) {
356  $qs = $v;
357  $msg = '';
358  } else {
359  $qs = $k;
360  $msg = self::escapeWikiText( $v );
361  if ( is_array( $msg ) ) {
362  $msg = implode( ' ', $msg );
363  }
364  }
365 
366  $qs = preg_replace( '/^\s*api\.php\?/', '', $qs );
367  $ret[$qs] = $this->msg( 'api-help-fallback-example', [ $msg ] );
368  }
369  }
370 
371  return $ret;
372  }
373 
379  public function getHelpUrls() {
380  return [];
381  }
382 
395  protected function getAllowedParams( /* $flags = 0 */ ) {
396  // int $flags is not declared because it causes "Strict standards"
397  // warning. Most derived classes do not implement it.
398  return [];
399  }
400 
405  public function shouldCheckMaxlag() {
406  return true;
407  }
408 
413  public function isReadMode() {
414  return true;
415  }
416 
428  public function isWriteMode() {
429  return false;
430  }
431 
436  public function mustBePosted() {
437  return $this->needsToken() !== false;
438  }
439 
445  public function isDeprecated() {
446  return false;
447  }
448 
455  public function isInternal() {
456  return false;
457  }
458 
477  public function needsToken() {
478  return false;
479  }
480 
490  protected function getWebUITokenSalt( array $params ) {
491  return null;
492  }
493 
506  public function getConditionalRequestData( $condition ) {
507  return null;
508  }
509 
512  /************************************************************************/
521  public function getModuleName() {
522  return $this->mModuleName;
523  }
524 
529  public function getModulePrefix() {
530  return $this->mModulePrefix;
531  }
532 
537  public function getMain() {
538  return $this->mMainModule;
539  }
540 
546  public function isMain() {
547  return $this === $this->mMainModule;
548  }
549 
555  public function getParent() {
556  return $this->isMain() ? null : $this->getMain();
557  }
558 
569  public function lacksSameOriginSecurity() {
570  // Main module has this method overridden
571  // Safety - avoid infinite loop:
572  if ( $this->isMain() ) {
573  self::dieDebug( __METHOD__, 'base method was called on main module.' );
574  }
575 
576  return $this->getMain()->lacksSameOriginSecurity();
577  }
578 
585  public function getModulePath() {
586  if ( $this->isMain() ) {
587  return 'main';
588  } elseif ( $this->getParent()->isMain() ) {
589  return $this->getModuleName();
590  } else {
591  return $this->getParent()->getModulePath() . '+' . $this->getModuleName();
592  }
593  }
594 
603  public function getModuleFromPath( $path ) {
604  $module = $this->getMain();
605  if ( $path === 'main' ) {
606  return $module;
607  }
608 
609  $parts = explode( '+', $path );
610  if ( count( $parts ) === 1 ) {
611  // In case the '+' was typed into URL, it resolves as a space
612  $parts = explode( ' ', $path );
613  }
614 
615  $count = count( $parts );
616  for ( $i = 0; $i < $count; $i++ ) {
617  $parent = $module;
618  $manager = $parent->getModuleManager();
619  if ( $manager === null ) {
620  $errorPath = implode( '+', array_slice( $parts, 0, $i ) );
621  $this->dieWithError( [ 'apierror-badmodule-nosubmodules', $errorPath ], 'badmodule' );
622  }
623  $module = $manager->getModule( $parts[$i] );
624 
625  if ( $module === null ) {
626  $errorPath = $i ? implode( '+', array_slice( $parts, 0, $i ) ) : $parent->getModuleName();
627  $this->dieWithError(
628  [ 'apierror-badmodule-badsubmodule', $errorPath, wfEscapeWikiText( $parts[$i] ) ],
629  'badmodule'
630  );
631  }
632  }
633 
634  return $module;
635  }
636 
641  public function getResult() {
642  // Main module has getResult() method overridden
643  // Safety - avoid infinite loop:
644  if ( $this->isMain() ) {
645  self::dieDebug( __METHOD__, 'base method was called on main module. ' );
646  }
647 
648  return $this->getMain()->getResult();
649  }
650 
655  public function getErrorFormatter() {
656  // Main module has getErrorFormatter() method overridden
657  // Safety - avoid infinite loop:
658  if ( $this->isMain() ) {
659  self::dieDebug( __METHOD__, 'base method was called on main module. ' );
660  }
661 
662  return $this->getMain()->getErrorFormatter();
663  }
664 
669  protected function getDB() {
670  if ( !isset( $this->mSlaveDB ) ) {
671  $this->mSlaveDB = wfGetDB( DB_REPLICA, 'api' );
672  }
673 
674  return $this->mSlaveDB;
675  }
676 
681  public function getContinuationManager() {
682  // Main module has getContinuationManager() method overridden
683  // Safety - avoid infinite loop:
684  if ( $this->isMain() ) {
685  self::dieDebug( __METHOD__, 'base method was called on main module. ' );
686  }
687 
688  return $this->getMain()->getContinuationManager();
689  }
690 
695  public function setContinuationManager( ApiContinuationManager $manager = null ) {
696  // Main module has setContinuationManager() method overridden
697  // Safety - avoid infinite loop:
698  if ( $this->isMain() ) {
699  self::dieDebug( __METHOD__, 'base method was called on main module. ' );
700  }
701 
702  $this->getMain()->setContinuationManager( $manager );
703  }
704 
707  /************************************************************************/
719  public function dynamicParameterDocumentation() {
720  return null;
721  }
722 
730  public function encodeParamName( $paramName ) {
731  if ( is_array( $paramName ) ) {
732  return array_map( function ( $name ) {
733  return $this->mModulePrefix . $name;
734  }, $paramName );
735  } else {
736  return $this->mModulePrefix . $paramName;
737  }
738  }
739 
749  public function extractRequestParams( $parseLimit = true ) {
750  // Cache parameters, for performance and to avoid T26564.
751  if ( !isset( $this->mParamCache[$parseLimit] ) ) {
752  $params = $this->getFinalParams();
753  $results = [];
754 
755  if ( $params ) { // getFinalParams() can return false
756  foreach ( $params as $paramName => $paramSettings ) {
757  $results[$paramName] = $this->getParameterFromSettings(
758  $paramName, $paramSettings, $parseLimit );
759  }
760  }
761  $this->mParamCache[$parseLimit] = $results;
762  }
763 
764  return $this->mParamCache[$parseLimit];
765  }
766 
773  protected function getParameter( $paramName, $parseLimit = true ) {
774  $paramSettings = $this->getFinalParams()[$paramName];
775 
776  return $this->getParameterFromSettings( $paramName, $paramSettings, $parseLimit );
777  }
778 
785  public function requireOnlyOneParameter( $params, $required /*...*/ ) {
786  $required = func_get_args();
787  array_shift( $required );
788 
789  $intersection = array_intersect( array_keys( array_filter( $params,
790  [ $this, 'parameterNotEmpty' ] ) ), $required );
791 
792  if ( count( $intersection ) > 1 ) {
793  $this->dieWithError( [
794  'apierror-invalidparammix',
795  Message::listParam( array_map(
796  function ( $p ) {
797  return '<var>' . $this->encodeParamName( $p ) . '</var>';
798  },
799  array_values( $intersection )
800  ) ),
801  count( $intersection ),
802  ] );
803  } elseif ( count( $intersection ) == 0 ) {
804  $this->dieWithError( [
805  'apierror-missingparam-one-of',
806  Message::listParam( array_map(
807  function ( $p ) {
808  return '<var>' . $this->encodeParamName( $p ) . '</var>';
809  },
810  array_values( $required )
811  ) ),
812  count( $required ),
813  ], 'missingparam' );
814  }
815  }
816 
823  public function requireMaxOneParameter( $params, $required /*...*/ ) {
824  $required = func_get_args();
825  array_shift( $required );
826 
827  $intersection = array_intersect( array_keys( array_filter( $params,
828  [ $this, 'parameterNotEmpty' ] ) ), $required );
829 
830  if ( count( $intersection ) > 1 ) {
831  $this->dieWithError( [
832  'apierror-invalidparammix',
833  Message::listParam( array_map(
834  function ( $p ) {
835  return '<var>' . $this->encodeParamName( $p ) . '</var>';
836  },
837  array_values( $intersection )
838  ) ),
839  count( $intersection ),
840  ] );
841  }
842  }
843 
851  public function requireAtLeastOneParameter( $params, $required /*...*/ ) {
852  $required = func_get_args();
853  array_shift( $required );
854 
855  $intersection = array_intersect(
856  array_keys( array_filter( $params, [ $this, 'parameterNotEmpty' ] ) ),
857  $required
858  );
859 
860  if ( count( $intersection ) == 0 ) {
861  $this->dieWithError( [
862  'apierror-missingparam-at-least-one-of',
863  Message::listParam( array_map(
864  function ( $p ) {
865  return '<var>' . $this->encodeParamName( $p ) . '</var>';
866  },
867  array_values( $required )
868  ) ),
869  count( $required ),
870  ], 'missingparam' );
871  }
872  }
873 
881  public function requirePostedParameters( $params, $prefix = 'prefix' ) {
882  // Skip if $wgDebugAPI is set or we're in internal mode
883  if ( $this->getConfig()->get( 'DebugAPI' ) || $this->getMain()->isInternalMode() ) {
884  return;
885  }
886 
887  $queryValues = $this->getRequest()->getQueryValues();
888  $badParams = [];
889  foreach ( $params as $param ) {
890  if ( $prefix !== 'noprefix' ) {
891  $param = $this->encodeParamName( $param );
892  }
893  if ( array_key_exists( $param, $queryValues ) ) {
894  $badParams[] = $param;
895  }
896  }
897 
898  if ( $badParams ) {
899  $this->dieWithError(
900  [ 'apierror-mustpostparams', implode( ', ', $badParams ), count( $badParams ) ]
901  );
902  }
903  }
904 
911  private function parameterNotEmpty( $x ) {
912  return !is_null( $x ) && $x !== false;
913  }
914 
926  public function getTitleOrPageId( $params, $load = false ) {
927  $this->requireOnlyOneParameter( $params, 'title', 'pageid' );
928 
929  $pageObj = null;
930  if ( isset( $params['title'] ) ) {
931  $titleObj = Title::newFromText( $params['title'] );
932  if ( !$titleObj || $titleObj->isExternal() ) {
933  $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['title'] ) ] );
934  }
935  if ( !$titleObj->canExist() ) {
936  $this->dieWithError( 'apierror-pagecannotexist' );
937  }
938  $pageObj = WikiPage::factory( $titleObj );
939  if ( $load !== false ) {
940  $pageObj->loadPageData( $load );
941  }
942  } elseif ( isset( $params['pageid'] ) ) {
943  if ( $load === false ) {
944  $load = 'fromdb';
945  }
946  $pageObj = WikiPage::newFromID( $params['pageid'], $load );
947  if ( !$pageObj ) {
948  $this->dieWithError( [ 'apierror-nosuchpageid', $params['pageid'] ] );
949  }
950  }
951 
952  return $pageObj;
953  }
954 
963  public function getTitleFromTitleOrPageId( $params ) {
964  $this->requireOnlyOneParameter( $params, 'title', 'pageid' );
965 
966  $titleObj = null;
967  if ( isset( $params['title'] ) ) {
968  $titleObj = Title::newFromText( $params['title'] );
969  if ( !$titleObj || $titleObj->isExternal() ) {
970  $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['title'] ) ] );
971  }
972  return $titleObj;
973  } elseif ( isset( $params['pageid'] ) ) {
974  $titleObj = Title::newFromID( $params['pageid'] );
975  if ( !$titleObj ) {
976  $this->dieWithError( [ 'apierror-nosuchpageid', $params['pageid'] ] );
977  }
978  }
979 
980  return $titleObj;
981  }
982 
991  protected function getWatchlistValue( $watchlist, $titleObj, $userOption = null ) {
992  $userWatching = $this->getUser()->isWatched( $titleObj, User::IGNORE_USER_RIGHTS );
993 
994  switch ( $watchlist ) {
995  case 'watch':
996  return true;
997 
998  case 'unwatch':
999  return false;
1000 
1001  case 'preferences':
1002  # If the user is already watching, don't bother checking
1003  if ( $userWatching ) {
1004  return true;
1005  }
1006  # If no user option was passed, use watchdefault and watchcreations
1007  if ( is_null( $userOption ) ) {
1008  return $this->getUser()->getBoolOption( 'watchdefault' ) ||
1009  $this->getUser()->getBoolOption( 'watchcreations' ) && !$titleObj->exists();
1010  }
1011 
1012  # Watch the article based on the user preference
1013  return $this->getUser()->getBoolOption( $userOption );
1014 
1015  case 'nochange':
1016  return $userWatching;
1017 
1018  default:
1019  return $userWatching;
1020  }
1021  }
1022 
1032  protected function getParameterFromSettings( $paramName, $paramSettings, $parseLimit ) {
1033  // Some classes may decide to change parameter names
1034  $encParamName = $this->encodeParamName( $paramName );
1035 
1036  // Shorthand
1037  if ( !is_array( $paramSettings ) ) {
1038  $paramSettings = [
1039  self::PARAM_DFLT => $paramSettings,
1040  ];
1041  }
1042 
1043  $default = isset( $paramSettings[self::PARAM_DFLT] )
1044  ? $paramSettings[self::PARAM_DFLT]
1045  : null;
1046  $multi = isset( $paramSettings[self::PARAM_ISMULTI] )
1047  ? $paramSettings[self::PARAM_ISMULTI]
1048  : false;
1049  $multiLimit1 = isset( $paramSettings[self::PARAM_ISMULTI_LIMIT1] )
1050  ? $paramSettings[self::PARAM_ISMULTI_LIMIT1]
1051  : null;
1052  $multiLimit2 = isset( $paramSettings[self::PARAM_ISMULTI_LIMIT2] )
1053  ? $paramSettings[self::PARAM_ISMULTI_LIMIT2]
1054  : null;
1055  $type = isset( $paramSettings[self::PARAM_TYPE] )
1056  ? $paramSettings[self::PARAM_TYPE]
1057  : null;
1058  $dupes = isset( $paramSettings[self::PARAM_ALLOW_DUPLICATES] )
1059  ? $paramSettings[self::PARAM_ALLOW_DUPLICATES]
1060  : false;
1061  $deprecated = isset( $paramSettings[self::PARAM_DEPRECATED] )
1062  ? $paramSettings[self::PARAM_DEPRECATED]
1063  : false;
1064  $deprecatedValues = isset( $paramSettings[self::PARAM_DEPRECATED_VALUES] )
1065  ? $paramSettings[self::PARAM_DEPRECATED_VALUES]
1066  : [];
1067  $required = isset( $paramSettings[self::PARAM_REQUIRED] )
1068  ? $paramSettings[self::PARAM_REQUIRED]
1069  : false;
1070  $allowAll = isset( $paramSettings[self::PARAM_ALL] )
1071  ? $paramSettings[self::PARAM_ALL]
1072  : false;
1073 
1074  // When type is not given, and no choices, the type is the same as $default
1075  if ( !isset( $type ) ) {
1076  if ( isset( $default ) ) {
1077  $type = gettype( $default );
1078  } else {
1079  $type = 'NULL'; // allow everything
1080  }
1081  }
1082 
1083  if ( $type == 'password' || !empty( $paramSettings[self::PARAM_SENSITIVE] ) ) {
1084  $this->getMain()->markParamsSensitive( $encParamName );
1085  }
1086 
1087  if ( $type == 'boolean' ) {
1088  if ( isset( $default ) && $default !== false ) {
1089  // Having a default value of anything other than 'false' is not allowed
1091  __METHOD__,
1092  "Boolean param $encParamName's default is set to '$default'. " .
1093  'Boolean parameters must default to false.'
1094  );
1095  }
1096 
1097  $value = $this->getMain()->getCheck( $encParamName );
1098  } elseif ( $type == 'upload' ) {
1099  if ( isset( $default ) ) {
1100  // Having a default value is not allowed
1102  __METHOD__,
1103  "File upload param $encParamName's default is set to " .
1104  "'$default'. File upload parameters may not have a default." );
1105  }
1106  if ( $multi ) {
1107  self::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
1108  }
1109  $value = $this->getMain()->getUpload( $encParamName );
1110  if ( !$value->exists() ) {
1111  // This will get the value without trying to normalize it
1112  // (because trying to normalize a large binary file
1113  // accidentally uploaded as a field fails spectacularly)
1114  $value = $this->getMain()->getRequest()->unsetVal( $encParamName );
1115  if ( $value !== null ) {
1116  $this->dieWithError(
1117  [ 'apierror-badupload', $encParamName ],
1118  "badupload_{$encParamName}"
1119  );
1120  }
1121  }
1122  } else {
1123  $value = $this->getMain()->getVal( $encParamName, $default );
1124 
1125  if ( isset( $value ) && $type == 'namespace' ) {
1127  if ( isset( $paramSettings[self::PARAM_EXTRA_NAMESPACES] ) &&
1128  is_array( $paramSettings[self::PARAM_EXTRA_NAMESPACES] )
1129  ) {
1130  $type = array_merge( $type, $paramSettings[self::PARAM_EXTRA_NAMESPACES] );
1131  }
1132  // Namespace parameters allow ALL_DEFAULT_STRING to be used to
1133  // specify all namespaces irrespective of PARAM_ALL.
1134  $allowAll = true;
1135  }
1136  if ( isset( $value ) && $type == 'submodule' ) {
1137  if ( isset( $paramSettings[self::PARAM_SUBMODULE_MAP] ) ) {
1138  $type = array_keys( $paramSettings[self::PARAM_SUBMODULE_MAP] );
1139  } else {
1140  $type = $this->getModuleManager()->getNames( $paramName );
1141  }
1142  }
1143 
1144  $request = $this->getMain()->getRequest();
1145  $rawValue = $request->getRawVal( $encParamName );
1146  if ( $rawValue === null ) {
1147  $rawValue = $default;
1148  }
1149 
1150  // Preserve U+001F for self::parseMultiValue(), or error out if that won't be called
1151  if ( isset( $value ) && substr( $rawValue, 0, 1 ) === "\x1f" ) {
1152  if ( $multi ) {
1153  // This loses the potential $wgContLang->checkTitleEncoding() transformation
1154  // done by WebRequest for $_GET. Let's call that a feature.
1155  $value = implode( "\x1f", $request->normalizeUnicode( explode( "\x1f", $rawValue ) ) );
1156  } else {
1157  $this->dieWithError( 'apierror-badvalue-notmultivalue', 'badvalue_notmultivalue' );
1158  }
1159  }
1160 
1161  // Check for NFC normalization, and warn
1162  if ( $rawValue !== $value ) {
1163  $this->handleParamNormalization( $paramName, $value, $rawValue );
1164  }
1165  }
1166 
1167  $allSpecifier = ( is_string( $allowAll ) ? $allowAll : self::ALL_DEFAULT_STRING );
1168  if ( $allowAll && $multi && is_array( $type ) && in_array( $allSpecifier, $type, true ) ) {
1170  __METHOD__,
1171  "For param $encParamName, PARAM_ALL collides with a possible value" );
1172  }
1173  if ( isset( $value ) && ( $multi || is_array( $type ) ) ) {
1174  $value = $this->parseMultiValue(
1175  $encParamName,
1176  $value,
1177  $multi,
1178  is_array( $type ) ? $type : null,
1179  $allowAll ? $allSpecifier : null,
1180  $multiLimit1,
1181  $multiLimit2
1182  );
1183  }
1184 
1185  if ( isset( $value ) ) {
1186  // More validation only when choices were not given
1187  // choices were validated in parseMultiValue()
1188  if ( !is_array( $type ) ) {
1189  switch ( $type ) {
1190  case 'NULL': // nothing to do
1191  break;
1192  case 'string':
1193  case 'text':
1194  case 'password':
1195  if ( $required && $value === '' ) {
1196  $this->dieWithError( [ 'apierror-missingparam', $paramName ] );
1197  }
1198  break;
1199  case 'integer': // Force everything using intval() and optionally validate limits
1200  $min = isset( $paramSettings[self::PARAM_MIN] ) ? $paramSettings[self::PARAM_MIN] : null;
1201  $max = isset( $paramSettings[self::PARAM_MAX] ) ? $paramSettings[self::PARAM_MAX] : null;
1202  $enforceLimits = isset( $paramSettings[self::PARAM_RANGE_ENFORCE] )
1203  ? $paramSettings[self::PARAM_RANGE_ENFORCE] : false;
1204 
1205  if ( is_array( $value ) ) {
1206  $value = array_map( 'intval', $value );
1207  if ( !is_null( $min ) || !is_null( $max ) ) {
1208  foreach ( $value as &$v ) {
1209  $this->validateLimit( $paramName, $v, $min, $max, null, $enforceLimits );
1210  }
1211  }
1212  } else {
1213  $value = intval( $value );
1214  if ( !is_null( $min ) || !is_null( $max ) ) {
1215  $this->validateLimit( $paramName, $value, $min, $max, null, $enforceLimits );
1216  }
1217  }
1218  break;
1219  case 'limit':
1220  if ( !$parseLimit ) {
1221  // Don't do any validation whatsoever
1222  break;
1223  }
1224  if ( !isset( $paramSettings[self::PARAM_MAX] )
1225  || !isset( $paramSettings[self::PARAM_MAX2] )
1226  ) {
1228  __METHOD__,
1229  "MAX1 or MAX2 are not defined for the limit $encParamName"
1230  );
1231  }
1232  if ( $multi ) {
1233  self::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
1234  }
1235  $min = isset( $paramSettings[self::PARAM_MIN] ) ? $paramSettings[self::PARAM_MIN] : 0;
1236  if ( $value == 'max' ) {
1237  $value = $this->getMain()->canApiHighLimits()
1238  ? $paramSettings[self::PARAM_MAX2]
1239  : $paramSettings[self::PARAM_MAX];
1240  $this->getResult()->addParsedLimit( $this->getModuleName(), $value );
1241  } else {
1242  $value = intval( $value );
1243  $this->validateLimit(
1244  $paramName,
1245  $value,
1246  $min,
1247  $paramSettings[self::PARAM_MAX],
1248  $paramSettings[self::PARAM_MAX2]
1249  );
1250  }
1251  break;
1252  case 'boolean':
1253  if ( $multi ) {
1254  self::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
1255  }
1256  break;
1257  case 'timestamp':
1258  if ( is_array( $value ) ) {
1259  foreach ( $value as $key => $val ) {
1260  $value[$key] = $this->validateTimestamp( $val, $encParamName );
1261  }
1262  } else {
1263  $value = $this->validateTimestamp( $value, $encParamName );
1264  }
1265  break;
1266  case 'user':
1267  if ( is_array( $value ) ) {
1268  foreach ( $value as $key => $val ) {
1269  $value[$key] = $this->validateUser( $val, $encParamName );
1270  }
1271  } else {
1272  $value = $this->validateUser( $value, $encParamName );
1273  }
1274  break;
1275  case 'upload': // nothing to do
1276  break;
1277  case 'tags':
1278  // If change tagging was requested, check that the tags are valid.
1279  if ( !is_array( $value ) && !$multi ) {
1280  $value = [ $value ];
1281  }
1283  if ( !$tagsStatus->isGood() ) {
1284  $this->dieStatus( $tagsStatus );
1285  }
1286  break;
1287  default:
1288  self::dieDebug( __METHOD__, "Param $encParamName's type is unknown - $type" );
1289  }
1290  }
1291 
1292  // Throw out duplicates if requested
1293  if ( !$dupes && is_array( $value ) ) {
1294  $value = array_unique( $value );
1295  }
1296 
1297  if ( in_array( $type, [ 'NULL', 'string', 'text', 'password' ], true ) ) {
1298  foreach ( (array)$value as $val ) {
1299  if ( isset( $paramSettings[self::PARAM_MAX_BYTES] )
1300  && strlen( $val ) > $paramSettings[self::PARAM_MAX_BYTES]
1301  ) {
1302  $this->dieWithError( [ 'apierror-maxbytes', $encParamName,
1303  $paramSettings[self::PARAM_MAX_BYTES] ] );
1304  }
1305  if ( isset( $paramSettings[self::PARAM_MAX_CHARS] )
1306  && mb_strlen( $val, 'UTF-8' ) > $paramSettings[self::PARAM_MAX_CHARS]
1307  ) {
1308  $this->dieWithError( [ 'apierror-maxchars', $encParamName,
1309  $paramSettings[self::PARAM_MAX_CHARS] ] );
1310  }
1311  }
1312  }
1313 
1314  // Set a warning if a deprecated parameter has been passed
1315  if ( $deprecated && $value !== false ) {
1316  $feature = $encParamName;
1317  $m = $this;
1318  while ( !$m->isMain() ) {
1319  $p = $m->getParent();
1320  $name = $m->getModuleName();
1321  $param = $p->encodeParamName( $p->getModuleManager()->getModuleGroup( $name ) );
1322  $feature = "{$param}={$name}&{$feature}";
1323  $m = $p;
1324  }
1325  $this->addDeprecation( [ 'apiwarn-deprecation-parameter', $encParamName ], $feature );
1326  }
1327 
1328  // Set a warning if a deprecated parameter value has been passed
1329  $usedDeprecatedValues = $deprecatedValues && $value !== false
1330  ? array_intersect( array_keys( $deprecatedValues ), (array)$value )
1331  : [];
1332  if ( $usedDeprecatedValues ) {
1333  $feature = "$encParamName=";
1334  $m = $this;
1335  while ( !$m->isMain() ) {
1336  $p = $m->getParent();
1337  $name = $m->getModuleName();
1338  $param = $p->encodeParamName( $p->getModuleManager()->getModuleGroup( $name ) );
1339  $feature = "{$param}={$name}&{$feature}";
1340  $m = $p;
1341  }
1342  foreach ( $usedDeprecatedValues as $v ) {
1343  $msg = $deprecatedValues[$v];
1344  if ( $msg === true ) {
1345  $msg = [ 'apiwarn-deprecation-parameter', "$encParamName=$v" ];
1346  }
1347  $this->addDeprecation( $msg, "$feature$v" );
1348  }
1349  }
1350  } elseif ( $required ) {
1351  $this->dieWithError( [ 'apierror-missingparam', $paramName ] );
1352  }
1353 
1354  return $value;
1355  }
1356 
1364  protected function handleParamNormalization( $paramName, $value, $rawValue ) {
1365  $encParamName = $this->encodeParamName( $paramName );
1366  $this->addWarning( [ 'apiwarn-badutf8', $encParamName ] );
1367  }
1368 
1376  protected function explodeMultiValue( $value, $limit ) {
1377  if ( substr( $value, 0, 1 ) === "\x1f" ) {
1378  $sep = "\x1f";
1379  $value = substr( $value, 1 );
1380  } else {
1381  $sep = '|';
1382  }
1383 
1384  return explode( $sep, $value, $limit );
1385  }
1386 
1404  protected function parseMultiValue( $valueName, $value, $allowMultiple, $allowedValues,
1405  $allSpecifier = null, $limit1 = null, $limit2 = null
1406  ) {
1407  if ( ( $value === '' || $value === "\x1f" ) && $allowMultiple ) {
1408  return [];
1409  }
1410  $limit1 = $limit1 ?: self::LIMIT_SML1;
1411  $limit2 = $limit2 ?: self::LIMIT_SML2;
1412 
1413  // This is a bit awkward, but we want to avoid calling canApiHighLimits()
1414  // because it unstubs $wgUser
1415  $valuesList = $this->explodeMultiValue( $value, $limit2 + 1 );
1416  $sizeLimit = count( $valuesList ) > $limit1 && $this->mMainModule->canApiHighLimits()
1417  ? $limit2
1418  : $limit1;
1419 
1420  if ( $allowMultiple && is_array( $allowedValues ) && $allSpecifier &&
1421  count( $valuesList ) === 1 && $valuesList[0] === $allSpecifier
1422  ) {
1423  return $allowedValues;
1424  }
1425 
1426  if ( self::truncateArray( $valuesList, $sizeLimit ) ) {
1427  $this->addDeprecation(
1428  [ 'apiwarn-toomanyvalues', $valueName, $sizeLimit ],
1429  "too-many-$valueName-for-{$this->getModulePath()}"
1430  );
1431  }
1432 
1433  if ( !$allowMultiple && count( $valuesList ) != 1 ) {
1434  // T35482 - Allow entries with | in them for non-multiple values
1435  if ( in_array( $value, $allowedValues, true ) ) {
1436  return $value;
1437  }
1438 
1439  $values = array_map( function ( $v ) {
1440  return '<kbd>' . wfEscapeWikiText( $v ) . '</kbd>';
1441  }, $allowedValues );
1442  $this->dieWithError( [
1443  'apierror-multival-only-one-of',
1444  $valueName,
1445  Message::listParam( $values ),
1446  count( $values ),
1447  ], "multival_$valueName" );
1448  }
1449 
1450  if ( is_array( $allowedValues ) ) {
1451  // Check for unknown values
1452  $unknown = array_map( 'wfEscapeWikiText', array_diff( $valuesList, $allowedValues ) );
1453  if ( count( $unknown ) ) {
1454  if ( $allowMultiple ) {
1455  $this->addWarning( [
1456  'apiwarn-unrecognizedvalues',
1457  $valueName,
1458  Message::listParam( $unknown, 'comma' ),
1459  count( $unknown ),
1460  ] );
1461  } else {
1462  $this->dieWithError(
1463  [ 'apierror-unrecognizedvalue', $valueName, wfEscapeWikiText( $valuesList[0] ) ],
1464  "unknown_$valueName"
1465  );
1466  }
1467  }
1468  // Now throw them out
1469  $valuesList = array_intersect( $valuesList, $allowedValues );
1470  }
1471 
1472  return $allowMultiple ? $valuesList : $valuesList[0];
1473  }
1474 
1485  protected function validateLimit( $paramName, &$value, $min, $max, $botMax = null,
1486  $enforceLimits = false
1487  ) {
1488  if ( !is_null( $min ) && $value < $min ) {
1489  $msg = ApiMessage::create(
1490  [ 'apierror-integeroutofrange-belowminimum',
1491  $this->encodeParamName( $paramName ), $min, $value ],
1492  'integeroutofrange',
1493  [ 'min' => $min, 'max' => $max, 'botMax' => $botMax ?: $max ]
1494  );
1495  $this->warnOrDie( $msg, $enforceLimits );
1496  $value = $min;
1497  }
1498 
1499  // Minimum is always validated, whereas maximum is checked only if not
1500  // running in internal call mode
1501  if ( $this->getMain()->isInternalMode() ) {
1502  return;
1503  }
1504 
1505  // Optimization: do not check user's bot status unless really needed -- skips db query
1506  // assumes $botMax >= $max
1507  if ( !is_null( $max ) && $value > $max ) {
1508  if ( !is_null( $botMax ) && $this->getMain()->canApiHighLimits() ) {
1509  if ( $value > $botMax ) {
1510  $msg = ApiMessage::create(
1511  [ 'apierror-integeroutofrange-abovebotmax',
1512  $this->encodeParamName( $paramName ), $botMax, $value ],
1513  'integeroutofrange',
1514  [ 'min' => $min, 'max' => $max, 'botMax' => $botMax ?: $max ]
1515  );
1516  $this->warnOrDie( $msg, $enforceLimits );
1517  $value = $botMax;
1518  }
1519  } else {
1520  $msg = ApiMessage::create(
1521  [ 'apierror-integeroutofrange-abovemax',
1522  $this->encodeParamName( $paramName ), $max, $value ],
1523  'integeroutofrange',
1524  [ 'min' => $min, 'max' => $max, 'botMax' => $botMax ?: $max ]
1525  );
1526  $this->warnOrDie( $msg, $enforceLimits );
1527  $value = $max;
1528  }
1529  }
1530  }
1531 
1538  protected function validateTimestamp( $value, $encParamName ) {
1539  // Confusing synonyms for the current time accepted by wfTimestamp()
1540  // (wfTimestamp() also accepts various non-strings and the string of 14
1541  // ASCII NUL bytes, but those can't get here)
1542  if ( !$value ) {
1543  $this->addDeprecation(
1544  [ 'apiwarn-unclearnowtimestamp', $encParamName, wfEscapeWikiText( $value ) ],
1545  'unclear-"now"-timestamp'
1546  );
1547  return wfTimestamp( TS_MW );
1548  }
1549 
1550  // Explicit synonym for the current time
1551  if ( $value === 'now' ) {
1552  return wfTimestamp( TS_MW );
1553  }
1554 
1555  $timestamp = wfTimestamp( TS_MW, $value );
1556  if ( $timestamp === false ) {
1557  $this->dieWithError(
1558  [ 'apierror-badtimestamp', $encParamName, wfEscapeWikiText( $value ) ],
1559  "badtimestamp_{$encParamName}"
1560  );
1561  }
1562 
1563  return $timestamp;
1564  }
1565 
1575  final public function validateToken( $token, array $params ) {
1576  $tokenType = $this->needsToken();
1578  if ( !isset( $salts[$tokenType] ) ) {
1579  throw new MWException(
1580  "Module '{$this->getModuleName()}' tried to use token type '$tokenType' " .
1581  'without registering it'
1582  );
1583  }
1584 
1585  $tokenObj = ApiQueryTokens::getToken(
1586  $this->getUser(), $this->getRequest()->getSession(), $salts[$tokenType]
1587  );
1588  if ( $tokenObj->match( $token ) ) {
1589  return true;
1590  }
1591 
1592  $webUiSalt = $this->getWebUITokenSalt( $params );
1593  if ( $webUiSalt !== null && $this->getUser()->matchEditToken(
1594  $token,
1595  $webUiSalt,
1596  $this->getRequest()
1597  ) ) {
1598  return true;
1599  }
1600 
1601  return false;
1602  }
1603 
1610  private function validateUser( $value, $encParamName ) {
1612  return $value;
1613  }
1614 
1615  $titleObj = Title::makeTitleSafe( NS_USER, $value );
1616 
1617  if ( $titleObj ) {
1618  $value = $titleObj->getText();
1619  }
1620 
1621  if (
1623  // We allow ranges as well, for blocks.
1624  !IP::isIPAddress( $value ) &&
1625  // See comment for User::isIP. We don't just call that function
1626  // here because it also returns true for things like
1627  // 300.300.300.300 that are neither valid usernames nor valid IP
1628  // addresses.
1629  !preg_match(
1630  '/^' . RE_IP_BYTE . '\.' . RE_IP_BYTE . '\.' . RE_IP_BYTE . '\.xxx$/',
1631  $value
1632  )
1633  ) {
1634  $this->dieWithError(
1635  [ 'apierror-baduser', $encParamName, wfEscapeWikiText( $value ) ],
1636  "baduser_{$encParamName}"
1637  );
1638  }
1639 
1640  return $value;
1641  }
1642 
1645  /************************************************************************/
1656  protected function setWatch( $watch, $titleObj, $userOption = null ) {
1657  $value = $this->getWatchlistValue( $watch, $titleObj, $userOption );
1658  if ( $value === null ) {
1659  return;
1660  }
1661 
1662  WatchAction::doWatchOrUnwatch( $value, $titleObj, $this->getUser() );
1663  }
1664 
1671  public static function truncateArray( &$arr, $limit ) {
1672  $modified = false;
1673  while ( count( $arr ) > $limit ) {
1674  array_pop( $arr );
1675  $modified = true;
1676  }
1677 
1678  return $modified;
1679  }
1680 
1687  public function getWatchlistUser( $params ) {
1688  if ( !is_null( $params['owner'] ) && !is_null( $params['token'] ) ) {
1689  $user = User::newFromName( $params['owner'], false );
1690  if ( !( $user && $user->getId() ) ) {
1691  $this->dieWithError(
1692  [ 'nosuchusershort', wfEscapeWikiText( $params['owner'] ) ], 'bad_wlowner'
1693  );
1694  }
1695  $token = $user->getOption( 'watchlisttoken' );
1696  if ( $token == '' || !hash_equals( $token, $params['token'] ) ) {
1697  $this->dieWithError( 'apierror-bad-watchlist-token', 'bad_wltoken' );
1698  }
1699  } else {
1700  if ( !$this->getUser()->isLoggedIn() ) {
1701  $this->dieWithError( 'watchlistanontext', 'notloggedin' );
1702  }
1703  $this->checkUserRightsAny( 'viewmywatchlist' );
1704  $user = $this->getUser();
1705  }
1706 
1707  return $user;
1708  }
1709 
1717  private static function escapeWikiText( $v ) {
1718  if ( is_array( $v ) ) {
1719  return array_map( 'self::escapeWikiText', $v );
1720  } else {
1721  return strtr( $v, [
1722  '__' => '_&#95;', '{' => '&#123;', '}' => '&#125;',
1723  '[[Category:' => '[[:Category:',
1724  '[[File:' => '[[:File:', '[[Image:' => '[[:Image:',
1725  ] );
1726  }
1727  }
1728 
1741  public static function makeMessage( $msg, IContextSource $context, array $params = null ) {
1742  if ( is_string( $msg ) ) {
1743  $msg = wfMessage( $msg );
1744  } elseif ( is_array( $msg ) ) {
1745  $msg = call_user_func_array( 'wfMessage', $msg );
1746  }
1747  if ( !$msg instanceof Message ) {
1748  return null;
1749  }
1750 
1751  $msg->setContext( $context );
1752  if ( $params ) {
1753  $msg->params( $params );
1754  }
1755 
1756  return $msg;
1757  }
1758 
1766  public function errorArrayToStatus( array $errors, User $user = null ) {
1767  if ( $user === null ) {
1768  $user = $this->getUser();
1769  }
1770 
1772  foreach ( $errors as $error ) {
1773  if ( is_array( $error ) && $error[0] === 'blockedtext' && $user->getBlock() ) {
1774  $status->fatal( ApiMessage::create(
1775  'apierror-blocked',
1776  'blocked',
1777  [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $user->getBlock() ) ]
1778  ) );
1779  } elseif ( is_array( $error ) && $error[0] === 'autoblockedtext' && $user->getBlock() ) {
1780  $status->fatal( ApiMessage::create(
1781  'apierror-autoblocked',
1782  'autoblocked',
1783  [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $user->getBlock() ) ]
1784  ) );
1785  } elseif ( is_array( $error ) && $error[0] === 'systemblockedtext' && $user->getBlock() ) {
1786  $status->fatal( ApiMessage::create(
1787  'apierror-systemblocked',
1788  'blocked',
1789  [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $user->getBlock() ) ]
1790  ) );
1791  } else {
1792  call_user_func_array( [ $status, 'fatal' ], (array)$error );
1793  }
1794  }
1795  return $status;
1796  }
1797 
1800  /************************************************************************/
1819  public function addWarning( $msg, $code = null, $data = null ) {
1820  $this->getErrorFormatter()->addWarning( $this->getModulePath(), $msg, $code, $data );
1821  }
1822 
1833  public function addDeprecation( $msg, $feature, $data = [] ) {
1834  $data = (array)$data;
1835  if ( $feature !== null ) {
1836  $data['feature'] = $feature;
1837  $this->logFeatureUsage( $feature );
1838  }
1839  $this->addWarning( $msg, 'deprecation', $data );
1840 
1841  // No real need to deduplicate here, ApiErrorFormatter does that for
1842  // us (assuming the hook is deterministic).
1843  $msgs = [ $this->msg( 'api-usage-mailinglist-ref' ) ];
1844  Hooks::run( 'ApiDeprecationHelp', [ &$msgs ] );
1845  if ( count( $msgs ) > 1 ) {
1846  $key = '$' . implode( ' $', range( 1, count( $msgs ) ) );
1847  $msg = ( new RawMessage( $key ) )->params( $msgs );
1848  } else {
1849  $msg = reset( $msgs );
1850  }
1851  $this->getMain()->addWarning( $msg, 'deprecation-help' );
1852  }
1853 
1866  public function addError( $msg, $code = null, $data = null ) {
1867  $this->getErrorFormatter()->addError( $this->getModulePath(), $msg, $code, $data );
1868  }
1869 
1878  public function addMessagesFromStatus( StatusValue $status, $types = [ 'warning', 'error' ] ) {
1879  $this->getErrorFormatter()->addMessagesFromStatus( $this->getModulePath(), $status, $types );
1880  }
1881 
1895  public function dieWithError( $msg, $code = null, $data = null, $httpCode = null ) {
1896  throw ApiUsageException::newWithMessage( $this, $msg, $code, $data, $httpCode );
1897  }
1898 
1907  public function dieWithException( $exception, array $options = [] ) {
1908  $this->dieWithError(
1909  $this->getErrorFormatter()->getMessageFromException( $exception, $options )
1910  );
1911  }
1912 
1919  private function warnOrDie( ApiMessage $msg, $enforceLimits = false ) {
1920  if ( $enforceLimits ) {
1921  $this->dieWithError( $msg );
1922  } else {
1923  $this->addWarning( $msg );
1924  }
1925  }
1926 
1935  public function dieBlocked( Block $block ) {
1936  // Die using the appropriate message depending on block type
1937  if ( $block->getType() == Block::TYPE_AUTO ) {
1938  $this->dieWithError(
1939  'apierror-autoblocked',
1940  'autoblocked',
1941  [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) ]
1942  );
1943  } else {
1944  $this->dieWithError(
1945  'apierror-blocked',
1946  'blocked',
1947  [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) ]
1948  );
1949  }
1950  }
1951 
1960  public function dieStatus( StatusValue $status ) {
1961  if ( $status->isGood() ) {
1962  throw new MWException( 'Successful status passed to ApiBase::dieStatus' );
1963  }
1964 
1965  // ApiUsageException needs a fatal status, but this method has
1966  // historically accepted any non-good status. Convert it if necessary.
1967  $status->setOK( false );
1968  if ( !$status->getErrorsByType( 'error' ) ) {
1969  $newStatus = Status::newGood();
1970  foreach ( $status->getErrorsByType( 'warning' ) as $err ) {
1971  call_user_func_array(
1972  [ $newStatus, 'fatal' ],
1973  array_merge( [ $err['message'] ], $err['params'] )
1974  );
1975  }
1976  if ( !$newStatus->getErrorsByType( 'error' ) ) {
1977  $newStatus->fatal( 'unknownerror-nocode' );
1978  }
1979  $status = $newStatus;
1980  }
1981 
1982  throw new ApiUsageException( $this, $status );
1983  }
1984 
1990  public function dieReadOnly() {
1991  $this->dieWithError(
1992  'apierror-readonly',
1993  'readonly',
1994  [ 'readonlyreason' => wfReadOnlyReason() ]
1995  );
1996  }
1997 
2006  public function checkUserRightsAny( $rights, $user = null ) {
2007  if ( !$user ) {
2008  $user = $this->getUser();
2009  }
2010  $rights = (array)$rights;
2011  if ( !call_user_func_array( [ $user, 'isAllowedAny' ], $rights ) ) {
2012  $this->dieWithError( [ 'apierror-permissiondenied', $this->msg( "action-{$rights[0]}" ) ] );
2013  }
2014  }
2015 
2024  public function checkTitleUserPermissions( Title $title, $actions, $user = null ) {
2025  if ( !$user ) {
2026  $user = $this->getUser();
2027  }
2028 
2029  $errors = [];
2030  foreach ( (array)$actions as $action ) {
2031  $errors = array_merge( $errors, $title->getUserPermissionsErrors( $action, $user ) );
2032  }
2033  if ( $errors ) {
2034  $this->dieStatus( $this->errorArrayToStatus( $errors, $user ) );
2035  }
2036  }
2037 
2049  public function dieWithErrorOrDebug( $msg, $code = null, $data = null, $httpCode = null ) {
2050  if ( $this->getConfig()->get( 'DebugAPI' ) !== true ) {
2051  $this->dieWithError( $msg, $code, $data, $httpCode );
2052  } else {
2053  $this->addWarning( $msg, $code, $data );
2054  }
2055  }
2056 
2066  protected function dieContinueUsageIf( $condition ) {
2067  if ( $condition ) {
2068  $this->dieWithError( 'apierror-badcontinue' );
2069  }
2070  }
2071 
2078  protected static function dieDebug( $method, $message ) {
2079  throw new MWException( "Internal error in $method: $message" );
2080  }
2081 
2088  public function logFeatureUsage( $feature ) {
2089  $request = $this->getRequest();
2090  $s = '"' . addslashes( $feature ) . '"' .
2091  ' "' . wfUrlencode( str_replace( ' ', '_', $this->getUser()->getName() ) ) . '"' .
2092  ' "' . $request->getIP() . '"' .
2093  ' "' . addslashes( $request->getHeader( 'Referer' ) ) . '"' .
2094  ' "' . addslashes( $this->getMain()->getUserAgent() ) . '"';
2095  wfDebugLog( 'api-feature-usage', $s, 'private' );
2096  }
2097 
2100  /************************************************************************/
2114  protected function getSummaryMessage() {
2115  return "apihelp-{$this->getModulePath()}-summary";
2116  }
2117 
2127  protected function getExtendedDescription() {
2128  return [ [
2129  "apihelp-{$this->getModulePath()}-extended-description",
2130  'api-help-no-extended-description',
2131  ] ];
2132  }
2133 
2144  public function getFinalSummary() {
2145  $msg = self::makeMessage( $this->getSummaryMessage(), $this->getContext(), [
2146  $this->getModulePrefix(),
2147  $this->getModuleName(),
2148  $this->getModulePath(),
2149  ] );
2150  if ( !$msg->exists() ) {
2151  wfDeprecated( 'API help "description" messages', '1.30' );
2152  $msg = self::makeMessage( $this->getDescriptionMessage(), $this->getContext(), [
2153  $this->getModulePrefix(),
2154  $this->getModuleName(),
2155  $this->getModulePath(),
2156  ] );
2157  $msg = self::makeMessage( 'rawmessage', $this->getContext(), [
2158  preg_replace( '/\n.*/s', '', $msg->text() )
2159  ] );
2160  }
2161  return $msg;
2162  }
2163 
2171  public function getFinalDescription() {
2172  $desc = $this->getDescription();
2173 
2174  // Avoid PHP 7.1 warning of passing $this by reference
2175  $apiModule = $this;
2176  Hooks::run( 'APIGetDescription', [ &$apiModule, &$desc ] );
2177  $desc = self::escapeWikiText( $desc );
2178  if ( is_array( $desc ) ) {
2179  $desc = implode( "\n", $desc );
2180  } else {
2181  $desc = (string)$desc;
2182  }
2183 
2184  $summary = self::makeMessage( $this->getSummaryMessage(), $this->getContext(), [
2185  $this->getModulePrefix(),
2186  $this->getModuleName(),
2187  $this->getModulePath(),
2188  ] );
2189  $extendedDescription = self::makeMessage(
2190  $this->getExtendedDescription(), $this->getContext(), [
2191  $this->getModulePrefix(),
2192  $this->getModuleName(),
2193  $this->getModulePath(),
2194  ]
2195  );
2196 
2197  if ( $summary->exists() ) {
2198  $msgs = [ $summary, $extendedDescription ];
2199  } else {
2200  wfDeprecated( 'API help "description" messages', '1.30' );
2201  $description = self::makeMessage( $this->getDescriptionMessage(), $this->getContext(), [
2202  $this->getModulePrefix(),
2203  $this->getModuleName(),
2204  $this->getModulePath(),
2205  ] );
2206  if ( !$description->exists() ) {
2207  $description = $this->msg( 'api-help-fallback-description', $desc );
2208  }
2209  $msgs = [ $description ];
2210  }
2211 
2212  Hooks::run( 'APIGetDescriptionMessages', [ $this, &$msgs ] );
2213 
2214  return $msgs;
2215  }
2216 
2225  public function getFinalParams( $flags = 0 ) {
2226  $params = $this->getAllowedParams( $flags );
2227  if ( !$params ) {
2228  $params = [];
2229  }
2230 
2231  if ( $this->needsToken() ) {
2232  $params['token'] = [
2233  self::PARAM_TYPE => 'string',
2234  self::PARAM_REQUIRED => true,
2235  self::PARAM_SENSITIVE => true,
2236  self::PARAM_HELP_MSG => [
2237  'api-help-param-token',
2238  $this->needsToken(),
2239  ],
2240  ] + ( isset( $params['token'] ) ? $params['token'] : [] );
2241  }
2242 
2243  // Avoid PHP 7.1 warning of passing $this by reference
2244  $apiModule = $this;
2245  Hooks::run( 'APIGetAllowedParams', [ &$apiModule, &$params, $flags ] );
2246 
2247  return $params;
2248  }
2249 
2257  public function getFinalParamDescription() {
2258  $prefix = $this->getModulePrefix();
2259  $name = $this->getModuleName();
2260  $path = $this->getModulePath();
2261 
2262  $desc = $this->getParamDescription();
2263 
2264  // Avoid PHP 7.1 warning of passing $this by reference
2265  $apiModule = $this;
2266  Hooks::run( 'APIGetParamDescription', [ &$apiModule, &$desc ] );
2267 
2268  if ( !$desc ) {
2269  $desc = [];
2270  }
2271  $desc = self::escapeWikiText( $desc );
2272 
2273  $params = $this->getFinalParams( self::GET_VALUES_FOR_HELP );
2274  $msgs = [];
2275  foreach ( $params as $param => $settings ) {
2276  if ( !is_array( $settings ) ) {
2277  $settings = [];
2278  }
2279 
2280  $d = isset( $desc[$param] ) ? $desc[$param] : '';
2281  if ( is_array( $d ) ) {
2282  // Special handling for prop parameters
2283  $d = array_map( function ( $line ) {
2284  if ( preg_match( '/^\s+(\S+)\s+-\s+(.+)$/', $line, $m ) ) {
2285  $line = "\n;{$m[1]}:{$m[2]}";
2286  }
2287  return $line;
2288  }, $d );
2289  $d = implode( ' ', $d );
2290  }
2291 
2292  if ( isset( $settings[self::PARAM_HELP_MSG] ) ) {
2293  $msg = $settings[self::PARAM_HELP_MSG];
2294  } else {
2295  $msg = $this->msg( "apihelp-{$path}-param-{$param}" );
2296  if ( !$msg->exists() ) {
2297  $msg = $this->msg( 'api-help-fallback-parameter', $d );
2298  }
2299  }
2300  $msg = self::makeMessage( $msg, $this->getContext(),
2301  [ $prefix, $param, $name, $path ] );
2302  if ( !$msg ) {
2303  self::dieDebug( __METHOD__,
2304  'Value in ApiBase::PARAM_HELP_MSG is not valid' );
2305  }
2306  $msgs[$param] = [ $msg ];
2307 
2308  if ( isset( $settings[self::PARAM_TYPE] ) &&
2309  $settings[self::PARAM_TYPE] === 'submodule'
2310  ) {
2311  if ( isset( $settings[self::PARAM_SUBMODULE_MAP] ) ) {
2312  $map = $settings[self::PARAM_SUBMODULE_MAP];
2313  } else {
2314  $prefix = $this->isMain() ? '' : ( $this->getModulePath() . '+' );
2315  $map = [];
2316  foreach ( $this->getModuleManager()->getNames( $param ) as $submoduleName ) {
2317  $map[$submoduleName] = $prefix . $submoduleName;
2318  }
2319  }
2320  ksort( $map );
2321  $submodules = [];
2322  $deprecatedSubmodules = [];
2323  foreach ( $map as $v => $m ) {
2324  $arr = &$submodules;
2325  $isDeprecated = false;
2326  $summary = null;
2327  try {
2328  $submod = $this->getModuleFromPath( $m );
2329  if ( $submod ) {
2330  $summary = $submod->getFinalSummary();
2331  $isDeprecated = $submod->isDeprecated();
2332  if ( $isDeprecated ) {
2333  $arr = &$deprecatedSubmodules;
2334  }
2335  }
2336  } catch ( ApiUsageException $ex ) {
2337  // Ignore
2338  }
2339  if ( $summary ) {
2340  $key = $summary->getKey();
2341  $params = $summary->getParams();
2342  } else {
2343  $key = 'api-help-undocumented-module';
2344  $params = [ $m ];
2345  }
2346  $m = new ApiHelpParamValueMessage( "[[Special:ApiHelp/$m|$v]]", $key, $params, $isDeprecated );
2347  $arr[] = $m->setContext( $this->getContext() );
2348  }
2349  $msgs[$param] = array_merge( $msgs[$param], $submodules, $deprecatedSubmodules );
2350  } elseif ( isset( $settings[self::PARAM_HELP_MSG_PER_VALUE] ) ) {
2351  if ( !is_array( $settings[self::PARAM_HELP_MSG_PER_VALUE] ) ) {
2352  self::dieDebug( __METHOD__,
2353  'ApiBase::PARAM_HELP_MSG_PER_VALUE is not valid' );
2354  }
2355  if ( !is_array( $settings[self::PARAM_TYPE] ) ) {
2356  self::dieDebug( __METHOD__,
2357  'ApiBase::PARAM_HELP_MSG_PER_VALUE may only be used when ' .
2358  'ApiBase::PARAM_TYPE is an array' );
2359  }
2360 
2361  $valueMsgs = $settings[self::PARAM_HELP_MSG_PER_VALUE];
2362  $deprecatedValues = isset( $settings[self::PARAM_DEPRECATED_VALUES] )
2363  ? $settings[self::PARAM_DEPRECATED_VALUES]
2364  : [];
2365 
2366  foreach ( $settings[self::PARAM_TYPE] as $value ) {
2367  if ( isset( $valueMsgs[$value] ) ) {
2368  $msg = $valueMsgs[$value];
2369  } else {
2370  $msg = "apihelp-{$path}-paramvalue-{$param}-{$value}";
2371  }
2372  $m = self::makeMessage( $msg, $this->getContext(),
2373  [ $prefix, $param, $name, $path, $value ] );
2374  if ( $m ) {
2375  $m = new ApiHelpParamValueMessage(
2376  $value,
2377  [ $m->getKey(), 'api-help-param-no-description' ],
2378  $m->getParams(),
2379  isset( $deprecatedValues[$value] )
2380  );
2381  $msgs[$param][] = $m->setContext( $this->getContext() );
2382  } else {
2383  self::dieDebug( __METHOD__,
2384  "Value in ApiBase::PARAM_HELP_MSG_PER_VALUE for $value is not valid" );
2385  }
2386  }
2387  }
2388 
2389  if ( isset( $settings[self::PARAM_HELP_MSG_APPEND] ) ) {
2390  if ( !is_array( $settings[self::PARAM_HELP_MSG_APPEND] ) ) {
2391  self::dieDebug( __METHOD__,
2392  'Value for ApiBase::PARAM_HELP_MSG_APPEND is not an array' );
2393  }
2394  foreach ( $settings[self::PARAM_HELP_MSG_APPEND] as $m ) {
2395  $m = self::makeMessage( $m, $this->getContext(),
2396  [ $prefix, $param, $name, $path ] );
2397  if ( $m ) {
2398  $msgs[$param][] = $m;
2399  } else {
2400  self::dieDebug( __METHOD__,
2401  'Value in ApiBase::PARAM_HELP_MSG_APPEND is not valid' );
2402  }
2403  }
2404  }
2405  }
2406 
2407  Hooks::run( 'APIGetParamDescriptionMessages', [ $this, &$msgs ] );
2408 
2409  return $msgs;
2410  }
2411 
2421  protected function getHelpFlags() {
2422  $flags = [];
2423 
2424  if ( $this->isDeprecated() ) {
2425  $flags[] = 'deprecated';
2426  }
2427  if ( $this->isInternal() ) {
2428  $flags[] = 'internal';
2429  }
2430  if ( $this->isReadMode() ) {
2431  $flags[] = 'readrights';
2432  }
2433  if ( $this->isWriteMode() ) {
2434  $flags[] = 'writerights';
2435  }
2436  if ( $this->mustBePosted() ) {
2437  $flags[] = 'mustbeposted';
2438  }
2439 
2440  return $flags;
2441  }
2442 
2454  protected function getModuleSourceInfo() {
2455  global $IP;
2456 
2457  if ( $this->mModuleSource !== false ) {
2458  return $this->mModuleSource;
2459  }
2460 
2461  // First, try to find where the module comes from...
2462  $rClass = new ReflectionClass( $this );
2463  $path = $rClass->getFileName();
2464  if ( !$path ) {
2465  // No path known?
2466  $this->mModuleSource = null;
2467  return null;
2468  }
2469  $path = realpath( $path ) ?: $path;
2470 
2471  // Build map of extension directories to extension info
2472  if ( self::$extensionInfo === null ) {
2473  $extDir = $this->getConfig()->get( 'ExtensionDirectory' );
2474  self::$extensionInfo = [
2475  realpath( __DIR__ ) ?: __DIR__ => [
2476  'path' => $IP,
2477  'name' => 'MediaWiki',
2478  'license-name' => 'GPL-2.0-or-later',
2479  ],
2480  realpath( "$IP/extensions" ) ?: "$IP/extensions" => null,
2481  realpath( $extDir ) ?: $extDir => null,
2482  ];
2483  $keep = [
2484  'path' => null,
2485  'name' => null,
2486  'namemsg' => null,
2487  'license-name' => null,
2488  ];
2489  foreach ( $this->getConfig()->get( 'ExtensionCredits' ) as $group ) {
2490  foreach ( $group as $ext ) {
2491  if ( !isset( $ext['path'] ) || !isset( $ext['name'] ) ) {
2492  // This shouldn't happen, but does anyway.
2493  continue;
2494  }
2495 
2496  $extpath = $ext['path'];
2497  if ( !is_dir( $extpath ) ) {
2498  $extpath = dirname( $extpath );
2499  }
2500  self::$extensionInfo[realpath( $extpath ) ?: $extpath] =
2501  array_intersect_key( $ext, $keep );
2502  }
2503  }
2504  foreach ( ExtensionRegistry::getInstance()->getAllThings() as $ext ) {
2505  $extpath = $ext['path'];
2506  if ( !is_dir( $extpath ) ) {
2507  $extpath = dirname( $extpath );
2508  }
2509  self::$extensionInfo[realpath( $extpath ) ?: $extpath] =
2510  array_intersect_key( $ext, $keep );
2511  }
2512  }
2513 
2514  // Now traverse parent directories until we find a match or run out of
2515  // parents.
2516  do {
2517  if ( array_key_exists( $path, self::$extensionInfo ) ) {
2518  // Found it!
2519  $this->mModuleSource = self::$extensionInfo[$path];
2520  return $this->mModuleSource;
2521  }
2522 
2523  $oldpath = $path;
2524  $path = dirname( $path );
2525  } while ( $path !== $oldpath );
2526 
2527  // No idea what extension this might be.
2528  $this->mModuleSource = null;
2529  return null;
2530  }
2531 
2543  public function modifyHelp( array &$help, array $options, array &$tocData ) {
2544  }
2545 
2548  /************************************************************************/
2562  protected function getDescription() {
2563  return false;
2564  }
2565 
2578  protected function getParamDescription() {
2579  return [];
2580  }
2581 
2598  protected function getExamples() {
2599  return false;
2600  }
2601 
2605  public function profileIn() {
2606  // No wfDeprecated() yet because extensions call this and might need to
2607  // keep doing so for BC.
2608  }
2609 
2613  public function profileOut() {
2614  // No wfDeprecated() yet because extensions call this and might need to
2615  // keep doing so for BC.
2616  }
2617 
2621  public function safeProfileOut() {
2622  wfDeprecated( __METHOD__, '1.25' );
2623  }
2624 
2628  public function profileDBIn() {
2629  wfDeprecated( __METHOD__, '1.25' );
2630  }
2631 
2635  public function profileDBOut() {
2636  wfDeprecated( __METHOD__, '1.25' );
2637  }
2638 
2643  protected function useTransactionalTimeLimit() {
2644  if ( $this->getRequest()->wasPosted() ) {
2646  }
2647  }
2648 
2653  public function setWarning( $warning ) {
2654  wfDeprecated( __METHOD__, '1.29' );
2655  $msg = new ApiRawMessage( $warning, 'warning' );
2656  $this->getErrorFormatter()->addWarning( $this->getModulePath(), $msg );
2657  }
2658 
2672  public function dieUsage( $description, $errorCode, $httpRespCode = 0, $extradata = null ) {
2673  wfDeprecated( __METHOD__, '1.29' );
2674  $this->dieWithError(
2675  new RawMessage( '$1', [ $description ] ),
2676  $errorCode,
2677  $extradata,
2678  $httpRespCode
2679  );
2680  }
2681 
2692  public function getErrorFromStatus( $status, &$extraData = null ) {
2693  wfDeprecated( __METHOD__, '1.29' );
2694  if ( $status->isGood() ) {
2695  throw new MWException( 'Successful status passed to ApiBase::dieStatus' );
2696  }
2697 
2698  $errors = $status->getErrorsByType( 'error' );
2699  if ( !$errors ) {
2700  // No errors? Assume the warnings should be treated as errors
2701  $errors = $status->getErrorsByType( 'warning' );
2702  }
2703  if ( !$errors ) {
2704  // Still no errors? Punt
2705  $errors = [ [ 'message' => 'unknownerror-nocode', 'params' => [] ] ];
2706  }
2707 
2708  if ( $errors[0]['message'] instanceof MessageSpecifier ) {
2709  $msg = $errors[0]['message'];
2710  } else {
2711  $msg = new Message( $errors[0]['message'], $errors[0]['params'] );
2712  }
2713  if ( !$msg instanceof IApiMessage ) {
2714  $key = $msg->getKey();
2715  $params = $msg->getParams();
2716  array_unshift( $params, isset( self::$messageMap[$key] ) ? self::$messageMap[$key] : $key );
2717  $msg = ApiMessage::create( $params );
2718  }
2719 
2720  return [
2721  $msg->getApiCode(),
2722  ApiErrorFormatter::stripMarkup( $msg->inLanguage( 'en' )->useDatabase( false )->text() )
2723  ];
2724  }
2725 
2734  private static $messageMap = [
2735  'unknownerror' => 'apierror-unknownerror',
2736  'unknownerror-nocode' => 'apierror-unknownerror-nocode',
2737  'ns-specialprotected' => 'ns-specialprotected',
2738  'protectedinterface' => 'protectedinterface',
2739  'namespaceprotected' => 'namespaceprotected',
2740  'customcssprotected' => 'customcssprotected',
2741  'customjsprotected' => 'customjsprotected',
2742  'cascadeprotected' => 'cascadeprotected',
2743  'protectedpagetext' => 'protectedpagetext',
2744  'protect-cantedit' => 'protect-cantedit',
2745  'deleteprotected' => 'deleteprotected',
2746  'badaccess-group0' => 'badaccess-group0',
2747  'badaccess-groups' => 'badaccess-groups',
2748  'titleprotected' => 'titleprotected',
2749  'nocreate-loggedin' => 'nocreate-loggedin',
2750  'nocreatetext' => 'nocreatetext',
2751  'movenologintext' => 'movenologintext',
2752  'movenotallowed' => 'movenotallowed',
2753  'confirmedittext' => 'confirmedittext',
2754  'blockedtext' => 'apierror-blocked',
2755  'autoblockedtext' => 'apierror-autoblocked',
2756  'systemblockedtext' => 'apierror-systemblocked',
2757  'actionthrottledtext' => 'apierror-ratelimited',
2758  'alreadyrolled' => 'alreadyrolled',
2759  'cantrollback' => 'cantrollback',
2760  'readonlytext' => 'readonlytext',
2761  'sessionfailure' => 'sessionfailure',
2762  'cannotdelete' => 'cannotdelete',
2763  'notanarticle' => 'apierror-missingtitle',
2764  'selfmove' => 'selfmove',
2765  'immobile_namespace' => 'apierror-immobilenamespace',
2766  'articleexists' => 'articleexists',
2767  'hookaborted' => 'hookaborted',
2768  'cantmove-titleprotected' => 'cantmove-titleprotected',
2769  'imagenocrossnamespace' => 'imagenocrossnamespace',
2770  'imagetypemismatch' => 'imagetypemismatch',
2771  'ip_range_invalid' => 'ip_range_invalid',
2772  'range_block_disabled' => 'range_block_disabled',
2773  'nosuchusershort' => 'nosuchusershort',
2774  'badipaddress' => 'badipaddress',
2775  'ipb_expiry_invalid' => 'ipb_expiry_invalid',
2776  'ipb_already_blocked' => 'ipb_already_blocked',
2777  'ipb_blocked_as_range' => 'ipb_blocked_as_range',
2778  'ipb_cant_unblock' => 'ipb_cant_unblock',
2779  'mailnologin' => 'apierror-cantsend',
2780  'ipbblocked' => 'ipbblocked',
2781  'ipbnounblockself' => 'ipbnounblockself',
2782  'usermaildisabled' => 'usermaildisabled',
2783  'blockedemailuser' => 'apierror-blockedfrommail',
2784  'notarget' => 'apierror-notarget',
2785  'noemail' => 'noemail',
2786  'rcpatroldisabled' => 'rcpatroldisabled',
2787  'markedaspatrollederror-noautopatrol' => 'markedaspatrollederror-noautopatrol',
2788  'delete-toobig' => 'delete-toobig',
2789  'movenotallowedfile' => 'movenotallowedfile',
2790  'userrights-no-interwiki' => 'userrights-no-interwiki',
2791  'userrights-nodatabase' => 'userrights-nodatabase',
2792  'nouserspecified' => 'nouserspecified',
2793  'noname' => 'noname',
2794  'summaryrequired' => 'apierror-summaryrequired',
2795  'import-rootpage-invalid' => 'import-rootpage-invalid',
2796  'import-rootpage-nosubpage' => 'import-rootpage-nosubpage',
2797  'readrequired' => 'apierror-readapidenied',
2798  'writedisabled' => 'apierror-noapiwrite',
2799  'writerequired' => 'apierror-writeapidenied',
2800  'missingparam' => 'apierror-missingparam',
2801  'invalidtitle' => 'apierror-invalidtitle',
2802  'nosuchpageid' => 'apierror-nosuchpageid',
2803  'nosuchrevid' => 'apierror-nosuchrevid',
2804  'nosuchuser' => 'nosuchusershort',
2805  'invaliduser' => 'apierror-invaliduser',
2806  'invalidexpiry' => 'apierror-invalidexpiry',
2807  'pastexpiry' => 'apierror-pastexpiry',
2808  'create-titleexists' => 'apierror-create-titleexists',
2809  'missingtitle-createonly' => 'apierror-missingtitle-createonly',
2810  'cantblock' => 'apierror-cantblock',
2811  'canthide' => 'apierror-canthide',
2812  'cantblock-email' => 'apierror-cantblock-email',
2813  'cantunblock' => 'apierror-permissiondenied-generic',
2814  'cannotundelete' => 'cannotundelete',
2815  'permdenied-undelete' => 'apierror-permissiondenied-generic',
2816  'createonly-exists' => 'apierror-articleexists',
2817  'nocreate-missing' => 'apierror-missingtitle',
2818  'cantchangecontentmodel' => 'apierror-cantchangecontentmodel',
2819  'nosuchrcid' => 'apierror-nosuchrcid',
2820  'nosuchlogid' => 'apierror-nosuchlogid',
2821  'protect-invalidaction' => 'apierror-protect-invalidaction',
2822  'protect-invalidlevel' => 'apierror-protect-invalidlevel',
2823  'toofewexpiries' => 'apierror-toofewexpiries',
2824  'cantimport' => 'apierror-cantimport',
2825  'cantimport-upload' => 'apierror-cantimport-upload',
2826  'importnofile' => 'importnofile',
2827  'importuploaderrorsize' => 'importuploaderrorsize',
2828  'importuploaderrorpartial' => 'importuploaderrorpartial',
2829  'importuploaderrortemp' => 'importuploaderrortemp',
2830  'importcantopen' => 'importcantopen',
2831  'import-noarticle' => 'import-noarticle',
2832  'importbadinterwiki' => 'importbadinterwiki',
2833  'import-unknownerror' => 'apierror-import-unknownerror',
2834  'cantoverwrite-sharedfile' => 'apierror-cantoverwrite-sharedfile',
2835  'sharedfile-exists' => 'apierror-fileexists-sharedrepo-perm',
2836  'mustbeposted' => 'apierror-mustbeposted',
2837  'show' => 'apierror-show',
2838  'specialpage-cantexecute' => 'apierror-specialpage-cantexecute',
2839  'invalidoldimage' => 'apierror-invalidoldimage',
2840  'nodeleteablefile' => 'apierror-nodeleteablefile',
2841  'fileexists-forbidden' => 'fileexists-forbidden',
2842  'fileexists-shared-forbidden' => 'fileexists-shared-forbidden',
2843  'filerevert-badversion' => 'filerevert-badversion',
2844  'noimageredirect-anon' => 'apierror-noimageredirect-anon',
2845  'noimageredirect-logged' => 'apierror-noimageredirect',
2846  'spamdetected' => 'apierror-spamdetected',
2847  'contenttoobig' => 'apierror-contenttoobig',
2848  'noedit-anon' => 'apierror-noedit-anon',
2849  'noedit' => 'apierror-noedit',
2850  'wasdeleted' => 'apierror-pagedeleted',
2851  'blankpage' => 'apierror-emptypage',
2852  'editconflict' => 'editconflict',
2853  'hashcheckfailed' => 'apierror-badmd5',
2854  'missingtext' => 'apierror-notext',
2855  'emptynewsection' => 'apierror-emptynewsection',
2856  'revwrongpage' => 'apierror-revwrongpage',
2857  'undo-failure' => 'undo-failure',
2858  'content-not-allowed-here' => 'content-not-allowed-here',
2859  'edit-hook-aborted' => 'edit-hook-aborted',
2860  'edit-gone-missing' => 'edit-gone-missing',
2861  'edit-conflict' => 'edit-conflict',
2862  'edit-already-exists' => 'edit-already-exists',
2863  'invalid-file-key' => 'apierror-invalid-file-key',
2864  'nouploadmodule' => 'apierror-nouploadmodule',
2865  'uploaddisabled' => 'uploaddisabled',
2866  'copyuploaddisabled' => 'copyuploaddisabled',
2867  'copyuploadbaddomain' => 'apierror-copyuploadbaddomain',
2868  'copyuploadbadurl' => 'apierror-copyuploadbadurl',
2869  'filename-tooshort' => 'filename-tooshort',
2870  'filename-toolong' => 'filename-toolong',
2871  'illegal-filename' => 'illegal-filename',
2872  'filetype-missing' => 'filetype-missing',
2873  'mustbeloggedin' => 'apierror-mustbeloggedin',
2874  ];
2875 
2881  private function parseMsgInternal( $error ) {
2882  $msg = Message::newFromSpecifier( $error );
2883  if ( !$msg instanceof IApiMessage ) {
2884  $key = $msg->getKey();
2885  if ( isset( self::$messageMap[$key] ) ) {
2886  $params = $msg->getParams();
2887  array_unshift( $params, self::$messageMap[$key] );
2888  } else {
2889  $params = [ 'apierror-unknownerror', wfEscapeWikiText( $key ) ];
2890  }
2891  $msg = ApiMessage::create( $params );
2892  }
2893  return $msg;
2894  }
2895 
2902  public function parseMsg( $error ) {
2903  wfDeprecated( __METHOD__, '1.29' );
2904  // Check whether someone passed the whole array, instead of one element as
2905  // documented. This breaks if it's actually an array of fallback keys, but
2906  // that's long-standing misbehavior introduced in r87627 to incorrectly
2907  // fix T30797.
2908  if ( is_array( $error ) ) {
2909  $first = reset( $error );
2910  if ( is_array( $first ) ) {
2911  wfDebug( __METHOD__ . ' was passed an array of arrays. ' . wfGetAllCallers( 5 ) );
2912  $error = $first;
2913  }
2914  }
2915 
2916  $msg = $this->parseMsgInternal( $error );
2917  return [
2918  'code' => $msg->getApiCode(),
2920  $msg->inLanguage( 'en' )->useDatabase( false )->text()
2921  ),
2922  'data' => $msg->getApiData()
2923  ];
2924  }
2925 
2932  public function dieUsageMsg( $error ) {
2933  wfDeprecated( __METHOD__, '1.29' );
2934  $this->dieWithError( $this->parseMsgInternal( $error ) );
2935  }
2936 
2945  public function dieUsageMsgOrDebug( $error ) {
2946  wfDeprecated( __METHOD__, '1.29' );
2947  $this->dieWithErrorOrDebug( $this->parseMsgInternal( $error ) );
2948  }
2949 
2958  protected function getDescriptionMessage() {
2959  return [ [
2960  "apihelp-{$this->getModulePath()}-description",
2961  "apihelp-{$this->getModulePath()}-summary",
2962  ] ];
2963  }
2964 
2966 }
2967 
ApiBase\dieUsageMsgOrDebug
dieUsageMsgOrDebug( $error)
Will only set a warning instead of failing if the global $wgDebugAPI is set to true.
Definition: ApiBase.php:2945
ApiMain
This is the main API class, used for both external and internal processing.
Definition: ApiMain.php:43
ContextSource\$context
IContextSource $context
Definition: ContextSource.php:33
ContextSource\getConfig
getConfig()
Definition: ContextSource.php:63
ApiBase\PARAM_SUBMODULE_MAP
const PARAM_SUBMODULE_MAP
(string[]) When PARAM_TYPE is 'submodule', map parameter values to submodule paths.
Definition: ApiBase.php:165
$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:273
ApiUsageException
Exception used to abort API execution with an error.
Definition: ApiUsageException.php:103
ContextSource\getContext
getContext()
Get the base IContextSource object.
Definition: ContextSource.php:40
ApiBase\$mMainModule
ApiMain $mMainModule
Definition: ApiBase.php:253
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:555
ApiBase\addWarning
addWarning( $msg, $code=null, $data=null)
Add a warning for this module.
Definition: ApiBase.php:1819
Block\getType
getType()
Get the type of target for this particular block.
Definition: Block.php:1450
ApiBase\getSummaryMessage
getSummaryMessage()
Return the summary message.
Definition: ApiBase.php:2114
MWNamespace\getValidNamespaces
static getValidNamespaces()
Returns an array of the namespaces (by integer id) that exist on the wiki.
Definition: MWNamespace.php:290
ApiBase\getFinalParams
getFinalParams( $flags=0)
Get final list of parameters, after hooks have had a chance to tweak it as needed.
Definition: ApiBase.php:2225
ApiBase\PARAM_REQUIRED
const PARAM_REQUIRED
(boolean) Is the parameter required?
Definition: ApiBase.php:111
ApiBase\parseMsg
parseMsg( $error)
Return the error message related to a certain array.
Definition: ApiBase.php:2902
ApiBase\parameterNotEmpty
parameterNotEmpty( $x)
Callback function used in requireOnlyOneParameter to check whether required parameters are set.
Definition: ApiBase.php:911
ApiBase\$mModuleSource
array null bool $mModuleSource
Definition: ApiBase.php:259
captcha-old.count
count
Definition: captcha-old.py:249
ApiBase\validateToken
validateToken( $token, array $params)
Validate the supplied token.
Definition: ApiBase.php:1575
ApiContinuationManager
This manages continuation state.
Definition: ApiContinuationManager.php:26
ApiBase\dieWithError
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
Definition: ApiBase.php:1895
ApiBase\PARAM_HELP_MSG
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition: ApiBase.php:124
ContextSource\msg
msg( $key)
Get a Message object with context set Parameters are the same as wfMessage()
Definition: ContextSource.php:168
ApiBase\getExamplesMessages
getExamplesMessages()
Returns usage examples for this module.
Definition: ApiBase.php:332
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:180
ApiBase\validateTimestamp
validateTimestamp( $value, $encParamName)
Validate and normalize parameters of type 'timestamp'.
Definition: ApiBase.php:1538
ApiBase\dieUsageMsg
dieUsageMsg( $error)
Output the error message related to a certain array.
Definition: ApiBase.php:2932
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:1968
ApiBase\profileDBIn
profileDBIn()
Definition: ApiBase.php:2628
ApiBase\$extensionInfo
static array $extensionInfo
Maps extension paths to info arrays.
Definition: ApiBase.php:250
ApiBase\getTitleOrPageId
getTitleOrPageId( $params, $load=false)
Get a WikiPage object from a title or pageid param, if possible.
Definition: ApiBase.php:926
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:87
ApiBase\getResult
getResult()
Get the result object.
Definition: ApiBase.php:641
ApiBase\getDescriptionMessage
getDescriptionMessage()
Return the description message.
Definition: ApiBase.php:2958
MessageSpecifier
Definition: MessageSpecifier.php:21
ApiBase\__construct
__construct(ApiMain $mainModule, $moduleName, $modulePrefix='')
Definition: ApiBase.php:266
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:2635
wfUrlencode
wfUrlencode( $s)
We want some things to be included as literal characters in our title URLs for prettiness,...
Definition: GlobalFunctions.php:340
ApiBase\mustBePosted
mustBePosted()
Indicates whether this module must be called with a POST request.
Definition: ApiBase.php:436
ApiBase\logFeatureUsage
logFeatureUsage( $feature)
Write logging information for API features to a debug log, for usage analysis.
Definition: ApiBase.php:2088
ApiBase\checkUserRightsAny
checkUserRightsAny( $rights, $user=null)
Helper function for permission-denied errors.
Definition: ApiBase.php:2006
ApiBase\shouldCheckMaxlag
shouldCheckMaxlag()
Indicates if this module needs maxlag to be checked.
Definition: ApiBase.php:405
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:2049
ApiBase\setContinuationManager
setContinuationManager(ApiContinuationManager $manager=null)
Set the continuation manager.
Definition: ApiBase.php:695
$params
$params
Definition: styleTest.css.php:40
ApiBase\getExtendedDescription
getExtendedDescription()
Return the extended help text message.
Definition: ApiBase.php:2127
ApiBase\PARAM_ISMULTI_LIMIT1
const PARAM_ISMULTI_LIMIT1
(integer) Maximum number of values, for normal users.
Definition: ApiBase.php:208
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:591
ApiBase\escapeWikiText
static escapeWikiText( $v)
A subset of wfEscapeWikiText for BC texts.
Definition: ApiBase.php:1717
$s
$s
Definition: mergeMessageFileList.php:187
ApiBase\getDB
getDB()
Gets a default replica DB connection object.
Definition: ApiBase.php:669
ApiBase\makeMessage
static makeMessage( $msg, IContextSource $context, array $params=null)
Create a Message from a string or array.
Definition: ApiBase.php:1741
ApiBase\addMessagesFromStatus
addMessagesFromStatus(StatusValue $status, $types=[ 'warning', 'error'])
Add warnings and/or errors from a Status.
Definition: ApiBase.php:1878
ApiBase\$mSlaveDB
$mSlaveDB
Definition: ApiBase.php:256
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:302
ContextSource\getRequest
getRequest()
Definition: ContextSource.php:71
ApiBase\dynamicParameterDocumentation
dynamicParameterDocumentation()
Indicate if the module supports dynamically-determined parameters that cannot be included in self::ge...
Definition: ApiBase.php:719
User\isValidUserName
static isValidUserName( $name)
Is the input a valid username?
Definition: User.php:969
ApiBase\modifyHelp
modifyHelp(array &$help, array $options, array &$tocData)
Called from ApiHelp before the pieces are joined together and returned.
Definition: ApiBase.php:2543
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:102
ContextSource\getUser
getUser()
Definition: ContextSource.php:120
IApiMessage
Interface for messages with machine-readable data for use by the API.
Definition: ApiMessage.php:42
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:202
ApiBase\lacksSameOriginSecurity
lacksSameOriginSecurity()
Returns true if the current request breaks the same-origin policy.
Definition: ApiBase.php:569
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:131
ApiUsageException\newWithMessage
static newWithMessage(ApiBase $module=null, $msg, $code=null, $data=null, $httpCode=0)
Definition: ApiUsageException.php:143
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:1075
ApiBase\getWebUITokenSalt
getWebUITokenSalt(array $params)
Fetch the salt used in the Web UI corresponding to this module.
Definition: ApiBase.php:490
ApiBase\isMain
isMain()
Returns true if this module is the main module ($this === $this->mMainModule), false otherwise.
Definition: ApiBase.php:546
ApiBase\isReadMode
isReadMode()
Indicates whether this module requires read rights.
Definition: ApiBase.php:413
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:37
ApiRawMessage
Extension of RawMessage implementing IApiMessage.
Definition: ApiMessage.php:275
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
ApiBase\getModuleFromPath
getModuleFromPath( $path)
Get a module from its module path.
Definition: ApiBase.php:603
ApiBase\getFinalParamDescription
getFinalParamDescription()
Get final parameter descriptions, after hooks have had a chance to tweak it as needed.
Definition: ApiBase.php:2257
ApiMessage
Extension of Message implementing IApiMessage.
Definition: ApiMessage.php:205
ApiBase\PARAM_SENSITIVE
const PARAM_SENSITIVE
(boolean) Is the parameter sensitive? Note 'password'-type fields are always sensitive regardless of ...
Definition: ApiBase.php:193
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:1935
ApiBase\PARAM_ISMULTI_LIMIT2
const PARAM_ISMULTI_LIMIT2
(integer) Maximum number of values, for users with the apihighimits right.
Definition: ApiBase.php:215
ApiBase\$messageMap
static $messageMap
Definition: ApiBase.php:2734
ApiBase\getParamDescription
getParamDescription()
Returns an array of parameter descriptions.
Definition: ApiBase.php:2578
ExtensionRegistry\getInstance
static getInstance()
Definition: ExtensionRegistry.php:88
ApiBase\getFinalSummary
getFinalSummary()
Get final module summary.
Definition: ApiBase.php:2144
ApiBase\profileOut
profileOut()
Definition: ApiBase.php:2613
ApiBase\PARAM_DEPRECATED
const PARAM_DEPRECATED
(boolean) Is the parameter deprecated (will show a warning)?
Definition: ApiBase.php:105
ApiBase\explodeMultiValue
explodeMultiValue( $value, $limit)
Split a multi-valued parameter string, like explode()
Definition: ApiBase.php:1376
ApiBase\PARAM_MIN
const PARAM_MIN
(integer) Lowest value allowed for the parameter, for PARAM_TYPE 'integer' and 'limit'.
Definition: ApiBase.php:99
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:934
WikiPage\factory
static factory(Title $title)
Create a WikiPage object of the appropriate class for the given title.
Definition: WikiPage.php:115
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Definition: GlobalFunctions.php:1111
ApiErrorFormatter\stripMarkup
static stripMarkup( $text)
Turn wikitext into something resembling plaintext.
Definition: ApiErrorFormatter.php:255
ApiBase\warnOrDie
warnOrDie(ApiMessage $msg, $enforceLimits=false)
Adds a warning to the output, else dies.
Definition: ApiBase.php:1919
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:317
ApiBase\getFinalDescription
getFinalDescription()
Get final module description, after hooks have had a chance to tweak it as needed.
Definition: ApiBase.php:2171
wfTransactionalTimeLimit
wfTransactionalTimeLimit()
Set PHP's time limit to the larger of php.ini or $wgTransactionalTimeLimit.
Definition: GlobalFunctions.php:3061
ApiBase\getModulePath
getModulePath()
Get the path to this module.
Definition: ApiBase.php:585
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2800
ApiBase\LIMIT_BIG1
const LIMIT_BIG1
Fast query, standard limit.
Definition: ApiBase.php:234
ApiBase\getDescription
getDescription()
Returns the description string for this module.
Definition: ApiBase.php:2562
ContextSource
The simplest way of implementing IContextSource is to hold a RequestContext as a member variable and ...
Definition: ContextSource.php:29
ApiQueryTokens\getTokenTypeSalts
static getTokenTypeSalts()
Get the salts for known token types.
Definition: ApiQueryTokens.php:63
$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:90
ApiBase\dieWithException
dieWithException( $exception, array $options=[])
Abort execution with an error derived from an exception.
Definition: ApiBase.php:1907
WatchAction\doWatchOrUnwatch
static doWatchOrUnwatch( $watch, Title $title, User $user)
Watch or unwatch a page.
Definition: WatchAction.php:91
ApiBase\parseMsgInternal
parseMsgInternal( $error)
Definition: ApiBase.php:2881
ApiBase\handleParamNormalization
handleParamNormalization( $paramName, $value, $rawValue)
Handle when a parameter was Unicode-normalized.
Definition: ApiBase.php:1364
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:445
ApiMessage\create
static create( $msg, $code=null, array $data=null)
Create an IApiMessage for the message.
Definition: ApiMessage.php:219
ApiBase\PARAM_EXTRA_NAMESPACES
const PARAM_EXTRA_NAMESPACES
(int[]) When PARAM_TYPE is 'namespace', include these as additional possible values.
Definition: ApiBase.php:186
ApiBase\ALL_DEFAULT_STRING
const ALL_DEFAULT_STRING
Definition: ApiBase.php:231
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:982
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)
Definition: ContextSource.php:55
ApiBase\getHelpUrls
getHelpUrls()
Return links to more detailed help pages about the module.
Definition: ApiBase.php:379
ApiBase\validateUser
validateUser( $value, $encParamName)
Validate and normalize parameters of type 'user'.
Definition: ApiBase.php:1610
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:991
ApiBase\needsToken
needsToken()
Returns the token type this module requires in order to execute.
Definition: ApiBase.php:477
ApiBase\getModulePrefix
getModulePrefix()
Get parameter prefix (usually two letters or an empty string).
Definition: ApiBase.php:529
ApiBase\$mModuleName
string $mModuleName
Definition: ApiBase.php:255
$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:2604
ApiBase\setWatch
setWatch( $watch, $titleObj, $userOption=null)
Set a watch (or unwatch) based the based on a watchlist parameter.
Definition: ApiBase.php:1656
ApiBase\getContinuationManager
getContinuationManager()
Get the continuation manager.
Definition: ApiBase.php:681
$line
$line
Definition: cdb.php:59
ApiBase\addError
addError( $msg, $code=null, $data=null)
Add an error for this module without aborting.
Definition: ApiBase.php:1866
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:562
ApiBase\extractRequestParams
extractRequestParams( $parseLimit=true)
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:749
ApiBase\getWatchlistUser
getWatchlistUser( $params)
Gets the user for whom to get the watchlist.
Definition: ApiBase.php:1687
RE_IP_BYTE
const RE_IP_BYTE
Definition: IP.php:29
$value
$value
Definition: styleTest.css.php:45
ApiBase\dieContinueUsageIf
dieContinueUsageIf( $condition)
Die with the 'badcontinue' error.
Definition: ApiBase.php:2066
ApiBase\addDeprecation
addDeprecation( $msg, $feature, $data=[])
Add a deprecation warning for this module.
Definition: ApiBase.php:1833
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:240
ApiBase\encodeParamName
encodeParamName( $paramName)
This method mangles parameter name based on the prefix supplied to the constructor.
Definition: ApiBase.php:730
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:2672
ApiBase\dieReadOnly
dieReadOnly()
Helper function for readonly errors.
Definition: ApiBase.php:1990
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:247
ApiBase\checkTitleUserPermissions
checkTitleUserPermissions(Title $title, $actions, $user=null)
Helper function for permission-denied errors.
Definition: ApiBase.php:2024
WikiPage\newFromID
static newFromID( $id, $from='fromdb')
Constructor from a page id.
Definition: WikiPage.php:153
ApiBase\useTransactionalTimeLimit
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
Definition: ApiBase.php:2643
ApiBase\getConditionalRequestData
getConditionalRequestData( $condition)
Returns data for HTTP conditional request mechanisms.
Definition: ApiBase.php:506
ApiBase\isWriteMode
isWriteMode()
Indicates whether this module requires write mode.
Definition: ApiBase.php:428
wfEscapeWikiText
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Definition: GlobalFunctions.php:1631
$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:1987
ApiBase\requireMaxOneParameter
requireMaxOneParameter( $params, $required)
Die if more than one of a certain set of parameters is set and not false.
Definition: ApiBase.php:823
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:785
ApiBase\PARAM_HELP_MSG_INFO
const PARAM_HELP_MSG_INFO
(array) Specify additional information tags for the parameter.
Definition: ApiBase.php:141
ApiBase\$mParamCache
$mParamCache
Definition: ApiBase.php:257
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:1671
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:53
ApiBase\profileIn
profileIn()
Definition: ApiBase.php:2605
wfGetAllCallers
wfGetAllCallers( $limit=3)
Return a string consisting of callers in the stack.
Definition: GlobalFunctions.php:1551
ApiBase\PARAM_RANGE_ENFORCE
const PARAM_RANGE_ENFORCE
(boolean) For PARAM_TYPE 'integer', enforce PARAM_MIN and PARAM_MAX?
Definition: ApiBase.php:117
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:148
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:483
ApiBase\requireAtLeastOneParameter
requireAtLeastOneParameter( $params, $required)
Die if none of a certain set of parameters is set and not false.
Definition: ApiBase.php:851
ApiBase\setWarning
setWarning( $warning)
Definition: ApiBase.php:2653
Title
Represents a title within MediaWiki.
Definition: Title.php:39
ApiBase\LIMIT_BIG2
const LIMIT_BIG2
Fast query, apihighlimits limit.
Definition: ApiBase.php:236
wfReadOnlyReason
wfReadOnlyReason()
Check if the site is in read-only mode and return the message if so.
Definition: GlobalFunctions.php:1263
ApiQueryTokens\getToken
static getToken(User $user, MediaWiki\Session\Session $session, $salt)
Get a token from a salt.
Definition: ApiQueryTokens.php:94
$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:1987
ApiBase\getModuleManager
getModuleManager()
Get the module manager, or null if this module has no sub-modules.
Definition: ApiBase.php:304
$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:783
ApiBase\isInternal
isInternal()
Indicates whether this module is "internal" Internal API modules are not (yet) intended for 3rd party...
Definition: ApiBase.php:455
ApiBase\getModuleSourceInfo
getModuleSourceInfo()
Returns information about the source of this module, if known.
Definition: ApiBase.php:2454
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:1485
$path
$path
Definition: NoLocalSettings.php:25
ApiBase\PARAM_DFLT
const PARAM_DFLT
(null|boolean|integer|string) Default value of the parameter.
Definition: ApiBase.php:48
ApiBase\getParameter
getParameter( $paramName, $parseLimit=true)
Get a value for the given parameter.
Definition: ApiBase.php:773
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:2621
Block
Definition: Block.php:27
ApiBase\dieStatus
dieStatus(StatusValue $status)
Throw an ApiUsageException based on the Status object.
Definition: ApiBase.php:1960
ApiBase\getModuleName
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:521
ApiBase\PARAM_ISMULTI
const PARAM_ISMULTI
(boolean) Accept multiple pipe-separated values for this parameter (e.g.
Definition: ApiBase.php:51
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:96
ApiBase\getAllowedParams
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
Definition: ApiBase.php:395
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:1404
$help
$help
Definition: mcc.php:32
ApiBase\getMain
getMain()
Get the main module.
Definition: ApiBase.php:537
ApiBase\getParameterFromSettings
getParameterFromSettings( $paramName, $paramSettings, $parseLimit)
Using the settings determine the value for the given parameter.
Definition: ApiBase.php:1032
$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). 'DeleteUnknownPreferences':Called by the cleanupPreferences.php maintenance script to build a WHERE clause with which to delete preferences that are not known about. This hook is used by extensions that have dynamically-named preferences that should not be deleted in the usual cleanup process. For example, the Gadgets extension creates preferences prefixed with 'gadget-', and so anything with that prefix is excluded from the deletion. &where:An array that will be passed as the $cond parameter to IDatabase::select() to determine what will be deleted from the user_properties table. $db:The IDatabase object, useful for accessing $db->buildLike() etc. '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:1255
ApiBase\PARAM_MAX_CHARS
const PARAM_MAX_CHARS
(integer) Maximum length of a string in characters (unicode codepoints).
Definition: ApiBase.php:227
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:963
User\IGNORE_USER_RIGHTS
const IGNORE_USER_RIGHTS
Definition: User.php:90
ApiBase\getExamples
getExamples()
Returns usage examples for this module.
Definition: ApiBase.php:2598
ApiBase\$mModulePrefix
string $mModulePrefix
Definition: ApiBase.php:255
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:157
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:172
ApiBase\PARAM_MAX_BYTES
const PARAM_MAX_BYTES
(integer) Maximum length of a string in bytes (in UTF-8 encoding).
Definition: ApiBase.php:221
ApiHelpParamValueMessage
Message subclass that prepends wikitext for API help.
Definition: ApiHelpParamValueMessage.php:32
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:53
Title\newFromID
static newFromID( $id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:416
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:203
$ext
$ext
Definition: router.php:55
ApiBase\dieDebug
static dieDebug( $method, $message)
Internal code errors should be reported with this method.
Definition: ApiBase.php:2078
ExternalUserNames\isExternal
static isExternal( $username)
Tells whether the username is external or not.
Definition: ExternalUserNames.php:115
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:1766
ApiBase\getErrorFormatter
getErrorFormatter()
Get the error formatter.
Definition: ApiBase.php:655
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:881
ApiQueryUserInfo\getBlockInfo
static getBlockInfo(Block $block)
Get basic info about a given block.
Definition: ApiQueryUserInfo.php:65
IP\isIPAddress
static isIPAddress( $ip)
Determine if a string is as valid IP address or network (CIDR prefix).
Definition: IP.php:77
ApiBase\getErrorFromStatus
getErrorFromStatus( $status, &$extraData=null)
Get error (as code, string) from a Status object.
Definition: ApiBase.php:2692
ApiBase\LIMIT_SML1
const LIMIT_SML1
Slow query, standard limit.
Definition: ApiBase.php:238
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:2421
$type
$type
Definition: testCompression.php:48