getDatabase(); $wgDBuser = 'wikiuser'; // Don't rely on MW default (T46251) $wgSQLMode = null; $wmgVersionNumber = $multiVersion->getVersionNumber(); # Used further down this file for per-wiki SiteConfiguration caching: # - MUST be in /tmp to allow atomic rename from elsewhere in /tmp. # - MUST vary by MediaWiki branch (aka MWMultiversion deployment version). # # Also used by MediaWiki core for various caching purposes, and may # be shared between wikis (e.g. does not need to vary by wgDBname). $wgCacheDirectory = '/tmp/mw-cache-' . $wmgVersionNumber; # Get all the service definitions $wmgAllServices = ServiceConfig::getInstance()->getAllServices(); # Shorthand when we have no master-replica situation to keep into account $wmgLocalServices = $wmgAllServices[$wmgDatacenter]; # The list of datacenters known to this realm $wmgDatacenters = ServiceConfig::getInstance()->getDatacenters(); if ( getenv( 'WMF_MAINTENANCE_OFFLINE' ) ) { // Prepare just enough configuration to allow // rebuildLocalisationCache.php and mergeMessageFileList.php to // run to completion without complaints. $wmgEtcdLastModifiedIndex = "wmgEtcdLastModifiedIndex uninitialized due to WMF_MAINTENANCE_OFFLINE"; $wgReadOnly = "In read-only mode because WMF_MAINTENANCE_OFFLINE is set"; $wmgMasterDatacenter = ServiceConfig::getInstance()->getDatacenter(); $wmgMasterServices = $wmgAllServices[$wmgMasterDatacenter]; $wmgLocalDbConfig = [ 'readOnlyBySection' => null, 'groupLoadsBySection' => [ 'DEFAULT' => [ '' => [ 'WMF_MAINTENANCE_OFFLINE_placeholder' => 0 ], ], ], 'hostsByName' => null, 'sectionLoads' => [], 'externalLoads' => [], ]; $wmgRemoteMasterDbConfig = null; } else { $etcdConfig = wmfSetupEtcd( $wmgLocalServices['etcd'] ); $wmgEtcdLastModifiedIndex = $etcdConfig->getModifiedIndex(); $wgReadOnly = $etcdConfig->get( "$wmgDatacenter/ReadOnly" ); $wmgMasterDatacenter = $etcdConfig->get( 'common/WMFMasterDatacenter' ); $wmgMasterServices = $wmgAllServices[$wmgMasterDatacenter]; // Database load balancer config (sectionLoads, groupLoadsBySection, …) // This is later merged into $wgLBFactoryConf by wmfApplyEtcdDBConfig(). // See also $wmgLocalDbConfig = $etcdConfig->get( "$wmgDatacenter/dbconfig" ); if ( $wmgDatacenter !== $wmgMasterDatacenter ) { $wmgRemoteMasterDbConfig = $etcdConfig->get( "$wmgMasterDatacenter/dbconfig" ); } else { $wmgRemoteMasterDbConfig = null; } // Define a callback for use by LBFactory::autoReconfigure(). // This allows long-running scripts to take into account changes to the database config (T298485). // The callback below does not support data center switches, but does support // read-only flags and changes to replica weights. In particular, it allows a replica // to be taken out of rotation. if ( PHP_SAPI === 'cli' ) { $wmgLBFactoryConfigCallback = static function () use ( $wmgLocalServices, $wmgDatacenter ) { // NOTE: Don't re-use the existing $etcdConfig, the entire point of this // callback is that the we want to re-load it to see if it has changed. $etcdConfig = wmfSetupEtcd( $wmgLocalServices['etcd'] ); $dbConfigFromEtcd = $etcdConfig->get( "$wmgDatacenter/dbconfig" ); $lbFactoryConf = []; wmfApplyEtcdDBConfig( $dbConfigFromEtcd, $lbFactoryConf ); $lbFactoryConf['class'] = 'LBFactoryMulti'; $lbFactoryConf['groupLoadsBySection']['s11'] = []; $lbFactoryConf['sectionLoads']['s11'] = [ 'clouddb2002-dev' => 1 ]; $lbFactoryConf['hostsByName']['clouddb2002-dev'] = '10.192.20.6'; return $lbFactoryConf; }; } else { $wmgLBFactoryConfigCallback = null; } unset( $etcdConfig ); } $wmgUdp2logDest = $wmgLocalServices['udp2log']; if ( $wgDBname === 'testwiki' ) { $wgDebugLogFile = "udp://{$wmgUdp2logDest}/testwiki"; } else { $wgDebugLogFile = '/dev/null'; } $wgConf = new SiteConfiguration; $wgConf->suffixes = MWMultiVersion::SUFFIXES; $wgConf->wikis = MWWikiversions::readDbListFile( $wmgRealm === 'labs' ? 'all-labs' : 'all' ); $wgConf->settings = MWConfigCacheGenerator::getStaticConfig( $wmgRealm ); $wgLocalDatabases = $wgConf->getLocalDatabases(); // Do not add wikimedia.org, because of other non-MediaWiki sites under that domain // Do not add wikidata.org, because of query.wikidata.org // If a non-MediaWiki site gets into this list, MediaWiki will not be able to // make HTTP requests to it. // At a minimum, all SUL-linked wikis must be included here for cross-wiki HTTP // requests to work (GlobalUserPage, cross-wiki notifications, etc.). // TODO: add remaining MediaWiki sites $wgLocalVirtualHosts = [ 'wikipedia.org', 'wiktionary.org', 'wikiquote.org', 'wikibooks.org', 'wikiquote.org', 'wikinews.org', 'wikisource.org', 'wikiversity.org', 'wikivoyage.org', 'www.mediawiki.org', 'www.wikidata.org', 'test.wikidata.org', 'api.wikimedia.org', 'ar.wikimedia.org', 'az.wikimedia.org', 'bd.wikimedia.org', 'be.wikimedia.org', 'br.wikimedia.org', 'ca.wikimedia.org', 'co.wikimedia.org', 'commons.wikimedia.org', 'dk.wikimedia.org', 'ee.wikimedia.org', 'fi.wikimedia.org', 'foundation.wikimedia.org', 'incubator.wikimedia.org', 'login.wikimedia.org', 'meta.wikimedia.org', 'mk.wikimedia.org', 'mx.wikimedia.org', 'nl.wikimedia.org', 'no.wikimedia.org', 'nyc.wikimedia.org', 'outreach.wikimedia.org', 'pl.wikimedia.org', 'pt.wikimedia.org', 'ru.wikimedia.org', 'se.wikimedia.org', 'species.wikimedia.org', 'test-commons.wikimedia.org', 'tr.wikimedia.org', 'ua.wikimedia.org', 've.wikimedia.org', 'wikimania.wikimedia.org', 'www.wikifunctions.org', 'm.wikifunctions.org', ]; $globals = MWConfigCacheGenerator::getConfigGlobals( $wgDBname, $wgConf, $wmgRealm ); // phpcs:ignore MediaWiki.Usage.ForbiddenFunctions.extract extract( $globals ); # Determine legacy site/lang pair for the current wiki [ $site, $lang ] = $wgConf->siteFromDB( $wgDBname ); # ------------------------------------------------------------------------- # Settings common to all wikis # Private settings such as passwords, that shouldn't be published # Needs to be before db.php require __DIR__ . '/../private/PrivateSettings.php'; require __DIR__ . '/logging.php'; require __DIR__ . '/filebackend.php'; require __DIR__ . '/mc.php'; if ( $wmgRealm === 'labs' ) { // Beta Cluster overrides require __DIR__ . '/mc-labs.php'; } # db-*.php needs $wgDebugDumpSql so should be loaded after logging.php. # Ref: db-production.php or db-labs.php require __DIR__ . "/db-$wmgRealm.php"; # Override certain settings in command-line mode # This must be after InitialiseSettings.php is processed (T197475) if ( PHP_SAPI === 'cli' ) { $wgShowExceptionDetails = true; } if ( XWikimediaDebug::getInstance()->hasOption( 'readonly' ) ) { $wgReadOnly = 'X-Wikimedia-Debug'; } $wgAllowedCorsHeaders[] = 'X-Wikimedia-Debug'; // In production, read the database loadbalancer config from etcd. // See https://wikitech.wikimedia.org/wiki/Dbctl // This must be called after db-{eqiad,codfw}.php has been loaded! // It overwrites a few sections of $wgLBFactoryConf with data from etcd. // In labs, the relevant key exists in etcd, but does not contain real data. // Only do this in production. if ( $wmgRealm === 'production' ) { wmfApplyEtcdDBConfig( $wmgLocalDbConfig, $wgLBFactoryConf ); // Add the config callback $wgLBFactoryConf['configCallback'] = $wmgLBFactoryConfigCallback; // labtestwiki is a one-off test server, using a wmcs-managed database. Cut // etcd out of the loop entirely for this one. $wgLBFactoryConf['groupLoadsBySection']['s11'] = []; $wgLBFactoryConf['sectionLoads']['s11'] = [ 'clouddb2002-dev' => 1 ]; $wgLBFactoryConf['hostsByName']['clouddb2002-dev'] = '10.192.20.6'; // Disable LoadMonitor in CLI, it doesn't provide much value in CLI. if ( PHP_SAPI === 'cli' ) { $wgLBFactoryConf['loadMonitorClass'] = '\Wikimedia\Rdbms\LoadMonitorNull'; } } // Set $wgProfiler to the value provided by PhpAutoPrepend.php if ( isset( $wmgProfiler ) ) { $wgProfiler = $wmgProfiler; } # Disallow web request DB transactions slower than this $wgMaxUserDBWriteDuration = 3; # Activate read-only mode for bots when lag is getting high. # This should be lower than 'max lag' in the LBFactory conf. $wgAPIMaxLagThreshold = 3; # Set the max memory used. ini_set( 'memory_limit', $wmgMemoryLimit ); # Change calls to wfShellWikiCmd() to use MWScript.php wrapper $wgHooks['wfShellWikiCmd'][] = 'MWMultiVersion::onWfShellMaintenanceCmd'; setlocale( LC_ALL, 'en_US.UTF-8' ); # ###################################################################### # Revision backend settings # ###################################################################### $wgCompressRevisions = true; $wgExternalStores = [ 'DB' ]; # ###################################################################### # Performance settings and restrictions # ###################################################################### // Replicas aren't fast enough to generate all special pages all the time. $wgMiserMode = true; $wgQueryCacheLimit = 5000; $wgParserCacheExpireTime = 86400 * 30; // Override talk pages to have 10 days only (T280605) $wgDiscussionToolsTalkPageParserCacheExpiry = 86400 * 10; // Old revision parser cache expire in 1 hour $wgOldRevisionParserCacheExpireTime = 3600; // This feature would vastly increase the size of the CDN cache, and increase // MW appserver load. $wgULSLanguageDetection = false; /** * Configure PHP request timeouts. These should be slightly less than the Apache * timeouts, so that the slightly more informative PHP error message is * delivered to the user, and so that we can verify that PHP timeouts actually * exist (T97192). */ if ( PHP_SAPI === 'cli' ) { // Should always be unlimited, this is probably redundant $wgRequestTimeLimit = 0; } elseif ( XWikimediaDebug::getInstance()->hasOption( 'shorttimeout' ) ) { // To probe for excimer-related memory corruption e.g. T293568 $wgRequestTimeLimit = 2; } else { // Videoscalers have a 1 day timeout. // Jobrunners have 20 minutes. // Everything else has 60 seconds for GETs and 200s for POSTs if ( ClusterConfig::getInstance()->isAsync() ) { if ( strpos( $_SERVER['HTTP_HOST'] ?? '', 'videoscaler.' ) === 0 ) { $wgRequestTimeLimit = 86400; } else { $wgRequestTimeLimit = 1200; } } else { if ( $_SERVER['REQUEST_METHOD'] === 'POST' ) { $wgRequestTimeLimit = 200; } else { $wgRequestTimeLimit = 60; } } } # ###################################################################### # Account- and notifications-related settings # ###################################################################### $wgSecureLogin = true; $wgMaxNameChars = 85; // Turn this on so UserMailer::send() will be able to send both text and html email $wgAllowHTMLEmail = true; $wgEnotifUserTalk = true; $wgEnotifWatchlist = true; // Keep this true; it's just whether the feature is available at all, not the default // setting. T142727 $wgEnotifMinorEdits = true; // Use the same expiry for core sessions as for CentralAuth sessions (T313496) $wgObjectCacheSessionExpiry = 86400; # ###################################################################### # Anti-abuse settings # ###################################################################### $wgEnableUserEmailMuteList = true; if ( $wmgUseGlobalPreferences ) { // Allow global preferences for email-blacklist and echo-notifications // to be auto-set where it is overridden $wgGlobalPreferencesAutoPrefs = [ 'email-blacklist', 'echo-notifications-blacklist' ]; } // T355034 new block_target schema $wgBlockTargetMigrationStage = SCHEMA_COMPAT_NEW; # ###################################################################### # Legal matters # ###################################################################### $wgRightsIcon = '//creativecommons.org/images/public/somerights20.png'; # ###################################################################### # ResourceLoader settings # ###################################################################### $wgInternalServer = $wgCanonicalServer; $wgArticlePath = '/wiki/$1'; $wgScriptPath = '/w'; $wgScript = "{$wgScriptPath}/index.php"; $wgRedirectScript = "{$wgScriptPath}/redirect.php"; $wgLoadScript = "{$wgScriptPath}/load.php"; // Don't include a hostname in $wgResourceBasePath and friends // - Goes wrong otherwise on mobile web (T106966, T112646) // - Improves performance by leveraging HTTP/2 // - $wgLocalStylePath MUST be relative // Apache rewrites /w/resources, /w/extensions, and /w/skins to /w/static.php (T99096) $wgResourceBasePath = '/w'; $wgExtensionAssetsPath = "{$wgResourceBasePath}/extensions"; $wgStylePath = "{$wgResourceBasePath}/skins"; $wgLocalStylePath = $wgStylePath; $wgResourceLoaderMaxQueryLength = 5000; $wgGitInfoCacheDirectory = "$IP/cache/gitinfo"; // @var string|bool: E-mail address to send notifications to, or false to disable notifications. $wmgAddWikiNotify = "newprojects@lists.wikimedia.org"; $wgLocalisationCacheConf['storeClass'] = LCStoreCDB::class; $wgLocalisationCacheConf['storeDirectory'] = "$IP/cache/l10n"; $wgLocalisationCacheConf['manualRecache'] = true; // Add some useful config data to query=siteinfo $wgHooks['APIQuerySiteInfoGeneralInfo'][] = static function ( $module, &$data ) { global $wmgMasterDatacenter, $wmgEtcdLastModifiedIndex, $wmgCirrusSearchDefaultCluster, $wgCirrusSearchDefaultCluster; $data['wmf-config'] = [ 'wmfMasterDatacenter' => $wmgMasterDatacenter, 'wmfEtcdLastModifiedIndex' => $wmgEtcdLastModifiedIndex, 'wmgCirrusSearchDefaultCluster' => $wmgCirrusSearchDefaultCluster, 'wgCirrusSearchDefaultCluster' => $wgCirrusSearchDefaultCluster, ]; }; $wgEmergencyContact = 'noc@wikipedia.org'; # Default address gets rejected by some mail hosts. # This email is used for more than just sending password resets, also e.g. Echo notifications # and random contact forms. $wgPasswordSender = 'wiki@wikimedia.org'; $wgRCMaxAge = 30 * 86400; $wgTmpDirectory = '/tmp'; $wgOverrideUcfirstCharacters = include __DIR__ . '/UcfirstOverrides.php'; # Object cache and session settings $wgSessionName = $wgDBname . 'Session'; $pcServers = []; foreach ( $wmgLocalServices['parsercache-dbs'] as $tag => $host ) { $pcServers[$tag] = [ 'type' => 'mysql', 'host' => $host, 'dbname' => 'parsercache', 'user' => $wgDBuser, 'password' => $wgDBpassword, 'flags' => 0, ]; } $wgObjectCaches['mysql-multiwrite'] = [ 'class' => 'MultiWriteBagOStuff', 'caches' => [ 0 => [ 'factory' => [ 'ObjectCache', 'getInstance' ], 'args' => [ 'mcrouter' ] ], 1 => [ 'class' => 'SqlBagOStuff', 'servers' => $pcServers, 'purgePeriod' => 0, 'tableName' => 'pc', 'shards' => 256, 'reportDupes' => false ], ], 'replication' => 'async', 'reportDupes' => false ]; $wgObjectCaches['kask-session'] = [ 'class' => 'RESTBagOStuff', 'url' => "{$wmgLocalServices['sessionstore']}/sessions/v1/", 'httpParams' => [ 'writeHeaders' => [ 'content-type' => 'application/octet-stream', ], 'writeMethod' => 'POST', ], 'serialization_type' => 'PHP', 'hmac_key' => $wmgSessionStoreHMACKey, 'extendedErrorBodyFields' => [ 'type', 'title', 'detail', 'instance' ] ]; $wgObjectCaches['kask-echoseen'] = [ 'class' => 'RESTBagOStuff', 'url' => "{$wmgLocalServices['echostore']}/echoseen/v1/", 'httpParams' => [ 'writeHeaders' => [ 'content-type' => 'application/octet-stream', ], 'writeMethod' => 'POST', ], 'serialization_type' => 'JSON', 'extendedErrorBodyFields' => [ 'type', 'title', 'detail', 'instance' ], 'reportDupes' => false, ]; $wgObjectCaches['db-mainstash'] = [ 'class' => 'SqlBagOStuff', 'cluster' => 'extension2', 'dbDomain' => 'mainstash', 'globalKeyLbDomain' => 'mainstash', 'tableName' => 'objectstash', 'multiPrimaryMode' => true, 'purgePeriod' => 100, 'purgeLimit' => 1000, 'reportDupes' => false ]; session_name( $lang . 'wikiSession' ); // Use PBKDF2 for password hashing (T70766) $wgPasswordDefault = 'pbkdf2'; // This needs to be increased as allowable by server performance $wgPasswordConfig['pbkdf2'] = [ 'class' => 'Pbkdf2Password', 'algo' => 'sha512', 'cost' => '128000', 'length' => '64', ]; // Temporary for T57420 $wgPasswordConfig['null'] = [ 'class' => InvalidPassword::class ]; // Password policies; see https://meta.wikimedia.org/wiki/Password_policy // // For global policies, see $wgCentralAuthGlobalPasswordPolicies below $wmgPrivilegedPolicy = [ 'MinimalPasswordLength' => [ 'value' => 10, 'suggestChangeOnLogin' => true, 'forceChange' => true ], // With MinimumPasswordLengthToLogin, if the length of the password is <= the value // of the policy, the user will be forced to use Special:PasswordReset or similar // to be able to get into their account 'MinimumPasswordLengthToLogin' => [ 'value' => 1 ], 'PasswordNotInCommonList' => [ 'value' => true, 'suggestChangeOnLogin' => true ], ]; if ( $wgDBname === 'labswiki' || $wgDBname === 'labtestwiki' ) { $wgPasswordPolicy['policies']['default']['MinimalPasswordLength'] = [ 'value' => 10, 'suggestChangeOnLogin' => true, 'forceChange' => true, ]; } else { foreach ( $wmgPrivilegedGroups as $group ) { // On non-SUL wikis this is the effective password policy. On SUL wikis, it will be overridden // in the PasswordPoliciesForUser hook, but still needed for Special:PasswordPolicies if ( $group === 'user' ) { $group = 'default'; // For e.g. private and fishbowl wikis; covers 'user' in password policies } $wgPasswordPolicy['policies'][$group] = array_merge( $wgPasswordPolicy['policies'][$group] ?? [], $wmgPrivilegedPolicy ); } } $wgPasswordPolicy['policies']['default']['MinimalPasswordLength'] = [ 'value' => 8, 'suggestChangeOnLogin' => false, ]; $wgPasswordPolicy['policies']['default']['PasswordNotInCommonList'] = [ 'value' => true, 'suggestChangeOnLogin' => true, ]; // Enforce password policy when users login on other wikis; also for sensitive global groups // FIXME does this just duplicate the global policy checks down in the main $wmgUseCentralAuth block? if ( $wmgUseCentralAuth ) { $wgHooks['PasswordPoliciesForUser'][] = static function ( User $user, array &$effectivePolicy ) use ( $wmgPrivilegedPolicy ) { $privilegedGroups = wmfGetPrivilegedGroups( $user ); if ( $privilegedGroups ) { $effectivePolicy = UserPasswordPolicy::maxOfPolicies( $effectivePolicy, $wmgPrivilegedPolicy ); if ( in_array( 'staff', $privilegedGroups, true ) ) { $effectivePolicy['MinimumPasswordLengthToLogin'] = [ 'value' => 10, ]; } } }; } if ( $wmgDisableAccountCreation ) { $wgGroupPermissions['*']['createaccount'] = false; $wgGroupPermissions['*']['autocreateaccount'] = true; } # ###################################################################### # Server security settings # ###################################################################### // Disable firejail in kubernetes, where it would not work. // Most shellouts, and any shellout depending on untrusted input in particular, // should use shellbox. $wgShellRestrictionMethod = ClusterConfig::getInstance()->isK8s() ? false : 'firejail'; $wgShellboxShell = '/bin/bash'; $wgUseImageMagick = true; // Please note: neither command exists in the container, and this should // never be called in production - still better to set it to something that could work on k8s. $wgImageMagickConvertCommand = ClusterConfig::getInstance()->isK8s() ? '/usr/bin/convert' : '/usr/local/bin/mediawiki-firejail-convert'; $wgSharpenParameter = '0x0.8'; # for IM>6.5, T26857 if ( $wmgUsePagedTiffHandler ) { wfLoadExtension( 'PagedTiffHandler' ); $wgTiffUseTiffinfo = true; $wgTiffMaxMetaSize = 1048576; // Force use of shellbox on mw on k8s. // We're not sending commons user traffic here so this can live for as long as needed // before we make upload-by-url asynchronous if ( !$wmgUsePagedTiffHandlerShellbox && ClusterConfig::getInstance()->isK8s() ) { $wmgUsePagedTiffHandlerShellbox = true; } if ( $wmgUsePagedTiffHandlerShellbox && $wmgLocalServices['shellbox-media'] ) { // Route pagedtiffhandler to the Shellbox named "shellbox-media". $wgShellboxUrls['pagedtiffhandler'] = $wmgLocalServices['shellbox-media']; // $wgShellboxSecretKey set in PrivateSettings.php } } // Use shellbox on k8s for handling djvu images. if ( ClusterConfig::getInstance()->isK8s() && $wmgLocalServices['shellbox-media'] ) { $wgDjvuUseBoxedCommand = true; $wgShellboxUrls['djvu'] = $wmgLocalServices['shellbox-media']; } // Disable $wgMaxImageArea checks, Thumbor uses a timeout instead // of a size limit (T291014#7367570) $wgMaxImageArea = false; $wgMaxAnimatedGifArea = 10e7; // 100MP $wgFileExtensions = array_merge( $wgFileExtensions, $wmgFileExtensions ); $wgProhibitedFileExtensions = array_merge( $wgProhibitedFileExtensions, $wmgProhibitedFileExtensions ); if ( isset( $wmgUploadStashMaxAge ) ) { $wgUploadStashMaxAge = $wmgUploadStashMaxAge; } if ( $wmgPrivateWikiUploads ) { # mav forced me to --midom $wgFileExtensions[] = 'ppt'; # mav forced me as well!!! -- Tim $wgFileExtensions[] = 'doc'; # adding since removed elsewhere now -- 2007-08-21 -- brion $wgFileExtensions[] = 'xls'; # delphine made me do it!!!!! --brion $wgFileExtensions[] = 'eps'; $wgFileExtensions[] = 'zip'; # OpenOffice, hell if we're going to allow doc we may as well have these too -- Tim $wgFileExtensions[] = 'odf'; $wgFileExtensions[] = 'odp'; $wgFileExtensions[] = 'ods'; $wgFileExtensions[] = 'odt'; $wgFileExtensions[] = 'odg'; // OpenOffice Graphics $wgFileExtensions[] = 'ott'; // Templates # Temporary for office work :P $wgFileExtensions[] = 'wmv'; $wgFileExtensions[] = 'dv'; $wgFileExtensions[] = 'avi'; $wgFileExtensions[] = 'mov'; $wgFileExtensions[] = 'aif'; // " $wgFileExtensions[] = 'aiff'; // " # Because I hate having to find print drivers -- tomasz $wgFileExtensions[] = 'ppd'; # InDesign & PhotoShop, Illustrator wanted for Chapters logo work $wgFileExtensions[] = 'indd'; $wgFileExtensions[] = 'inx'; $wgFileExtensions[] = 'psd'; $wgFileExtensions[] = 'ai'; # Pete made me --Roan $wgFileExtensions[] = 'omniplan'; # Dia Diagrams files --fred. $wgFileExtensions[] = 'dia'; # Font files (so we can upload Montserrat to donatewiki) --Roan $wgFileExtensions[] = 'woff'; $wgFileExtensions[] = 'woff2'; // To allow OpenOffice doc formats we need to not exclude zip files $wgMimeTypeExclusions = array_diff( $wgMimeTypeExclusions, [ 'application/zip' ] ); } # ###################################################################### # SVG renderer settings # ###################################################################### $wgSVGConverter = 'rsvg-secure'; $wgSVGConverterPath = '/usr/bin'; $wgSVGMaxSize = 4096; // 1024's a bit low? # Hack for rsvg broken by security patch $wgSVGConverters['rsvg-broken'] = '$path/rsvg-convert -w $width -h $height -o $output < $input'; # This converter will only work when rsvg has a suitable security patch $wgSVGConverters['rsvg-secure'] = '$path/rsvg-convert --no-external-files -w $width -h $height -o $output $input'; # ###################################################################### # DJVU renderer settings # ###################################################################### $wgDjvuDump = '/usr/bin/djvudump'; $wgDjvuRenderer = '/usr/bin/ddjvu'; $wgDjvuTxt = '/usr/bin/djvutxt'; # ###################################################################### # StatsD/Metrics Settings # ###################################################################### $wgStatsFormat = 'dogstatsd'; $wgStatsTarget = 'udp://localhost:9125'; $wgStatsdServer = $wmgLocalServices['statsd']; # ###################################################################### # Reverse proxy Configuration # ###################################################################### $wgUseCdn = true; if ( $wmgRealm === 'production' ) { require __DIR__ . '/reverse-proxy.php'; } elseif ( $wmgRealm === 'labs' ) { $wgStatsdMetricPrefix = 'BetaMediaWiki'; require __DIR__ . '/reverse-proxy-labs.php'; } // CORS (cross-domain AJAX, T22814) // This lists the domains that are accepted as *origins* of CORS requests // DO NOT add domains here that aren't WMF wikis unless you really know what you're doing if ( $wmgUseCORS ) { $wgCrossSiteAJAXdomains = [ '*.wikipedia.org', '*.wikinews.org', '*.wiktionary.org', '*.wikibooks.org', '*.wikiversity.org', '*.wikisource.org', 'wikisource.org', '*.wikiquote.org', 'www.wikidata.org', 'm.wikidata.org', 'test.wikidata.org', 'test.m.wikidata.org', '*.wikivoyage.org', 'www.mediawiki.org', 'm.mediawiki.org', 'www.wikifunctions.org', 'm.wikifunctions.org', 'advisory.wikimedia.org', 'advisory.m.wikimedia.org', 'affcom.wikimedia.org', 'api.wikimedia.org', 'auditcom.wikimedia.org', 'boardgovcom.wikimedia.org', 'board.wikimedia.org', 'chair.wikimedia.org', 'checkuser.wikimedia.org', 'checkuser.m.wikimedia.org', 'collab.wikimedia.org', 'commons.wikimedia.org', 'commons.m.wikimedia.org', 'test-commons.wikimedia.org', 'test-commons.m.wikimedia.org', 'donate.wikimedia.org', 'exec.wikimedia.org', 'foundation.wikimedia.org', 'foundation.m.wikimedia.org', 'grants.wikimedia.org', 'incubator.wikimedia.org', 'incubator.m.wikimedia.org', 'internal.wikimedia.org', 'login.wikimedia.org', 'meta.wikimedia.org', 'meta.m.wikimedia.org', 'movementroles.wikimedia.org', 'office.wikimedia.org', 'office.m.wikimedia.org', 'outreach.wikimedia.org', 'outreach.m.wikimedia.org', 'quality.wikimedia.org', 'quality.m.wikimedia.org', 'searchcom.wikimedia.org', 'spcom.wikimedia.org', 'species.wikimedia.org', 'species.m.wikimedia.org', 'steward.wikimedia.org', 'steward.m.wikimedia.org', 'strategy.wikimedia.org', 'strategy.m.wikimedia.org', 'usability.wikimedia.org', 'usability.m.wikimedia.org', 'vrt-wiki.wikimedia.org', 'vrt-wiki.m.wikimedia.org', 'wikimania.wikimedia.org', 'wikimania.m.wikimedia.org', 'wikimania????.wikimedia.org', 'wikimania????.m.wikimedia.org', 'wikimaniateam.wikimedia.org', 'wikimaniateam.m.wikimedia.org', 'am.wikimedia.org', 'am.m.wikimedia.org', 'ar.wikimedia.org', 'ar.m.wikimedia.org', 'bd.wikimedia.org', 'bd.m.wikimedia.org', 'be.wikimedia.org', 'be.m.wikimedia.org', 'br.wikimedia.org', 'br.m.wikimedia.org', 'ca.wikimedia.org', 'ca.m.wikimedia.org', 'cn.wikimedia.org', 'cn.m.wikimedia.org', 'co.wikimedia.org', 'co.m.wikimedia.org', 'dk.wikimedia.org', 'dk.m.wikimedia.org', 'ec.wikimedia.org', 'ec.m.wikimedia.org', 'ee.wikimedia.org', 'ee.m.wikimedia.org', 'et.wikimedia.org', 'et.m.wikimedia.org', 'fi.wikimedia.org', 'fi.m.wikimedia.org', 'ge.wikimedia.org', 'ge.m.wikimedia.org', 'hi.wikimedia.org', 'hi.m.wikimedia.org', 'id.wikimedia.org', 'id.m.wikimedia.org', 'il.wikimedia.org', 'il.m.wikimedia.org', 'mai.wikimedia.org', 'mai.m.wikimedia.org', 'mk.wikimedia.org', 'mk.m.wikimedia.org', 'mx.wikimedia.org', 'mx.m.wikimedia.org', 'nl.wikimedia.org', 'nl.m.wikimedia.org', 'noboard-chapters.wikimedia.org', 'no.wikimedia.org', 'no.m.wikimedia.org', 'nyc.wikimedia.org', 'nyc.m.wikimedia.org', 'nz.wikimedia.org', 'nz.m.wikimedia.org', 'punjabi.wikimedia.org', 'punjabi.m.wikimedia.org', 'pa-us.wikimedia.org', 'pa-us.m.wikimedia.org', 'pl.wikimedia.org', 'pl.m.wikimedia.org', 'pt.wikimedia.org', 'pt.m.wikimedia.org', 'romd.wikimedia.org', 'romd.m.wikimedia.org', 'rs.wikimedia.org', 'rs.m.wikimedia.org', 'ru.wikimedia.org', 'ru.m.wikimedia.org', 'se.wikimedia.org', 'se.m.wikimedia.org', 'tr.wikimedia.org', 'tr.m.wikimedia.org', 'ua.wikimedia.org', 'ua.m.wikimedia.org', 'wb.wikimedia.org', 'wb.m.wikimedia.org', ]; } wfLoadSkins( [ 'Vector', 'MonoBook', 'Modern', 'CologneBlue', 'Timeless' ] ); // Grants and rights // Note these have to be visible on all wikis, not just the ones the // extension is enabled on, for proper display in OAuth pages and such. // Adding Flaggedrevs rights so that they are available for globalgroups/staff rights - JRA 2013-07-22 $wgAvailableRights[] = 'autoreview'; $wgAvailableRights[] = 'autoreviewrestore'; $wgAvailableRights[] = 'movestable'; $wgAvailableRights[] = 'review'; $wgAvailableRights[] = 'stablesettings'; $wgAvailableRights[] = 'unreviewedpages'; $wgAvailableRights[] = 'validate'; $wgGrantPermissions['editprotected']['movestable'] = true; // So that protection rights can be assigned to global groups $wgAvailableRights[] = 'templateeditor'; $wgAvailableRights[] = 'editeditorprotected'; $wgAvailableRights[] = 'editextendedsemiprotected'; $wgAvailableRights[] = 'extendedconfirmed'; $wgAvailableRights[] = 'editautoreviewprotected'; $wgAvailableRights[] = 'editautopatrolprotected'; $wgAvailableRights[] = 'edittrustedprotected'; $wgGrantPermissions['editprotected']['templateeditor'] = true; $wgGrantPermissions['editprotected']['editeditorprotected'] = true; $wgGrantPermissions['editprotected']['editextendedsemiprotected'] = true; $wgGrantPermissions['editprotected']['extendedconfirmed'] = true; $wgGrantPermissions['editprotected']['editautoreviewprotected'] = true; $wgGrantPermissions['editprotected']['editautopatrolprotected'] = true; $wgGrantPermissions['editprotected']['edittrustedprotected'] = true; $wgGrantPermissions['editprotected']['edit-legal'] = true; // Adding Flow's rights so that they are available for global groups/staff rights $wgAvailableRights[] = 'flow-create-board'; $wgAvailableRights[] = 'flow-edit-post'; $wgAvailableRights[] = 'flow-suppress'; $wgAvailableRights[] = 'flow-hide'; $wgAvailableRights[] = 'flow-delete'; // Adding GrowthExperiments's rights $wgAvailableRights[] = 'setmentor'; $wgAvailableRights[] = 'managementors'; $wgAvailableRights[] = 'enrollasmentor'; // Rights needed to interact with wikibase $wgGrantPermissions['createeditmovepage']['property-create'] = true; $wgGrantPermissions['editpage']['item-term'] = true; $wgGrantPermissions['editpage']['item-merge'] = true; $wgGrantPermissions['editpage']['property-term'] = true; $wgGrantPermissions['editpage']['item-redirect'] = true; // Extension:Newsletter, so that they are available for global groups --MA 2017.09.09 $wgAvailableRights[] = 'newsletter-create'; $wgAvailableRights[] = 'newsletter-delete'; $wgAvailableRights[] = 'newsletter-manage'; $wgAvailableRights[] = 'newsletter-restore'; // Enable a "viewdeletedfile" userright for [[m:Global deleted image review]] (T16801) $wgAvailableRights[] = 'viewdeletedfile'; $wgHooks['TitleQuickPermissions'][] = static function ( Title $title, User $user, $action, &$errors, $doExpensiveQueries, $short ) { return ( !in_array( $action, [ 'deletedhistory', 'deletedtext' ] ) || !$title->inNamespaces( NS_FILE, NS_FILE_TALK ) || !$user->isAllowed( 'viewdeletedfile' ) ); }; // Assign "editautopatrolprotected" to sysops and bots, if autopatroller restriction level is enabled if ( in_array( 'editautopatrolprotected', $wgRestrictionLevels ) ) { $wgGroupPermissions['sysop']['editautopatrolprotected'] = true; $wgGroupPermissions['bot']['editautopatrolprotected'] = true; } // Extension:StopForumSpam rights, so they can be assigned to global groups (T334856) $wgAvailableRights[] = 'sfsblock-bypass'; # ###################################################################### # Logo settings # ###################################################################### // Pieced together so that wikis can inherit their logo from their project/default if ( isset( $wmgSiteLogo1x ) ) { $wgLogos = [ '1x' => $wmgSiteLogo1x ?? null, '1.5x' => $wmgSiteLogo1_5x ?? null, '2x' => $wmgSiteLogo2x ?? null, 'icon' => $wmgSiteLogoIcon ?? null, 'wordmark' => $wmgSiteLogoWordmark ?? null, 'tagline' => $wmgSiteLogoTagline ?? null, 'variants' => $wmgSiteLogoVariants ?? null, ]; } // Max width modifications $wgVectorMaxWidthOptions['exclude']['namespaces'] = $wmgVectorMaxWidthOptionsNamespaces; # ###################################################################### # Extensions # ###################################################################### // Yeah the next 3000 or so lines is for extension configuration except for the // bits that aren't. if ( $wmgUseTimeline ) { wfLoadExtension( 'timeline' ); $wgTimelineFileBackend = 'local-multiwrite'; // Filenames in Shellbox container with no .ttf suffix $wgTimelineFonts = [ 'freesans' => '/srv/app/fonts/FreeSans', // FreeSansWMF has been generated from FreeSans and FreeSerif by using this script with fontforge: // Open("FreeSans.ttf"); // MergeFonts("FreeSerif.ttf"); // SetFontNames("FreeSans-WMF", "FreeSans WMF", "FreeSans WMF Regular", "Regular", ""); // Generate("FreeSansWMF.ttf", "", 4 ); 'freesanswmf' => '/srv/app/fonts/FreeSansWMF', 'unifont' => '/srv/app/fonts/unifont' // TODO: add noto fonts ]; $wgTimelineFonts['default'] = $wgTimelineFonts[$wmgTimelineDefaultFont]; // Route easytimeline to the Shellbox named "shellbox-timeline". $wgShellboxUrls['easytimeline'] = $wmgLocalServices['shellbox-timeline']; } // TODO: This should be handled by LocalServices, not here. $wgCopyUploadProxy = ( $wmgRealm !== 'labs' ) ? $wmgLocalServices['urldownloader'] : false; $wgUploadThumbnailRenderHttpCustomHost = $wmgHostnames['upload']; $wgUploadThumbnailRenderHttpCustomDomain = $wmgLocalServices['upload']; if ( $wmgUseLocalHTTPProxy || ClusterConfig::getInstance()->isK8s() ) { $wgLocalHTTPProxy = $wmgLocalServices['mwapi'] ?? false; } if ( $wmgUseWikiHiero ) { wfLoadExtension( 'wikihiero' ); } wfLoadExtension( 'SiteMatrix' ); // Config for sitematrix $wgSiteMatrixFile = ( $wmgRealm === 'labs' ) ? "$IP/../langlist-labs" : "$IP/../langlist"; $wgSiteMatrixSites = [ 'wiki' => [ 'name' => 'Wikipedia', 'host' => 'www.wikipedia.org', 'prefix' => 'w', ], 'wiktionary' => [ 'name' => 'Wiktionary', 'host' => 'www.wiktionary.org', 'prefix' => 'wikt', ], 'wikibooks' => [ 'name' => 'Wikibooks', 'host' => 'www.wikibooks.org', 'prefix' => 'b', ], 'wikinews' => [ 'name' => 'Wikinews', 'host' => 'www.wikinews.org', 'prefix' => 'n', ], 'wikiquote' => [ 'name' => 'Wikiquote', 'host' => 'www.wikiquote.org', 'prefix' => 'q', ], 'wikisource' => [ 'name' => 'Wikisource', 'host' => 'www.wikisource.org', 'prefix' => 's', ], 'wikiversity' => [ 'name' => 'Wikiversity', 'host' => 'www.wikiversity.org', 'prefix' => 'v', ], 'wikivoyage' => [ 'name' => 'Wikivoyage', 'host' => 'www.wikivoyage.org', 'prefix' => 'voy', ], ]; $wgSiteMatrixClosedSites = MWWikiversions::readDbListFile( 'closed' ); $wgSiteMatrixPrivateSites = MWWikiversions::readDbListFile( 'private' ); $wgSiteMatrixFishbowlSites = MWWikiversions::readDbListFile( 'fishbowl' ); $wgSiteMatrixNonGlobalSites = MWWikiversions::readDbListFile( 'nonglobal' ); // list of codex icons to use for interwiki (based on SiteMatrix) search results widget // https://phabricator.wikimedia.org/T315269 $wgInterwikiLogoOverride = [ 'w' => 'logoWikipedia', 'wikt' => 'logoWiktionary', 'b' => 'logoWikibooks', 'n' => 'logoWikinews', 'q' => 'logoWikiquote', 's' => 'logoWikisource', 'v' => 'logoWikiversity', 'voy' => 'logoWikivoyage', ]; if ( $wmgUseCharInsert ) { wfLoadExtension( 'CharInsert' ); } if ( $wmgUseParserFunctions ) { wfLoadExtension( 'ParserFunctions' ); } $wgExpensiveParserFunctionLimit = 500; if ( $wmgUseCite ) { wfLoadExtension( 'Cite' ); } if ( $wmgUseCiteThisPage ) { wfLoadExtension( 'CiteThisPage' ); } if ( $wmgUseInputBox ) { wfLoadExtension( 'InputBox' ); } if ( $wmgUseImageMap ) { wfLoadExtension( 'ImageMap' ); } if ( $wmgUseSyntaxHighlight ) { wfLoadExtension( 'SyntaxHighlight_GeSHi' ); if ( $wmgUseSyntaxHighlightShellbox && $wmgLocalServices['shellbox-syntaxhighlight'] ) { // Route syntaxhighlight to the Shellbox named "shellbox-syntaxhighlight". $wgShellboxUrls['syntaxhighlight'] = $wmgLocalServices['shellbox-syntaxhighlight']; // $wgShellboxSecretKey set in PrivateSettings.php $wgPygmentizePath = '/srv/app/pygmentize'; } } if ( $wmgUsePoem ) { wfLoadExtension( 'Poem' ); } // Per-wiki config for Flagged Revisions if ( $wmgUseFlaggedRevs ) { // Include load of extension, and its config. include __DIR__ . '/flaggedrevs.php'; } if ( $wmgUseCategoryTree ) { wfLoadExtension( 'CategoryTree' ); } if ( $wmgUseProofreadPage ) { wfLoadExtension( 'ProofreadPage' ); if ( $wgDBname === 'dewikisource' ) { $wgGroupPermissions['*']['pagequality'] = true; # 27516 } if ( $wmgProofreadPageShowHeaders ) { $wgDefaultUserOptions['proofreadpage-showheaders'] = 1; } // Wikisource requires special handling (disable fixed width on page and index namespaces) // See T300563#7665461 and T74525 $wgVectorMaxWidthOptions['exclude']['namespaces'][] = $wgProofreadPageNamespaceIds['page']; // as well as T352162 $wgVectorMaxWidthOptions['exclude']['namespaces'][] = $wgProofreadPageNamespaceIds['index']; } if ( $wmgUseLabeledSectionTransclusion ) { wfLoadExtension( 'LabeledSectionTransclusion' ); } if ( $wmgUseSpamBlacklist ) { wfLoadExtension( 'SpamBlacklist' ); $wgBlacklistSettings = [ 'email' => [ 'files' => [ 'https://meta.wikimedia.org/w/index.php?title=Email_blacklist&action=raw&sb_ver=1' ], ], 'spam' => [ 'files' => [ 'https://meta.wikimedia.org/w/index.php?title=Spam_blacklist&action=raw&sb_ver=1' ], ], ]; $wgLogSpamBlacklistHits = true; } wfLoadExtension( 'TitleBlacklist' ); $wgTitleBlacklistBlockAutoAccountCreation = false; if ( $wmgUseGlobalTitleBlacklist ) { $wgTitleBlacklistSources = [ 'meta' => [ 'type' => 'url', 'src' => "https://meta.wikimedia.org/w/index.php?title=Title_blacklist&action=raw&tb_ver=1", ], ]; } if ( $wmgUseQuiz ) { wfLoadExtension( 'Quiz' ); } if ( $wmgUseGadgets ) { wfLoadExtension( 'Gadgets' ); } if ( $wmgUseTimedMediaHandler ) { wfLoadExtension( 'TimedMediaHandler' ); $wgTimedTextForeignNamespaces = [ 'commonswiki' => 102 ]; if ( $wgDBname === 'commonswiki' ) { $wgTimedTextNS = 102; } // enable transcoding on all wikis that allow uploads $wgEnableTranscode = $wgEnableUploads; // The default $wgEnabledTranscodeSet will include WebM VP9 flat file // transcodes from 240p to 2160p. Leave them enabled site-wide. // // Also enable a single WebM VP8 flat file for backwards compatibility. $wgEnabledTranscodeSet['360p.webm'] = true; // Enable HLS adaptive streaming tracks for compatibility with iOS // -- brion vibber, October 2023 // // This is a soft-launch, with playback of the HLS tracks used on // iOS browsers and app web views but not yet on desktop. // // Eventually these will replace most of the WebM transcodes, as they // can be played back in other browsers using Media Source Extensions // giving us a single universal track set that can adapt to screen // size/density and network conditions automatically. // // This whole section can be minimized or removed later once the new // tracks are enabled by default: // MP3 stereo audio for iOS 16 $wgEnabledTranscodeSet['stereo.audio.mp3'] = true; // Opus stereo audio for iOS 17 $wgEnabledTranscodeSet['stereo.audio.opus.mp4'] = true; // MJPEG SDR video for older iOS devices without hardware VP9 codec $wgEnabledTranscodeSet['144p.video.mjpeg.mov'] = true; // VP9 SDR video for newer iOS devices (circa iPhone 12) $wgEnabledTranscodeSet['240p.video.vp9.mp4'] = true; $wgEnabledTranscodeSet['360p.video.vp9.mp4'] = true; $wgEnabledTranscodeSet['480p.video.vp9.mp4'] = true; $wgEnabledTranscodeSet['720p.video.vp9.mp4'] = true; $wgEnabledTranscodeSet['1080p.video.vp9.mp4'] = true; $wgEnabledTranscodeSet['1440p.video.vp9.mp4'] = true; $wgEnabledTranscodeSet['2160p.video.vp9.mp4'] = true; // tmh1/2 have 12 cores and need lots of shared memory // for ffmpeg, which mmaps large input files $wgTranscodeBackgroundMemoryLimit = 4 * 1024 * 1024; // 4GB // This allows using 2x the threads for VP9 encoding, but will // fail if running a too-old ffmpeg version. $wgFFmpegVP9RowMT = true; // VP9 encoding benefits from more threads; up to 4 for HD or // 8 when using row-based multithreading. // // Note compression of second pass is "spiky", alternating between // single-threaded and multithreaded portions, so you can somewhat // overcommit process threads per CPU thread. $wgFFmpegThreads = 8; // HD transcodes of full-length films/docs/conference vids can // take several hours, and sometimes over 12. Bump up from default // 8 hour limit to 16 to avoid wasting the time we've already spent // when breaking these off. // Then double that for VP9, which is more intense on the CPU. $wgTranscodeBackgroundTimeLimit = 32 * 3600; // ffmpeg tends to use about 175% CPU when dual-threaded, so hits // say an 8-hour ulimit in 4-6 hours. This tends to cut // off very large files at very high resolution just before // they finish, wasting a lot of time. // Pad it back out so we don't waste that CPU time with a fail! $wgTranscodeBackgroundTimeLimit *= $wgFFmpegThreads; // Minimum size for an embed video player $wgMinimumVideoPlayerSize = $wmgMinimumVideoPlayerSize; // use new ffmpeg build w/ VP9 & Opus support $wgFFmpegLocation = '/usr/bin/ffmpeg'; // For MIDI to Ogg/MP3 conversion: // add Debian paths for fluidsynth and the sound font to use $wgTmhFluidsynthLocation = '/usr/bin/fluidsynth'; $wgTmhSoundfontLocation = '/usr/share/sounds/sf2/FluidR3_GM.sf2'; // The type of HTML5 player to use $wgTmhWebPlayer = 'videojs'; } if ( $wmgUseUploadsLink ) { wfLoadExtension( 'UploadsLink' ); } // Set default search experience to MediaSearch (T297484) if ( $wmgUseMediaSearch ) { $wgDefaultUserOptions['search-special-page'] = 'MediaSearch'; } if ( $wmgUseUrlShortener ) { wfLoadExtension( 'UrlShortener' ); $wgUrlShortenerTemplate = '/$1'; $wgUrlShortenerServer = 'https://w.wiki'; $wgVirtualDomainsMapping['virtual-urlshortener'] = [ 'cluster' => 'extension1', 'db' => 'wikishared' ]; $wgUrlShortenerAllowedDomains = [ '(.*\.)?wikipedia\.org', '(.*\.)?wiktionary\.org', '(.*\.)?wikibooks\.org', '(.*\.)?wikinews\.org', '(.*\.)?wikiquote\.org', '(.*\.)?wikisource\.org', '(.*\.)?wikiversity\.org', '(.*\.)?wikivoyage\.org', '(.*\.)?wikimedia\.org', '(.*\.)?wikidata\.org', '(.*\.)?wikifunctions\.org', '(.*\.)?mediawiki\.org', ]; $wgUrlShortenerApprovedDomains = [ '*.wikipedia.org', '*.wiktionary.org', '*.wikibooks.org', '*.wikinews.org', '*.wikiquote.org', '*.wikisource.org', '*.wikiversity.org', '*.wikivoyage.org', '*.wikimedia.org', '*.wikidata.org', '*.wikifunctions.org', '*.mediawiki.org', ]; $wgGroupPermissions['sysop']['urlshortener-manage-url'] = false; $wgGroupPermissions['sysop']['urlshortener-view-log'] = false; // Never ever change this config // Changing it would change target of all short urls $wgUrlShortenerIdSet = '23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz$'; $wgUrlShortenerEnableQrCode = true; // T348487 } if ( $wmgPFEnableStringFunctions ) { $wgPFEnableStringFunctions = true; } if ( $wgDBname === 'mediawikiwiki' ) { wfLoadExtension( 'ExtensionDistributor' ); $wgExtDistAPIConfig = [ 'class' => GerritExtDistProvider::class, 'apiUrl' => 'https://gerrit.wikimedia.org/r/projects/mediawiki%2F$TYPE%2F$EXT/branches', 'tarballUrl' => 'https://extdist.wmflabs.org/dist/$TYPE/$EXT-$REF-$SHA.tar.gz', 'tarballName' => '$EXT-$REF-$SHA.tar.gz', 'repoListUrl' => 'https://gerrit.wikimedia.org/r/projects/?b=master&p=mediawiki/$TYPE/', 'sourceUrl' => 'https://gerrit.wikimedia.org/r/mediawiki/$TYPE/$EXT.git', // Use the url-downloader proxy to reach out to gerrit. T340483 'proxy' => $wgCopyUploadProxy, ]; // Current stable release $wgExtDistDefaultSnapshot = 'REL1_41'; // Current development snapshot $wgExtDistCandidateSnapshot = 'REL1_42'; // Available snapshots $wgExtDistSnapshotRefs = [ 'master', 'REL1_42', 'REL1_41', 'REL1_40', 'REL1_39', ]; // Use Graphite for popular list $wgExtDistGraphiteRenderApi = 'https://graphite.wikimedia.org/render'; } // CentralAuth needed so that user CentralIds match if ( $wmgUseCentralAuth && $wmgUseGlobalBlocking ) { wfLoadExtension( 'GlobalBlocking' ); $wgVirtualDomainsMapping['virtual-globalblocking'] = [ 'db' => 'centralauth' ]; // leave this in place for now $wgGlobalBlockingDatabase = 'centralauth'; $wgApplyGlobalBlocks = $wmgApplyGlobalBlocks; $wgGlobalBlockingBlockXFF = true; // Apply blocks to IPs in XFF (T25343) } wfLoadExtension( 'TrustedXFF' ); if ( $wmgUseContactPage ) { wfLoadExtension( 'ContactPage' ); $wgContactConfig = []; $wgContactConfig['default'] = [ 'RecipientUser' => null, 'SenderEmail' => null, 'SenderName' => 'Contact Form on ' . $wgSitename, 'RequireDetails' => false, 'IncludeIP' => false, 'MustBeLoggedIn' => false, 'RLModules' => [], 'RLStyleModules' => [], 'AdditionalFields' => [ 'Text' => [ 'label-message' => 'emailmessage', 'type' => 'textarea', 'rows' => 20, 'required' => true, ], ], ]; $wgContactConfig['default'] = array_merge( $wgContactConfig['default'], $wmgContactPageConf ); if ( $wgDBname === 'metawiki' ) { include __DIR__ . '/MetaContactPages.php'; $wgContactConfig['stewards'] = [ // T98625 'RecipientUser' => 'Wikimedia Stewards', 'SenderEmail' => $wgPasswordSender, 'RequireDetails' => true, 'IncludeIP' => true, 'AdditionalFields' => [ 'Text' => [ 'label-message' => 'emailmessage', 'type' => 'textarea', 'rows' => 20, 'required' => true ], 'Disclaimer' => [ 'label-message' => 'contactpage-stewards-disclaimer-label', 'type' => 'info' ] ] ]; } if ( $wgDBname === 'enwiki' ) { include __DIR__ . '/EnWikiContactPages.php'; } } // At the moment securepoll doesn't work on k8s, or on newer linux distributions, // see T209892. TODO: properly disable it on votewiki when gnupg1 isn't available. if ( $wmgUseSecurePoll ) { wfLoadExtension( 'SecurePoll' ); $wgSecurePollUseNamespace = $wmgSecurePollUseNamespace; $wgHooks['SecurePoll_JumpUrl'][] = static function ( $page, &$url ) use ( $site, $lang ) { $url = wfAppendQuery( $url, [ 'site' => $site, 'lang' => $lang ] ); }; $wgSecurePollCreateWikiGroups = [ 'securepollglobal' => 'securepoll-dblist-securepollglobal' ]; // T303135 / T287780 $wgSecurePollExcludedWikis = [ 'labswiki', 'labtestwiki', 'loginwiki' ]; // T173393 - This is number of days after the election ends, not // number of days after the vote was cast. Lower to 60 days so that // overall time retained is not > 90 days. $wgSecurePollKeepPrivateInfoDays = 60; // T209802 - gpg2 is untested and evidently broken $wgSecurePollGPGCommand = 'gpg1'; if ( strpos( ClusterConfig::getInstance()->getHostname(), 'mwmaint' ) === 0 ) { $wgSecurePollShowErrorDetail = true; } } // PoolCounter if ( $wmgUsePoolCounter ) { include __DIR__ . '/PoolCounterSettings.php'; } if ( $wmgUseSecureLinkFixer ) { wfLoadExtension( 'SecureLinkFixer' ); } if ( $wmgUseScore ) { wfLoadExtension( 'Score' ); $wgScoreSafeMode = true; if ( $wmgUseScoreShellbox && $wmgLocalServices['shellbox'] ) { // Route score to the Shellbox named "shellbox". $wgShellboxUrls['score'] = $wmgLocalServices['shellbox']; // $wgShellboxSecretKey set in PrivateSettings.php $wgScoreImageMagickConvert = '/usr/bin/convert'; } else { # Emergency mode in which Score is disabled $wgScoreLilyPond = '/dev/null'; $wgScoreDisableExec = true; $wgScoreLilyPondFakeVersion = '2.22.0'; } } $wgHiddenPrefs[] = 'realname'; # e-mailing password based on e-mail address (T36386) $wgPasswordResetRoutes['email'] = true; if ( $wgDBname === 'labswiki' || $wgDBname === 'labtestwiki' ) { $wgUseInstantCommons = true; } if ( $wgDBname === 'nostalgiawiki' ) { # Link back to current version from the archive funhouse // phpcs:ignore MediaWiki.ControlStructures.AssignmentInControlStructures.AssignmentInControlStructures // phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.Found if ( ( isset( $_REQUEST['title'] ) && ( $title = $_REQUEST['title'] ) ) // phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.Found || ( isset( $_SERVER['PATH_INFO'] ) && ( $title = substr( $_SERVER['PATH_INFO'], 1 ) ) ) ) { if ( preg_match( '/^(.*)\\/Talk$/', $title, $matches ) ) { $title = 'Talk:' . $matches[1]; } $wgSiteNotice = "[//en.wikipedia.org/wiki/" . htmlspecialchars( urlencode( $title ) ) . ' See the current version of this page on Wikipedia]'; } else { $wgSiteNotice = "[//en.wikipedia.org/ See current Wikipedia]"; } // Nostalgia skin wfLoadSkin( 'Nostalgia' ); } $wgFooterIcons['copyright']['copyright'] = '' . 'Wikimedia Foundation'; # :SEARCH: # All wikis are special and get Cirrus :) # Must come *AFTER* PoolCounterSettings.php wfLoadExtension( 'Elastica' ); wfLoadExtension( 'CirrusSearch' ); include __DIR__ . '/CirrusSearch-common.php'; $wgInvalidateCacheOnLocalSettingsChange = false; $wgEnableUserEmail = true; $wgNoFollowLinks = true; // In case the MediaWiki default changed, T44594 # XFF log for incident response $wgExtensionFunctions[] = static function () { if ( isset( $_SERVER['REQUEST_METHOD'] ) && $_SERVER['REQUEST_METHOD'] === 'POST' // T129982 && $_SERVER['HTTP_HOST'] !== 'jobrunner.discovery.wmnet' ) { $uri = ( ( $_SERVER['HTTPS'] ?? null ) ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; $logger = LoggerFactory::getInstance( 'xff' ); $logger->info( "{date}\t{uri}\t{xff}, {remoteaddr}\t{wpSave}", [ 'date' => gmdate( 'r' ), 'uri' => $uri, 'xff' => $_SERVER['HTTP_X_FORWARDED_FOR'] ?? '', 'remoteaddr' => $_SERVER['REMOTE_ADDR'], 'wpSave' => ( $_REQUEST['wpSave'] ?? null ) ? 'save' : '', ] ); } }; // T26313, turn off minordefault on enwiki if ( $wgDBname === 'enwiki' ) { $wgHiddenPrefs[] = 'minordefault'; } $wgHooks['SkinAddFooterLinks'][] = static function ( $sk, $key, &$footerlinks ) use ( $wmgUseFooterContactLink, $wmgUseFooterCodeOfConductLink, $wmgUseFooterTechCodeOfConductLink ) { if ( $key !== 'places' ) { return; } if ( $wmgUseFooterContactLink ) { $footerlinks['contact'] = Html::element( 'a', [ 'href' => $sk->msg( 'contact-url' )->escaped() ], $sk->msg( 'contact' )->text() ); } if ( $wmgUseFooterCodeOfConductLink ) { $urlKey = 'wm-codeofconduct-url'; $msgKey = 'wm-codeofconduct'; } elseif ( $wmgUseFooterTechCodeOfConductLink ) { $urlKey = 'wm-techcodeofconduct-url'; $msgKey = 'wm-techcodeofconduct'; } $footerlinks['wm-codeofconduct'] = Html::element( 'a', [ 'href' => $sk->msg( $urlKey )->escaped() ], $sk->msg( $msgKey )->text() ); }; // T35186: turn off incomplete feature action=imagerotate $wgAPIModules['imagerotate'] = 'ApiDisabled'; if ( $wmgUseDynamicPageList ) { wfLoadExtension( 'intersection' ); $wgDLPMaxCacheTime = 604800; } $wgGroupPermissions['bureaucrat']['renameuser'] = $wmgAllowLocalRenameuser; if ( $wmgUseSpecialNuke ) { wfLoadExtension( 'Nuke' ); } if ( $wmgUseTorBlock ) { wfLoadExtension( 'TorBlock' ); $wgTorIPs = [ '91.198.174.232', '208.80.152.2', '208.80.152.134' ]; $wgTorAutoConfirmAge = 90 * 86400; $wgTorAutoConfirmCount = 100; $wgTorDisableAdminBlocks = false; $wgTorTagChanges = false; $wgGroupPermissions['user']['torunblocked'] = false; $wgTorBlockProxy = $wgCopyUploadProxy; } if ( $wmgUseRSSExtension ) { wfLoadExtension( 'RSS' ); $wgRSSProxy = $wgCopyUploadProxy; $wgRSSUrlWhitelist = $wmgRSSUrlWhitelist; } if ( $wgMaxCredits === 0 ) { $wgActions['credits'] = false; } # Process group overrides $wgGroupPermissions['steward' ]['userrights'] = true; $wgGroupPermissions['bureaucrat']['userrights'] = false; $wgGroupPermissions['sysop']['bigdelete'] = false; // quick hack foreach ( $groupOverrides2 as $group => $permissions ) { if ( !array_key_exists( $group, $wgGroupPermissions ) ) { $wgGroupPermissions[$group] = []; } $wgGroupPermissions[$group] = $permissions + $wgGroupPermissions[$group]; } foreach ( $groupOverrides as $group => $permissions ) { if ( !array_key_exists( $group, $wgGroupPermissions ) ) { $wgGroupPermissions[$group] = []; } $wgGroupPermissions[$group] = $permissions + $wgGroupPermissions[$group]; } if ( $wgDBname === 'loginwiki' ) { $wgGroupPermissions['*'] = [ 'read' => true, 'autocreateaccount' => true, 'editmyoptions' => true, // T158871 ]; $wgGroupPermissions['user'] = [ 'read' => true, 'writeapi' => true, ]; $wgGroupPermissions['autoconfirmed'] = [ 'read' => true, 'writeapi' => true, ]; unset( $wgGroupPermissions['import'] ); unset( $wgGroupPermissions['transwiki'] ); $wgGroupPermissions['sysop']['editinterface'] = false; } $wgAutopromote = [ 'autoconfirmed' => [ '&', [ APCOND_EDITCOUNT, $wgAutoConfirmCount ], [ APCOND_AGE, $wgAutoConfirmAge ], ], ]; if ( is_array( $wmgAutopromoteExtraGroups ) ) { $wgAutopromote += $wmgAutopromoteExtraGroups; } $wgAutopromoteOnce = [ 'onEdit' => $wmgAutopromoteOnceonEdit, ]; if ( is_array( $wmgExtraImplicitGroups ) ) { $wgImplicitGroups = array_merge( $wgImplicitGroups, $wmgExtraImplicitGroups ); } if ( $wmgRealm == 'labs' ) { $wgHTTPTimeout = 10; } if ( $wgRequestTimeLimit ) { // Set the maximum HTTP client timeout equal to the current request timeout (T245170) $wgHTTPMaxTimeout = $wgHTTPMaxConnectTimeout = $wgRequestTimeLimit; } if ( isset( $_REQUEST['captchabypass'] ) && $_REQUEST['captchabypass'] == $wmgCaptchaPassword ) { $wmgEnableCaptcha = false; } if ( $wmgEnableCaptcha ) { wfLoadExtension( 'ConfirmEdit' ); wfLoadExtension( 'ConfirmEdit/FancyCaptcha' ); $wgGroupPermissions['autoconfirmed']['skipcaptcha'] = true; $wgCaptchaFileBackend = 'global-multiwrite'; $wgCaptchaSecret = $wmgCaptchaSecret; $wgCaptchaDirectoryLevels = 3; $wgCaptchaStorageClass = CaptchaCacheStore::class; $wgCaptchaClass = FancyCaptcha::class; $wgCaptchaWhitelist = '#^(https?:)?//([.a-z0-9-]+\\.)?((wikimedia|wikipedia|wiktionary|wikiquote|wikibooks|wikisource|wikispecies|mediawiki|wikinews|wikiversity|wikivoyage|wikidata|wikifunctions|wmflabs)\.org' . '|dnsstuff\.com|completewhois\.com|wikimedia\.de)([?/\#]|$)#i'; // 'XRumer' spambot // adds non-real links // http://meta.wikimedia.org/wiki/User:Cometstyles/XRumer // http://meta.wikimedia.org/wiki/User:Jorunn/tracks // (added 2008-05-08 -- brion) $wgCaptchaRegexes[] = '/ 'centralauth' ]; // Enable cross-origin session cookies (T252236). $wgCookieSameSite = 'None'; $wgCentralAuthDryRun = false; $wgCentralAuthCookies = true; $wgCentralAuthUseEventLogging = true; $wgCentralAuthPreventUnattached = true; foreach ( $wmgLocalServices['irc'] as $address ) { $wgCentralAuthRC[] = [ 'formatter' => IRCColourfulCARCFeedFormatter::class, 'uri' => "udp://$address:$wmgRC2UDPPort/#central\t", ]; } $wgCentralAuthLoginWiki = 'loginwiki'; $wgCentralAuthAutoLoginWikis = $wmgCentralAuthAutoLoginWikis; $wgCentralAuthCookieDomain = $wmgCentralAuthCookieDomain; $wgCentralAuthLoginIcon = $wmgCentralAuthLoginIcon; // Temporary fix for T350695: when setting a CentralAuth cookie without a 'domain' cookie attribute, // clear pre-existing cookies with a domain attribute. // Temporary fix for T351685: when setting a CentralAuth cookie with domain=wikisource.org, // clear pre-existing cookies without a domain attribute // Can be removed after 2024-11-08. $wgHooks['WebResponseSetCookie'][] = static function ( &$name, &$value, &$expire, &$options ) { global $wgDBname, $wmgCentralAuthWebResponseSetCookieRecurse, $wgServer; $realName = ( $options['prefix'] ?? '' ) . $name; $centralAuthCookies = [ 'centralauth_Session', 'centralauth_User', 'centralauth_Token', 'centralauth_LoggedOut' ]; $shouldNotHaveCookieWithDomain = in_array( $wgDBname, [ 'commonswiki', 'metawiki' ] ); $shouldNotHaveCookieWithoutDomain = $wgDBname === 'sourceswiki'; $isSettingDomain = (bool)( $options['domain'] ?? '' ); if ( in_array( $realName, $centralAuthCookies ) && ( $isSettingDomain ? $shouldNotHaveCookieWithoutDomain : $shouldNotHaveCookieWithDomain ) && !$wmgCentralAuthWebResponseSetCookieRecurse ) { $webResponse = RequestContext::getMain()->getRequest()->response(); $clearOptions = $options; if ( $isSettingDomain ) { $clearOptions['domain'] = ''; } else { $serverUrl = wfExpandUrl( $wgServer, PROTO_CANONICAL ); if ( MobileContext::singleton()->usingMobileDomain() ) { $serverUrl = MobileContext::singleton()->getMobileUrl( $serverUrl ); } $parsedUrl = wfParseUrl( $serverUrl ); $clearOptions['domain'] = $parsedUrl['host']; } $wmgCentralAuthWebResponseSetCookieRecurse = true; $webResponse->clearCookie( $name, $clearOptions ); $wmgCentralAuthWebResponseSetCookieRecurse = false; } }; // T359957 Add per-domain origin trial tokens for opting out of third-party cookie blocking. Trial will expire at end of 2024. $wgOriginTrials[] = $wmgCentralAuthThirdPartyCookieDeprecationTrialToken; /** * This function is used for both the CentralAuthWikiList and * GlobalUserPageWikis hooks. * * @param array &$list * @return bool */ function wmfCentralAuthWikiList( &$list ) { global $wgLocalDatabases, $wgSiteMatrixPrivateSites, $wgSiteMatrixFishbowlSites, $wgSiteMatrixClosedSites, $wgSiteMatrixNonGlobalSites; $list = array_diff( $wgLocalDatabases, $wgSiteMatrixPrivateSites, $wgSiteMatrixFishbowlSites, $wgSiteMatrixClosedSites, $wgSiteMatrixNonGlobalSites ); return false; } $wgHooks['CentralAuthWikiList'][] = 'wmfCentralAuthWikiList'; // Let's give it another try $wgCentralAuthCreateOnView = true; // Attempt to attach unattached accounts by password on login $wgCentralAuthAutoMigrate = true; // Try to create global accounts if one doesn't exist and it's safe $wgCentralAuthAutoMigrateNonGlobalAccounts = true; // Enables Special:GlobalRenameRequest $wgCentralAuthEnableGlobalRenameRequest = true; // Only allow users with global accounts to login $wgCentralAuthStrict = true; // Create some local accounts as soon as the global registration happens $wgCentralAuthAutoCreateWikis = [ 'loginwiki', 'metawiki' ]; // Link global block blockers to user pages on Meta $wgCentralAuthGlobalBlockInterwikiPrefix = 'meta'; // See T104371 and [[m:Requests_for_comment/Password_policy_for_users_with_certain_advanced_permissions]] foreach ( $wmgPrivilegedGlobalGroups as $group ) { $wgCentralAuthGlobalPasswordPolicies[$group] = $wmgPrivilegedPolicy; if ( $group === 'staff' ) { // Require 10 byte password for staff. $wgCentralAuthGlobalPasswordPolicies[$group]['MinimumPasswordLengthToLogin'] = 10; } } // Check global rename log on meta for new accounts $wgCentralAuthOldNameAntiSpoofWiki = 'metawiki'; $wgVirtualDomainsMapping['virtual-botpasswords'] = [ 'db' => 'metawiki' ]; } // Config for GlobalCssJs // Only enable on CentralAuth wikis if ( $wmgUseGlobalCssJs && $wmgUseCentralAuth ) { wfLoadExtension( 'GlobalCssJs' ); // Disable site-wide global css/js $wgUseGlobalSiteCssJs = false; // Setup metawiki as central wiki $wgResourceLoaderSources['metawiki'] = [ 'apiScript' => '//meta.wikimedia.org/w/api.php', 'loadScript' => '//meta.wikimedia.org/w/load.php', ]; $wgGlobalCssJsConfig = [ 'wiki' => 'metawiki', // database name 'source' => 'metawiki', // ResourceLoader source name ]; } if ( $wmgUseGlobalUserPage && $wmgUseCentralAuth ) { wfLoadExtension( 'GlobalUserPage' ); $wgGlobalUserPageAPIUrl = 'https://meta.wikimedia.org/w/api.php'; $wgGlobalUserPageDBname = 'metawiki'; $wgHooks['GlobalUserPageWikis'][] = 'wmfCentralAuthWikiList'; } if ( $wmgLocalAuthLoginOnly && $wmgUseCentralAuth ) { // T57420: prevent creation of local password records for SUL users if ( isset( $wgAuthManagerAutoConfig['primaryauth'][\MediaWiki\Auth\LocalPasswordPrimaryAuthenticationProvider::class] ) ) { $wgAuthManagerAutoConfig['primaryauth'][\MediaWiki\Auth\LocalPasswordPrimaryAuthenticationProvider::class]['args'][0]['loginOnly'] = true; } } if ( $wmgUseApiFeatureUsage ) { wfLoadExtension( 'ApiFeatureUsage' ); $wgApiFeatureUsageQueryEngineConf = [ 'class' => ApiFeatureUsageQueryEngineElastica::class, 'serverList' => $wmgLocalServices['search-chi'], ]; } // taking it live 2006-12-15 brion wfLoadExtension( 'DismissableSiteNotice' ); $wgDismissableSiteNoticeForAnons = true; // T59732 $wgMajorSiteNoticeID = '2'; /** * Get an array of groups (in $wmgPrivilegedGroups) that $username is part of * * @param UserIdentity $user * @return array Any elevated/privileged groups the user is a member of */ function wmfGetPrivilegedGroups( $user ) { global $wmgUseCentralAuth, $wmgPrivilegedGroups, $wmgPrivilegedGlobalGroups; if ( $wmgUseCentralAuth && CentralAuthUser::getInstanceByName( $user->getName() )->exists() ) { $centralUser = CentralAuthUser::getInstanceByName( $user->getName() ); try { $groups = array_intersect( array_merge( $wmgPrivilegedGroups, $wmgPrivilegedGlobalGroups ), array_merge( $centralUser->getGlobalGroups(), $centralUser->getLocalGroups() ) ); } catch ( Exception $e ) { // Don't block login if we can't query attached (T119736) MWExceptionHandler::logException( $e ); $groups = array_merge( MediaWikiServices::getInstance() ->getUserGroupManager() ->getUserGroups( $user ), $centralUser->getGlobalGroups() ); } } else { // use effective groups, as we set 'user' as privileged for private/fishbowl wikis $groups = array_intersect( $wmgPrivilegedGroups, MediaWikiServices::getInstance() ->getUserGroupManager() ->getUserEffectiveGroups( $user ) ); } return $groups; } // log suspicious or sensitive login attempts $wgHooks['AuthManagerLoginAuthenticateAudit'][] = static function ( $response, $user, $username ) { $guessed = false; if ( !$user && $username ) { $user = MediaWikiServices::getInstance() ->getUserIdentityLookup() ->getUserIdentityByName( $username ); $guessed = true; } if ( !$user || !in_array( $response->status, [ AuthenticationResponse::PASS, AuthenticationResponse::FAIL ], true ) ) { return; } global $wgRequest; $headers = function_exists( 'apache_request_headers' ) ? apache_request_headers() : []; $successful = $response->status === AuthenticationResponse::PASS; $privGroups = wmfGetPrivilegedGroups( $user ); $channel = $successful ? 'goodpass' : 'badpass'; if ( $privGroups ) { $channel .= '-priv'; } $logger = LoggerFactory::getInstance( $channel ); $verb = $successful ? 'succeeded' : 'failed'; $logger->info( "Login $verb for {priv} {name} from {clientip} - {xff} - {ua} - {geocookie}: {messagestr}", [ 'successful' => $successful, 'groups' => implode( ', ', $privGroups ), 'priv' => ( $privGroups ? 'elevated' : 'normal' ), 'name' => $user->getName(), 'clientip' => $wgRequest->getIP(), 'xff' => @$headers['X-Forwarded-For'], 'ua' => @$headers['User-Agent'], 'guessed' => $guessed, 'msgname' => $response->message ? $response->message->getKey() : '-', 'messagestr' => $response->message ? $response->message->inLanguage( 'en' )->text() : '', 'geocookie' => $wgRequest->getCookie( 'GeoIP', '' ), ] ); }; // log sysop password changes $wgHooks['ChangeAuthenticationDataAudit'][] = static function ( $req, $status ) { global $wgRequest; $user = MediaWikiServices::getInstance() ->getUserIdentityLookup() ->getUserIdentityByName( $req->username ); $status = Status::wrap( $status ); if ( $req instanceof \MediaWiki\Auth\PasswordAuthenticationRequest ) { $privGroups = wmfGetPrivilegedGroups( $user ); $priv = ( $privGroups ? 'elevated' : 'normal' ); if ( $priv === 'elevated' ) { $headers = function_exists( 'apache_request_headers' ) ? apache_request_headers() : []; $logger = LoggerFactory::getInstance( 'badpass' ); $logger->info( 'Password change in prefs for {priv} {name}: {status} - {clientip} - {xff} - {ua} - {geocookie}', [ 'name' => $user->getName(), 'groups' => implode( ', ', $privGroups ), 'priv' => $priv, 'status' => $status->isGood() ? 'ok' : $status->getWikiText( null, null, 'en' ), 'clientip' => $wgRequest->getIP(), 'xff' => @$headers['X-Forwarded-For'], 'ua' => @$headers['User-Agent'], 'geocookie' => $wgRequest->getCookie( 'GeoIP', '' ), ] ); } } }; // Passed to ulimit $wgMaxShellFileSize = 512 * 1024; // Kilobytes $wgMaxShellMemory = 1024 * 1024; // Kilobytes $wgMaxShellTime = 50; // seconds // Use a cgroup for shell execution. // This will cause shell execution to fail if the cgroup is not installed. // If some misc server doesn't have the cgroup installed, you can create it // with: mkdir -p -m777 /sys/fs/cgroup/memory/mediawiki/job $wgShellCgroup = '/sys/fs/cgroup/memory/mediawiki/job'; switch ( $wmgRealm ) { case 'production': $wgImageMagickTempDir = '/tmp/magick-tmp'; break; case 'labs': $wgImageMagickTempDir = '/tmp/a/magick-tmp'; break; } // Banner notice system if ( $wmgUseCentralNotice ) { wfLoadExtension( 'CentralNotice' ); // for DNS prefetching $wgCentralHost = "//{$wmgHostnames['meta']}"; // for banner loading if ( $wmgRealm === 'production' && $wgDBname === 'testwiki' ) { $wgCentralSelectedBannerDispatcher = "//test.wikipedia.org/w/index.php?title=Special:BannerLoader"; // No caching for banners on testwiki, so we can develop them there a bit faster - NeilK 2012-01-16 // Never set this to zero on a highly trafficked wiki, there are server-melting consequences $wgNoticeBannerMaxAge = 0; } else { $wgCentralSelectedBannerDispatcher = "//{$wmgHostnames['meta']}/w/index.php?title=Special:BannerLoader"; } // Relative URL which is hardcoded to HTTP 204 in Varnish config. $wgCentralBannerRecorder = "{$wgServer}/beacon/impression"; // Allow only these domains to access CentralNotice data through the reporter $wgNoticeReporterDomains = 'https://donate.wikimedia.org'; $wgCentralDBname = 'metawiki'; $wgNoticeInfrastructure = false; $wgCentralNoticeAdminGroup = false; // ESI test; see T308799 $wgCentralNoticeESITestString = ''; if ( $wmgRealm == 'production' && $wgDBname === 'testwiki' ) { // test.wikipedia.org has its own central database: $wgCentralDBname = 'testwiki'; $wgNoticeInfrastructure = true; } elseif ( $wgDBname === 'metawiki' ) { $wgNoticeInfrastructure = true; } if ( $wgNoticeInfrastructure ) { // List of available wiki groups within CentralNotice // (Only referenced from the infrastructure special pages.) $wgNoticeProjects = [ "wikipedia", "wiktionary", "wikiquote", "wikibooks", "wikidata", "wikinews", "wikisource", "wikiversity", "wikivoyage", "wikimedia", "commons", "meta", "wikispecies", "test", "mediawiki", ]; $wgCentralNoticeMessageProtectRight = 'banner-protect'; } // Set fundraising banners to use HTTPS on foundation wiki $wgNoticeFundraisingUrl = 'https://donate.wikimedia.org/wiki/Special:LandingCheck'; // Enable the CentralNotice/Translate integration $wgNoticeUseTranslateExtension = true; // T51905 $wgNoticeUseLanguageConversion = true; // *** Hide Cookies *** // A little bit of historical breadcrumbs: // In 2012 we expired cookies on 2012-12-26, then everyone had // a two week expiration until 2013-01-22 whereupon we introduced // a year long expiration. For the 2013 fundraiser starting // 2013-12-02 we're now using a 10 month expiration. // For the 2014 fundraiser it's 250 days, though we can change it // retroactively as the cookie value now has a create date and reason. // 'close' duration is used for the banner X button // 'donate' duration is used for cookie set on Thank You page $wgNoticeCookieDurations = [ 'close' => 604800, // 1 week 'donate' => 21600000, // 250 days ]; // T18821 // Updates made here also need to be reflected in // foundation.wikimedia.org/wiki/Template:HideBanners $wgNoticeHideUrls = [ '//en.wikipedia.org/w/index.php?title=Special:HideBanners', '//meta.wikimedia.org/w/index.php?title=Special:HideBanners', '//commons.wikimedia.org/w/index.php?title=Special:HideBanners', '//species.wikimedia.org/w/index.php?title=Special:HideBanners', '//en.wikibooks.org/w/index.php?title=Special:HideBanners', '//en.wikiquote.org/w/index.php?title=Special:HideBanners', '//en.wikisource.org/w/index.php?title=Special:HideBanners', '//en.wikinews.org/w/index.php?title=Special:HideBanners', '//en.wikiversity.org/w/index.php?title=Special:HideBanners', '//www.mediawiki.org/w/index.php?title=Special:HideBanners', ]; // Emit CSP headers on banner previews. This can go away when full CSP // support (T135963) is deployed. // www.pages04.net is used by Wikimedia Fundraising to enable 'remind me later' banner functionality, which submits email addresses to our email campaign vendor $wgCentralNoticeContentSecurityPolicy = "script-src 'unsafe-eval' blob: 'self' meta.wikimedia.org *.wikimedia.org *.wikipedia.org *.wikinews.org *.wiktionary.org *.wikibooks.org *.wikiversity.org *.wikisource.org wikisource.org *.wikiquote.org *.wikidata.org *.wikifunctions.org *.wikivoyage.org *.mediawiki.org 'unsafe-inline'; default-src 'self' data: blob: upload.wikimedia.org https://commons.wikimedia.org meta.wikimedia.org *.wikimedia.org *.wikipedia.org *.wikinews.org *.wiktionary.org *.wikibooks.org *.wikiversity.org *.wikisource.org wikisource.org *.wikiquote.org *.wikidata.org *.wikifunctions.org *.wikivoyage.org *.mediawiki.org wikimedia.org www.pages04.net; style-src 'self' data: blob: upload.wikimedia.org https://commons.wikimedia.org meta.wikimedia.org *.wikimedia.org *.wikipedia.org *.wikinews.org *.wiktionary.org *.wikibooks.org *.wikiversity.org *.wikisource.org wikisource.org *.wikiquote.org *.wikidata.org *.wikifunctions.org *.wikivoyage.org *.mediawiki.org wikimedia.org 'unsafe-inline';"; } // Load our site-specific l10n extension wfLoadExtension( 'WikimediaMessages' ); if ( $wgDBname === 'enwiki' ) { // Please don't interfere with our hundreds of wikis ability to manage themselves. // Only use this shitty hack for enwiki. Thanks. // -- brion 2008-04-10 $wgHooks['getUserPermissionsErrorsExpensive'][] = static function ( &$title, &$user, $action, &$result ) { if ( $action !== 'delete' && $action !== 'move' ) { return true; } $main = Title::newMainPage(); $mainText = $main->getPrefixedDBkey(); if ( $mainText === $title->getPrefixedDBkey() ) { $result = [ 'cant-delete-main-page' ]; return false; } }; } if ( $wgDBname === 'enwiki' || $wgDBname === 'fawiki' ) { // T59569, T105118 // // If it's an anonymous user creating a page in the English and Persian Wikipedia // Draft namespace, tell TitleQuickPermissions to abort the normal // checkQuickPermissions checks. This lets anonymous users create a page in this // namespace, even though they don't have the general 'createpage' right. // // It does not affect other checks from getUserPermissionsErrorsInternal // (e.g. protection and blocking). // // Returning true tells it to proceed as normal in other cases. $wgHooks['TitleQuickPermissions'][] = static function ( Title $title, User $user, $action, &$errors, $doExpensiveQueries, $short ) { return ( $action !== 'create' || $title->getNamespace() !== 118 || !$user->isAnon() ); }; } if ( $wmgUseCollection ) { // PediaPress / PDF generation // Intentionally loaded *before* the Wikisource extension below. wfLoadExtension( 'Collection' ); // Use pediapress server for POD function (T73675) $wgCollectionCommandToServeURL = [ 'zip_post' => "{$wmgLocalServices['urldownloader']}|https://pediapress.com/wmfup/", ]; $wgCollectionPODPartners = [ 'pediapress' => [ 'name' => 'PediaPress', 'url' => 'https://pediapress.com/', 'posturl' => 'https://pediapress.com/api/collections/', 'infopagetitle' => 'coll-order_info_article', ], ]; // MediaWiki namespace is not a good default $wgCommunityCollectionNamespace = NS_PROJECT; // Allow collecting Help pages $wgCollectionArticleNamespaces[] = NS_HELP; $wgCollectionFormats = [ 'rdf2latex' => 'PDF', 'rdf2text' => 'TXT', ]; if ( !$wmgUseElectronPdfService ) { $wgCollectionShowRenderNotes[] = 'coll-rendering_finished_note_article_rdf2latex'; } $wgCollectionPortletForLoggedInUsersOnly = $wmgCollectionPortletForLoggedInUsersOnly; $wgCollectionArticleNamespaces = $wmgCollectionArticleNamespaces; $wgCollectionPortletFormats = $wmgCollectionPortletFormats; } if ( $wmgUseElectronPdfService ) { wfLoadExtension( 'ElectronPdfService' ); } wfLoadExtension( 'AdvancedSearch' ); # Various system to allow/prevent flooding # (including exemptions for scheduled outreach events) require __DIR__ . '/throttle.php'; require __DIR__ . '/throttle-analyze.php'; if ( $wmgUseNewUserMessage ) { wfLoadExtension( 'NewUserMessage' ); $wgNewUserSuppressRC = true; $wgNewUserMinorEdit = $wmgNewUserMinorEdit; $wgNewUserMessageOnAutoCreate = $wmgNewUserMessageOnAutoCreate; } # AbuseFilter wfLoadExtension( 'AbuseFilter' ); include __DIR__ . '/abusefilter.php'; if ( $wmgUseGlobalAbuseFilters ) { $wgAbuseFilterCentralDB = $wmgAbuseFilterCentralDB; $wgAbuseFilterIsCentral = ( $wgDBname === $wgAbuseFilterCentralDB ); } # PdfHandler if ( $wmgUsePdfHandler ) { wfLoadExtension( 'PdfHandler' ); // Force use of shellbox on mw on k8s. // We're not sending commons user traffic here so this can live for as long as needed // before we make upload-by-url asynchronous if ( !$wmgUsePdfHandlerShellbox && ClusterConfig::getInstance()->isK8s() ) { $wmgUsePdfHandlerShellbox = true; } if ( $wmgUsePdfHandlerShellbox && $wmgLocalServices['shellbox-media'] ) { // Route pdfhandler to the Shellbox named "shellbox-media". $wgShellboxUrls['pdfhandler'] = $wmgLocalServices['shellbox-media']; // $wgShellboxSecretKey set in PrivateSettings.php } else { $wgPdfProcessor = '/usr/local/bin/mediawiki-firejail-ghostscript'; $wgPdfPostProcessor = '/usr/local/bin/mediawiki-firejail-convert'; } } # WikiEditor wfLoadExtension( 'WikiEditor' ); $wgDefaultUserOptions['usebetatoolbar'] = 1; if ( $wmgEnableLandingCheck ) { wfLoadExtension( 'LandingCheck' ); $wgPriorityCountries = [ // === Fundraising Chapers 'DE', 'CH', // === Blacklisted countries 'BY', 'CD', 'CI', 'CU', 'IQ', 'IR', 'KP', 'LB', 'LY', 'MM', 'SD', 'SO', 'SY', 'YE', 'ZW', ]; $wgLandingCheckPriorityURLBase = "//foundation.wikimedia.org/wiki/Special:LandingCheck"; $wgLandingCheckNormalURLBase = "//donate.wikimedia.org/wiki/Special:LandingCheck"; } if ( $wmgEnableFundraiserLandingPage ) { wfLoadExtension( 'FundraiserLandingPage' ); } if ( $wmgUseLiquidThreads || $wmgLiquidThreadsFrozen ) { require_once __DIR__ . '/liquidthreads.php'; } if ( $wmgUseGlobalUsage ) { wfLoadExtension( 'GlobalUsage' ); $wgGlobalUsageDatabase = 'commonswiki'; $wgGlobalUsageSharedRepoWiki = 'commonswiki'; $wgGlobalUsagePurgeBacklinks = true; } wfLoadExtension( 'TemplateStyles' ); // allow protocol-relative URLs per T188760 $wgTemplateStylesAllowedUrls = [ 'audio' => [ '<^(?:https:)?//upload\\.wikimedia\\.org/wikipedia/commons/>', ], 'image' => [ '<^(?:https:)?//upload\\.wikimedia\\.org/wikipedia/commons/>', ], 'svg' => [ '<^(?:https:)?//upload\\.wikimedia\\.org/wikipedia/commons/[^?#]*\\.svg(?:[?#]|$)>', ], 'font' => [], 'namespace' => [ '<.>' ], 'css' => [], ]; wfLoadExtension( 'CodeMirror' ); // Must be loaded BEFORE VisualEditor, or things will break if ( $wmgUseArticleCreationWorkflow ) { wfLoadExtension( 'ArticleCreationWorkflow' ); } $wgDefaultUserOptions['thumbsize'] = $wmgThumbsizeIndex; $wgDefaultUserOptions['showhiddencats'] = $wmgShowHiddenCats; $wgDefaultUserOptions['watchcreations'] = true; // Temporary override: WMF is not hardcore enough to enable this. // See T37785, T38316, T47022 about it. $wgDefaultUserOptions['watchdefault'] = (int)$wmgWatchlistDefault; $wgDefaultUserOptions['watchmoves'] = (int)$wmgWatchMoves; $wgDefaultUserOptions['watchrollback'] = (int)$wmgWatchRollback; $wgDefaultUserOptions['enotifminoredits'] = $wmgEnotifMinorEditsUserDefault; $wgDefaultUserOptions['enotifwatchlistpages'] = 0; $wgDefaultUserOptions['usenewrc'] = (int)$wmgEnhancedRecentChanges; $wgDefaultUserOptions['extendwatchlist'] = (int)$wmgEnhancedWatchlist; $wgDefaultUserOptions['forceeditsummary'] = (int)$wmgForceEditSummary; $wgDefaultUserOptions['wlshowwikibase'] = (int)$wmgShowWikidataInWatchlist; if ( $wmgUseMassMessage ) { wfLoadExtension( 'MassMessage' ); } if ( $wmgUseSandboxLink ) { wfLoadExtension( 'SandboxLink' ); } if ( $wmgUseUploadWizard ) { wfLoadExtension( 'UploadWizard' ); $wgUploadStashScalerBaseUrl = "//{$wmgHostnames['upload']}/$site/$lang/thumb/temp"; $wgUploadWizardConfig = [ # 'debug' => true, // Normally we don't include API keys in CommonSettings, but this key // isn't private since it's used on the client-side, i.e. anyone can see // it in the outgoing AJAX requests to Flickr. 'flickrApiKey' => 'e9d8174a79c782745289969a45d350e8', // Slowwwwwwww 'campaignExpensiveStatsEnabled' => false, ]; $wgUploadWizardConfig['enableChunked'] = 'opt-in'; $wgUploadWizardConfig['altUploadForm'] = $wmgAltUploadForm; // T35513 if ( $wgDBname === 'testwiki' ) { $wgUploadWizardConfig['feedbackPage'] = 'Prototype_upload_wizard_feedback'; $wgUploadWizardConfig["missingCategoriesWikiText"] = '

