1{ config, lib, pkgs, serverInfo, php, ... }:
2
3with lib;
4
5let
6
7 httpd = serverInfo.serverConfig.package;
8
9 version24 = !versionOlder httpd.version "2.4";
10
11 allGranted = if version24 then ''
12 Require all granted
13 '' else ''
14 Order allow,deny
15 Allow from all
16 '';
17
18 mediawikiConfig = pkgs.writeText "LocalSettings.php"
19 ''
20 <?php
21 # Copied verbatim from the default (generated) LocalSettings.php.
22 if( defined( 'MW_INSTALL_PATH' ) ) {
23 $IP = MW_INSTALL_PATH;
24 } else {
25 $IP = dirname( __FILE__ );
26 }
27
28 $path = array( $IP, "$IP/includes", "$IP/languages" );
29 set_include_path( implode( PATH_SEPARATOR, $path ) . PATH_SEPARATOR . get_include_path() );
30
31 require_once( "$IP/includes/DefaultSettings.php" );
32
33 if ( $wgCommandLineMode ) {
34 if ( isset( $_SERVER ) && array_key_exists( 'REQUEST_METHOD', $_SERVER ) ) {
35 die( "This script must be run from the command line\n" );
36 }
37 }
38
39 $wgScriptPath = "${config.urlPrefix}";
40
41 # We probably need to set $wgSecretKey and $wgCacheEpoch.
42
43 # Paths to external programs.
44 $wgDiff3 = "${pkgs.diffutils}/bin/diff3";
45 $wgDiff = "${pkgs.diffutils}/bin/diff";
46 $wgImageMagickConvertCommand = "${pkgs.imagemagick.out}/bin/convert";
47
48 #$wgDebugLogFile = "/tmp/mediawiki_debug_log.txt";
49
50 # Database configuration.
51 $wgDBtype = "${config.dbType}";
52 $wgDBserver = "${config.dbServer}";
53 $wgDBuser = "${config.dbUser}";
54 $wgDBpassword = "${config.dbPassword}";
55 $wgDBname = "${config.dbName}";
56
57 # E-mail.
58 $wgEmergencyContact = "${config.emergencyContact}";
59 $wgPasswordSender = "${config.passwordSender}";
60
61 $wgSitename = "${config.siteName}";
62
63 ${optionalString (config.logo != "") ''
64 $wgLogo = "${config.logo}";
65 ''}
66
67 ${optionalString (config.articleUrlPrefix != "") ''
68 $wgArticlePath = "${config.articleUrlPrefix}/$1";
69 ''}
70
71 ${optionalString config.enableUploads ''
72 $wgEnableUploads = true;
73 $wgUploadDirectory = "${config.uploadDir}";
74 ''}
75
76 ${optionalString (config.defaultSkin != "") ''
77 $wgDefaultSkin = "${config.defaultSkin}";
78 ''}
79
80 ${config.extraConfig}
81 ?>
82 '';
83
84 # Unpack Mediawiki and put the config file in its root directory.
85 mediawikiRoot = pkgs.stdenv.mkDerivation rec {
86 name= "mediawiki-1.27.3";
87
88 src = pkgs.fetchurl {
89 url = "http://download.wikimedia.org/mediawiki/1.27/${name}.tar.gz";
90 sha256 = "08x8mvc0y1gwq8rg0zm98wc6hc5j8imb6dcpx6s7392j5dc71m0i";
91 };
92
93 skins = config.skins;
94 extensions = config.extensions;
95
96 buildPhase =
97 ''
98 for skin in $skins; do
99 cp -prvd $skin/* skins/
100 done
101 for extension in $extensions; do
102 cp -prvd $extension/* extensions/
103 done
104 ''; # */
105
106 installPhase =
107 ''
108 mkdir -p $out
109 cp -r * $out
110 cp ${mediawikiConfig} $out/LocalSettings.php
111 sed -i \
112 -e 's|/bin/bash|${pkgs.bash}/bin/bash|g' \
113 -e 's|/usr/bin/timeout|${pkgs.coreutils}/bin/timeout|g' \
114 $out/includes/limit.sh \
115 $out/includes/GlobalFunctions.php
116 '';
117 };
118
119 mediawikiScripts = pkgs.runCommand "mediawiki-${config.id}-scripts"
120 { buildInputs = [ pkgs.makeWrapper ]; }
121 ''
122 mkdir -p $out/bin
123 for i in changePassword.php createAndPromote.php userOptions.php edit.php nukePage.php update.php; do
124 makeWrapper ${php}/bin/php $out/bin/mediawiki-${config.id}-$(basename $i .php) \
125 --add-flags ${mediawikiRoot}/maintenance/$i
126 done
127 '';
128
129in
130
131{
132
133 extraConfig =
134 ''
135 ${optionalString config.enableUploads ''
136 Alias ${config.urlPrefix}/images ${config.uploadDir}
137
138 <Directory ${config.uploadDir}>
139 ${allGranted}
140 Options -Indexes
141 </Directory>
142 ''}
143
144 ${if config.urlPrefix != "" then "Alias ${config.urlPrefix} ${mediawikiRoot}" else ''
145 RewriteEngine On
146 RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
147 RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-d
148 ${concatMapStringsSep "\n" (u: "RewriteCond %{REQUEST_URI} !^${u.urlPath}") serverInfo.vhostConfig.servedDirs}
149 ${concatMapStringsSep "\n" (u: "RewriteCond %{REQUEST_URI} !^${u.urlPath}") serverInfo.vhostConfig.servedFiles}
150 RewriteRule ${if config.enableUploads
151 then "!^/images"
152 else "^.*\$"
153 } %{DOCUMENT_ROOT}/${if config.articleUrlPrefix == ""
154 then ""
155 else "${config.articleUrlPrefix}/"
156 }index.php [L]
157 ''}
158
159 <Directory ${mediawikiRoot}>
160 ${allGranted}
161 DirectoryIndex index.php
162 </Directory>
163
164 ${optionalString (config.articleUrlPrefix != "") ''
165 Alias ${config.articleUrlPrefix} ${mediawikiRoot}/index.php
166 ''}
167 '';
168
169 documentRoot = if config.urlPrefix == "" then mediawikiRoot else null;
170
171 enablePHP = true;
172
173 options = {
174
175 id = mkOption {
176 default = "main";
177 description = ''
178 A unique identifier necessary to keep multiple MediaWiki server
179 instances on the same machine apart. This is used to
180 disambiguate the administrative scripts, which get names like
181 mediawiki-$id-change-password.
182 '';
183 };
184
185 dbType = mkOption {
186 default = "postgres";
187 example = "mysql";
188 description = "Database type.";
189 };
190
191 dbName = mkOption {
192 default = "mediawiki";
193 description = "Name of the database that holds the MediaWiki data.";
194 };
195
196 dbServer = mkOption {
197 default = ""; # use a Unix domain socket
198 example = "10.0.2.2";
199 description = ''
200 The location of the database server. Leave empty to use a
201 database server running on the same machine through a Unix
202 domain socket.
203 '';
204 };
205
206 dbUser = mkOption {
207 default = "mediawiki";
208 description = "The user name for accessing the database.";
209 };
210
211 dbPassword = mkOption {
212 default = "";
213 example = "foobar";
214 description = ''
215 The password of the database user. Warning: this is stored in
216 cleartext in the Nix store!
217 '';
218 };
219
220 emergencyContact = mkOption {
221 default = serverInfo.serverConfig.adminAddr;
222 example = "admin@example.com";
223 description = ''
224 Emergency contact e-mail address. Defaults to the Apache
225 admin address.
226 '';
227 };
228
229 passwordSender = mkOption {
230 default = serverInfo.serverConfig.adminAddr;
231 example = "password@example.com";
232 description = ''
233 E-mail address from which password confirmations originate.
234 Defaults to the Apache admin address.
235 '';
236 };
237
238 siteName = mkOption {
239 default = "MediaWiki";
240 example = "Foobar Wiki";
241 description = "Name of the wiki";
242 };
243
244 logo = mkOption {
245 default = "";
246 example = "/images/logo.png";
247 description = "The URL of the site's logo (which should be a 135x135px image).";
248 };
249
250 urlPrefix = mkOption {
251 default = "/w";
252 description = ''
253 The URL prefix under which the Mediawiki service appears.
254 '';
255 };
256
257 articleUrlPrefix = mkOption {
258 default = "/wiki";
259 example = "";
260 description = ''
261 The URL prefix under which article pages appear,
262 e.g. http://server/wiki/Page. Leave empty to use the main URL
263 prefix, e.g. http://server/w/index.php?title=Page.
264 '';
265 };
266
267 enableUploads = mkOption {
268 default = false;
269 description = "Whether to enable file uploads.";
270 };
271
272 uploadDir = mkOption {
273 default = throw "You must specify `uploadDir'.";
274 example = "/data/mediawiki-upload";
275 description = "The directory that stores uploaded files.";
276 };
277
278 defaultSkin = mkOption {
279 default = "";
280 example = "nostalgia";
281 description = "Set this value to change the default skin used by MediaWiki.";
282 };
283
284 skins = mkOption {
285 default = [];
286 type = types.listOf types.path;
287 description =
288 ''
289 List of paths whose content is copied to the ‘skins’
290 subdirectory of the MediaWiki installation.
291 '';
292 };
293
294 extensions = mkOption {
295 default = [];
296 type = types.listOf types.path;
297 description =
298 ''
299 List of paths whose content is copied to the 'extensions'
300 subdirectory of the MediaWiki installation.
301 '';
302 };
303
304 extraConfig = mkOption {
305 type = types.lines;
306 default = "";
307 example =
308 ''
309 $wgEnableEmail = false;
310 '';
311 description = ''
312 Any additional text to be appended to MediaWiki's
313 configuration file. This is a PHP script. For configuration
314 settings, see <link xlink:href='http://www.mediawiki.org/wiki/Manual:Configuration_settings'/>.
315 '';
316 };
317
318 };
319
320 extraPath = [ mediawikiScripts ];
321
322 # !!! Need to specify that Apache has a dependency on PostgreSQL!
323
324 startupScript = pkgs.writeScript "mediawiki_startup.sh"
325 # Initialise the database automagically if we're using a Postgres
326 # server on localhost.
327 (optionalString (config.dbType == "postgres" && config.dbServer == "") ''
328 if ! ${pkgs.postgresql}/bin/psql -l | grep -q ' ${config.dbName} ' ; then
329 ${pkgs.postgresql}/bin/createuser --no-superuser --no-createdb --no-createrole "${config.dbUser}" || true
330 ${pkgs.postgresql}/bin/createdb "${config.dbName}" -O "${config.dbUser}"
331 ( echo 'CREATE LANGUAGE plpgsql;'
332 cat ${mediawikiRoot}/maintenance/postgres/tables.sql
333 echo 'CREATE TEXT SEARCH CONFIGURATION public.default ( COPY = pg_catalog.english );'
334 echo COMMIT
335 ) | ${pkgs.postgresql}/bin/psql -U "${config.dbUser}" "${config.dbName}"
336 fi
337 ${php}/bin/php ${mediawikiRoot}/maintenance/update.php
338 '');
339
340 robotsEntries = optionalString (config.articleUrlPrefix != "")
341 ''
342 User-agent: *
343 Disallow: ${config.urlPrefix}/
344 Disallow: ${config.articleUrlPrefix}/Special:Search
345 Disallow: ${config.articleUrlPrefix}/Special:Random
346 '';
347
348}