MediaWiki master
ApiMain.php
Go to the documentation of this file.
1<?php
10namespace MediaWiki\Api;
11
12use LogicException;
13use MediaWiki;
37use Throwable;
38use UnexpectedValueException;
43use Wikimedia\Parsoid\Core\SectionMetadata;
44use Wikimedia\ScopedCallback;
46use Wikimedia\Timestamp\ConvertibleTimestamp;
47use Wikimedia\Timestamp\TimestampException;
48use Wikimedia\Timestamp\TimestampFormat as TS;
49
66class ApiMain extends ApiBase {
70 private const API_DEFAULT_FORMAT = 'jsonfm';
71
75 private const API_DEFAULT_USELANG = 'user';
76
80 private const MODULES = [
81 'login' => [
82 'class' => ApiLogin::class,
83 'services' => [
84 'AuthManager',
85 'UserIdentityUtils'
86 ],
87 ],
88 'clientlogin' => [
89 'class' => ApiClientLogin::class,
90 'services' => [
91 'AuthManager',
92 'UrlUtils',
93 ],
94 ],
95 'logout' => [
96 'class' => ApiLogout::class,
97 'services' => [
98 'SessionManager',
99 ],
100 ],
101 'createaccount' => [
102 'class' => ApiAMCreateAccount::class,
103 'services' => [
104 'AuthManager',
105 'UrlUtils',
106 ],
107 ],
108 'linkaccount' => [
109 'class' => ApiLinkAccount::class,
110 'services' => [
111 'AuthManager',
112 'UrlUtils',
113 ],
114 ],
115 'unlinkaccount' => [
116 'class' => ApiRemoveAuthenticationData::class,
117 'services' => [
118 'AuthManager',
119 ],
120 ],
121 'changeauthenticationdata' => [
122 'class' => ApiChangeAuthenticationData::class,
123 'services' => [
124 'AuthManager',
125 ],
126 ],
127 'removeauthenticationdata' => [
128 'class' => ApiRemoveAuthenticationData::class,
129 'services' => [
130 'AuthManager',
131 ],
132 ],
133 'resetpassword' => [
134 'class' => ApiResetPassword::class,
135 'services' => [
136 'PasswordReset',
137 ]
138 ],
139 'query' => [
140 'class' => ApiQuery::class,
141 'services' => [
142 'ObjectFactory',
143 'WikiExporterFactory',
144 'TitleFormatter',
145 'TitleFactory',
146 ]
147 ],
148 'expandtemplates' => [
149 'class' => ApiExpandTemplates::class,
150 'services' => [
151 'RevisionStore',
152 'ParserFactory',
153 ]
154 ],
155 'parse' => [
156 'class' => ApiParse::class,
157 'services' => [
158 'RevisionLookup',
159 'SkinFactory',
160 'LanguageNameUtils',
161 'LinkBatchFactory',
162 'LinkCache',
163 'ContentHandlerFactory',
164 'ParserFactory',
165 'WikiPageFactory',
166 'ContentRenderer',
167 'ContentTransformer',
168 'CommentFormatter',
169 'TempUserCreator',
170 'UserFactory',
171 'UrlUtils',
172 'TitleFormatter',
173 'JsonCodec',
174 ]
175 ],
176 'stashedit' => [
177 'class' => ApiStashEdit::class,
178 'services' => [
179 'ContentHandlerFactory',
180 'PageEditStash',
181 'RevisionLookup',
182 'StatsFactory',
183 'WikiPageFactory',
184 'TempUserCreator',
185 'UserFactory',
186 ]
187 ],
188 'opensearch' => [
189 'class' => ApiOpenSearch::class,
190 'services' => [
191 'LinkBatchFactory',
192 'SearchEngineConfig',
193 'SearchEngineFactory',
194 'UrlUtils',
195 ]
196 ],
197 'feedcontributions' => [
198 'class' => ApiFeedContributions::class,
199 'services' => [
200 'RevisionStore',
201 'LinkRenderer',
202 'LinkBatchFactory',
203 'HookContainer',
204 'DBLoadBalancerFactory',
205 'NamespaceInfo',
206 'UserFactory',
207 'CommentFormatter',
208 ]
209 ],
210 'feedrecentchanges' => [
211 'class' => ApiFeedRecentChanges::class,
212 'services' => [
213 'SpecialPageFactory',
214 'TempUserConfig',
215 ]
216 ],
217 'feedwatchlist' => [
218 'class' => ApiFeedWatchlist::class,
219 'services' => [
220 'ParserFactory',
221 ]
222 ],
223 'help' => [
224 'class' => ApiHelp::class,
225 'services' => [
226 'SkinFactory',
227 ]
228 ],
229 'paraminfo' => [
230 'class' => ApiParamInfo::class,
231 'services' => [
232 'UserFactory',
233 ],
234 ],
235 'rsd' => [
236 'class' => ApiRsd::class,
237 ],
238 'compare' => [
239 'class' => ApiComparePages::class,
240 'services' => [
241 'RevisionStore',
242 'ArchivedRevisionLookup',
243 'SlotRoleRegistry',
244 'ContentHandlerFactory',
245 'ContentTransformer',
246 'CommentFormatter',
247 'TempUserCreator',
248 'UserFactory',
249 ]
250 ],
251 'checktoken' => [
252 'class' => ApiCheckToken::class,
253 ],
254 'cspreport' => [
255 'class' => ApiCSPReport::class,
256 ],
257 'validatepassword' => [
258 'class' => ApiValidatePassword::class,
259 'services' => [
260 'AuthManager',
261 'UserFactory',
262 ]
263 ],
264
265 // Write modules
266 'purge' => [
267 'class' => ApiPurge::class,
268 'services' => [
269 'WikiPageFactory',
270 'TitleFormatter',
271 ],
272 ],
273 'setnotificationtimestamp' => [
274 'class' => ApiSetNotificationTimestamp::class,
275 'services' => [
276 'DBLoadBalancerFactory',
277 'RevisionStore',
278 'WatchedItemStore',
279 'TitleFormatter',
280 'TitleFactory',
281 ]
282 ],
283 'rollback' => [
284 'class' => ApiRollback::class,
285 'services' => [
286 'RollbackPageFactory',
287 'WatchlistManager',
288 'WatchedItemStore',
289 'UserOptionsLookup',
290 ]
291 ],
292 'delete' => [
293 'class' => ApiDelete::class,
294 'services' => [
295 'RepoGroup',
296 'WatchlistManager',
297 'WatchedItemStore',
298 'UserOptionsLookup',
299 'DeletePageFactory',
300 ]
301 ],
302 'undelete' => [
303 'class' => ApiUndelete::class,
304 'services' => [
305 'WatchlistManager',
306 'WatchedItemStore',
307 'UserOptionsLookup',
308 'UndeletePageFactory',
309 'WikiPageFactory',
310 ]
311 ],
312 'protect' => [
313 'class' => ApiProtect::class,
314 'services' => [
315 'WatchlistManager',
316 'WatchedItemStore',
317 'UserOptionsLookup',
318 'RestrictionStore',
319 ]
320 ],
321 'block' => [
322 'class' => ApiBlock::class,
323 'services' => [
324 'BlockPermissionCheckerFactory',
325 'BlockUserFactory',
326 'UserIdentityLookup',
327 'WatchedItemStore',
328 'BlockTargetFactory',
329 'BlockActionInfo',
330 'DatabaseBlockStore',
331 'WatchlistManager',
332 'UserOptionsLookup',
333 ]
334 ],
335 'unblock' => [
336 'class' => ApiUnblock::class,
337 'services' => [
338 'BlockPermissionCheckerFactory',
339 'UnblockUserFactory',
340 'UserIdentityLookup',
341 'WatchedItemStore',
342 'WatchlistManager',
343 'UserOptionsLookup',
344 'DatabaseBlockStore',
345 'BlockTargetFactory',
346 ]
347 ],
348 'move' => [
349 'class' => ApiMove::class,
350 'services' => [
351 'MovePageFactory',
352 'RepoGroup',
353 'WatchlistManager',
354 'WatchedItemStore',
355 'UserOptionsLookup',
356 ]
357 ],
358 'edit' => [
359 'class' => ApiEditPage::class,
360 'services' => [
361 'ContentHandlerFactory',
362 'RevisionLookup',
363 'WatchedItemStore',
364 'WikiPageFactory',
365 'WatchlistManager',
366 'UserOptionsLookup',
367 'RedirectLookup',
368 'TempUserCreator',
369 'UserFactory',
370 'ShadowPageLoader',
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 'xml' => [
512 'class' => ApiFormatXml::class,
513 ],
514 'xmlfm' => [
515 'class' => ApiFormatXml::class,
516 ],
517 'rawfm' => [
518 'class' => ApiFormatJson::class,
519 ],
520 'none' => [
521 'class' => ApiFormatNone::class,
522 ],
523 ];
524
526 private $mPrinter;
527
529 private $mModuleMgr;
530
532 private $mResult;
533
535 private $mErrorFormatter;
536
538 private $mParamValidator;
539
541 private $mContinuationManager;
542
544 private $mAction;
545
547 private $mEnableWrite;
548
550 private $mInternalMode;
551
553 private $mModule;
554
556 private $mCacheMode = 'private';
557
559 private $mCacheControl = [];
560
562 private $mParamsUsed = [];
563
565 private $mParamsSensitive = [];
566
568 private $lacksSameOriginSecurity = null;
569
571 private $statsFactory;
572
584 public function __construct( $context = null, $enableWrite = false, $internal = null ) {
585 if ( $context === null ) {
586 $context = RequestContext::getMain();
587 } elseif ( $context instanceof WebRequest ) {
588 // BC for pre-1.19
589 $request = $context;
590 $context = RequestContext::getMain();
591 }
592 // We set a derivative context so we can change stuff later
593 $derivativeContext = new DerivativeContext( $context );
594 $this->setContext( $derivativeContext );
595
596 if ( isset( $request ) ) {
597 $derivativeContext->setRequest( $request );
598 } else {
599 $request = $this->getRequest();
600 }
601
602 $this->mInternalMode = $internal ?? ( $request instanceof FauxRequest );
603
604 // Special handling for the main module: $parent === $this
605 parent::__construct( $this, $this->mInternalMode ? 'main_int' : 'main' );
606
607 $config = $this->getConfig();
608 // TODO inject stuff, see T265644
609 $services = MediaWikiServices::getInstance();
610
611 if ( !$this->mInternalMode ) {
612 // If we're in a mode that breaks the same-origin policy, strip
613 // user credentials for security.
614 if ( $this->lacksSameOriginSecurity() ) {
615 wfDebug( "API: stripping user credentials when the same-origin policy is not applied" );
616 $user = $services->getUserFactory()->newAnonymous();
617 $derivativeContext->setUser( $user );
618 $request->response()->header( 'MediaWiki-Login-Suppressed: true' );
619 }
620 }
621
622 $this->mParamValidator = new ApiParamValidator(
623 $this,
624 $services->getObjectFactory()
625 );
626
627 $this->statsFactory = $services->getStatsFactory();
628
629 $this->mResult =
631
632 // Setup uselang. This doesn't use $this->getParameter()
633 // because we're not ready to handle errors yet.
634 // Optimisation: Avoid slow getVal(), this isn't user-generated content.
635 $uselang = $request->getRawVal( 'uselang' ) ?? self::API_DEFAULT_USELANG;
636 if ( $uselang === 'user' ) {
637 // Assume the parent context is going to return the user language
638 // for uselang=user (see T85635).
639 } else {
640 if ( $uselang === 'content' ) {
641 $uselang = $services->getContentLanguageCode()->toString();
642 }
643 $code = RequestContext::sanitizeLangCode( $uselang );
644 $derivativeContext->setLanguage( $code );
645 if ( !$this->mInternalMode ) {
646 // phpcs:disable MediaWiki.Usage.ExtendClassUsage.FunctionVarUsage
647 global $wgLang;
648 $wgLang = $derivativeContext->getLanguage();
649 RequestContext::getMain()->setLanguage( $wgLang );
650 // phpcs:enable
651 }
652 }
653
654 // Set up the error formatter. This doesn't use $this->getParameter()
655 // because we're not ready to handle errors yet.
656 // Optimisation: Avoid slow getVal(), this isn't user-generated content.
657 $errorFormat = $request->getRawVal( 'errorformat' ) ?? 'bc';
658 $errorLangCode = $request->getRawVal( 'errorlang' ) ?? 'uselang';
659 $errorsUseDB = $request->getCheck( 'errorsuselocal' );
660 if ( in_array( $errorFormat, [ 'plaintext', 'wikitext', 'html', 'raw', 'none' ], true ) ) {
661 if ( $errorLangCode === 'uselang' ) {
662 $errorLang = $this->getLanguage();
663 } elseif ( $errorLangCode === 'content' ) {
664 $errorLang = $services->getContentLanguage();
665 } else {
666 $errorLangCode = RequestContext::sanitizeLangCode( $errorLangCode );
667 $errorLang = $services->getLanguageFactory()->getLanguage( $errorLangCode );
668 }
669 $this->mErrorFormatter = new ApiErrorFormatter(
670 $this->mResult,
671 $errorLang,
672 $errorFormat,
673 $errorsUseDB
674 );
675 } else {
676 $this->mErrorFormatter = new ApiErrorFormatter_BackCompat( $this->mResult );
677 }
678 $this->mResult->setErrorFormatter( $this->getErrorFormatter() );
679
680 $this->mModuleMgr = new ApiModuleManager(
681 $this,
682 $services->getObjectFactory()
683 );
684 $this->mModuleMgr->addModules( self::MODULES, 'action' );
685 $this->mModuleMgr->addModules( $config->get( MainConfigNames::APIModules ), 'action' );
686 $this->mModuleMgr->addModules( self::FORMATS, 'format' );
687 $this->mModuleMgr->addModules( $config->get( MainConfigNames::APIFormatModules ), 'format' );
688
689 $this->getHookRunner()->onApiMain__moduleManager( $this->mModuleMgr );
690
691 $this->mContinuationManager = null;
692 $this->mEnableWrite = $enableWrite;
693 }
694
699 public function isInternalMode() {
700 return $this->mInternalMode;
701 }
702
708 public function getResult() {
709 return $this->mResult;
710 }
711
716 public function lacksSameOriginSecurity() {
717 if ( $this->lacksSameOriginSecurity !== null ) {
718 return $this->lacksSameOriginSecurity;
719 }
720
721 $request = $this->getRequest();
722
723 // JSONP mode
724 if ( $request->getCheck( 'callback' ) ||
725 // Anonymous CORS
726 $request->getRawVal( 'origin' ) === '*' ||
727 // Header to be used from XMLHTTPRequest when the request might
728 // otherwise be used for XSS.
729 $request->getHeader( 'Treat-as-Untrusted' ) !== false ||
730 (
731 // Authenticated CORS with unsupported session provider (including preflight request)
732 $request->getCheck( 'crossorigin' ) &&
733 !$request->getSession()->getProvider()->safeAgainstCsrf()
734 )
735 ) {
736 $this->lacksSameOriginSecurity = true;
737 return true;
738 }
739
740 // Allow extensions to override.
741 $this->lacksSameOriginSecurity = !$this->getHookRunner()
742 ->onRequestHasSameOriginSecurity( $request );
743 return $this->lacksSameOriginSecurity;
744 }
745
750 public function getErrorFormatter() {
751 return $this->mErrorFormatter;
752 }
753
757 public function getContinuationManager() {
758 return $this->mContinuationManager;
759 }
760
764 public function setContinuationManager( ?ApiContinuationManager $manager = null ) {
765 if ( $manager !== null && $this->mContinuationManager !== null ) {
766 throw new UnexpectedValueException(
767 __METHOD__ . ': tried to set manager from ' . $manager->getSource() .
768 ' when a manager is already set from ' . $this->mContinuationManager->getSource()
769 );
770 }
771 $this->mContinuationManager = $manager;
772 }
773
775 return $this->mParamValidator;
776 }
777
783 public function getModule() {
784 return $this->mModule;
785 }
786
792 public function getStatsFactory() {
793 return $this->getMain()->statsFactory;
794 }
795
801 public function getPrinter() {
802 return $this->mPrinter;
803 }
804
810 public function setCacheMaxAge( $maxage ) {
811 $this->setCacheControl( [
812 'max-age' => $maxage,
813 's-maxage' => $maxage
814 ] );
815 }
816
842 public function setCacheMode( $mode ) {
843 if ( !in_array( $mode, [ 'private', 'public', 'anon-public-user-private' ] ) ) {
844 wfDebug( __METHOD__ . ": unrecognised cache mode \"$mode\"" );
845
846 // Ignore for forwards-compatibility
847 return;
848 }
849
850 if ( !$this->getPermissionManager()->isEveryoneAllowed( 'read' ) ) {
851 // Private wiki, only private headers
852 if ( $mode !== 'private' ) {
853 wfDebug( __METHOD__ . ": ignoring request for $mode cache mode, private wiki" );
854
855 return;
856 }
857 }
858
859 if ( $mode === 'public' && $this->getParameter( 'uselang' ) === 'user' ) {
860 // User language is used for i18n, so we don't want to publicly
861 // cache. Anons are ok, because if they have non-default language
862 // then there's an appropriate Vary header set by whatever set
863 // their non-default language.
864 wfDebug( __METHOD__ . ": downgrading cache mode 'public' to " .
865 "'anon-public-user-private' due to uselang=user" );
866 $mode = 'anon-public-user-private';
867 }
868
869 wfDebug( __METHOD__ . ": setting cache mode $mode" );
870 $this->mCacheMode = $mode;
871 }
872
874 public function getCacheMode() {
875 return $this->mCacheMode;
876 }
877
888 public function setCacheControl( $directives ) {
889 $this->mCacheControl = $directives + $this->mCacheControl;
890 }
891
899 public function createPrinterByName( $format ) {
900 $printer = $this->mModuleMgr->getModule( $format, 'format', /* $ignoreCache */ true );
901 if ( $printer === null ) {
902 $this->dieWithError(
903 [ 'apierror-unknownformat', wfEscapeWikiText( $format ) ], 'unknown_format'
904 );
905 }
906
907 // @phan-suppress-next-line PhanTypeMismatchReturnSuperType
908 return $printer;
909 }
910
914 public function execute() {
915 if ( $this->mInternalMode ) {
916 $this->executeAction();
917 } else {
918 $this->executeActionWithErrorHandling();
919 }
920 }
921
926 protected function executeActionWithErrorHandling() {
927 // Verify the CORS header before executing the action
928 if ( !$this->handleCORS() ) {
929 // handleCORS() has sent a 403, abort
930 return;
931 }
932
933 // Exit here if the request method was OPTIONS
934 // (assume there will be a followup GET or POST)
935 if ( $this->getRequest()->getMethod() === 'OPTIONS' ) {
936 return;
937 }
938
939 // In case an error occurs during data output,
940 // clear the output buffer and print just the error information
941 $obLevel = ob_get_level();
942 ob_start();
943
944 $t = microtime( true );
945 $isError = false;
946 try {
947 $this->executeAction();
948 $runTime = microtime( true ) - $t;
949 $this->logRequest( $runTime );
950
951 $this->statsFactory->getTiming( 'api_executeTiming_seconds' )
952 ->setLabel( 'module', $this->mModule->getModuleName() )
953 ->observe( 1000 * $runTime );
954
955 if ( !$this->mModule || $this->mModule->getModuleName() !== 'query' ) {
956 // Skip query module metrics; we will record them in the query module itself.
957 $this->recordUnifiedMetrics();
958 }
959 } catch ( Throwable $e ) {
960 // If executeAction threw before the time was set, reset it
961 $runTime ??= microtime( true ) - $t;
962 $this->handleException( $e, $runTime );
963 $this->logRequest( microtime( true ) - $t, $e );
964 $isError = true;
965 }
966
967 // Disable the client cache on the output so that BlockManager::trackBlockWithCookie is executed
968 // as part of MediaWiki::preOutputCommit().
969 if (
970 $this->mCacheMode === 'private'
971 || (
972 $this->mCacheMode === 'anon-public-user-private'
973 && $this->getRequest()->getSession()->isPersistent()
974 )
975 ) {
976 $this->getContext()->getOutput()->disableClientCache();
977 $this->getContext()->getOutput()->considerCacheSettingsFinal();
978 }
979
980 // Commit DBs and send any related cookies and headers
981 MediaWiki::preOutputCommit( $this->getContext() );
982
983 // Send cache headers after any code which might generate an error, to
984 // avoid sending public cache headers for errors.
985 $this->sendCacheHeaders( $isError );
986
987 // Executing the action might have already messed with the output
988 // buffers.
989 while ( ob_get_level() > $obLevel ) {
990 ob_end_flush();
991 }
992 }
993
1001 protected function handleException( Throwable $e, $latency = 0 ) {
1002 $statsModuleName = $this->mModule ? $this->mModule->getModuleName() : 'main';
1003
1004 // Collect stats on errors (T396613).
1005 // NOTE: We only count fatal errors, a mere call to addError() or
1006 // addWarning() does not count towards these states. That could
1007 // be added in the future, but should use a different stats key.
1008 $stats = $this->statsFactory->getCounter( 'api_errors' )
1009 ->setLabel( 'module', $statsModuleName );
1010
1011 // T65145: Rollback any open database transactions
1012 if ( !$e instanceof ApiUsageException ) {
1013 // ApiUsageExceptions are intentional, so don't rollback if that's the case
1014 MWExceptionHandler::rollbackPrimaryChangesAndLog(
1015 $e,
1016 MWExceptionHandler::CAUGHT_BY_ENTRYPOINT
1017 );
1018 $stats->setLabel( 'exception_cause', 'server-error' );
1019 } else {
1020 $stats->setLabel( 'exception_cause', 'client-error' );
1021 }
1022
1023 // Allow extra cleanup and logging
1024 $this->getHookRunner()->onApiMain__onException( $this, $e );
1025
1026 // Handle any kind of exception by outputting properly formatted error message.
1027 // If this fails, an unhandled exception should be thrown so that global error
1028 // handler will process and log it.
1029
1030 $errCodes = $this->substituteResultWithError( $e );
1031 sort( $errCodes );
1032
1033 // Error results should not be cached
1034 $this->setCacheMode( 'private' );
1035
1036 $response = $this->getRequest()->response();
1037 $headerStr = 'MediaWiki-API-Error: ' . implode( ', ', $errCodes );
1038 $response->header( $headerStr );
1039
1040 // Reset and print just the error message
1041 ob_clean();
1042
1043 // Printer may not be initialized if the extractRequestParams() fails for the main module
1044 $this->createErrorPrinter();
1045
1046 $stats->setLabel( 'error_code', implode( '_', $errCodes ) );
1047 $stats->increment();
1048
1049 // Unified metrics
1050 if ( !$this->mModule || $this->mModule->getModuleName() !== 'query' ) {
1051 // Skip query module metrics; we will record them in the query module itself.
1052 $this->recordUnifiedMetrics(
1053 [
1054 'status' => implode( '_', $errCodes ), // Failure codes
1055 ]
1056 );
1057
1058 }
1059
1060 // Get desired HTTP code from an ApiUsageException. Don't use codes from other
1061 // exception types, as they are unlikely to be intended as an HTTP code.
1062 $httpCode = $e instanceof ApiUsageException ? $e->getCode() : 0;
1063
1064 $failed = false;
1065 try {
1066 $this->printResult( $httpCode );
1067 } catch ( ApiUsageException $ex ) {
1068 // The error printer itself is failing. Try suppressing its request
1069 // parameters and redo.
1070 $failed = true;
1071 $this->addWarning( 'apiwarn-errorprinterfailed' );
1072 foreach ( $ex->getStatusValue()->getMessages() as $error ) {
1073 try {
1074 $this->mPrinter->addWarning( $error );
1075 } catch ( Throwable ) {
1076 // WTF?
1077 $this->addWarning( $error );
1078 }
1079 }
1080 }
1081 if ( $failed ) {
1082 $this->mPrinter = null;
1083 $this->createErrorPrinter();
1084 // @phan-suppress-next-line PhanNonClassMethodCall False positive
1085 $this->mPrinter->forceDefaultParams();
1086 if ( $httpCode ) {
1087 $response->statusHeader( 200 ); // Reset in case the fallback doesn't want a non-200
1088 }
1089 $this->printResult( $httpCode );
1090 }
1091 }
1092
1103 public static function handleApiBeforeMainException( Throwable $e ) {
1104 ob_start();
1105
1106 try {
1107 $main = new self( RequestContext::getMain(), false );
1108 $main->handleException( $e );
1109 $main->logRequest( 0, $e );
1110 } catch ( Throwable ) {
1111 // Nope, even that didn't work. Punt.
1112 throw $e;
1113 }
1114
1115 // Reset cache headers
1116 $main->sendCacheHeaders( true );
1117
1118 ob_end_flush();
1119 }
1120
1142 public function handleCORS() {
1143 $originParam = $this->getParameter( 'origin' ); // defaults to null
1144 $crossOriginParam = $this->getParameter( 'crossorigin' ); // defaults to false
1145 if ( $originParam === null && !$crossOriginParam ) {
1146 // No origin/crossorigin parameter, nothing to do
1147 return true;
1148 }
1149
1150 $request = $this->getRequest();
1151 $response = $request->response();
1152 $requestedMethod = $request->getHeader( 'Access-Control-Request-Method' );
1153 $preflight = $request->getMethod() === 'OPTIONS' && $requestedMethod !== false;
1154
1155 $allowTiming = false;
1156 $varyOrigin = true;
1157
1158 if ( $originParam !== null && $crossOriginParam ) {
1159 $response->statusHeader( 403 );
1160 $response->header( 'Cache-control: no-cache' );
1161 echo "'origin' and 'crossorigin' parameters cannot be used together\n";
1162
1163 return false;
1164 }
1165 if ( $crossOriginParam && !$request->getSession()->getProvider()->safeAgainstCsrf() && !$preflight ) {
1166 $response->statusHeader( 403 );
1167 $response->header( 'Cache-control: no-cache' );
1168 $language = MediaWikiServices::getInstance()->getLanguageFactory()->getLanguage( 'en' );
1169 $described = $request->getSession()->getProvider()->describe( $language );
1170 echo "'crossorigin' cannot be used with $described\n";
1171
1172 return false;
1173 }
1174
1175 if ( $originParam === '*' || $crossOriginParam ) {
1176 // Request for CORS without browser-supplied credentials (e.g. cookies):
1177 // may be anonymous (origin=*) or authenticated with request-supplied
1178 // credentials (crossorigin=1 + Authorization header).
1179 // Technically we should check for the presence of an Origin header
1180 // and not process it as CORS if it's not set, but that would
1181 // require us to vary on Origin for all 'origin=*' requests which
1182 // we don't want to do.
1183 $matchedOrigin = true;
1184 $allowOrigin = '*';
1185 $allowCredentials = 'false';
1186 $varyOrigin = false; // No need to vary
1187 } else {
1188 // Non-anonymous CORS, check we allow the domain
1189
1190 // Origin: header is a space-separated list of origins, check all of them
1191 $originHeader = $request->getHeader( 'Origin' );
1192 if ( $originHeader === false ) {
1193 $origins = [];
1194 } else {
1195 $originHeader = trim( $originHeader );
1196 $origins = preg_split( '/\s+/', $originHeader );
1197 }
1198
1199 if ( !in_array( $originParam, $origins ) ) {
1200 // origin parameter set but incorrect
1201 // Send a 403 response
1202 $response->statusHeader( 403 );
1203 $response->header( 'Cache-Control: no-cache' );
1204 echo "'origin' parameter does not match Origin header\n";
1205
1206 return false;
1207 }
1208
1209 $config = $this->getConfig();
1210 $origin = Origin::parseHeaderList( $origins );
1211 $matchedOrigin = $origin->match(
1214 );
1215
1216 $allowOrigin = $originHeader;
1217 $allowCredentials = 'true';
1218 $allowTiming = $originHeader;
1219 }
1220
1221 if ( $matchedOrigin ) {
1222 if ( $preflight ) {
1223 // We allow the actual request to send the following headers
1224 $requestedHeaders = $request->getHeader( 'Access-Control-Request-Headers' );
1225 $allowedHeaders = $this->getConfig()->get( MainConfigNames::AllowedCorsHeaders );
1226 if ( $requestedHeaders !== false ) {
1227 if ( !self::matchRequestedHeaders( $requestedHeaders, $allowedHeaders ) ) {
1228 $response->header( 'MediaWiki-CORS-Rejection: Unsupported header requested in preflight' );
1229 return true;
1230 }
1231 $response->header( 'Access-Control-Allow-Headers: ' . $requestedHeaders );
1232 }
1233
1234 // We only allow the actual request to be GET, POST, or HEAD
1235 $response->header( 'Access-Control-Allow-Methods: POST, GET, HEAD' );
1236 }
1237
1238 $response->header( "Access-Control-Allow-Origin: $allowOrigin" );
1239 $response->header( "Access-Control-Allow-Credentials: $allowCredentials" );
1240 // https://www.w3.org/TR/resource-timing/#timing-allow-origin
1241 if ( $allowTiming !== false ) {
1242 $response->header( "Timing-Allow-Origin: $allowTiming" );
1243 }
1244
1245 if ( !$preflight ) {
1246 $response->header(
1247 'Access-Control-Expose-Headers: MediaWiki-API-Error, Retry-After, X-Database-Lag, '
1248 . 'MediaWiki-Login-Suppressed'
1249 );
1250 }
1251 } else {
1252 $response->header( 'MediaWiki-CORS-Rejection: Origin mismatch' );
1253 }
1254
1255 if ( $varyOrigin ) {
1256 $this->getOutput()->addVaryHeader( 'Origin' );
1257 }
1258
1259 return true;
1260 }
1261
1270 protected static function matchRequestedHeaders( $requestedHeaders, $allowedHeaders ) {
1271 if ( trim( $requestedHeaders ) === '' ) {
1272 return true;
1273 }
1274 $requestedHeaders = explode( ',', $requestedHeaders );
1275 $allowedHeaders = array_change_key_case(
1276 array_fill_keys( $allowedHeaders, true ), CASE_LOWER );
1277 foreach ( $requestedHeaders as $rHeader ) {
1278 $rHeader = strtolower( trim( $rHeader ) );
1279 if ( !isset( $allowedHeaders[$rHeader] ) ) {
1280 LoggerFactory::getInstance( 'api-warning' )->warning(
1281 'CORS preflight failed on requested header: {header}', [
1282 'header' => $rHeader
1283 ]
1284 );
1285 return false;
1286 }
1287 }
1288 return true;
1289 }
1290
1296 protected function sendCacheHeaders( $isError ) {
1297 $response = $this->getRequest()->response();
1298 $out = $this->getOutput();
1299
1300 $out->addVaryHeader( 'Treat-as-Untrusted' );
1301
1302 $config = $this->getConfig();
1303
1304 if ( $config->get( MainConfigNames::VaryOnXFP ) ) {
1305 $out->addVaryHeader( 'X-Forwarded-Proto' );
1306 }
1307
1308 if ( !$isError && $this->mModule &&
1309 ( $this->getRequest()->getMethod() === 'GET' || $this->getRequest()->getMethod() === 'HEAD' )
1310 ) {
1311 $etag = $this->mModule->getConditionalRequestData( 'etag' );
1312 if ( $etag !== null ) {
1313 $response->header( "ETag: $etag" );
1314 }
1315 $lastMod = $this->mModule->getConditionalRequestData( 'last-modified' );
1316 if ( $lastMod !== null ) {
1317 $response->header( 'Last-Modified: ' . wfTimestamp( TS::RFC2822, $lastMod ) );
1318 }
1319 }
1320
1321 // The logic should be:
1322 // $this->mCacheControl['max-age'] is set?
1323 // Use it, the module knows better than our guess.
1324 // !$this->mModule || $this->mModule->isWriteMode(), and mCacheMode is private?
1325 // Use 0 because we can guess caching is probably the wrong thing to do.
1326 // Use $this->getParameter( 'maxage' ), which already defaults to 0.
1327 $maxage = 0;
1328 if ( isset( $this->mCacheControl['max-age'] ) ) {
1329 $maxage = $this->mCacheControl['max-age'];
1330 } elseif ( ( !$isError && $this->mModule && !$this->mModule->isWriteMode() ) ||
1331 $this->mCacheMode !== 'private'
1332 ) {
1333 $maxage = $this->getParameter( 'maxage' );
1334 }
1335 $privateCache = 'private, must-revalidate, max-age=' . $maxage;
1336
1337 if ( $this->mCacheMode == 'private' ) {
1338 $response->header( "Cache-Control: $privateCache" );
1339 return;
1340 }
1341
1342 if ( $this->mCacheMode == 'anon-public-user-private' ) {
1343 $out->addVaryHeader( 'Cookie' );
1344 $response->header( $out->getVaryHeader() );
1345 if ( $this->getRequest()->getSession()->isPersistent() ) {
1346 // Logged in or otherwise has session (e.g. anonymous users who have edited)
1347 // Mark request private
1348 $response->header( "Cache-Control: $privateCache" );
1349
1350 return;
1351 } // else anonymous, send public headers below
1352 }
1353
1354 // Send public headers
1355 $response->header( $out->getVaryHeader() );
1356
1357 // If nobody called setCacheMaxAge(), use the (s)maxage parameters
1358 if ( !isset( $this->mCacheControl['s-maxage'] ) ) {
1359 $this->mCacheControl['s-maxage'] = $this->getParameter( 'smaxage' );
1360 }
1361 if ( !isset( $this->mCacheControl['max-age'] ) ) {
1362 $this->mCacheControl['max-age'] = $this->getParameter( 'maxage' );
1363 }
1364
1365 if ( !$this->mCacheControl['s-maxage'] && !$this->mCacheControl['max-age'] ) {
1366 // Public cache not requested
1367 // Sending a Vary header in this case is harmless, and protects us
1368 // against conditional calls of setCacheMaxAge().
1369 $response->header( "Cache-Control: $privateCache" );
1370
1371 return;
1372 }
1373
1374 $this->mCacheControl['public'] = true;
1375
1376 // Send an Expires header
1377 $maxAge = min( $this->mCacheControl['s-maxage'], $this->mCacheControl['max-age'] );
1378 $expiryUnixTime = ( $maxAge == 0 ? 1 : time() + $maxAge );
1379 $response->header( 'Expires: ' . wfTimestamp( TS::RFC2822, $expiryUnixTime ) );
1380
1381 // Construct the Cache-Control header
1382 $ccHeader = '';
1383 $separator = '';
1384 foreach ( $this->mCacheControl as $name => $value ) {
1385 if ( is_bool( $value ) ) {
1386 if ( $value ) {
1387 $ccHeader .= $separator . $name;
1388 $separator = ', ';
1389 }
1390 } else {
1391 $ccHeader .= $separator . "$name=$value";
1392 $separator = ', ';
1393 }
1394 }
1395
1396 $response->header( "Cache-Control: $ccHeader" );
1397 }
1398
1402 private function createErrorPrinter() {
1403 if ( !$this->mPrinter ) {
1404 $value = $this->getRequest()->getVal( 'format', self::API_DEFAULT_FORMAT );
1405 if ( !$this->mModuleMgr->isDefined( $value, 'format' ) ) {
1406 $value = self::API_DEFAULT_FORMAT;
1407 }
1408 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable getVal does not return null here
1409 $this->mPrinter = $this->createPrinterByName( $value );
1410 }
1411
1412 // Printer may not be able to handle errors. This is particularly
1413 // likely if the module returns something for getCustomPrinter().
1414 if ( !$this->mPrinter->canPrintErrors() ) {
1415 $this->mPrinter = $this->createPrinterByName( self::API_DEFAULT_FORMAT );
1416 }
1417 }
1418
1434 protected function errorMessagesFromException( Throwable $e, $type = 'error' ) {
1435 $messages = [];
1436 if ( $e instanceof ApiUsageException ) {
1437 foreach ( $e->getStatusValue()->getMessages( $type ) as $msg ) {
1438 $messages[] = ApiMessage::create( $msg );
1439 }
1440 } elseif ( $type !== 'error' ) {
1441 // None of the rest have any messages for non-error types
1442 } else {
1443 // TODO: Avoid embedding arbitrary class names in the error code.
1444 $class = preg_replace( '#^Wikimedia\\\\Rdbms\\\\#', '', get_class( $e ) );
1445 $code = 'internal_api_error_' . $class;
1446 $data = [ 'errorclass' => get_class( $e ) ];
1447 if ( MWExceptionRenderer::shouldShowExceptionDetails() ) {
1448 if ( $e instanceof ILocalizedException ) {
1449 $msg = $e->getMessageObject();
1450 } elseif ( $e instanceof MessageSpecifier ) {
1451 $msg = Message::newFromSpecifier( $e );
1452 } else {
1453 $msg = wfEscapeWikiText( $e->getMessage() );
1454 }
1455 $params = [ 'apierror-exceptioncaught', WebRequest::getRequestId(), $msg ];
1456 } else {
1457 $params = [ 'apierror-exceptioncaughttype', WebRequest::getRequestId(), get_class( $e ) ];
1458 }
1459
1460 $messages[] = ApiMessage::create( $params, $code, $data );
1461 }
1462 return $messages;
1463 }
1464
1470 protected function substituteResultWithError( Throwable $e ) {
1471 $result = $this->getResult();
1472 $formatter = $this->getErrorFormatter();
1473 $config = $this->getConfig();
1474 $errorCodes = [];
1475
1476 // Remember existing warnings and errors across the reset
1477 $errors = $result->getResultData( [ 'errors' ] );
1478 $warnings = $result->getResultData( [ 'warnings' ] );
1479 $result->reset();
1480 if ( $warnings !== null ) {
1481 $result->addValue( null, 'warnings', $warnings, ApiResult::NO_SIZE_CHECK );
1482 }
1483 if ( $errors !== null ) {
1484 $result->addValue( null, 'errors', $errors, ApiResult::NO_SIZE_CHECK );
1485
1486 // Collect the copied error codes for the return value
1487 foreach ( $errors as $error ) {
1488 if ( isset( $error['code'] ) ) {
1489 $errorCodes[$error['code']] = true;
1490 }
1491 }
1492 }
1493
1494 // Add errors from the exception
1495 $modulePath = $e instanceof ApiUsageException ? $e->getModulePath() : null;
1496 foreach ( $this->errorMessagesFromException( $e, 'error' ) as $msg ) {
1497 if ( ApiErrorFormatter::isValidApiCode( $msg->getApiCode() ) ) {
1498 $errorCodes[$msg->getApiCode()] = true;
1499 } else {
1500 LoggerFactory::getInstance( 'api-warning' )->error( 'Invalid API error code "{code}"', [
1501 'code' => $msg->getApiCode(),
1502 'exception' => $e,
1503 ] );
1504 $errorCodes['<invalid-code>'] = true;
1505 }
1506 $formatter->addError( $modulePath, $msg );
1507 }
1508 foreach ( $this->errorMessagesFromException( $e, 'warning' ) as $msg ) {
1509 $formatter->addWarning( $modulePath, $msg );
1510 }
1511
1512 // Add additional data. Path depends on whether we're in BC mode or not.
1513 // Data depends on the type of exception.
1514 if ( $formatter instanceof ApiErrorFormatter_BackCompat ) {
1515 $path = [ 'error' ];
1516 } else {
1517 $path = null;
1518 }
1519 if ( $e instanceof ApiUsageException ) {
1520 $link = (string)MediaWikiServices::getInstance()->getUrlUtils()->expand( wfScript( 'api' ) );
1521 $result->addContentValue(
1522 $path,
1523 'docref',
1524 trim(
1525 $this->msg( 'api-usage-docref', $link )->inLanguage( $formatter->getLanguage() )->text()
1526 . ' '
1527 . $this->msg( 'api-usage-mailinglist-ref' )->inLanguage( $formatter->getLanguage() )->text()
1528 )
1529 );
1530 } elseif ( $config->get( MainConfigNames::ShowExceptionDetails ) ) {
1531 $result->addContentValue(
1532 $path,
1533 'trace',
1534 $this->msg( 'api-exception-trace',
1535 get_class( $e ),
1536 $e->getFile(),
1537 $e->getLine(),
1538 MWExceptionHandler::getRedactedTraceAsString( $e )
1539 )->inLanguage( $formatter->getLanguage() )->text()
1540 );
1541 }
1542
1543 // Add the id and such
1544 $this->addRequestedFields( [ 'servedby' ] );
1545
1546 return array_keys( $errorCodes );
1547 }
1548
1554 protected function addRequestedFields( $force = [] ) {
1555 $result = $this->getResult();
1556
1557 $requestid = $this->getParameter( 'requestid' );
1558 if ( $requestid !== null ) {
1559 $result->addValue( null, 'requestid', $requestid, ApiResult::NO_SIZE_CHECK );
1560 }
1561
1562 if ( $this->getConfig()->get( MainConfigNames::ShowHostnames ) && (
1563 in_array( 'servedby', $force, true ) || $this->getParameter( 'servedby' )
1564 ) ) {
1565 $result->addValue( null, 'servedby', wfHostname(), ApiResult::NO_SIZE_CHECK );
1566 }
1567
1568 if ( $this->getParameter( 'curtimestamp' ) ) {
1569 $result->addValue( null, 'curtimestamp', wfTimestamp( TS::ISO_8601 ), ApiResult::NO_SIZE_CHECK );
1570 }
1571
1572 if ( $this->getParameter( 'responselanginfo' ) ) {
1573 $result->addValue(
1574 null,
1575 'uselang',
1576 $this->getLanguage()->getCode(),
1578 );
1579 $result->addValue(
1580 null,
1581 'errorlang',
1582 $this->getErrorFormatter()->getLanguage()->getCode(),
1584 );
1585 }
1586 }
1587
1592 protected function setupExecuteAction() {
1593 $this->addRequestedFields();
1594
1595 $params = $this->extractRequestParams();
1596 $this->mAction = $params['action'];
1597
1598 return $params;
1599 }
1600
1606 protected function setupModule() {
1607 // Instantiate the module requested by the user
1608 $module = $this->mModuleMgr->getModule( $this->mAction, 'action' );
1609 if ( $module === null ) {
1610 // Probably can't happen
1611 // @codeCoverageIgnoreStart
1612 $this->dieWithError(
1613 [ 'apierror-unknownaction', wfEscapeWikiText( $this->mAction ) ],
1614 'unknown_action'
1615 );
1616 // @codeCoverageIgnoreEnd
1617 }
1618 $moduleParams = $module->extractRequestParams();
1619
1620 // Check token, if necessary
1621 if ( $module->needsToken() === true ) {
1622 throw new LogicException(
1623 "Module '{$module->getModuleName()}' must be updated for the new token handling. " .
1624 'See documentation for ApiBase::needsToken for details.'
1625 );
1626 }
1627 if ( $module->needsToken() ) {
1628 if ( !$module->mustBePosted() ) {
1629 throw new LogicException(
1630 "Module '{$module->getModuleName()}' must require POST to use tokens."
1631 );
1632 }
1633
1634 if ( !isset( $moduleParams['token'] ) ) {
1635 // Probably can't happen
1636 // @codeCoverageIgnoreStart
1637 $module->dieWithError( [ 'apierror-missingparam', 'token' ] );
1638 // @codeCoverageIgnoreEnd
1639 }
1640
1641 $module->requirePostedParameters( [ 'token' ] );
1642
1643 if ( !$module->validateToken( $moduleParams['token'], $moduleParams ) ) {
1644 $module->dieWithError( 'apierror-badtoken' );
1645 }
1646 }
1647
1648 return $module;
1649 }
1650
1654 private function getMaxLag() {
1655 $services = MediaWikiServices::getInstance();
1656 $dbLag = $services->getDBLoadBalancer()->getMaxLag();
1657 $lagInfo = [
1658 'host' => $dbLag[0],
1659 'lag' => $dbLag[1],
1660 'type' => 'db'
1661 ];
1662
1663 $jobQueueLagFactor =
1664 $this->getConfig()->get( MainConfigNames::JobQueueIncludeInMaxLagFactor );
1665 if ( $jobQueueLagFactor ) {
1666 // Turn total number of jobs into seconds by using the configured value
1667 $totalJobs = array_sum( $services->getJobQueueGroup()->getQueueSizes() );
1668 $jobQueueLag = $totalJobs / (float)$jobQueueLagFactor;
1669 if ( $jobQueueLag > $lagInfo['lag'] ) {
1670 $lagInfo = [
1671 'host' => wfHostname(), // XXX: Is there a better value that could be used?
1672 'lag' => $jobQueueLag,
1673 'type' => 'jobqueue',
1674 'jobs' => $totalJobs,
1675 ];
1676 }
1677 }
1678
1679 $this->getHookRunner()->onApiMaxLagInfo( $lagInfo );
1680
1681 return $lagInfo;
1682 }
1683
1690 protected function checkMaxLag( $module, $params ) {
1691 if ( $module->shouldCheckMaxlag() && isset( $params['maxlag'] ) ) {
1692 $maxLag = $params['maxlag'];
1693 $lagInfo = $this->getMaxLag();
1694 if ( $lagInfo['lag'] > $maxLag ) {
1695 $response = $this->getRequest()->response();
1696
1697 $response->header( 'Retry-After: ' . max( (int)$maxLag, 5 ) );
1698 $response->header( 'X-Database-Lag: ' . (int)$lagInfo['lag'] );
1699
1700 if ( $this->getConfig()->get( MainConfigNames::ShowHostnames ) ) {
1701 $this->dieWithError(
1702 [ 'apierror-maxlag', $lagInfo['lag'], $lagInfo['host'] ],
1703 'maxlag',
1704 $lagInfo
1705 );
1706 }
1707
1708 $this->dieWithError( [ 'apierror-maxlag-generic', $lagInfo['lag'] ], 'maxlag', $lagInfo );
1709 }
1710 }
1711
1712 return true;
1713 }
1714
1736 protected function checkConditionalRequestHeaders( $module ) {
1737 if ( $this->mInternalMode ) {
1738 // No headers to check in internal mode
1739 return true;
1740 }
1741
1742 if ( $this->getRequest()->getMethod() !== 'GET' && $this->getRequest()->getMethod() !== 'HEAD' ) {
1743 // Don't check POSTs
1744 return true;
1745 }
1746
1747 $return304 = false;
1748
1749 $ifNoneMatch = array_diff(
1750 $this->getRequest()->getHeader( 'If-None-Match', WebRequest::GETHEADER_LIST ) ?: [],
1751 [ '' ]
1752 );
1753 if ( $ifNoneMatch ) {
1754 // @phan-suppress-next-line PhanImpossibleTypeComparison
1755 if ( $ifNoneMatch === [ '*' ] ) {
1756 // API responses always "exist"
1757 $etag = '*';
1758 } else {
1759 $etag = $module->getConditionalRequestData( 'etag' );
1760 }
1761 }
1762 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable $etag is declared when $ifNoneMatch is true
1763 if ( $ifNoneMatch && $etag !== null ) {
1764 $test = str_starts_with( $etag, 'W/' ) ? substr( $etag, 2 ) : $etag;
1765 $match = array_map( static function ( $s ) {
1766 return str_starts_with( $s, 'W/' ) ? substr( $s, 2 ) : $s;
1767 }, $ifNoneMatch );
1768 $return304 = in_array( $test, $match, true );
1769 } else {
1770 $value = trim( $this->getRequest()->getHeader( 'If-Modified-Since' ) );
1771
1772 // Some old browsers sends sizes after the date, like this:
1773 // Wed, 20 Aug 2003 06:51:19 GMT; length=5202
1774 // Ignore that.
1775 $i = strpos( $value, ';' );
1776 if ( $i !== false ) {
1777 $value = trim( substr( $value, 0, $i ) );
1778 }
1779
1780 if ( $value !== '' ) {
1781 try {
1782 $ts = new ConvertibleTimestamp( $value );
1783 if (
1784 // RFC 7231 IMF-fixdate
1785 $ts->getTimestamp( TS::RFC2822 ) === $value ||
1786 // RFC 850
1787 $ts->format( 'l, d-M-y H:i:s' ) . ' GMT' === $value ||
1788 // asctime (with and without space-padded day)
1789 $ts->format( 'D M j H:i:s Y' ) === $value ||
1790 $ts->format( 'D M j H:i:s Y' ) === $value
1791 ) {
1792 $config = $this->getConfig();
1793 $lastMod = $module->getConditionalRequestData( 'last-modified' );
1794 if ( $lastMod !== null ) {
1795 // Mix in some MediaWiki modification times
1796 $modifiedTimes = [
1797 'page' => $lastMod,
1798 'user' => $this->getUser()->getTouched(),
1799 'epoch' => $config->get( MainConfigNames::CacheEpoch ),
1800 ];
1801
1802 if ( $config->get( MainConfigNames::UseCdn ) ) {
1803 // T46570: the core page itself may not change, but resources might
1804 $modifiedTimes['sepoch'] = wfTimestamp(
1805 TS::MW, time() - $config->get( MainConfigNames::CdnMaxAge )
1806 );
1807 }
1808 $this->getHookRunner()->onOutputPageCheckLastModified( $modifiedTimes, $this->getOutput() );
1809 $lastMod = max( $modifiedTimes );
1810 $return304 = wfTimestamp( TS::MW, $lastMod ) <= $ts->getTimestamp( TS::MW );
1811 }
1812 }
1813 } catch ( TimestampException ) {
1814 // Invalid timestamp, ignore it
1815 }
1816 }
1817 }
1818
1819 if ( $return304 ) {
1820 $this->getRequest()->response()->statusHeader( 304 );
1821
1822 // Avoid outputting the compressed representation of a zero-length body
1823 // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
1824 @ini_set( 'zlib.output_compression', 0 );
1825 wfResetOutputBuffers( false );
1826
1827 return false;
1828 }
1829
1830 return true;
1831 }
1832
1837 protected function checkExecutePermissions( $module ) {
1838 $user = $this->getUser();
1839 if ( $module->isReadMode() && !$this->getPermissionManager()->isEveryoneAllowed( 'read' ) &&
1840 !$this->getAuthority()->isAllowed( 'read' )
1841 ) {
1842 $this->dieWithError( 'apierror-readapidenied' );
1843 }
1844
1845 if ( $module->isWriteMode() ) {
1846 if ( !$this->mEnableWrite ) {
1847 $this->dieWithError( 'apierror-noapiwrite' );
1848 } elseif ( $this->getRequest()->getHeader( 'Promise-Non-Write-API-Action' ) ) {
1849 $this->dieWithError( 'apierror-promised-nonwrite-api' );
1850 }
1851
1852 $this->checkReadOnly( $module );
1853 }
1854
1855 // Allow extensions to stop execution for arbitrary reasons.
1856 // TODO: change hook to accept Authority
1857 $message = 'hookaborted';
1858 if ( !$this->getHookRunner()->onApiCheckCanExecute( $module, $user, $message ) ) {
1859 $this->dieWithError( $message );
1860 }
1861 }
1862
1867 protected function checkReadOnly( $module ) {
1868 if ( MediaWikiServices::getInstance()->getReadOnlyMode()->isReadOnly() ) {
1869 $this->dieReadOnly();
1870 }
1871
1872 if ( $module->isWriteMode()
1873 && $this->getUser()->isBot()
1874 && MediaWikiServices::getInstance()->getDBLoadBalancer()->hasReplicaServers()
1875 ) {
1876 $this->checkBotReadOnly();
1877 }
1878 }
1879
1883 private function checkBotReadOnly() {
1884 // Figure out how many servers have passed the lag threshold
1885 $numLagged = 0;
1886 $lagLimit = $this->getConfig()->get( MainConfigNames::APIMaxLagThreshold );
1887 $laggedServers = [];
1888 $loadBalancer = MediaWikiServices::getInstance()->getDBLoadBalancer();
1889 foreach ( $loadBalancer->getLagTimes() as $serverIndex => $lag ) {
1890 if ( $lag > $lagLimit ) {
1891 ++$numLagged;
1892 $laggedServers[] = $loadBalancer->getServerName( $serverIndex ) . " ({$lag}s)";
1893 }
1894 }
1895
1896 // If a majority of replica DBs are too lagged then disallow writes
1897 $replicaCount = $loadBalancer->getServerCount() - 1;
1898 if ( $numLagged >= ceil( $replicaCount / 2 ) ) {
1899 $laggedServers = implode( ', ', $laggedServers );
1900 wfDebugLog(
1901 'api-readonly', // Deprecate this channel in favor of api-warning?
1902 "Api request failed as read only because the following DBs are lagged: $laggedServers"
1903 );
1904 LoggerFactory::getInstance( 'api-warning' )->warning(
1905 "Api request failed as read only because the following DBs are lagged: {laggeddbs}", [
1906 'laggeddbs' => $laggedServers,
1907 ]
1908 );
1909
1910 $this->dieWithError(
1911 'readonly_lag',
1912 'readonly',
1913 [ 'readonlyreason' => "Waiting for $numLagged lagged database(s)" ]
1914 );
1915 }
1916 }
1917
1922 protected function checkAsserts( $params ) {
1923 if ( isset( $params['assert'] ) ) {
1924 $user = $this->getUser();
1925 switch ( $params['assert'] ) {
1926 case 'anon':
1927 if ( $user->isRegistered() ) {
1928 $this->dieWithError( 'apierror-assertanonfailed' );
1929 }
1930 break;
1931 case 'user':
1932 if ( !$user->isRegistered() ) {
1933 $this->dieWithError( 'apierror-assertuserfailed' );
1934 }
1935 break;
1936 case 'bot':
1937 if ( !$this->getAuthority()->isAllowed( 'bot' ) ) {
1938 $this->dieWithError( 'apierror-assertbotfailed' );
1939 }
1940 break;
1941 }
1942 }
1943 if ( isset( $params['assertuser'] ) ) {
1944 // TODO inject stuff, see T265644
1945 $assertUser = MediaWikiServices::getInstance()->getUserFactory()
1946 ->newFromName( $params['assertuser'], UserRigorOptions::RIGOR_NONE );
1947 if ( !$assertUser || !$this->getUser()->equals( $assertUser ) ) {
1948 $this->dieWithError(
1949 [ 'apierror-assertnameduserfailed', wfEscapeWikiText( $params['assertuser'] ) ]
1950 );
1951 }
1952 }
1953 }
1954
1960 protected function setupExternalResponse( $module, $params ) {
1961 $validMethods = [ 'GET', 'HEAD', 'POST', 'OPTIONS' ];
1962 $request = $this->getRequest();
1963
1964 if ( !in_array( $request->getMethod(), $validMethods ) ) {
1965 $this->dieWithError( 'apierror-invalidmethod', null, null, 405 );
1966 }
1967
1968 if ( !$request->wasPosted() && $module->mustBePosted() ) {
1969 // Module requires POST. GET request might still be allowed
1970 // if $wgDebugApi is true, otherwise fail.
1971 $this->dieWithErrorOrDebug( [ 'apierror-mustbeposted', $this->mAction ] );
1972 }
1973
1974 if ( $request->wasPosted() ) {
1975 if ( !$request->getHeader( 'Content-Type' ) ) {
1976 $this->addDeprecation(
1977 'apiwarn-deprecation-post-without-content-type', 'post-without-content-type'
1978 );
1979 }
1980 $contentLength = $request->getHeader( 'Content-Length' );
1981 $maxPostSize = wfShorthandToInteger( ini_get( 'post_max_size' ), 0 );
1982 if ( $maxPostSize && $contentLength > $maxPostSize ) {
1983 $this->dieWithError(
1984 [ 'apierror-http-contenttoolarge', Message::sizeParam( $maxPostSize ) ],
1985 null, null, 413
1986 );
1987 }
1988 if ( array_intersect_key(
1989 array_diff_assoc( $request->getPostValues(), $request->getQueryValuesOnly() ),
1990 $request->getQueryValuesOnly() ) ) {
1991 $this->dieWithError(
1992 [ 'apierror-invalidpostparams' ], null, null, 400
1993 );
1994 }
1995 }
1996
1997 // See if custom printer is used
1998 $this->mPrinter = $module->getCustomPrinter() ??
1999 // Create an appropriate printer if not set
2000 $this->createPrinterByName( $params['format'] );
2001
2002 if ( $request->getProtocol() === 'http' &&
2003 (
2004 $this->getConfig()->get( MainConfigNames::ForceHTTPS ) ||
2005 $request->getSession()->shouldForceHTTPS() ||
2006 $this->getUser()->requiresHTTPS()
2007 )
2008 ) {
2009 $this->addDeprecation( 'apiwarn-deprecation-httpsexpected', 'https-expected' );
2010 }
2011 }
2012
2016 protected function executeAction() {
2017 $params = $this->setupExecuteAction();
2018
2019 // Check asserts early so e.g. errors in parsing a module's parameters due to being
2020 // logged out don't override the client's intended "am I logged in?" check.
2021 $this->checkAsserts( $params );
2022
2023 $module = $this->setupModule();
2024 $this->mModule = $module;
2025
2026 if ( !$this->mInternalMode ) {
2027 ProfilingContext::singleton()->init( MW_ENTRY_POINT, $module->getModuleName() );
2028 $this->setRequestExpectations( $module );
2029 }
2030
2031 $this->checkExecutePermissions( $module );
2032
2033 if ( !$this->checkMaxLag( $module, $params ) ) {
2034 return;
2035 }
2036
2037 if ( !$this->checkConditionalRequestHeaders( $module ) ) {
2038 return;
2039 }
2040
2041 if ( !$this->mInternalMode ) {
2042 $this->setupExternalResponse( $module, $params );
2043 }
2044
2045 $scope = LoggerFactory::getContext()->addScoped( [
2046 'context.api_module_name' => $module->getModuleName(),
2047 'context.api_client_useragent' => $this->getUserAgent(),
2048 ] );
2049 $module->execute();
2050 ScopedCallback::consume( $scope );
2051 $this->getHookRunner()->onAPIAfterExecute( $module );
2052
2053 $this->reportUnusedParams();
2054
2055 if ( !$this->mInternalMode ) {
2056 MWDebug::appendDebugInfoToApiResult( $this->getContext(), $this->getResult() );
2057
2058 $this->printResult();
2059 }
2060 }
2061
2065 protected function setRequestExpectations( ApiBase $module ) {
2066 $request = $this->getRequest();
2067
2068 $trxLimits = $this->getConfig()->get( MainConfigNames::TrxProfilerLimits );
2069 $trxProfiler = Profiler::instance()->getTransactionProfiler();
2070 $trxProfiler->setLogger( LoggerFactory::getInstance( 'rdbms' ) );
2071 $trxProfiler->setStatsFactory( MediaWikiServices::getInstance()->getStatsFactory() );
2072 $trxProfiler->setRequestMethod( $request->getMethod() );
2073 if ( $request->hasSafeMethod() ) {
2074 $trxProfiler->setExpectations( $trxLimits['GET'], __METHOD__ );
2075 } elseif ( $request->wasPosted() && !$module->isWriteMode() ) {
2076 $trxProfiler->setExpectations( $trxLimits['POST-nonwrite'], __METHOD__ );
2077 } else {
2078 $trxProfiler->setExpectations( $trxLimits['POST'], __METHOD__ );
2079 }
2080 }
2081
2087 protected function logRequest( $time, ?Throwable $e = null ) {
2088 $request = $this->getRequest();
2089
2090 $user = $this->getUser();
2091 $performer = [
2092 'user_text' => $user->getName(),
2093 ];
2094 if ( $user->isRegistered() ) {
2095 $performer['user_id'] = $user->getId();
2096 }
2097 $logCtx = [
2098 // https://gerrit.wikimedia.org/g/mediawiki/event-schemas/+/master/jsonschema/mediawiki/api/request
2099 '$schema' => '/mediawiki/api/request/1.0.0',
2100 'meta' => [
2101 'request_id' => WebRequest::getRequestId(),
2103 ->getGlobalIdGenerator()->newUUIDv4(),
2104 'domain' => $this->getConfig()->get( MainConfigNames::ServerName ),
2105 // If using the EventBus extension (as intended) with this log channel,
2106 // this stream name will map to a Kafka topic.
2107 'stream' => 'mediawiki.api-request'
2108 ],
2109 'http' => [
2110 'method' => $request->getMethod(),
2111 'client_ip' => $request->getIP()
2112 ],
2113 'performer' => $performer,
2114 'database' => WikiMap::getCurrentWikiDbDomain()->getId(),
2115 'backend_time_ms' => (int)round( $time * 1000 ),
2116 ];
2117
2118 // If set, these headers will be logged in http.request_headers.
2119 $httpRequestHeadersToLog = [ 'accept-language', 'referer', 'user-agent', 'content-type' ];
2120 foreach ( $httpRequestHeadersToLog as $header ) {
2121 if ( $request->getHeader( $header ) ) {
2122 // Set the header in http.request_headers
2123 $logCtx['http']['request_headers'][$header] = $request->getHeader( $header );
2124 }
2125 }
2126
2127 if ( $e ) {
2128 $logCtx['api_error_codes'] = [];
2129 foreach ( $this->errorMessagesFromException( $e ) as $msg ) {
2130 $logCtx['api_error_codes'][] = $msg->getApiCode();
2131 }
2132 }
2133
2134 // Construct space separated message for 'api' log channel
2135 $msg = "API {$request->getMethod()} " .
2136 wfUrlencode( str_replace( ' ', '_', $this->getUser()->getName() ) ) .
2137 " {$logCtx['http']['client_ip']} " .
2138 "T={$logCtx['backend_time_ms']}ms";
2139
2140 $sensitive = array_fill_keys( $this->getSensitiveParams(), true );
2141 foreach ( $this->getParamsUsed() as $name ) {
2142 $value = $request->getVal( $name );
2143 if ( $value === null ) {
2144 continue;
2145 }
2146
2147 if ( isset( $sensitive[$name] ) ) {
2148 $value = '[redacted]';
2149 $encValue = '[redacted]';
2150 } elseif ( strlen( $value ) > 256 ) {
2151 $value = substr( $value, 0, 256 );
2152 $encValue = $this->encodeRequestLogValue( $value ) . '[...]';
2153 } else {
2154 $encValue = $this->encodeRequestLogValue( $value );
2155 }
2156
2157 $logCtx['params'][$name] = $value;
2158 $msg .= " {$name}={$encValue}";
2159 }
2160
2161 // Log an unstructured message to the api channel.
2162 wfDebugLog( 'api', $msg, 'private' );
2163
2164 // The api-request channel a structured data log channel.
2165 wfDebugLog( 'api-request', '', 'private', $logCtx );
2166 }
2167
2173 protected function encodeRequestLogValue( $s ) {
2174 static $table = [];
2175 if ( !$table ) {
2176 $chars = ';@$!*(),/:';
2177 $numChars = strlen( $chars );
2178 for ( $i = 0; $i < $numChars; $i++ ) {
2179 $table[rawurlencode( $chars[$i] )] = $chars[$i];
2180 }
2181 }
2182
2183 return strtr( rawurlencode( $s ), $table );
2184 }
2185
2190 protected function getParamsUsed() {
2191 return array_keys( $this->mParamsUsed );
2192 }
2193
2198 public function markParamsUsed( $params ) {
2199 $this->mParamsUsed += array_fill_keys( (array)$params, true );
2200 }
2201
2207 protected function getSensitiveParams() {
2208 return array_keys( $this->mParamsSensitive );
2209 }
2210
2220 public function markParamsSensitive( $params ) {
2221 $this->mParamsSensitive += array_fill_keys( (array)$params, true );
2222 }
2223
2230 public function getVal( $name, $default = null ) {
2231 $this->mParamsUsed[$name] = true;
2232
2233 $ret = $this->getRequest()->getVal( $name );
2234 if ( $ret === null ) {
2235 if ( $this->getRequest()->getArray( $name ) !== null ) {
2236 // See T12262 for why we don't just implode( '|', ... ) the
2237 // array.
2238 $this->addWarning( [ 'apiwarn-unsupportedarray', $name ] );
2239 }
2240 $ret = $default;
2241 }
2242 return $ret;
2243 }
2244
2251 public function getCheck( $name ) {
2252 $this->mParamsUsed[$name] = true;
2253 return $this->getRequest()->getCheck( $name );
2254 }
2255
2263 public function getUpload( $name ) {
2264 $this->mParamsUsed[$name] = true;
2265
2266 return $this->getRequest()->getUpload( $name );
2267 }
2268
2273 protected function reportUnusedParams() {
2274 $paramsUsed = $this->getParamsUsed();
2275 $allParams = $this->getRequest()->getValueNames();
2276
2277 if ( !$this->mInternalMode ) {
2278 // Printer has not yet executed; don't warn that its parameters are unused
2279 $printerParams = $this->mPrinter->encodeParamName(
2280 array_keys( $this->mPrinter->getFinalParams() ?: [] )
2281 );
2282 $unusedParams = array_diff( $allParams, $paramsUsed, $printerParams );
2283 } else {
2284 $unusedParams = array_diff( $allParams, $paramsUsed );
2285 }
2286
2287 if ( count( $unusedParams ) ) {
2288 $this->addWarning( [
2289 'apierror-unrecognizedparams',
2290 Message::listParam( array_map( wfEscapeWikiText( ... ), $unusedParams ), ListType::COMMA ),
2291 count( $unusedParams )
2292 ] );
2293 }
2294 }
2295
2301 protected function printResult( $httpCode = 0 ) {
2302 if ( $this->getConfig()->get( MainConfigNames::DebugAPI ) !== false ) {
2303 $this->addWarning( 'apiwarn-wgdebugapi' );
2304 }
2305
2306 $printer = $this->mPrinter;
2307 $printer->initPrinter( false );
2308 if ( $httpCode ) {
2309 $printer->setHttpStatus( $httpCode );
2310 }
2311 $printer->execute();
2312 $printer->closePrinter();
2313 }
2314
2318 public function isReadMode() {
2319 return false;
2320 }
2321
2327 public function getAllowedParams() {
2328 return [
2329 'action' => [
2330 ParamValidator::PARAM_DEFAULT => 'help',
2331 ParamValidator::PARAM_TYPE => 'submodule',
2332 ],
2333 'format' => [
2334 ParamValidator::PARAM_DEFAULT => self::API_DEFAULT_FORMAT,
2335 ParamValidator::PARAM_TYPE => 'submodule',
2336 ],
2337 'maxlag' => [
2338 ParamValidator::PARAM_TYPE => 'integer'
2339 ],
2340 'smaxage' => [
2341 ParamValidator::PARAM_TYPE => 'integer',
2342 ParamValidator::PARAM_DEFAULT => 0,
2343 IntegerDef::PARAM_MIN => 0,
2344 ],
2345 'maxage' => [
2346 ParamValidator::PARAM_TYPE => 'integer',
2347 ParamValidator::PARAM_DEFAULT => 0,
2348 IntegerDef::PARAM_MIN => 0,
2349 ],
2350 'assert' => [
2351 ParamValidator::PARAM_TYPE => [ 'anon', 'user', 'bot' ]
2352 ],
2353 'assertuser' => [
2354 ParamValidator::PARAM_TYPE => 'user',
2355 UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'temp' ],
2356 ],
2357 'requestid' => null,
2358 'servedby' => false,
2359 'curtimestamp' => false,
2360 'responselanginfo' => false,
2361 'origin' => null,
2362 'crossorigin' => false,
2363 'uselang' => [
2364 ParamValidator::PARAM_DEFAULT => self::API_DEFAULT_USELANG,
2365 ],
2366 'variant' => null,
2367 'errorformat' => [
2368 ParamValidator::PARAM_TYPE => [ 'plaintext', 'wikitext', 'html', 'raw', 'none', 'bc' ],
2369 ParamValidator::PARAM_DEFAULT => 'bc',
2371 ],
2372 'errorlang' => [
2373 ParamValidator::PARAM_DEFAULT => 'uselang',
2374 ],
2375 'errorsuselocal' => [
2376 ParamValidator::PARAM_DEFAULT => false,
2377 ],
2378 ];
2379 }
2380
2382 protected function getExamplesMessages() {
2383 return [
2384 'action=help'
2385 => 'apihelp-help-example-main',
2386 'action=help&recursivesubmodules=1&toc'
2387 => 'apihelp-help-example-recursive',
2388 ];
2389 }
2390
2395 public function modifyHelp( array &$help, array $options, array &$tocData ) {
2396 if ( !empty( $options['nolead'] ) ) {
2397 return;
2398 }
2399
2400 $helpBefore = [];
2401 $helpAfter = [];
2402 $tocDataBefore = [];
2403
2404 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset Must set when nolead is not set
2405 $level = $options['headerlevel'];
2406 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset Must set when nolead is not set
2407 $tocnumber = &$options['tocnumber'];
2408 $tocnumberBefore = 0;
2409
2410 $header = $this->msg( 'api-help-general-header' )->parse();
2411 $headline = Html::rawElement(
2412 'h' . min( 6, $level - 1 ),
2413 [ 'class' => 'apihelp-header', 'id' => 'main/general' ],
2414 $header
2415 );
2416 $helpBefore['general'] = $headline;
2417 $helpBefore['general'] .= $this->msg( 'api-help-general' )->parseAsBlock();
2418 if ( !isset( $tocData['main/general'] ) ) {
2419 $anchor = 'main/general';
2420 $tocDataBefore['main/general'] = new SectionMetadata(
2421 tocLevel: count( $tocnumber ) - 1,
2422 hLevel: $level - 1,
2423 line: $header,
2424 number: '0',
2425 index: '',
2426 anchor: $anchor,
2427 linkAnchor: Sanitizer::escapeIdForLink( $anchor ),
2428 );
2429 // FIXME: I don't love numbering the sections from 0, but counting is hard.
2430 // Someone should rewrite this code so that the numbers are assigned automatically.
2431 }
2432 $header = $this->msg( 'api-help-methods-header' )->parse();
2433 $headline = Html::rawElement(
2434 'h' . min( 6, $level ),
2435 [ 'class' => 'apihelp-header', 'id' => 'main/methods' ],
2436 $header
2437 );
2438 $helpBefore['methods'] = $headline;
2439 $helpBefore['methods'] .= $this->msg( 'api-help-methods' )->parseAsBlock();
2440 if ( !isset( $tocData['main/methods'] ) ) {
2441 $tocnumberBefore++;
2442 $anchor = 'main/methods';
2443 $tocDataBefore['main/methods'] = new SectionMetadata(
2444 tocLevel: count( $tocnumber ),
2445 hLevel: $level,
2446 line: $header,
2447 number: '0.' . $tocnumberBefore,
2448 index: '',
2449 anchor: $anchor,
2450 linkAnchor: Sanitizer::escapeIdForLink( $anchor ),
2451 );
2452 }
2453
2454 $header = $this->msg( 'api-help-datatypes-header' )->parse();
2455 $headline = Html::rawElement(
2456 'h' . min( 6, $level ),
2457 [ 'class' => 'apihelp-header', 'id' => 'main/datatypes' ],
2458 $header
2459 );
2460 $helpBefore['datatypes'] = $headline;
2461 $helpBefore['datatypes'] .= $this->msg( 'api-help-datatypes-top' )->parseAsBlock();
2462 $helpBefore['datatypes'] .= '<dl>';
2463 foreach ( $this->getParamValidator()->knownTypes() as $type ) {
2464 $m = $this->msg( "api-help-datatype-$type" );
2465 if ( !$m->isDisabled() ) {
2466 $helpBefore['datatypes'] .= Html::element( 'dt', [ 'id' => "main/datatype/$type" ], $type );
2467 $helpBefore['datatypes'] .= Html::rawElement( 'dd', [], $m->parseAsBlock() );
2468 }
2469 }
2470 $helpBefore['datatypes'] .= '</dl>';
2471 if ( !isset( $tocData['main/datatypes'] ) ) {
2472 $tocnumberBefore++;
2473 $anchor = 'main/datatypes';
2474 $tocDataBefore['main/datatypes'] = new SectionMetadata(
2475 tocLevel: count( $tocnumber ),
2476 hLevel: $level,
2477 line: $header,
2478 number: '0.' . $tocnumberBefore,
2479 index: '',
2480 anchor: $anchor,
2481 linkAnchor: Sanitizer::escapeIdForLink( $anchor ),
2482 );
2483 }
2484
2485 $header = $this->msg( 'api-help-limits-header' )->parse();
2486 $headline = Html::rawElement(
2487 'h' . min( 6, $level ),
2488 [ 'class' => 'apihelp-header', 'id' => 'main/limits' ],
2489 $header
2490 );
2491 $helpBefore['limits'] = $headline;
2492 $helpBefore['limits'] .= $this->msg( 'api-help-limits' )
2494 ->parseAsBlock();
2495
2496 // TODO inject stuff, see T265644
2497 $groupPermissionsLookup = MediaWikiServices::getInstance()->getGroupPermissionsLookup();
2498
2499 $groups = $groupPermissionsLookup->getGroupsWithPermission( 'apihighlimits' );
2500 if ( $groups ) {
2501 $groupDescs = array_map( $this->getLanguage()->getGroupName( ... ), $groups );
2502
2503 $helpBefore['limits'] .= $this->msg( 'api-help-limits-apihighlimits' )
2505 ->params( Message::listParam( $groupDescs ) )->parseAsBlock();
2506 }
2507
2508 if ( !isset( $tocData['main/limits'] ) ) {
2509 $tocnumberBefore++;
2510 $anchor = 'main/limits';
2511 $tocDataBefore['main/limits'] = new SectionMetadata(
2512 tocLevel: count( $tocnumber ),
2513 hLevel: $level,
2514 line: $header,
2515 number: '0.' . $tocnumberBefore,
2516 index: '',
2517 anchor: $anchor,
2518 linkAnchor: Sanitizer::escapeIdForLink( $anchor ),
2519 );
2520 }
2521
2522 $header = $this->msg( 'api-help-templatedparams-header' )->parse();
2523 $headline = Html::rawElement(
2524 'h' . min( 6, $level ),
2525 [ 'class' => 'apihelp-header', 'id' => 'main/templatedparams' ],
2526 $header
2527 );
2528 $helpBefore['templatedparams'] = $headline;
2529 $helpBefore['templatedparams'] .= $this->msg( 'api-help-templatedparams' )->parseAsBlock();
2530 if ( !isset( $tocData['main/templatedparams'] ) ) {
2531 $tocnumberBefore++;
2532 $anchor = 'main/templatedparams';
2533 $tocDataBefore['main/templatedparams'] = new SectionMetadata(
2534 tocLevel: count( $tocnumber ),
2535 hLevel: $level,
2536 line: $header,
2537 number: '0.' . $tocnumberBefore,
2538 index: '',
2539 anchor: $anchor,
2540 linkAnchor: Sanitizer::escapeIdForLink( $anchor ),
2541 );
2542 }
2543
2544 $header = $this->msg( 'api-credits-header' )->parse();
2545 $headline = Html::rawElement(
2546 'h' . min( 6, $level - 1 ),
2547 [ 'class' => 'apihelp-header', 'id' => 'main/credits' ],
2548 $header
2549 );
2550 $helpAfter['credits'] = $headline;
2551 $helpAfter['credits'] .= $this->msg( 'api-credits' )->useDatabase( false )->parseAsBlock();
2552 if ( !isset( $tocData['main/credits'] ) ) {
2553 $tocnumber[$level - 1]++;
2554 $tocnumber[$level] = 0;
2555 $anchor = 'main/credits';
2556 $tocData['main/credits'] = new SectionMetadata(
2557 tocLevel: count( $tocnumber ) - 1,
2558 hLevel: $level - 1,
2559 line: $header,
2560 number: implode( '.', array_slice( $tocnumber, 0, -1 ) ),
2561 index: '',
2562 anchor: $anchor,
2563 linkAnchor: Sanitizer::escapeIdForLink( $anchor ),
2564 );
2565 // FIXME: The number of the next TOC item after "Credits" will be off by one.
2566 // Since "Credits" is usually the last section, we don't really mind.
2567 // Someone should rewrite this code so that the numbers are assigned automatically.
2568 }
2569
2570 $help = [
2571 Html::openElement( 'div', [ 'class' => 'apihelp-general' ] ),
2572 ...$helpBefore,
2573 Html::closeElement( 'div' ),
2574 ...$help,
2575 ...$helpAfter,
2576 ];
2577 $tocData = [ ...$tocDataBefore, ...$tocData ];
2578 }
2579
2581 private $mCanApiHighLimits = null;
2582
2587 public function canApiHighLimits() {
2588 if ( $this->mCanApiHighLimits === null ) {
2589 $this->mCanApiHighLimits = $this->getAuthority()->isAllowed( 'apihighlimits' );
2590 }
2591
2592 return $this->mCanApiHighLimits;
2593 }
2594
2599 public function getModuleManager() {
2600 return $this->mModuleMgr;
2601 }
2602
2611 public function getUserAgent() {
2612 $agent = (string)$this->getRequest()->getHeader( 'Api-user-agent' );
2613 if ( $agent == '' ) {
2614 $agent = $this->getRequest()->getHeader( 'User-agent' );
2615 }
2616
2617 return $agent;
2618 }
2619}
2620
2627class_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.
$wgLang
Definition Setup.php:556
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
const LIMIT_SML1
Slow query, standard limit.
Definition ApiBase.php:235
getHookRunner()
Get an ApiHookRunner for running core API hooks.
Definition ApiBase.php:781
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
const LIMIT_BIG1
Fast query, standard limit.
Definition ApiBase.php:231
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:66
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:1270
canApiHighLimits()
Check whether the current user is allowed to use high limits.
Definition ApiMain.php:2587
handleCORS()
Check the &origin= and/or &crossorigin= query parameters and respond appropriately.
Definition ApiMain.php:1142
getSensitiveParams()
Get the request parameters that should be considered sensitive.
Definition ApiMain.php:2207
static handleApiBeforeMainException(Throwable $e)
Handle a throwable from the ApiBeforeMain hook.
Definition ApiMain.php:1103
getErrorFormatter()
Get the ApiErrorFormatter object associated with current request.
Definition ApiMain.php:750
getStatsFactory()
Get the stats factory.
Definition ApiMain.php:792
checkConditionalRequestHeaders( $module)
Check selected RFC 7232 precondition headers.
Definition ApiMain.php:1736
checkMaxLag( $module, $params)
Check the max lag if necessary.
Definition ApiMain.php:1690
getResult()
Get the ApiResult object associated with current request.
Definition ApiMain.php:708
__construct( $context=null, $enableWrite=false, $internal=null)
Constructs an instance of ApiMain that utilizes the module and format specified by $request.
Definition ApiMain.php:584
setRequestExpectations(ApiBase $module)
Set database connection, query, and write expectations given this module request.
Definition ApiMain.php:2065
markParamsUsed( $params)
Mark parameters as used.
Definition ApiMain.php:2198
encodeRequestLogValue( $s)
Encode a value in a format suitable for a space-separated log line.
Definition ApiMain.php:2173
getAllowedParams()
See ApiBase for description.
Definition ApiMain.php:2327
setCacheControl( $directives)
Set directives (key/value pairs) for the Cache-Control header.
Definition ApiMain.php:888
checkReadOnly( $module)
Check if the DB is read-only for this user.
Definition ApiMain.php:1867
getModuleManager()
Overrides to return this instance's module manager.
Definition ApiMain.php:2599
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:2395
isInternalMode()
Return true if the API was started by other PHP code using MediaWiki\Request\FauxRequest.
Definition ApiMain.php:699
getUserAgent()
Fetches the user agent used for this request.
Definition ApiMain.php:2611
sendCacheHeaders( $isError)
Send caching headers.
Definition ApiMain.php:1296
setupExecuteAction()
Set up for the execution.
Definition ApiMain.php:1592
setCacheMode( $mode)
Set the type of caching headers which will be sent.
Definition ApiMain.php:842
getUpload( $name)
Get a request upload, and register the fact that it was used, for logging.
Definition ApiMain.php:2263
getCheck( $name)
Get a boolean request value, and register the fact that the parameter was used, for logging.
Definition ApiMain.php:2251
reportUnusedParams()
Report unused parameters, so the client gets a hint in case it gave us parameters we don't know,...
Definition ApiMain.php:2273
substituteResultWithError(Throwable $e)
Replace the result data with the information about a throwable.
Definition ApiMain.php:1470
handleException(Throwable $e, $latency=0)
Handle a throwable as an API response.
Definition ApiMain.php:1001
execute()
Execute api request.
Definition ApiMain.php:914
setContinuationManager(?ApiContinuationManager $manager=null)
Definition ApiMain.php:764
executeAction()
Execute the actual module, without any error handling.
Definition ApiMain.php:2016
errorMessagesFromException(Throwable $e, $type='error')
Create an error message for the given throwable.
Definition ApiMain.php:1434
getPrinter()
Get the result formatter object.
Definition ApiMain.php:801
setCacheMaxAge( $maxage)
Set how long the response should be cached.
Definition ApiMain.php:810
setupModule()
Set up the module for response.
Definition ApiMain.php:1606
lacksSameOriginSecurity()
Get the security flag for the current request.
Definition ApiMain.php:716
getExamplesMessages()
Returns usage examples for this module.Return value has query strings as keys, with values being eith...
Definition ApiMain.php:2382
printResult( $httpCode=0)
Print results using the current printer.
Definition ApiMain.php:2301
getVal( $name, $default=null)
Get a request value, and register the fact that it was used, for logging.
Definition ApiMain.php:2230
executeActionWithErrorHandling()
Execute an action, and in case of an error, erase whatever partial results have been accumulated,...
Definition ApiMain.php:926
createPrinterByName( $format)
Create an instance of an output formatter by its name.
Definition ApiMain.php:899
getParamsUsed()
Get the request parameters used in the course of the preceding execute() request.
Definition ApiMain.php:2190
logRequest( $time, ?Throwable $e=null)
Log the preceding request.
Definition ApiMain.php:2087
addRequestedFields( $force=[])
Add requested fields to the result.
Definition ApiMain.php:1554
checkAsserts( $params)
Check asserts of the user's rights.
Definition ApiMain.php:1922
checkExecutePermissions( $module)
Check for sufficient permissions to execute.
Definition ApiMain.php:1837
markParamsSensitive( $params)
Mark parameters as sensitive.
Definition ApiMain.php:2220
setupExternalResponse( $module, $params)
Check POST for external response and setup result printer.
Definition ApiMain.php:1960
getModule()
Get the API module object.
Definition ApiMain.php:783
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:44
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:34
Profiler base class that defines the interface and some shared functionality.
Definition Profiler.php:26
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
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, 'ThumbnailBuckets'=> null, 'ThumbnailMinimumBucketDistance'=> 50, 'UploadThumbnailRenderMap'=>[], 'UploadThumbnailRenderMethod'=> 'jobqueue', 'UploadThumbnailRenderHttpCustomHost'=> false, 'UploadThumbnailRenderHttpCustomDomain'=> false, 'UseTinyRGBForJPGThumbnails'=> false, 'GalleryOptions'=>[], 'ThumbUpright'=> 0.75, 'DirectoryMode'=> 511, 'ResponsiveImages'=> true, 'ImagePreconnect'=> false, 'TrackMediaRequestProvenance'=> 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, 'PasswordExpirationDays'=> false, 'PasswordExpireGrace'=> 604800, 'SMTP'=> false, 'AdditionalMailParams'=> null, 'AllowHTMLEmail'=> false, 'EnotifFromEditor'=> false, 'EmailAuthentication'=> true, 'EmailConfirmationBanner'=> false, '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, 'ExternalLinksDomainGaps'=>[], 'ContentHandlers'=>['wikitext'=>['class'=> 'MediaWiki\\Content\\WikitextContentHandler', 'services'=>['TitleFactory', 'ParserFactory', 'GlobalIdGenerator', 'LanguageNameUtils', 'LinkRenderer', 'MagicWordFactory', 'ParsoidParserFactory',],], 'javascript'=>['class'=> 'MediaWiki\\Content\\JavaScriptContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup', 'CodeHighlighter',],], 'json'=>['class'=> 'MediaWiki\\Content\\JsonContentHandler', 'services'=>['ParsoidParserFactory', 'TitleFactory',],], 'css'=>['class'=> 'MediaWiki\\Content\\CssContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup', 'CodeHighlighter',],], 'vue'=>['class'=> 'MediaWiki\\Content\\VueContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'CodeHighlighter',],], '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,],], 'postproc-pcache'=>['default'=>['minCpuTime'=> 9223372036854775807,],], 'parsoid-pcache'=>['default'=>['minCpuTime'=> 0,],], 'postproc-parsoid-pcache'=>['default'=>['minCpuTime'=> 0,],],], 'ChronologyProtectorSecret'=> '', 'ParserCacheExpireTime'=> 86400, 'ParserCacheAsyncExpireTime'=> 60, 'ParserCacheAsyncRefreshJobs'=> true, 'OldRevisionParserCacheExpireTime'=> 3600, 'ObjectCacheSessionExpiry'=> 3600, 'PHPSessionHandling'=> 'warn', 'SuspiciousIpExpiry'=> false, 'SessionPbkdf2Iterations'=> 10001, 'UseSessionCookieJwt'=> 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, ], 'NamespacesWithoutAutoSummaries' => [ ], '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, '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, ], 'MediaWiki\\Auth\\PreviouslyRenamedAccountPreAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\PreviouslyRenamedAccountPreAuthenticationProvider', 'services' => [ 'ConnectionProvider', 'UserFactory', ], '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, 'autocreateaccount' => 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, 'createwithcontentmodel' => true, 'logout' => 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, 'createpreviouslyrenamedaccount' => 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, 'createwithcontentmodel' => true, 'pagelang' => true, ], 'editprotected' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editprotected' => true, ], 'editmycssjs' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, ], 'editmyoptions' => [ 'editmyoptions' => true, 'editmyuserjson' => true, ], 'editinterface' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, ], 'editsiteconfig' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => 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, 'createwithcontentmodel' => 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, 'createwithcontentmodel' => 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, 'createwithcontentmodel' => true, 'editprotected' => true, 'protect' => true, ], 'viewmywatchlist' => [ 'viewmywatchlist' => true, ], 'editmywatchlist' => [ 'editmywatchlist' => true, ], 'sendemail' => [ 'sendemail' => true, ], 'createaccount' => [ 'createaccount' => true, ], 'privateinfo' => [ 'viewmyprivateinfo' => true, ], 'mergehistory' => [ 'mergehistory' => true, ], 'managesessions' => [ 'logout' => 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', 'managesessions' => '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, 'CSPUseReportURIDirective' => 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, 'ApiClientErrorSampleRate' => 1.0, '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: 'https: 'git@github\\.com:(.*?)(\\.git)?' => 'https: ], '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' => [ ], '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, 'mw-edited-other-users-js' => true, 'mw-edited-other-users-css' => 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, 'EnableWatchstarPopover' => 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\\RecentChanges\\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', 'Promise-Non-Write-API-Action', 'Access-Control-Max-Age', 'Authorization', ], 'RestAPIAdditionalRouteFiles' => [ ], 'RestSandboxSpecs' => [ ], 'RestModuleOverrides' => [ ], '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, 'GenerateReqIDFormat' => 'rand24', '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, 'UsePostprocCacheLegacy' => false, 'UsePostprocCacheParsoid' => true, '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', ], 'ThumbnailBuckets' => [ 'array', 'null', ], 'UploadThumbnailRenderMap' => 'object', 'GalleryOptions' => 'object', 'DjvuDump' => [ 'string', 'null', ], 'DjvuRenderer' => [ 'string', 'null', ], 'DjvuTxt' => [ 'string', 'null', ], 'DjvuPostProcessor' => [ 'string', 'null', ], 'SMTP' => [ 'boolean', 'object', ], 'EnotifFromEditor' => 'boolean', 'EmailConfirmationBanner' => 'boolean', 'EnotifRevealEditorAddress' => 'boolean', 'UsersNotifiedOnAllChanges' => 'object', 'DBmwschema' => [ 'string', 'null', ], 'SharedTables' => 'array', 'DBservers' => [ 'boolean', 'array', ], 'LBFactoryConf' => 'object', 'LocalDatabases' => 'array', 'VirtualDomainsMapping' => 'object', 'FileSchemaMigrationStage' => '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', 'NamespacesWithoutAutoSummaries' => 'array', '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', ], 'CSPUseReportURIDirective' => [ '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', 'OverrideSiteFeed' => 'object', 'FeedClasses' => 'object', 'AdvertisedFeedTypes' => 'array', 'SoftwareTags' => 'object', 'RecentChangesFlags' => 'object', 'WatchlistExpiry' => 'boolean', 'EnableWatchstarPopover' => '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', 'RestModuleOverrides' => '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', ], 'GenerateReqIDFormat' => 'string', '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', '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', 'RestModuleOverrides' => 'array_replace_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', ], ], ], ], 'RestModuleOverrides' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'mode' => [ 'type' => 'string', ], ], 'required' => [ 'mode', ], ], ], '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