MediaWiki REL1_31
ApiBase.php
Go to the documentation of this file.
1<?php
24
37abstract 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
106
111 const PARAM_REQUIRED = 8;
112
118
124 const PARAM_HELP_MSG = 10;
125
132
142
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
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
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() {
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 /************************************************************************/
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
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' ) {
1126 $type = MWNamespace::getValidNamespaces();
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
1771 $status = Status::newGood();
1772 foreach ( $errors as $error ) {
1773 if ( is_array( $error ) && $error[0] === 'blockedtext' && $user->getBlock() ) {
1775 'apierror-blocked',
1776 'blocked',
1777 [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $user->getBlock() ) ]
1778 ) );
1779 } elseif ( is_array( $error ) && $error[0] === 'autoblockedtext' && $user->getBlock() ) {
1781 'apierror-autoblocked',
1782 'autoblocked',
1783 [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $user->getBlock() ) ]
1784 ) );
1785 } elseif ( is_array( $error ) && $error[0] === 'systemblockedtext' && $user->getBlock() ) {
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] )
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
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfUrlencode( $s)
We want some things to be included as literal characters in our title URLs for prettiness,...
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
wfReadOnlyReason()
Check if the site is in read-only mode and return the message if so.
wfGetAllCallers( $limit=3)
Return a string consisting of callers in the stack.
wfTransactionalTimeLimit()
Set PHP's time limit to the larger of php.ini or $wgTransactionalTimeLimit.
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
const RE_IP_BYTE
Definition IP.php:29
$IP
Definition WebStart.php:52
$line
Definition cdb.php:59
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:37
getModulePrefix()
Get parameter prefix (usually two letters or an empty string).
Definition ApiBase.php:529
const PARAM_REQUIRED
(boolean) Is the parameter required?
Definition ApiBase.php:111
const PARAM_MAX2
(integer) Max value allowed for the parameter for users with the apihighlimits right,...
Definition ApiBase.php:96
const PARAM_SUBMODULE_MAP
(string[]) When PARAM_TYPE is 'submodule', map parameter values to submodule paths.
Definition ApiBase.php:165
const PARAM_DEPRECATED
(boolean) Is the parameter deprecated (will show a warning)?
Definition ApiBase.php:105
checkUserRightsAny( $rights, $user=null)
Helper function for permission-denied errors.
Definition ApiBase.php:2006
safeProfileOut()
Definition ApiBase.php:2621
getParameter( $paramName, $parseLimit=true)
Get a value for the given parameter.
Definition ApiBase.php:773
getModuleFromPath( $path)
Get a module from its module path.
Definition ApiBase.php:603
getDB()
Gets a default replica DB connection object.
Definition ApiBase.php:669
encodeParamName( $paramName)
This method mangles parameter name based on the prefix supplied to the constructor.
Definition ApiBase.php:730
parameterNotEmpty( $x)
Callback function used in requireOnlyOneParameter to check whether required parameters are set.
Definition ApiBase.php:911
static makeMessage( $msg, IContextSource $context, array $params=null)
Create a Message from a string or array.
Definition ApiBase.php:1741
const PARAM_MAX
(integer) Max value allowed for the parameter, for PARAM_TYPE 'integer' and 'limit'.
Definition ApiBase.php:90
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
Definition ApiBase.php:1895
getWatchlistUser( $params)
Gets the user for whom to get the watchlist.
Definition ApiBase.php:1687
getParamDescription()
Returns an array of parameter descriptions.
Definition ApiBase.php:2578
isDeprecated()
Indicates whether this module is deprecated.
Definition ApiBase.php:445
parseMsgInternal( $error)
Definition ApiBase.php:2881
dieContinueUsageIf( $condition)
Die with the 'badcontinue' error.
Definition ApiBase.php:2066
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
const PARAM_DEPRECATED_VALUES
(array) When PARAM_TYPE is an array, this indicates which of the values are deprecated.
Definition ApiBase.php:202
array null bool $mModuleSource
Definition ApiBase.php:259
getParent()
Get the parent of this module.
Definition ApiBase.php:555
validateUser( $value, $encParamName)
Validate and normalize parameters of type 'user'.
Definition ApiBase.php:1610
static dieDebug( $method, $message)
Internal code errors should be reported with this method.
Definition ApiBase.php:2078
getHelpUrls()
Return links to more detailed help pages about the module.
Definition ApiBase.php:379
const PARAM_ISMULTI_LIMIT1
(integer) Maximum number of values, for normal users.
Definition ApiBase.php:208
getErrorFromStatus( $status, &$extraData=null)
Get error (as code, string) from a Status object.
Definition ApiBase.php:2692
getModuleManager()
Get the module manager, or null if this module has no sub-modules.
Definition ApiBase.php:304
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
getMain()
Get the main module.
Definition ApiBase.php:537
const PARAM_TYPE
(string|string[]) Either an array of allowed value strings, or a string type as described below.
Definition ApiBase.php:87
getCustomPrinter()
If the module may only be used with a certain format module, it should override this method to return...
Definition ApiBase.php:317
setWatch( $watch, $titleObj, $userOption=null)
Set a watch (or unwatch) based the based on a watchlist parameter.
Definition ApiBase.php:1656
const PARAM_SENSITIVE
(boolean) Is the parameter sensitive? Note 'password'-type fields are always sensitive regardless of ...
Definition ApiBase.php:193
parseMsg( $error)
Return the error message related to a certain array.
Definition ApiBase.php:2902
const PARAM_HELP_MSG_INFO
(array) Specify additional information tags for the parameter.
Definition ApiBase.php:141
getErrorFormatter()
Get the error formatter.
Definition ApiBase.php:655
isWriteMode()
Indicates whether this module requires write mode.
Definition ApiBase.php:428
const PARAM_DFLT
(null|boolean|integer|string) Default value of the parameter.
Definition ApiBase.php:48
isInternal()
Indicates whether this module is "internal" Internal API modules are not (yet) intended for 3rd party...
Definition ApiBase.php:455
getFinalSummary()
Get final module summary.
Definition ApiBase.php:2144
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
getDescription()
Returns the description string for this module.
Definition ApiBase.php:2562
const PARAM_HELP_MSG_APPEND
((string|array|Message)[]) Specify additional i18n messages to append to the normal message for this ...
Definition ApiBase.php:131
dieReadOnly()
Helper function for readonly errors.
Definition ApiBase.php:1990
const PARAM_ALLOW_DUPLICATES
(boolean) Allow the same value to be set more than once when PARAM_ISMULTI is true?
Definition ApiBase.php:102
getSummaryMessage()
Return the summary message.
Definition ApiBase.php:2114
modifyHelp(array &$help, array $options, array &$tocData)
Called from ApiHelp before the pieces are joined together and returned.
Definition ApiBase.php:2543
errorArrayToStatus(array $errors, User $user=null)
Turn an array of message keys or key+param arrays into a Status.
Definition ApiBase.php:1766
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
__construct(ApiMain $mainModule, $moduleName, $modulePrefix='')
Definition ApiBase.php:266
addError( $msg, $code=null, $data=null)
Add an error for this module without aborting.
Definition ApiBase.php:1866
getExamplesMessages()
Returns usage examples for this module.
Definition ApiBase.php:332
extractRequestParams( $parseLimit=true)
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:749
addMessagesFromStatus(StatusValue $status, $types=[ 'warning', 'error'])
Add warnings and/or errors from a Status.
Definition ApiBase.php:1878
const PARAM_ISMULTI_LIMIT2
(integer) Maximum number of values, for users with the apihighimits right.
Definition ApiBase.php:215
const PARAM_MAX_CHARS
(integer) Maximum length of a string in characters (unicode codepoints).
Definition ApiBase.php:227
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
addDeprecation( $msg, $feature, $data=[])
Add a deprecation warning for this module.
Definition ApiBase.php:1833
dieUsageMsg( $error)
Output the error message related to a certain array.
Definition ApiBase.php:2932
getExtendedDescription()
Return the extended help text message.
Definition ApiBase.php:2127
setWarning( $warning)
Definition ApiBase.php:2653
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
Definition ApiBase.php:395
getModuleSourceInfo()
Returns information about the source of this module, if known.
Definition ApiBase.php:2454
const PARAM_SUBMODULE_PARAM_PREFIX
(string) When PARAM_TYPE is 'submodule', used to indicate the 'g' prefix added by ApiQueryGeneratorBa...
Definition ApiBase.php:172
static escapeWikiText( $v)
A subset of wfEscapeWikiText for BC texts.
Definition ApiBase.php:1717
const PARAM_MIN
(integer) Lowest value allowed for the parameter, for PARAM_TYPE 'integer' and 'limit'.
Definition ApiBase.php:99
profileDBOut()
Definition ApiBase.php:2635
mustBePosted()
Indicates whether this module must be called with a POST request.
Definition ApiBase.php:436
getWebUITokenSalt(array $params)
Fetch the salt used in the Web UI corresponding to this module.
Definition ApiBase.php:490
static array $extensionInfo
Maps extension paths to info arrays.
Definition ApiBase.php:250
const LIMIT_BIG1
Fast query, standard limit.
Definition ApiBase.php:234
isMain()
Returns true if this module is the main module ($this === $this->mMainModule), false otherwise.
Definition ApiBase.php:546
const LIMIT_SML2
Slow query, apihighlimits limit.
Definition ApiBase.php:240
handleParamNormalization( $paramName, $value, $rawValue)
Handle when a parameter was Unicode-normalized.
Definition ApiBase.php:1364
ApiMain $mMainModule
Definition ApiBase.php:253
validateLimit( $paramName, &$value, $min, $max, $botMax=null, $enforceLimits=false)
Validate the value against the minimum and user/bot maximum limits.
Definition ApiBase.php:1485
getFinalDescription()
Get final module description, after hooks have had a chance to tweak it as needed.
Definition ApiBase.php:2171
string $mModuleName
Definition ApiBase.php:255
const PARAM_MAX_BYTES
(integer) Maximum length of a string in bytes (in UTF-8 encoding).
Definition ApiBase.php:221
warnOrDie(ApiMessage $msg, $enforceLimits=false)
Adds a warning to the output, else dies.
Definition ApiBase.php:1919
static truncateArray(&$arr, $limit)
Truncate an array to a certain length.
Definition ApiBase.php:1671
string $mModulePrefix
Definition ApiBase.php:255
setContinuationManager(ApiContinuationManager $manager=null)
Set the continuation manager.
Definition ApiBase.php:695
profileOut()
Definition ApiBase.php:2613
getResult()
Get the result object.
Definition ApiBase.php:641
requireMaxOneParameter( $params, $required)
Die if more than one of a certain set of parameters is set and not false.
Definition ApiBase.php:823
needsToken()
Returns the token type this module requires in order to execute.
Definition ApiBase.php:477
getDescriptionMessage()
Return the description message.
Definition ApiBase.php:2958
getParameterFromSettings( $paramName, $paramSettings, $parseLimit)
Using the settings determine the value for the given parameter.
Definition ApiBase.php:1032
getModulePath()
Get the path to this module.
Definition ApiBase.php:585
const PARAM_RANGE_ENFORCE
(boolean) For PARAM_TYPE 'integer', enforce PARAM_MIN and PARAM_MAX?
Definition ApiBase.php:117
logFeatureUsage( $feature)
Write logging information for API features to a debug log, for usage analysis.
Definition ApiBase.php:2088
const PARAM_EXTRA_NAMESPACES
(int[]) When PARAM_TYPE is 'namespace', include these as additional possible values.
Definition ApiBase.php:186
shouldCheckMaxlag()
Indicates if this module needs maxlag to be checked.
Definition ApiBase.php:405
getHelpFlags()
Generates the list of flags for the help screen and for action=paraminfo.
Definition ApiBase.php:2421
const LIMIT_SML1
Slow query, standard limit.
Definition ApiBase.php:238
dieUsageMsgOrDebug( $error)
Will only set a warning instead of failing if the global $wgDebugAPI is set to true.
Definition ApiBase.php:2945
getFinalParams( $flags=0)
Get final list of parameters, after hooks have had a chance to tweak it as needed.
Definition ApiBase.php:2225
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
static $messageMap
Definition ApiBase.php:2734
isReadMode()
Indicates whether this module requires read rights.
Definition ApiBase.php:413
getExamples()
Returns usage examples for this module.
Definition ApiBase.php:2598
dieWithException( $exception, array $options=[])
Abort execution with an error derived from an exception.
Definition ApiBase.php:1907
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition ApiBase.php:124
addWarning( $msg, $code=null, $data=null)
Add a warning for this module.
Definition ApiBase.php:1819
requireAtLeastOneParameter( $params, $required)
Die if none of a certain set of parameters is set and not false.
Definition ApiBase.php:851
getTitleFromTitleOrPageId( $params)
Get a Title object from a title or pageid param, if possible.
Definition ApiBase.php:963
const PARAM_ALL
(boolean|string) When PARAM_TYPE has a defined set of values and PARAM_ISMULTI is true,...
Definition ApiBase.php:180
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
const LIMIT_BIG2
Fast query, apihighlimits limit.
Definition ApiBase.php:236
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:521
getTitleOrPageId( $params, $load=false)
Get a WikiPage object from a title or pageid param, if possible.
Definition ApiBase.php:926
validateTimestamp( $value, $encParamName)
Validate and normalize parameters of type 'timestamp'.
Definition ApiBase.php:1538
dieStatus(StatusValue $status)
Throw an ApiUsageException based on the Status object.
Definition ApiBase.php:1960
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
Definition ApiBase.php:2643
getFinalParamDescription()
Get final parameter descriptions, after hooks have had a chance to tweak it as needed.
Definition ApiBase.php:2257
checkTitleUserPermissions(Title $title, $actions, $user=null)
Helper function for permission-denied errors.
Definition ApiBase.php:2024
dieBlocked(Block $block)
Throw an ApiUsageException, which will (if uncaught) call the main module's error handler and die wit...
Definition ApiBase.php:1935
const ALL_DEFAULT_STRING
Definition ApiBase.php:231
dynamicParameterDocumentation()
Indicate if the module supports dynamically-determined parameters that cannot be included in self::ge...
Definition ApiBase.php:719
getContinuationManager()
Get the continuation manager.
Definition ApiBase.php:681
explodeMultiValue( $value, $limit)
Split a multi-valued parameter string, like explode()
Definition ApiBase.php:1376
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
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
validateToken( $token, array $params)
Validate the supplied token.
Definition ApiBase.php:1575
getConditionalRequestData( $condition)
Returns data for HTTP conditional request mechanisms.
Definition ApiBase.php:506
profileDBIn()
Definition ApiBase.php:2628
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
profileIn()
Definition ApiBase.php:2605
const PARAM_ISMULTI
(boolean) Accept multiple pipe-separated values for this parameter (e.g.
Definition ApiBase.php:51
lacksSameOriginSecurity()
Returns true if the current request breaks the same-origin policy.
Definition ApiBase.php:569
This manages continuation state.
static stripMarkup( $text)
Turn wikitext into something resembling plaintext.
Message subclass that prepends wikitext for API help.
This is the main API class, used for both external and internal processing.
Definition ApiMain.php:43
Extension of Message implementing IApiMessage.
static create( $msg, $code=null, array $data=null)
Create an IApiMessage for the message.
static getTokenTypeSalts()
Get the salts for known token types.
static getToken(User $user, MediaWiki\Session\Session $session, $salt)
Get a token from a salt.
static getBlockInfo(Block $block)
Get basic info about a given block.
Extension of RawMessage implementing IApiMessage.
Exception used to abort API execution with an error.
static newWithMessage(ApiBase $module=null, $msg, $code=null, $data=null, $httpCode=0)
getType()
Get the type of target for this particular block.
Definition Block.php:1450
const TYPE_AUTO
Definition Block.php:86
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...
The simplest way of implementing IContextSource is to hold a RequestContext as a member variable and ...
msg( $key)
Get a Message object with context set Parameters are the same as wfMessage()
IContextSource $context
getContext()
Get the base IContextSource object.
setContext(IContextSource $context)
static isExternal( $username)
Tells whether the username is external or not.
MediaWiki exception.
The Message class provides methods which fulfil two basic services:
Definition Message.php:159
Variant of the Message class.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Represents a title within MediaWiki.
Definition Title.php:39
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition User.php:53
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition User.php:591
const IGNORE_USER_RIGHTS
Definition User.php:90
static isValidUserName( $name)
Is the input a valid username?
Definition User.php:969
static doWatchOrUnwatch( $watch, Title $title, User $user)
Watch or unwatch a page.
when a variable name is used in a it is silently declared as a new local masking the global
Definition design.txt:95
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
const NS_USER
Definition Defines.php:76
the array() calling protocol came about after MediaWiki 1.4rc1.
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:2806
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:181
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:2001
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:865
namespace and then decline to actually register it file or subcat img or subcat $title
Definition hooks.txt:964
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;div ...>$1&lt;/div>"). - flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException':Called before an exception(or PHP error) is logged. This is meant for integration with external error aggregation services
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:2005
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
Allows to change the fields on the form that will be generated $name
Definition hooks.txt:302
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 local account $user
Definition hooks.txt:247
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:37
Interface for messages with machine-readable data for use by the API.
Interface for objects which can provide a MediaWiki context on request.
Basic database interface for live and lazy-loaded relation database handles.
Definition IDatabase.php:38
$help
Definition mcc.php:32
const DB_REPLICA
Definition defines.php:25
if(!is_readable( $file)) $ext
Definition router.php:55
$params