1{
2 config,
3 pkgs,
4 lib,
5 ...
6}:
7
8let
9
10 inherit (lib)
11 mkDefault
12 mkEnableOption
13 mkPackageOption
14 mkForce
15 mkIf
16 mkMerge
17 mkOption
18 ;
19 inherit (lib)
20 concatStringsSep
21 literalExpression
22 mapAttrsToList
23 optional
24 optionals
25 optionalString
26 types
27 ;
28
29 cfg = config.services.mediawiki;
30 fpm = config.services.phpfpm.pools.mediawiki;
31 user = "mediawiki";
32 group =
33 if cfg.webserver == "apache" then
34 config.services.httpd.group
35 else if cfg.webserver == "nginx" then
36 config.services.nginx.group
37 else
38 "mediawiki";
39
40 cacheDir = "/var/cache/mediawiki";
41 stateDir = "/var/lib/mediawiki";
42
43 # https://www.mediawiki.org/wiki/Compatibility
44 php = pkgs.php82;
45
46 pkg = pkgs.stdenv.mkDerivation rec {
47 pname = "mediawiki-full";
48 inherit (src) version;
49 src = cfg.package;
50
51 installPhase = ''
52 mkdir -p $out
53 cp -r * $out/
54
55 # try removing directories before symlinking to allow overwriting any builtin extension or skin
56 ${concatStringsSep "\n" (
57 mapAttrsToList (k: v: ''
58 rm -rf $out/share/mediawiki/skins/${k}
59 ln -s ${v} $out/share/mediawiki/skins/${k}
60 '') cfg.skins
61 )}
62
63 ${concatStringsSep "\n" (
64 mapAttrsToList (k: v: ''
65 rm -rf $out/share/mediawiki/extensions/${k}
66 ln -s ${
67 if v != null then v else "$src/share/mediawiki/extensions/${k}"
68 } $out/share/mediawiki/extensions/${k}
69 '') cfg.extensions
70 )}
71 '';
72 };
73
74 mediawikiScripts =
75 pkgs.runCommand "mediawiki-scripts"
76 {
77 nativeBuildInputs = [ pkgs.makeWrapper ];
78 preferLocalBuild = true;
79 }
80 ''
81 mkdir -p $out/bin
82 makeWrapper ${php}/bin/php $out/bin/mediawiki-maintenance \
83 --set MEDIAWIKI_CONFIG ${mediawikiConfig} \
84 --add-flags ${pkg}/share/mediawiki/maintenance/run.php
85
86 for i in changePassword createAndPromote deleteUserEmail resetUserEmail userOptions edit nukePage update importDump run; do
87 script="$out/bin/mediawiki-$i"
88 cat <<'EOF' >"$script"
89 #!${pkgs.runtimeShell}
90 become=(exec)
91 if [[ "$(id -u)" != ${user} ]]; then
92 become=(exec /run/wrappers/bin/sudo -u ${user} --)
93 fi
94 "${"$"}{become[@]}" ${placeholder "out"}/bin/mediawiki-maintenance \
95 EOF
96 if [[ "$i" != "run" ]]; then
97 echo " ${pkg}/share/mediawiki/maintenance/$i.php \"\$@\"" >>"$script"
98 else
99 echo " ${pkg}/share/mediawiki/maintenance/\$1.php \"\''${@:2}\"" >>"$script"
100 fi
101 chmod +x "$script"
102 done
103 '';
104
105 dbAddr =
106 if cfg.database.socket == null then
107 "${cfg.database.host}:${toString cfg.database.port}"
108 else if cfg.database.type == "mysql" then
109 "${cfg.database.host}:${cfg.database.socket}"
110 else if cfg.database.type == "postgres" then
111 "${cfg.database.socket}"
112 else
113 throw "Unsupported database type: ${cfg.database.type} for socket: ${cfg.database.socket}";
114
115 mediawikiConfig = pkgs.writeTextFile {
116 name = "LocalSettings.php";
117 checkPhase = ''
118 ${php}/bin/php --syntax-check "$target"
119 '';
120 text = ''
121 <?php
122 # Protect against web entry
123 if ( !defined( 'MEDIAWIKI' ) ) {
124 exit;
125 }
126
127 $wgSitename = "${cfg.name}";
128 $wgMetaNamespace = false;
129
130 ## The URL base path to the directory containing the wiki;
131 ## defaults for all runtime URL paths are based off of this.
132 ## For more information on customizing the URLs
133 ## (like /w/index.php/Page_title to /wiki/Page_title) please see:
134 ## https://www.mediawiki.org/wiki/Manual:Short_URL
135 $wgScriptPath = "${lib.optionalString (cfg.webserver == "nginx") "/w"}";
136
137 ## The protocol and server name to use in fully-qualified URLs
138 $wgServer = "${cfg.url}";
139
140 ## The URL path to static resources (images, scripts, etc.)
141 $wgResourceBasePath = $wgScriptPath;
142
143 ${lib.optionalString (cfg.webserver == "nginx") ''
144 $wgArticlePath = "/wiki/$1";
145 $wgUsePathInfo = true;
146 ''}
147
148 ## The URL path to the logo. Make sure you change this from the default,
149 ## or else you'll overwrite your logo when you upgrade!
150 $wgLogo = "$wgResourceBasePath/resources/assets/wiki.png";
151
152 ## UPO means: this is also a user preference option
153
154 $wgEnableEmail = true;
155 $wgEnableUserEmail = true; # UPO
156
157 $wgPasswordSender = "${cfg.passwordSender}";
158
159 $wgEnotifUserTalk = false; # UPO
160 $wgEnotifWatchlist = false; # UPO
161 $wgEmailAuthentication = true;
162
163 ## Database settings
164 $wgDBtype = "${cfg.database.type}";
165 $wgDBserver = "${dbAddr}";
166 $wgDBport = "${toString cfg.database.port}";
167 $wgDBname = "${cfg.database.name}";
168 $wgDBuser = "${cfg.database.user}";
169 ${optionalString (
170 cfg.database.passwordFile != null
171 ) "$wgDBpassword = file_get_contents(\"${cfg.database.passwordFile}\");"}
172
173 ${optionalString (cfg.database.type == "mysql" && cfg.database.tablePrefix != null) ''
174 # MySQL specific settings
175 $wgDBprefix = "${cfg.database.tablePrefix}";
176 ''}
177
178 ${optionalString (cfg.database.type == "mysql") ''
179 # MySQL table options to use during installation or update
180 $wgDBTableOptions = "ENGINE=InnoDB, DEFAULT CHARSET=binary";
181 ''}
182
183 ## Shared memory settings
184 $wgMainCacheType = CACHE_NONE;
185 $wgMemCachedServers = [];
186
187 ${optionalString (cfg.uploadsDir != null) ''
188 $wgEnableUploads = true;
189 $wgUploadDirectory = "${cfg.uploadsDir}";
190 ''}
191
192 $wgUseImageMagick = true;
193 $wgImageMagickConvertCommand = "${pkgs.imagemagick}/bin/convert";
194
195 # InstantCommons allows wiki to use images from https://commons.wikimedia.org
196 $wgUseInstantCommons = false;
197
198 # Periodically send a pingback to https://www.mediawiki.org/ with basic data
199 # about this MediaWiki instance. The Wikimedia Foundation shares this data
200 # with MediaWiki developers to help guide future development efforts.
201 $wgPingback = true;
202
203 ## If you use ImageMagick (or any other shell command) on a
204 ## Linux server, this will need to be set to the name of an
205 ## available UTF-8 locale
206 $wgShellLocale = "C.UTF-8";
207
208 ## Set $wgCacheDirectory to a writable directory on the web server
209 ## to make your wiki go slightly faster. The directory should not
210 ## be publicly accessible from the web.
211 $wgCacheDirectory = "${cacheDir}";
212
213 # Site language code, should be one of the list in ./languages/data/Names.php
214 $wgLanguageCode = "en";
215
216 $wgSecretKey = file_get_contents("${stateDir}/secret.key");
217
218 # Changing this will log out all existing sessions.
219 $wgAuthenticationTokenVersion = "";
220
221 ## For attaching licensing metadata to pages, and displaying an
222 ## appropriate copyright notice / icon. GNU Free Documentation
223 ## License and Creative Commons licenses are supported so far.
224 $wgRightsPage = ""; # Set to the title of a wiki page that describes your license/copyright
225 $wgRightsUrl = "";
226 $wgRightsText = "";
227 $wgRightsIcon = "";
228
229 # Path to the GNU diff3 utility. Used for conflict resolution.
230 $wgDiff = "${pkgs.diffutils}/bin/diff";
231 $wgDiff3 = "${pkgs.diffutils}/bin/diff3";
232
233 # Enabled skins.
234 ${concatStringsSep "\n" (mapAttrsToList (k: v: "wfLoadSkin('${k}');") cfg.skins)}
235
236 # Enabled extensions.
237 ${concatStringsSep "\n" (mapAttrsToList (k: v: "wfLoadExtension('${k}');") cfg.extensions)}
238
239
240 # End of automatically generated settings.
241 # Add more configuration options below.
242
243 ${cfg.extraConfig}
244 '';
245 };
246
247 withTrailingSlash = str: if lib.hasSuffix "/" str then str else "${str}/";
248in
249{
250 # interface
251 options = {
252 services.mediawiki = {
253
254 enable = mkEnableOption "MediaWiki";
255
256 package = mkPackageOption pkgs "mediawiki" { };
257
258 finalPackage = mkOption {
259 type = types.package;
260 readOnly = true;
261 default = pkg;
262 defaultText = literalExpression "pkg";
263 description = ''
264 The final package used by the module. This is the package that will have extensions and skins installed.
265 '';
266 };
267
268 name = mkOption {
269 type = types.str;
270 default = "MediaWiki";
271 example = "Foobar Wiki";
272 description = "Name of the wiki.";
273 };
274
275 url = mkOption {
276 type = types.str;
277 default =
278 if cfg.webserver == "apache" then
279 "${
280 if
281 cfg.httpd.virtualHost.addSSL || cfg.httpd.virtualHost.forceSSL || cfg.httpd.virtualHost.onlySSL
282 then
283 "https"
284 else
285 "http"
286 }://${cfg.httpd.virtualHost.hostName}"
287 else if cfg.webserver == "nginx" then
288 let
289 hasSSL = host: host.forceSSL || host.addSSL;
290 in
291 "${
292 if hasSSL config.services.nginx.virtualHosts.${cfg.nginx.hostName} then "https" else "http"
293 }://${cfg.nginx.hostName}"
294 else
295 "http://localhost";
296 defaultText = ''
297 if "mediawiki uses ssl" then "{"https" else "http"}://''${cfg.hostName}" else "http://localhost";
298 '';
299 example = "https://wiki.example.org";
300 description = "URL of the wiki.";
301 };
302
303 uploadsDir = mkOption {
304 type = types.nullOr types.path;
305 default = "${stateDir}/uploads";
306 description = ''
307 This directory is used for uploads of pictures. The directory passed here is automatically
308 created and permissions adjusted as required.
309 '';
310 };
311
312 passwordFile = mkOption {
313 type = types.path;
314 description = ''
315 A file containing the initial password for the administrator account "admin".
316 '';
317 example = "/run/keys/mediawiki-password";
318 };
319
320 passwordSender = mkOption {
321 type = types.str;
322 default =
323 if cfg.webserver == "apache" then
324 if cfg.httpd.virtualHost.adminAddr != null then
325 cfg.httpd.virtualHost.adminAddr
326 else
327 config.services.httpd.adminAddr
328 else
329 "root@localhost";
330 defaultText = literalExpression ''
331 if cfg.webserver == "apache" then
332 if cfg.httpd.virtualHost.adminAddr != null then
333 cfg.httpd.virtualHost.adminAddr
334 else
335 config.services.httpd.adminAddr else "root@localhost"
336 '';
337 description = "Contact address for password reset.";
338 };
339
340 skins = mkOption {
341 default = { };
342 type = types.attrsOf types.path;
343 description = ''
344 Attribute set of paths whose content is copied to the {file}`skins`
345 subdirectory of the MediaWiki installation in addition to the default skins.
346 '';
347 };
348
349 extensions = mkOption {
350 default = { };
351 type = types.attrsOf (types.nullOr types.path);
352 description = ''
353 Attribute set of paths whose content is copied to the {file}`extensions`
354 subdirectory of the MediaWiki installation and enabled in configuration.
355
356 Use `null` instead of path to enable extensions that are part of MediaWiki.
357 '';
358 example = literalExpression ''
359 {
360 Matomo = pkgs.fetchzip {
361 url = "https://github.com/DaSchTour/matomo-mediawiki-extension/archive/v4.0.1.tar.gz";
362 sha256 = "0g5rd3zp0avwlmqagc59cg9bbkn3r7wx7p6yr80s644mj6dlvs1b";
363 };
364 ParserFunctions = null;
365 }
366 '';
367 };
368
369 webserver = mkOption {
370 type = types.enum [
371 "apache"
372 "none"
373 "nginx"
374 ];
375 default = "apache";
376 description = "Webserver to use.";
377 };
378
379 database = {
380 type = mkOption {
381 type = types.enum [
382 "mysql"
383 "postgres"
384 "mssql"
385 "oracle"
386 ];
387 default = "mysql";
388 description = "Database engine to use. MySQL/MariaDB is the database of choice by MediaWiki developers.";
389 };
390
391 host = mkOption {
392 type = types.str;
393 default = "localhost";
394 description = "Database host address.";
395 };
396
397 port = mkOption {
398 type = types.port;
399 default = if cfg.database.type == "mysql" then 3306 else 5432;
400 defaultText = literalExpression "3306";
401 description = "Database host port.";
402 };
403
404 name = mkOption {
405 type = types.str;
406 default = "mediawiki";
407 description = "Database name.";
408 };
409
410 user = mkOption {
411 type = types.str;
412 default = "mediawiki";
413 description = "Database user.";
414 };
415
416 passwordFile = mkOption {
417 type = types.nullOr types.path;
418 default = null;
419 example = "/run/keys/mediawiki-dbpassword";
420 description = ''
421 A file containing the password corresponding to
422 {option}`database.user`.
423 '';
424 };
425
426 tablePrefix = mkOption {
427 type = types.nullOr types.str;
428 default = null;
429 description = ''
430 If you only have access to a single database and wish to install more than
431 one version of MediaWiki, or have other applications that also use the
432 database, you can give the table names a unique prefix to stop any naming
433 conflicts or confusion.
434 See <https://www.mediawiki.org/wiki/Manual:$wgDBprefix>.
435 '';
436 };
437
438 socket = mkOption {
439 type = types.nullOr types.path;
440 default =
441 if (cfg.database.type == "mysql" && cfg.database.createLocally) then
442 "/run/mysqld/mysqld.sock"
443 else if (cfg.database.type == "postgres" && cfg.database.createLocally) then
444 "/run/postgresql"
445 else
446 null;
447 defaultText = literalExpression "/run/mysqld/mysqld.sock";
448 description = "Path to the unix socket file to use for authentication.";
449 };
450
451 createLocally = mkOption {
452 type = types.bool;
453 default = cfg.database.type == "mysql" || cfg.database.type == "postgres";
454 defaultText = literalExpression "true";
455 description = ''
456 Create the database and database user locally.
457 This currently only applies if database type "mysql" is selected.
458 '';
459 };
460 };
461
462 nginx.hostName = mkOption {
463 type = types.str;
464 example = literalExpression ''wiki.example.com'';
465 default = "localhost";
466 description = ''
467 The hostname to use for the nginx virtual host.
468 This is used to generate the nginx configuration.
469 '';
470 };
471
472 httpd.virtualHost = mkOption {
473 type = types.submodule (import ../web-servers/apache-httpd/vhost-options.nix);
474 example = literalExpression ''
475 {
476 hostName = "mediawiki.example.org";
477 adminAddr = "webmaster@example.org";
478 forceSSL = true;
479 enableACME = true;
480 }
481 '';
482 description = ''
483 Apache configuration can be done by adapting {option}`services.httpd.virtualHosts`.
484 See [](#opt-services.httpd.virtualHosts) for further information.
485 '';
486 };
487
488 poolConfig = mkOption {
489 type =
490 with types;
491 attrsOf (oneOf [
492 str
493 int
494 bool
495 ]);
496 default = {
497 "pm" = "dynamic";
498 "pm.max_children" = 32;
499 "pm.start_servers" = 2;
500 "pm.min_spare_servers" = 2;
501 "pm.max_spare_servers" = 4;
502 "pm.max_requests" = 500;
503 };
504 description = ''
505 Options for the MediaWiki PHP pool. See the documentation on `php-fpm.conf`
506 for details on configuration directives.
507 '';
508 };
509
510 extraConfig = mkOption {
511 type = types.lines;
512 description = ''
513 Any additional text to be appended to MediaWiki's
514 LocalSettings.php configuration file. For configuration
515 settings, see <https://www.mediawiki.org/wiki/Manual:Configuration_settings>.
516 '';
517 default = "";
518 example = ''
519 $wgEnableEmail = false;
520 '';
521 };
522
523 };
524 };
525
526 imports = [
527 (lib.mkRenamedOptionModule
528 [ "services" "mediawiki" "virtualHost" ]
529 [ "services" "mediawiki" "httpd" "virtualHost" ]
530 )
531 ];
532
533 # implementation
534 config = mkIf cfg.enable {
535
536 assertions = [
537 {
538 assertion =
539 cfg.database.createLocally -> (cfg.database.type == "mysql" || cfg.database.type == "postgres");
540 message = "services.mediawiki.createLocally is currently only supported for database type 'mysql' and 'postgres'";
541 }
542 {
543 assertion =
544 cfg.database.createLocally -> cfg.database.user == user && cfg.database.name == cfg.database.user;
545 message = "services.mediawiki.database.user must be set to ${user} if services.mediawiki.database.createLocally is set true";
546 }
547 {
548 assertion = cfg.database.createLocally -> cfg.database.socket != null;
549 message = "services.mediawiki.database.socket must be set if services.mediawiki.database.createLocally is set to true";
550 }
551 {
552 assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
553 message = "a password cannot be specified if services.mediawiki.database.createLocally is set to true";
554 }
555 ];
556
557 services.mediawiki.skins = {
558 MonoBook = "${cfg.package}/share/mediawiki/skins/MonoBook";
559 Timeless = "${cfg.package}/share/mediawiki/skins/Timeless";
560 Vector = "${cfg.package}/share/mediawiki/skins/Vector";
561 };
562
563 services.mysql = mkIf (cfg.database.type == "mysql" && cfg.database.createLocally) {
564 enable = true;
565 package = mkDefault pkgs.mariadb;
566 ensureDatabases = [ cfg.database.name ];
567 ensureUsers = [
568 {
569 name = cfg.database.user;
570 ensurePermissions = {
571 "${cfg.database.name}.*" = "ALL PRIVILEGES";
572 };
573 }
574 ];
575 };
576
577 services.postgresql = mkIf (cfg.database.type == "postgres" && cfg.database.createLocally) {
578 enable = true;
579 ensureDatabases = [ cfg.database.name ];
580 ensureUsers = [
581 {
582 name = cfg.database.user;
583 ensureDBOwnership = true;
584 }
585 ];
586 };
587
588 services.phpfpm.pools.mediawiki = {
589 inherit user group;
590 phpEnv.MEDIAWIKI_CONFIG = "${mediawikiConfig}";
591 phpPackage = php;
592 settings =
593 (
594 if (cfg.webserver == "apache") then
595 {
596 "listen.owner" = config.services.httpd.user;
597 "listen.group" = config.services.httpd.group;
598 }
599 else if (cfg.webserver == "nginx") then
600 {
601 "listen.owner" = config.services.nginx.user;
602 "listen.group" = config.services.nginx.group;
603 }
604 else
605 {
606 "listen.owner" = user;
607 "listen.group" = group;
608 }
609 )
610 // cfg.poolConfig;
611 };
612
613 services.httpd = lib.mkIf (cfg.webserver == "apache") {
614 enable = true;
615 extraModules = [ "proxy_fcgi" ];
616 virtualHosts.${cfg.httpd.virtualHost.hostName} = mkMerge [
617 cfg.httpd.virtualHost
618 {
619 documentRoot = mkForce "${pkg}/share/mediawiki";
620 extraConfig = ''
621 <Directory "${pkg}/share/mediawiki">
622 <FilesMatch "\.php$">
623 <If "-f %{REQUEST_FILENAME}">
624 SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/"
625 </If>
626 </FilesMatch>
627
628 Require all granted
629 DirectoryIndex index.php
630 AllowOverride All
631 </Directory>
632 ''
633 + optionalString (cfg.uploadsDir != null) ''
634 Alias "/images" "${cfg.uploadsDir}"
635 <Directory "${cfg.uploadsDir}">
636 Require all granted
637 </Directory>
638 '';
639 }
640 ];
641 };
642 # inspired by https://www.mediawiki.org/wiki/Manual:Short_URL/Nginx
643 services.nginx = lib.mkIf (cfg.webserver == "nginx") {
644 enable = true;
645 virtualHosts.${config.services.mediawiki.nginx.hostName} = {
646 root = "${pkg}/share/mediawiki";
647 locations = {
648 "~ ^/w/(index|load|api|thumb|opensearch_desc|rest|img_auth)\\.php$".extraConfig = ''
649 rewrite ^/w/(.*) /$1 break;
650 include ${config.services.nginx.package}/conf/fastcgi.conf;
651 fastcgi_index index.php;
652 fastcgi_pass unix:${config.services.phpfpm.pools.mediawiki.socket};
653 '';
654 "/w/images/".alias = withTrailingSlash cfg.uploadsDir;
655 # Deny access to deleted images folder
656 "/w/images/deleted".extraConfig = ''
657 deny all;
658 '';
659 # MediaWiki assets (usually images)
660 "~ ^/w/resources/(assets|lib|src)".extraConfig = ''
661 rewrite ^/w(/.*) $1 break;
662 add_header Cache-Control "public";
663 expires 7d;
664 '';
665 # Assets, scripts and styles from skins and extensions
666 "~ ^/w/(skins|extensions)/.+\\.(css|js|gif|jpg|jpeg|png|svg|wasm|ttf|woff|woff2)$".extraConfig = ''
667 rewrite ^/w(/.*) $1 break;
668 add_header Cache-Control "public";
669 expires 7d;
670 '';
671
672 # Handling for Mediawiki REST API, see [[mw:API:REST_API]]
673 "/w/rest.php/".tryFiles = "$uri $uri/ /w/rest.php?$query_string";
674
675 # Handling for the article path (pretty URLs)
676 "/wiki/".extraConfig = ''
677 rewrite ^/wiki/(?<pagename>.*)$ /w/index.php;
678 '';
679
680 # Explicit access to the root website, redirect to main page (adapt as needed)
681 "= /".extraConfig = ''
682 return 301 /wiki/;
683 '';
684
685 # Every other entry point will be disallowed.
686 # Add specific rules for other entry points/images as needed above this
687 "/".extraConfig = ''
688 return 404;
689 '';
690 };
691 };
692 };
693
694 systemd.tmpfiles.rules = [
695 "d '${stateDir}' 0750 ${user} ${group} - -"
696 "d '${cacheDir}' 0750 ${user} ${group} - -"
697 ]
698 ++ optionals (cfg.uploadsDir != null) [
699 "d '${cfg.uploadsDir}' 0750 ${user} ${group} - -"
700 "Z '${cfg.uploadsDir}' 0750 ${user} ${group} - -"
701 ];
702
703 systemd.services.mediawiki-init = {
704 wantedBy = [ "multi-user.target" ];
705 before = [ "phpfpm-mediawiki.service" ];
706 after =
707 optional (cfg.database.type == "mysql" && cfg.database.createLocally) "mysql.service"
708 ++ optional (cfg.database.type == "postgres" && cfg.database.createLocally) "postgresql.target";
709 script = ''
710 if ! test -e "${stateDir}/secret.key"; then
711 tr -dc A-Za-z0-9 </dev/urandom 2>/dev/null | head -c 64 > ${stateDir}/secret.key
712 fi
713
714 echo "exit( \$this->getPrimaryDB()->tableExists( 'user' ) ? 1 : 0 );" | \
715 ${php}/bin/php ${pkg}/share/mediawiki/maintenance/run.php eval --conf ${mediawikiConfig} && \
716 ${php}/bin/php ${pkg}/share/mediawiki/maintenance/install.php \
717 --confpath /tmp \
718 --scriptpath / \
719 --dbserver ${lib.escapeShellArg dbAddr} \
720 --dbport ${toString cfg.database.port} \
721 --dbname ${lib.escapeShellArg cfg.database.name} \
722 ${
723 optionalString (
724 cfg.database.tablePrefix != null
725 ) "--dbprefix ${lib.escapeShellArg cfg.database.tablePrefix}"
726 } \
727 --dbuser ${lib.escapeShellArg cfg.database.user} \
728 ${
729 optionalString (
730 cfg.database.passwordFile != null
731 ) "--dbpassfile ${lib.escapeShellArg cfg.database.passwordFile}"
732 } \
733 --passfile ${lib.escapeShellArg cfg.passwordFile} \
734 --dbtype ${cfg.database.type} \
735 ${lib.escapeShellArg cfg.name} \
736 admin
737
738 ${php}/bin/php ${pkg}/share/mediawiki/maintenance/update.php --conf ${mediawikiConfig} --quick --skip-external-dependencies
739 '';
740
741 serviceConfig = {
742 Type = "oneshot";
743 User = user;
744 Group = group;
745 PrivateTmp = true;
746 };
747 };
748
749 systemd.services.httpd.after =
750 optional (
751 cfg.webserver == "apache" && cfg.database.createLocally && cfg.database.type == "mysql"
752 ) "mysql.service"
753 ++ optional (
754 cfg.webserver == "apache" && cfg.database.createLocally && cfg.database.type == "postgres"
755 ) "postgresql.target";
756
757 users.users.${user} = {
758 inherit group;
759 isSystemUser = true;
760 };
761 users.groups.${group} = { };
762
763 environment.systemPackages = [ mediawikiScripts ];
764 };
765}