389 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			389 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
{ config, lib, pkgs, ... }:
 | 
						|
 | 
						|
let
 | 
						|
 | 
						|
  inherit (lib) mkEnableOption mkForce mkIf mkMerge mkOption optionalAttrs recursiveUpdate types maintainers;
 | 
						|
  inherit (lib) concatMapStringsSep flatten mapAttrs mapAttrs' mapAttrsToList nameValuePair concatMapStringSep;
 | 
						|
 | 
						|
  eachSite = config.services.dokuwiki;
 | 
						|
 | 
						|
  user = "dokuwiki";
 | 
						|
  group = config.services.nginx.group;
 | 
						|
 | 
						|
  dokuwikiAclAuthConfig = cfg: pkgs.writeText "acl.auth.php" ''
 | 
						|
    # acl.auth.php
 | 
						|
    # <?php exit()?>
 | 
						|
    #
 | 
						|
    # Access Control Lists
 | 
						|
    #
 | 
						|
    ${toString cfg.acl}
 | 
						|
  '';
 | 
						|
 | 
						|
  dokuwikiLocalConfig = cfg: pkgs.writeText "local.php" ''
 | 
						|
    <?php
 | 
						|
    $conf['savedir'] = '${cfg.stateDir}';
 | 
						|
    $conf['superuser'] = '${toString cfg.superUser}';
 | 
						|
    $conf['useacl'] = '${toString cfg.aclUse}';
 | 
						|
    $conf['disableactions'] = '${cfg.disableActions}';
 | 
						|
    ${toString cfg.extraConfig}
 | 
						|
  '';
 | 
						|
 | 
						|
  dokuwikiPluginsLocalConfig = cfg: pkgs.writeText "plugins.local.php" ''
 | 
						|
    <?php
 | 
						|
    ${cfg.pluginsConfig}
 | 
						|
  '';
 | 
						|
 | 
						|
  pkg = hostName: cfg: pkgs.stdenv.mkDerivation rec {
 | 
						|
    pname = "dokuwiki-${hostName}";
 | 
						|
    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.";
 | 
						|
 | 
						|
      package = mkOption {
 | 
						|
        type = types.package;
 | 
						|
        default = pkgs.dokuwiki;
 | 
						|
        description = "Which dokuwiki package to use.";
 | 
						|
      };
 | 
						|
 | 
						|
      hostName = mkOption {
 | 
						|
        type = types.str;
 | 
						|
        default = "localhost";
 | 
						|
        description = "FQDN for the instance.";
 | 
						|
      };
 | 
						|
 | 
						|
      stateDir = mkOption {
 | 
						|
        type = types.path;
 | 
						|
        default = "/var/lib/dokuwiki/${name}/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 <link xlink:href="https://www.dokuwiki.org/acl"/>
 | 
						|
          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}/acl.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 <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 = "/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 <link xlink:href="https://www.dokuwiki.org/config:superuser"/> 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: <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 {
 | 
						|
        type = types.nullOr types.lines;
 | 
						|
        default = null;
 | 
						|
        example = ''
 | 
						|
          $conf['title'] = 'My Wiki';
 | 
						|
          $conf['userewrite'] = 1;
 | 
						|
        '';
 | 
						|
        description = ''
 | 
						|
          DokuWiki configuration. Refer to
 | 
						|
          <link xlink:href="https://www.dokuwiki.org/config"/>
 | 
						|
          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.
 | 
						|
              <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
 | 
						|
                nativeBuildInputs = [ 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
 | 
						|
                nativeBuildInputs = [ 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 <literal>php-fpm.conf</literal>
 | 
						|
          for details on configuration directives.
 | 
						|
        '';
 | 
						|
      };
 | 
						|
 | 
						|
      nginx = mkOption {
 | 
						|
        type = types.submodule (
 | 
						|
          recursiveUpdate
 | 
						|
            (import ../web-servers/nginx/vhost-options.nix { inherit config lib; }) {}
 | 
						|
        );
 | 
						|
        default = {};
 | 
						|
        example = {
 | 
						|
          serverAliases = [
 | 
						|
            "wiki.\${config.networking.domain}"
 | 
						|
          ];
 | 
						|
          # To enable encryption and let let's encrypt take care of certificate
 | 
						|
          forceSSL = true;
 | 
						|
          enableACME = true;
 | 
						|
        };
 | 
						|
        description = ''
 | 
						|
          With this option, you can customize the nginx virtualHost settings.
 | 
						|
        '';
 | 
						|
      };
 | 
						|
    };
 | 
						|
  };
 | 
						|
in
 | 
						|
{
 | 
						|
  # interface
 | 
						|
  options = {
 | 
						|
    services.dokuwiki = mkOption {
 | 
						|
      type = types.attrsOf (types.submodule siteOpts);
 | 
						|
      default = {};
 | 
						|
      description = "Sepcification of one or more dokuwiki sites to serve.";
 | 
						|
    };
 | 
						|
  };
 | 
						|
 | 
						|
  # implementation
 | 
						|
 | 
						|
  config = mkIf (eachSite != {}) {
 | 
						|
 | 
						|
    warnings = mapAttrsToList (hostName: cfg: mkIf (cfg.superUser == null) "Not setting services.dokuwiki.${hostName} superUser will impair your ability to administer DokuWiki") eachSite;
 | 
						|
 | 
						|
    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 = 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 = mapAttrs (hostName: cfg:  mkMerge [ cfg.nginx {
 | 
						|
        root = mkForce "${pkg hostName cfg}/share/dokuwiki";
 | 
						|
        extraConfig = lib.optionalString (cfg.nginx.addSSL || cfg.nginx.forceSSL || cfg.nginx.onlySSL || cfg.nginx.enableACME) "fastcgi_param HTTPS on;";
 | 
						|
 | 
						|
        locations."~ /(conf/|bin/|inc/|install.php)" = {
 | 
						|
          extraConfig = "deny all;";
 | 
						|
        };
 | 
						|
 | 
						|
        locations."~ ^/data/" = {
 | 
						|
          root = "${cfg.stateDir}";
 | 
						|
          extraConfig = "internal;";
 | 
						|
        };
 | 
						|
 | 
						|
        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."@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 = ''
 | 
						|
              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-${hostName}".socket};
 | 
						|
              ${lib.optionalString (cfg.nginx.addSSL || cfg.nginx.forceSSL || cfg.nginx.onlySSL || cfg.nginx.enableACME) "fastcgi_param HTTPS on;"}
 | 
						|
          '';
 | 
						|
        };
 | 
						|
      }]) eachSite;
 | 
						|
    };
 | 
						|
 | 
						|
    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} - -"
 | 
						|
      "d ${cfg.stateDir}/locks 0750 ${user} ${group} - -"
 | 
						|
      "d ${cfg.stateDir}/media 0750 ${user} ${group} - -"
 | 
						|
      "d ${cfg.stateDir}/media_attic 0750 ${user} ${group} - -"
 | 
						|
      "d ${cfg.stateDir}/media_meta 0750 ${user} ${group} - -"
 | 
						|
      "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;
 | 
						|
    };
 | 
						|
  };
 | 
						|
 | 
						|
  meta.maintainers = with maintainers; [ _1000101 ];
 | 
						|
 | 
						|
}
 |