{ config, pkgs, lib, ... }: let inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption; inherit (lib) concatStringsSep literalExample mapAttrsToList optional optionals optionalString types; cfg = config.services.mediawiki; fpm = config.services.phpfpm.pools.mediawiki; user = "mediawiki"; group = config.services.httpd.group; cacheDir = "/var/cache/mediawiki"; stateDir = "/var/lib/mediawiki"; pkg = pkgs.stdenv.mkDerivation rec { pname = "mediawiki-full"; version = src.version; src = cfg.package; installPhase = '' mkdir -p $out cp -r * $out/ rm -rf $out/share/mediawiki/skins/* rm -rf $out/share/mediawiki/extensions/* ${concatStringsSep "\n" (mapAttrsToList (k: v: '' ln -s ${v} $out/share/mediawiki/skins/${k} '') cfg.skins)} ${concatStringsSep "\n" (mapAttrsToList (k: v: '' ln -s ${v} $out/share/mediawiki/extensions/${k} '') cfg.extensions)} ''; }; mediawikiScripts = pkgs.runCommand "mediawiki-scripts" { buildInputs = [ pkgs.makeWrapper ]; preferLocalBuild = true; } '' mkdir -p $out/bin for i in changePassword.php createAndPromote.php userOptions.php edit.php nukePage.php update.php; do makeWrapper ${pkgs.php}/bin/php $out/bin/mediawiki-$(basename $i .php) \ --set MEDIAWIKI_CONFIG ${mediawikiConfig} \ --add-flags ${pkg}/share/mediawiki/maintenance/$i done ''; mediawikiConfig = pkgs.writeText "LocalSettings.php" '' database.user. ''; }; tablePrefix = mkOption { type = types.nullOr types.str; default = null; description = '' If you only have access to a single database and wish to install more than one version of MediaWiki, or have other applications that also use the database, you can give the table names a unique prefix to stop any naming conflicts or confusion. See . ''; }; socket = mkOption { type = types.nullOr types.path; default = if cfg.database.createLocally then "/run/mysqld/mysqld.sock" else null; defaultText = "/run/mysqld/mysqld.sock"; description = "Path to the unix socket file to use for authentication."; }; createLocally = mkOption { type = types.bool; default = cfg.database.type == "mysql"; defaultText = "true"; description = '' Create the database and database user locally. This currently only applies if database type "mysql" is selected. ''; }; }; virtualHost = mkOption { type = types.submodule ({ options = import ../web-servers/apache-httpd/per-server-options.nix { inherit lib; forMainServer = false; }; }); example = literalExample '' { hostName = "mediawiki.example.org"; enableSSL = true; adminAddr = "webmaster@example.org"; sslServerCert = "/var/lib/acme/mediawiki.example.org/full.pem"; sslServerKey = "/var/lib/acme/mediawiki.example.org/key.pem"; } ''; description = '' Apache configuration can be done by adapting . See for further information. ''; }; poolConfig = mkOption { type = with types; attrsOf (oneOf [ str int bool ]); default = { "pm" = "dynamic"; "pm.max_children" = 32; "pm.start_servers" = 2; "pm.min_spare_servers" = 2; "pm.max_spare_servers" = 4; "pm.max_requests" = 500; }; description = '' Options for the MediaWiki PHP pool. See the documentation on php-fpm.conf for details on configuration directives. ''; }; extraConfig = mkOption { type = types.lines; description = '' Any additional text to be appended to MediaWiki's LocalSettings.php configuration file. For configuration settings, see . ''; default = ""; example = '' $wgEnableEmail = false; ''; }; }; }; # implementation config = mkIf cfg.enable { assertions = [ { assertion = cfg.database.createLocally -> cfg.database.type == "mysql"; message = "services.mediawiki.createLocally is currently only supported for database type 'mysql'"; } { assertion = cfg.database.createLocally -> cfg.database.user == user; message = "services.mediawiki.database.user must be set to ${user} if services.mediawiki.database.createLocally is set true"; } { assertion = cfg.database.createLocally -> cfg.database.socket != null; message = "services.mediawiki.database.socket must be set if services.mediawiki.database.createLocally is set to true"; } { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null; message = "a password cannot be specified if services.mediawiki.database.createLocally is set to true"; } ]; services.mediawiki.skins = { MonoBook = "${cfg.package}/share/mediawiki/skins/MonoBook"; Timeless = "${cfg.package}/share/mediawiki/skins/Timeless"; Vector = "${cfg.package}/share/mediawiki/skins/Vector"; }; services.mysql = mkIf cfg.database.createLocally { enable = true; package = mkDefault pkgs.mariadb; ensureDatabases = [ cfg.database.name ]; ensureUsers = [ { name = cfg.database.user; ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; }; } ]; }; services.phpfpm.pools.mediawiki = { inherit user group; phpEnv.MEDIAWIKI_CONFIG = "${mediawikiConfig}"; settings = { "listen.owner" = config.services.httpd.user; "listen.group" = config.services.httpd.group; } // cfg.poolConfig; }; services.httpd = { enable = true; adminAddr = mkDefault cfg.virtualHost.adminAddr; extraModules = [ "proxy_fcgi" ]; virtualHosts = [ (mkMerge [ cfg.virtualHost { documentRoot = mkForce "${pkg}/share/mediawiki"; extraConfig = '' SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/" Require all granted DirectoryIndex index.php AllowOverride All '' + optionalString (cfg.uploadsDir != null) '' Alias "/images" "${cfg.uploadsDir}" Require all granted ''; } ]) ]; }; systemd.tmpfiles.rules = [ "d '${stateDir}' 0750 ${user} ${group} - -" "d '${cacheDir}' 0750 ${user} ${group} - -" ] ++ optionals (cfg.uploadsDir != null) [ "d '${cfg.uploadsDir}' 0750 ${user} ${group} - -" "Z '${cfg.uploadsDir}' 0750 ${user} ${group} - -" ]; systemd.services.mediawiki-init = { wantedBy = [ "multi-user.target" ]; before = [ "phpfpm-mediawiki.service" ]; after = optional cfg.database.createLocally "mysql.service"; script = '' if ! test -e "${stateDir}/secret.key"; then tr -dc A-Za-z0-9 /dev/null | head -c 64 > ${stateDir}/secret.key fi echo "exit( wfGetDB( DB_MASTER )->tableExists( 'user' ) ? 1 : 0 );" | \ ${pkgs.php}/bin/php ${pkg}/share/mediawiki/maintenance/eval.php --conf ${mediawikiConfig} && \ ${pkgs.php}/bin/php ${pkg}/share/mediawiki/maintenance/install.php \ --confpath /tmp \ --scriptpath / \ --dbserver ${cfg.database.host}${optionalString (cfg.database.socket != null) ":${cfg.database.socket}"} \ --dbport ${toString cfg.database.port} \ --dbname ${cfg.database.name} \ ${optionalString (cfg.database.tablePrefix != null) "--dbprefix ${cfg.database.tablePrefix}"} \ --dbuser ${cfg.database.user} \ ${optionalString (cfg.database.passwordFile != null) "--dbpassfile ${cfg.database.passwordFile}"} \ --passfile ${cfg.passwordFile} \ ${cfg.name} \ admin ${pkgs.php}/bin/php ${pkg}/share/mediawiki/maintenance/update.php --conf ${mediawikiConfig} --quick ''; serviceConfig = { Type = "oneshot"; User = user; Group = group; PrivateTmp = true; }; }; systemd.services.httpd.after = optional (cfg.database.createLocally && cfg.database.type == "mysql") "mysql.service"; users.users.${user}.group = group; environment.systemPackages = [ mediawikiScripts ]; }; }