Hey, no categories?

'; unset( $wgUploadWizardConfig['fallbackToAltUploadForm'] ); } elseif ( $wgDBname === 'commonswiki' ) { $wgUploadWizardConfig['feedbackPage'] = 'Commons:Upload_Wizard_feedback'; # Set by neilk, 2011-11-01, per erik $wgUploadWizardConfig["missingCategoriesWikiText"] = "{{subst:unc}}"; $wgUploadWizardConfig['flickrBlacklistPage'] = 'User:FlickreviewR/bad-authors'; $wgUploadWizardConfig['customLicenseTemplate'] = 'Template:License_template_tag'; } // Enable Structured Data captions on upload if ( $wmgUseWikibaseMediaInfo ) { $wgUploadWizardConfig['wikibase']['enabled'] = true; $wgUploadWizardConfig['wikibase']['statements'] = $wmgMediaInfoEnableUploadWizardStatements; } } if ( $wmgUseMediaSearch ) { wfLoadExtension( 'MediaSearch' ); } if ( $wmgCustomUploadDialog ) { $wgUploadDialog = [ 'fields' => [ 'description' => true, 'date' => true, 'categories' => true, ], 'licensemessages' => [ // Messages defined in WikimediaMessages: upload-form-label-own-work-message-commons, // upload-form-label-not-own-work-message-commons, upload-form-label-not-own-work-local-commons 'local' => $wgDBname === 'commonswiki' ? 'commons' : 'generic-local', 'foreign' => $wgDBname === 'commonswiki' ? 'commons' : 'generic-foreign', ], 'comment' => 'Uploaded while editing "$PAGENAME" on $HOST', 'format' => [ 'filepage' => '== {{int:filedesc}} == {{Information |description=$DESCRIPTION |date=$DATE |source=$SOURCE |author=$AUTHOR }} == {{int:license-header}} == $LICENSE $CATEGORIES ', 'description' => '{{$LANGUAGE|1=$TEXT}}', 'ownwork' => '{{own}}', 'license' => '{{self|cc-by-sa-4.0}}', 'uncategorized' => '{{subst:unc}}', ], ]; } if ( $wmgUseBetaFeatures ) { wfLoadExtension( 'BetaFeatures' ); } if ( $wmgUseCommonsMetadata ) { wfLoadExtension( 'CommonsMetadata' ); $wgCommonsMetadataSetTrackingCategories = true; $wgCommonsMetadataForceRecalculate = $wmgCommonsMetadataForceRecalculate; } if ( $wmgUseMultimediaViewer ) { wfLoadExtension( 'MultimediaViewer' ); } if ( $wmgUsePopups ) { wfLoadExtension( 'Popups' ); } if ( $wmgUseLinter ) { wfLoadExtension( 'Linter' ); } if ( !isset( $wgVirtualRestConfig ) && ( $wmgUseRestbaseVRS || $wmgUseParsoid || $wmgUseCollection ) ) { $wgVirtualRestConfig = [ 'modules' => [], 'global' => [ 'domain' => $wgCanonicalServer, 'timeout' => 360, 'forwardCookies' => false, 'HTTPProxy' => null, ] ]; } if ( $wmgUseRestbaseVRS ) { $wgVirtualRestConfig['modules']['restbase'] = [ 'url' => $wmgLocalServices['restbase'], 'domain' => $wgCanonicalServer, 'forwardCookies' => false, 'parsoidCompat' => false ]; } if ( $wmgUseParsoid ) { $wmgParsoidURL = $wmgLocalServices['parsoid']; $wgVirtualRestConfig['modules']['parsoid'] = [ 'url' => $wmgParsoidURL, 'prefix' => $wgDBname, // The wiki prefix to use; deprecated 'domain' => $wgCanonicalServer, 'forwardCookies' => $wmgParsoidForwardCookies, 'restbaseCompat' => false ]; } if ( $wmgUseVisualEditor ) { wfLoadExtension( 'VisualEditor' ); // Tab configuration if ( $wmgVisualEditorIsSecondaryEditor ) { $wgDefaultUserOptions['visualeditor-editor'] = 'wikitext'; } else { $wgDefaultUserOptions['visualeditor-editor'] = 'visualeditor'; } if ( $wmgVisualEditorEnableWikitext ) { $wgDefaultUserOptions['visualeditor-newwikitext'] = true; } // Show identical preferences on all wikis, but keep the old beta feature config // for compatibility with preferences previously set by users (T335056) $wgVisualEditorEnableBetaFeature = !$wmgVisualEditorDefault; // Feedback configuration if ( $wmgVisualEditorConsolidateFeedback ) { $wgVisualEditorFeedbackAPIURL = 'https://www.mediawiki.org/w/api.php'; $wgVisualEditorFeedbackTitle = 'VisualEditor/Feedback'; $wgVisualEditorSourceFeedbackTitle = '2017 wikitext editor/Feedback'; } // Citoid wfLoadExtension( 'Citoid' ); $wgCitoidFullRestbaseURL = "/api/rest_"; } if ( $wmgUseTemplateData ) { // T61702 - 2015-07-20 // TemplateData enabled for all wikis - 2014-09-29 wfLoadExtension( 'TemplateData' ); // TemplateData GUI enabled for all wikis - 2014-11-06 $wgTemplateDataUseGUI = true; // TemplateWizard enabled for all TemplateData wikis – T202545 wfLoadExtension( 'TemplateWizard' ); } if ( $wmgUseGoogleNewsSitemap ) { wfLoadExtension( 'GoogleNewsSitemap' ); $wgGNSMfallbackCategory = $wmgGNSMfallbackCategory; $wgGNSMcommentNamespace = $wmgGNSMcommentNamespace; } if ( $wmgUseCLDR ) { wfLoadExtension( 'cldr' ); } if ( $wmgUseIncubator ) { wfLoadExtension( 'WikimediaIncubator' ); $wmincClosedWikis = $wgSiteMatrixClosedSites; } if ( $wmgUseWikiLove ) { wfLoadExtension( 'WikiLove' ); $wgWikiLoveLogging = true; $wgDefaultUserOptions['wikilove-enabled'] = 1; } if ( $wmgUseGuidedTour ) { wfLoadExtension( 'GuidedTour' ); } if ( $wmgUseMobileApp ) { wfLoadExtension( 'MobileApp' ); } wfLoadSkin( 'MinervaNeue' ); $wgMinervaNightModeOptions['exclude']['querystring'] = $wmgMinervaNightModeQueryString; $wgMinervaNightModeOptions['exclude']['namespaces'] = $wmgMinervaNightModeExcludeNamespaces; $wgMinervaNightModeOptions['exclude']['pagetitles'] = $wmgMinervaNightModeExcludeTitles; $wgVectorNightModeOptions = $wgMinervaNightModeOptions; # Mobile-related configuration if ( $wmgUseMobileFrontend ) { wfLoadExtension( 'MobileFrontend' ); require_once 'MobileUrlCallback.php'; $wgMobileUrlCallback = 'wmfMobileUrlCallback'; $wgMFMobileHeader = 'X-Subdomain'; } else { // For sites without MobileFrontend, instead enable Vector's "responsive" state. $wgVectorResponsive = true; } // Enable this everywhere except where GeoData isn't available $wgMFNearby = $wmgEnableGeoData; $wgMFNearbyRange = $wgMaxGeoSearchRadius; // Turn on volunteer recruitment $wgMFEnableJSConsoleRecruitment = true; // Brute-force bandwidth optimization by stripping srcset (T119797) $wgMFStripResponsiveImages = true; $wgMFUseWikibase = true; # MUST be after MobileFrontend initialization if ( $wmgEnableTextExtracts ) { wfLoadExtension( 'TextExtracts' ); } if ( $wmgUseSubPageList3 ) { wfLoadExtension( 'SubPageList3' ); } // Serve 'Powered by MediaWiki' badge from /static/images instead of // $wgResourceBasePath so we can set far-future expires. $wgFooterIcons['poweredby']['mediawiki']['src'] = $wmgPoweredByMediaWikiIcon['1x']; $wgFooterIcons['poweredby']['mediawiki']['srcset'] = $wmgPoweredByMediaWikiIcon['1.5x'] . ' 1.5x, ' . $wmgPoweredByMediaWikiIcon['2x'] . ' 2x'; if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' ) { $wgCookieSecure = true; $_SERVER['HTTPS'] = 'on'; // Fake this so MW goes into HTTPS mode } $wgVaryOnXFP = true; $wgCookieExpiration = 30 * 86400; $wgExtendedLoginCookieExpiration = 365 * 86400; if ( $wmgUseMath ) { wfLoadExtension( 'Math' ); if ( $wmgUseMathML && $wmgUseRestbaseVRS ) { $wgDefaultUserOptions['math'] = 'mathml'; } // This variable points to non-WMF servers by default. // Prevent accidental use. // Create LateXML database table before enabling LaTeXML T309686 $wgMathLaTeXMLUrl = null; $wgMathMathMLUrl = $wmgLocalServices['mathoid']; // Increase the number of concurrent connections made to RESTBase $wgMathConcurrentReqs = 150; // Set up $wgMathFullRestbaseURL - similar to VE RESTBase config above // HACK: $wgServerName is not available yet at this point, it's set by Setup.php // so use a hook $wgExtensionFunctions[] = static function () { global $wgServerName, $wgMathFullRestbaseURL, $wmgRealm; $wgMathFullRestbaseURL = $wmgRealm === 'production' ? 'https://wikimedia.org/api/rest_' // T136205 : "//$wgServerName/api/rest_"; }; } if ( $wmgUseBabel ) { wfLoadExtension( 'Babel' ); $wgBabelCategoryNames = $wmgBabelCategoryNames; $wgBabelMainCategory = $wmgBabelMainCategory; $wgBabelDefaultLevel = $wmgBabelDefaultLevel; $wgBabelUseUserLanguage = $wmgBabelUseUserLanguage; $wgBabelUseDatabase = true; if ( $wmgUseCentralAuth ) { $wgBabelCentralDb = 'metawiki'; } } if ( $wmgUseBounceHandler ) { wfLoadExtension( 'BounceHandler' ); // $wmgVERPsecret is set in PrivateSettings.php $wgVERPsecret = $wmgVERPsecret; $wgVERPdomainPart = 'wikimedia.org'; $wgBounceHandlerUnconfirmUsers = true; $wgBounceRecordLimit = 5; $wgBounceHandlerCluster = 'extension1'; $wgBounceHandlerSharedDB = 'wikishared'; $wgBounceHandlerInternalIPs = [ '208.80.154.76', # mx1001 '2620:0:861:3:208:80:154:76', # mx1001 '208.80.153.45', # mx2001 '2620:0:860:2:208:80:153:45', # mx2001 ]; } if ( $wmgUseTranslate ) { wfLoadExtension( 'Translate' ); $wgGroupPermissions['*']['translate'] = true; $wgGroupPermissions['translationadmin']['pagetranslation'] = true; $wgGroupPermissions['translationadmin']['translate-manage'] = true; $wgGroupPermissions['translationadmin']['translate-import'] = true; // T42341 $wgGroupPermissions['translationadmin']['pagelang'] = true; // T153209 $wgGroupPermissions['user']['translate-messagereview'] = true; $wgGroupPermissions['user']['translate-groupreview'] = true; $wgGroupPermissions['sysop']['pagelang'] = true; // T153209 $wgAddGroups['bureaucrat'][] = 'translationadmin'; // T178793 $wgRemoveGroups['bureaucrat'][] = 'translationadmin'; // T178793 $wgGroupsAddToSelf['sysop'][] = 'translationadmin'; // T178793 $wgGroupsRemoveFromSelf['sysop'][] = 'translationadmin'; // T178793 $wgTranslateDocumentationLanguageCode = 'qqq'; $wgPageLanguageUseDB = true; // T153209 $wgTranslateTranslationServices = []; if ( $wmgUseTranslationMemory ) { $wgTranslateTranslationDefaultService = 'default'; // If the downtime is long (> 10mins) consider disabling // mirroring in this var to avoid logspam about ttm updates // then plan to refresh this index via ttmserver-export when // it's back up. // NOTE: these settings are also used for the labs cluster // where codfw may not be available $wgTranslateServices = [ // Switch to 'eqiad' or 'codfw' if you plan to bring down // the elastic cluster equals to $wmgDatacenter 'default' => [ 'service' => $wmgDatacenter, 'writable' => false ], 'eqiad' => [ 'service' => 'eqiad', 'writable' => true ], 'codfw' => [ 'service' => 'codfw', 'writable' => true ], ]; foreach ( $wgTranslateServices as $service => $conf ) { if ( !isset( $wmgAllServices[$conf['service']]['search-chi'] ) ) { continue; } // see https://www.mediawiki.org/wiki/Help:Extension:Translate/Translation_memories#Configuration $wgTranslateTranslationServices[$service] = [ 'type' => 'ttmserver', 'class' => 'ElasticSearchTTMServer', 'shards' => 1, 'replicas' => 1, 'index' => $wmgTranslateESIndex, 'cutoff' => 0.65, 'writable' => $conf['writable'], 'use_wikimedia_extra' => true, 'config' => [ 'servers' => array_map( static function ( $hostConfig ) { if ( is_array( $hostConfig ) ) { return $hostConfig; } return [ 'host' => $hostConfig, 'port' => 9243, 'transport' => 'Https', ]; }, $wmgAllServices[$conf['service']]['search-chi'] ), ], ]; } unset( $wgTranslateServices ); } else { $wgTranslateTranslationDefaultService = false; } $wgTranslateWorkflowStates = $wmgTranslateWorkflowStates; $wgTranslateRcFilterDefault = $wmgTranslateRcFilterDefault; $wgTranslateUsePreSaveTransform = true; // T39304 $wgEnablePageTranslation = true; $wgTranslateDelayedMessageIndexRebuild = true; // Deprecated language codes $wgTranslateDisabledTargetLanguages = [ '*' => [ 'gan-hans' => 'Translate in gan please.', 'gan-hant' => 'Translate in gan please.', 'ike-cans' => 'Translate in iu please.', 'ike-latn' => 'Translate in iu please.', 'kk-cyrl' => 'Translate in kk please.', 'kk-latn' => 'Translate in kk please.', 'kk-arab' => 'Translate in kk please.', 'kk-kz' => 'Translate in kk please.', 'kk-tr' => 'Translate in kk please.', 'kk-cn' => 'Translate in kk please.', 'ku-latn' => 'Translate in ku please.', 'ku-arab' => 'Translate in ku please.', 'shi-tfng' => 'Translate in shi please.', 'shi-latn' => 'Translate in shi please.', 'sr-ec' => 'Translate in sr please.', 'sr-el' => 'Translate in sr please.', 'tg-latn' => 'Translate in tg please.', 'zh-hans' => 'Translate in zh please.', 'zh-hant' => 'Translate in zh please.', 'zh-cn' => 'Translate in zh please.', 'zh-hk' => 'Translate in zh please.', 'zh-mo' => 'Translate in zh please.', 'zh-my' => 'Translate in zh please.', 'zh-sg' => 'Translate in zh please.', 'zh-tw' => 'Translate in zh please.', ], ]; if ( $wgDBname === 'commonswiki' ) { $wgTranslateMessageNamespaces[] = NS_MEDIAWIKI; $wgHooks['TranslatePostInitGroups'][] = static function ( &$cc ) { $id = 'wiki-translatable'; $mg = new WikiMessageGroup( $id, 'translatable-messages' ); $mg->setLabel( 'Interface' ); $mg->setDescription( 'Messages used in the custom interface of this wiki' ); $cc[$id] = $mg; return true; }; } $wgSpecialPages['ManageMessageGroups'] = DisabledSpecialPage::getCallback( 'ManageMessageGroups' ); $wgTranslateStatsProviders['registrations'] = null; $wgTranslateTranslationServices['Apertium'] = [ 'type' => 'cxserver', 'host' => $wmgLocalServices['cxserver'], 'timeout' => 3, ]; if ( $wmgTranslateUseMinT ) { $wgTranslateTranslationServices['MinT'] = [ 'type' => 'mint', 'host' => $wmgLocalServices['cxserver'], 'timeout' => 3, ]; } } if ( $wmgUseTranslationNotifications ) { wfLoadExtension( 'TranslationNotifications' ); $wgTranslationNotificationsContactMethods['talkpage-elsewhere'] = true; } if ( $wmgUseFundraisingTranslateWorkflow ) { wfLoadExtension( 'FundraisingTranslateWorkflow' ); } if ( $wmgUseVips ) { wfLoadExtension( 'VipsScaler' ); $wgVipsOptions = [ [ 'conditions' => [ 'mimeType' => 'image/png', 'minArea' => 2e7, ], ], [ 'conditions' => [ 'mimeType' => 'image/tiff', 'minShrinkFactor' => 1.2, 'minArea' => 5e7, ], 'sharpen' => [ 'sigma' => 0.8 ], ], ]; } if ( $wmgUseShortUrl ) { wfLoadExtension( 'ShortUrl' ); $wgShortUrlTemplate = "/s/$1"; } if ( $wmgUseFeaturedFeeds ) { wfLoadExtension( 'FeaturedFeeds' ); require_once __DIR__ . '/FeaturedFeedsWMF.php'; } $wgDisplayFeedsInSidebar = false; if ( $wmgEnablePageTriage ) { wfLoadExtension( 'PageTriage' ); $wgAddGroups['bureaucrat'][] = 'copyviobot'; // T206731 $wgRemoveGroups['bureaucrat'][] = 'copyviobot'; // T206731 } if ( $wmgEnableInterwiki ) { wfLoadExtension( 'Interwiki' ); $wgInterwikiViewOnly = true; } # Avoid excessive CPU due to cache misses from rapid invalidations $wgJobBackoffThrottling['htmlCacheUpdate'] = 50; // pages/sec per runner # Job types to exclude from the default queue processing. Aka the very long # one. That will exclude the types from any queries such as nextJobDB.php # We have to set this for any project cause we usually run PHP script against # the 'aawiki' database, but might as well run it against another name. # Timed Media Handler: $wgJobTypesExcludedFromDefaultQueue[] = 'webVideoTranscode'; $wgJobTypesExcludedFromDefaultQueue[] = 'webVideoTranscodePrioritized'; if ( $wmgUseEducationProgram ) { $wgExtraNamespaces[446] = 'Education_Program'; $wgExtraNamespaces[447] = 'Education_Program_talk'; $wgNamespacesWithSubpages[447] = true; } if ( $wmgEnableGeoData ) { wfLoadExtension( 'GeoData' ); $wgGeoDataBackend = 'elastic'; $wgMaxCoordinatesPerPage = 2000; $wgGeoDataDebug = true; } if ( $wmgUseEcho ) { // LoginNotify code loaded before Notifications wfLoadExtension( 'LoginNotify' ); $wgNotifyTypeAvailabilityByCategory['login-success']['web'] = false; $wgLoginNotifyAttemptsNewIP = 3; $wgLoginNotifyUseSeenTable = true; $wgLoginNotifyUseCheckUser = false; // Less than 90 days per data retention guidelines, minus one bucket for rounding. $wgLoginNotifySeenExpiry = 80 * 86400; $wgLoginNotifySeenBucketSize = 8 * 86400; if ( $wmgUseCentralAuth ) { $wgLoginNotifyUseCentralId = true; $wgVirtualDomainsMapping['virtual-LoginNotify'] = [ 'cluster' => 'extension1', 'db' => 'wikishared' ]; } // This is intentionally loaded *before* the GlobalPreferences extension (below). wfLoadExtension( 'Echo' ); // Common settings, never varied $wgEchoPerUserBlacklist = true; $wgEchoMaxMentionsInEditSummary = 5; $wgEchoEnableEmailBatch = $wmgEchoEnableEmailBatch; $wgEchoEmailFooterAddress = $wmgEchoEmailFooterAddress; $wgEchoNotificationIcons['site']['url'] = $wmgEchoSiteNotificationIconUrl; // Define the cluster database, false to use main database $wgEchoCluster = $wmgEchoCluster; // CentralAuth is extra check to be absolutely sure we don't enable on non-SUL // wikis. if ( $wmgUseCentralAuth && $wmgEchoCrossWikiByDefault ) { $wgEchoCrossWikiNotifications = true; if ( $wmgEchoCrossWikiByDefault ) { $wgDefaultUserOptions['echo-cross-wiki-notifications'] = 1; } } // Whether to make mention failure/success notifications available $wgEchoMentionStatusNotifications = true; // Enable tracking table only on SULed wikis if ( $wmgUseCentralAuth ) { $wgEchoSharedTrackingDB = 'wikishared'; // Explicitly set this to 'extension1', because some wikis have $wgEchoCluster set to false $wgEchoSharedTrackingCluster = 'extension1'; } // Default user options: subscriptions foreach ( $wmgEchoDefaultUserSubscriptions as $where => $notifications ) { foreach ( $notifications as $notification => $value ) { $option = 'echo-subscriptions-' . $where . '-' . $notification; $wgDefaultUserOptions[$option] = $value; } } // Conditional defaults (T353225) // NOTE: testwiki has different conditional defaults start if ( in_array( $wgDBname, [ 'testwiki', 'loginwiki' ] ) ) { $startTimestamp = '20130501000000'; } else { $startTimestamp = '20240208200000'; } $wgConditionalUserOptions['echo-subscriptions-web-reverted'] = [ [ false, [ CUDCOND_AFTER, $startTimestamp ] ] ]; $wgConditionalUserOptions['echo-subscriptions-web-article-linked'] = $wgConditionalUserOptions['echo-subscriptions-email-mention'] = $wgConditionalUserOptions['echo-subscriptions-email-article-linked'] = [ [ true, [ CUDCOND_AFTER, $startTimestamp ] ] ]; unset( $startTimestamp ); // Push notifications $wgEchoEnablePush = $wmgEchoEnablePush ?? false; $wgEchoPushServiceBaseUrl = "{$wmgLocalServices['push-notifications']}/v1/message"; $wgEchoPushMaxSubscriptionsPerUser = 10; // Set up the push notifier type if push is enabled. // If/when this is promoted to all wikis, this config can be moved directly into extension.json // along with the original notifier types ('web' and 'email'). if ( $wgEchoEnablePush ) { $wgEchoNotifiers['push'] = [ PushNotifier::class, 'notifyWithPush' ]; $wgDefaultNotifyTypeAvailability['push'] = true; $wgNotifyTypeAvailabilityByCategory['system']['push'] = false; $wgNotifyTypeAvailabilityByCategory['system-noemail']['push'] = false; } // Limit the 'push-subscription-manager' group to Meta-Wiki only (T261625) $wgExtensionFunctions[] = static function () { global $wgGroupPermissions, $wgDBname; if ( $wgDBname !== 'metawiki' ) { unset( $wgGroupPermissions['push-subscription-manager'] ); } }; } // Wikitech specific settings if ( $wgDBname === 'labswiki' || $wgDBname === 'labtestwiki' ) { $wgEmailConfirmToEdit = true; $wgEnableCreativeCommonsRdf = true; // Don't depend on other DB servers $wgDefaultExternalStore = false; $wgGroupPermissions['contentadmin'] = $wgGroupPermissions['sysop']; $wgGroupPermissions['contentadmin']['editinterface'] = false; $wgGroupPermissions['contentadmin']['tboverride'] = false; $wgGroupPermissions['contentadmin']['titleblacklistlog'] = false; $wgGroupPermissions['contentadmin']['override-antispoof'] = false; $wgGroupPermissions['contentadmin']['createaccount'] = false; # We don't want random strangers playing on labtestwiki, aka codfw1dev # For prod wikitech ('labswiki') user registration is now managed by # Bitu, https://idm.wikimedia.org. $wgGroupPermissions['*']['createaccount'] = false; if ( $wgDBname === 'labswiki' ) { // Allow autocreating accounts from IDM. $wgGroupPermissions['*']['autocreateaccount'] = true; // Also block account creations by sysops just in case. $wgGroupPermissions['sysop']['createaccount'] = false; // Password resets are handled by IDM too. $wgPasswordResetRoutes = []; } // These are somehow not added as they are assigned to 'sysop' in the respective extension.json $wgGroupPermissions['contentadmin']['nuke'] = true; $wgGroupPermissions['contentadmin']['massmessage'] = true; $wgGroupPermissions['contentadmin']['spamblacklistlog'] = true; // Grant AbuseFilter permissions regular sysops have - T242593 $wgGroupPermissions['contentadmin']['abusefilter-log-detail'] = true; $wgGroupPermissions['contentadmin']['abusefilter-log-private'] = true; $wgGroupPermissions['contentadmin']['abusefilter-modify'] = true; $wgGroupPermissions['contentadmin']['abusefilter-modify-restricted'] = true; $wgGroupPermissions['contentadmin']['abusefilter-view-private'] = true; if ( $wgDBname === 'labswiki' ) { $wgCookieDomain = "wikitech.wikimedia.org"; // TODO: Is this really necessary? } elseif ( $wgDBname === 'labtestwiki' ) { $wgCookieDomain = "labtestwikitech.wikimedia.org"; // TODO: Is this really necessary? } // Some settings specific to wikitech's extensions include __DIR__ . '/wikitech.php'; if ( $wgDBname === 'labtestwiki' ) { // wgReadOnly is set by etcdConfig using datacenter-global configs. // since labtestwikitech uses its own database, $wgReadOnly shouldn't // be determined from etcd. $wgReadOnly = null; } } if ( $wmgUseThanks ) { wfLoadExtension( 'Thanks' ); } if ( $wmgUseEntitySchema ) { wfLoadExtension( 'EntitySchema' ); } // Flow configuration // We always set this variable, as it's required by the create table // maintenance script. This allows to deploy Flow on non standard wikis. $wgFlowDefaultWikiDb = $wmgFlowDefaultWikiDb; if ( $wmgUseFlow && $wmgUseParsoid ) { wfLoadExtension( 'Flow' ); // Flow Parsoid - These are now specified directly as Flow-specific // configuration variables, though it currently uses the same Parsoid URL // as VisualEditor does. $wgFlowParsoidURL = $wmgParsoidURL; $wgFlowParsoidPrefix = $wgDBname; $wgFlowParsoidTimeout = 50; if ( $wmgParsoidForwardCookies ) { $wgFlowParsoidForwardCookies = true; } if ( $wmgUseVisualEditor ) { $wgDefaultUserOptions['flow-editor'] = 'visualeditor'; } foreach ( $wmgFlowNamespaces as $namespace ) { $wgNamespaceContentModels[$namespace] = 'flow-board'; // CONTENT_MODEL_FLOW_BOARD } // Requires that Parsoid is available for all wikis using Flow. $wgFlowContentFormat = 'html'; if ( $wmgFlowEnglishNamespaceOnly ) { // HACK: Only use English namespace names, override the localized namespace names // This is needed for languages where the translation for Thread: (from LQT) // and Topic: (from Flow) are the same, like in Portuguese. In those cases we // make the Flow Topic: namespace only use the English name, so the translated name // will still point to the LQT namespace. $wgExtraNamespaces[2600] = 'Topic'; // NS_TOPIC } $wgFlowDefaultWikiDb = $wmgFlowDefaultWikiDb; $wgFlowCluster = $wmgFlowCluster; $wgFlowExternalStore = $wgDefaultExternalStore; $wgFlowMaintenanceMode = $wmgFlowMaintenanceMode; if ( $wmgFlowAllowAutoconfirmedEdit ) { $wgGroupPermissions['autoconfirmed']['flow-edit-post'] = true; } $wgFlowEnableOptInBetaFeature = $wmgFlowEnableOptInBetaFeature; // On wikis that have Flow as a beta feature or in an entire namespace, // give sysops the right to create and move Flow boards if ( $wgFlowEnableOptInBetaFeature || $wmgFlowNamespaces ) { $wgGroupPermissions['sysop']['flow-create-board'] = true; } } if ( $wmgUseDisambiguator ) { wfLoadExtension( 'Disambiguator' ); } if ( $wmgUseDiscussionTools ) { wfLoadExtension( 'DiscussionTools' ); // Auto topic subscriptions are initially disabled while in beta (T290500) $wgDefaultUserOptions['discussiontools-autotopicsub'] = 0; } if ( $wmgUseCodeEditorForCore || $wmgUseScribunto ) { wfLoadExtension( 'CodeEditor' ); $wgCodeEditorEnableCore = $wmgUseCodeEditorForCore; } if ( $wmgUseScribunto ) { wfLoadExtension( 'Scribunto' ); $wgScribuntoUseGeSHi = true; $wgScribuntoUseCodeEditor = true; $wgScribuntoGatherFunctionStats = true; // ori, 29-Oct-2015 $wgScribuntoSlowFunctionThreshold = 0.99; $wgScribuntoDefaultEngine = 'luasandbox'; $wgScribuntoEngineConf['luasandbox']['cpuLimit'] = 10; $wgScribuntoEngineConf['luasandbox']['maxLangCacheSize'] = 200; // see T85461#3100878 $wgScribuntoEngineConf['luasandbox']['memoryLimit'] = $wmgScribuntoMemoryLimit; } if ( $wmgUseSubpageSortkey ) { wfLoadExtension( 'SubpageSortkey' ); $wgSubpageSortkeyByNamespace = $wmgSubpageSortkeyByNamespace; } if ( $wmgUseGeoCrumbs ) { wfLoadExtension( 'GeoCrumbs' ); } if ( $wmgUseCalendar ) { wfLoadExtension( 'Calendar' ); } if ( $wmgUseMapSources ) { wfLoadExtension( 'MapSources' ); } if ( $wmgUseCreditsSource ) { wfLoadExtension( 'CreditsSource' ); } if ( $wmgUseTocTree ) { wfLoadExtension( 'TocTree' ); $wgDefaultUserOptions['toc-floated'] = $wmgUseFloatedToc; } if ( $wmgUseInsider ) { wfLoadExtension( 'Insider' ); } if ( $wmgUseRelatedArticles ) { wfLoadExtension( 'RelatedArticles' ); $wgRelatedArticlesLoggingBucketSize = 0; $wgRelatedArticlesOnlyUseCirrusSearch = false; $wgRelatedArticlesDescriptionSource = 'wikidata'; } if ( $wmgUseRevisionSlider ) { wfLoadExtension( 'RevisionSlider' ); } if ( $wmgUseTwoColConflict ) { wfLoadExtension( 'TwoColConflict' ); $wgTwoColConflictBetaFeature = $wmgTwoColConflictBetaFeature; // Enable oversampled event tracking during limited study period (T249616) $wgTwoColConflictTrackingOversample = true; } if ( $wmgUseEventLogging ) { wfLoadExtension( 'EventLogging' ); wfLoadExtension( 'EventStreamConfig' ); // All wikis reference metawiki. $wgEventLoggingBaseUri = $wgCanonicalServer . '/beacon/event'; $wgEventLoggingDBname = 'metawiki'; $wgEventLoggingSchemaApiUri = 'https://meta.wikimedia.org/w/api.php'; // For compat, also register the Schema NS on test2.wikipedia.org, // because this wiki used to be its own EventLogging repo (T196309) if ( $wgDBname === $wgEventLoggingDBname || $wgDBname === 'test2wiki' ) { // T47031 $wgExtraNamespaces[470] = 'Schema'; $wgExtraNamespaces[471] = 'Schema_talk'; } if ( $wgDBname === $wgEventLoggingDBname ) { wfLoadExtension( 'CodeEditor' ); $wgCodeEditorEnableCore = $wmgUseCodeEditorForCore; // For safety's sake } // Depends on EventLogging if ( $wmgUseCampaigns ) { wfLoadExtension( 'Campaigns' ); } // Depends on EventLogging if ( $wmgUseWikimediaEvents ) { wfLoadExtension( 'WikimediaEvents' ); $wgWMEStatsdBaseUri = '/beacon/statsv'; if ( $wgDBname === 'testwiki' ) { $wgGroupPermissions['data-qa']['perform-data-qa'] = true; // T276515 } } // Depends on EventLogging if ( $wmgUseNavigationTiming ) { wfLoadExtension( 'NavigationTiming' ); } } wfLoadExtension( 'XAnalytics' ); if ( $wmgUseUniversalLanguageSelector ) { wfLoadExtension( 'UniversalLanguageSelector' ); $wgULSGeoService = false; $wgULSAnonCanChangeLanguage = false; $wgULSPosition = $wmgULSPosition; $wgULSIMEEnabled = $wmgULSIMEEnabled; $wgULSWebfontsEnabled = $wmgULSWebfontsEnabled; if ( $wmgUseCodeEditorForCore || $wmgUseScribunto ) { $wgULSNoImeSelectors[] = '.ace_editor textarea'; } if ( $wmgUseTranslate && $wmgULSPosition === 'personal' ) { $wgTranslatePageTranslationULS = true; } // Compact Language Links … // … as a beta feature (see T136677 for beta to stable) $wgULSCompactLanguageLinksBetaFeature = $wmgULSCompactLanguageLinksBetaFeature; // … as a stable feature $wgDefaultUserOptions['compact-language-links'] = 1; } if ( $wmgUseFileExporter ) { wfLoadExtension( 'FileExporter' ); if ( $wmgUseFileExporter !== true ) { $wgFileExporterTarget = $wmgUseFileExporter; } } if ( $wmgUseFileImporter ) { wfLoadExtension( 'FileImporter' ); $wgFileImporterCommonsHelperServer = 'https://www.mediawiki.org'; $wgFileImporterCommonsHelperBasePageName = 'Extension:FileImporter/Data/'; $wgFileImporterCommonsHelperHelpPage = 'https://www.mediawiki.org/wiki/Extension:FileImporter/List_of_configured_wikis'; $wgFileImporterSourceSiteServices = [ $wmgUseFileImporter ]; // Temporary solution until we have a stable implementation for this // https://gerrit.wikimedia.org/r/440857 $wgFileImporterInterWikiMap = [ 'test.wikipedia.org' => 'testwiki', 'test2.wikipedia.org' => 'test2wiki', 'mediawiki.org' => 'mw', 'ar.wikipedia.org' => 'w:ar', // T196969 'de.wikipedia.org' => 'w:de', // T196969 'fa.wikipedia.org' => 'w:fa', // T196969 ]; $wgFileImporterWikidataEntityEndpoint = 'https://www.wikidata.org/wiki/Special:EntityData/'; $wgFileImporterWikidataNowCommonsEntity = 'Q5611625'; $wgFileImporterSourceWikiDeletion = true; $wgFileImporterSourceWikiTemplating = true; // Temporarily enable for testing, see T228851 if ( $wgDBname === 'testwiki' ) { $wgFileImporterWikidataEntityEndpoint = 'https://test.wikidata.org/wiki/Special:EntityData/'; $wgFileImporterWikidataNowCommonsEntity = 'Q210317'; } } if ( $wmgUseContentTranslation ) { wfLoadExtension( 'ContentTranslation' ); // T76200: Public URL for cxserver instance $wgContentTranslationSiteTemplates['cx'] = '//cxserver.wikimedia.org/v1'; $wgContentTranslationSiteTemplates['cookieDomain'] = '.wikipedia.org'; $wgContentTranslationTranslateInTarget = $wmgContentTranslationTranslateInTarget; $wgContentTranslationUnmodifiedMTThresholdForPublish = $wmgContentTranslationUnmodifiedMTThresholdForPublish; if ( $wmgContentTranslationCluster ) { $wgContentTranslationCluster = $wmgContentTranslationCluster; } $wgContentTranslationCampaigns = $wmgContentTranslationCampaigns; $wgContentTranslationCXServerAuth = [ 'algorithm' => 'HS256', // This is set in PrivateSettings.php 'key' => $wmgContentTranslationCXServerAuthKey, 'age' => '3600', ]; } if ( $wmgUseExternalGuidance ) { wfLoadExtension( 'ExternalGuidance' ); $wgExternalGuidanceMTReferrers = [ 'translate.google.com', 'translate.googleusercontent.com' ]; $wgExternalGuidanceKnownServices = [ 'Google', 'translate.google.com', 'translate.googleusercontent.com' ]; } if ( $wmgUseCognate ) { wfLoadExtension( 'Cognate' ); $wgVirtualDomainsMapping['virtual-cognate'] = [ 'cluster' => 'extension1', 'db' => 'cognate_wiktionary' ]; $wgCognateNamespaces = [ 0 ]; // Temp b/c for T348526 $wgCognateDb = 'cognate_' . $wmgUseCognate; $wgCognateCluster = 'extension1'; } if ( $wmgUseInterwikiSorting ) { $wgInterwikiSortingInterwikiSortOrders = include __DIR__ . '/InterwikiSortOrders.php'; wfLoadExtension( 'InterwikiSorting' ); } if ( $wmgUseWikibaseRepo || $wmgUseWikibaseClient || $wmgUseWikibaseMediaInfo ) { include __DIR__ . '/Wikibase.php'; } // Turn off exact search match redirects if ( $wmgDoNotRedirectOnSearchMatch ) { $wgSearchMatchRedirectPreference = true; $wgDefaultUserOptions['search-match-redirect'] = false; } // put this here to ensure it is available for localisation cache rebuild $wgWBClientSettings['repoSiteName'] = 'wikibase-repo-name'; if ( $wmgUseTemplateSandbox ) { wfLoadExtension( 'TemplateSandbox' ); } if ( $wmgUsePageAssessments ) { wfLoadExtension( 'PageAssessments' ); } if ( $wmgUsePageImages ) { wfLoadExtension( 'PageImages' ); } if ( $wmgUseSearchExtraNS ) { wfLoadExtension( 'SearchExtraNS' ); } if ( $wmgUseJsonConfig ) { wfLoadExtension( 'JsonConfig' ); if ( $wmgUseGraphWithJsonNamespace ) { $wgJsonConfigModels['Json.JsonConfig'] = null; $wgJsonConfigs['Json.JsonConfig'] = [ 'namespace' => 486, 'nsName' => 'Data', 'isLocal' => true, 'pattern' => '/^Json:./', ]; } } if ( $wmgEnableJsonConfigDataMode ) { // Safety: before extension.json, these values were initialized by JsonConfig.php $wgJsonConfigModels = $wgJsonConfigModels ?? []; $wgJsonConfigs = $wgJsonConfigs ?? []; $wgJsonConfigEnableLuaSupport = true; // https://www.mediawiki.org/wiki/Extension:JsonConfig#Configuration $wgJsonConfigModels['Tabular.JsonConfig'] = 'JsonConfig\JCTabularContent'; $wgJsonConfigs['Tabular.JsonConfig'] = [ 'namespace' => 486, 'nsName' => 'Data', // page name must end in ".tab", and contain at least one symbol 'pattern' => '/.\.tab$/', 'license' => 'CC0-1.0', 'isLocal' => false, ]; $wgJsonConfigModels['Map.JsonConfig'] = 'JsonConfig\JCMapDataContent'; $wgJsonConfigs['Map.JsonConfig'] = [ 'namespace' => 486, 'nsName' => 'Data', // page name must end in ".map", and contain at least one symbol 'pattern' => '/.\.map$/', 'license' => 'CC0-1.0', 'isLocal' => false, ]; // Enable Tabular data namespace on Commons - T148745 // Enable Map (GeoJSON) data namespace on Commons - T149548 // TODO: Consider whether this hard-coding to Commons is appropriate if ( $wgDBname === 'commonswiki' ) { // Ensure we have a stable cross-wiki title resolution // See JCSingleton::parseTitle() $wgJsonConfigInterwikiPrefix = "meta"; $wgJsonConfigs['Tabular.JsonConfig']['store'] = true; $wgJsonConfigs['Map.JsonConfig']['store'] = true; } else { $wgJsonConfigInterwikiPrefix = "commons"; $wgJsonConfigs['Tabular.JsonConfig']['remote'] = [ 'url' => 'https://commons.wikimedia.org/w/api.php' ]; $wgJsonConfigs['Map.JsonConfig']['remote'] = [ 'url' => 'https://commons.wikimedia.org/w/api.php' ]; } } // Enable Config:Dashiki: sub-namespace on meta.wikimedia.org - T156971 if ( $wmgEnableDashikiData && $wmgUseJsonConfig ) { // Dashiki sub-namespace Config:Dashiki: is configured in extension.json wfLoadExtension( 'Dashiki' ); } if ( $wmgUseGraph ) { wfLoadExtension( 'Graph' ); // **** THIS LIST MUST MATCH puppet/hieradata/role/common/scb.yaml **** // See https://www.mediawiki.org/wiki/Extension:Graph#External_data // $wgGraphAllowedDomains = [ 'https' => [ 'mediawiki.org', 'wikibooks.org', 'wikidata.org', 'wikimedia.org', 'wikinews.org', 'wikipedia.org', 'wikiquote.org', 'wikisource.org', 'wikiversity.org', 'wikivoyage.org', 'wiktionary.org', ], 'wikirawupload' => [ 'upload.wikimedia.org', ], 'wikidatasparql' => [ 'query.wikidata.org', ], 'geoshape' => [ 'maps.wikimedia.org', ], ]; } elseif ( $wmgHideGraphTags ) { // Hide raw tags that are displayed due to T334895 // Note this still uses messages from E:Graph, which are available // as long as it is in wmf-config/extension-list. $wgHooks['ParserFirstCallInit'][] = 'wmfAddGraphTagToHideRawUsage'; $wgTrackingCategories[] = 'graph-tracking-category'; $wgTrackingCategories[] = 'graph-disabled-category'; function wmfAddGraphTagToHideRawUsage( Parser $parser ) { $parser->setHook( 'graph', 'wmfRenderEmptyGraphTag' ); } function wmfRenderEmptyGraphTag( $input, array $args, Parser $parser, PPFrame $frame ) { $parser->addTrackingCategory( 'graph-tracking-category' ); $parser->addTrackingCategory( 'graph-disabled-category' ); $msg = $parser->msg( 'graph-disabled' ); if ( $msg->isDisabled() ) { return ''; } else { return $msg->parseAsBlock(); } } } if ( $wmgUseOAuth ) { wfLoadExtension( 'OAuth' ); if ( in_array( $wgDBname, [ 'labswiki', 'labtestwiki' ] ) ) { // Wikitech and its testing variant use local OAuth tables $wgMWOAuthCentralWiki = false; } else { $wgMWOAuthCentralWiki = 'metawiki'; $wgMWOAuthSharedUserSource = 'CentralAuth'; } $wgMWOAuthSecureTokenTransfer = true; $wgOAuth2GrantExpirationInterval = 'PT4H'; $wgOAuth2RefreshTokenTTL = 'P365D'; if ( $wgMWOAuthCentralWiki === $wgDBname || $wgMWOAuthCentralWiki === false ) { // Management interfaces are available on the central wiki or wikis // that are using local OAuth tables $wgGroupPermissions['user']['mwoauthproposeconsumer'] = true; $wgGroupPermissions['user']['mwoauthupdateownconsumer'] = true; $wgGroupPermissions['oauthadmin']['mwoauthmanageconsumer'] = true; $wgOAuthGroupsToNotify = [ 'oauthadmin' ]; } } if ( $wmgUseOAuthRateLimiter ) { wfLoadExtension( 'OAuthRateLimiter' ); $wgOAuthRateLimiterDefaultClientTier = 'default'; // As defined in T246271 $wgOAuthRateLimiterTierConfig = [ // demo added for demoing/testing purposes 'demo' => [ 'ratelimit' => [ 'requests_per_unit' => 10, 'unit' => 'HOUR' ], ], 'default' => [ 'ratelimit' => [ 'requests_per_unit' => 5000, 'unit' => 'HOUR' ], ], 'preferred' => [ 'ratelimit' => [ 'requests_per_unit' => 25000, 'unit' => 'HOUR' ], ], 'internal' => [ 'ratelimit' => [ 'requests_per_unit' => 100000, 'unit' => 'HOUR' ], ], 'wme' => [ 'ratelimit' => [ 'requests_per_unit' => 250000, 'unit' => 'HOUR' ], ], // More info: T345394 'wikieducation' => [ 'ratelimit' => [ 'requests_per_unit' => 150000, 'unit' => 'HOUR' ], ], ]; } // T15712 if ( $wmgUseJosa ) { wfLoadExtension( 'Josa' ); } if ( $wmgUseOATHAuth ) { wfLoadExtension( 'OATHAuth' ); if ( $wmgOATHAuthDisableRight ) { $wgGroupPermissions['user']['oathauth-enable'] = false; foreach ( $wmgPrivilegedGroups as $group ) { if ( isset( $wgGroupPermissions[$group] ) ) { $wgGroupPermissions[$group]['oathauth-enable'] = true; } } } $wgGroupPermissions['sysop']['oathauth-disable-for-user'] = false; $wgGroupPermissions['sysop']['oathauth-view-log'] = false; $wgGroupPermissions['sysop']['oathauth-verify-user'] = false; // T209749 if ( $wmgUseCentralAuth ) { $wgOATHAuthAccountPrefix = 'Wikimedia'; $wgVirtualDomainsMapping['virtual-oathauth'] = [ 'db' => 'centralauth' ]; // TODO: remove once relevant patches have been merged and shipped $wgOATHAuthDatabase = 'centralauth'; } if ( $wmgUseWebAuthn ) { wfLoadExtension( 'WebAuthn' ); } } if ( $wmgUseMediaModeration ) { if ( $wmgRealm === 'production' ) { $wgMediaModerationHttpProxy = $wmgLocalServices['urldownloader']; } $wgVirtualDomainsMapping['virtual-mediamoderation'] = [ 'cluster' => 'extension1', 'db' => false ]; wfLoadExtension( 'MediaModeration' ); // This relies on configuration in PrivateSettings.php: // - $wgMediaModerationPhotoDNASubscriptionKey // - $wgMediaModerationRecipientList $wgMediaModerationFrom = 'no-reply@wikimedia.org'; } if ( $wmgUseORES ) { wfLoadExtension( 'ORES' ); $wgOresBaseUrl = 'http://localhost:6010/'; $wgOresFrontendBaseUrl = 'https://ores.wikimedia.org/'; $wgOresLiftWingBaseUrl = 'http://localhost:6031/'; $wgDefaultUserOptions['oresDamagingPref'] = $wgDefaultUserOptions['rcOresDamagingPref'] = $wmgOresDefaultSensitivityLevel; // Backwards compatibility for upcoming config format change if ( isset( $wgOresFiltersThresholds['goodfaith']['good'] ) ) { $wgOresFiltersThresholds['goodfaith']['likelygood'] = $wgOresFiltersThresholds['goodfaith']['good']; } if ( isset( $wgOresFiltersThresholds['goodfaith']['bad'] ) ) { $wgOresFiltersThresholds['goodfaith']['likelybad'] = $wgOresFiltersThresholds['goodfaith']['bad']; } } if ( $wmgUseNewsletter ) { wfLoadExtension( 'Newsletter' ); } if ( $wmgUseRealMe ) { wfLoadExtension( 'RealMe' ); } ### End (roughly) of general extensions ######################## $wgApplyIpBlocksToXff = true; // Soft-block private IPs and other shared resources. // Note this has no effect on edits from traffic proxied through any of these // IPs with a trusted XFF header, only edits where MediaWiki is actually going // to attribute the edit (or other logged action) to one of these IPs, e.g. // that it would show up on [[Special:Contributions/127.0.0.1]]. $wgSoftBlockRanges = array_merge( [ // Ranges that aren't supposed to be publicly routable '0.0.0.0/8', // "This host on this network" (RFC 6890) '10.0.0.0/8', // Private '100.64.0.0/10', // "Shared address space" for internal routing (RFC 6598) '127.0.0.0/8', // Loopback '169.254.0.0/16', // Link local // 172.16.0.0/12 is handled below, don't add it here. '192.0.0.0/24', // IETF Protocol Assignments (RFC 6890) '192.0.2.0/24', // Documentation '192.168.0.0/16', // Private '198.18.0.0/15', // Benchmarking (RFC 2544) '198.51.100.0/24', // Documentation '203.0.113.0/24', // Documentation '240.0.0.0/4', // Reserved (RFC 1112) '::/128', // Unspecified address '::1/128', // Loopback '::ffff:0:0/96', // IPv4 mapped (these should be coming in as IPv4, not IPv6) '100::/64', // Discard-only address block '2001:2::/48', // Benchmarking '2001:db8::/32', // Documentation '2001:10::/28', // ORCHID (RFC 4843) 'fc00::/7', // Local communications, not globally routable (RFC 4193) 'fe80::/10', // Link-local // We shouldn't be getting edits via multicast either '224.0.0.0/4', 'ff00::/8', ], // Addresses used by WMF, people should log in to edit from them directly. $wgCdnServersNoPurge ); if ( $wmgAllowLabsAnonEdits ) { // CI makes anonymous edits on some wikis, so don't block Cloud VPS // private addresses when this feature flag is true. $wgSoftBlockRanges = array_merge( $wgSoftBlockRanges, [ // 172.16.0.0/16 is allowed '172.17.0.0/16', '172.18.0.0/15', '172.20.0.0/14', '172.24.0.0/13', ] ); } else { // Cloud VPS users shouldn't be editing anonymously on most wikis, so we // can block anonymous edits from the whole private ranges. $wgSoftBlockRanges[] = '172.16.0.0/12'; // Cloud VPS VMs with floating/public addresses $wgSoftBlockRanges[] = '185.15.56.0/24'; } // On Special:Version, link to useful release notes $wgHooks['SpecialVersionVersionUrl'][] = static function ( $version, &$versionUrl ) { $matches = []; preg_match( "/^(\d+\.\d+)(?:\.0-)?wmf\.?(\d+)?$/", $version, $matches ); if ( $matches ) { $versionUrl = "//www.mediawiki.org/wiki/MediaWiki_{$matches[1]}"; if ( isset( $matches[2] ) ) { $versionUrl .= "/wmf.{$matches[2]}"; } return false; } }; if ( $wmgAllowRobotsControlInAllNamespaces ) { $wgExemptFromUserRobotsControl = []; } else { $wgExemptFromUserRobotsControl = array_merge( $wgContentNamespaces, $wmgExemptFromUserRobotsControlExtra ); } // additional "language names", adding to Names.php data $wgExtraLanguageNames = $wmgExtraLanguageNames; if ( $wmgUseCheckUser ) { wfLoadExtension( 'CheckUser' ); if ( $wmgUseCentralAuth ) { // T128605 // Only for CA wikis - will break stuff otherwise $wgCheckUserCAMultiLock = [ 'centralDB' => 'metawiki', 'groups' => [ 'steward' ] ]; } // T239288 - CheckUser logs pertaining to spam blacklist logged actions appear redacted $wgCheckUserLogAdditionalRights[] = 'spamblacklistlog'; } if ( $wmgUseIPInfo ) { wfLoadExtension( 'IPInfo' ); // n.b. if you are looking for this path on mwmaint or deployment servers, you will not find it. // It is only present on application servers. See // https://codesearch.wmcloud.org/search/?q=GeoIPInfo&files=&excludeFiles=&repos=#operations/puppet // for list of relevant sections of operations/puppet config. $wgIPInfoGeoIP2EnterprisePath = '/usr/share/GeoIPInfo/'; // Consult the Legal team before updating these, since they // must remain compatible with our contract with MaxMind $wgGroupPermissions['autoconfirmed']['ipinfo'] = true; $wgGroupPermissions['autoconfirmed']['ipinfo-view-basic'] = true; $wgGroupPermissions['sysop']['ipinfo-view-full'] = true; $wgGroupPermissions['checkuser']['ipinfo-view-full'] = true; $wgGroupPermissions['checkuser']['ipinfo-view-log'] = true; $wgIPInfoIpoidUrl = $wmgLocalServices['ipoid']; } // Temporary accounts // Ensure no users can be crated that match temporary account names (T361021). // This is used even if `$wgAutoCreateTempUser['enabled']` is false. $wgAutoCreateTempUser['reservedPattern'] = '~2$1'; if ( $wmgUseCentralAuth ) { // If CentralAuth is installed, then use the centralauth provider to ensure that a new temporary account // uses a unique serial number across all wikis. This will have no effect if // `$wgAutoCreateTempUser['enabled']` is false. $wgAutoCreateTempUser['serialProvider'] = [ 'type' => 'centralauth', 'numShards' => 8, ]; } else { // If CentralAuth is not installed, then use the local provider. // This will have no effect if `$wgAutoCreateTempUser['enabled']` is false. $wgAutoCreateTempUser['serialProvider'] = [ 'type' => 'local', 'numShards' => 8, ]; } // Add the year to the username to make it easier to identify the year the tmeporary account was created // and identify based on the username if the temporary account has expired. $wgAutoCreateTempUser['serialProvider']['useYear'] = true; // We only need to match ~2$1 because the year will start with 2 for the foreseeable future // and it prevents the need to rename users on production which start with ~ but not ~2 (T349507). // This will have no effect if `$wgAutoCreateTempUser['enabled']` is false. $wgAutoCreateTempUser['matchPattern'] = '~2$1'; // Start numbers at 1500 to avoid using any numbers defined in T337090 which are considered defamatory. // This will have no effect if `$wgAutoCreateTempUser['enabled']` is false. $wgAutoCreateTempUser['serialMapping'] = [ 'type' => 'plain-numeric', 'offset' => 1500 ]; // This is by default false, but enforcing it here in case the default is changed (T355880, T359043) $wgAutoCreateTempUser['enabled'] = false; // T39211 $wgUseCombinedLoginLink = false; if ( $wmgUseRC2UDP ) { if ( $wmgRC2UDPPrefix === false ) { $matches = null; if ( preg_match( '/^(https?:)?\/\/(.+)\.org$/', $wgServer, $matches ) && isset( $matches[2] ) ) { $wmgRC2UDPPrefix = "#{$matches[2]}\t"; } } foreach ( $wmgLocalServices['irc'] as $i => $address ) { $wgRCFeeds["irc$i"] = [ 'formatter' => 'IRCColourfulRCFeedFormatter', 'uri' => "udp://$address:$wmgRC2UDPPort/$wmgRC2UDPPrefix", 'add_interwiki_prefix' => false, 'omit_bots' => false, ]; } } $wgDefaultUserOptions['watchlistdays'] = $wmgWatchlistNumberOfDaysShow; if ( $wmgUseWikidataPageBanner ) { wfLoadExtension( 'WikidataPageBanner' ); } if ( $wmgUseQuickSurveys ) { wfLoadExtension( 'QuickSurveys' ); } if ( $wmgUseEventBus ) { wfLoadExtension( 'EventBus' ); // For analytics purposes, we forward the X-Client-IP header to eventgate. // eventgate will use this to set a default http.client_ip in event data when relevant. // https://phabricator.wikimedia.org/T288853 // // NOTE: if you change request timeout values here, you should // also change eventgate timeout and prestop_sleep settings. // - https://gerrit.wikimedia.org/r/plugins/gitiles/operations/deployment-charts/+/fd8492c76352ac48d07ebb8d950d3281b91181fa/charts/eventgate/values.yaml#59 // - https://phabricator.wikimedia.org/T349823 $wgEventServices = [ 'eventgate-analytics' => [ 'url' => "{$wmgLocalServices['eventgate-analytics']}/v1/events?hasty=true", 'timeout' => 11, 'x_client_ip_forwarding_enabled' => true, ], 'eventgate-analytics-external' => [ 'url' => "{$wmgLocalServices['eventgate-analytics-external']}/v1/events?hasty=true", 'timeout' => 11, 'x_client_ip_forwarding_enabled' => true, ], 'eventgate-main' => [ 'url' => "{$wmgLocalServices['eventgate-main']}/v1/events", 'timeout' => 62, // envoy overall req timeout + 1 ] ]; $wgRCFeeds['eventbus'] = [ 'formatter' => EventBusRCFeedFormatter::class, 'class' => EventBusRCFeedEngine::class, ]; if ( $wmgUseClusterJobqueue ) { $wgJobTypeConf['default'] = [ 'class' => JobQueueEventBus::class, 'readOnlyReason' => false ]; } $wgEventBusEnableRunJobAPI = ClusterConfig::getInstance()->isAsync(); } if ( $wmgUseCapiunto ) { wfLoadExtension( 'Capiunto' ); } if ( $wmgUseKartographer && $wmgUseJsonConfig ) { wfLoadExtension( 'Kartographer' ); $wgKartographerMapServer = 'https://maps.wikimedia.org'; if ( $wmgEnableGeoData && $wmgKartographerNearby ) { $wgKartographerNearby = true; } } if ( $wmgUsePageViewInfo ) { wfLoadExtension( 'PageViewInfo' ); $wgPageViewInfoWikimediaEndpoint = $wmgLocalServices['rest-gateway'] . '/wikimedia.org/v1'; } if ( $wgDBname === 'foundationwiki' ) { // Foundationwiki has raw html enabled. Attempt to prevent people // from accidentally violating the privacy policy with external scripts. // Note, we need all WMF domains in here due to Special:HideBanners // being loaded as an image from various domains on donation thank you // pages. $wgHooks['BeforePageDisplay'][] = static function ( $out, $skin ) { $resp = $out->getRequest()->response(); $cspHeader = "default-src *.wikimedia.org *.wikipedia.org *.wiktionary.org *.wikisource.org *.wikibooks.org *.wikiversity.org *.wikiquote.org *.wikinews.org www.mediawiki.org www.wikidata.org *.wikifunctions.org *.wikivoyage.org data: blob: 'self'; script-src *.wikimedia.org 'unsafe-inline' 'unsafe-eval' 'self'; style-src *.wikimedia.org data: 'unsafe-inline' 'self'; report-uri /w/api.php?action=cspreport&format=none&reportonly=1&source=wmfwiki&"; $resp->header( "Content-Security-Policy-Report-Only: $cspHeader" ); }; } if ( $wmgUse3d ) { wfLoadExtension( '3D' ); $wgTrustedMediaFormats[] = 'application/sla'; $wg3dProcessor = [ '/usr/bin/xvfb-run', '-a', '-s', '-ac -screen 0 1280x1024x24', '/srv/deployment/3d2png/deploy/src/3d2png.js' ]; if ( $wmgUseMultimediaViewer ) { $wgMediaViewerExtensions['stl'] = 'mmv.3d'; } if ( $wmgUpload3d ) { $wgFileExtensions[] = 'stl'; } } if ( $wmgUseReadingLists ) { wfLoadExtension( 'ReadingLists' ); $wgVirtualDomainsMapping['virtual-readinglists'] = [ 'cluster' => 'extension1', 'db' => 'wikishared' ]; $wgReadingListsMaxEntriesPerList = 5000; $wgReadingListAndroidAppDownloadLink = 'https://play.google.com/store/apps/details?id=org.wikipedia&referrer=utm_source%3DreadingLists'; $wgReadingListiOSAppDownloadLink = 'https://apps.apple.com/app/apple-store/id324715238?pt=208305&ct=shared-reading-list-landing&mt=8'; } if ( $wmgUseGlobalPreferences && $wmgUseCentralAuth ) { // This is intentionally loaded *after* the Echo extension (above). wfLoadExtension( 'GlobalPreferences' ); } if ( $wmgUseWikisource ) { // Intentionally loaded *after* the Collection extension above. wfLoadExtension( 'Wikisource' ); $wgWikisourceHttpProxy = $wgCopyUploadProxy; } if ( $wmgUseGrowthExperiments ) { wfLoadExtension( 'GrowthExperiments' ); // T298122 temporary fix while mobile-only quality gate gets removed $wgDefaultUserOptions['growthexperiments-addimage-desktop'] = 1; $wgGEImageRecommendationServiceUrl = $wmgLocalServices['image-suggestion']; $wgGELinkRecommendationServiceUrl = $wmgLocalServices['linkrecommendation']; } if ( $wmgUseWikiLambda ) { wfLoadExtension( 'WikiLambda' ); $wgWikiLambdaOrchestratorLocation = $wmgLocalServices['wikifunctions-orchestrator']; $wgWikiLambdaObjectCache = 'mcrouter-wikifunctions'; } if ( $wmgUseWikistories ) { wfLoadExtension( 'Wikistories' ); } if ( PHP_SAPI === 'cli' ) { // Needed for the "abstracts" XML dumps. We only load it for PHP from the CLI. // Must be loaded explicitly here so that autoloading works when specifying // its PHP class name as CLI option to the maintenance script. wfLoadExtension( 'ActiveAbstract' ); } if ( $wmgUseCSPReportOnly || $wmgUseCSPReportOnlyHasSession || $wmgUseCSP ) { // Temporary global whitelist for origins used by trusted // opt-in scripts, until a per-user ability for this exists. // T207900#4846582 $wgCSPFalsePositiveUrls['https://cvn.wmflabs.org'] = true; $wgCSPFalsePositiveUrls['https://tools.wmflabs.org/intuition/'] = true; $wgCSPFalsePositiveUrls['https://intuition.toolforge.org/'] = true; $wgExtensionFunctions[] = static function () { global $wgCSPReportOnlyHeader, $wmgUseCSPReportOnly, $wmgApprovedContentSecurityPolicyDomains, $wmgUseCSP, $wgCSPHeader; if ( !$wmgUseCSPReportOnly && !$wmgUseCSP ) { // This means that $wmgUseCSPReportOnlyHasSession // is set, so only logged in users should trigger this. if ( defined( 'MW_NO_SESSION' ) || MW_ENTRY_POINT === 'cli' || !MediaWiki\Session\SessionManager::getGlobalSession()->isPersistent() ) { // There is no session, so don't set CSP, as we care more // about actual users, and this allows quick revert since // not cached in varnish return; } } if ( $wmgUseCSPReportOnly ) { $wgCSPReportOnlyHeader['useNonces'] = false; $wgCSPReportOnlyHeader['includeCORS'] = false; $wgCSPReportOnlyHeader['default-src'] = array_merge( $wmgApprovedContentSecurityPolicyDomains, [ // Needed for Math. Remove when/if math is fixed. 'wikimedia.org', ] ); $wgCSPReportOnlyHeader['script-src'] = $wmgApprovedContentSecurityPolicyDomains; } if ( $wmgUseCSP ) { $wgCSPHeader['useNonces'] = false; $wgCSPHeader['includeCORS'] = false; $wgCSPHeader['default-src'] = array_merge( $wmgApprovedContentSecurityPolicyDomains, [ // Needed for Math. Remove when/if math is fixed. 'wikimedia.org', ] ); $wgCSPHeader['script-src'] = $wmgApprovedContentSecurityPolicyDomains; } }; } if ( $wmgUseCampaignEvents ) { wfLoadExtension( 'CampaignEvents' ); wfLoadExtension( 'WikimediaCampaignEvents' ); $wgCampaignEventsDatabaseCluster = 'extension1'; if ( $wgDBname === 'metawiki' ) { $wgCampaignEventsDatabaseName = 'wikishared'; } $wgVirtualDomainsMapping['virtual-campaignevents'] = [ 'cluster' => 'extension1', 'db' => $wmgCampaignEventsUseCentralDB ? 'wikishared' : false, ]; $wgCampaignEventsProgramsAndEventsDashboardInstance = 'production'; } if ( $wmgRealm === 'labs' ) { require __DIR__ . '/CommonSettings-labs.php'; } foreach ( $wgGroupPermissions as $group => $_ ) { if ( $group !== 'interface-admin' && ( !empty( $wgGroupPermissions[$group]['editsitecss'] ) || !empty( $wgGroupPermissions[$group]['editsitejs'] ) || !empty( $wgGroupPermissions[$group]['editusercss'] ) || !empty( $wgGroupPermissions[$group]['edituserjs'] ) || !empty( $wgGroupPermissions[$group]['editmyuserjsredirect'] ) ) ) { // enforce that interace-admin is the only group that can edit non-own CSS/JS unset( $wgGroupPermissions[$group]['editsitecss'], $wgGroupPermissions[$group]['editsitejs'], $wgGroupPermissions[$group]['editusercss'], $wgGroupPermissions[$group]['edituserjs'], $wgGroupPermissions[$group]['editmyuserjsredirect'] ); } } if ( $wmgShowRollbackConfirmationDefaultUserOptions ) { $wgDefaultUserOptions['showrollbackconfirmation'] = 1; } if ( $wmgUseWikimediaEditorTasks ) { wfLoadExtension( 'WikimediaEditorTasks' ); } // T283003: TheWikipediaLibrary requires GlobalPreferences and CentralAuth to be installed if ( $wmgUseTheWikipediaLibrary && $wmgUseGlobalPreferences && $wmgUseCentralAuth ) { wfLoadExtension( 'TheWikipediaLibrary' ); } if ( $wmgUseWikimediaApiPortal ) { wfLoadSkin( 'WikimediaApiPortal' ); } if ( $wmgUseWikimediaApiPortalOAuth ) { wfLoadExtension( 'WikimediaApiPortalOAuth' ); } if ( $wmgUseGlobalWatchlist ) { wfLoadExtension( 'GlobalWatchlist' ); } if ( $wmgUseNearbyPages ) { wfLoadExtension( 'NearbyPages' ); } if ( $wmgUseImageSuggestions ) { wfLoadExtension( 'ImageSuggestions' ); } if ( $wmgUseSearchVue ) { wfLoadExtension( 'SearchVue' ); } if ( $wmgUseStopForumSpam ) { wfLoadExtension( 'StopForumSpam' ); $wgSFSIPListLocation = 'https://www.stopforumspam.com/downloads/listed_ip_90_ipv46_all.gz'; $wgSFSValidateIPListLocationMD5 = 'https://www.stopforumspam.com/downloads/listed_ip_90_ipv46_all.gz.md5'; $wgSFSProxy = $wgCopyUploadProxy; } if ( $wmgUsePhonos ) { wfLoadExtension( 'Phonos' ); // $wgPhonosApiKeyGoogle in PrivateSettings $wgPhonosEngine = 'google'; $wgPhonosFileBackend = 'global-multiwrite'; $wgPhonosApiProxy = $wgCopyUploadProxy; } if ( $wmgUseVueTest ) { wfLoadExtension( 'VueTest' ); } if ( $wmgUsePageNotice ) { wfLoadExtension( 'PageNotice' ); } // This is a temporary hack for hooking up Parsoid/PHP with MediaWiki // This is just the regular check out of parsoid in that week's vendor $parsoidDir = "$IP/vendor/wikimedia/parsoid"; $wgParsoidSettings = [ 'useSelser' => true, 'linting' => true, ]; if ( ClusterConfig::getInstance()->isParsoid() ) { // Parsoid testing special case if ( ClusterConfig::getInstance()->getHostname() === 'scandium' ) { // Scandium has its own special check out of parsoid for testing. $parsoidDir = __DIR__ . "/../../parsoid-testing"; // Override settings specific to round-trip testing on scandium require_once "$parsoidDir/tests/RTTestSettings.php"; } // Only load Parsoid extension (aka internal Parsoid REST API) on // Parsoid cluster wfLoadExtension( 'Parsoid', "$parsoidDir/extension.json" ); } unset( $parsoidDir ); // End of temporary hack for hooking up Parsoid/PHP with MediaWiki if ( $wmgUseParserMigration ) { wfLoadExtension( 'ParserMigration' ); } // T350653 if ( $wmgEditRecoveryDefaultUserOptions ) { $wgDefaultUserOptions['editrecovery'] = 1; } // Community configuration if ( $wmgUseCommunityConfiguration ) { wfLoadExtension( 'CommunityConfiguration' ); $wgCommunityConfigurationBugReportingToolURL = 'https://phabricator.wikimedia.org/maniphest/task/edit/form/43'; } // phpcs:ignore MediaWiki.Files.ClassMatchesFilename.NotMatch class ClosedWikiProvider extends \MediaWiki\Auth\AbstractPreAuthenticationProvider { /** * @param User $user * @param bool $autocreate * @param array $options * @return \StatusValue */ public function testUserForCreation( $user, $autocreate, array $options = [] ) { $logger = \MediaWiki\Logger\LoggerFactory::getInstance( 'authentication' ); $logger->info( 'Running ClosedWikiProvider for {name}', [ 'name' => $user->getName() ] ); if ( $user->getId() ) { // User already exists, do not block authentication $logger->info( 'User {name} passed ClosedWikiProvider check, account already exists', [ 'name' => $user->getName() ] ); return \StatusValue::newGood(); } $central = CentralAuthUser::getInstance( $user ); if ( $central->hasGlobalPermission( 'createaccount' ) || $central->hasGlobalPermission( 'autocreateaccount' ) ) { $logger->info( 'User {name} passed ClosedWikiProvider check, has permissions', [ 'name' => $user->getName() ] ); // User can autocreate account per global permissions return \StatusValue::newGood(); } $logger->error( 'Account autocreation denied for {name} by ClosedWikiProvider', [ 'name' => $user->getName() ] ); return \StatusValue::newFatal( 'authmanager-autocreate-noperm' ); } } if ( in_array( $wgDBname, MWWikiversions::readDbListFile( 'closed' ) ) && $wmgUseCentralAuth ) { $wgAuthManagerAutoConfig['preauth'][\ClosedWikiProvider::class] = [ 'class' => \ClosedWikiProvider::class, 'sort' => 0, ]; } $wgLogRestrictions = array_merge( $wgLogRestrictions, $wmgLogRestrictions ); # THIS MUST BE AFTER ALL EXTENSIONS ARE INCLUDED # # REALLY ... we're not kidding here ... NO EXTENSIONS AFTER if ( !defined( 'MW_NO_EXTENSION_MESSAGES' ) ) { require __DIR__ . "/ExtensionMessages-$wmgVersionNumber.php"; }