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