Merge pull request #83769 from dadada/nixos/dokuwiki-multi-server

nixos/dokuwiki: add support for multi-site, additional plugins and templates
This commit is contained in:
Marek Mahut 2020-04-20 19:39:48 +02:00 committed by GitHub
commit 60100a7c92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 382 additions and 221 deletions

View File

@ -3,13 +3,14 @@
let let
inherit (lib) mkEnableOption mkForce mkIf mkMerge mkOption optionalAttrs recursiveUpdate types; inherit (lib) mkEnableOption mkForce mkIf mkMerge mkOption optionalAttrs recursiveUpdate types;
inherit (lib) concatMapStringsSep flatten mapAttrs mapAttrs' mapAttrsToList nameValuePair concatMapStringSep;
cfg = config.services.dokuwiki; eachSite = config.services.dokuwiki;
user = config.services.nginx.user; user = "dokuwiki";
group = config.services.nginx.group; group = config.services.nginx.group;
dokuwikiAclAuthConfig = pkgs.writeText "acl.auth.php" '' dokuwikiAclAuthConfig = cfg: pkgs.writeText "acl.auth.php" ''
# acl.auth.php # acl.auth.php
# <?php exit()?> # <?php exit()?>
# #
@ -18,24 +19,54 @@ let
${toString cfg.acl} ${toString cfg.acl}
''; '';
dokuwikiLocalConfig = pkgs.writeText "local.php" '' dokuwikiLocalConfig = cfg: pkgs.writeText "local.php" ''
<?php <?php
$conf['savedir'] = '${cfg.stateDir}'; $conf['savedir'] = '${cfg.stateDir}';
$conf['superuser'] = '${toString cfg.superUser}'; $conf['superuser'] = '${toString cfg.superUser}';
$conf['useacl'] = '${toString cfg.aclUse}'; $conf['useacl'] = '${toString cfg.aclUse}';
$conf['disableactions'] = '${cfg.disableActions}';
${toString cfg.extraConfig} ${toString cfg.extraConfig}
''; '';
dokuwikiPluginsLocalConfig = pkgs.writeText "plugins.local.php" '' dokuwikiPluginsLocalConfig = cfg: pkgs.writeText "plugins.local.php" ''
<?php <?php
${cfg.pluginsConfig} ${cfg.pluginsConfig}
''; '';
in pkg = hostName: cfg: pkgs.stdenv.mkDerivation rec {
{ pname = "dokuwiki-${hostName}";
options.services.dokuwiki = { version = src.version;
src = cfg.package;
installPhase = ''
mkdir -p $out
cp -r * $out/
# symlink the dokuwiki config
ln -s ${dokuwikiLocalConfig cfg} $out/share/dokuwiki/local.php
# symlink plugins config
ln -s ${dokuwikiPluginsLocalConfig cfg} $out/share/dokuwiki/plugins.local.php
# symlink acl
ln -s ${dokuwikiAclAuthConfig cfg} $out/share/dokuwiki/acl.auth.php
# symlink additional plugin(s) and templates(s)
${concatMapStringsSep "\n" (template: "ln -s ${template} $out/share/dokuwiki/lib/tpl/${template.name}") cfg.templates}
${concatMapStringsSep "\n" (plugin: "ln -s ${plugin} $out/share/dokuwiki/lib/plugins/${plugin.name}") cfg.plugins}
'';
};
siteOpts = { config, lib, name, ...}: {
options = {
enable = mkEnableOption "DokuWiki web application."; enable = mkEnableOption "DokuWiki web application.";
package = mkOption {
type = types.package;
default = pkgs.dokuwiki;
description = "Which dokuwiki package to use.";
};
hostName = mkOption { hostName = mkOption {
type = types.str; type = types.str;
default = "localhost"; default = "localhost";
@ -44,7 +75,7 @@ in
stateDir = mkOption { stateDir = mkOption {
type = types.path; type = types.path;
default = "/var/lib/dokuwiki/data"; default = "/var/lib/dokuwiki/${name}/data";
description = "Location of the dokuwiki state directory."; description = "Location of the dokuwiki state directory.";
}; };
@ -56,18 +87,22 @@ in
Access Control Lists: see <link xlink:href="https://www.dokuwiki.org/acl"/> Access Control Lists: see <link xlink:href="https://www.dokuwiki.org/acl"/>
Mutually exclusive with services.dokuwiki.aclFile Mutually exclusive with services.dokuwiki.aclFile
Set this to a value other than null to take precedence over aclFile option. Set this to a value other than null to take precedence over aclFile option.
Warning: Consider using aclFile instead if you do not
want to store the ACL in the world-readable Nix store.
''; '';
}; };
aclFile = mkOption { aclFile = mkOption {
type = types.nullOr types.path; type = with types; nullOr str;
default = null; default = if (config.aclUse && config.acl == null) then "/var/lib/dokuwiki/${name}/users.auth.php" else null;
description = '' description = ''
Location of the dokuwiki acl rules. Mutually exclusive with services.dokuwiki.acl Location of the dokuwiki acl rules. Mutually exclusive with services.dokuwiki.acl
Mutually exclusive with services.dokuwiki.acl which is preferred. Mutually exclusive with services.dokuwiki.acl which is preferred.
Consult documentation <link xlink:href="https://www.dokuwiki.org/acl"/> for further instructions. Consult documentation <link xlink:href="https://www.dokuwiki.org/acl"/> for further instructions.
Example: <link xlink:href="https://github.com/splitbrain/dokuwiki/blob/master/conf/acl.auth.php.dist"/> Example: <link xlink:href="https://github.com/splitbrain/dokuwiki/blob/master/conf/acl.auth.php.dist"/>
''; '';
example = "/var/lib/dokuwiki/${name}/acl.auth.php";
}; };
aclUse = mkOption { aclUse = mkOption {
@ -104,14 +139,26 @@ in
}; };
usersFile = mkOption { usersFile = mkOption {
type = types.nullOr types.path; type = with types; nullOr str;
default = null; default = if config.aclUse then "/var/lib/dokuwiki/${name}/users.auth.php" else null;
description = '' description = ''
Location of the dokuwiki users file. List of users. Format: Location of the dokuwiki users file. List of users. Format:
login:passwordhash:Real Name:email:groups,comma,separated login:passwordhash:Real Name:email:groups,comma,separated
Create passwordHash easily by using:$ mkpasswd -5 password `pwgen 8 1` Create passwordHash easily by using:$ mkpasswd -5 password `pwgen 8 1`
Example: <link xlink:href="https://github.com/splitbrain/dokuwiki/blob/master/conf/users.auth.php.dist"/> Example: <link xlink:href="https://github.com/splitbrain/dokuwiki/blob/master/conf/users.auth.php.dist"/>
''; '';
example = "/var/lib/dokuwiki/${name}/users.auth.php";
};
disableActions = mkOption {
type = types.nullOr types.str;
default = "";
example = "search,register";
description = ''
Disable individual action modes. Refer to
<link xlink:href="https://www.dokuwiki.org/config:action_modes"/>
for details on supported values.
'';
}; };
extraConfig = mkOption { extraConfig = mkOption {
@ -128,6 +175,61 @@ in
''; '';
}; };
plugins = mkOption {
type = types.listOf types.path;
default = [];
description = ''
List of path(s) to respective plugin(s) which are copied from the 'plugin' directory.
<note><para>These plugins need to be packaged before use, see example.</para></note>
'';
example = ''
# Let's package the icalevents plugin
plugin-icalevents = pkgs.stdenv.mkDerivation {
name = "icalevents";
# Download the plugin from the dokuwiki site
src = pkgs.fetchurl {
url = https://github.com/real-or-random/dokuwiki-plugin-icalevents/releases/download/2017-06-16/dokuwiki-plugin-icalevents-2017-06-16.zip;
sha256 = "e40ed7dd6bbe7fe3363bbbecb4de481d5e42385b5a0f62f6a6ce6bf3a1f9dfa8";
};
sourceRoot = ".";
# We need unzip to build this package
buildInputs = [ pkgs.unzip ];
# Installing simply means copying all files to the output directory
installPhase = "mkdir -p $out; cp -R * $out/";
};
# And then pass this theme to the plugin list like this:
plugins = [ plugin-icalevents ];
'';
};
templates = mkOption {
type = types.listOf types.path;
default = [];
description = ''
List of path(s) to respective template(s) which are copied from the 'tpl' directory.
<note><para>These templates need to be packaged before use, see example.</para></note>
'';
example = ''
# Let's package the bootstrap3 theme
template-bootstrap3 = pkgs.stdenv.mkDerivation {
name = "bootstrap3";
# Download the theme from the dokuwiki site
src = pkgs.fetchurl {
url = https://github.com/giterlizzi/dokuwiki-template-bootstrap3/archive/v2019-05-22.zip;
sha256 = "4de5ff31d54dd61bbccaf092c9e74c1af3a4c53e07aa59f60457a8f00cfb23a6";
};
# We need unzip to build this package
buildInputs = [ pkgs.unzip ];
# Installing simply means copying all files to the output directory
installPhase = "mkdir -p $out; cp -R * $out/";
};
# And then pass this theme to the template list like this:
templates = [ template-bootstrap3 ];
'';
};
poolConfig = mkOption { poolConfig = mkOption {
type = with types; attrsOf (oneOf [ str int bool ]); type = with types; attrsOf (oneOf [ str int bool ]);
default = { default = {
@ -166,34 +268,46 @@ in
''; '';
}; };
}; };
};
in
{
# interface
options = {
services.dokuwiki = mkOption {
type = types.attrsOf (types.submodule siteOpts);
default = {};
description = "Sepcification of one or more dokuwiki sites to service.";
};
};
# implementation # implementation
config = mkIf cfg.enable { config = mkIf (eachSite != {}) {
warnings = mkIf (cfg.superUser == null) ["Not setting services.dokuwiki.superUser will impair your ability to administer DokuWiki"]; warnings = mapAttrsToList (hostName: cfg: mkIf (cfg.superUser == null) "Not setting services.dokuwiki.${hostName} superUser will impair your ability to administer DokuWiki") eachSite;
assertions = [ assertions = flatten (mapAttrsToList (hostName: cfg:
{ [{
assertion = cfg.aclUse -> (cfg.acl != null || cfg.aclFile != null); assertion = cfg.aclUse -> (cfg.acl != null || cfg.aclFile != null);
message = "Either services.dokuwiki.acl or services.dokuwiki.aclFile is mandatory when aclUse is true"; message = "Either services.dokuwiki.${hostName}.acl or services.dokuwiki.${hostName}.aclFile is mandatory if aclUse true";
} }
{ {
assertion = cfg.usersFile != null -> cfg.aclUse != false; assertion = cfg.usersFile != null -> cfg.aclUse != false;
message = "services.dokuwiki.aclUse must be true when usersFile is not null"; message = "services.dokuwiki.${hostName}.aclUse must must be true if usersFile is not null";
} }
]; ]) eachSite);
services.phpfpm.pools.dokuwiki = { services.phpfpm.pools = mapAttrs' (hostName: cfg: (
nameValuePair "dokuwiki-${hostName}" {
inherit user; inherit user;
inherit group; inherit group;
phpEnv = { phpEnv = {
DOKUWIKI_LOCAL_CONFIG = "${dokuwikiLocalConfig}"; DOKUWIKI_LOCAL_CONFIG = "${dokuwikiLocalConfig cfg}";
DOKUWIKI_PLUGINS_LOCAL_CONFIG = "${dokuwikiPluginsLocalConfig}"; DOKUWIKI_PLUGINS_LOCAL_CONFIG = "${dokuwikiPluginsLocalConfig cfg}";
} // optionalAttrs (cfg.usersFile != null) { } // optionalAttrs (cfg.usersFile != null) {
DOKUWIKI_USERS_AUTH_CONFIG = "${cfg.usersFile}"; DOKUWIKI_USERS_AUTH_CONFIG = "${cfg.usersFile}";
} //optionalAttrs (cfg.aclUse) { } //optionalAttrs (cfg.aclUse) {
DOKUWIKI_ACL_AUTH_CONFIG = if (cfg.acl != null) then "${dokuwikiAclAuthConfig}" else "${toString cfg.aclFile}"; DOKUWIKI_ACL_AUTH_CONFIG = if (cfg.acl != null) then "${dokuwikiAclAuthConfig cfg}" else "${toString cfg.aclFile}";
}; };
settings = { settings = {
@ -201,14 +315,12 @@ in
"listen.owner" = user; "listen.owner" = user;
"listen.group" = group; "listen.group" = group;
} // cfg.poolConfig; } // cfg.poolConfig;
}; })) eachSite;
services.nginx = { services.nginx = {
enable = true; enable = true;
virtualHosts = mapAttrs (hostName: cfg: mkMerge [ cfg.nginx {
virtualHosts = { root = mkForce "${pkg hostName cfg}/share/dokuwiki";
${cfg.hostName} = mkMerge [ cfg.nginx {
root = mkForce "${pkgs.dokuwiki}/share/dokuwiki/";
extraConfig = "fastcgi_param HTTPS on;"; extraConfig = "fastcgi_param HTTPS on;";
locations."~ /(conf/|bin/|inc/|install.php)" = { locations."~ /(conf/|bin/|inc/|install.php)" = {
@ -246,16 +358,14 @@ in
include ${pkgs.nginx}/conf/fastcgi_params; include ${pkgs.nginx}/conf/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param REDIRECT_STATUS 200; fastcgi_param REDIRECT_STATUS 200;
fastcgi_pass unix:${config.services.phpfpm.pools.dokuwiki.socket}; fastcgi_pass unix:${config.services.phpfpm.pools."dokuwiki-${hostName}".socket};
fastcgi_param HTTPS on; fastcgi_param HTTPS on;
''; '';
}; };
}]; }]) eachSite;
}; };
}; systemd.tmpfiles.rules = flatten (mapAttrsToList (hostName: cfg: [
systemd.tmpfiles.rules = [
"d ${cfg.stateDir}/attic 0750 ${user} ${group} - -" "d ${cfg.stateDir}/attic 0750 ${user} ${group} - -"
"d ${cfg.stateDir}/cache 0750 ${user} ${group} - -" "d ${cfg.stateDir}/cache 0750 ${user} ${group} - -"
"d ${cfg.stateDir}/index 0750 ${user} ${group} - -" "d ${cfg.stateDir}/index 0750 ${user} ${group} - -"
@ -266,7 +376,13 @@ in
"d ${cfg.stateDir}/meta 0750 ${user} ${group} - -" "d ${cfg.stateDir}/meta 0750 ${user} ${group} - -"
"d ${cfg.stateDir}/pages 0750 ${user} ${group} - -" "d ${cfg.stateDir}/pages 0750 ${user} ${group} - -"
"d ${cfg.stateDir}/tmp 0750 ${user} ${group} - -" "d ${cfg.stateDir}/tmp 0750 ${user} ${group} - -"
]; ] ++ lib.optional (cfg.aclFile != null) "C ${cfg.aclFile} 0640 ${user} ${group} - ${pkg hostName cfg}/share/dokuwiki/conf/acl.auth.php.dist"
++ lib.optional (cfg.usersFile != null) "C ${cfg.usersFile} 0640 ${user} ${group} - ${pkg hostName cfg}/share/dokuwiki/conf/users.auth.php.dist"
) eachSite);
users.users.${user} = {
group = group;
isSystemUser = true;
};
}; };
} }

