From 49bacbc4e5dde423aed265ba4c2a9468d106b977 Mon Sep 17 00:00:00 2001 From: SomeRandomDeveloper Date: Fri, 20 Feb 2026 21:27:56 +0100 Subject: [PATCH 01/13] feat(config): add CentralAuth --- config/MWCExtensions.php | 16 ++++++++++++++++ config/MWCFunctions.php | 8 ++++++++ 2 files changed, 24 insertions(+) diff --git a/config/MWCExtensions.php b/config/MWCExtensions.php index 33213a6..a96688e 100644 --- a/config/MWCExtensions.php +++ b/config/MWCExtensions.php @@ -51,6 +51,10 @@ public function Analytics(): self { return $this->ext( 'Analytics' ); } + public function AntiSpoof(): self { + return $this->ext( 'AntiSpoof' ); + } + public function ApprovedRevs(): self { return $this->ext( 'ApprovedRevs' ); } @@ -100,6 +104,18 @@ public function Cargo(): self { return $this->ext( 'Cargo' ); } + public function CentralAuth( + string $centralWiki, + ): self { + global $wgMwcFarm; + if ( !$wgMwcFarm ) { + throw new Exception( 'Please call setupFarm() before using ' . __METHOD__ . '!' ); + } + return $this + ->virtualDomainMapping( 'virtual-centralauth', $centralWiki ) + ->ext( 'CentralAuth' ); + } + public function CentralNotice(): self { return $this ->ext( 'CentralNotice' ) diff --git a/config/MWCFunctions.php b/config/MWCFunctions.php index 71846dd..4d3ac63 100644 --- a/config/MWCFunctions.php +++ b/config/MWCFunctions.php @@ -139,6 +139,14 @@ public function defaultUserOption( string $option, mixed $value ): self { } ); } + public function virtualDomainMapping( string $virtual, string $db, ?string $cluster = null ): self { + $mapping = [ 'db' => $db ]; + if ( $cluster !== null ) { + $mapping['cluster'] = $cluster; + } + return $this->modConf( 'wgVirtualDomainsMapping', static fn ( &$c ) => $c[$virtual] = $mapping ); + } + public function env( string $key, string $default = '' ): string { global $wgMwcEnv; // integration test fix From 5a88fe0ca9dbd4e8726582148474179afcdc88d0 Mon Sep 17 00:00:00 2001 From: SomeRandomDeveloper Date: Fri, 20 Feb 2026 21:28:24 +0100 Subject: [PATCH 02/13] feat(farm): add user management interface --- config/Farm/CentralAuthUserManagement.php | 19 ++++++++++++ config/Farm/IFarmUserManagement.php | 14 +++++++++ config/Farm/MWCFarm.php | 36 +++++++++++++++++++++-- config/Farm/StandaloneUserManagement.php | 17 +++++++++++ 4 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 config/Farm/CentralAuthUserManagement.php create mode 100644 config/Farm/IFarmUserManagement.php create mode 100644 config/Farm/StandaloneUserManagement.php diff --git a/config/Farm/CentralAuthUserManagement.php b/config/Farm/CentralAuthUserManagement.php new file mode 100644 index 0000000..f3e2b04 --- /dev/null +++ b/config/Farm/CentralAuthUserManagement.php @@ -0,0 +1,19 @@ +CentralAuth( + $farm->getCentralWiki() + ); + } + +} diff --git a/config/Farm/IFarmUserManagement.php b/config/Farm/IFarmUserManagement.php new file mode 100644 index 0000000..2c881b8 --- /dev/null +++ b/config/Farm/IFarmUserManagement.php @@ -0,0 +1,14 @@ + $wikis * @param array $settings - * @param string $defaultWiki The wiki that will be used for maintenance scripts by default + * @param string $centralWiki The central wiki (will be used for maintenance scripts by default) + * @param int $userManagementType One of the USER_MANAGEMENT_ constants */ public function __construct( private readonly array $wikis, private array $settings, - private readonly string $defaultWiki, + private readonly string $centralWiki, + private readonly int $userManagementType, ) { + require_once 'IFarmUserManagement.php'; + switch ( $this->userManagementType ) { + case self::USER_MANAGEMENT_STANDALONE: + require_once 'StandaloneUserManagement.php'; + $this->userManagement = new StandaloneUserManagement(); + break; + case self::USER_MANAGEMENT_CENTRAL_AUTH: + require_once 'CentralAuthUserManagement.php'; + $this->userManagement = new CentralAuthUserManagement(); + break; + } } public function apply( MediaWikiConfig $mwc ): void { @@ -30,7 +49,7 @@ public function apply( MediaWikiConfig $mwc ): void { if ( defined( 'MW_DB' ) ) { $wikiId = MW_DB; } elseif ( MW_ENTRY_POINT === 'cli' ) { - $wikiId = $this->defaultWiki; + $wikiId = $this->centralWiki; } else { $subdomain = explode( '.', $_SERVER['SERVER_NAME'] )[0]; if ( !array_key_exists( $subdomain, $this->wikis ) ) { @@ -48,6 +67,9 @@ public function apply( MediaWikiConfig $mwc ): void { $siteConfiguration->suffixes = [ 'wiki' ]; $siteConfiguration->settings = $this->settings; + // Setup user management before config + $this->userManagement->setup( $this, $mwc ); + foreach ( $siteConfiguration->getAll( $wikiId ) as $key => $value ) { $mwc->conf( $key, $value ); } @@ -67,4 +89,12 @@ public function getWikis(): array { return $this->wikis; } + public function getUserManagement(): IFarmUserManagement { + return $this->userManagement; + } + + public function getCentralWiki(): string { + return $this->centralWiki; + } + } diff --git a/config/Farm/StandaloneUserManagement.php b/config/Farm/StandaloneUserManagement.php new file mode 100644 index 0000000..5fb6774 --- /dev/null +++ b/config/Farm/StandaloneUserManagement.php @@ -0,0 +1,17 @@ + Date: Sat, 21 Feb 2026 01:29:11 +0100 Subject: [PATCH 03/13] feat(config): support SUL3 --- config/Farm/CentralAuthUserManagement.php | 63 ++++++++++++++++++++++- config/Farm/IFarmUserManagement.php | 8 ++- config/Farm/MWCFarm.php | 20 ++++--- config/MWCExtensions.php | 5 +- features/sul3/sul3-apache.Dockerfile | 10 ++++ features/sul3/sul3.md | 20 +++++++ 6 files changed, 115 insertions(+), 11 deletions(-) create mode 100644 features/sul3/sul3-apache.Dockerfile create mode 100644 features/sul3/sul3.md diff --git a/config/Farm/CentralAuthUserManagement.php b/config/Farm/CentralAuthUserManagement.php index f3e2b04..6458ea8 100644 --- a/config/Farm/CentralAuthUserManagement.php +++ b/config/Farm/CentralAuthUserManagement.php @@ -2,18 +2,79 @@ namespace MediaWikiConfig\Farm; +use MediaWiki\Request\WebRequest; use MediaWikiConfig\MediaWikiConfig; /** * Use CentralAuth to manage shared user accounts across wikis. + * + * ## Setup after enabling + * + * 1. `mwutil run CentralAuth:migratePass0.php` + * 2. `mwutil run CentralAuth:migratePass1.php` + * 3. `INSERT INTO global_group_permissions (ggp_group,ggp_permission) VALUES ('steward','globalgrouppermissions'), + * ('steward','globalgroupmembership');` + * 4. `INSERT IGNORE INTO global_user_groups (gug_user, gug_group) VALUES ((SELECT gu_id FROM globaluser WHERE + * gu_name='Admin'), 'steward');` */ class CentralAuthUserManagement implements IFarmUserManagement { /** @inheritDoc */ - function setup( MWCFarm $farm, MediaWikiConfig $mwc ): void { + public function setup( MWCFarm $farm, MediaWikiConfig $mwc ): void { $mwc->CentralAuth( $farm->getCentralWiki() ); + $mwc->conf( 'wgCentralAuthLoginWiki', $farm->getCentralWiki() ); + $this->enableSUL3( $farm, $mwc ); + } + + private function enableSUL3( MWCFarm $farm, MediaWikiConfig $mwc ) { + $mwc + ->conf( 'wgCentralAuthSharedDomainCallback', fn ( $dbname ) => $this->getAuthDomain( $mwc, $dbname ) ) + ->conf( 'wgCentralAuthEnableSul3', true ) + ->conf( 'wgServer', WebRequest::detectServer( true ) ) + ->cloneConf( 'wgCanonicalServer', 'wgServer' ); + + if ( $mwc->getConf( 'wgServer' ) === $this->getAuthDomain( $mwc, null ) ) { + $dbName = $mwc->getConf( 'wgDBname' ); + $mwc + ->conf( 'wgEnableSidebarCache', false ) + ->conf( 'wgCentralAuthCookieDomain', '' ) + ->conf( 'wgCookiePrefix', 'auth' ) + ->conf( 'wgSessionName', 'authSession' ) + ->conf( 'wgScriptPath', "/$dbName/w" ) + ->conf( 'wgLoadScript', $mwc->getConf( 'wgScriptPath' ) . '/load.php' ) + ->conf( 'wgCanonicalServer', $this->getAuthDomain( $mwc, null ) ) + ->conf( 'wgScript', $mwc->getConf( 'wgScriptPath' ) . '/index.php' ) + ->conf( 'wgResourceBasePath', "/$dbName/w" ) + ->conf( 'wgExtensionAssetsPath', $mwc->getConf( 'wgResourceBasePath' ) . '/extensions' ) + ->conf( 'wgStylePath', $mwc->getConf( 'wgResourceBasePath' ) . '/skins' ) + ->cloneConf( 'wgLocalStylePath', 'wgStylePath' ) + ->conf( 'wgArticlePath', "/$dbName/wiki/\$1" ) + ->conf( 'wgServer', $this->getAuthDomain( $mwc, null ) ); + } + } + + /** @inheritDoc */ + public function overrideWikiExists( MWCFarm $farm, MediaWikiConfig $mwc, string $subdomain ): ?string { + if ( $subdomain !== 'auth' ) { + return null; + } + // Taken from https://github.com/miraheze/mw-config/blob/main/initialise/MirahezeFunctions.php#L287-L298 + $requestUri = $_SERVER['REQUEST_URI']; + $pathBits = explode( '/', $requestUri, 3 ); + if ( count( $pathBits ) < 3 ) { + trigger_error( + "Invalid request URI (requestUri=$requestUri), can't determine wiki.\n", + E_USER_ERROR + ); + } + return $pathBits[1]; + } + + private function getAuthDomain( MediaWikiConfig $mwc, ?string $dbName ): string { + $port = $mwc->env( 'MW_DOCKER_PORT' ); + return "http://auth.localhost:$port" . ( $dbName ? "/$dbName" : '' ); } } diff --git a/config/Farm/IFarmUserManagement.php b/config/Farm/IFarmUserManagement.php index 2c881b8..e15b280 100644 --- a/config/Farm/IFarmUserManagement.php +++ b/config/Farm/IFarmUserManagement.php @@ -9,6 +9,12 @@ interface IFarmUserManagement { /** * Initialize the user management and set all relevant config options. */ - function setup( MWCFarm $farm, MediaWikiConfig $mwc ): void; + public function setup( MWCFarm $farm, MediaWikiConfig $mwc ): void; + + /** + * Override the check for whether a wiki exists. + * Used for CentralAuth/SUL3. + */ + public function overrideWikiExists( MWCFarm $farm, MediaWikiConfig $mwc, string $subdomain ): ?string; } diff --git a/config/Farm/MWCFarm.php b/config/Farm/MWCFarm.php index 3a47737..6bd3695 100644 --- a/config/Farm/MWCFarm.php +++ b/config/Farm/MWCFarm.php @@ -45,6 +45,9 @@ public function apply( MediaWikiConfig $mwc ): void { $serverVals[$dbname] = "http://$subdomain.localhost:$port"; } $this->settings['wgServer'] = $serverVals; + $this->settings['wgArticlePath'] = [ + 'default' => $mwc->getConf( 'wgArticlePath' ), + ]; if ( defined( 'MW_DB' ) ) { $wikiId = MW_DB; @@ -52,10 +55,13 @@ public function apply( MediaWikiConfig $mwc ): void { $wikiId = $this->centralWiki; } else { $subdomain = explode( '.', $_SERVER['SERVER_NAME'] )[0]; - if ( !array_key_exists( $subdomain, $this->wikis ) ) { - $this->showWikiMap(); - } else { - $wikiId = $this->wikis[$subdomain]; + $wikiId = $this->userManagement->overrideWikiExists( $this, $mwc, $subdomain ); + if ( $wikiId === null ) { + if ( !array_key_exists( $subdomain, $this->wikis ) ) { + $this->showWikiMap(); + } else { + $wikiId = $this->wikis[$subdomain]; + } } } @@ -67,14 +73,14 @@ public function apply( MediaWikiConfig $mwc ): void { $siteConfiguration->suffixes = [ 'wiki' ]; $siteConfiguration->settings = $this->settings; - // Setup user management before config - $this->userManagement->setup( $this, $mwc ); - foreach ( $siteConfiguration->getAll( $wikiId ) as $key => $value ) { $mwc->conf( $key, $value ); } $mwc->conf( 'wgConf', $siteConfiguration ); + + // Setup user management after config + $this->userManagement->setup( $this, $mwc ); } private function showWikiMap(): never { diff --git a/config/MWCExtensions.php b/config/MWCExtensions.php index a96688e..3683a75 100644 --- a/config/MWCExtensions.php +++ b/config/MWCExtensions.php @@ -111,9 +111,10 @@ public function CentralAuth( if ( !$wgMwcFarm ) { throw new Exception( 'Please call setupFarm() before using ' . __METHOD__ . '!' ); } + // Call wfLoadExtension *before* setting the virtual domain mapping and similar settings + $this->ext( 'CentralAuth' ); return $this - ->virtualDomainMapping( 'virtual-centralauth', $centralWiki ) - ->ext( 'CentralAuth' ); + ->virtualDomainMapping( 'virtual-centralauth', $centralWiki ); } public function CentralNotice(): self { diff --git a/features/sul3/sul3-apache.Dockerfile b/features/sul3/sul3-apache.Dockerfile new file mode 100644 index 0000000..df6e699 --- /dev/null +++ b/features/sul3/sul3-apache.Dockerfile @@ -0,0 +1,10 @@ +# Important: Make sure the version here matches the latest version of the mediawiki-web image in docker-compose.yml +FROM docker-registry.wikimedia.org/dev/bookworm-apache2:1.0.1 + +# Append SUL3 load.php rewrite first, then generic wiki index rewrite, after "RewriteEngine On" +RUN grep -q "wiki rewrite rules" /etc/apache2/sites-available/000-default.conf || \ + sed -i '/RewriteEngine On/a \ + # wiki rewrite rules\n\ + RewriteRule ^/[^/]+wiki/w/load\.php$ /w/load.php [L]\n\ + RewriteRule ^/[^/]+wiki(/.*)?$ %{DOCUMENT_ROOT}/w/index.php [L]' \ + /etc/apache2/sites-available/000-default.conf diff --git a/features/sul3/sul3.md b/features/sul3/sul3.md new file mode 100644 index 0000000..5f1f3b4 --- /dev/null +++ b/features/sul3/sul3.md @@ -0,0 +1,20 @@ +# SUL3 + +Custom image to add a rewrite to apache that rewrites /.* to index.php + +docker-compose.override.yml: + +```yml +services: + mediawiki-web: + build: + dockerfile: features/sul3/sul3-apache.Dockerfile + context: . +``` + +Then run the following commands: + +```shell +docker compose --env-file config/.env -p main build mediawiki-web +mwutil recreate mediawiki-web +``` From 7388e2dfccf595055730862290d686d69edf60d3 Mon Sep 17 00:00:00 2001 From: SomeRandomDeveloper Date: Mon, 23 Feb 2026 14:09:16 +0100 Subject: [PATCH 04/13] feat(config): add getFarm method --- config/MWCConfig.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/config/MWCConfig.php b/config/MWCConfig.php index 133eb5b..e9e8fe9 100644 --- a/config/MWCConfig.php +++ b/config/MWCConfig.php @@ -96,4 +96,9 @@ public function setupFarm( MWCFarm $farm ): self { return $this; } + public function getFarm(): ?MWCFarm { + global $wgMwcFarm; + return $wgMwcFarm; + } + } From b2fa7cd49bdd44ee9025071ebdf4fb78c48ca52f Mon Sep 17 00:00:00 2001 From: SomeRandomDeveloper Date: Mon, 23 Feb 2026 14:09:31 +0100 Subject: [PATCH 05/13] feat(config): add BetaFeatures, CodeEditor, Variables, VariablesLua --- config/MWCExtensions.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/config/MWCExtensions.php b/config/MWCExtensions.php index 3683a75..ef1e835 100644 --- a/config/MWCExtensions.php +++ b/config/MWCExtensions.php @@ -77,6 +77,10 @@ public function AutoCreatePage(): self { return $this->ext( 'AutoCreatePage' ); } + public function BetaFeatures(): self { + return $this->ext( 'BetaFeatures' ); + } + public function Bootstrap(): self { return $this->ext( 'Bootstrap' ); } @@ -170,6 +174,10 @@ public function Cite(): self { return $this->ext( 'Cite' ); } + public function CodeEditor(): self { + return $this->ext( 'CodeEditor' ); + } + public function CodeMirror( CodeMirrorVersion $version = CodeMirrorVersion::V6 ): self { if ( $version === CodeMirrorVersion::V6 ) { $this->conf( 'wgCodeMirrorV6', true ); @@ -905,6 +913,17 @@ public function UserVerification(): self { return $this->ext( 'UserVerification' ); } + public function Variables(): self { + return $this->ext( 'Variables' ); + } + + public function VariablesLua(): self { + return $this + ->Scribunto() + ->Variables() + ->ext( 'VariablesLua' ); + } + public function Video(): self { return $this->ext( 'Video' ); } From 5783b58ba90096a568cb6fa6ce6b2ed7b619ed5a Mon Sep 17 00:00:00 2001 From: SomeRandomDeveloper Date: Mon, 23 Feb 2026 14:09:44 +0100 Subject: [PATCH 06/13] feat(config): set central wiki for GUP if using a farm --- config/MWCExtensions.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config/MWCExtensions.php b/config/MWCExtensions.php index ef1e835..3b8b54f 100644 --- a/config/MWCExtensions.php +++ b/config/MWCExtensions.php @@ -367,6 +367,10 @@ public function Gadgets(): self { } public function GlobalUserPage( string $apiUrl ): self { + $farm = $this->getFarm(); + if ( $farm ) { + $this->conf( 'wgGlobalUserPageDBname', $farm->getCentralWiki() ); + } return $this ->ext( 'GlobalUserPage' ) ->conf( 'wgGlobalUserPageAPIUrl', $apiUrl ); From 257d862f84bbc0d55414941519d5397133370fab Mon Sep 17 00:00:00 2001 From: SomeRandomDeveloper Date: Mon, 23 Feb 2026 14:17:57 +0100 Subject: [PATCH 07/13] refactor(config): add appendToIndexedConfArray --- config/MWCExtensions.php | 2 +- config/MWCFunctions.php | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/config/MWCExtensions.php b/config/MWCExtensions.php index 3b8b54f..f88fc77 100644 --- a/config/MWCExtensions.php +++ b/config/MWCExtensions.php @@ -993,7 +993,7 @@ public function WikiCategoryTagCloud(): self { public function WikiEditor(): self { return $this ->ext( 'WikiEditor' ) - ->modConf( 'wgHiddenPrefs', static fn ( &$c ) => $c[] = 'usebetatoolbar' ) + ->appendToIndexedConfArray( 'wgHiddenPrefs', 'usebetatoolbar' ) ->defaultUserOption( 'usebetatoolbar', 1 ); } diff --git a/config/MWCFunctions.php b/config/MWCFunctions.php index 4d3ac63..d402ccc 100644 --- a/config/MWCFunctions.php +++ b/config/MWCFunctions.php @@ -85,6 +85,10 @@ public function modConf( string $name, callable $modify, mixed $defaultIfNotSet return $this->conf( $name, $val ); } + public function appendToIndexedConfArray( string $name, mixed $value ): self { + return $this->modConf( $name, static fn ( &$c ) => $c[] = $value ); + } + /** * @param string $name * // phpcs:ignore MediaWiki.Commenting.FunctionComment.ObjectTypeHintParam From 5d43223ad61a05dd1c5cc8267b3d1a3c0b9ca6b4 Mon Sep 17 00:00:00 2001 From: SomeRandomDeveloper Date: Mon, 23 Feb 2026 14:18:09 +0100 Subject: [PATCH 08/13] feat(config): add QuickInstantCommons --- config/MWCExtensions.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/config/MWCExtensions.php b/config/MWCExtensions.php index f88fc77..8902b8b 100644 --- a/config/MWCExtensions.php +++ b/config/MWCExtensions.php @@ -640,6 +640,30 @@ public function ProtectSite(): self { return $this->ext( 'ProtectSite' ); } + public function QuickInstantCommons( ?string $apiUrl = null, ?string $name = null ): self { + if ( $apiUrl !== null ) { + // See https://www.mediawiki.org/wiki/Extension:QuickInstantCommons#Advanced_Configuration + $this->appendToIndexedConfArray( 'wgForeignFileRepos', [ + 'class' => '\MediaWiki\Extension\QuickInstantCommons\Repo', + 'name' => $name ?? 'externalrepowiki', + 'directory' => $this->getConf( 'wgUploadDirectory' ), + 'apibase' => $apiUrl, + 'hashLevels' => 2, + 'thumbUrl' => false, + 'fetchDescription' => true, + 'descriptionCacheExpiry' => 43200, + 'transformVia404' => false, + 'abbrvThreshold' => 160, + 'apiMetadataExpiry' => 60*60*24, + 'disabledMediaHandlers' => [] + ] ); + } + return $this + ->ext( 'QuickInstantCommons' ) + ->conf( 'wgUseInstantCommons', false ) + ->conf( 'wgUseQuickInstantCommons', $apiUrl === null ); + } + public function QuizGame(): self { return $this ->SocialProfile() From 57e9e0e99a111e99985c64d6751322a342ef3095 Mon Sep 17 00:00:00 2001 From: SomeRandomDeveloper Date: Tue, 24 Feb 2026 21:41:32 +0100 Subject: [PATCH 09/13] feat(config): add GlobalWatchlist, Loops, MediaSearch --- config/MWCExtensions.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/config/MWCExtensions.php b/config/MWCExtensions.php index 8902b8b..a0a4c2a 100644 --- a/config/MWCExtensions.php +++ b/config/MWCExtensions.php @@ -380,6 +380,10 @@ public function GlobalUserrights(): self { return $this->ext( 'GlobalUserrights' ); } + public function GlobalWatchlist(): self { + return $this->ext( 'GlobalWatchlist' ); + } + public function GrowthExperiments(): self { // https://www.mediawiki.org/wiki/Extension:GrowthExperiments/developer_setup return $this @@ -472,6 +476,10 @@ public function Lockdown(): self { return $this->ext( 'Lockdown' ); } + public function Loops(): self { + return $this->ext( 'Loops' ); + } + public function Maps(): self { return $this->ext( 'Maps' ); } @@ -482,6 +490,11 @@ public function MassEditRegex(): self { ->grantPermission( 'sysop', 'masseditregex' ); } + public function MediaSearch(): self { + // TODO are there any dependencies or settings? + return $this->ext( 'MediaSearch' ); + } + public function MediaUploader(): self { return $this->ext( 'MediaUploader' ); } From 64575771f16d2094a1514f6822d9af55fe602adf Mon Sep 17 00:00:00 2001 From: SomeRandomDeveloper Date: Tue, 24 Feb 2026 21:41:48 +0100 Subject: [PATCH 10/13] feat(config): add RandomSelection --- config/MWCExtensions.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/config/MWCExtensions.php b/config/MWCExtensions.php index a0a4c2a..85f47ff 100644 --- a/config/MWCExtensions.php +++ b/config/MWCExtensions.php @@ -668,7 +668,7 @@ public function QuickInstantCommons( ?string $apiUrl = null, ?string $name = nul 'transformVia404' => false, 'abbrvThreshold' => 160, 'apiMetadataExpiry' => 60*60*24, - 'disabledMediaHandlers' => [] + 'disabledMediaHandlers' => [], ] ); } return $this @@ -687,6 +687,10 @@ public function RandomImageByCategory(): self { return $this->ext( 'RandomImageByCategory' ); } + public function RandomSelection(): self { + return $this->ext( 'RandomSelection' ); + } + public function RatePage(): self { return $this->ext( 'RatePage' ); } From 00294ff7806639bb89fef28f478fcbbae997f3ae Mon Sep 17 00:00:00 2001 From: SomeRandomDeveloper Date: Tue, 3 Mar 2026 14:09:07 +0100 Subject: [PATCH 11/13] feat(config): add customProtectionLevel function --- config/MWCFunctions.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/config/MWCFunctions.php b/config/MWCFunctions.php index d402ccc..0d397e9 100644 --- a/config/MWCFunctions.php +++ b/config/MWCFunctions.php @@ -173,4 +173,16 @@ public function onBeforePageDisplay( $out, $skin ): void { } ); } + public function customProtectionLevel( string $name, bool $cascading, ?string $grantTo = null ): self { + if ( $cascading ) { + $this->appendToIndexedConfArray( 'wgCascadingRestrictionLevels', $name ); + } + if ( $grantTo !== null ) { + $this->grantPermission( $grantTo, $name ); + } + return $this + ->appendToIndexedConfArray( 'wgRestrictionLevels', $name ) + ->appendToIndexedConfArray( 'wgAvailableRights', $name ); + } + } From ff289e2f2b8199e3df1fec091663860bebfdbbe2 Mon Sep 17 00:00:00 2001 From: SomeRandomDeveloper Date: Tue, 3 Mar 2026 14:09:28 +0100 Subject: [PATCH 12/13] chore: bump bookworm and jobrunner images Also consolidate the custom dockerfiles into one file per container. --- docker-compose.yml | 4 ++-- .../apache.Dockerfile} | 0 .../fpm.Dockerfile} | 11 +++++++-- features/dockerfiles/jobrunner.Dockerfile | 18 ++++++++++++++ features/luasandbox/luasandbox.md | 23 ------------------ features/profiling/profiling.Dockerfile | 4 ---- features/profiling/profiling.md | 24 ------------------- features/sul3/sul3.md | 20 ---------------- 8 files changed, 29 insertions(+), 75 deletions(-) rename features/{sul3/sul3-apache.Dockerfile => dockerfiles/apache.Dockerfile} (100%) rename features/{luasandbox/luasandbox.Dockerfile => dockerfiles/fpm.Dockerfile} (55%) create mode 100644 features/dockerfiles/jobrunner.Dockerfile delete mode 100644 features/luasandbox/luasandbox.md delete mode 100644 features/profiling/profiling.Dockerfile delete mode 100644 features/profiling/profiling.md delete mode 100644 features/sul3/sul3.md diff --git a/docker-compose.yml b/docker-compose.yml index f70c9ba..0282785 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: mediawiki: - image: docker-registry.wikimedia.org/dev/bookworm-php83-fpm:1.0.0 + image: docker-registry.wikimedia.org/dev/bookworm-php83-fpm:1.0.0-s1 user: "${MW_DOCKER_UID}:${MW_DOCKER_GID}" extra_hosts: - "host.docker.internal:host-gateway" @@ -28,7 +28,7 @@ services: env_file: config/.env networks: [ mw ] mediawiki-jobrunner: - image: docker-registry.wikimedia.org/dev/bookworm-php83-jobrunner:1.0.0 + image: docker-registry.wikimedia.org/dev/bookworm-php83-jobrunner:1.0.0-s1 user: "${MW_DOCKER_UID}:${MW_DOCKER_GID}" volumes: - "./core:${MW_INSTALL_PATH}:cached" diff --git a/features/sul3/sul3-apache.Dockerfile b/features/dockerfiles/apache.Dockerfile similarity index 100% rename from features/sul3/sul3-apache.Dockerfile rename to features/dockerfiles/apache.Dockerfile diff --git a/features/luasandbox/luasandbox.Dockerfile b/features/dockerfiles/fpm.Dockerfile similarity index 55% rename from features/luasandbox/luasandbox.Dockerfile rename to features/dockerfiles/fpm.Dockerfile index e651996..7354891 100644 --- a/features/luasandbox/luasandbox.Dockerfile +++ b/features/dockerfiles/fpm.Dockerfile @@ -1,11 +1,18 @@ -FROM docker-registry.wikimedia.org/dev/bookworm-php83-fpm:1.0.0 AS build +FROM docker-registry.wikimedia.org/dev/bookworm-php83-fpm:1.0.0-s1 AS build +# Compile LuaSandbox from source WORKDIR /src RUN git clone https://gerrit.wikimedia.org/r/mediawiki/php/luasandbox RUN apt update && apt install php8.3-dev liblua5.1-0-dev -y RUN cd luasandbox && phpize && ./configure && make -FROM docker-registry.wikimedia.org/dev/bookworm-php83-fpm:1.0.0 +FROM docker-registry.wikimedia.org/dev/bookworm-php83-fpm:1.0.0-s1 +# Install LuaSandbox +RUN apt update && apt install liblua5.1-0 -y COPY --from=build /src/luasandbox/modules/luasandbox.so /usr/lib/php/20230831/luasandbox.so RUN echo 'extension=luasandbox.so' > /etc/php/8.3/mods-available/luasandbox.ini && phpenmod luasandbox + +# Install Excimer +RUN apt-get update && \ + apt-get install php8.3-excimer diff --git a/features/dockerfiles/jobrunner.Dockerfile b/features/dockerfiles/jobrunner.Dockerfile new file mode 100644 index 0000000..4b6e809 --- /dev/null +++ b/features/dockerfiles/jobrunner.Dockerfile @@ -0,0 +1,18 @@ +FROM docker-registry.wikimedia.org/dev/bookworm-php83-jobrunner:1.0.0-s1 as build + +# Compile LuaSandbox from source +WORKDIR /src +RUN git clone https://gerrit.wikimedia.org/r/mediawiki/php/luasandbox +RUN apt update && apt install php8.3-dev liblua5.1-0-dev -y +RUN cd luasandbox && phpize && ./configure && make + +FROM docker-registry.wikimedia.org/dev/bookworm-php83-jobrunner:1.0.0-s1 + +# Install LuaSandbox +RUN apt update && apt install liblua5.1-0 -y +COPY --from=build /src/luasandbox/modules/luasandbox.so /usr/lib/php/20230831/luasandbox.so +RUN echo 'extension=luasandbox.so' > /etc/php/8.3/mods-available/luasandbox.ini && phpenmod luasandbox + +# Install Excimer +RUN apt-get update && \ + apt-get install php8.3-excimer diff --git a/features/luasandbox/luasandbox.md b/features/luasandbox/luasandbox.md deleted file mode 100644 index 961ee12..0000000 --- a/features/luasandbox/luasandbox.md +++ /dev/null @@ -1,23 +0,0 @@ -# LuaSandbox - -docker-compose.override.yml: - -```yml -services: - mediawiki: - build: - dockerfile: features/luasandbox/luasandbox.Dockerfile - context: . -``` - -LocalSettings.php: -```php -$c->Scribunto( $c::SCRIBUNTO_ENGINE_LUASANDBOX ); -``` - -Then run the following commands: - -```shell -docker compose --env-file config/.env -p main build mediawiki -mwutil recreate -``` diff --git a/features/profiling/profiling.Dockerfile b/features/profiling/profiling.Dockerfile deleted file mode 100644 index a232501..0000000 --- a/features/profiling/profiling.Dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -FROM docker-registry.wikimedia.org/dev/bookworm-php83-fpm:1.0.0 - -RUN apt-get update && \ - apt-get install php8.3-excimer diff --git a/features/profiling/profiling.md b/features/profiling/profiling.md deleted file mode 100644 index b6e7ad6..0000000 --- a/features/profiling/profiling.md +++ /dev/null @@ -1,24 +0,0 @@ -# Profiling - -docker-compose.override.yml: - -```yml -services: - mediawiki: - build: - dockerfile: features/profiling/profiling.Dockerfile - context: . -``` - -LocalSettings.php: -```php -MediaWikiConfig::getInstance()->enableTraceLogging(); -``` - -Then run the following commands: - -```shell -docker compose --env-file config/.env -p main build mediawiki -mwutil recreate -mwutil profiling watch -``` diff --git a/features/sul3/sul3.md b/features/sul3/sul3.md deleted file mode 100644 index 5f1f3b4..0000000 --- a/features/sul3/sul3.md +++ /dev/null @@ -1,20 +0,0 @@ -# SUL3 - -Custom image to add a rewrite to apache that rewrites /.* to index.php - -docker-compose.override.yml: - -```yml -services: - mediawiki-web: - build: - dockerfile: features/sul3/sul3-apache.Dockerfile - context: . -``` - -Then run the following commands: - -```shell -docker compose --env-file config/.env -p main build mediawiki-web -mwutil recreate mediawiki-web -``` From d9179b257f1ff36ca979b33f050c79fca6476cab Mon Sep 17 00:00:00 2001 From: SomeRandomDeveloper Date: Tue, 3 Mar 2026 15:35:08 +0100 Subject: [PATCH 13/13] fix(config): lint issues --- config/Farm/StandaloneUserManagement.php | 9 +++++++-- config/MWCExtensions.php | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/config/Farm/StandaloneUserManagement.php b/config/Farm/StandaloneUserManagement.php index 5fb6774..74b9cb6 100644 --- a/config/Farm/StandaloneUserManagement.php +++ b/config/Farm/StandaloneUserManagement.php @@ -10,8 +10,13 @@ class StandaloneUserManagement implements IFarmUserManagement { /** @inheritDoc */ - function setup( MWCFarm $farm, MediaWikiConfig $mwc ): void { - // Don't do anything, this is already the default behaviour of MW + public function setup( MWCFarm $farm, MediaWikiConfig $mwc ): void { + // Don't do anything, this is already the default behavior of MW + } + + /** @inheritDoc */ + public function overrideWikiExists( MWCFarm $farm, MediaWikiConfig $mwc, string $subdomain ): ?string { + return null; } } diff --git a/config/MWCExtensions.php b/config/MWCExtensions.php index 85f47ff..44211b5 100644 --- a/config/MWCExtensions.php +++ b/config/MWCExtensions.php @@ -667,7 +667,7 @@ public function QuickInstantCommons( ?string $apiUrl = null, ?string $name = nul 'descriptionCacheExpiry' => 43200, 'transformVia404' => false, 'abbrvThreshold' => 160, - 'apiMetadataExpiry' => 60*60*24, + 'apiMetadataExpiry' => 60 * 60 * 24, 'disabledMediaHandlers' => [], ] ); }