diff --git a/nixos/modules/services/web-apps/dokuwiki.nix b/nixos/modules/services/web-apps/dokuwiki.nix
index 07af7aa0dfe..76e18266a27 100644
--- a/nixos/modules/services/web-apps/dokuwiki.nix
+++ b/nixos/modules/services/web-apps/dokuwiki.nix
@@ -3,13 +3,14 @@
let
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;
- dokuwikiAclAuthConfig = pkgs.writeText "acl.auth.php" ''
+ dokuwikiAclAuthConfig = cfg: pkgs.writeText "acl.auth.php" ''
# acl.auth.php
#
#
@@ -18,244 +19,353 @@ let
${toString cfg.acl}
'';
- dokuwikiLocalConfig = pkgs.writeText "local.php" ''
+ dokuwikiLocalConfig = cfg: pkgs.writeText "local.php" ''
+ Mutually exclusive with services.dokuwiki.aclFile
+ 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 {
+ type = with types; nullOr str;
+ default = if (config.aclUse && config.acl == null) then "/var/lib/dokuwiki/${name}/users.auth.php" else null;
+ description = ''
+ Location of the dokuwiki acl rules. Mutually exclusive with services.dokuwiki.acl
+ Mutually exclusive with services.dokuwiki.acl which is preferred.
+ Consult documentation for further instructions.
+ Example:
+ '';
+ example = "/var/lib/dokuwiki/${name}/acl.auth.php";
+ };
+
+ aclUse = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ Necessary for users to log in into the system.
+ Also limits anonymous users. When disabled,
+ everyone is able to create and edit content.
+ '';
+ };
+
+ pluginsConfig = mkOption {
+ type = types.lines;
+ default = ''
+ $plugins['authad'] = 0;
+ $plugins['authldap'] = 0;
+ $plugins['authmysql'] = 0;
+ $plugins['authpgsql'] = 0;
+ '';
+ description = ''
+ List of the dokuwiki (un)loaded plugins.
+ '';
+ };
+
+ superUser = mkOption {
+ type = types.nullOr types.str;
+ default = "@admin";
+ description = ''
+ You can set either a username, a list of usernames (“admin1,admin2”),
+ or the name of a group by prepending an @ char to the groupname
+ Consult documentation for further instructions.
+ '';
+ };
+
+ usersFile = mkOption {
+ type = with types; nullOr str;
+ default = if config.aclUse then "/var/lib/dokuwiki/${name}/users.auth.php" else null;
+ description = ''
+ Location of the dokuwiki users file. List of users. Format:
+ login:passwordhash:Real Name:email:groups,comma,separated
+ Create passwordHash easily by using:$ mkpasswd -5 password `pwgen 8 1`
+ Example:
+ '';
+ 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
+
+ for details on supported values.
+ '';
+ };
+
+ extraConfig = mkOption {
+ type = types.nullOr types.lines;
+ default = null;
+ example = ''
+ $conf['title'] = 'My Wiki';
+ $conf['userewrite'] = 1;
+ '';
+ description = ''
+ DokuWiki configuration. Refer to
+
+ for details on supported values.
+ '';
+ };
+
+ plugins = mkOption {
+ type = types.listOf types.path;
+ default = [];
+ description = ''
+ List of path(s) to respective plugin(s) which are copied from the 'plugin' directory.
+ These plugins need to be packaged before use, see example.
+ '';
+ 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.
+ These templates need to be packaged before use, see example.
+ '';
+ 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 {
+ 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 dokuwiki PHP pool. See the documentation on php-fpm.conf
+ for details on configuration directives.
+ '';
+ };
+
+ nginx = mkOption {
+ type = types.submodule (
+ recursiveUpdate
+ (import ../web-servers/nginx/vhost-options.nix { inherit config lib; })
+ {
+ # Enable encryption by default,
+ options.forceSSL.default = true;
+ options.enableACME.default = true;
+ }
+ );
+ default = {forceSSL = true; enableACME = true;};
+ example = {
+ serverAliases = [
+ "wiki.\${config.networking.domain}"
+ ];
+ enableACME = false;
+ };
+ description = ''
+ With this option, you can customize the nginx virtualHost which already has sensible defaults for DokuWiki.
+ '';
+ };
+ };
+ };
in
{
- options.services.dokuwiki = {
- enable = mkEnableOption "DokuWiki web application.";
-
- hostName = mkOption {
- type = types.str;
- default = "localhost";
- description = "FQDN for the instance.";
- };
-
- stateDir = mkOption {
- type = types.path;
- default = "/var/lib/dokuwiki/data";
- description = "Location of the dokuwiki state directory.";
- };
-
- acl = mkOption {
- type = types.nullOr types.lines;
- default = null;
- example = "* @ALL 8";
- description = ''
- Access Control Lists: see
- Mutually exclusive with services.dokuwiki.aclFile
- Set this to a value other than null to take precedence over aclFile option.
- '';
- };
-
- aclFile = mkOption {
- type = types.nullOr types.path;
- default = null;
- description = ''
- Location of the dokuwiki acl rules. Mutually exclusive with services.dokuwiki.acl
- Mutually exclusive with services.dokuwiki.acl which is preferred.
- Consult documentation for further instructions.
- Example:
- '';
- };
-
- aclUse = mkOption {
- type = types.bool;
- default = true;
- description = ''
- Necessary for users to log in into the system.
- Also limits anonymous users. When disabled,
- everyone is able to create and edit content.
- '';
- };
-
- pluginsConfig = mkOption {
- type = types.lines;
- default = ''
- $plugins['authad'] = 0;
- $plugins['authldap'] = 0;
- $plugins['authmysql'] = 0;
- $plugins['authpgsql'] = 0;
- '';
- description = ''
- List of the dokuwiki (un)loaded plugins.
- '';
- };
-
- superUser = mkOption {
- type = types.nullOr types.str;
- default = "@admin";
- description = ''
- You can set either a username, a list of usernames (“admin1,admin2”),
- or the name of a group by prepending an @ char to the groupname
- Consult documentation for further instructions.
- '';
- };
-
- usersFile = mkOption {
- type = types.nullOr types.path;
- default = null;
- description = ''
- Location of the dokuwiki users file. List of users. Format:
- login:passwordhash:Real Name:email:groups,comma,separated
- Create passwordHash easily by using:$ mkpasswd -5 password `pwgen 8 1`
- Example:
- '';
- };
-
- extraConfig = mkOption {
- type = types.nullOr types.lines;
- default = null;
- example = ''
- $conf['title'] = 'My Wiki';
- $conf['userewrite'] = 1;
- '';
- description = ''
- DokuWiki configuration. Refer to
-
- for details on supported values.
- '';
- };
-
- 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 dokuwiki PHP pool. See the documentation on php-fpm.conf
- for details on configuration directives.
- '';
- };
-
- nginx = mkOption {
- type = types.submodule (
- recursiveUpdate
- (import ../web-servers/nginx/vhost-options.nix { inherit config lib; })
- {
- # Enable encryption by default,
- options.forceSSL.default = true;
- options.enableACME.default = true;
- }
- );
- default = {forceSSL = true; enableACME = true;};
- example = {
- serverAliases = [
- "wiki.\${config.networking.domain}"
- ];
- enableACME = false;
- };
- description = ''
- With this option, you can customize the nginx virtualHost which already has sensible defaults for DokuWiki.
- '';
+ # interface
+ options = {
+ services.dokuwiki = mkOption {
+ type = types.attrsOf (types.submodule siteOpts);
+ default = {};
+ description = "Sepcification of one or more dokuwiki sites to service.";
};
};
# 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 = [
- {
- assertion = cfg.aclUse -> (cfg.acl != null || cfg.aclFile != null);
- message = "Either services.dokuwiki.acl or services.dokuwiki.aclFile is mandatory when aclUse is true";
- }
- {
- assertion = cfg.usersFile != null -> cfg.aclUse != false;
- message = "services.dokuwiki.aclUse must be true when usersFile is not null";
- }
- ];
+ assertions = flatten (mapAttrsToList (hostName: cfg:
+ [{
+ assertion = cfg.aclUse -> (cfg.acl != null || cfg.aclFile != null);
+ message = "Either services.dokuwiki.${hostName}.acl or services.dokuwiki.${hostName}.aclFile is mandatory if aclUse true";
+ }
+ {
+ assertion = cfg.usersFile != null -> cfg.aclUse != false;
+ message = "services.dokuwiki.${hostName}.aclUse must must be true if usersFile is not null";
+ }
+ ]) eachSite);
- services.phpfpm.pools.dokuwiki = {
- inherit user;
- inherit group;
- phpEnv = {
- DOKUWIKI_LOCAL_CONFIG = "${dokuwikiLocalConfig}";
- DOKUWIKI_PLUGINS_LOCAL_CONFIG = "${dokuwikiPluginsLocalConfig}";
- } //optionalAttrs (cfg.usersFile != null) {
- DOKUWIKI_USERS_AUTH_CONFIG = "${cfg.usersFile}";
- } //optionalAttrs (cfg.aclUse) {
- DOKUWIKI_ACL_AUTH_CONFIG = if (cfg.acl != null) then "${dokuwikiAclAuthConfig}" else "${toString cfg.aclFile}";
- };
-
- settings = {
- "listen.mode" = "0660";
- "listen.owner" = user;
- "listen.group" = group;
- } // cfg.poolConfig;
- };
+ services.phpfpm.pools = mapAttrs' (hostName: cfg: (
+ nameValuePair "dokuwiki-${hostName}" {
+ inherit user;
+ inherit group;
+ phpEnv = {
+ DOKUWIKI_LOCAL_CONFIG = "${dokuwikiLocalConfig cfg}";
+ DOKUWIKI_PLUGINS_LOCAL_CONFIG = "${dokuwikiPluginsLocalConfig cfg}";
+ } // optionalAttrs (cfg.usersFile != null) {
+ DOKUWIKI_USERS_AUTH_CONFIG = "${cfg.usersFile}";
+ } //optionalAttrs (cfg.aclUse) {
+ DOKUWIKI_ACL_AUTH_CONFIG = if (cfg.acl != null) then "${dokuwikiAclAuthConfig cfg}" else "${toString cfg.aclFile}";
+ };
+
+ settings = {
+ "listen.mode" = "0660";
+ "listen.owner" = user;
+ "listen.group" = group;
+ } // cfg.poolConfig;
+ })) eachSite;
services.nginx = {
enable = true;
-
- virtualHosts = {
- ${cfg.hostName} = mkMerge [ cfg.nginx {
- root = mkForce "${pkgs.dokuwiki}/share/dokuwiki/";
- extraConfig = "fastcgi_param HTTPS on;";
+ virtualHosts = mapAttrs (hostName: cfg: mkMerge [ cfg.nginx {
+ root = mkForce "${pkg hostName cfg}/share/dokuwiki";
+ extraConfig = "fastcgi_param HTTPS on;";
- locations."~ /(conf/|bin/|inc/|install.php)" = {
- extraConfig = "deny all;";
- };
+ locations."~ /(conf/|bin/|inc/|install.php)" = {
+ extraConfig = "deny all;";
+ };
- locations."~ ^/data/" = {
- root = "${cfg.stateDir}";
- extraConfig = "internal;";
- };
+ locations."~ ^/data/" = {
+ root = "${cfg.stateDir}";
+ extraConfig = "internal;";
+ };
- locations."~ ^/lib.*\.(js|css|gif|png|ico|jpg|jpeg)$" = {
- extraConfig = "expires 365d;";
- };
+ locations."~ ^/lib.*\.(js|css|gif|png|ico|jpg|jpeg)$" = {
+ extraConfig = "expires 365d;";
+ };
- locations."/" = {
- priority = 1;
- index = "doku.php";
- extraConfig = ''try_files $uri $uri/ @dokuwiki;'';
- };
+ locations."/" = {
+ priority = 1;
+ index = "doku.php";
+ extraConfig = ''try_files $uri $uri/ @dokuwiki;'';
+ };
- locations."@dokuwiki" = {
- extraConfig = ''
+ locations."@dokuwiki" = {
+ extraConfig = ''
# rewrites "doku.php/" out of the URLs if you set the userwrite setting to .htaccess in dokuwiki config page
rewrite ^/_media/(.*) /lib/exe/fetch.php?media=$1 last;
rewrite ^/_detail/(.*) /lib/exe/detail.php?media=$1 last;
rewrite ^/_export/([^/]+)/(.*) /doku.php?do=export_$1&id=$2 last;
rewrite ^/(.*) /doku.php?id=$1&$args last;
- '';
- };
+ '';
+ };
- locations."~ \.php$" = {
- extraConfig = ''
+ locations."~ \.php$" = {
+ extraConfig = ''
try_files $uri $uri/ /doku.php;
include ${pkgs.nginx}/conf/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
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;
- '';
- };
- }];
- };
-
+ '';
+ };
+ }]) eachSite;
};
- systemd.tmpfiles.rules = [
+ systemd.tmpfiles.rules = flatten (mapAttrsToList (hostName: cfg: [
"d ${cfg.stateDir}/attic 0750 ${user} ${group} - -"
"d ${cfg.stateDir}/cache 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}/pages 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;
+ };
};
}
diff --git a/nixos/tests/dokuwiki.nix b/nixos/tests/dokuwiki.nix
index 38bde10f47e..2b907133ed5 100644
--- a/nixos/tests/dokuwiki.nix
+++ b/nixos/tests/dokuwiki.nix
@@ -1,29 +1,74 @@
-import ./make-test-python.nix ({ lib, ... }:
+import ./make-test-python.nix ({ pkgs, ... }:
-with lib;
-
-{
- name = "dokuwiki";
- meta.maintainers = with maintainers; [ maintainers."1000101" ];
-
- nodes.machine =
- { pkgs, ... }:
- { services.dokuwiki = {
- enable = true;
- acl = " ";
- superUser = null;
- nginx = {
- forceSSL = false;
- enableACME = false;
- };
- };
+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";
+ meta.maintainers = with pkgs.lib.maintainers; [ "1000101" ];
+
+ machine = { ... }: {
+ services.dokuwiki."site1.local" = {
+ aclUse = false;
+ superUser = "admin";
+ nginx = {
+ forceSSL = 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 = ''
- machine.start()
- machine.wait_for_unit("phpfpm-dokuwiki.service")
+ site_names = ["site1.local", "site2.local"]
+
+ 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_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'")
'';
})