View File

@ -1,29 +1,74 @@
import ./make-test-python.nix ({ lib, ... }: import ./make-test-python.nix ({ pkgs, ... }:
with lib; let
template-bootstrap3 = pkgs.stdenv.mkDerivation {
name = "bootstrap3";
# Download the theme from the dokuwiki site
src = pkgs.fetchurl {
url = https://github.com/giterlizzi/dokuwiki-template-bootstrap3/archive/v2019-05-22.zip;
sha256 = "4de5ff31d54dd61bbccaf092c9e74c1af3a4c53e07aa59f60457a8f00cfb23a6";
};
# We need unzip to build this package
buildInputs = [ pkgs.unzip ];
# Installing simply means copying all files to the output directory
installPhase = "mkdir -p $out; cp -R * $out/";
};
{
# Let's package the icalevents plugin
plugin-icalevents = pkgs.stdenv.mkDerivation {
name = "icalevents";
# Download the plugin from the dokuwiki site
src = pkgs.fetchurl {
url = https://github.com/real-or-random/dokuwiki-plugin-icalevents/releases/download/2017-06-16/dokuwiki-plugin-icalevents-2017-06-16.zip;
sha256 = "e40ed7dd6bbe7fe3363bbbecb4de481d5e42385b5a0f62f6a6ce6bf3a1f9dfa8";
};
# We need unzip to build this package
buildInputs = [ pkgs.unzip ];
sourceRoot = ".";
# Installing simply means copying all files to the output directory
installPhase = "mkdir -p $out; cp -R * $out/";
};
in {
name = "dokuwiki"; name = "dokuwiki";
meta.maintainers = with maintainers; [ maintainers."1000101" ]; meta.maintainers = with pkgs.lib.maintainers; [ "1000101" ];
nodes.machine = machine = { ... }: {
{ pkgs, ... }: services.dokuwiki."site1.local" = {
{ services.dokuwiki = { aclUse = false;
enable = true; superUser = "admin";
acl = " ";
superUser = null;
nginx = { nginx = {
forceSSL = false; forceSSL = false;
enableACME = false; enableACME = false;
}; };
}; };
services.dokuwiki."site2.local" = {
aclUse = true;
superUser = "admin";
nginx = {
forceSSL = false;
enableACME = false;
};
templates = [ template-bootstrap3 ];
plugins = [ plugin-icalevents ];
};
networking.hosts."127.0.0.1" = [ "site1.local" "site2.local" ];
}; };
testScript = '' testScript = ''
machine.start() site_names = ["site1.local", "site2.local"]
machine.wait_for_unit("phpfpm-dokuwiki.service")
start_all()
machine.wait_for_unit("phpfpm-dokuwiki-site1.local.service")
machine.wait_for_unit("phpfpm-dokuwiki-site2.local.service")
machine.wait_for_unit("nginx.service") machine.wait_for_unit("nginx.service")
machine.wait_for_open_port(80) machine.wait_for_open_port(80)
machine.succeed("curl -sSfL http://localhost/ | grep 'DokuWiki'")
machine.succeed("curl -sSfL http://site1.local/ | grep 'DokuWiki'")
machine.succeed("curl -sSfL http://site2.local/ | grep 'DokuWiki'")
''; '';
}) })