44 use ApiBlockInfoTrait;
282 'blockedtext' => [
'apierror-blocked',
'blocked' ],
283 'blockedtext-partial' => [
'apierror-blocked-partial',
'blocked' ],
284 'autoblockedtext' => [
'apierror-autoblocked',
'autoblocked' ],
285 'systemblockedtext' => [
'apierror-systemblocked',
'blocked' ],
286 'blockedtext-composite' => [
'apierror-blocked',
'blocked' ],
304 $this->mMainModule = $mainModule;
305 $this->mModuleName = $moduleName;
306 $this->mModulePrefix = $modulePrefix;
334 abstract public function execute();
572 self::dieDebug( __METHOD__,
'base method was called on main module.' );
575 return $this->
getMain()->lacksSameOriginSecurity();
604 if (
$path ===
'main' ) {
608 $parts = explode(
'+',
$path );
609 if ( count( $parts ) === 1 ) {
611 $parts = explode(
' ',
$path );
614 $count = count( $parts );
615 for ( $i = 0; $i < $count; $i++ ) {
617 $manager = $parent->getModuleManager();
618 if ( $manager ===
null ) {
619 $errorPath = implode(
'+', array_slice( $parts, 0, $i ) );
620 $this->
dieWithError( [
'apierror-badmodule-nosubmodules', $errorPath ],
'badmodule' );
622 $module = $manager->getModule( $parts[$i] );
624 if ( $module ===
null ) {
625 $errorPath = $i ? implode(
'+', array_slice( $parts, 0, $i ) ) : $parent->getModuleName();
627 [
'apierror-badmodule-badsubmodule', $errorPath,
wfEscapeWikiText( $parts[$i] ) ],
644 self::dieDebug( __METHOD__,
'base method was called on main module. ' );
647 return $this->
getMain()->getResult();
658 self::dieDebug( __METHOD__,
'base method was called on main module. ' );
661 return $this->
getMain()->getErrorFormatter();
669 if ( !isset( $this->mReplicaDB ) ) {
684 self::dieDebug( __METHOD__,
'base method was called on main module. ' );
687 return $this->
getMain()->getContinuationManager();
698 self::dieDebug( __METHOD__,
'base method was called on main module. ' );
701 $this->
getMain()->setContinuationManager( $manager );
711 return MediaWikiServices::getInstance()->getPermissionManager();
740 if ( is_array( $paramName ) ) {
741 return array_map(
function ( $name ) {
742 return $this->mModulePrefix . $name;
745 return $this->mModulePrefix . $paramName;
762 if ( is_bool( $options ) ) {
763 $options = [
'parseLimit' => $options ];
766 'parseLimit' =>
true,
770 $parseLimit = (bool)$options[
'parseLimit'];
773 if ( !isset( $this->mParamCache[$parseLimit] ) ) {
781 foreach ( $params as $paramName => $paramSettings ) {
782 if ( isset( $paramSettings[self::PARAM_TEMPLATE_VARS] ) ) {
787 $paramName, $paramSettings, $parseLimit
790 $results[$paramName] = $ex;
800 while ( $toProcess ) {
801 list( $name, $targets, $settings ) = array_shift( $toProcess );
803 foreach ( $targets as $placeholder => $target ) {
804 if ( !array_key_exists( $target, $results ) ) {
809 if ( !is_array( $results[$target] ) || !$results[$target] ) {
818 unset( $targets[$placeholder] );
819 $placeholder =
'{' . $placeholder .
'}';
821 foreach ( $results[$target] as $value ) {
822 if ( !preg_match(
'/^[^{}]*$/', $value ) ) {
825 if ( !isset( $warned[$encTargetName][$value] ) ) {
826 $warned[$encTargetName][$value] =
true;
828 'apiwarn-ignoring-invalid-templated-value',
836 $newName = str_replace( $placeholder, $value, $name );
841 $results[$newName] = $ex;
845 foreach ( $targets as $k => $v ) {
846 $newTargets[$k] = str_replace( $placeholder, $value, $v );
848 $toProcess[] = [ $newName, $newTargets, $settings ];
855 $this->mParamCache[$parseLimit] = $results;
858 $ret = $this->mParamCache[$parseLimit];
859 if ( !$options[
'safeMode'] ) {
860 foreach ( $ret as $v ) {
867 return $this->mParamCache[$parseLimit];
878 'parseLimit' => $parseLimit,
894 $required = func_get_args();
895 array_shift( $required );
897 $intersection = array_intersect( array_keys( array_filter( $params,
898 [ $this,
'parameterNotEmpty' ] ) ), $required );
900 if ( count( $intersection ) > 1 ) {
902 'apierror-invalidparammix',
903 Message::listParam( array_map(
907 array_values( $intersection )
909 count( $intersection ),
911 } elseif ( count( $intersection ) == 0 ) {
913 'apierror-missingparam-one-of',
914 Message::listParam( array_map(
918 array_values( $required )
932 $required = func_get_args();
933 array_shift( $required );
935 $intersection = array_intersect( array_keys( array_filter( $params,
936 [ $this,
'parameterNotEmpty' ] ) ), $required );
938 if ( count( $intersection ) > 1 ) {
940 'apierror-invalidparammix',
941 Message::listParam( array_map(
945 array_values( $intersection )
947 count( $intersection ),
960 $required = func_get_args();
961 array_shift( $required );
963 $intersection = array_intersect(
964 array_keys( array_filter( $params, [ $this,
'parameterNotEmpty' ] ) ),
968 if ( count( $intersection ) == 0 ) {
970 'apierror-missingparam-at-least-one-of',
971 Message::listParam( array_map(
975 array_values( $required )
991 if ( $this->
getConfig()->
get(
'DebugAPI' ) || $this->
getMain()->isInternalMode() ) {
995 $queryValues = $this->
getRequest()->getQueryValuesOnly();
997 foreach ( $params as $param ) {
998 if ( $prefix !==
'noprefix' ) {
1001 if ( array_key_exists( $param, $queryValues ) ) {
1002 $badParams[] = $param;
1008 [
'apierror-mustpostparams', implode(
', ', $badParams ), count( $badParams ) ]
1020 return !is_null( $x ) && $x !==
false;
1038 if ( isset( $params[
'title'] ) ) {
1040 if ( !$titleObj || $titleObj->isExternal() ) {
1043 if ( !$titleObj->canExist() ) {
1047 if ( $load !==
false ) {
1048 $pageObj->loadPageData( $load );
1050 } elseif ( isset( $params[
'pageid'] ) ) {
1051 if ( $load ===
false ) {
1056 $this->
dieWithError( [
'apierror-nosuchpageid', $params[
'pageid'] ] );
1075 if ( isset( $params[
'title'] ) ) {
1077 if ( !$titleObj || $titleObj->isExternal() ) {
1081 } elseif ( isset( $params[
'pageid'] ) ) {
1084 $this->
dieWithError( [
'apierror-nosuchpageid', $params[
'pageid'] ] );
1102 switch ( $watchlist ) {
1110 # If the user is already watching, don't bother checking
1111 if ( $userWatching ) {
1114 # If no user option was passed, use watchdefault and watchcreations
1115 if ( is_null( $userOption ) ) {
1116 return $this->
getUser()->getBoolOption(
'watchdefault' ) ||
1117 $this->
getUser()->getBoolOption(
'watchcreations' ) && !$titleObj->exists();
1120 # Watch the article based on the user preference
1121 return $this->
getUser()->getBoolOption( $userOption );
1124 return $userWatching;
1127 return $userWatching;
1145 if ( !is_array( $paramSettings ) ) {
1147 self::PARAM_DFLT => $paramSettings,
1163 if ( !isset(
$type ) ) {
1164 if ( isset( $default ) ) {
1165 $type = gettype( $default );
1171 if (
$type ==
'password' || !empty( $paramSettings[self::PARAM_SENSITIVE] ) ) {
1172 $this->
getMain()->markParamsSensitive( $encParamName );
1175 if (
$type ==
'boolean' ) {
1176 if ( isset( $default ) && $default !==
false ) {
1180 "Boolean param $encParamName's default is set to '$default'. " .
1181 'Boolean parameters must default to false.'
1185 $value = $this->
getMain()->getCheck( $encParamName );
1187 } elseif (
$type ==
'upload' ) {
1188 if ( isset( $default ) ) {
1192 "File upload param $encParamName's default is set to " .
1193 "'$default'. File upload parameters may not have a default." );
1196 self::dieDebug( __METHOD__,
"Multi-values not supported for $encParamName" );
1198 $value = $this->
getMain()->getUpload( $encParamName );
1199 $provided = $value->exists();
1200 if ( !$value->exists() ) {
1204 $value = $this->
getMain()->getRequest()->unsetVal( $encParamName );
1205 if ( $value !==
null ) {
1207 [
'apierror-badupload', $encParamName ],
1208 "badupload_{$encParamName}"
1213 $value = $this->
getMain()->getVal( $encParamName, $default );
1214 $provided = $this->
getMain()->getCheck( $encParamName );
1216 if ( isset( $value ) &&
$type ==
'namespace' ) {
1217 $type = MediaWikiServices::getInstance()->getNamespaceInfo()->
1218 getValidNamespaces();
1219 if ( isset( $paramSettings[self::PARAM_EXTRA_NAMESPACES] ) &&
1220 is_array( $paramSettings[self::PARAM_EXTRA_NAMESPACES] )
1222 $type = array_merge(
$type, $paramSettings[self::PARAM_EXTRA_NAMESPACES] );
1228 if ( isset( $value ) &&
$type ==
'submodule' ) {
1229 if ( isset( $paramSettings[self::PARAM_SUBMODULE_MAP] ) ) {
1230 $type = array_keys( $paramSettings[self::PARAM_SUBMODULE_MAP] );
1236 $request = $this->
getMain()->getRequest();
1237 $rawValue = $request->getRawVal( $encParamName );
1238 if ( $rawValue ===
null ) {
1239 $rawValue = $default;
1243 if ( isset( $value ) && substr( $rawValue, 0, 1 ) ===
"\x1f" ) {
1247 $value = implode(
"\x1f", $request->normalizeUnicode( explode(
"\x1f", $rawValue ) ) );
1249 $this->
dieWithError(
'apierror-badvalue-notmultivalue',
'badvalue_notmultivalue' );
1254 if ( $rawValue !== $value ) {
1260 if ( $allowAll && $multi && is_array(
$type ) && in_array( $allSpecifier,
$type,
true ) ) {
1263 "For param $encParamName, PARAM_ALL collides with a possible value" );
1265 if ( isset( $value ) && ( $multi || is_array(
$type ) ) ) {
1271 $allowAll ? $allSpecifier :
null,
1277 if ( isset( $value ) ) {
1280 if ( !is_array(
$type ) ) {
1287 if ( $required && $value ===
'' ) {
1288 $this->
dieWithError( [
'apierror-missingparam', $encParamName ] );
1296 if ( is_array( $value ) ) {
1297 $value = array_map(
'intval', $value );
1298 if ( !is_null( $min ) || !is_null( $max ) ) {
1299 foreach ( $value as &$v ) {
1300 $this->
validateLimit( $paramName, $v, $min, $max,
null, $enforceLimits );
1304 $value = (int)$value;
1305 if ( !is_null( $min ) || !is_null( $max ) ) {
1306 $this->
validateLimit( $paramName, $value, $min, $max,
null, $enforceLimits );
1312 if ( $value !==
'max' ) {
1313 $value = (int)$value;
1316 self::dieDebug( __METHOD__,
"Multi-values not supported for $encParamName" );
1318 if ( !$parseLimit ) {
1322 if ( !isset( $paramSettings[self::PARAM_MAX] )
1323 || !isset( $paramSettings[self::PARAM_MAX2] )
1327 "MAX1 or MAX2 are not defined for the limit $encParamName"
1330 if ( $value ===
'max' ) {
1331 $value = $this->
getMain()->canApiHighLimits()
1339 $paramSettings[self::PARAM_MIN] ?? 0,
1340 $paramSettings[self::PARAM_MAX],
1341 $paramSettings[self::PARAM_MAX2]
1347 self::dieDebug( __METHOD__,
"Multi-values not supported for $encParamName" );
1351 if ( is_array( $value ) ) {
1352 foreach ( $value as $key => $val ) {
1360 if ( is_array( $value ) ) {
1361 foreach ( $value as $key => $val ) {
1362 $value[$key] = $this->
validateUser( $val, $encParamName );
1372 if ( !is_array( $value ) && !$multi ) {
1373 $value = [ $value ];
1376 if ( !$tagsStatus->isGood() ) {
1381 self::dieDebug( __METHOD__,
"Param $encParamName's type is unknown - $type" );
1386 if ( !$dupes && is_array( $value ) ) {
1387 $value = array_unique( $value );
1390 if ( in_array(
$type, [
'NULL',
'string',
'text',
'password' ],
true ) ) {
1391 foreach ( (array)$value as $val ) {
1392 if ( isset( $paramSettings[self::PARAM_MAX_BYTES] )
1393 && strlen( $val ) > $paramSettings[self::PARAM_MAX_BYTES]
1395 $this->
dieWithError( [
'apierror-maxbytes', $encParamName,
1396 $paramSettings[self::PARAM_MAX_BYTES] ] );
1398 if ( isset( $paramSettings[self::PARAM_MAX_CHARS] )
1399 && mb_strlen( $val,
'UTF-8' ) > $paramSettings[self::PARAM_MAX_CHARS]
1401 $this->
dieWithError( [
'apierror-maxchars', $encParamName,
1402 $paramSettings[self::PARAM_MAX_CHARS] ] );
1408 if ( $deprecated && $provided ) {
1409 $feature = $encParamName;
1411 while ( !$m->isMain() ) {
1412 $p = $m->getParent();
1413 $name = $m->getModuleName();
1414 $param = $p->encodeParamName( $p->getModuleManager()->getModuleGroup( $name ) );
1415 $feature =
"{$param}={$name}&{$feature}";
1418 $this->
addDeprecation( [
'apiwarn-deprecation-parameter', $encParamName ], $feature );
1422 $usedDeprecatedValues = $deprecatedValues && $provided
1423 ? array_intersect( array_keys( $deprecatedValues ), (array)$value )
1425 if ( $usedDeprecatedValues ) {
1426 $feature =
"$encParamName=";
1428 while ( !$m->isMain() ) {
1429 $p = $m->getParent();
1430 $name = $m->getModuleName();
1431 $param = $p->encodeParamName( $p->getModuleManager()->getModuleGroup( $name ) );
1432 $feature =
"{$param}={$name}&{$feature}";
1435 foreach ( $usedDeprecatedValues as $v ) {
1436 $msg = $deprecatedValues[$v];
1437 if ( $msg ===
true ) {
1438 $msg = [
'apiwarn-deprecation-parameter',
"$encParamName=$v" ];
1443 } elseif ( $required ) {
1444 $this->
dieWithError( [
'apierror-missingparam', $encParamName ] );
1459 $this->
addWarning( [
'apiwarn-badutf8', $encParamName ] );
1470 if ( substr( $value, 0, 1 ) ===
"\x1f" ) {
1472 $value = substr( $value, 1 );
1477 return explode( $sep, $value, $limit );
1498 $allSpecifier =
null, $limit1 =
null, $limit2 =
null
1500 if ( ( $value ===
'' || $value ===
"\x1f" ) && $allowMultiple ) {
1509 $sizeLimit = count( $valuesList ) > $limit1 && $this->mMainModule->canApiHighLimits()
1513 if ( $allowMultiple && is_array( $allowedValues ) && $allSpecifier &&
1514 count( $valuesList ) === 1 && $valuesList[0] === $allSpecifier
1516 return $allowedValues;
1519 if ( count( $valuesList ) > $sizeLimit ) {
1521 [
'apierror-toomanyvalues', $valueName, $sizeLimit ],
1522 "too-many-$valueName"
1526 if ( !$allowMultiple && count( $valuesList ) != 1 ) {
1528 if ( in_array( $value, $allowedValues,
true ) ) {
1532 $values = array_map(
function ( $v ) {
1534 }, $allowedValues );
1536 'apierror-multival-only-one-of',
1538 Message::listParam( $values ),
1540 ],
"multival_$valueName" );
1543 if ( is_array( $allowedValues ) ) {
1545 $unknown = array_map(
'wfEscapeWikiText', array_diff( $valuesList, $allowedValues ) );
1546 if ( count( $unknown ) ) {
1547 if ( $allowMultiple ) {
1549 'apiwarn-unrecognizedvalues',
1551 Message::listParam( $unknown,
'comma' ),
1556 [
'apierror-unrecognizedvalue', $valueName,
wfEscapeWikiText( $valuesList[0] ) ],
1557 "unknown_$valueName"
1562 $valuesList = array_intersect( $valuesList, $allowedValues );
1565 return $allowMultiple ? $valuesList : $valuesList[0];
1578 protected function validateLimit( $paramName, &$value, $min, $max, $botMax =
null,
1579 $enforceLimits =
false
1581 if ( !is_null( $min ) && $value < $min ) {
1583 [
'apierror-integeroutofrange-belowminimum',
1585 'integeroutofrange',
1586 [
'min' => $min,
'max' => $max,
'botMax' => $botMax ?: $max ]
1589 $this->
warnOrDie( $msg, $enforceLimits );
1595 if ( $this->
getMain()->isInternalMode() ) {
1601 if ( !is_null( $max ) && $value > $max ) {
1602 if ( !is_null( $botMax ) && $this->
getMain()->canApiHighLimits() ) {
1603 if ( $value > $botMax ) {
1605 [
'apierror-integeroutofrange-abovebotmax',
1607 'integeroutofrange',
1608 [
'min' => $min,
'max' => $max,
'botMax' => $botMax ?: $max ]
1611 $this->
warnOrDie( $msg, $enforceLimits );
1616 [
'apierror-integeroutofrange-abovemax',
1618 'integeroutofrange',
1619 [
'min' => $min,
'max' => $max,
'botMax' => $botMax ?: $max ]
1622 $this->
warnOrDie( $msg, $enforceLimits );
1640 [
'apiwarn-unclearnowtimestamp', $encParamName,
wfEscapeWikiText( $value ) ],
1641 'unclear-"now"-timestamp'
1647 if ( $value ===
'now' ) {
1652 if ( $timestamp ===
false ) {
1655 "badtimestamp_{$encParamName}"
1674 if ( !isset( $salts[$tokenType] ) ) {
1676 "Module '{$this->getModuleName()}' tried to use token type '$tokenType' " .
1677 'without registering it'
1684 if ( $tokenObj->match( $token ) ) {
1689 if ( $webUiSalt !==
null && $this->
getUser()->matchEditToken(
1712 if ( $name !==
false ) {
1733 "baduser_{$encParamName}"
1750 protected function setWatch( $watch, $titleObj, $userOption =
null ) {
1752 if ( $value ===
null ) {
1766 if ( !is_null( $params[
'owner'] ) && !is_null( $params[
'token'] ) ) {
1768 if ( !( $user && $user->getId() ) ) {
1773 $token = $user->getOption(
'watchlisttoken' );
1774 if ( $token ==
'' || !hash_equals( $token, $params[
'token'] ) ) {
1775 $this->
dieWithError(
'apierror-bad-watchlist-token',
'bad_wltoken' );
1778 if ( !$this->
getUser()->isLoggedIn() ) {
1779 $this->
dieWithError(
'watchlistanontext',
'notloggedin' );
1801 if ( is_string( $msg ) ) {
1803 } elseif ( is_array( $msg ) ) {
1806 if ( !$msg instanceof
Message ) {
1812 $msg->params( $params );
1826 if ( $user ===
null ) {
1831 foreach ( $errors as $error ) {
1832 if ( !is_array( $error ) ) {
1833 $error = [ $error ];
1835 if ( is_string( $error[0] ) && isset( self::$blockMsgMap[$error[0]] ) && $user->getBlock() ) {
1836 list( $msg, $code ) = self::$blockMsgMap[$error[0]];
1838 [
'blockinfo' => $this->getBlockDetails( $user->getBlock() ) ]
1854 if ( $user ===
null ) {
1858 foreach ( self::$blockMsgMap as $msg => list( $apiMsg, $code ) ) {
1859 if (
$status->hasMessage( $msg ) && $user->getBlock() ) {
1861 [
'blockinfo' => $this->getBlockDetails( $user->getBlock() ) ]
1888 foreach ( $fields as list( $table, $field ) ) {
1889 if ( isset( self::$filterIDsCache[$table][$field] ) ) {
1890 $row = self::$filterIDsCache[$table][$field];
1892 $row = $this->
getDB()->selectRow(
1895 'min_id' =>
"MIN($field)",
1896 'max_id' =>
"MAX($field)",
1901 self::$filterIDsCache[$table][$field] = $row;
1903 $min = min( $min, $row->min_id );
1904 $max = max( $max, $row->max_id );
1906 return array_filter( $ids,
function ( $id ) use ( $min, $max ) {
1907 return ( is_int( $id ) && $id >= 0 || ctype_digit( $id ) )
1908 && $id >= $min && $id <= $max;
1948 $data = (array)$data;
1949 if ( $feature !==
null ) {
1950 $data[
'feature'] = $feature;
1953 $this->
addWarning( $msg,
'deprecation', $data );
1957 $msgs = [ $this->
msg(
'api-usage-mailinglist-ref' ) ];
1958 Hooks::run(
'ApiDeprecationHelp', [ &$msgs ] );
1959 if ( count( $msgs ) > 1 ) {
1960 $key =
'$' . implode(
' $', range( 1, count( $msgs ) ) );
1961 $msg = (
new RawMessage( $key ) )->params( $msgs );
1963 $msg = reset( $msgs );
1965 $this->
getMain()->addWarning( $msg,
'deprecation-help' );
1980 public function addError( $msg, $code =
null, $data =
null ) {
2014 public function dieWithError( $msg, $code =
null, $data =
null, $httpCode =
null ) {
2040 if ( $enforceLimits ) {
2057 if ( $block->
getType() == DatabaseBlock::TYPE_AUTO ) {
2059 'apierror-autoblocked',
2061 [
'blockinfo' => $this->getBlockDetails( $block ) ]
2065 'apierror-blocked-partial',
2067 [
'blockinfo' => $this->getBlockDetails( $block ) ]
2073 [
'blockinfo' => $this->getBlockDetails( $block ) ]
2088 throw new MWException(
'Successful status passed to ApiBase::dieStatus' );
2094 if ( !
$status->getErrorsByType(
'error' ) ) {
2096 foreach (
$status->getErrorsByType(
'warning' ) as $err ) {
2097 $newStatus->fatal( $err[
'message'], ...$err[
'params'] );
2099 if ( !$newStatus->getErrorsByType(
'error' ) ) {
2100 $newStatus->fatal(
'unknownerror-nocode' );
2116 'apierror-readonly',
2134 $rights = (array)$rights;
2136 ->userHasAnyRight( $user, ...$rights )
2138 $this->
dieWithError( [
'apierror-permissiondenied', $this->
msg(
"action-{$rights[0]}" ) ] );
2157 if ( !is_array( $options ) ) {
2158 wfDeprecated(
'$user as the third parameter to ' . __METHOD__,
'1.33' );
2159 $options = [
'user' => $options ];
2161 $user = $options[
'user'] ?? $this->
getUser();
2164 foreach ( (array)$actions as $action ) {
2165 $errors = array_merge(
2172 if ( !empty( $options[
'autoblock'] ) ) {
2173 $user->spreadAnyEditBlock();
2192 if ( $this->
getConfig()->
get(
'DebugAPI' ) !==
true ) {
2220 protected static function dieDebug( $method, $message ) {
2221 throw new MWException(
"Internal error in $method: $message" );
2231 static $loggedFeatures = [];
2235 if ( isset( $loggedFeatures[$feature] ) ) {
2238 $loggedFeatures[$feature] =
true;
2242 'feature' => $feature,
2244 'username' => str_replace(
' ',
'_', $this->
getUser()->getName() ),
2245 'ip' => $request->getIP(),
2246 'referer' => (string)$request->getHeader(
'Referer' ),
2247 'agent' => $this->
getMain()->getUserAgent(),
2251 $s =
'"' . addslashes( $ctx[
'feature'] ) .
'"' .
2253 ' "' . $ctx[
'ip'] .
'"' .
2254 ' "' . addslashes( $ctx[
'referer'] ) .
'"' .
2255 ' "' . addslashes( $ctx[
'agent'] ) .
'"';
2257 wfDebugLog(
'api-feature-usage',
$s,
'private', $ctx );
2277 return "apihelp-{$this->getModulePath()}-summary";
2291 "apihelp-{$this->getModulePath()}-extended-description",
2292 'api-help-no-extended-description',
2332 $msgs = [ $summary, $extendedDescription ];
2334 Hooks::run(
'APIGetDescriptionMessages', [ $this, &$msgs ] );
2354 $params[
'token'] = [
2355 self::PARAM_TYPE =>
'string',
2356 self::PARAM_REQUIRED =>
true,
2357 self::PARAM_SENSITIVE =>
true,
2358 self::PARAM_HELP_MSG => [
2359 'api-help-param-token',
2362 ] + ( $params[
'token'] ?? [] );
2367 Hooks::run(
'APIGetAllowedParams', [ &$apiModule, &$params, $flags ] );
2386 foreach ( $params as $param => $settings ) {
2387 if ( !is_array( $settings ) ) {
2391 if ( isset( $settings[self::PARAM_HELP_MSG] ) ) {
2394 $msg = $this->
msg(
"apihelp-{$path}-param-{$param}" );
2397 [ $prefix, $param, $name,
$path ] );
2400 'Value in ApiBase::PARAM_HELP_MSG is not valid' );
2402 $msgs[$param] = [ $msg ];
2404 if ( isset( $settings[self::PARAM_TYPE] ) &&
2405 $settings[self::PARAM_TYPE] ===
'submodule'
2407 if ( isset( $settings[self::PARAM_SUBMODULE_MAP] ) ) {
2412 foreach ( $this->
getModuleManager()->getNames( $param ) as $submoduleName ) {
2413 $map[$submoduleName] = $prefix . $submoduleName;
2418 $deprecatedSubmodules = [];
2419 foreach ( $map as $v => $m ) {
2420 $arr = &$submodules;
2421 $isDeprecated =
false;
2426 $summary = $submod->getFinalSummary();
2427 $isDeprecated = $submod->isDeprecated();
2428 if ( $isDeprecated ) {
2429 $arr = &$deprecatedSubmodules;
2436 $key = $summary->getKey();
2437 $params = $summary->getParams();
2439 $key =
'api-help-undocumented-module';
2443 $arr[] = $m->setContext( $this->
getContext() );
2445 $msgs[$param] = array_merge( $msgs[$param], $submodules, $deprecatedSubmodules );
2446 } elseif ( isset( $settings[self::PARAM_HELP_MSG_PER_VALUE] ) ) {
2447 if ( !is_array( $settings[self::PARAM_HELP_MSG_PER_VALUE] ) ) {
2449 'ApiBase::PARAM_HELP_MSG_PER_VALUE is not valid' );
2451 if ( !is_array( $settings[self::PARAM_TYPE] ) ) {
2453 'ApiBase::PARAM_HELP_MSG_PER_VALUE may only be used when ' .
2454 'ApiBase::PARAM_TYPE is an array' );
2460 foreach ( $settings[self::PARAM_TYPE] as $value ) {
2461 if ( isset( $valueMsgs[$value] ) ) {
2462 $msg = $valueMsgs[$value];
2464 $msg =
"apihelp-{$path}-paramvalue-{$param}-{$value}";
2467 [ $prefix, $param, $name,
$path, $value ] );
2472 [ $m->getKey(),
'api-help-param-no-description' ],
2474 isset( $deprecatedValues[$value] )
2476 $msgs[$param][] = $m->setContext( $this->
getContext() );
2479 "Value in ApiBase::PARAM_HELP_MSG_PER_VALUE for $value is not valid" );
2484 if ( isset( $settings[self::PARAM_HELP_MSG_APPEND] ) ) {
2485 if ( !is_array( $settings[self::PARAM_HELP_MSG_APPEND] ) ) {
2487 'Value for ApiBase::PARAM_HELP_MSG_APPEND is not an array' );
2489 foreach ( $settings[self::PARAM_HELP_MSG_APPEND] as $m ) {
2491 [ $prefix, $param, $name,
$path ] );
2493 $msgs[$param][] = $m;
2496 'Value in ApiBase::PARAM_HELP_MSG_APPEND is not valid' );
2502 Hooks::run(
'APIGetParamDescriptionMessages', [ $this, &$msgs ] );
2520 $flags[] =
'deprecated';
2523 $flags[] =
'internal';
2526 $flags[] =
'readrights';
2529 $flags[] =
'writerights';
2532 $flags[] =
'mustbeposted';
2552 if ( $this->mModuleSource !==
false ) {
2557 $rClass =
new ReflectionClass( $this );
2558 $path = $rClass->getFileName();
2561 $this->mModuleSource =
null;
2567 if ( self::$extensionInfo ===
null ) {
2568 $extDir = $this->
getConfig()->get(
'ExtensionDirectory' );
2569 self::$extensionInfo = [
2570 realpath( __DIR__ ) ?: __DIR__ => [
2572 'name' =>
'MediaWiki',
2573 'license-name' =>
'GPL-2.0-or-later',
2575 realpath(
"$IP/extensions" ) ?:
"$IP/extensions" =>
null,
2576 realpath( $extDir ) ?: $extDir =>
null,
2582 'license-name' =>
null,
2584 foreach ( $this->
getConfig()->
get(
'ExtensionCredits' ) as $group ) {
2585 foreach ( $group as
$ext ) {
2586 if ( !isset(
$ext[
'path'] ) || !isset(
$ext[
'name'] ) ) {
2591 $extpath =
$ext[
'path'];
2592 if ( !is_dir( $extpath ) ) {
2593 $extpath = dirname( $extpath );
2595 self::$extensionInfo[realpath( $extpath ) ?: $extpath] =
2596 array_intersect_key(
$ext, $keep );
2600 $extpath =
$ext[
'path'];
2601 if ( !is_dir( $extpath ) ) {
2602 $extpath = dirname( $extpath );
2604 self::$extensionInfo[realpath( $extpath ) ?: $extpath] =
2605 array_intersect_key(
$ext, $keep );
2612 if ( array_key_exists(
$path, self::$extensionInfo ) ) {
2614 $this->mModuleSource = self::$extensionInfo[
$path];
2620 }
while (
$path !== $oldpath );
2623 $this->mModuleSource =
null;