MediaWiki master
ApiMain.php
Go to the documentation of this file.
1<?php
10namespace MediaWiki\Api;
11
12use LogicException;
13use MediaWiki;
38use Throwable;
39use UnexpectedValueException;
44use Wikimedia\Parsoid\Core\SectionMetadata;
45use Wikimedia\ScopedCallback;
47use Wikimedia\Timestamp\ConvertibleTimestamp;
48use Wikimedia\Timestamp\TimestampException;
49use Wikimedia\Timestamp\TimestampFormat as TS;
50
67class ApiMain extends ApiBase {
71 private const API_DEFAULT_FORMAT = 'jsonfm';
72
76 private const API_DEFAULT_USELANG = 'user';
77
81 private const MODULES = [
82 'login' => [
83 'class' => ApiLogin::class,
84 'services' => [
85 'AuthManager',
86 'UserIdentityUtils'
87 ],
88 ],
89 'clientlogin' => [
90 'class' => ApiClientLogin::class,
91 'services' => [
92 'AuthManager',
93 'UrlUtils',
94 ],
95 ],
96 'logout' => [
97 'class' => ApiLogout::class,
98 ],
99 'createaccount' => [
100 'class' => ApiAMCreateAccount::class,
101 'services' => [
102 'AuthManager',
103 'UrlUtils',
104 ],
105 ],
106 'linkaccount' => [
107 'class' => ApiLinkAccount::class,
108 'services' => [
109 'AuthManager',
110 'UrlUtils',
111 ],
112 ],
113 'unlinkaccount' => [
114 'class' => ApiRemoveAuthenticationData::class,
115 'services' => [
116 'AuthManager',
117 ],
118 ],
119 'changeauthenticationdata' => [
120 'class' => ApiChangeAuthenticationData::class,
121 'services' => [
122 'AuthManager',
123 ],
124 ],
125 'removeauthenticationdata' => [
126 'class' => ApiRemoveAuthenticationData::class,
127 'services' => [
128 'AuthManager',
129 ],
130 ],
131 'resetpassword' => [
132 'class' => ApiResetPassword::class,
133 'services' => [
134 'PasswordReset',
135 ]
136 ],
137 'query' => [
138 'class' => ApiQuery::class,
139 'services' => [
140 'ObjectFactory',
141 'WikiExporterFactory',
142 'TitleFormatter',
143 'TitleFactory',
144 ]
145 ],
146 'expandtemplates' => [
147 'class' => ApiExpandTemplates::class,
148 'services' => [
149 'RevisionStore',
150 'ParserFactory',
151 ]
152 ],
153 'parse' => [
154 'class' => ApiParse::class,
155 'services' => [
156 'RevisionLookup',
157 'SkinFactory',
158 'LanguageNameUtils',
159 'LinkBatchFactory',
160 'LinkCache',
161 'ContentHandlerFactory',
162 'ParserFactory',
163 'WikiPageFactory',
164 'ContentRenderer',
165 'ContentTransformer',
166 'CommentFormatter',
167 'TempUserCreator',
168 'UserFactory',
169 'UrlUtils',
170 'TitleFormatter',
171 'JsonCodec',
172 ]
173 ],
174 'stashedit' => [
175 'class' => ApiStashEdit::class,
176 'services' => [
177 'ContentHandlerFactory',
178 'PageEditStash',
179 'RevisionLookup',
180 'StatsFactory',
181 'WikiPageFactory',
182 'TempUserCreator',
183 'UserFactory',
184 ]
185 ],
186 'opensearch' => [
187 'class' => ApiOpenSearch::class,
188 'services' => [
189 'LinkBatchFactory',
190 'SearchEngineConfig',
191 'SearchEngineFactory',
192 'UrlUtils',
193 ]
194 ],
195 'feedcontributions' => [
196 'class' => ApiFeedContributions::class,
197 'services' => [
198 'RevisionStore',
199 'LinkRenderer',
200 'LinkBatchFactory',
201 'HookContainer',
202 'DBLoadBalancerFactory',
203 'NamespaceInfo',
204 'UserFactory',
205 'CommentFormatter',
206 ]
207 ],
208 'feedrecentchanges' => [
209 'class' => ApiFeedRecentChanges::class,
210 'services' => [
211 'SpecialPageFactory',
212 'TempUserConfig',
213 ]
214 ],
215 'feedwatchlist' => [
216 'class' => ApiFeedWatchlist::class,
217 'services' => [
218 'ParserFactory',
219 ]
220 ],
221 'help' => [
222 'class' => ApiHelp::class,
223 'services' => [
224 'SkinFactory',
225 ]
226 ],
227 'paraminfo' => [
228 'class' => ApiParamInfo::class,
229 'services' => [
230 'UserFactory',
231 ],
232 ],
233 'rsd' => [
234 'class' => ApiRsd::class,
235 ],
236 'compare' => [
237 'class' => ApiComparePages::class,
238 'services' => [
239 'RevisionStore',
240 'ArchivedRevisionLookup',
241 'SlotRoleRegistry',
242 'ContentHandlerFactory',
243 'ContentTransformer',
244 'CommentFormatter',
245 'TempUserCreator',
246 'UserFactory',
247 ]
248 ],
249 'checktoken' => [
250 'class' => ApiCheckToken::class,
251 ],
252 'cspreport' => [
253 'class' => ApiCSPReport::class,
254 'services' => [
255 'UrlUtils',
256 ]
257 ],
258 'validatepassword' => [
259 'class' => ApiValidatePassword::class,
260 'services' => [
261 'AuthManager',
262 'UserFactory',
263 ]
264 ],
265
266 // Write modules
267 'purge' => [
268 'class' => ApiPurge::class,
269 'services' => [
270 'WikiPageFactory',
271 'TitleFormatter',
272 ],
273 ],
274 'setnotificationtimestamp' => [
275 'class' => ApiSetNotificationTimestamp::class,
276 'services' => [
277 'DBLoadBalancerFactory',
278 'RevisionStore',
279 'WatchedItemStore',
280 'TitleFormatter',
281 'TitleFactory',
282 ]
283 ],
284 'rollback' => [
285 'class' => ApiRollback::class,
286 'services' => [
287 'RollbackPageFactory',
288 'WatchlistManager',
289 'WatchedItemStore',
290 'UserOptionsLookup',
291 ]
292 ],
293 'delete' => [
294 'class' => ApiDelete::class,
295 'services' => [
296 'RepoGroup',
297 'WatchlistManager',
298 'WatchedItemStore',
299 'UserOptionsLookup',
300 'DeletePageFactory',
301 ]
302 ],
303 'undelete' => [
304 'class' => ApiUndelete::class,
305 'services' => [
306 'WatchlistManager',
307 'WatchedItemStore',
308 'UserOptionsLookup',
309 'UndeletePageFactory',
310 'WikiPageFactory',
311 ]
312 ],
313 'protect' => [
314 'class' => ApiProtect::class,
315 'services' => [
316 'WatchlistManager',
317 'WatchedItemStore',
318 'UserOptionsLookup',
319 'RestrictionStore',
320 ]
321 ],
322 'block' => [
323 'class' => ApiBlock::class,
324 'services' => [
325 'BlockPermissionCheckerFactory',
326 'BlockUserFactory',
327 'UserIdentityLookup',
328 'WatchedItemStore',
329 'BlockTargetFactory',
330 'BlockActionInfo',
331 'DatabaseBlockStore',
332 'WatchlistManager',
333 'UserOptionsLookup',
334 ]
335 ],
336 'unblock' => [
337 'class' => ApiUnblock::class,
338 'services' => [
339 'BlockPermissionCheckerFactory',
340 'UnblockUserFactory',
341 'UserIdentityLookup',
342 'WatchedItemStore',
343 'WatchlistManager',
344 'UserOptionsLookup',
345 'DatabaseBlockStore',
346 'BlockTargetFactory',
347 ]
348 ],
349 'move' => [
350 'class' => ApiMove::class,
351 'services' => [
352 'MovePageFactory',
353 'RepoGroup',
354 'WatchlistManager',
355 'WatchedItemStore',
356 'UserOptionsLookup',
357 ]
358 ],
359 'edit' => [
360 'class' => ApiEditPage::class,
361 'services' => [
362 'ContentHandlerFactory',
363 'RevisionLookup',
364 'WatchedItemStore',
365 'WikiPageFactory',
366 'WatchlistManager',
367 'UserOptionsLookup',
368 'RedirectLookup',
369 'TempUserCreator',
370 'UserFactory',
371 ]
372 ],
373 'upload' => [
374 'class' => ApiUpload::class,
375 'services' => [
376 'JobQueueGroup',
377 'WatchlistManager',
378 'WatchedItemStore',
379 'UserOptionsLookup',
380 'RepoGroup',
381 ]
382 ],
383 'filerevert' => [
384 'class' => ApiFileRevert::class,
385 'services' => [
386 'RepoGroup',
387 ]
388 ],
389 'emailuser' => [
390 'class' => ApiEmailUser::class,
391 'services' => [
392 'EmailUserFactory',
393 'UserFactory',
394 ]
395 ],
396 'watch' => [
397 'class' => ApiWatch::class,
398 'services' => [
399 'WatchlistManager',
400 'TitleFormatter',
401 'WatchlistLabelStore',
402 'WatchedItemStore',
403 'NamespaceInfo',
404 ]
405 ],
406 'patrol' => [
407 'class' => ApiPatrol::class,
408 'services' => [
409 'RevisionStore',
410 'PatrolManager',
411 'RecentChangeLookup',
412 ]
413 ],
414 'import' => [
415 'class' => ApiImport::class,
416 'services' => [
417 'WikiImporterFactory',
418 ]
419 ],
420 'clearhasmsg' => [
421 'class' => ApiClearHasMsg::class,
422 'services' => [
423 'TalkPageNotificationManager',
424 ]
425 ],
426 'userrights' => [
427 'class' => ApiUserrights::class,
428 'services' => [
429 'UserGroupManager',
430 'WatchedItemStore',
431 'WatchlistManager',
432 'UserOptionsLookup',
433 'UserGroupAssignmentService',
434 'MultiFormatUserIdentityLookup',
435 ]
436 ],
437 'options' => [
438 'class' => ApiOptions::class,
439 'services' => [
440 'UserOptionsManager',
441 'PreferencesFactory',
442 ],
443 ],
444 'imagerotate' => [
445 'class' => ApiImageRotate::class,
446 'services' => [
447 'RepoGroup',
448 'TempFSFileFactory',
449 'TitleFactory',
450 ]
451 ],
452 'revisiondelete' => [
453 'class' => ApiRevisionDelete::class,
454 ],
455 'managetags' => [
456 'class' => ApiManageTags::class,
457 ],
458 'tag' => [
459 'class' => ApiTag::class,
460 'services' => [
461 'DBLoadBalancerFactory',
462 'RevisionStore',
463 'ChangeTagsStore',
464 'RecentChangeLookup',
465 ]
466 ],
467 'mergehistory' => [
468 'class' => ApiMergeHistory::class,
469 'services' => [
470 'MergeHistoryFactory',
471 ],
472 ],
473 'setpagelanguage' => [
474 'class' => ApiSetPageLanguage::class,
475 'services' => [
476 'DBLoadBalancerFactory',
477 'LanguageNameUtils',
478 ]
479 ],
480 'changecontentmodel' => [
481 'class' => ApiChangeContentModel::class,
482 'services' => [
483 'ContentHandlerFactory',
484 'ContentModelChangeFactory',
485 ]
486 ],
487 'acquiretempusername' => [
488 'class' => ApiAcquireTempUserName::class,
489 'services' => [
490 'TempUserCreator',
491 ]
492 ],
493 'languagesearch' => [
494 'class' => ApiLanguageSearch::class,
495 'services' => [
496 'LanguageNameSearch',
497 ],
498 ],
499 ];
500
504 private const FORMATS = [
505 'json' => [
506 'class' => ApiFormatJson::class,
507 ],
508 'jsonfm' => [
509 'class' => ApiFormatJson::class,
510 ],
511 'php' => [
512 'class' => ApiFormatPhp::class,
513 ],
514 'phpfm' => [
515 'class' => ApiFormatPhp::class,
516 ],
517 'xml' => [
518 'class' => ApiFormatXml::class,
519 ],
520 'xmlfm' => [
521 'class' => ApiFormatXml::class,
522 ],
523 'rawfm' => [
524 'class' => ApiFormatJson::class,
525 ],
526 'none' => [
527 'class' => ApiFormatNone::class,
528 ],
529 ];
530
537 private const RIGHTS_MAP = [
538 'apihighlimits' => [
539 'msg' => 'api-help-right-apihighlimits',
541 ]
542 ];
543
545 private $mPrinter;
546
548 private $mModuleMgr;
549
551 private $mResult;
552
554 private $mErrorFormatter;
555
557 private $mParamValidator;
558
560 private $mContinuationManager;
561
563 private $mAction;
564
566 private $mEnableWrite;
567
569 private $mInternalMode;
570
572 private $mModule;
573
575 private $mCacheMode = 'private';
576
578 private $mCacheControl = [];
579
581 private $mParamsUsed = [];
582
584 private $mParamsSensitive = [];
585
587 private $lacksSameOriginSecurity = null;
588
590 private $statsFactory;
591
603 public function __construct( $context = null, $enableWrite = false, $internal = null ) {
604 if ( $context === null ) {
605 $context = RequestContext::getMain();
606 } elseif ( $context instanceof WebRequest ) {
607 // BC for pre-1.19
608 $request = $context;
609 $context = RequestContext::getMain();
610 }
611 // We set a derivative context so we can change stuff later
612 $derivativeContext = new DerivativeContext( $context );
613 $this->setContext( $derivativeContext );
614
615 if ( isset( $request ) ) {
616 $derivativeContext->setRequest( $request );
617 } else {
618 $request = $this->getRequest();
619 }
620
621 $this->mInternalMode = $internal ?? ( $request instanceof FauxRequest );
622
623 // Special handling for the main module: $parent === $this
624 parent::__construct( $this, $this->mInternalMode ? 'main_int' : 'main' );
625
626 $config = $this->getConfig();
627 // TODO inject stuff, see T265644
628 $services = MediaWikiServices::getInstance();
629
630 if ( !$this->mInternalMode ) {
631 // If we're in a mode that breaks the same-origin policy, strip
632 // user credentials for security.
633 if ( $this->lacksSameOriginSecurity() ) {
634 wfDebug( "API: stripping user credentials when the same-origin policy is not applied" );
635 $user = $services->getUserFactory()->newAnonymous();
637 $derivativeContext->setUser( $user );
638 $request->response()->header( 'MediaWiki-Login-Suppressed: true' );
639 }
640 }
641
642 $this->mParamValidator = new ApiParamValidator(
643 $this,
644 $services->getObjectFactory()
645 );
646
647 $this->statsFactory = $services->getStatsFactory();
648
649 $this->mResult =
651
652 // Setup uselang. This doesn't use $this->getParameter()
653 // because we're not ready to handle errors yet.
654 // Optimisation: Avoid slow getVal(), this isn't user-generated content.
655 $uselang = $request->getRawVal( 'uselang' ) ?? self::API_DEFAULT_USELANG;
656 if ( $uselang === 'user' ) {
657 // Assume the parent context is going to return the user language
658 // for uselang=user (see T85635).
659 } else {
660 if ( $uselang === 'content' ) {
661 $uselang = $services->getContentLanguageCode()->toString();
662 }
663 $code = RequestContext::sanitizeLangCode( $uselang );
664 $derivativeContext->setLanguage( $code );
665 if ( !$this->mInternalMode ) {
666 // phpcs:disable MediaWiki.Usage.ExtendClassUsage.FunctionVarUsage
667 global $wgLang;
668 $wgLang = $derivativeContext->getLanguage();
669 RequestContext::getMain()->setLanguage( $wgLang );
670 // phpcs:enable
671 }
672 }
673
674 // Set up the error formatter. This doesn't use $this->getParameter()
675 // because we're not ready to handle errors yet.
676 // Optimisation: Avoid slow getVal(), this isn't user-generated content.
677 $errorFormat = $request->getRawVal( 'errorformat' ) ?? 'bc';
678 $errorLangCode = $request->getRawVal( 'errorlang' ) ?? 'uselang';
679 $errorsUseDB = $request->getCheck( 'errorsuselocal' );
680 if ( in_array( $errorFormat, [ 'plaintext', 'wikitext', 'html', 'raw', 'none' ], true ) ) {
681 if ( $errorLangCode === 'uselang' ) {
682 $errorLang = $this->getLanguage();
683 } elseif ( $errorLangCode === 'content' ) {
684 $errorLang = $services->getContentLanguage();
685 } else {
686 $errorLangCode = RequestContext::sanitizeLangCode( $errorLangCode );
687 $errorLang = $services->getLanguageFactory()->getLanguage( $errorLangCode );
688 }
689 $this->mErrorFormatter = new ApiErrorFormatter(
690 $this->mResult,
691 $errorLang,
692 $errorFormat,
693 $errorsUseDB
694 );
695 } else {
696 $this->mErrorFormatter = new ApiErrorFormatter_BackCompat( $this->mResult );
697 }
698 $this->mResult->setErrorFormatter( $this->getErrorFormatter() );
699
700 $this->mModuleMgr = new ApiModuleManager(
701 $this,
702 $services->getObjectFactory()
703 );
704 $this->mModuleMgr->addModules( self::MODULES, 'action' );
705 $this->mModuleMgr->addModules( $config->get( MainConfigNames::APIModules ), 'action' );
706 $this->mModuleMgr->addModules( self::FORMATS, 'format' );
707 $this->mModuleMgr->addModules( $config->get( MainConfigNames::APIFormatModules ), 'format' );
708
709 $this->getHookRunner()->onApiMain__moduleManager( $this->mModuleMgr );
710
711 $this->mContinuationManager = null;
712 $this->mEnableWrite = $enableWrite;
713 }
714
719 public function isInternalMode() {
720 return $this->mInternalMode;
721 }
722
728 public function getResult() {
729 return $this->mResult;
730 }
731
736 public function lacksSameOriginSecurity() {
737 if ( $this->lacksSameOriginSecurity !== null ) {
738 return $this->lacksSameOriginSecurity;
739 }
740
741 $request = $this->getRequest();
742
743 // JSONP mode
744 if ( $request->getCheck( 'callback' ) ||
745 // Anonymous CORS
746 $request->getRawVal( 'origin' ) === '*' ||
747 // Header to be used from XMLHTTPRequest when the request might
748 // otherwise be used for XSS.
749 $request->getHeader( 'Treat-as-Untrusted' ) !== false ||
750 (
751 // Authenticated CORS with unsupported session provider (including preflight request)
752 $request->getCheck( 'crossorigin' ) &&
753 !$request->getSession()->getProvider()->safeAgainstCsrf()
754 )
755 ) {
756 $this->lacksSameOriginSecurity = true;
757 return true;
758 }
759
760 // Allow extensions to override.
761 $this->lacksSameOriginSecurity = !$this->getHookRunner()
762 ->onRequestHasSameOriginSecurity( $request );
763 return $this->lacksSameOriginSecurity;
764 }
765
770 public function getErrorFormatter() {
771 return $this->mErrorFormatter;
772 }
773
777 public function getContinuationManager() {
778 return $this->mContinuationManager;
779 }
780
784 public function setContinuationManager( ?ApiContinuationManager $manager = null ) {
785 if ( $manager !== null && $this->mContinuationManager !== null ) {
786 throw new UnexpectedValueException(
787 __METHOD__ . ': tried to set manager from ' . $manager->getSource() .
788 ' when a manager is already set from ' . $this->mContinuationManager->getSource()
789 );
790 }
791 $this->mContinuationManager = $manager;
792 }
793
795 return $this->mParamValidator;
796 }
797
803 public function getModule() {
804 return $this->mModule;
805 }
806
812 public function getStatsFactory() {
813 return $this->getMain()->statsFactory;
814 }
815
821 public function getPrinter() {
822 return $this->mPrinter;
823 }
824
830 public function setCacheMaxAge( $maxage ) {
831 $this->setCacheControl( [
832 'max-age' => $maxage,
833 's-maxage' => $maxage
834 ] );
835 }
836
862 public function setCacheMode( $mode ) {
863 if ( !in_array( $mode, [ 'private', 'public', 'anon-public-user-private' ] ) ) {
864 wfDebug( __METHOD__ . ": unrecognised cache mode \"$mode\"" );
865
866 // Ignore for forwards-compatibility
867 return;
868 }
869
870 if ( !$this->getPermissionManager()->isEveryoneAllowed( 'read' ) ) {
871 // Private wiki, only private headers
872 if ( $mode !== 'private' ) {
873 wfDebug( __METHOD__ . ": ignoring request for $mode cache mode, private wiki" );
874
875 return;
876 }
877 }
878
879 if ( $mode === 'public' && $this->getParameter( 'uselang' ) === 'user' ) {
880 // User language is used for i18n, so we don't want to publicly
881 // cache. Anons are ok, because if they have non-default language
882 // then there's an appropriate Vary header set by whatever set
883 // their non-default language.
884 wfDebug( __METHOD__ . ": downgrading cache mode 'public' to " .
885 "'anon-public-user-private' due to uselang=user" );
886 $mode = 'anon-public-user-private';
887 }
888
889 wfDebug( __METHOD__ . ": setting cache mode $mode" );
890 $this->mCacheMode = $mode;
891 }
892
894 public function getCacheMode() {
895 return $this->mCacheMode;
896 }
897
908 public function setCacheControl( $directives ) {
909 $this->mCacheControl = $directives + $this->mCacheControl;
910 }
911
919 public function createPrinterByName( $format ) {
920 $printer = $this->mModuleMgr->getModule( $format, 'format', /* $ignoreCache */ true );
921 if ( $printer === null ) {
922 $this->dieWithError(
923 [ 'apierror-unknownformat', wfEscapeWikiText( $format ) ], 'unknown_format'
924 );
925 }
926
927 // @phan-suppress-next-line PhanTypeMismatchReturnSuperType
928 return $printer;
929 }
930
934 public function execute() {
935 if ( $this->mInternalMode ) {
936 $this->executeAction();
937 } else {
938 $this->executeActionWithErrorHandling();
939 }
940 }
941
946 protected function executeActionWithErrorHandling() {
947 // Verify the CORS header before executing the action
948 if ( !$this->handleCORS() ) {
949 // handleCORS() has sent a 403, abort
950 return;
951 }
952
953 // Exit here if the request method was OPTIONS
954 // (assume there will be a followup GET or POST)
955 if ( $this->getRequest()->getMethod() === 'OPTIONS' ) {
956 return;
957 }
958
959 // In case an error occurs during data output,
960 // clear the output buffer and print just the error information
961 $obLevel = ob_get_level();
962 ob_start();
963
964 $t = microtime( true );
965 $isError = false;
966 try {
967 $this->executeAction();
968 $runTime = microtime( true ) - $t;
969 $this->logRequest( $runTime );
970
971 $this->statsFactory->getTiming( 'api_executeTiming_seconds' )
972 ->setLabel( 'module', $this->mModule->getModuleName() )
973 ->observe( 1000 * $runTime );
974
975 if ( !$this->mModule || $this->mModule->getModuleName() !== 'query' ) {
976 // Skip query module metrics; we will record them in the query module itself.
977 $this->recordUnifiedMetrics( $runTime );
978 }
979 } catch ( Throwable $e ) {
980 // If executeAction threw before the time was set, reset it
981 $runTime ??= microtime( true ) - $t;
982 $this->handleException( $e, $runTime );
983 $this->logRequest( microtime( true ) - $t, $e );
984 $isError = true;
985 }
986
987 // Disable the client cache on the output so that BlockManager::trackBlockWithCookie is executed
988 // as part of MediaWiki::preOutputCommit().
989 if (
990 $this->mCacheMode === 'private'
991 || (
992 $this->mCacheMode === 'anon-public-user-private'
993 && $this->getRequest()->getSession()->isPersistent()
994 )
995 ) {
996 $this->getContext()->getOutput()->disableClientCache();
997 $this->getContext()->getOutput()->considerCacheSettingsFinal();
998 }
999
1000 // Commit DBs and send any related cookies and headers
1001 MediaWiki::preOutputCommit( $this->getContext() );
1002
1003 // Send cache headers after any code which might generate an error, to
1004 // avoid sending public cache headers for errors.
1005 $this->sendCacheHeaders( $isError );
1006
1007 // Executing the action might have already messed with the output
1008 // buffers.
1009 while ( ob_get_level() > $obLevel ) {
1010 ob_end_flush();
1011 }
1012 }
1013
1021 protected function handleException( Throwable $e, $latency = 0 ) {
1022 $statsModuleName = $this->mModule ? $this->mModule->getModuleName() : 'main';
1023
1024 // Collect stats on errors (T396613).
1025 // NOTE: We only count fatal errors, a mere call to addError() or
1026 // addWarning() does not count towards these states. That could
1027 // be added in the future, but should use a different stats key.
1028 $stats = $this->statsFactory->getCounter( 'api_errors' )
1029 ->setLabel( 'module', $statsModuleName );
1030
1031 // T65145: Rollback any open database transactions
1032 if ( !$e instanceof ApiUsageException ) {
1033 // ApiUsageExceptions are intentional, so don't rollback if that's the case
1034 MWExceptionHandler::rollbackPrimaryChangesAndLog(
1035 $e,
1036 MWExceptionHandler::CAUGHT_BY_ENTRYPOINT
1037 );
1038 $stats->setLabel( 'exception_cause', 'server-error' );
1039 } else {
1040 $stats->setLabel( 'exception_cause', 'client-error' );
1041 }
1042
1043 // Allow extra cleanup and logging
1044 $this->getHookRunner()->onApiMain__onException( $this, $e );
1045
1046 // Handle any kind of exception by outputting properly formatted error message.
1047 // If this fails, an unhandled exception should be thrown so that global error
1048 // handler will process and log it.
1049
1050 $errCodes = $this->substituteResultWithError( $e );
1051 sort( $errCodes );
1052
1053 // Error results should not be cached
1054 $this->setCacheMode( 'private' );
1055
1056 $response = $this->getRequest()->response();
1057 $headerStr = 'MediaWiki-API-Error: ' . implode( ', ', $errCodes );
1058 $response->header( $headerStr );
1059
1060 // Reset and print just the error message
1061 ob_clean();
1062
1063 // Printer may not be initialized if the extractRequestParams() fails for the main module
1064 $this->createErrorPrinter();
1065
1066 $stats->setLabel( 'error_code', implode( '_', $errCodes ) );
1067 $stats->increment();
1068
1069 // Unified metrics
1070 if ( !$this->mModule || $this->mModule->getModuleName() !== 'query' ) {
1071 // Skip query module metrics; we will record them in the query module itself.
1072 $this->recordUnifiedMetrics(
1073 $latency,
1074 [
1075 'status' => implode( '_', $errCodes ), // Failure codes
1076 ]
1077 );
1078
1079 }
1080
1081 // Get desired HTTP code from an ApiUsageException. Don't use codes from other
1082 // exception types, as they are unlikely to be intended as an HTTP code.
1083 $httpCode = $e instanceof ApiUsageException ? $e->getCode() : 0;
1084
1085 $failed = false;
1086 try {
1087 $this->printResult( $httpCode );
1088 } catch ( ApiUsageException $ex ) {
1089 // The error printer itself is failing. Try suppressing its request
1090 // parameters and redo.
1091 $failed = true;
1092 $this->addWarning( 'apiwarn-errorprinterfailed' );
1093 foreach ( $ex->getStatusValue()->getMessages() as $error ) {
1094 try {
1095 $this->mPrinter->addWarning( $error );
1096 } catch ( Throwable ) {
1097 // WTF?
1098 $this->addWarning( $error );
1099 }
1100 }
1101 }
1102 if ( $failed ) {
1103 $this->mPrinter = null;
1104 $this->createErrorPrinter();
1105 // @phan-suppress-next-line PhanNonClassMethodCall False positive
1106 $this->mPrinter->forceDefaultParams();
1107 if ( $httpCode ) {
1108 $response->statusHeader( 200 ); // Reset in case the fallback doesn't want a non-200
1109 }
1110 $this->printResult( $httpCode );
1111 }
1112 }
1113
1124 public static function handleApiBeforeMainException( Throwable $e ) {
1125 ob_start();
1126
1127 try {
1128 $main = new self( RequestContext::getMain(), false );
1129 $main->handleException( $e );
1130 $main->logRequest( 0, $e );
1131 } catch ( Throwable ) {
1132 // Nope, even that didn't work. Punt.
1133 throw $e;
1134 }
1135
1136 // Reset cache headers
1137 $main->sendCacheHeaders( true );
1138
1139 ob_end_flush();
1140 }
1141
1162 protected function handleCORS() {
1163 $originParam = $this->getParameter( 'origin' ); // defaults to null
1164 $crossOriginParam = $this->getParameter( 'crossorigin' ); // defaults to false
1165 if ( $originParam === null && !$crossOriginParam ) {
1166 // No origin/crossorigin parameter, nothing to do
1167 return true;
1168 }
1169
1170 $request = $this->getRequest();
1171 $response = $request->response();
1172 $requestedMethod = $request->getHeader( 'Access-Control-Request-Method' );
1173 $preflight = $request->getMethod() === 'OPTIONS' && $requestedMethod !== false;
1174
1175 $allowTiming = false;
1176 $varyOrigin = true;
1177
1178 if ( $originParam !== null && $crossOriginParam ) {
1179 $response->statusHeader( 403 );
1180 $response->header( 'Cache-control: no-cache' );
1181 echo "'origin' and 'crossorigin' parameters cannot be used together\n";
1182
1183 return false;
1184 }
1185 if ( $crossOriginParam && !$request->getSession()->getProvider()->safeAgainstCsrf() && !$preflight ) {
1186 $response->statusHeader( 403 );
1187 $response->header( 'Cache-control: no-cache' );
1188 $language = MediaWikiServices::getInstance()->getLanguageFactory()->getLanguage( 'en' );
1189 $described = $request->getSession()->getProvider()->describe( $language );
1190 echo "'crossorigin' cannot be used with $described\n";
1191
1192 return false;
1193 }
1194
1195 if ( $originParam === '*' || $crossOriginParam ) {
1196 // Request for CORS without browser-supplied credentials (e.g. cookies):
1197 // may be anonymous (origin=*) or authenticated with request-supplied
1198 // credentials (crossorigin=1 + Authorization header).
1199 // Technically we should check for the presence of an Origin header
1200 // and not process it as CORS if it's not set, but that would
1201 // require us to vary on Origin for all 'origin=*' requests which
1202 // we don't want to do.
1203 $matchedOrigin = true;
1204 $allowOrigin = '*';
1205 $allowCredentials = 'false';
1206 $varyOrigin = false; // No need to vary
1207 } else {
1208 // Non-anonymous CORS, check we allow the domain
1209
1210 // Origin: header is a space-separated list of origins, check all of them
1211 $originHeader = $request->getHeader( 'Origin' );
1212 if ( $originHeader === false ) {
1213 $origins = [];
1214 } else {
1215 $originHeader = trim( $originHeader );
1216 $origins = preg_split( '/\s+/', $originHeader );
1217 }
1218
1219 if ( !in_array( $originParam, $origins ) ) {
1220 // origin parameter set but incorrect
1221 // Send a 403 response
1222 $response->statusHeader( 403 );
1223 $response->header( 'Cache-Control: no-cache' );
1224 echo "'origin' parameter does not match Origin header\n";
1225
1226 return false;
1227 }
1228
1229 $config = $this->getConfig();
1230 $origin = Origin::parseHeaderList( $origins );
1231 $matchedOrigin = $origin->match(
1234 );
1235
1236 $allowOrigin = $originHeader;
1237 $allowCredentials = 'true';
1238 $allowTiming = $originHeader;
1239 }
1240
1241 if ( $matchedOrigin ) {
1242 if ( $preflight ) {
1243 // We allow the actual request to send the following headers
1244 $requestedHeaders = $request->getHeader( 'Access-Control-Request-Headers' );
1245 $allowedHeaders = $this->getConfig()->get( MainConfigNames::AllowedCorsHeaders );
1246 if ( $requestedHeaders !== false ) {
1247 if ( !self::matchRequestedHeaders( $requestedHeaders, $allowedHeaders ) ) {
1248 $response->header( 'MediaWiki-CORS-Rejection: Unsupported header requested in preflight' );
1249 return true;
1250 }
1251 $response->header( 'Access-Control-Allow-Headers: ' . $requestedHeaders );
1252 }
1253
1254 // We only allow the actual request to be GET, POST, or HEAD
1255 $response->header( 'Access-Control-Allow-Methods: POST, GET, HEAD' );
1256 }
1257
1258 $response->header( "Access-Control-Allow-Origin: $allowOrigin" );
1259 $response->header( "Access-Control-Allow-Credentials: $allowCredentials" );
1260 // https://www.w3.org/TR/resource-timing/#timing-allow-origin
1261 if ( $allowTiming !== false ) {
1262 $response->header( "Timing-Allow-Origin: $allowTiming" );
1263 }
1264
1265 if ( !$preflight ) {
1266 $response->header(
1267 'Access-Control-Expose-Headers: MediaWiki-API-Error, Retry-After, X-Database-Lag, '
1268 . 'MediaWiki-Login-Suppressed'
1269 );
1270 }
1271 } else {
1272 $response->header( 'MediaWiki-CORS-Rejection: Origin mismatch' );
1273 }
1274
1275 if ( $varyOrigin ) {
1276 $this->getOutput()->addVaryHeader( 'Origin' );
1277 }
1278
1279 return true;
1280 }
1281
1290 protected static function matchRequestedHeaders( $requestedHeaders, $allowedHeaders ) {
1291 if ( trim( $requestedHeaders ) === '' ) {
1292 return true;
1293 }
1294 $requestedHeaders = explode( ',', $requestedHeaders );
1295 $allowedHeaders = array_change_key_case(
1296 array_fill_keys( $allowedHeaders, true ), CASE_LOWER );
1297 foreach ( $requestedHeaders as $rHeader ) {
1298 $rHeader = strtolower( trim( $rHeader ) );
1299 if ( !isset( $allowedHeaders[$rHeader] ) ) {
1300 LoggerFactory::getInstance( 'api-warning' )->warning(
1301 'CORS preflight failed on requested header: {header}', [
1302 'header' => $rHeader
1303 ]
1304 );
1305 return false;
1306 }
1307 }
1308 return true;
1309 }
1310
1316 protected function sendCacheHeaders( $isError ) {
1317 $response = $this->getRequest()->response();
1318 $out = $this->getOutput();
1319
1320 $out->addVaryHeader( 'Treat-as-Untrusted' );
1321
1322 $config = $this->getConfig();
1323
1324 if ( $config->get( MainConfigNames::VaryOnXFP ) ) {
1325 $out->addVaryHeader( 'X-Forwarded-Proto' );
1326 }
1327
1328 if ( !$isError && $this->mModule &&
1329 ( $this->getRequest()->getMethod() === 'GET' || $this->getRequest()->getMethod() === 'HEAD' )
1330 ) {
1331 $etag = $this->mModule->getConditionalRequestData( 'etag' );
1332 if ( $etag !== null ) {
1333 $response->header( "ETag: $etag" );
1334 }
1335 $lastMod = $this->mModule->getConditionalRequestData( 'last-modified' );
1336 if ( $lastMod !== null ) {
1337 $response->header( 'Last-Modified: ' . wfTimestamp( TS::RFC2822, $lastMod ) );
1338 }
1339 }
1340
1341 // The logic should be:
1342 // $this->mCacheControl['max-age'] is set?
1343 // Use it, the module knows better than our guess.
1344 // !$this->mModule || $this->mModule->isWriteMode(), and mCacheMode is private?
1345 // Use 0 because we can guess caching is probably the wrong thing to do.
1346 // Use $this->getParameter( 'maxage' ), which already defaults to 0.
1347 $maxage = 0;
1348 if ( isset( $this->mCacheControl['max-age'] ) ) {
1349 $maxage = $this->mCacheControl['max-age'];
1350 } elseif ( ( !$isError && $this->mModule && !$this->mModule->isWriteMode() ) ||
1351 $this->mCacheMode !== 'private'
1352 ) {
1353 $maxage = $this->getParameter( 'maxage' );
1354 }
1355 $privateCache = 'private, must-revalidate, max-age=' . $maxage;
1356
1357 if ( $this->mCacheMode == 'private' ) {
1358 $response->header( "Cache-Control: $privateCache" );
1359 return;
1360 }
1361
1362 if ( $this->mCacheMode == 'anon-public-user-private' ) {
1363 $out->addVaryHeader( 'Cookie' );
1364 $response->header( $out->getVaryHeader() );
1365 if ( $this->getRequest()->getSession()->isPersistent() ) {
1366 // Logged in or otherwise has session (e.g. anonymous users who have edited)
1367 // Mark request private
1368 $response->header( "Cache-Control: $privateCache" );
1369
1370 return;
1371 } // else anonymous, send public headers below
1372 }
1373
1374 // Send public headers
1375 $response->header( $out->getVaryHeader() );
1376
1377 // If nobody called setCacheMaxAge(), use the (s)maxage parameters
1378 if ( !isset( $this->mCacheControl['s-maxage'] ) ) {
1379 $this->mCacheControl['s-maxage'] = $this->getParameter( 'smaxage' );
1380 }
1381 if ( !isset( $this->mCacheControl['max-age'] ) ) {
1382 $this->mCacheControl['max-age'] = $this->getParameter( 'maxage' );
1383 }
1384
1385 if ( !$this->mCacheControl['s-maxage'] && !$this->mCacheControl['max-age'] ) {
1386 // Public cache not requested
1387 // Sending a Vary header in this case is harmless, and protects us
1388 // against conditional calls of setCacheMaxAge().
1389 $response->header( "Cache-Control: $privateCache" );
1390
1391 return;
1392 }
1393
1394 $this->mCacheControl['public'] = true;
1395
1396 // Send an Expires header
1397 $maxAge = min( $this->mCacheControl['s-maxage'], $this->mCacheControl['max-age'] );
1398 $expiryUnixTime = ( $maxAge == 0 ? 1 : time() + $maxAge );
1399 $response->header( 'Expires: ' . wfTimestamp( TS::RFC2822, $expiryUnixTime ) );
1400
1401 // Construct the Cache-Control header
1402 $ccHeader = '';
1403 $separator = '';
1404 foreach ( $this->mCacheControl as $name => $value ) {
1405 if ( is_bool( $value ) ) {
1406 if ( $value ) {
1407 $ccHeader .= $separator . $name;
1408 $separator = ', ';
1409 }
1410 } else {
1411 $ccHeader .= $separator . "$name=$value";
1412 $separator = ', ';
1413 }
1414 }
1415
1416 $response->header( "Cache-Control: $ccHeader" );
1417 }
1418
1422 private function createErrorPrinter() {
1423 if ( !$this->mPrinter ) {
1424 $value = $this->getRequest()->getVal( 'format', self::API_DEFAULT_FORMAT );
1425 if ( !$this->mModuleMgr->isDefined( $value, 'format' ) ) {
1426 $value = self::API_DEFAULT_FORMAT;
1427 }
1428 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable getVal does not return null here
1429 $this->mPrinter = $this->createPrinterByName( $value );
1430 }
1431
1432 // Printer may not be able to handle errors. This is particularly
1433 // likely if the module returns something for getCustomPrinter().
1434 if ( !$this->mPrinter->canPrintErrors() ) {
1435 $this->mPrinter = $this->createPrinterByName( self::API_DEFAULT_FORMAT );
1436 }
1437 }
1438
1454 protected function errorMessagesFromException( Throwable $e, $type = 'error' ) {
1455 $messages = [];
1456 if ( $e instanceof ApiUsageException ) {
1457 foreach ( $e->getStatusValue()->getMessages( $type ) as $msg ) {
1458 $messages[] = ApiMessage::create( $msg );
1459 }
1460 } elseif ( $type !== 'error' ) {
1461 // None of the rest have any messages for non-error types
1462 } else {
1463 // TODO: Avoid embedding arbitrary class names in the error code.
1464 $class = preg_replace( '#^Wikimedia\\\\Rdbms\\\\#', '', get_class( $e ) );
1465 $code = 'internal_api_error_' . $class;
1466 $data = [ 'errorclass' => get_class( $e ) ];
1467 if ( MWExceptionRenderer::shouldShowExceptionDetails() ) {
1468 if ( $e instanceof ILocalizedException ) {
1469 $msg = $e->getMessageObject();
1470 } elseif ( $e instanceof MessageSpecifier ) {
1471 $msg = Message::newFromSpecifier( $e );
1472 } else {
1473 $msg = wfEscapeWikiText( $e->getMessage() );
1474 }
1475 $params = [ 'apierror-exceptioncaught', WebRequest::getRequestId(), $msg ];
1476 } else {
1477 $params = [ 'apierror-exceptioncaughttype', WebRequest::getRequestId(), get_class( $e ) ];
1478 }
1479
1480 $messages[] = ApiMessage::create( $params, $code, $data );
1481 }
1482 return $messages;
1483 }
1484
1490 protected function substituteResultWithError( Throwable $e ) {
1491 $result = $this->getResult();
1492 $formatter = $this->getErrorFormatter();
1493 $config = $this->getConfig();
1494 $errorCodes = [];
1495
1496 // Remember existing warnings and errors across the reset
1497 $errors = $result->getResultData( [ 'errors' ] );
1498 $warnings = $result->getResultData( [ 'warnings' ] );
1499 $result->reset();
1500 if ( $warnings !== null ) {
1501 $result->addValue( null, 'warnings', $warnings, ApiResult::NO_SIZE_CHECK );
1502 }
1503 if ( $errors !== null ) {
1504 $result->addValue( null, 'errors', $errors, ApiResult::NO_SIZE_CHECK );
1505
1506 // Collect the copied error codes for the return value
1507 foreach ( $errors as $error ) {
1508 if ( isset( $error['code'] ) ) {
1509 $errorCodes[$error['code']] = true;
1510 }
1511 }
1512 }
1513
1514 // Add errors from the exception
1515 $modulePath = $e instanceof ApiUsageException ? $e->getModulePath() : null;
1516 foreach ( $this->errorMessagesFromException( $e, 'error' ) as $msg ) {
1517 if ( ApiErrorFormatter::isValidApiCode( $msg->getApiCode() ) ) {
1518 $errorCodes[$msg->getApiCode()] = true;
1519 } else {
1520 LoggerFactory::getInstance( 'api-warning' )->error( 'Invalid API error code "{code}"', [
1521 'code' => $msg->getApiCode(),
1522 'exception' => $e,
1523 ] );
1524 $errorCodes['<invalid-code>'] = true;
1525 }
1526 $formatter->addError( $modulePath, $msg );
1527 }
1528 foreach ( $this->errorMessagesFromException( $e, 'warning' ) as $msg ) {
1529 $formatter->addWarning( $modulePath, $msg );
1530 }
1531
1532 // Add additional data. Path depends on whether we're in BC mode or not.
1533 // Data depends on the type of exception.
1534 if ( $formatter instanceof ApiErrorFormatter_BackCompat ) {
1535 $path = [ 'error' ];
1536 } else {
1537 $path = null;
1538 }
1539 if ( $e instanceof ApiUsageException ) {
1540 $link = (string)MediaWikiServices::getInstance()->getUrlUtils()->expand( wfScript( 'api' ) );
1541 $result->addContentValue(
1542 $path,
1543 'docref',
1544 trim(
1545 $this->msg( 'api-usage-docref', $link )->inLanguage( $formatter->getLanguage() )->text()
1546 . ' '
1547 . $this->msg( 'api-usage-mailinglist-ref' )->inLanguage( $formatter->getLanguage() )->text()
1548 )
1549 );
1550 } elseif ( $config->get( MainConfigNames::ShowExceptionDetails ) ) {
1551 $result->addContentValue(
1552 $path,
1553 'trace',
1554 $this->msg( 'api-exception-trace',
1555 get_class( $e ),
1556 $e->getFile(),
1557 $e->getLine(),
1558 MWExceptionHandler::getRedactedTraceAsString( $e )
1559 )->inLanguage( $formatter->getLanguage() )->text()
1560 );
1561 }
1562
1563 // Add the id and such
1564 $this->addRequestedFields( [ 'servedby' ] );
1565
1566 return array_keys( $errorCodes );
1567 }
1568
1574 protected function addRequestedFields( $force = [] ) {
1575 $result = $this->getResult();
1576
1577 $requestid = $this->getParameter( 'requestid' );
1578 if ( $requestid !== null ) {
1579 $result->addValue( null, 'requestid', $requestid, ApiResult::NO_SIZE_CHECK );
1580 }
1581
1582 if ( $this->getConfig()->get( MainConfigNames::ShowHostnames ) && (
1583 in_array( 'servedby', $force, true ) || $this->getParameter( 'servedby' )
1584 ) ) {
1585 $result->addValue( null, 'servedby', wfHostname(), ApiResult::NO_SIZE_CHECK );
1586 }
1587
1588 if ( $this->getParameter( 'curtimestamp' ) ) {
1589 $result->addValue( null, 'curtimestamp', wfTimestamp( TS::ISO_8601 ), ApiResult::NO_SIZE_CHECK );
1590 }
1591
1592 if ( $this->getParameter( 'responselanginfo' ) ) {
1593 $result->addValue(
1594 null,
1595 'uselang',
1596 $this->getLanguage()->getCode(),
1598 );
1599 $result->addValue(
1600 null,
1601 'errorlang',
1602 $this->getErrorFormatter()->getLanguage()->getCode(),
1604 );
1605 }
1606 }
1607
1612 protected function setupExecuteAction() {
1613 $this->addRequestedFields();
1614
1615 $params = $this->extractRequestParams();
1616 $this->mAction = $params['action'];
1617
1618 return $params;
1619 }
1620
1626 protected function setupModule() {
1627 // Instantiate the module requested by the user
1628 $module = $this->mModuleMgr->getModule( $this->mAction, 'action' );
1629 if ( $module === null ) {
1630 // Probably can't happen
1631 // @codeCoverageIgnoreStart
1632 $this->dieWithError(
1633 [ 'apierror-unknownaction', wfEscapeWikiText( $this->mAction ) ],
1634 'unknown_action'
1635 );
1636 // @codeCoverageIgnoreEnd
1637 }
1638 $moduleParams = $module->extractRequestParams();
1639
1640 // Check token, if necessary
1641 if ( $module->needsToken() === true ) {
1642 throw new LogicException(
1643 "Module '{$module->getModuleName()}' must be updated for the new token handling. " .
1644 'See documentation for ApiBase::needsToken for details.'
1645 );
1646 }
1647 if ( $module->needsToken() ) {
1648 if ( !$module->mustBePosted() ) {
1649 throw new LogicException(
1650 "Module '{$module->getModuleName()}' must require POST to use tokens."
1651 );
1652 }
1653
1654 if ( !isset( $moduleParams['token'] ) ) {
1655 // Probably can't happen
1656 // @codeCoverageIgnoreStart
1657 $module->dieWithError( [ 'apierror-missingparam', 'token' ] );
1658 // @codeCoverageIgnoreEnd
1659 }
1660
1661 $module->requirePostedParameters( [ 'token' ] );
1662
1663 if ( !$module->validateToken( $moduleParams['token'], $moduleParams ) ) {
1664 $module->dieWithError( 'apierror-badtoken' );
1665 }
1666 }
1667
1668 return $module;
1669 }
1670
1674 private function getMaxLag() {
1675 $services = MediaWikiServices::getInstance();
1676 $dbLag = $services->getDBLoadBalancer()->getMaxLag();
1677 $lagInfo = [
1678 'host' => $dbLag[0],
1679 'lag' => $dbLag[1],
1680 'type' => 'db'
1681 ];
1682
1683 $jobQueueLagFactor =
1684 $this->getConfig()->get( MainConfigNames::JobQueueIncludeInMaxLagFactor );
1685 if ( $jobQueueLagFactor ) {
1686 // Turn total number of jobs into seconds by using the configured value
1687 $totalJobs = array_sum( $services->getJobQueueGroup()->getQueueSizes() );
1688 $jobQueueLag = $totalJobs / (float)$jobQueueLagFactor;
1689 if ( $jobQueueLag > $lagInfo['lag'] ) {
1690 $lagInfo = [
1691 'host' => wfHostname(), // XXX: Is there a better value that could be used?
1692 'lag' => $jobQueueLag,
1693 'type' => 'jobqueue',
1694 'jobs' => $totalJobs,
1695 ];
1696 }
1697 }
1698
1699 $this->getHookRunner()->onApiMaxLagInfo( $lagInfo );
1700
1701 return $lagInfo;
1702 }
1703
1710 protected function checkMaxLag( $module, $params ) {
1711 if ( $module->shouldCheckMaxlag() && isset( $params['maxlag'] ) ) {
1712 $maxLag = $params['maxlag'];
1713 $lagInfo = $this->getMaxLag();
1714 if ( $lagInfo['lag'] > $maxLag ) {
1715 $response = $this->getRequest()->response();
1716
1717 $response->header( 'Retry-After: ' . max( (int)$maxLag, 5 ) );
1718 $response->header( 'X-Database-Lag: ' . (int)$lagInfo['lag'] );
1719
1720 if ( $this->getConfig()->get( MainConfigNames::ShowHostnames ) ) {
1721 $this->dieWithError(
1722 [ 'apierror-maxlag', $lagInfo['lag'], $lagInfo['host'] ],
1723 'maxlag',
1724 $lagInfo
1725 );
1726 }
1727
1728 $this->dieWithError( [ 'apierror-maxlag-generic', $lagInfo['lag'] ], 'maxlag', $lagInfo );
1729 }
1730 }
1731
1732 return true;
1733 }
1734
1756 protected function checkConditionalRequestHeaders( $module ) {
1757 if ( $this->mInternalMode ) {
1758 // No headers to check in internal mode
1759 return true;
1760 }
1761
1762 if ( $this->getRequest()->getMethod() !== 'GET' && $this->getRequest()->getMethod() !== 'HEAD' ) {
1763 // Don't check POSTs
1764 return true;
1765 }
1766
1767 $return304 = false;
1768
1769 $ifNoneMatch = array_diff(
1770 $this->getRequest()->getHeader( 'If-None-Match', WebRequest::GETHEADER_LIST ) ?: [],
1771 [ '' ]
1772 );
1773 if ( $ifNoneMatch ) {
1774 // @phan-suppress-next-line PhanImpossibleTypeComparison
1775 if ( $ifNoneMatch === [ '*' ] ) {
1776 // API responses always "exist"
1777 $etag = '*';
1778 } else {
1779 $etag = $module->getConditionalRequestData( 'etag' );
1780 }
1781 }
1782 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable $etag is declared when $ifNoneMatch is true
1783 if ( $ifNoneMatch && $etag !== null ) {
1784 $test = str_starts_with( $etag, 'W/' ) ? substr( $etag, 2 ) : $etag;
1785 $match = array_map( static function ( $s ) {
1786 return str_starts_with( $s, 'W/' ) ? substr( $s, 2 ) : $s;
1787 }, $ifNoneMatch );
1788 $return304 = in_array( $test, $match, true );
1789 } else {
1790 $value = trim( $this->getRequest()->getHeader( 'If-Modified-Since' ) );
1791
1792 // Some old browsers sends sizes after the date, like this:
1793 // Wed, 20 Aug 2003 06:51:19 GMT; length=5202
1794 // Ignore that.
1795 $i = strpos( $value, ';' );
1796 if ( $i !== false ) {
1797 $value = trim( substr( $value, 0, $i ) );
1798 }
1799
1800 if ( $value !== '' ) {
1801 try {
1802 $ts = new ConvertibleTimestamp( $value );
1803 if (
1804 // RFC 7231 IMF-fixdate
1805 $ts->getTimestamp( TS::RFC2822 ) === $value ||
1806 // RFC 850
1807 $ts->format( 'l, d-M-y H:i:s' ) . ' GMT' === $value ||
1808 // asctime (with and without space-padded day)
1809 $ts->format( 'D M j H:i:s Y' ) === $value ||
1810 $ts->format( 'D M j H:i:s Y' ) === $value
1811 ) {
1812 $config = $this->getConfig();
1813 $lastMod = $module->getConditionalRequestData( 'last-modified' );
1814 if ( $lastMod !== null ) {
1815 // Mix in some MediaWiki modification times
1816 $modifiedTimes = [
1817 'page' => $lastMod,
1818 'user' => $this->getUser()->getTouched(),
1819 'epoch' => $config->get( MainConfigNames::CacheEpoch ),
1820 ];
1821
1822 if ( $config->get( MainConfigNames::UseCdn ) ) {
1823 // T46570: the core page itself may not change, but resources might
1824 $modifiedTimes['sepoch'] = wfTimestamp(
1825 TS::MW, time() - $config->get( MainConfigNames::CdnMaxAge )
1826 );
1827 }
1828 $this->getHookRunner()->onOutputPageCheckLastModified( $modifiedTimes, $this->getOutput() );
1829 $lastMod = max( $modifiedTimes );
1830 $return304 = wfTimestamp( TS::MW, $lastMod ) <= $ts->getTimestamp( TS::MW );
1831 }
1832 }
1833 } catch ( TimestampException ) {
1834 // Invalid timestamp, ignore it
1835 }
1836 }
1837 }
1838
1839 if ( $return304 ) {
1840 $this->getRequest()->response()->statusHeader( 304 );
1841
1842 // Avoid outputting the compressed representation of a zero-length body
1843 // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
1844 @ini_set( 'zlib.output_compression', 0 );
1845 wfResetOutputBuffers( false );
1846
1847 return false;
1848 }
1849
1850 return true;
1851 }
1852
1857 protected function checkExecutePermissions( $module ) {
1858 $user = $this->getUser();
1859 if ( $module->isReadMode() && !$this->getPermissionManager()->isEveryoneAllowed( 'read' ) &&
1860 !$this->getAuthority()->isAllowed( 'read' )
1861 ) {
1862 $this->dieWithError( 'apierror-readapidenied' );
1863 }
1864
1865 if ( $module->isWriteMode() ) {
1866 if ( !$this->mEnableWrite ) {
1867 $this->dieWithError( 'apierror-noapiwrite' );
1868 } elseif ( $this->getRequest()->getHeader( 'Promise-Non-Write-API-Action' ) ) {
1869 $this->dieWithError( 'apierror-promised-nonwrite-api' );
1870 }
1871
1872 $this->checkReadOnly( $module );
1873 }
1874
1875 // Allow extensions to stop execution for arbitrary reasons.
1876 // TODO: change hook to accept Authority
1877 $message = 'hookaborted';
1878 if ( !$this->getHookRunner()->onApiCheckCanExecute( $module, $user, $message ) ) {
1879 $this->dieWithError( $message );
1880 }
1881 }
1882
1887 protected function checkReadOnly( $module ) {
1888 if ( MediaWikiServices::getInstance()->getReadOnlyMode()->isReadOnly() ) {
1889 $this->dieReadOnly();
1890 }
1891
1892 if ( $module->isWriteMode()
1893 && $this->getUser()->isBot()
1894 && MediaWikiServices::getInstance()->getDBLoadBalancer()->hasReplicaServers()
1895 ) {
1896 $this->checkBotReadOnly();
1897 }
1898 }
1899
1903 private function checkBotReadOnly() {
1904 // Figure out how many servers have passed the lag threshold
1905 $numLagged = 0;
1906 $lagLimit = $this->getConfig()->get( MainConfigNames::APIMaxLagThreshold );
1907 $laggedServers = [];
1908 $loadBalancer = MediaWikiServices::getInstance()->getDBLoadBalancer();
1909 foreach ( $loadBalancer->getLagTimes() as $serverIndex => $lag ) {
1910 if ( $lag > $lagLimit ) {
1911 ++$numLagged;
1912 $laggedServers[] = $loadBalancer->getServerName( $serverIndex ) . " ({$lag}s)";
1913 }
1914 }
1915
1916 // If a majority of replica DBs are too lagged then disallow writes
1917 $replicaCount = $loadBalancer->getServerCount() - 1;
1918 if ( $numLagged >= ceil( $replicaCount / 2 ) ) {
1919 $laggedServers = implode( ', ', $laggedServers );
1920 wfDebugLog(
1921 'api-readonly', // Deprecate this channel in favor of api-warning?
1922 "Api request failed as read only because the following DBs are lagged: $laggedServers"
1923 );
1924 LoggerFactory::getInstance( 'api-warning' )->warning(
1925 "Api request failed as read only because the following DBs are lagged: {laggeddbs}", [
1926 'laggeddbs' => $laggedServers,
1927 ]
1928 );
1929
1930 $this->dieWithError(
1931 'readonly_lag',
1932 'readonly',
1933 [ 'readonlyreason' => "Waiting for $numLagged lagged database(s)" ]
1934 );
1935 }
1936 }
1937
1942 protected function checkAsserts( $params ) {
1943 if ( isset( $params['assert'] ) ) {
1944 $user = $this->getUser();
1945 switch ( $params['assert'] ) {
1946 case 'anon':
1947 if ( $user->isRegistered() ) {
1948 $this->dieWithError( 'apierror-assertanonfailed' );
1949 }
1950 break;
1951 case 'user':
1952 if ( !$user->isRegistered() ) {
1953 $this->dieWithError( 'apierror-assertuserfailed' );
1954 }
1955 break;
1956 case 'bot':
1957 if ( !$this->getAuthority()->isAllowed( 'bot' ) ) {
1958 $this->dieWithError( 'apierror-assertbotfailed' );
1959 }
1960 break;
1961 }
1962 }
1963 if ( isset( $params['assertuser'] ) ) {
1964 // TODO inject stuff, see T265644
1965 $assertUser = MediaWikiServices::getInstance()->getUserFactory()
1966 ->newFromName( $params['assertuser'], UserRigorOptions::RIGOR_NONE );
1967 if ( !$assertUser || !$this->getUser()->equals( $assertUser ) ) {
1968 $this->dieWithError(
1969 [ 'apierror-assertnameduserfailed', wfEscapeWikiText( $params['assertuser'] ) ]
1970 );
1971 }
1972 }
1973 }
1974
1980 protected function setupExternalResponse( $module, $params ) {
1981 $validMethods = [ 'GET', 'HEAD', 'POST', 'OPTIONS' ];
1982 $request = $this->getRequest();
1983
1984 if ( !in_array( $request->getMethod(), $validMethods ) ) {
1985 $this->dieWithError( 'apierror-invalidmethod', null, null, 405 );
1986 }
1987
1988 if ( !$request->wasPosted() && $module->mustBePosted() ) {
1989 // Module requires POST. GET request might still be allowed
1990 // if $wgDebugApi is true, otherwise fail.
1991 $this->dieWithErrorOrDebug( [ 'apierror-mustbeposted', $this->mAction ] );
1992 }
1993
1994 if ( $request->wasPosted() ) {
1995 if ( !$request->getHeader( 'Content-Type' ) ) {
1996 $this->addDeprecation(
1997 'apiwarn-deprecation-post-without-content-type', 'post-without-content-type'
1998 );
1999 }
2000 $contentLength = $request->getHeader( 'Content-Length' );
2001 $maxPostSize = wfShorthandToInteger( ini_get( 'post_max_size' ), 0 );
2002 if ( $maxPostSize && $contentLength > $maxPostSize ) {
2003 $this->dieWithError(
2004 [ 'apierror-http-contenttoolarge', Message::sizeParam( $maxPostSize ) ],
2005 null, null, 413
2006 );
2007 }
2008 }
2009
2010 // See if custom printer is used
2011 $this->mPrinter = $module->getCustomPrinter() ??
2012 // Create an appropriate printer if not set
2013 $this->createPrinterByName( $params['format'] );
2014
2015 if ( $request->getProtocol() === 'http' &&
2016 (
2017 $this->getConfig()->get( MainConfigNames::ForceHTTPS ) ||
2018 $request->getSession()->shouldForceHTTPS() ||
2019 $this->getUser()->requiresHTTPS()
2020 )
2021 ) {
2022 $this->addDeprecation( 'apiwarn-deprecation-httpsexpected', 'https-expected' );
2023 }
2024 }
2025
2029 protected function executeAction() {
2030 $params = $this->setupExecuteAction();
2031
2032 // Check asserts early so e.g. errors in parsing a module's parameters due to being
2033 // logged out don't override the client's intended "am I logged in?" check.
2034 $this->checkAsserts( $params );
2035
2036 $module = $this->setupModule();
2037 $this->mModule = $module;
2038
2039 if ( !$this->mInternalMode ) {
2040 ProfilingContext::singleton()->init( MW_ENTRY_POINT, $module->getModuleName() );
2041 $this->setRequestExpectations( $module );
2042 }
2043
2044 $this->checkExecutePermissions( $module );
2045
2046 if ( !$this->checkMaxLag( $module, $params ) ) {
2047 return;
2048 }
2049
2050 if ( !$this->checkConditionalRequestHeaders( $module ) ) {
2051 return;
2052 }
2053
2054 if ( !$this->mInternalMode ) {
2055 $this->setupExternalResponse( $module, $params );
2056 }
2057
2058 $scope = LoggerFactory::getContext()->addScoped( [
2059 'context.api_module_name' => $module->getModuleName(),
2060 'context.api_client_useragent' => $this->getUserAgent(),
2061 ] );
2062 $module->execute();
2063 ScopedCallback::consume( $scope );
2064 $this->getHookRunner()->onAPIAfterExecute( $module );
2065
2066 $this->reportUnusedParams();
2067
2068 if ( !$this->mInternalMode ) {
2069 MWDebug::appendDebugInfoToApiResult( $this->getContext(), $this->getResult() );
2070
2071 $this->printResult();
2072 }
2073 }
2074
2078 protected function setRequestExpectations( ApiBase $module ) {
2079 $request = $this->getRequest();
2080
2081 $trxLimits = $this->getConfig()->get( MainConfigNames::TrxProfilerLimits );
2082 $trxProfiler = Profiler::instance()->getTransactionProfiler();
2083 $trxProfiler->setLogger( LoggerFactory::getInstance( 'rdbms' ) );
2084 $trxProfiler->setStatsFactory( MediaWikiServices::getInstance()->getStatsFactory() );
2085 $trxProfiler->setRequestMethod( $request->getMethod() );
2086 if ( $request->hasSafeMethod() ) {
2087 $trxProfiler->setExpectations( $trxLimits['GET'], __METHOD__ );
2088 } elseif ( $request->wasPosted() && !$module->isWriteMode() ) {
2089 $trxProfiler->setExpectations( $trxLimits['POST-nonwrite'], __METHOD__ );
2090 } else {
2091 $trxProfiler->setExpectations( $trxLimits['POST'], __METHOD__ );
2092 }
2093 }
2094
2100 protected function logRequest( $time, ?Throwable $e = null ) {
2101 $request = $this->getRequest();
2102
2103 $user = $this->getUser();
2104 $performer = [
2105 'user_text' => $user->getName(),
2106 ];
2107 if ( $user->isRegistered() ) {
2108 $performer['user_id'] = $user->getId();
2109 }
2110 $logCtx = [
2111 // https://gerrit.wikimedia.org/g/mediawiki/event-schemas/+/master/jsonschema/mediawiki/api/request
2112 '$schema' => '/mediawiki/api/request/1.0.0',
2113 'meta' => [
2114 'request_id' => WebRequest::getRequestId(),
2116 ->getGlobalIdGenerator()->newUUIDv4(),
2117 'domain' => $this->getConfig()->get( MainConfigNames::ServerName ),
2118 // If using the EventBus extension (as intended) with this log channel,
2119 // this stream name will map to a Kafka topic.
2120 'stream' => 'mediawiki.api-request'
2121 ],
2122 'http' => [
2123 'method' => $request->getMethod(),
2124 'client_ip' => $request->getIP()
2125 ],
2126 'performer' => $performer,
2127 'database' => WikiMap::getCurrentWikiDbDomain()->getId(),
2128 'backend_time_ms' => (int)round( $time * 1000 ),
2129 ];
2130
2131 // If set, these headers will be logged in http.request_headers.
2132 $httpRequestHeadersToLog = [ 'accept-language', 'referer', 'user-agent' ];
2133 foreach ( $httpRequestHeadersToLog as $header ) {
2134 if ( $request->getHeader( $header ) ) {
2135 // Set the header in http.request_headers
2136 $logCtx['http']['request_headers'][$header] = $request->getHeader( $header );
2137 }
2138 }
2139
2140 if ( $e ) {
2141 $logCtx['api_error_codes'] = [];
2142 foreach ( $this->errorMessagesFromException( $e ) as $msg ) {
2143 $logCtx['api_error_codes'][] = $msg->getApiCode();
2144 }
2145 }
2146
2147 // Construct space separated message for 'api' log channel
2148 $msg = "API {$request->getMethod()} " .
2149 wfUrlencode( str_replace( ' ', '_', $this->getUser()->getName() ) ) .
2150 " {$logCtx['http']['client_ip']} " .
2151 "T={$logCtx['backend_time_ms']}ms";
2152
2153 $sensitive = array_fill_keys( $this->getSensitiveParams(), true );
2154 foreach ( $this->getParamsUsed() as $name ) {
2155 $value = $request->getVal( $name );
2156 if ( $value === null ) {
2157 continue;
2158 }
2159
2160 if ( isset( $sensitive[$name] ) ) {
2161 $value = '[redacted]';
2162 $encValue = '[redacted]';
2163 } elseif ( strlen( $value ) > 256 ) {
2164 $value = substr( $value, 0, 256 );
2165 $encValue = $this->encodeRequestLogValue( $value ) . '[...]';
2166 } else {
2167 $encValue = $this->encodeRequestLogValue( $value );
2168 }
2169
2170 $logCtx['params'][$name] = $value;
2171 $msg .= " {$name}={$encValue}";
2172 }
2173
2174 // Log an unstructured message to the api channel.
2175 wfDebugLog( 'api', $msg, 'private' );
2176
2177 // The api-request channel a structured data log channel.
2178 wfDebugLog( 'api-request', '', 'private', $logCtx );
2179 }
2180
2186 protected function encodeRequestLogValue( $s ) {
2187 static $table = [];
2188 if ( !$table ) {
2189 $chars = ';@$!*(),/:';
2190 $numChars = strlen( $chars );
2191 for ( $i = 0; $i < $numChars; $i++ ) {
2192 $table[rawurlencode( $chars[$i] )] = $chars[$i];
2193 }
2194 }
2195
2196 return strtr( rawurlencode( $s ), $table );
2197 }
2198
2203 protected function getParamsUsed() {
2204 return array_keys( $this->mParamsUsed );
2205 }
2206
2211 public function markParamsUsed( $params ) {
2212 $this->mParamsUsed += array_fill_keys( (array)$params, true );
2213 }
2214
2220 protected function getSensitiveParams() {
2221 return array_keys( $this->mParamsSensitive );
2222 }
2223
2233 public function markParamsSensitive( $params ) {
2234 $this->mParamsSensitive += array_fill_keys( (array)$params, true );
2235 }
2236
2243 public function getVal( $name, $default = null ) {
2244 $this->mParamsUsed[$name] = true;
2245
2246 $ret = $this->getRequest()->getVal( $name );
2247 if ( $ret === null ) {
2248 if ( $this->getRequest()->getArray( $name ) !== null ) {
2249 // See T12262 for why we don't just implode( '|', ... ) the
2250 // array.
2251 $this->addWarning( [ 'apiwarn-unsupportedarray', $name ] );
2252 }
2253 $ret = $default;
2254 }
2255 return $ret;
2256 }
2257
2264 public function getCheck( $name ) {
2265 $this->mParamsUsed[$name] = true;
2266 return $this->getRequest()->getCheck( $name );
2267 }
2268
2276 public function getUpload( $name ) {
2277 $this->mParamsUsed[$name] = true;
2278
2279 return $this->getRequest()->getUpload( $name );
2280 }
2281
2286 protected function reportUnusedParams() {
2287 $paramsUsed = $this->getParamsUsed();
2288 $allParams = $this->getRequest()->getValueNames();
2289
2290 if ( !$this->mInternalMode ) {
2291 // Printer has not yet executed; don't warn that its parameters are unused
2292 $printerParams = $this->mPrinter->encodeParamName(
2293 array_keys( $this->mPrinter->getFinalParams() ?: [] )
2294 );
2295 $unusedParams = array_diff( $allParams, $paramsUsed, $printerParams );
2296 } else {
2297 $unusedParams = array_diff( $allParams, $paramsUsed );
2298 }
2299
2300 if ( count( $unusedParams ) ) {
2301 $this->addWarning( [
2302 'apierror-unrecognizedparams',
2303 // Do not use `wfEscapeWikiText( ... )` here for compatibility with PHP <8.1.4
2304 // https://gerrit.wikimedia.org/r/c/mediawiki/core/+/1160800/comment/92e67687_ab221188/
2305 Message::listParam( array_map( 'wfEscapeWikiText', $unusedParams ), ListType::COMMA ),
2306 count( $unusedParams )
2307 ] );
2308 }
2309 }
2310
2316 protected function printResult( $httpCode = 0 ) {
2317 if ( $this->getConfig()->get( MainConfigNames::DebugAPI ) !== false ) {
2318 $this->addWarning( 'apiwarn-wgdebugapi' );
2319 }
2320
2321 $printer = $this->mPrinter;
2322 $printer->initPrinter( false );
2323 if ( $httpCode ) {
2324 $printer->setHttpStatus( $httpCode );
2325 }
2326 $printer->execute();
2327 $printer->closePrinter();
2328 }
2329
2333 public function isReadMode() {
2334 return false;
2335 }
2336
2342 public function getAllowedParams() {
2343 return [
2344 'action' => [
2345 ParamValidator::PARAM_DEFAULT => 'help',
2346 ParamValidator::PARAM_TYPE => 'submodule',
2347 ],
2348 'format' => [
2349 ParamValidator::PARAM_DEFAULT => self::API_DEFAULT_FORMAT,
2350 ParamValidator::PARAM_TYPE => 'submodule',
2351 ],
2352 'maxlag' => [
2353 ParamValidator::PARAM_TYPE => 'integer'
2354 ],
2355 'smaxage' => [
2356 ParamValidator::PARAM_TYPE => 'integer',
2357 ParamValidator::PARAM_DEFAULT => 0,
2358 IntegerDef::PARAM_MIN => 0,
2359 ],
2360 'maxage' => [
2361 ParamValidator::PARAM_TYPE => 'integer',
2362 ParamValidator::PARAM_DEFAULT => 0,
2363 IntegerDef::PARAM_MIN => 0,
2364 ],
2365 'assert' => [
2366 ParamValidator::PARAM_TYPE => [ 'anon', 'user', 'bot' ]
2367 ],
2368 'assertuser' => [
2369 ParamValidator::PARAM_TYPE => 'user',
2370 UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'temp' ],
2371 ],
2372 'requestid' => null,
2373 'servedby' => false,
2374 'curtimestamp' => false,
2375 'responselanginfo' => false,
2376 'origin' => null,
2377 'crossorigin' => false,
2378 'uselang' => [
2379 ParamValidator::PARAM_DEFAULT => self::API_DEFAULT_USELANG,
2380 ],
2381 'variant' => null,
2382 'errorformat' => [
2383 ParamValidator::PARAM_TYPE => [ 'plaintext', 'wikitext', 'html', 'raw', 'none', 'bc' ],
2384 ParamValidator::PARAM_DEFAULT => 'bc',
2386 ],
2387 'errorlang' => [
2388 ParamValidator::PARAM_DEFAULT => 'uselang',
2389 ],
2390 'errorsuselocal' => [
2391 ParamValidator::PARAM_DEFAULT => false,
2392 ],
2393 ];
2394 }
2395
2397 protected function getExamplesMessages() {
2398 return [
2399 'action=help'
2400 => 'apihelp-help-example-main',
2401 'action=help&recursivesubmodules=1'
2402 => 'apihelp-help-example-recursive',
2403 ];
2404 }
2405
2410 public function modifyHelp( array &$help, array $options, array &$tocData ) {
2411 // Wish PHP had an "array_insert_before". Instead, we have to manually
2412 // reindex the array to get 'permissions' in the right place.
2413 $oldHelp = $help;
2414 $help = [];
2415 foreach ( $oldHelp as $k => $v ) {
2416 if ( $k === 'submodules' ) {
2417 $help['permissions'] = '';
2418 }
2419 $help[$k] = $v;
2420 }
2421 $help['datatypes'] = '';
2422 $help['templatedparams'] = '';
2423 $help['credits'] = '';
2424
2425 // Fill 'permissions'
2426 $help['permissions'] .= Html::openElement( 'div',
2427 [ 'class' => [ 'apihelp-block', 'apihelp-permissions' ] ] );
2428 $m = $this->msg( 'api-help-permissions' );
2429 if ( !$m->isDisabled() ) {
2430 $help['permissions'] .= Html::rawElement( 'div', [ 'class' => 'apihelp-block-head' ],
2431 $m->numParams( count( self::RIGHTS_MAP ) )->parse()
2432 );
2433 }
2434 $help['permissions'] .= Html::openElement( 'dl' );
2435 // TODO inject stuff, see T265644
2436 $groupPermissionsLookup = MediaWikiServices::getInstance()->getGroupPermissionsLookup();
2437 foreach ( self::RIGHTS_MAP as $right => $rightMsg ) {
2438 $help['permissions'] .= Html::element( 'dt', [], $right );
2439
2440 $rightMsg = $this->msg( $rightMsg['msg'], $rightMsg['params'] )->parse();
2441 $help['permissions'] .= Html::rawElement( 'dd', [], $rightMsg );
2442
2443 $groups = array_map( static function ( $group ) {
2444 return $group == '*' ? 'all' : $group;
2445 }, $groupPermissionsLookup->getGroupsWithPermission( $right ) );
2446
2447 $help['permissions'] .= Html::rawElement( 'dd', [],
2448 $this->msg( 'api-help-permissions-granted-to' )
2449 ->numParams( count( $groups ) )
2450 ->params( Message::listParam( $groups ) )
2451 ->parse()
2452 );
2453 }
2454 $help['permissions'] .= Html::closeElement( 'dl' );
2455 $help['permissions'] .= Html::closeElement( 'div' );
2456
2457 // Fill 'datatypes', 'templatedparams', and 'credits', if applicable
2458 if ( empty( $options['nolead'] ) ) {
2459 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset Must set when nolead is not set
2460 $level = $options['headerlevel'];
2461 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset Must set when nolead is not set
2462 $tocnumber = &$options['tocnumber'];
2463
2464 $header = $this->msg( 'api-help-datatypes-header' )->parse();
2465 $headline = Html::rawElement(
2466 'h' . min( 6, $level ),
2467 [ 'class' => 'apihelp-header', 'id' => 'main/datatypes' ],
2468 $header
2469 );
2470 $help['datatypes'] .= $headline;
2471 $help['datatypes'] .= $this->msg( 'api-help-datatypes-top' )->parseAsBlock();
2472 $help['datatypes'] .= '<dl>';
2473 foreach ( $this->getParamValidator()->knownTypes() as $type ) {
2474 $m = $this->msg( "api-help-datatype-$type" );
2475 if ( !$m->isDisabled() ) {
2476 $help['datatypes'] .= Html::element( 'dt', [ 'id' => "main/datatype/$type" ], $type );
2477 $help['datatypes'] .= Html::rawElement( 'dd', [], $m->parseAsBlock() );
2478 }
2479 }
2480 $help['datatypes'] .= '</dl>';
2481 if ( !isset( $tocData['main/datatypes'] ) ) {
2482 $tocnumber[$level]++;
2483 $anchor = 'main/datatypes';
2484 $tocData['main/datatypes'] = new SectionMetadata(
2485 tocLevel: count( $tocnumber ),
2486 hLevel: $level,
2487 line: $header,
2488 number: implode( '.', $tocnumber ),
2489 index: '',
2490 anchor: $anchor,
2491 linkAnchor: Sanitizer::escapeIdForLink( $anchor ),
2492 );
2493 }
2494
2495 $header = $this->msg( 'api-help-templatedparams-header' )->parse();
2496 $headline = Html::rawElement(
2497 'h' . min( 6, $level ),
2498 [ 'class' => 'apihelp-header', 'id' => 'main/templatedparams' ],
2499 $header
2500 );
2501 $help['templatedparams'] .= $headline;
2502 $help['templatedparams'] .= $this->msg( 'api-help-templatedparams' )->parseAsBlock();
2503 if ( !isset( $tocData['main/templatedparams'] ) ) {
2504 $tocnumber[$level]++;
2505 $anchor = 'main/templatedparams';
2506 $tocData['main/templatedparams'] = new SectionMetadata(
2507 tocLevel: count( $tocnumber ),
2508 hLevel: $level,
2509 line: $header,
2510 number: implode( '.', $tocnumber ),
2511 index: '',
2512 anchor: $anchor,
2513 linkAnchor: Sanitizer::escapeIdForLink( $anchor ),
2514 );
2515 }
2516
2517 $header = $this->msg( 'api-credits-header' )->parse();
2518 $headline = Html::rawElement(
2519 'h' . min( 6, $level ),
2520 [ 'class' => 'apihelp-header', 'id' => 'main/credits' ],
2521 $header
2522 );
2523 $help['credits'] .= $headline;
2524 $help['credits'] .= $this->msg( 'api-credits' )->useDatabase( false )->parseAsBlock();
2525 if ( !isset( $tocData['main/credits'] ) ) {
2526 $tocnumber[$level]++;
2527 $anchor = 'main/credits';
2528 $tocData['main/credits'] = new SectionMetadata(
2529 tocLevel: count( $tocnumber ),
2530 hLevel: $level,
2531 line: $header,
2532 number: implode( '.', $tocnumber ),
2533 index: '',
2534 anchor: $anchor,
2535 linkAnchor: Sanitizer::escapeIdForLink( $anchor ),
2536 );
2537 }
2538 }
2539 }
2540
2542 private $mCanApiHighLimits = null;
2543
2548 public function canApiHighLimits() {
2549 if ( $this->mCanApiHighLimits === null ) {
2550 $this->mCanApiHighLimits = $this->getAuthority()->isAllowed( 'apihighlimits' );
2551 }
2552
2553 return $this->mCanApiHighLimits;
2554 }
2555
2560 public function getModuleManager() {
2561 return $this->mModuleMgr;
2562 }
2563
2572 public function getUserAgent() {
2573 $agent = (string)$this->getRequest()->getHeader( 'Api-user-agent' );
2574 if ( $agent == '' ) {
2575 $agent = $this->getRequest()->getHeader( 'User-agent' );
2576 }
2577
2578 return $agent;
2579 }
2580}
2581
2588class_alias( ApiMain::class, 'ApiMain' );
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,...
wfEscapeWikiText( $input)
Escapes the given text so that it may be output using addWikiText() without any linking,...
wfShorthandToInteger(?string $string='', int $default=-1)
Converts shorthand byte notation to integer form.
wfHostname()
Get host name of the current machine, for use in error reporting.
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.
wfScript( $script='index')
Get the URL path to a MediaWiki entry point.
wfResetOutputBuffers( $resetGzipEncoding=true)
Clear away any user-level output buffers, discarding contents.
if(MW_ENTRY_POINT==='index') if(!defined( 'MW_NO_SESSION') &&MW_ENTRY_POINT !=='cli' $wgLang
Definition Setup.php:551
const MW_ENTRY_POINT
Definition api.php:21
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:60
getHookRunner()
Get an ApiHookRunner for running core API hooks.
Definition ApiBase.php:766
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, or 'string' with PARAM_ISMULTI,...
Definition ApiBase.php:206
const LIMIT_SML2
Slow query, apihighlimits limit.
Definition ApiBase.php:237
const LIMIT_BIG2
Fast query, apihighlimits limit.
Definition ApiBase.php:233
isWriteMode()
Indicates whether this module requires write access to the wiki.
Definition ApiBase.php:436
Format errors and warnings in the old style, for backwards compatibility.
Formats errors and warnings for the API, and add them to the associated ApiResult.
static isValidApiCode( $code)
Test whether a code is a valid API error code.
This is the abstract base class for API formatters.
This is the main API class, used for both external and internal processing.
Definition ApiMain.php:67
static matchRequestedHeaders( $requestedHeaders, $allowedHeaders)
Attempt to validate the value of Access-Control-Request-Headers against a list of headers that we all...
Definition ApiMain.php:1290
canApiHighLimits()
Check whether the current user is allowed to use high limits.
Definition ApiMain.php:2548
handleCORS()
Check the &origin= and/or &crossorigin= query parameters and respond appropriately.
Definition ApiMain.php:1162
getSensitiveParams()
Get the request parameters that should be considered sensitive.
Definition ApiMain.php:2220
static handleApiBeforeMainException(Throwable $e)
Handle a throwable from the ApiBeforeMain hook.
Definition ApiMain.php:1124
getErrorFormatter()
Get the ApiErrorFormatter object associated with current request.
Definition ApiMain.php:770
getStatsFactory()
Get the stats factory.
Definition ApiMain.php:812
checkConditionalRequestHeaders( $module)
Check selected RFC 7232 precondition headers.
Definition ApiMain.php:1756
checkMaxLag( $module, $params)
Check the max lag if necessary.
Definition ApiMain.php:1710
getResult()
Get the ApiResult object associated with current request.
Definition ApiMain.php:728
__construct( $context=null, $enableWrite=false, $internal=null)
Constructs an instance of ApiMain that utilizes the module and format specified by $request.
Definition ApiMain.php:603
setRequestExpectations(ApiBase $module)
Set database connection, query, and write expectations given this module request.
Definition ApiMain.php:2078
markParamsUsed( $params)
Mark parameters as used.
Definition ApiMain.php:2211
encodeRequestLogValue( $s)
Encode a value in a format suitable for a space-separated log line.
Definition ApiMain.php:2186
getAllowedParams()
See ApiBase for description.
Definition ApiMain.php:2342
setCacheControl( $directives)
Set directives (key/value pairs) for the Cache-Control header.
Definition ApiMain.php:908
checkReadOnly( $module)
Check if the DB is read-only for this user.
Definition ApiMain.php:1887
getModuleManager()
Overrides to return this instance's module manager.
Definition ApiMain.php:2560
modifyHelp(array &$help, array $options, array &$tocData)
Called from ApiHelp before the pieces are joined together and returned.This exists mainly for ApiMain...
Definition ApiMain.php:2410
isInternalMode()
Return true if the API was started by other PHP code using MediaWiki\Request\FauxRequest.
Definition ApiMain.php:719
getUserAgent()
Fetches the user agent used for this request.
Definition ApiMain.php:2572
sendCacheHeaders( $isError)
Send caching headers.
Definition ApiMain.php:1316
setupExecuteAction()
Set up for the execution.
Definition ApiMain.php:1612
setCacheMode( $mode)
Set the type of caching headers which will be sent.
Definition ApiMain.php:862
getUpload( $name)
Get a request upload, and register the fact that it was used, for logging.
Definition ApiMain.php:2276
getCheck( $name)
Get a boolean request value, and register the fact that the parameter was used, for logging.
Definition ApiMain.php:2264
reportUnusedParams()
Report unused parameters, so the client gets a hint in case it gave us parameters we don't know,...
Definition ApiMain.php:2286
substituteResultWithError(Throwable $e)
Replace the result data with the information about a throwable.
Definition ApiMain.php:1490
handleException(Throwable $e, $latency=0)
Handle a throwable as an API response.
Definition ApiMain.php:1021
execute()
Execute api request.
Definition ApiMain.php:934
setContinuationManager(?ApiContinuationManager $manager=null)
Definition ApiMain.php:784
executeAction()
Execute the actual module, without any error handling.
Definition ApiMain.php:2029
errorMessagesFromException(Throwable $e, $type='error')
Create an error message for the given throwable.
Definition ApiMain.php:1454
getPrinter()
Get the result formatter object.
Definition ApiMain.php:821
setCacheMaxAge( $maxage)
Set how long the response should be cached.
Definition ApiMain.php:830
setupModule()
Set up the module for response.
Definition ApiMain.php:1626
lacksSameOriginSecurity()
Get the security flag for the current request.
Definition ApiMain.php:736
getExamplesMessages()
Returns usage examples for this module.Return value has query strings as keys, with values being eith...
Definition ApiMain.php:2397
printResult( $httpCode=0)
Print results using the current printer.
Definition ApiMain.php:2316
getVal( $name, $default=null)
Get a request value, and register the fact that it was used, for logging.
Definition ApiMain.php:2243
executeActionWithErrorHandling()
Execute an action, and in case of an error, erase whatever partial results have been accumulated,...
Definition ApiMain.php:946
createPrinterByName( $format)
Create an instance of an output formatter by its name.
Definition ApiMain.php:919
getParamsUsed()
Get the request parameters used in the course of the preceding execute() request.
Definition ApiMain.php:2203
logRequest( $time, ?Throwable $e=null)
Log the preceding request.
Definition ApiMain.php:2100
addRequestedFields( $force=[])
Add requested fields to the result.
Definition ApiMain.php:1574
checkAsserts( $params)
Check asserts of the user's rights.
Definition ApiMain.php:1942
checkExecutePermissions( $module)
Check for sufficient permissions to execute.
Definition ApiMain.php:1857
markParamsSensitive( $params)
Mark parameters as sensitive.
Definition ApiMain.php:2233
setupExternalResponse( $module, $params)
Check POST for external response and setup result printer.
Definition ApiMain.php:1980
getModule()
Get the API module object.
Definition ApiMain.php:803
static create( $msg, $code=null, ?array $data=null)
Create an IApiMessage for the message.
This class holds a list of modules and handles instantiation.
This class represents the result of the API operations.
Definition ApiResult.php:34
const NO_SIZE_CHECK
For addValue() and similar functions, do not check size while adding a value Don't use this unless yo...
Definition ApiResult.php:57
Exception used to abort API execution with an error.
getStatusValue()
Fetch the error status.
This wraps a bunch of the API-specific parameter validation logic.
setContext(IContextSource $context)
An IContextSource implementation which will inherit context from another source but allow individual ...
Group all the pieces relevant to the context of a request into one instance.
Debug toolbar.
Definition MWDebug.php:35
Handler class for MWExceptions.
Class to expose exceptions to the client (API bots, users, admins using CLI scripts)
This class is a collection of static functions that serve two purposes:
Definition Html.php:43
Create PSR-3 logger objects.
A class containing constants representing the names of configuration variables.
const TrxProfilerLimits
Name constant for the TrxProfilerLimits setting, for use with Config::get()
const ForceHTTPS
Name constant for the ForceHTTPS setting, for use with Config::get()
const ServerName
Name constant for the ServerName setting, for use with Config::get()
const APIFormatModules
Name constant for the APIFormatModules setting, for use with Config::get()
const UseCdn
Name constant for the UseCdn setting, for use with Config::get()
const JobQueueIncludeInMaxLagFactor
Name constant for the JobQueueIncludeInMaxLagFactor setting, for use with Config::get()
const CrossSiteAJAXdomainExceptions
Name constant for the CrossSiteAJAXdomainExceptions setting, for use with Config::get()
const CdnMaxAge
Name constant for the CdnMaxAge setting, for use with Config::get()
const AllowedCorsHeaders
Name constant for the AllowedCorsHeaders setting, for use with Config::get()
const ShowExceptionDetails
Name constant for the ShowExceptionDetails setting, for use with Config::get()
const APIModules
Name constant for the APIModules setting, for use with Config::get()
const VaryOnXFP
Name constant for the VaryOnXFP setting, for use with Config::get()
const APIMaxLagThreshold
Name constant for the APIMaxLagThreshold setting, for use with Config::get()
const CacheEpoch
Name constant for the CacheEpoch setting, for use with Config::get()
const DebugAPI
Name constant for the DebugAPI setting, for use with Config::get()
const CrossSiteAJAXdomains
Name constant for the CrossSiteAJAXdomains setting, for use with Config::get()
const APIMaxResultSize
Name constant for the APIMaxResultSize setting, for use with Config::get()
const ShowHostnames
Name constant for the ShowHostnames setting, for use with Config::get()
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
The Message class deals with fetching and processing of interface message into a variety of formats.
Definition Message.php:144
static newFromSpecifier( $value)
Transform a MessageSpecifier or a primitive value used interchangeably with specifiers (a message key...
Definition Message.php:492
static listParam(array $list, $type=ListType::AND)
Definition Message.php:1355
static sizeParam( $size)
Definition Message.php:1322
Type definition for user types.
Definition UserDef.php:27
HTML sanitizer for MediaWiki.
Definition Sanitizer.php:32
Profiler base class that defines the interface and some shared functionality.
Definition Profiler.php:25
Class for tracking request-level classification information for profiling/stats/logging.
WebRequest clone which takes values from a provided array.
Object to access the $_FILES array.
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form,...
A class to assist with the parsing of Origin header according to the RFC 6454 https://tools....
Definition Origin.php:12
static parseHeaderList(array $headerList)
Parse an Origin header list as returned by RequestInterface::getHeader().
Definition Origin.php:28
Stub object for the global user ($wgUser) that makes it possible to change the relevant underlying ob...
static setUser( $user)
Reset the stub global user to a different "real" user object, while ensuring that any method calls on...
Tools for dealing with other locally-hosted wikis.
Definition WikiMap.php:19
Service for formatting and validating API parameters.
Type definition for integer types.
This is the primary interface for validating metrics definitions, caching defined metrics,...
return[ 'config-schema-inverse'=>['default'=>['ConfigRegistry'=>['main'=> 'MediaWiki\\Config\\GlobalVarConfig::newInstance',], 'Sitename'=> 'MediaWiki', 'Server'=> false, 'CanonicalServer'=> false, 'ServerName'=> false, 'AssumeProxiesUseDefaultProtocolPorts'=> true, 'HttpsPort'=> 443, 'ForceHTTPS'=> false, 'ScriptPath'=> '/wiki', 'UsePathInfo'=> null, 'Script'=> false, 'LoadScript'=> false, 'RestPath'=> false, 'StylePath'=> false, 'LocalStylePath'=> false, 'ExtensionAssetsPath'=> false, 'ExtensionDirectory'=> null, 'StyleDirectory'=> null, 'ArticlePath'=> false, 'UploadPath'=> false, 'ImgAuthPath'=> false, 'ThumbPath'=> false, 'UploadDirectory'=> false, 'FileCacheDirectory'=> false, 'Logo'=> false, 'Logos'=> false, 'Favicon'=> '/favicon.ico', 'AppleTouchIcon'=> false, 'ReferrerPolicy'=> false, 'TmpDirectory'=> false, 'UploadBaseUrl'=> '', 'UploadStashScalerBaseUrl'=> false, 'ActionPaths'=>[], 'MainPageIsDomainRoot'=> false, 'EnableUploads'=> false, 'UploadStashMaxAge'=> 21600, 'EnableAsyncUploads'=> false, 'EnableAsyncUploadsByURL'=> false, 'UploadMaintenance'=> false, 'IllegalFileChars'=> ':\\/\\\\', 'DeletedDirectory'=> false, 'ImgAuthDetails'=> false, 'ImgAuthUrlPathMap'=>[], 'LocalFileRepo'=>['class'=> 'MediaWiki\\FileRepo\\LocalRepo', 'name'=> 'local', 'directory'=> null, 'scriptDirUrl'=> null, 'favicon'=> null, 'url'=> null, 'hashLevels'=> null, 'thumbScriptUrl'=> null, 'transformVia404'=> null, 'deletedDir'=> null, 'deletedHashLevels'=> null, 'updateCompatibleMetadata'=> null, 'reserializeMetadata'=> null,], 'ForeignFileRepos'=>[], 'UseInstantCommons'=> false, 'UseSharedUploads'=> false, 'SharedUploadDirectory'=> null, 'SharedUploadPath'=> null, 'HashedSharedUploadDirectory'=> true, 'RepositoryBaseUrl'=> 'https:'FetchCommonsDescriptions'=> false, 'SharedUploadDBname'=> false, 'SharedUploadDBprefix'=> '', 'CacheSharedUploads'=> true, 'ForeignUploadTargets'=>['local',], 'UploadDialog'=>['fields'=>['description'=> true, 'date'=> false, 'categories'=> false,], 'licensemessages'=>['local'=> 'generic-local', 'foreign'=> 'generic-foreign',], 'comment'=>['local'=> '', 'foreign'=> '',], 'format'=>['filepage'=> ' $DESCRIPTION', 'description'=> ' $TEXT', 'ownwork'=> '', 'license'=> '', 'uncategorized'=> '',],], 'FileBackends'=>[], 'LockManagers'=>[], 'ShowEXIF'=> null, 'UpdateCompatibleMetadata'=> false, 'AllowCopyUploads'=> false, 'CopyUploadsDomains'=>[], 'CopyUploadsFromSpecialUpload'=> false, 'CopyUploadProxy'=> false, 'CopyUploadTimeout'=> false, 'CopyUploadAllowOnWikiDomainConfig'=> false, 'MaxUploadSize'=> 104857600, 'MinUploadChunkSize'=> 1024, 'UploadNavigationUrl'=> false, 'UploadMissingFileUrl'=> false, 'ThumbnailScriptPath'=> false, 'SharedThumbnailScriptPath'=> false, 'HashedUploadDirectory'=> true, 'CSPUploadEntryPoint'=> true, 'FileExtensions'=>['png', 'gif', 'jpg', 'jpeg', 'webp',], 'ProhibitedFileExtensions'=>['html', 'htm', 'js', 'jsb', 'mhtml', 'mht', 'xhtml', 'xht', 'php', 'phtml', 'php3', 'php4', 'php5', 'phps', 'phar', 'shtml', 'jhtml', 'pl', 'py', 'cgi', 'exe', 'scr', 'dll', 'msi', 'vbs', 'bat', 'com', 'pif', 'cmd', 'vxd', 'cpl', 'xml',], 'MimeTypeExclusions'=>['text/html', 'application/javascript', 'text/javascript', 'text/x-javascript', 'application/x-shellscript', 'application/x-php', 'text/x-php', 'text/x-python', 'text/x-perl', 'text/x-bash', 'text/x-sh', 'text/x-csh', 'text/scriptlet', 'application/x-msdownload', 'application/x-msmetafile', 'application/java', 'application/xml', 'text/xml',], 'CheckFileExtensions'=> true, 'StrictFileExtensions'=> true, 'DisableUploadScriptChecks'=> false, 'UploadSizeWarning'=> false, 'TrustedMediaFormats'=>['BITMAP', 'AUDIO', 'VIDEO', 'image/svg+xml', 'application/pdf',], 'MediaHandlers'=>[], 'NativeImageLazyLoading'=> false, 'ParserTestMediaHandlers'=>['image/jpeg'=> 'MockBitmapHandler', 'image/png'=> 'MockBitmapHandler', 'image/gif'=> 'MockBitmapHandler', 'image/tiff'=> 'MockBitmapHandler', 'image/webp'=> 'MockBitmapHandler', 'image/x-ms-bmp'=> 'MockBitmapHandler', 'image/x-bmp'=> 'MockBitmapHandler', 'image/x-xcf'=> 'MockBitmapHandler', 'image/svg+xml'=> 'MockSvgHandler', 'image/vnd.djvu'=> 'MockDjVuHandler',], 'UseImageResize'=> true, 'UseImageMagick'=> false, 'ImageMagickConvertCommand'=> '/usr/bin/convert', 'MaxInterlacingAreas'=>[], 'SharpenParameter'=> '0x0.4', 'SharpenReductionThreshold'=> 0.85, 'ImageMagickTempDir'=> false, 'CustomConvertCommand'=> false, 'JpegTran'=> '/usr/bin/jpegtran', 'JpegPixelFormat'=> 'yuv420', 'JpegQuality'=> 80, 'Exiv2Command'=> '/usr/bin/exiv2', 'Exiftool'=> '/usr/bin/exiftool', 'SVGConverters'=>['ImageMagick'=> ' $path/convert -background "#ffffff00" -thumbnail $widthx$height\\! $input PNG:$output', 'inkscape'=> ' $path/inkscape -w $width -o $output $input', 'batik'=> 'java -Djava.awt.headless=true -jar $path/batik-rasterizer.jar -w $width -d $output $input', 'rsvg'=> ' $path/rsvg-convert -w $width -h $height -o $output $input', 'ImagickExt'=>['SvgHandler::rasterizeImagickExt',],], 'SVGConverter'=> 'ImageMagick', 'SVGConverterPath'=> '', 'SVGMaxSize'=> 5120, 'SVGMetadataCutoff'=> 5242880, 'SVGNativeRendering'=> true, 'SVGNativeRenderingSizeLimit'=> 51200, 'MediaInTargetLanguage'=> true, 'MaxImageArea'=> 12500000, 'MaxAnimatedGifArea'=> 12500000, 'TiffThumbnailType'=>[], 'ThumbnailEpoch'=> '20030516000000', 'AttemptFailureEpoch'=> 1, 'IgnoreImageErrors'=> false, 'GenerateThumbnailOnParse'=> true, 'ShowArchiveThumbnails'=> true, 'EnableAutoRotation'=> null, 'Antivirus'=> null, 'AntivirusSetup'=>['clamav'=>['command'=> 'clamscan --no-summary ', 'codemap'=>[0=> 0, 1=> 1, 52=> -1, ' *'=> false,], 'messagepattern'=> '/.*?:(.*)/sim',],], 'AntivirusRequired'=> true, 'VerifyMimeType'=> true, 'MimeTypeFile'=> 'internal', 'MimeInfoFile'=> 'internal', 'MimeDetectorCommand'=> null, 'TrivialMimeDetection'=> false, 'XMLMimeTypes'=>['http:'svg'=> 'image/svg+xml', 'http:'http:'html'=> 'text/html',], 'ImageLimits'=>[[320, 240,], [640, 480,], [800, 600,], [1024, 768,], [1280, 1024,], [2560, 2048,],], 'ThumbLimits'=>[120, 150, 180, 200, 220, 250, 300, 400,], 'ThumbnailNamespaces'=>[6,], 'ThumbnailSteps'=> null, 'ThumbnailStepsRatio'=> null, 'ThumbnailBuckets'=> null, 'ThumbnailMinimumBucketDistance'=> 50, 'UploadThumbnailRenderMap'=>[], 'UploadThumbnailRenderMethod'=> 'jobqueue', 'UploadThumbnailRenderHttpCustomHost'=> false, 'UploadThumbnailRenderHttpCustomDomain'=> false, 'UseTinyRGBForJPGThumbnails'=> false, 'GalleryOptions'=>[], 'ThumbUpright'=> 0.75, 'DirectoryMode'=> 511, 'ResponsiveImages'=> true, 'ImagePreconnect'=> false, 'DjvuUseBoxedCommand'=> false, 'DjvuDump'=> null, 'DjvuRenderer'=> null, 'DjvuTxt'=> null, 'DjvuPostProcessor'=> 'pnmtojpeg', 'DjvuOutputExtension'=> 'jpg', 'EmergencyContact'=> false, 'PasswordSender'=> false, 'NoReplyAddress'=> false, 'EnableEmail'=> true, 'EnableUserEmail'=> true, 'UserEmailUseReplyTo'=> true, 'PasswordReminderResendTime'=> 24, 'NewPasswordExpiry'=> 604800, 'UserEmailConfirmationTokenExpiry'=> 604800, 'UserEmailConfirmationUseHTML'=> false, 'PasswordExpirationDays'=> false, 'PasswordExpireGrace'=> 604800, 'SMTP'=> false, 'AdditionalMailParams'=> null, 'AllowHTMLEmail'=> false, 'EnotifFromEditor'=> false, 'EmailAuthentication'=> true, 'EnotifWatchlist'=> false, 'EnotifUserTalk'=> false, 'EnotifRevealEditorAddress'=> false, 'EnotifMinorEdits'=> true, 'EnotifUseRealName'=> false, 'UsersNotifiedOnAllChanges'=>[], 'DBname'=> 'my_wiki', 'DBmwschema'=> null, 'DBprefix'=> '', 'DBserver'=> 'localhost', 'DBport'=> 5432, 'DBuser'=> 'wikiuser', 'DBpassword'=> '', 'DBtype'=> 'mysql', 'DBssl'=> false, 'DBcompress'=> false, 'DBStrictWarnings'=> false, 'DBadminuser'=> null, 'DBadminpassword'=> null, 'SearchType'=> null, 'SearchTypeAlternatives'=> null, 'DBTableOptions'=> 'ENGINE=InnoDB, DEFAULT CHARSET=binary', 'SQLMode'=> '', 'SQLiteDataDir'=> '', 'SharedDB'=> null, 'SharedPrefix'=> false, 'SharedTables'=>['user', 'user_properties', 'user_autocreate_serial',], 'SharedSchema'=> false, 'DBservers'=> false, 'LBFactoryConf'=>['class'=> 'Wikimedia\\Rdbms\\LBFactorySimple',], 'DataCenterUpdateStickTTL'=> 10, 'DBerrorLog'=> false, 'DBerrorLogTZ'=> false, 'LocalDatabases'=>[], 'DatabaseReplicaLagWarning'=> 10, 'DatabaseReplicaLagCritical'=> 30, 'MaxExecutionTimeForExpensiveQueries'=> 0, 'VirtualDomainsMapping'=>[], 'FileSchemaMigrationStage'=> 3, 'ImageLinksSchemaMigrationStage'=> 768, 'ExternalLinksDomainGaps'=>[], 'ContentHandlers'=>['wikitext'=>['class'=> 'MediaWiki\\Content\\WikitextContentHandler', 'services'=>['TitleFactory', 'ParserFactory', 'GlobalIdGenerator', 'LanguageNameUtils', 'LinkRenderer', 'MagicWordFactory', 'ParsoidParserFactory',],], 'javascript'=>['class'=> 'MediaWiki\\Content\\JavaScriptContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup',],], 'json'=>['class'=> 'MediaWiki\\Content\\JsonContentHandler', 'services'=>['ParsoidParserFactory', 'TitleFactory',],], 'css'=>['class'=> 'MediaWiki\\Content\\CssContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup',],], 'vue'=>['class'=> 'MediaWiki\\Content\\VueContentHandler', 'services'=>['MainConfig', 'ParserFactory',],], 'text'=> 'MediaWiki\\Content\\TextContentHandler', 'unknown'=> 'MediaWiki\\Content\\FallbackContentHandler',], 'NamespaceContentModels'=>[], 'TextModelsToParse'=>['wikitext', 'javascript', 'css',], 'CompressRevisions'=> false, 'ExternalStores'=>[], 'ExternalServers'=>[], 'DefaultExternalStore'=> false, 'RevisionCacheExpiry'=> 604800, 'PageLanguageUseDB'=> false, 'DiffEngine'=> null, 'ExternalDiffEngine'=> false, 'Wikidiff2Options'=>[], 'RequestTimeLimit'=> null, 'TransactionalTimeLimit'=> 120, 'CriticalSectionTimeLimit'=> 180.0, 'MiserMode'=> false, 'DisableQueryPages'=> false, 'QueryCacheLimit'=> 1000, 'WantedPagesThreshold'=> 1, 'AllowSlowParserFunctions'=> false, 'AllowSchemaUpdates'=> true, 'MaxArticleSize'=> 2048, 'MemoryLimit'=> '50M', 'PoolCounterConf'=> null, 'PoolCountClientConf'=>['servers'=>['127.0.0.1',], 'timeout'=> 0.1,], 'MaxUserDBWriteDuration'=> false, 'MaxJobDBWriteDuration'=> false, 'LinkHolderBatchSize'=> 1000, 'MaximumMovedPages'=> 100, 'ForceDeferredUpdatesPreSend'=> false, 'MultiShardSiteStats'=> false, 'CacheDirectory'=> false, 'MainCacheType'=> 0, 'MessageCacheType'=> -1, 'ParserCacheType'=> -1, 'SessionCacheType'=> -1, 'AnonSessionCacheType'=> false, 'LanguageConverterCacheType'=> -1, 'ObjectCaches'=>[0=>['class'=> 'Wikimedia\\ObjectCache\\EmptyBagOStuff', 'reportDupes'=> false,], 1=>['class'=> 'MediaWiki\\ObjectCache\\SqlBagOStuff', 'loggroup'=> 'SQLBagOStuff',], 'memcached-php'=>['class'=> 'Wikimedia\\ObjectCache\\MemcachedPhpBagOStuff', 'loggroup'=> 'memcached',], 'memcached-pecl'=>['class'=> 'Wikimedia\\ObjectCache\\MemcachedPeclBagOStuff', 'loggroup'=> 'memcached',], 'hash'=>['class'=> 'Wikimedia\\ObjectCache\\HashBagOStuff', 'reportDupes'=> false,], 'apc'=>['class'=> 'Wikimedia\\ObjectCache\\APCUBagOStuff', 'reportDupes'=> false,], 'apcu'=>['class'=> 'Wikimedia\\ObjectCache\\APCUBagOStuff', 'reportDupes'=> false,],], 'WANObjectCache'=>[], 'MicroStashType'=> -1, 'MainStash'=> 1, 'ParsoidCacheConfig'=>['StashType'=> null, 'StashDuration'=> 86400, 'WarmParsoidParserCache'=> false,], 'ParsoidSelectiveUpdateSampleRate'=> 0, 'ParserCacheFilterConfig'=>['pcache'=>['default'=>['minCpuTime'=> 0,],], 'parsoid-pcache'=>['default'=>['minCpuTime'=> 0,],], 'postproc-pcache'=>['default'=>['minCpuTime'=> 9223372036854775807,],], 'postproc-parsoid-pcache'=>['default'=>['minCpuTime'=> 9223372036854775807,],],], 'ChronologyProtectorSecret'=> '', 'ParserCacheExpireTime'=> 86400, 'ParserCacheAsyncExpireTime'=> 60, 'ParserCacheAsyncRefreshJobs'=> true, 'OldRevisionParserCacheExpireTime'=> 3600, 'ObjectCacheSessionExpiry'=> 3600, 'PHPSessionHandling'=> 'warn', 'SuspiciousIpExpiry'=> false, 'SessionPbkdf2Iterations'=> 10001, 'UseSessionCookieJwt'=> false, 'UseSessionCookieForBotPasswords'=> false, 'JwtSessionCookieIssuer'=> null, 'MemCachedServers'=>['127.0.0.1:11211',], 'MemCachedPersistent'=> false, 'MemCachedTimeout'=> 500000, 'UseLocalMessageCache'=> false, 'AdaptiveMessageCache'=> false, 'LocalisationCacheConf'=>['class'=> 'MediaWiki\\Language\\LocalisationCache', 'store'=> 'detect', 'storeClass'=> false, 'storeDirectory'=> false, 'storeServer'=>[], 'forceRecache'=> false, 'manualRecache'=> false,], 'CachePages'=> true, 'CacheEpoch'=> '20030516000000', 'GitInfoCacheDirectory'=> false, 'UseFileCache'=> false, 'FileCacheDepth'=> 2, 'RenderHashAppend'=> '', 'EnableSidebarCache'=> false, 'SidebarCacheExpiry'=> 86400, 'UseGzip'=> false, 'InvalidateCacheOnLocalSettingsChange'=> true, 'ExtensionInfoMTime'=> false, 'EnableRemoteBagOStuffTests'=> false, 'UseCdn'=> false, 'VaryOnXFP'=> false, 'InternalServer'=> false, 'CdnMaxAge'=> 18000, 'CdnMaxageLagged'=> 30, 'CdnMaxageStale'=> 10, 'CdnReboundPurgeDelay'=> 0, 'CdnMaxageSubstitute'=> 60, 'ForcedRawSMaxage'=> 300, 'CdnServers'=>[], 'CdnServersNoPurge'=>[], 'HTCPRouting'=>[], 'HTCPMulticastTTL'=> 1, 'UsePrivateIPs'=> false, 'CdnMatchParameterOrder'=> true, 'LanguageCode'=> 'en', 'GrammarForms'=>[], 'InterwikiMagic'=> true, 'HideInterlanguageLinks'=> false, 'ExtraInterlanguageLinkPrefixes'=>[], 'InterlanguageLinkCodeMap'=>[], 'ExtraLanguageNames'=>[], 'ExtraLanguageCodes'=>['bh'=> 'bho', 'no'=> 'nb', 'simple'=> 'en',], 'DummyLanguageCodes'=>[], 'AllUnicodeFixes'=> false, 'LegacyEncoding'=> false, 'AmericanDates'=> false, 'TranslateNumerals'=> true, 'UseDatabaseMessages'=> true, 'MaxMsgCacheEntrySize'=> 10000, 'DisableLangConversion'=> false, 'DisableTitleConversion'=> false, 'DefaultLanguageVariant'=> false, 'UsePigLatinVariant'=> false, 'DisabledVariants'=>[], 'VariantArticlePath'=> false, 'UseXssLanguage'=> false, 'LoginLanguageSelector'=> false, 'ForceUIMsgAsContentMsg'=>[], 'RawHtmlMessages'=>[], 'Localtimezone'=> null, 'LocalTZoffset'=> null, 'OverrideUcfirstCharacters'=>[], 'MimeType'=> 'text/html', 'Html5Version'=> null, 'EditSubmitButtonLabelPublish'=> false, 'XhtmlNamespaces'=>[], 'SiteNotice'=> '', 'BrowserFormatDetection'=> 'telephone=no', 'SkinMetaTags'=>[], 'DefaultSkin'=> 'vector-2022', 'FallbackSkin'=> 'fallback', 'SkipSkins'=>[], 'DisableOutputCompression'=> false, 'FragmentMode'=>['html5', 'legacy',], 'ExternalInterwikiFragmentMode'=> 'legacy', 'FooterIcons'=>['copyright'=>['copyright'=>[],], 'poweredby'=>['mediawiki'=>['src'=> null, 'url'=> 'https:'alt'=> 'Powered by MediaWiki', 'lang'=> 'en',],],], 'UseCombinedLoginLink'=> false, 'Edititis'=> false, 'Send404Code'=> true, 'ShowRollbackEditCount'=> 10, 'EnableCanonicalServerLink'=> false, 'InterwikiLogoOverride'=>[], 'ResourceModules'=>[], 'ResourceModuleSkinStyles'=>[], 'ResourceLoaderSources'=>[], 'ResourceBasePath'=> null, 'ResourceLoaderMaxage'=>[], 'ResourceLoaderDebug'=> false, 'ResourceLoaderMaxQueryLength'=> false, 'ResourceLoaderValidateJS'=> true, 'ResourceLoaderEnableJSProfiler'=> false, 'ResourceLoaderStorageEnabled'=> true, 'ResourceLoaderStorageVersion'=> 1, 'ResourceLoaderEnableSourceMapLinks'=> true, 'AllowSiteCSSOnRestrictedPages'=> false, 'VueDevelopmentMode'=> false, 'CodexDevelopmentDir'=> null, 'MetaNamespace'=> false, 'MetaNamespaceTalk'=> false, 'CanonicalNamespaceNames'=>[-2=> 'Media', -1=> 'Special', 0=> '', 1=> 'Talk', 2=> 'User', 3=> 'User_talk', 4=> 'Project', 5=> 'Project_talk', 6=> 'File', 7=> 'File_talk', 8=> 'MediaWiki', 9=> 'MediaWiki_talk', 10=> 'Template', 11=> 'Template_talk', 12=> 'Help', 13=> 'Help_talk', 14=> 'Category', 15=> 'Category_talk',], 'ExtraNamespaces'=>[], 'ExtraGenderNamespaces'=>[], 'NamespaceAliases'=>[], 'LegalTitleChars'=> ' %!"$&\'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF+', 'CapitalLinks' => true, 'CapitalLinkOverrides' => [ ], 'NamespacesWithSubpages' => [ 1 => true, 2 => true, 3 => true, 4 => true, 5 => true, 7 => true, 8 => true, 9 => true, 10 => true, 11 => true, 12 => true, 13 => true, 15 => true, ], 'ContentNamespaces' => [ 0, ], 'ShortPagesNamespaceExclusions' => [ ], 'ExtraSignatureNamespaces' => [ ], 'InvalidRedirectTargets' => [ 'Filepath', 'Mypage', 'Mytalk', 'Redirect', 'Mylog', ], 'DisableHardRedirects' => false, 'FixDoubleRedirects' => false, 'LocalInterwikis' => [ ], 'InterwikiExpiry' => 10800, 'InterwikiCache' => false, 'InterwikiScopes' => 3, 'InterwikiFallbackSite' => 'wiki', 'RedirectSources' => false, 'SiteTypes' => [ 'mediawiki' => 'MediaWiki\\Site\\MediaWikiSite', ], 'MaxTocLevel' => 999, 'MaxPPNodeCount' => 1000000, 'MaxTemplateDepth' => 100, 'MaxPPExpandDepth' => 100, 'UrlProtocols' => [ 'bitcoin:', 'ftp: 'ftps: 'geo:', 'git: 'gopher: 'http: 'https: 'irc: 'ircs: 'magnet:', 'mailto:', 'matrix:', 'mms: 'news:', 'nntp: 'redis: 'sftp: 'sip:', 'sips:', 'sms:', 'ssh: 'svn: 'tel:', 'telnet: 'urn:', 'wikipedia: 'worldwind: 'xmpp:', ' ], 'CleanSignatures' => true, 'AllowExternalImages' => false, 'AllowExternalImagesFrom' => '', 'EnableImageWhitelist' => false, 'TidyConfig' => [ ], 'ParsoidSettings' => [ 'useSelser' => true, ], 'ParsoidExperimentalParserFunctionOutput' => false, 'UseLegacyMediaStyles' => false, 'RawHtml' => false, 'ExternalLinkTarget' => false, 'NoFollowLinks' => true, 'NoFollowNsExceptions' => [ ], 'NoFollowDomainExceptions' => [ 'mediawiki.org', ], 'RegisterInternalExternals' => false, 'ExternalLinksIgnoreDomains' => [ ], 'AllowDisplayTitle' => true, 'RestrictDisplayTitle' => true, 'ExpensiveParserFunctionLimit' => 100, 'PreprocessorCacheThreshold' => 1000, 'EnableScaryTranscluding' => false, 'TranscludeCacheExpiry' => 3600, 'EnableMagicLinks' => [ 'ISBN' => false, 'PMID' => false, 'RFC' => false, ], 'ParserEnableUserLanguage' => false, 'ArticleCountMethod' => 'link', 'ActiveUserDays' => 30, 'LearnerEdits' => 10, 'LearnerMemberSince' => 4, 'ExperiencedUserEdits' => 500, 'ExperiencedUserMemberSince' => 30, 'ManualRevertSearchRadius' => 15, 'RevertedTagMaxDepth' => 15, 'CentralIdLookupProviders' => [ 'local' => [ 'class' => 'MediaWiki\\User\\CentralId\\LocalIdLookup', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', 'HideUserUtils', ], ], ], 'CentralIdLookupProvider' => 'local', 'UserRegistrationProviders' => [ 'local' => [ 'class' => 'MediaWiki\\User\\Registration\\LocalUserRegistrationProvider', 'services' => [ 'ConnectionProvider', ], ], ], 'PasswordPolicy' => [ 'policies' => [ 'bureaucrat' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'sysop' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'interface-admin' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'bot' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'default' => [ 'MinimalPasswordLength' => [ 'value' => 8, 'suggestChangeOnLogin' => true, ], 'PasswordCannotBeSubstringInUsername' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], 'PasswordCannotMatchDefaults' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], 'MaximalPasswordLength' => [ 'value' => 4096, 'suggestChangeOnLogin' => true, ], 'PasswordNotInCommonList' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], ], ], 'checks' => [ 'MinimalPasswordLength' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMinimalPasswordLength', ], 'MinimumPasswordLengthToLogin' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMinimumPasswordLengthToLogin', ], 'PasswordCannotBeSubstringInUsername' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordCannotBeSubstringInUsername', ], 'PasswordCannotMatchDefaults' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordCannotMatchDefaults', ], 'MaximalPasswordLength' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMaximalPasswordLength', ], 'PasswordNotInCommonList' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordNotInCommonList', ], ], ], 'AuthManagerConfig' => null, 'AuthManagerAutoConfig' => [ 'preauth' => [ 'MediaWiki\\Auth\\ThrottlePreAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\ThrottlePreAuthenticationProvider', 'sort' => 0, ], ], 'primaryauth' => [ 'MediaWiki\\Auth\\TemporaryPasswordPrimaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\TemporaryPasswordPrimaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', 'UserOptionsLookup', ], 'args' => [ [ 'authoritative' => false, ], ], 'sort' => 0, ], 'MediaWiki\\Auth\\LocalPasswordPrimaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\LocalPasswordPrimaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', ], 'args' => [ [ 'authoritative' => true, ], ], 'sort' => 100, ], ], 'secondaryauth' => [ 'MediaWiki\\Auth\\CheckBlocksSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\CheckBlocksSecondaryAuthenticationProvider', 'sort' => 0, ], 'MediaWiki\\Auth\\ResetPasswordSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\ResetPasswordSecondaryAuthenticationProvider', 'sort' => 100, ], 'MediaWiki\\Auth\\EmailNotificationSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\EmailNotificationSecondaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', ], 'sort' => 200, ], ], ], 'RememberMe' => 'choose', 'ReauthenticateTime' => [ 'default' => 3600, ], 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => [ 'default' => true, ], 'ChangeCredentialsBlacklist' => [ 'MediaWiki\\Auth\\TemporaryPasswordAuthenticationRequest', ], 'RemoveCredentialsBlacklist' => [ 'MediaWiki\\Auth\\PasswordAuthenticationRequest', ], 'InvalidPasswordReset' => true, 'PasswordDefault' => 'pbkdf2', 'PasswordConfig' => [ 'A' => [ 'class' => 'MediaWiki\\Password\\MWOldPassword', ], 'B' => [ 'class' => 'MediaWiki\\Password\\MWSaltedPassword', ], 'pbkdf2-legacyA' => [ 'class' => 'MediaWiki\\Password\\LayeredParameterizedPassword', 'types' => [ 'A', 'pbkdf2', ], ], 'pbkdf2-legacyB' => [ 'class' => 'MediaWiki\\Password\\LayeredParameterizedPassword', 'types' => [ 'B', 'pbkdf2', ], ], 'bcrypt' => [ 'class' => 'MediaWiki\\Password\\BcryptPassword', 'cost' => 9, ], 'pbkdf2' => [ 'class' => 'MediaWiki\\Password\\Pbkdf2PasswordUsingOpenSSL', 'algo' => 'sha512', 'cost' => '30000', 'length' => '64', ], 'argon2' => [ 'class' => 'MediaWiki\\Password\\Argon2Password', 'algo' => 'auto', ], ], 'PasswordResetRoutes' => [ 'username' => true, 'email' => true, ], 'MaxSigChars' => 255, 'SignatureValidation' => 'warning', 'SignatureAllowedLintErrors' => [ 'obsolete-tag', ], 'MaxNameChars' => 255, 'ReservedUsernames' => [ 'MediaWiki default', 'Conversion script', 'Maintenance script', 'Template namespace initialisation script', 'ScriptImporter', 'Delete page script', 'Move page script', 'Command line script', 'Unknown user', 'msg:double-redirect-fixer', 'msg:usermessage-editor', 'msg:proxyblocker', 'msg:sorbs', 'msg:spambot_username', 'msg:autochange-username', ], 'DefaultUserOptions' => [ 'ccmeonemails' => 0, 'date' => 'default', 'diffonly' => 0, 'diff-type' => 'table', 'disablemail' => 0, 'editfont' => 'monospace', 'editondblclick' => 0, 'editrecovery' => 0, 'editsectiononrightclick' => 0, 'email-allow-new-users' => 1, 'enotifminoredits' => 0, 'enotifrevealaddr' => 0, 'enotifusertalkpages' => 1, 'enotifwatchlistpages' => 1, 'extendwatchlist' => 1, 'fancysig' => 0, 'forceeditsummary' => 0, 'forcesafemode' => 0, 'gender' => 'unknown', 'hidecategorization' => 1, 'hideminor' => 0, 'hidepatrolled' => 0, 'imagesize' => 2, 'minordefault' => 0, 'newpageshidepatrolled' => 0, 'nickname' => '', 'norollbackdiff' => 0, 'prefershttps' => 1, 'previewonfirst' => 0, 'previewontop' => 1, 'pst-cssjs' => 1, 'rcdays' => 7, 'rcenhancedfilters-disable' => 0, 'rclimit' => 50, 'requireemail' => 0, 'search-match-redirect' => true, 'search-special-page' => 'Search', 'search-thumbnail-extra-namespaces' => true, 'searchlimit' => 20, 'showhiddencats' => 0, 'shownumberswatching' => 1, 'showrollbackconfirmation' => 0, 'skin' => false, 'skin-responsive' => 1, 'thumbsize' => 5, 'underline' => 2, 'useeditwarning' => 1, 'uselivepreview' => 0, 'usenewrc' => 1, 'watchcreations' => 1, 'watchcreations-expiry' => 'infinite', 'watchdefault' => 1, 'watchdefault-expiry' => 'infinite', 'watchdeletion' => 0, 'watchlistdays' => 7, 'watchlisthideanons' => 0, 'watchlisthidebots' => 0, 'watchlisthidecategorization' => 1, 'watchlisthideliu' => 0, 'watchlisthideminor' => 0, 'watchlisthideown' => 0, 'watchlisthidepatrolled' => 0, 'watchlistreloadautomatically' => 0, 'watchlistunwatchlinks' => 0, 'watchmoves' => 0, 'watchrollback' => 0, 'watchuploads' => 1, 'watchrollback-expiry' => 'infinite', 'watchstar-expiry' => 'infinite', 'wlenhancedfilters-disable' => 0, 'wllimit' => 250, ], 'ConditionalUserOptions' => [ ], 'HiddenPrefs' => [ ], 'UserJsPrefLimit' => 100, 'InvalidUsernameCharacters' => '@:>=', 'UserrightsInterwikiDelimiter' => '@', 'SecureLogin' => false, 'AuthenticationTokenVersion' => null, 'SessionProviders' => [ 'MediaWiki\\Session\\CookieSessionProvider' => [ 'class' => 'MediaWiki\\Session\\CookieSessionProvider', 'args' => [ [ 'priority' => 30, ], ], 'services' => [ 'JwtCodec', 'UrlUtils', ], ], 'MediaWiki\\Session\\BotPasswordSessionProvider' => [ 'class' => 'MediaWiki\\Session\\BotPasswordSessionProvider', 'args' => [ [ 'priority' => 75, ], ], 'services' => [ 'GrantsInfo', ], ], ], 'AutoCreateTempUser' => [ 'known' => false, 'enabled' => false, 'actions' => [ 'edit', ], 'genPattern' => '~$1', 'matchPattern' => null, 'reservedPattern' => '~$1', 'serialProvider' => [ 'type' => 'local', 'useYear' => true, ], 'serialMapping' => [ 'type' => 'readable-numeric', ], 'expireAfterDays' => 90, 'notifyBeforeExpirationDays' => 10, ], 'AutoblockExemptions' => [ ], 'AutoblockExpiry' => 86400, 'BlockAllowsUTEdit' => true, 'BlockCIDRLimit' => [ 'IPv4' => 16, 'IPv6' => 19, ], 'BlockDisablesLogin' => false, 'EnableMultiBlocks' => false, 'WhitelistRead' => false, 'WhitelistReadRegexp' => false, 'EmailConfirmToEdit' => false, 'HideIdentifiableRedirects' => true, 'GroupPermissions' => [ '*' => [ 'createaccount' => true, 'read' => true, 'edit' => true, 'createpage' => true, 'createtalk' => true, 'viewmyprivateinfo' => true, 'editmyprivateinfo' => true, 'editmyoptions' => true, ], 'user' => [ 'move' => true, 'move-subpages' => true, 'move-rootuserpages' => true, 'move-categorypages' => true, 'movefile' => true, 'read' => true, 'edit' => true, 'createpage' => true, 'createtalk' => true, 'upload' => true, 'reupload' => true, 'reupload-shared' => true, 'minoredit' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, 'editmyuserjsredirect' => true, 'sendemail' => true, 'applychangetags' => true, 'changetags' => true, 'viewmywatchlist' => true, 'editmywatchlist' => true, ], 'autoconfirmed' => [ 'autoconfirmed' => true, 'editsemiprotected' => true, ], 'bot' => [ 'bot' => true, 'autoconfirmed' => true, 'editsemiprotected' => true, 'nominornewtalk' => true, 'autopatrol' => true, 'suppressredirect' => true, 'apihighlimits' => true, ], 'sysop' => [ 'block' => true, 'createaccount' => true, 'delete' => true, 'bigdelete' => true, 'deletedhistory' => true, 'deletedtext' => true, 'undelete' => true, 'editcontentmodel' => true, 'editinterface' => true, 'editsitejson' => true, 'edituserjson' => true, 'import' => true, 'importupload' => true, 'move' => true, 'move-subpages' => true, 'move-rootuserpages' => true, 'move-categorypages' => true, 'patrol' => true, 'autopatrol' => true, 'protect' => true, 'editprotected' => true, 'rollback' => true, 'upload' => true, 'reupload' => true, 'reupload-shared' => true, 'unwatchedpages' => true, 'autoconfirmed' => true, 'editsemiprotected' => true, 'ipblock-exempt' => true, 'blockemail' => true, 'markbotedits' => true, 'apihighlimits' => true, 'browsearchive' => true, 'noratelimit' => true, 'movefile' => true, 'unblockself' => true, 'suppressredirect' => true, 'mergehistory' => true, 'managechangetags' => true, 'deletechangetags' => true, ], 'interface-admin' => [ 'editinterface' => true, 'editsitecss' => true, 'editsitejson' => true, 'editsitejs' => true, 'editusercss' => true, 'edituserjson' => true, 'edituserjs' => true, ], 'bureaucrat' => [ 'userrights' => true, 'noratelimit' => true, 'renameuser' => true, ], 'suppress' => [ 'hideuser' => true, 'suppressrevision' => true, 'viewsuppressed' => true, 'suppressionlog' => true, 'deleterevision' => true, 'deletelogentry' => true, ], ], 'PrivilegedGroups' => [ 'bureaucrat', 'interface-admin', 'suppress', 'sysop', ], 'RevokePermissions' => [ ], 'GroupInheritsPermissions' => [ ], 'ImplicitGroups' => [ '*', 'user', 'autoconfirmed', ], 'GroupsAddToSelf' => [ ], 'GroupsRemoveFromSelf' => [ ], 'RestrictedGroups' => [ ], 'UserRequirementsPrivateConditions' => [ ], 'RestrictionTypes' => [ 'create', 'edit', 'move', 'upload', ], 'RestrictionLevels' => [ '', 'autoconfirmed', 'sysop', ], 'CascadingRestrictionLevels' => [ 'sysop', ], 'SemiprotectedRestrictionLevels' => [ 'autoconfirmed', ], 'NamespaceProtection' => [ ], 'NonincludableNamespaces' => [ ], 'AutoConfirmAge' => 0, 'AutoConfirmCount' => 0, 'Autopromote' => [ 'autoconfirmed' => [ '&', [ 1, null, ], [ 2, null, ], ], ], 'AutopromoteOnce' => [ 'onEdit' => [ ], ], 'AutopromoteOnceLogInRC' => true, 'AutopromoteOnceRCExcludedGroups' => [ ], 'AddGroups' => [ ], 'RemoveGroups' => [ ], 'AvailableRights' => [ ], 'ImplicitRights' => [ ], 'DeleteRevisionsLimit' => 0, 'DeleteRevisionsBatchSize' => 1000, 'HideUserContribLimit' => 1000, 'AccountCreationThrottle' => [ [ 'count' => 0, 'seconds' => 86400, ], ], 'TempAccountCreationThrottle' => [ [ 'count' => 1, 'seconds' => 600, ], [ 'count' => 6, 'seconds' => 86400, ], ], 'TempAccountNameAcquisitionThrottle' => [ [ 'count' => 60, 'seconds' => 86400, ], ], 'SpamRegex' => [ ], 'SummarySpamRegex' => [ ], 'EnableDnsBlacklist' => false, 'DnsBlacklistUrls' => [ ], 'ProxyList' => [ ], 'ProxyWhitelist' => [ ], 'SoftBlockRanges' => [ ], 'ApplyIpBlocksToXff' => false, 'RateLimits' => [ 'edit' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], 'user' => [ 90, 60, ], ], 'move' => [ 'newbie' => [ 2, 120, ], 'user' => [ 8, 60, ], ], 'upload' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], ], 'rollback' => [ 'user' => [ 10, 60, ], 'newbie' => [ 5, 120, ], ], 'mailpassword' => [ 'ip' => [ 5, 3600, ], ], 'sendemail' => [ 'ip' => [ 5, 86400, ], 'newbie' => [ 5, 86400, ], 'user' => [ 20, 86400, ], ], 'changeemail' => [ 'ip-all' => [ 10, 3600, ], 'user' => [ 4, 86400, ], ], 'confirmemail' => [ 'ip-all' => [ 10, 3600, ], 'user' => [ 4, 86400, ], ], 'purge' => [ 'ip' => [ 30, 60, ], 'user' => [ 30, 60, ], ], 'linkpurge' => [ 'ip' => [ 30, 60, ], 'user' => [ 30, 60, ], ], 'renderfile' => [ 'ip' => [ 700, 30, ], 'user' => [ 700, 30, ], ], 'renderfile-nonstandard' => [ 'ip' => [ 70, 30, ], 'user' => [ 70, 30, ], ], 'stashedit' => [ 'ip' => [ 30, 60, ], 'newbie' => [ 30, 60, ], ], 'stashbasehtml' => [ 'ip' => [ 5, 60, ], 'newbie' => [ 5, 60, ], ], 'changetags' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], ], 'editcontentmodel' => [ 'newbie' => [ 2, 120, ], 'user' => [ 8, 60, ], ], ], 'RateLimitsExcludedIPs' => [ ], 'PutIPinRC' => true, 'QueryPageDefaultLimit' => 50, 'ExternalQuerySources' => [ ], 'PasswordAttemptThrottle' => [ [ 'count' => 5, 'seconds' => 300, ], [ 'count' => 150, 'seconds' => 172800, ], ], 'GrantPermissions' => [ 'basic' => [ 'autocreateaccount' => true, 'autoconfirmed' => true, 'autopatrol' => true, 'editsemiprotected' => true, 'ipblock-exempt' => true, 'nominornewtalk' => true, 'patrolmarks' => true, 'read' => true, 'unwatchedpages' => true, ], 'highvolume' => [ 'bot' => true, 'apihighlimits' => true, 'noratelimit' => true, 'markbotedits' => true, ], 'import' => [ 'import' => true, 'importupload' => true, ], 'editpage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'pagelang' => true, ], 'editprotected' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editprotected' => true, ], 'editmycssjs' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, ], 'editmyoptions' => [ 'editmyoptions' => true, 'editmyuserjson' => true, ], 'editinterface' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, ], 'editsiteconfig' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, 'editusercss' => true, 'edituserjs' => true, 'editsitecss' => true, 'editsitejs' => true, ], 'createeditmovepage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createpage' => true, 'createtalk' => true, 'delete-redirect' => true, 'move' => true, 'move-rootuserpages' => true, 'move-subpages' => true, 'move-categorypages' => true, 'suppressredirect' => true, ], 'uploadfile' => [ 'upload' => true, 'reupload-own' => true, ], 'uploadeditmovefile' => [ 'upload' => true, 'reupload-own' => true, 'reupload' => true, 'reupload-shared' => true, 'upload_by_url' => true, 'movefile' => true, 'suppressredirect' => true, ], 'patrol' => [ 'patrol' => true, ], 'rollback' => [ 'rollback' => true, ], 'blockusers' => [ 'block' => true, 'blockemail' => true, ], 'viewdeleted' => [ 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, ], 'viewrestrictedlogs' => [ 'suppressionlog' => true, ], 'delete' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, 'delete' => true, 'bigdelete' => true, 'deletelogentry' => true, 'deleterevision' => true, 'undelete' => true, ], 'oversight' => [ 'suppressrevision' => true, 'viewsuppressed' => true, ], 'protect' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editprotected' => true, 'protect' => true, ], 'viewmywatchlist' => [ 'viewmywatchlist' => true, ], 'editmywatchlist' => [ 'editmywatchlist' => true, ], 'sendemail' => [ 'sendemail' => true, ], 'createaccount' => [ 'createaccount' => true, ], 'privateinfo' => [ 'viewmyprivateinfo' => true, ], 'mergehistory' => [ 'mergehistory' => true, ], ], 'GrantPermissionGroups' => [ 'basic' => 'hidden', 'editpage' => 'page-interaction', 'createeditmovepage' => 'page-interaction', 'editprotected' => 'page-interaction', 'patrol' => 'page-interaction', 'uploadfile' => 'file-interaction', 'uploadeditmovefile' => 'file-interaction', 'sendemail' => 'email', 'viewmywatchlist' => 'watchlist-interaction', 'editviewmywatchlist' => 'watchlist-interaction', 'editmycssjs' => 'customization', 'editmyoptions' => 'customization', 'editinterface' => 'administration', 'editsiteconfig' => 'administration', 'rollback' => 'administration', 'blockusers' => 'administration', 'delete' => 'administration', 'viewdeleted' => 'administration', 'viewrestrictedlogs' => 'administration', 'protect' => 'administration', 'oversight' => 'administration', 'createaccount' => 'administration', 'mergehistory' => 'administration', 'import' => 'administration', 'highvolume' => 'high-volume', 'privateinfo' => 'private-information', ], 'GrantRiskGroups' => [ 'basic' => 'low', 'editpage' => 'low', 'createeditmovepage' => 'low', 'editprotected' => 'vandalism', 'patrol' => 'low', 'uploadfile' => 'low', 'uploadeditmovefile' => 'low', 'sendemail' => 'security', 'viewmywatchlist' => 'low', 'editviewmywatchlist' => 'low', 'editmycssjs' => 'security', 'editmyoptions' => 'security', 'editinterface' => 'vandalism', 'editsiteconfig' => 'security', 'rollback' => 'low', 'blockusers' => 'vandalism', 'delete' => 'vandalism', 'viewdeleted' => 'vandalism', 'viewrestrictedlogs' => 'security', 'protect' => 'vandalism', 'oversight' => 'security', 'createaccount' => 'low', 'mergehistory' => 'vandalism', 'import' => 'security', 'highvolume' => 'low', 'privateinfo' => 'low', ], 'EnableBotPasswords' => true, 'BotPasswordsCluster' => false, 'BotPasswordsDatabase' => false, 'BotPasswordsLimit' => 100, 'SecretKey' => false, 'JwtPrivateKey' => false, 'JwtPublicKey' => false, 'AllowUserJs' => false, 'AllowUserCss' => false, 'AllowUserCssPrefs' => true, 'UseSiteJs' => true, 'UseSiteCss' => true, 'BreakFrames' => false, 'EditPageFrameOptions' => 'DENY', 'ApiFrameOptions' => 'DENY', 'CSPHeader' => false, 'CSPReportOnlyHeader' => false, 'CSPFalsePositiveUrls' => [ 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'chrome-extension' => true, ], 'AllowCrossOrigin' => false, 'RestAllowCrossOriginCookieAuth' => false, 'SessionSecret' => false, 'CookieExpiration' => 2592000, 'ExtendedLoginCookieExpiration' => 15552000, 'SessionCookieJwtExpiration' => 14400, 'CookieDomain' => '', 'CookiePath' => '/', 'CookieSecure' => 'detect', 'CookiePrefix' => false, 'CookieHttpOnly' => true, 'CookieSameSite' => null, 'CacheVaryCookies' => [ ], 'SessionName' => false, 'CookieSetOnAutoblock' => true, 'CookieSetOnIpBlock' => true, 'DebugLogFile' => '', 'DebugLogPrefix' => '', 'DebugRedirects' => false, 'DebugRawPage' => false, 'DebugComments' => false, 'DebugDumpSql' => false, 'TrxProfilerLimits' => [ 'GET' => [ 'masterConns' => 0, 'writes' => 0, 'readQueryTime' => 5, 'readQueryRows' => 10000, ], 'POST' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 100000, 'maxAffected' => 1000, ], 'POST-nonwrite' => [ 'writes' => 0, 'readQueryTime' => 5, 'readQueryRows' => 10000, ], 'PostSend-GET' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 10000, 'maxAffected' => 1000, 'masterConns' => 0, 'writes' => 0, ], 'PostSend-POST' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 100000, 'maxAffected' => 1000, ], 'JobRunner' => [ 'readQueryTime' => 30, 'writeQueryTime' => 5, 'readQueryRows' => 100000, 'maxAffected' => 500, ], 'Maintenance' => [ 'writeQueryTime' => 5, 'maxAffected' => 1000, ], ], 'DebugLogGroups' => [ ], 'MWLoggerDefaultSpi' => [ 'class' => 'MediaWiki\\Logger\\LegacySpi', ], 'ShowDebug' => false, 'SpecialVersionShowHooks' => false, 'ShowExceptionDetails' => false, 'LogExceptionBacktrace' => true, 'PropagateErrors' => true, 'ShowHostnames' => false, 'OverrideHostname' => false, 'DevelopmentWarnings' => false, 'DeprecationReleaseLimit' => false, 'Profiler' => [ ], 'StatsdServer' => false, 'StatsdMetricPrefix' => 'MediaWiki', 'StatsTarget' => null, 'StatsFormat' => null, 'StatsPrefix' => 'mediawiki', 'OpenTelemetryConfig' => null, 'PageInfoTransclusionLimit' => 50, 'EnableJavaScriptTest' => false, 'CachePrefix' => false, 'DebugToolbar' => false, 'DisableTextSearch' => false, 'AdvancedSearchHighlighting' => false, 'SearchHighlightBoundaries' => '[\\p{Z}\\p{P}\\p{C}]', 'OpenSearchTemplates' => [ 'application/x-suggestions+json' => false, 'application/x-suggestions+xml' => false, ], 'OpenSearchDefaultLimit' => 10, 'OpenSearchDescriptionLength' => 100, 'SearchSuggestCacheExpiry' => 1200, 'DisableSearchUpdate' => false, 'NamespacesToBeSearchedDefault' => [ true, ], 'DisableInternalSearch' => false, 'SearchForwardUrl' => null, 'SitemapNamespaces' => false, 'SitemapNamespacesPriorities' => false, 'SitemapApiConfig' => [ ], 'SpecialSearchFormOptions' => [ ], 'SearchMatchRedirectPreference' => false, 'SearchRunSuggestedQuery' => true, 'Diff3' => '/usr/bin/diff3', 'Diff' => '/usr/bin/diff', 'PreviewOnOpenNamespaces' => [ 14 => true, ], 'UniversalEditButton' => true, 'UseAutomaticEditSummaries' => true, 'CommandLineDarkBg' => false, 'ReadOnly' => null, 'ReadOnlyWatchedItemStore' => false, 'ReadOnlyFile' => false, 'UpgradeKey' => false, 'GitBin' => '/usr/bin/git', 'GitRepositoryViewers' => [ 'https: 'ssh: ], 'InstallerInitialPages' => [ [ 'titlemsg' => 'mainpage', 'text' => '{{subst:int:mainpagetext}}{{subst:int:mainpagedocfooter}}', ], ], 'RCMaxAge' => 7776000, 'WatchersMaxAge' => 15552000, 'UnwatchedPageSecret' => 1, 'RCFilterByAge' => false, 'RCLinkLimits' => [ 50, 100, 250, 500, ], 'RCLinkDays' => [ 1, 3, 7, 14, 30, ], 'RCFeeds' => [ ], 'RCEngines' => [ 'redis' => 'MediaWiki\\RCFeed\\RedisPubSubFeedEngine', 'udp' => 'MediaWiki\\RCFeed\\UDPRCFeedEngine', ], 'RCWatchCategoryMembership' => false, 'UseRCPatrol' => true, 'StructuredChangeFiltersLiveUpdatePollingRate' => 3, 'UseNPPatrol' => true, 'UseFilePatrol' => true, 'Feed' => true, 'FeedLimit' => 50, 'FeedCacheTimeout' => 60, 'FeedDiffCutoff' => 32768, 'OverrideSiteFeed' => [ ], 'FeedClasses' => [ 'rss' => 'MediaWiki\\Feed\\RSSFeed', 'atom' => 'MediaWiki\\Feed\\AtomFeed', ], 'AdvertisedFeedTypes' => [ 'atom', ], 'RCShowWatchingUsers' => false, 'RCShowChangedSize' => true, 'RCChangedSizeThreshold' => 500, 'ShowUpdatedMarker' => true, 'DisableAnonTalk' => false, 'UseTagFilter' => true, 'SoftwareTags' => [ 'mw-contentmodelchange' => true, 'mw-new-redirect' => true, 'mw-removed-redirect' => true, 'mw-changed-redirect-target' => true, 'mw-blank' => true, 'mw-replace' => true, 'mw-recreated' => true, 'mw-rollback' => true, 'mw-undo' => true, 'mw-manual-revert' => true, 'mw-reverted' => true, 'mw-server-side-upload' => true, 'mw-ipblock-appeal' => true, ], 'UnwatchedPageThreshold' => false, 'RecentChangesFlags' => [ 'newpage' => [ 'letter' => 'newpageletter', 'title' => 'recentchanges-label-newpage', 'legend' => 'recentchanges-legend-newpage', 'grouping' => 'any', ], 'minor' => [ 'letter' => 'minoreditletter', 'title' => 'recentchanges-label-minor', 'legend' => 'recentchanges-legend-minor', 'class' => 'minoredit', 'grouping' => 'all', ], 'bot' => [ 'letter' => 'boteditletter', 'title' => 'recentchanges-label-bot', 'legend' => 'recentchanges-legend-bot', 'class' => 'botedit', 'grouping' => 'all', ], 'unpatrolled' => [ 'letter' => 'unpatrolledletter', 'title' => 'recentchanges-label-unpatrolled', 'legend' => 'recentchanges-legend-unpatrolled', 'grouping' => 'any', ], ], 'WatchlistExpiry' => false, 'EnableWatchlistLabels' => false, 'WatchlistLabelsMaxPerUser' => 100, 'WatchlistPurgeRate' => 0.1, 'WatchlistExpiryMaxDuration' => '1 year', 'EnableChangesListQueryPartitioning' => false, 'RightsPage' => null, 'RightsUrl' => null, 'RightsText' => null, 'RightsIcon' => null, 'UseCopyrightUpload' => false, 'MaxCredits' => 0, 'ShowCreditsIfMax' => true, 'ImportSources' => [ ], 'ImportTargetNamespace' => null, 'ExportAllowHistory' => true, 'ExportMaxHistory' => 0, 'ExportAllowListContributors' => false, 'ExportMaxLinkDepth' => 0, 'ExportFromNamespaces' => false, 'ExportAllowAll' => false, 'ExportPagelistLimit' => 5000, 'XmlDumpSchemaVersion' => '0.11', 'WikiFarmSettingsDirectory' => null, 'WikiFarmSettingsExtension' => 'yaml', 'ExtensionFunctions' => [ ], 'ExtensionMessagesFiles' => [ ], 'MessagesDirs' => [ ], 'TranslationAliasesDirs' => [ ], 'ExtensionEntryPointListFiles' => [ ], 'EnableParserLimitReporting' => true, 'ValidSkinNames' => [ ], 'SpecialPages' => [ ], 'ExtensionCredits' => [ ], 'Hooks' => [ ], 'ServiceWiringFiles' => [ ], 'JobClasses' => [ 'deletePage' => 'MediaWiki\\Page\\DeletePageJob', 'refreshLinks' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'deleteLinks' => 'MediaWiki\\Page\\DeleteLinksJob', 'htmlCacheUpdate' => 'MediaWiki\\JobQueue\\Jobs\\HTMLCacheUpdateJob', 'sendMail' => [ 'class' => 'MediaWiki\\Mail\\EmaillingJob', 'services' => [ 'Emailer', ], ], 'enotifNotify' => [ 'class' => 'MediaWiki\\RecentChanges\\RecentChangeNotifyJob', 'services' => [ 'RecentChangeLookup', ], ], 'fixDoubleRedirect' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\DoubleRedirectJob', 'services' => [ 'RevisionLookup', 'MagicWordFactory', 'WikiPageFactory', ], 'needsPage' => true, ], 'AssembleUploadChunks' => 'MediaWiki\\JobQueue\\Jobs\\AssembleUploadChunksJob', 'PublishStashedFile' => 'MediaWiki\\JobQueue\\Jobs\\PublishStashedFileJob', 'ThumbnailRender' => 'MediaWiki\\JobQueue\\Jobs\\ThumbnailRenderJob', 'UploadFromUrl' => 'MediaWiki\\JobQueue\\Jobs\\UploadFromUrlJob', 'recentChangesUpdate' => 'MediaWiki\\RecentChanges\\RecentChangesUpdateJob', 'refreshLinksPrioritized' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'refreshLinksDynamic' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'activityUpdateJob' => 'MediaWiki\\Watchlist\\ActivityUpdateJob', 'categoryMembershipChange' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\CategoryMembershipChangeJob', 'services' => [ 'RecentChangeFactory', ], ], 'CategoryCountUpdateJob' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\CategoryCountUpdateJob', 'services' => [ 'ConnectionProvider', 'NamespaceInfo', ], ], 'clearUserWatchlist' => 'MediaWiki\\Watchlist\\ClearUserWatchlistJob', 'watchlistExpiry' => 'MediaWiki\\Watchlist\\WatchlistExpiryJob', 'cdnPurge' => 'MediaWiki\\JobQueue\\Jobs\\CdnPurgeJob', 'userGroupExpiry' => 'MediaWiki\\User\\UserGroupExpiryJob', 'clearWatchlistNotifications' => 'MediaWiki\\Watchlist\\ClearWatchlistNotificationsJob', 'userOptionsUpdate' => 'MediaWiki\\User\\Options\\UserOptionsUpdateJob', 'revertedTagUpdate' => 'MediaWiki\\JobQueue\\Jobs\\RevertedTagUpdateJob', 'null' => 'MediaWiki\\JobQueue\\Jobs\\NullJob', 'userEditCountInit' => 'MediaWiki\\User\\UserEditCountInitJob', 'parsoidCachePrewarm' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\ParsoidCachePrewarmJob', 'services' => [ 'ParserOutputAccess', 'PageStore', 'RevisionLookup', 'ParsoidSiteConfig', ], 'needsPage' => false, ], 'renameUserTable' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserTableJob', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', ], ], 'renameUserDerived' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserDerivedJob', 'services' => [ 'RenameUserFactory', 'UserFactory', ], ], 'renameUser' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserTableJob', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', ], ], ], 'JobTypesExcludedFromDefaultQueue' => [ 'AssembleUploadChunks', 'PublishStashedFile', 'UploadFromUrl', ], 'JobBackoffThrottling' => [ ], 'JobTypeConf' => [ 'default' => [ 'class' => 'MediaWiki\\JobQueue\\JobQueueDB', 'order' => 'random', 'claimTTL' => 3600, ], ], 'JobQueueIncludeInMaxLagFactor' => false, 'SpecialPageCacheUpdates' => [ 'Statistics' => [ 'MediaWiki\\Deferred\\SiteStatsUpdate', 'cacheUpdate', ], ], 'PagePropLinkInvalidations' => [ 'hiddencat' => 'categorylinks', ], 'CategoryMagicGallery' => true, 'CategoryPagingLimit' => 200, 'CategoryCollation' => 'uppercase', 'TempCategoryCollations' => [ ], 'SortedCategories' => false, 'TrackingCategories' => [ ], 'LogTypes' => [ '', 'block', 'protect', 'rights', 'delete', 'upload', 'move', 'import', 'interwiki', 'patrol', 'merge', 'suppress', 'tag', 'managetags', 'contentmodel', 'renameuser', ], 'LogRestrictions' => [ 'suppress' => 'suppressionlog', ], 'FilterLogTypes' => [ 'patrol' => true, 'tag' => true, 'newusers' => false, ], 'LogNames' => [ '' => 'all-logs-page', 'block' => 'blocklogpage', 'protect' => 'protectlogpage', 'rights' => 'rightslog', 'delete' => 'dellogpage', 'upload' => 'uploadlogpage', 'move' => 'movelogpage', 'import' => 'importlogpage', 'patrol' => 'patrol-log-page', 'merge' => 'mergelog', 'suppress' => 'suppressionlog', ], 'LogHeaders' => [ '' => 'alllogstext', 'block' => 'blocklogtext', 'delete' => 'dellogpagetext', 'import' => 'importlogpagetext', 'merge' => 'mergelogpagetext', 'move' => 'movelogpagetext', 'patrol' => 'patrol-log-header', 'protect' => 'protectlogtext', 'rights' => 'rightslogtext', 'suppress' => 'suppressionlogtext', 'upload' => 'uploadlogpagetext', ], 'LogActions' => [ ], 'LogActionsHandlers' => [ 'block/block' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'block/reblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'block/unblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'contentmodel/change' => 'MediaWiki\\Logging\\ContentModelLogFormatter', 'contentmodel/new' => 'MediaWiki\\Logging\\ContentModelLogFormatter', 'delete/delete' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/delete_redir' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/delete_redir2' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/event' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/restore' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/revision' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'import/interwiki' => 'MediaWiki\\Logging\\ImportLogFormatter', 'import/upload' => 'MediaWiki\\Logging\\ImportLogFormatter', 'interwiki/iw_add' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'interwiki/iw_delete' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'interwiki/iw_edit' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'managetags/activate' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/create' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/deactivate' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/delete' => 'MediaWiki\\Logging\\LogFormatter', 'merge/merge' => [ 'class' => 'MediaWiki\\Logging\\MergeLogFormatter', 'services' => [ 'TitleParser', ], ], 'merge/merge-into' => [ 'class' => 'MediaWiki\\Logging\\MergeLogFormatter', 'services' => [ 'TitleParser', ], ], 'move/move' => [ 'class' => 'MediaWiki\\Logging\\MoveLogFormatter', 'services' => [ 'TitleParser', ], ], 'move/move_redir' => [ 'class' => 'MediaWiki\\Logging\\MoveLogFormatter', 'services' => [ 'TitleParser', ], ], 'patrol/patrol' => 'MediaWiki\\Logging\\PatrolLogFormatter', 'patrol/autopatrol' => 'MediaWiki\\Logging\\PatrolLogFormatter', 'protect/modify' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/move_prot' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/protect' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/unprotect' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'renameuser/renameuser' => [ 'class' => 'MediaWiki\\Logging\\RenameuserLogFormatter', 'services' => [ 'TitleParser', ], ], 'rights/autopromote' => 'MediaWiki\\Logging\\RightsLogFormatter', 'rights/rights' => 'MediaWiki\\Logging\\RightsLogFormatter', 'suppress/block' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'suppress/delete' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'suppress/event' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'suppress/reblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'suppress/revision' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'tag/update' => 'MediaWiki\\Logging\\TagLogFormatter', 'upload/overwrite' => 'MediaWiki\\Logging\\UploadLogFormatter', 'upload/revert' => 'MediaWiki\\Logging\\UploadLogFormatter', 'upload/upload' => 'MediaWiki\\Logging\\UploadLogFormatter', ], 'ActionFilteredLogs' => [ 'block' => [ 'block' => [ 'block', ], 'reblock' => [ 'reblock', ], 'unblock' => [ 'unblock', ], ], 'contentmodel' => [ 'change' => [ 'change', ], 'new' => [ 'new', ], ], 'delete' => [ 'delete' => [ 'delete', ], 'delete_redir' => [ 'delete_redir', 'delete_redir2', ], 'restore' => [ 'restore', ], 'event' => [ 'event', ], 'revision' => [ 'revision', ], ], 'import' => [ 'interwiki' => [ 'interwiki', ], 'upload' => [ 'upload', ], ], 'managetags' => [ 'create' => [ 'create', ], 'delete' => [ 'delete', ], 'activate' => [ 'activate', ], 'deactivate' => [ 'deactivate', ], ], 'move' => [ 'move' => [ 'move', ], 'move_redir' => [ 'move_redir', ], ], 'newusers' => [ 'create' => [ 'create', 'newusers', ], 'create2' => [ 'create2', ], 'autocreate' => [ 'autocreate', ], 'byemail' => [ 'byemail', ], ], 'protect' => [ 'protect' => [ 'protect', ], 'modify' => [ 'modify', ], 'unprotect' => [ 'unprotect', ], 'move_prot' => [ 'move_prot', ], ], 'rights' => [ 'rights' => [ 'rights', ], 'autopromote' => [ 'autopromote', ], ], 'suppress' => [ 'event' => [ 'event', ], 'revision' => [ 'revision', ], 'delete' => [ 'delete', ], 'block' => [ 'block', ], 'reblock' => [ 'reblock', ], ], 'upload' => [ 'upload' => [ 'upload', ], 'overwrite' => [ 'overwrite', ], 'revert' => [ 'revert', ], ], ], 'NewUserLog' => true, 'PageCreationLog' => true, 'AllowSpecialInclusion' => true, 'DisableQueryPageUpdate' => false, 'CountCategorizedImagesAsUsed' => false, 'MaxRedirectLinksRetrieved' => 500, 'RangeContributionsCIDRLimit' => [ 'IPv4' => 16, 'IPv6' => 32, ], 'Actions' => [ ], 'DefaultRobotPolicy' => 'index,follow', 'NamespaceRobotPolicies' => [ ], 'ArticleRobotPolicies' => [ ], 'ExemptFromUserRobotsControl' => null, 'DebugAPI' => false, 'APIModules' => [ ], 'APIFormatModules' => [ ], 'APIMetaModules' => [ ], 'APIPropModules' => [ ], 'APIListModules' => [ ], 'APIMaxDBRows' => 5000, 'APIMaxResultSize' => 8388608, 'APIMaxUncachedDiffs' => 1, 'APIMaxLagThreshold' => 7, 'APICacheHelpTimeout' => 3600, 'APIUselessQueryPages' => [ 'MIMEsearch', 'LinkSearch', ], 'AjaxLicensePreview' => true, 'CrossSiteAJAXdomains' => [ ], 'CrossSiteAJAXdomainExceptions' => [ ], 'AllowedCorsHeaders' => [ 'Accept', 'Accept-Language', 'Content-Language', 'Content-Type', 'Accept-Encoding', 'DNT', 'Origin', 'User-Agent', 'Api-User-Agent', 'Access-Control-Max-Age', 'Authorization', ], 'RestAPIAdditionalRouteFiles' => [ ], 'RestSandboxSpecs' => [ ], 'MaxShellMemory' => 307200, 'MaxShellFileSize' => 102400, 'MaxShellTime' => 180, 'MaxShellWallClockTime' => 180, 'ShellCgroup' => false, 'PhpCli' => '/usr/bin/php', 'ShellRestrictionMethod' => 'autodetect', 'ShellboxUrls' => [ 'default' => null, ], 'ShellboxSecretKey' => null, 'ShellboxShell' => '/bin/sh', 'HTTPTimeout' => 25, 'HTTPConnectTimeout' => 5.0, 'HTTPMaxTimeout' => 0, 'HTTPMaxConnectTimeout' => 0, 'HTTPImportTimeout' => 25, 'AsyncHTTPTimeout' => 25, 'HTTPProxy' => '', 'LocalVirtualHosts' => [ ], 'LocalHTTPProxy' => false, 'AllowExternalReqID' => false, 'JobRunRate' => 1, 'RunJobsAsync' => false, 'UpdateRowsPerJob' => 300, 'UpdateRowsPerQuery' => 100, 'RedirectOnLogin' => null, 'VirtualRestConfig' => [ 'paths' => [ ], 'modules' => [ ], 'global' => [ 'timeout' => 360, 'forwardCookies' => false, 'HTTPProxy' => null, ], ], 'EventRelayerConfig' => [ 'default' => [ 'class' => 'Wikimedia\\EventRelayer\\EventRelayerNull', ], ], 'Pingback' => false, 'OriginTrials' => [ ], 'ReportToExpiry' => 86400, 'ReportToEndpoints' => [ ], 'FeaturePolicyReportOnly' => [ ], 'SkinsPreferred' => [ 'vector-2022', 'vector', ], 'SpecialContributeSkinsEnabled' => [ ], 'SpecialContributeNewPageTarget' => null, 'EnableEditRecovery' => false, 'EditRecoveryExpiry' => 2592000, 'UseCodexSpecialBlock' => false, 'ShowLogoutConfirmation' => false, 'EnableProtectionIndicators' => true, 'OutputPipelineStages' => [ ], 'FeatureShutdown' => [ ], 'CloneArticleParserOutput' => true, 'UseLeximorph' => false, 'UsePostprocCache' => false, 'UsePostprocCacheLegacy' => false, 'UsePostprocCacheParsoid' => false, 'ParserOptionsLogUnsafeSampleRate' => 0, ], 'type' => [ 'ConfigRegistry' => 'object', 'AssumeProxiesUseDefaultProtocolPorts' => 'boolean', 'ForceHTTPS' => 'boolean', 'ExtensionDirectory' => [ 'string', 'null', ], 'StyleDirectory' => [ 'string', 'null', ], 'UploadDirectory' => [ 'string', 'boolean', 'null', ], 'Logos' => [ 'object', 'boolean', ], 'ReferrerPolicy' => [ 'array', 'string', 'boolean', ], 'ActionPaths' => 'object', 'MainPageIsDomainRoot' => 'boolean', 'ImgAuthUrlPathMap' => 'object', 'LocalFileRepo' => 'object', 'ForeignFileRepos' => 'array', 'UseSharedUploads' => 'boolean', 'SharedUploadDirectory' => [ 'string', 'null', ], 'SharedUploadPath' => [ 'string', 'null', ], 'HashedSharedUploadDirectory' => 'boolean', 'FetchCommonsDescriptions' => 'boolean', 'SharedUploadDBname' => [ 'boolean', 'string', ], 'SharedUploadDBprefix' => 'string', 'CacheSharedUploads' => 'boolean', 'ForeignUploadTargets' => 'array', 'UploadDialog' => 'object', 'FileBackends' => 'object', 'LockManagers' => 'array', 'CopyUploadsDomains' => 'array', 'CopyUploadTimeout' => [ 'boolean', 'integer', ], 'SharedThumbnailScriptPath' => [ 'string', 'boolean', ], 'HashedUploadDirectory' => 'boolean', 'CSPUploadEntryPoint' => 'boolean', 'FileExtensions' => 'array', 'ProhibitedFileExtensions' => 'array', 'MimeTypeExclusions' => 'array', 'TrustedMediaFormats' => 'array', 'MediaHandlers' => 'object', 'NativeImageLazyLoading' => 'boolean', 'ParserTestMediaHandlers' => 'object', 'MaxInterlacingAreas' => 'object', 'SVGConverters' => 'object', 'SVGNativeRendering' => [ 'string', 'boolean', ], 'MaxImageArea' => [ 'string', 'integer', 'boolean', ], 'TiffThumbnailType' => 'array', 'GenerateThumbnailOnParse' => 'boolean', 'EnableAutoRotation' => [ 'boolean', 'null', ], 'Antivirus' => [ 'string', 'null', ], 'AntivirusSetup' => 'object', 'MimeDetectorCommand' => [ 'string', 'null', ], 'XMLMimeTypes' => 'object', 'ImageLimits' => 'array', 'ThumbLimits' => 'array', 'ThumbnailNamespaces' => 'array', 'ThumbnailSteps' => [ 'array', 'null', ], 'ThumbnailStepsRatio' => [ 'number', 'null', ], 'ThumbnailBuckets' => [ 'array', 'null', ], 'UploadThumbnailRenderMap' => 'object', 'GalleryOptions' => 'object', 'DjvuDump' => [ 'string', 'null', ], 'DjvuRenderer' => [ 'string', 'null', ], 'DjvuTxt' => [ 'string', 'null', ], 'DjvuPostProcessor' => [ 'string', 'null', ], 'UserEmailConfirmationUseHTML' => 'boolean', 'SMTP' => [ 'boolean', 'object', ], 'EnotifFromEditor' => 'boolean', 'EnotifRevealEditorAddress' => 'boolean', 'UsersNotifiedOnAllChanges' => 'object', 'DBmwschema' => [ 'string', 'null', ], 'SharedTables' => 'array', 'DBservers' => [ 'boolean', 'array', ], 'LBFactoryConf' => 'object', 'LocalDatabases' => 'array', 'VirtualDomainsMapping' => 'object', 'FileSchemaMigrationStage' => 'integer', 'ImageLinksSchemaMigrationStage' => 'integer', 'ExternalLinksDomainGaps' => 'object', 'ContentHandlers' => 'object', 'NamespaceContentModels' => 'object', 'TextModelsToParse' => 'array', 'ExternalStores' => 'array', 'ExternalServers' => 'object', 'DefaultExternalStore' => [ 'array', 'boolean', ], 'RevisionCacheExpiry' => 'integer', 'PageLanguageUseDB' => 'boolean', 'DiffEngine' => [ 'string', 'null', ], 'ExternalDiffEngine' => [ 'string', 'boolean', ], 'Wikidiff2Options' => 'object', 'RequestTimeLimit' => [ 'integer', 'null', ], 'CriticalSectionTimeLimit' => 'number', 'PoolCounterConf' => [ 'object', 'null', ], 'PoolCountClientConf' => 'object', 'MaxUserDBWriteDuration' => [ 'integer', 'boolean', ], 'MaxJobDBWriteDuration' => [ 'integer', 'boolean', ], 'MultiShardSiteStats' => 'boolean', 'ObjectCaches' => 'object', 'WANObjectCache' => 'object', 'MicroStashType' => [ 'string', 'integer', ], 'ParsoidCacheConfig' => 'object', 'ParsoidSelectiveUpdateSampleRate' => 'integer', 'ParserCacheFilterConfig' => 'object', 'ChronologyProtectorSecret' => 'string', 'PHPSessionHandling' => 'string', 'SuspiciousIpExpiry' => [ 'integer', 'boolean', ], 'MemCachedServers' => 'array', 'LocalisationCacheConf' => 'object', 'ExtensionInfoMTime' => [ 'integer', 'boolean', ], 'CdnServers' => 'object', 'CdnServersNoPurge' => 'object', 'HTCPRouting' => 'object', 'GrammarForms' => 'object', 'ExtraInterlanguageLinkPrefixes' => 'array', 'InterlanguageLinkCodeMap' => 'object', 'ExtraLanguageNames' => 'object', 'ExtraLanguageCodes' => 'object', 'DummyLanguageCodes' => 'object', 'DisabledVariants' => 'object', 'ForceUIMsgAsContentMsg' => 'object', 'RawHtmlMessages' => 'array', 'OverrideUcfirstCharacters' => 'object', 'XhtmlNamespaces' => 'object', 'BrowserFormatDetection' => 'string', 'SkinMetaTags' => 'object', 'SkipSkins' => 'object', 'FragmentMode' => 'array', 'FooterIcons' => 'object', 'InterwikiLogoOverride' => 'array', 'ResourceModules' => 'object', 'ResourceModuleSkinStyles' => 'object', 'ResourceLoaderSources' => 'object', 'ResourceLoaderMaxage' => 'object', 'ResourceLoaderMaxQueryLength' => [ 'integer', 'boolean', ], 'CanonicalNamespaceNames' => 'object', 'ExtraNamespaces' => 'object', 'ExtraGenderNamespaces' => 'object', 'NamespaceAliases' => 'object', 'CapitalLinkOverrides' => 'object', 'NamespacesWithSubpages' => 'object', 'ContentNamespaces' => 'array', 'ShortPagesNamespaceExclusions' => 'array', 'ExtraSignatureNamespaces' => 'array', 'InvalidRedirectTargets' => 'array', 'LocalInterwikis' => 'array', 'InterwikiCache' => [ 'boolean', 'object', ], 'SiteTypes' => 'object', 'UrlProtocols' => 'array', 'TidyConfig' => 'object', 'ParsoidSettings' => 'object', 'ParsoidExperimentalParserFunctionOutput' => 'boolean', 'NoFollowNsExceptions' => 'array', 'NoFollowDomainExceptions' => 'array', 'ExternalLinksIgnoreDomains' => 'array', 'EnableMagicLinks' => 'object', 'ManualRevertSearchRadius' => 'integer', 'RevertedTagMaxDepth' => 'integer', 'CentralIdLookupProviders' => 'object', 'CentralIdLookupProvider' => 'string', 'UserRegistrationProviders' => 'object', 'PasswordPolicy' => 'object', 'AuthManagerConfig' => [ 'object', 'null', ], 'AuthManagerAutoConfig' => 'object', 'RememberMe' => 'string', 'ReauthenticateTime' => 'object', 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => 'object', 'ChangeCredentialsBlacklist' => 'array', 'RemoveCredentialsBlacklist' => 'array', 'PasswordConfig' => 'object', 'PasswordResetRoutes' => 'object', 'SignatureAllowedLintErrors' => 'array', 'ReservedUsernames' => 'array', 'DefaultUserOptions' => 'object', 'ConditionalUserOptions' => 'object', 'HiddenPrefs' => 'array', 'UserJsPrefLimit' => 'integer', 'AuthenticationTokenVersion' => [ 'string', 'null', ], 'SessionProviders' => 'object', 'AutoCreateTempUser' => 'object', 'AutoblockExemptions' => 'array', 'BlockCIDRLimit' => 'object', 'EnableMultiBlocks' => 'boolean', 'GroupPermissions' => 'object', 'PrivilegedGroups' => 'array', 'RevokePermissions' => 'object', 'GroupInheritsPermissions' => 'object', 'ImplicitGroups' => 'array', 'GroupsAddToSelf' => 'object', 'GroupsRemoveFromSelf' => 'object', 'RestrictedGroups' => 'object', 'UserRequirementsPrivateConditions' => 'array', 'RestrictionTypes' => 'array', 'RestrictionLevels' => 'array', 'CascadingRestrictionLevels' => 'array', 'SemiprotectedRestrictionLevels' => 'array', 'NamespaceProtection' => 'object', 'NonincludableNamespaces' => 'object', 'Autopromote' => 'object', 'AutopromoteOnce' => 'object', 'AutopromoteOnceRCExcludedGroups' => 'array', 'AddGroups' => 'object', 'RemoveGroups' => 'object', 'AvailableRights' => 'array', 'ImplicitRights' => 'array', 'AccountCreationThrottle' => [ 'integer', 'array', ], 'TempAccountCreationThrottle' => 'array', 'TempAccountNameAcquisitionThrottle' => 'array', 'SpamRegex' => 'array', 'SummarySpamRegex' => 'array', 'DnsBlacklistUrls' => 'array', 'ProxyList' => [ 'string', 'array', ], 'ProxyWhitelist' => 'array', 'SoftBlockRanges' => 'array', 'RateLimits' => 'object', 'RateLimitsExcludedIPs' => 'array', 'ExternalQuerySources' => 'object', 'PasswordAttemptThrottle' => 'array', 'GrantPermissions' => 'object', 'GrantPermissionGroups' => 'object', 'GrantRiskGroups' => 'object', 'EnableBotPasswords' => 'boolean', 'BotPasswordsCluster' => [ 'string', 'boolean', ], 'BotPasswordsDatabase' => [ 'string', 'boolean', ], 'BotPasswordsLimit' => 'integer', 'CSPHeader' => [ 'boolean', 'object', ], 'CSPReportOnlyHeader' => [ 'boolean', 'object', ], 'CSPFalsePositiveUrls' => 'object', 'AllowCrossOrigin' => 'boolean', 'RestAllowCrossOriginCookieAuth' => 'boolean', 'CookieSameSite' => [ 'string', 'null', ], 'CacheVaryCookies' => 'array', 'TrxProfilerLimits' => 'object', 'DebugLogGroups' => 'object', 'MWLoggerDefaultSpi' => 'object', 'Profiler' => 'object', 'StatsTarget' => [ 'string', 'null', ], 'StatsFormat' => [ 'string', 'null', ], 'StatsPrefix' => 'string', 'OpenTelemetryConfig' => [ 'object', 'null', ], 'OpenSearchTemplates' => 'object', 'NamespacesToBeSearchedDefault' => 'object', 'SitemapNamespaces' => [ 'boolean', 'array', ], 'SitemapNamespacesPriorities' => [ 'boolean', 'object', ], 'SitemapApiConfig' => 'object', 'SpecialSearchFormOptions' => 'object', 'SearchMatchRedirectPreference' => 'boolean', 'SearchRunSuggestedQuery' => 'boolean', 'PreviewOnOpenNamespaces' => 'object', 'ReadOnlyWatchedItemStore' => 'boolean', 'GitRepositoryViewers' => 'object', 'InstallerInitialPages' => 'array', 'RCLinkLimits' => 'array', 'RCLinkDays' => 'array', 'RCFeeds' => 'object', 'RCEngines' => 'object', 'OverrideSiteFeed' => 'object', 'FeedClasses' => 'object', 'AdvertisedFeedTypes' => 'array', 'SoftwareTags' => 'object', 'RecentChangesFlags' => 'object', 'WatchlistExpiry' => 'boolean', 'EnableWatchlistLabels' => 'boolean', 'WatchlistLabelsMaxPerUser' => 'integer', 'WatchlistPurgeRate' => 'number', 'WatchlistExpiryMaxDuration' => [ 'string', 'null', ], 'EnableChangesListQueryPartitioning' => 'boolean', 'ImportSources' => 'object', 'ExtensionFunctions' => 'array', 'ExtensionMessagesFiles' => 'object', 'MessagesDirs' => 'object', 'TranslationAliasesDirs' => 'object', 'ExtensionEntryPointListFiles' => 'object', 'ValidSkinNames' => 'object', 'SpecialPages' => 'object', 'ExtensionCredits' => 'object', 'Hooks' => 'object', 'ServiceWiringFiles' => 'array', 'JobClasses' => 'object', 'JobTypesExcludedFromDefaultQueue' => 'array', 'JobBackoffThrottling' => 'object', 'JobTypeConf' => 'object', 'SpecialPageCacheUpdates' => 'object', 'PagePropLinkInvalidations' => 'object', 'TempCategoryCollations' => 'array', 'SortedCategories' => 'boolean', 'TrackingCategories' => 'array', 'LogTypes' => 'array', 'LogRestrictions' => 'object', 'FilterLogTypes' => 'object', 'LogNames' => 'object', 'LogHeaders' => 'object', 'LogActions' => 'object', 'LogActionsHandlers' => 'object', 'ActionFilteredLogs' => 'object', 'RangeContributionsCIDRLimit' => 'object', 'Actions' => 'object', 'NamespaceRobotPolicies' => 'object', 'ArticleRobotPolicies' => 'object', 'ExemptFromUserRobotsControl' => [ 'array', 'null', ], 'APIModules' => 'object', 'APIFormatModules' => 'object', 'APIMetaModules' => 'object', 'APIPropModules' => 'object', 'APIListModules' => 'object', 'APIUselessQueryPages' => 'array', 'CrossSiteAJAXdomains' => 'object', 'CrossSiteAJAXdomainExceptions' => 'object', 'AllowedCorsHeaders' => 'array', 'RestAPIAdditionalRouteFiles' => 'array', 'RestSandboxSpecs' => 'object', 'ShellRestrictionMethod' => [ 'string', 'boolean', ], 'ShellboxUrls' => 'object', 'ShellboxSecretKey' => [ 'string', 'null', ], 'ShellboxShell' => [ 'string', 'null', ], 'HTTPTimeout' => 'number', 'HTTPConnectTimeout' => 'number', 'HTTPMaxTimeout' => 'number', 'HTTPMaxConnectTimeout' => 'number', 'LocalVirtualHosts' => 'object', 'LocalHTTPProxy' => [ 'string', 'boolean', ], 'VirtualRestConfig' => 'object', 'EventRelayerConfig' => 'object', 'Pingback' => 'boolean', 'OriginTrials' => 'array', 'ReportToExpiry' => 'integer', 'ReportToEndpoints' => 'array', 'FeaturePolicyReportOnly' => 'array', 'SkinsPreferred' => 'array', 'SpecialContributeSkinsEnabled' => 'array', 'SpecialContributeNewPageTarget' => [ 'string', 'null', ], 'EnableEditRecovery' => 'boolean', 'EditRecoveryExpiry' => 'integer', 'UseCodexSpecialBlock' => 'boolean', 'ShowLogoutConfirmation' => 'boolean', 'EnableProtectionIndicators' => 'boolean', 'OutputPipelineStages' => 'object', 'FeatureShutdown' => 'array', 'CloneArticleParserOutput' => 'boolean', 'UseLeximorph' => 'boolean', 'UsePostprocCache' => 'boolean', 'UsePostprocCacheLegacy' => 'boolean', 'UsePostprocCacheParsoid' => 'boolean', 'ParserOptionsLogUnsafeSampleRate' => 'integer', ], 'mergeStrategy' => [ 'TiffThumbnailType' => 'replace', 'LBFactoryConf' => 'replace', 'InterwikiCache' => 'replace', 'PasswordPolicy' => 'array_replace_recursive', 'AuthManagerAutoConfig' => 'array_plus_2d', 'GroupPermissions' => 'array_plus_2d', 'RevokePermissions' => 'array_plus_2d', 'AddGroups' => 'array_merge_recursive', 'RemoveGroups' => 'array_merge_recursive', 'RateLimits' => 'array_plus_2d', 'GrantPermissions' => 'array_plus_2d', 'MWLoggerDefaultSpi' => 'replace', 'Profiler' => 'replace', 'Hooks' => 'array_merge_recursive', 'VirtualRestConfig' => 'array_plus_2d', ], 'dynamicDefault' => [ 'UsePathInfo' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultUsePathInfo', ], ], 'Script' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultScript', ], ], 'LoadScript' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLoadScript', ], ], 'RestPath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultRestPath', ], ], 'StylePath' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultStylePath', ], ], 'LocalStylePath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocalStylePath', ], ], 'ExtensionAssetsPath' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultExtensionAssetsPath', ], ], 'ArticlePath' => [ 'use' => [ 'Script', 'UsePathInfo', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultArticlePath', ], ], 'UploadPath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultUploadPath', ], ], 'FileCacheDirectory' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultFileCacheDirectory', ], ], 'Logo' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLogo', ], ], 'DeletedDirectory' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultDeletedDirectory', ], ], 'ShowEXIF' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultShowEXIF', ], ], 'SharedPrefix' => [ 'use' => [ 'DBprefix', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultSharedPrefix', ], ], 'SharedSchema' => [ 'use' => [ 'DBmwschema', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultSharedSchema', ], ], 'DBerrorLogTZ' => [ 'use' => [ 'Localtimezone', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultDBerrorLogTZ', ], ], 'Localtimezone' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocaltimezone', ], ], 'LocalTZoffset' => [ 'use' => [ 'Localtimezone', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocalTZoffset', ], ], 'ResourceBasePath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultResourceBasePath', ], ], 'MetaNamespace' => [ 'use' => [ 'Sitename', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultMetaNamespace', ], ], 'CookieSecure' => [ 'use' => [ 'ForceHTTPS', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultCookieSecure', ], ], 'CookiePrefix' => [ 'use' => [ 'SharedDB', 'SharedPrefix', 'SharedTables', 'DBname', 'DBprefix', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultCookiePrefix', ], ], 'ReadOnlyFile' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultReadOnlyFile', ], ], ], ], 'config-schema' => [ 'UploadStashScalerBaseUrl' => [ 'deprecated' => 'since 1.36 Use thumbProxyUrl in $wgLocalFileRepo', ], 'IllegalFileChars' => [ 'deprecated' => 'since 1.41; no longer customizable', ], 'ThumbnailNamespaces' => [ 'items' => [ 'type' => 'integer', ], ], 'LocalDatabases' => [ 'items' => [ 'type' => 'string', ], ], 'ParserCacheFilterConfig' => [ 'additionalProperties' => [ 'type' => 'object', 'description' => 'A map of namespace IDs to filter definitions.', 'additionalProperties' => [ 'type' => 'object', 'description' => 'A map of filter names to values.', 'properties' => [ 'minCpuTime' => [ 'type' => 'number', ], ], ], ], ], 'PHPSessionHandling' => [ 'deprecated' => 'since 1.45 Integration with PHP session handling will be removed in the future', ], 'RawHtmlMessages' => [ 'items' => [ 'type' => 'string', ], ], 'InterwikiLogoOverride' => [ 'items' => [ 'type' => 'string', ], ], 'LegalTitleChars' => [ 'deprecated' => 'since 1.41; use Extension:TitleBlacklist to customize', ], 'ReauthenticateTime' => [ 'additionalProperties' => [ 'type' => 'integer', ], ], 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => [ 'additionalProperties' => [ 'type' => 'boolean', ], ], 'ChangeCredentialsBlacklist' => [ 'items' => [ 'type' => 'string', ], ], 'RemoveCredentialsBlacklist' => [ 'items' => [ 'type' => 'string', ], ], 'GroupPermissions' => [ 'additionalProperties' => [ 'type' => 'object', 'additionalProperties' => [ 'type' => 'boolean', ], ], ], 'GroupInheritsPermissions' => [ 'additionalProperties' => [ 'type' => 'string', ], ], 'AvailableRights' => [ 'items' => [ 'type' => 'string', ], ], 'ImplicitRights' => [ 'items' => [ 'type' => 'string', ], ], 'SoftBlockRanges' => [ 'items' => [ 'type' => 'string', ], ], 'ExternalQuerySources' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'enabled' => [ 'type' => 'boolean', 'default' => false, ], 'url' => [ 'type' => 'string', 'format' => 'uri', ], 'timeout' => [ 'type' => 'integer', 'default' => 10, ], ], 'required' => [ 'enabled', 'url', ], 'additionalProperties' => false, ], ], 'GrantPermissions' => [ 'additionalProperties' => [ 'type' => 'object', 'additionalProperties' => [ 'type' => 'boolean', ], ], ], 'GrantPermissionGroups' => [ 'additionalProperties' => [ 'type' => 'string', ], ], 'SitemapNamespacesPriorities' => [ 'deprecated' => 'since 1.45 and ignored', ], 'SitemapApiConfig' => [ 'additionalProperties' => [ 'enabled' => [ 'type' => 'bool', ], 'sitemapsPerIndex' => [ 'type' => 'int', ], 'pagesPerSitemap' => [ 'type' => 'int', ], 'expiry' => [ 'type' => 'int', ], ], ], 'SoftwareTags' => [ 'additionalProperties' => [ 'type' => 'boolean', ], ], 'JobBackoffThrottling' => [ 'additionalProperties' => [ 'type' => 'number', ], ], 'JobTypeConf' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'class' => [ 'type' => 'string', ], 'order' => [ 'type' => 'string', ], 'claimTTL' => [ 'type' => 'integer', ], ], ], ], 'TrackingCategories' => [ 'deprecated' => 'since 1.25 Extensions should now register tracking categories using the new extension registration system.', ], 'RangeContributionsCIDRLimit' => [ 'additionalProperties' => [ 'type' => 'integer', ], ], 'RestSandboxSpecs' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'url' => [ 'type' => 'string', 'format' => 'url', ], 'name' => [ 'type' => 'string', ], 'file' => [ 'type' => 'string', ], 'msg' => [ 'type' => 'string', 'description' => 'a message key', ], ], ], ], 'ShellboxUrls' => [ 'additionalProperties' => [ 'type' => [ 'string', 'boolean', 'null', ], ], ], ], 'obsolete-config' => [ 'MangleFlashPolicy' => 'Since 1.39; no longer has any effect.', 'EnableOpenSearchSuggest' => 'Since 1.35, no longer used', 'AutoloadAttemptLowercase' => 'Since 1.40; no longer has any effect.', ],]
Interface for objects which can provide a MediaWiki context on request.
Interface for MediaWiki-localized exceptions.
Shared interface for rigor levels when dealing with User methods.
element(SerializerNode $parent, SerializerNode $node, $contents)
Helper trait for implementations \DAO.
ListType
The constants used to specify list types.
Definition ListType.php:9