nixpkgs/nixos/modules/services/web-apps/moodle.nix
Silvan Mosberger 4f0dadbf38 treewide: format all inactive Nix files
After final improvements to the official formatter implementation,
this commit now performs the first treewide reformat of Nix files using it.
This is part of the implementation of RFC 166.

Only "inactive" files are reformatted, meaning only files that
aren't being touched by any PR with activity in the past 2 months.
This is to avoid conflicts for PRs that might soon be merged.
Later we can do a full treewide reformat to get the rest,
which should not cause as many conflicts.

A CI check has already been running for some time to ensure that new and
already-formatted files are formatted, so the files being reformatted here
should also stay formatted.

This commit was automatically created and can be verified using

    nix-build a08b3a4d19.tar.gz \
      --argstr baseRev b32a094368
    result/bin/apply-formatting $NIXPKGS_PATH
2024-12-10 20:26:33 +01:00

404 lines
10 KiB
Nix

{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
mkDefault
mkEnableOption
mkPackageOption
mkForce
mkIf
mkMerge
mkOption
types
;
inherit (lib)
concatStringsSep
literalExpression
mapAttrsToList
optional
optionalString
;
cfg = config.services.moodle;
fpm = config.services.phpfpm.pools.moodle;
user = "moodle";
group = config.services.httpd.group;
stateDir = "/var/lib/moodle";
moodleConfig = pkgs.writeText "config.php" ''
<?php // Moodle configuration file
unset($CFG);
global $CFG;
$CFG = new stdClass();
$CFG->dbtype = '${
{
mysql = "mariadb";
pgsql = "pgsql";
}
.${cfg.database.type}
}';
$CFG->dblibrary = 'native';
$CFG->dbhost = '${cfg.database.host}';
$CFG->dbname = '${cfg.database.name}';
$CFG->dbuser = '${cfg.database.user}';
${optionalString (
cfg.database.passwordFile != null
) "$CFG->dbpass = file_get_contents('${cfg.database.passwordFile}');"}
$CFG->prefix = 'mdl_';
$CFG->dboptions = array (
'dbpersist' => 0,
'dbport' => '${toString cfg.database.port}',
${optionalString (cfg.database.socket != null) "'dbsocket' => '${cfg.database.socket}',"}
'dbcollation' => 'utf8mb4_unicode_ci',
);
$CFG->wwwroot = '${
if cfg.virtualHost.addSSL || cfg.virtualHost.forceSSL || cfg.virtualHost.onlySSL then
"https"
else
"http"
}://${cfg.virtualHost.hostName}';
$CFG->dataroot = '${stateDir}';
$CFG->admin = 'admin';
$CFG->directorypermissions = 02777;
$CFG->disableupdateautodeploy = true;
$CFG->pathtogs = '${pkgs.ghostscript}/bin/gs';
$CFG->pathtophp = '${phpExt}/bin/php';
$CFG->pathtodu = '${pkgs.coreutils}/bin/du';
$CFG->aspellpath = '${pkgs.aspell}/bin/aspell';
$CFG->pathtodot = '${pkgs.graphviz}/bin/dot';
${cfg.extraConfig}
require_once('${cfg.package}/share/moodle/lib/setup.php');
// There is no php closing tag in this file,
// it is intentional because it prevents trailing whitespace problems!
'';
mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql";
pgsqlLocal = cfg.database.createLocally && cfg.database.type == "pgsql";
phpExt = pkgs.php83.buildEnv {
extensions =
{ all, ... }:
with all;
[
iconv
mbstring
curl
openssl
tokenizer
soap
ctype
zip
gd
simplexml
dom
intl
sqlite3
pgsql
pdo_sqlite
pdo_pgsql
pdo_odbc
pdo_mysql
pdo
mysqli
session
zlib
xmlreader
fileinfo
filter
opcache
exif
sodium
];
extraConfig = "max_input_vars = 5000";
};
in
{
# interface
options.services.moodle = {
enable = mkEnableOption "Moodle web application";
package = mkPackageOption pkgs "moodle" { };
initialPassword = mkOption {
type = types.str;
example = "correcthorsebatterystaple";
description = ''
Specifies the initial password for the admin, i.e. the password assigned if the user does not already exist.
The password specified here is world-readable in the Nix store, so it should be changed promptly.
'';
};
database = {
type = mkOption {
type = types.enum [
"mysql"
"pgsql"
];
default = "mysql";
description = "Database engine to use.";
};
host = mkOption {
type = types.str;
default = "localhost";
description = "Database host address.";
};
port = mkOption {
type = types.port;
description = "Database host port.";
default =
{
mysql = 3306;
pgsql = 5432;
}
.${cfg.database.type};
defaultText = literalExpression "3306";
};
name = mkOption {
type = types.str;
default = "moodle";
description = "Database name.";
};
user = mkOption {
type = types.str;
default = "moodle";
description = "Database user.";
};
passwordFile = mkOption {
type = types.nullOr types.path;
default = null;
example = "/run/keys/moodle-dbpassword";
description = ''
A file containing the password corresponding to
{option}`database.user`.
'';
};
socket = mkOption {
type = types.nullOr types.path;
default =
if mysqlLocal then
"/run/mysqld/mysqld.sock"
else if pgsqlLocal then
"/run/postgresql"
else
null;
defaultText = literalExpression "/run/mysqld/mysqld.sock";
description = "Path to the unix socket file to use for authentication.";
};
createLocally = mkOption {
type = types.bool;
default = true;
description = "Create the database and database user locally.";
};
};
virtualHost = mkOption {
type = types.submodule (import ../web-servers/apache-httpd/vhost-options.nix);
example = literalExpression ''
{
hostName = "moodle.example.org";
adminAddr = "webmaster@example.org";
forceSSL = true;
enableACME = true;
}
'';
description = ''
Apache configuration can be done by adapting {option}`services.httpd.virtualHosts`.
See [](#opt-services.httpd.virtualHosts) for further information.
'';
};
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 Moodle PHP pool. See the documentation on `php-fpm.conf`
for details on configuration directives.
'';
};
extraConfig = mkOption {
type = types.lines;
default = "";
description = ''
Any additional text to be appended to the config.php
configuration file. This is a PHP script. For configuration
details, see <https://docs.moodle.org/37/en/Configuration_file>.
'';
example = ''
$CFG->disableupdatenotifications = true;
'';
};
};
# implementation
config = mkIf cfg.enable {
assertions = [
{
assertion =
cfg.database.createLocally -> cfg.database.user == user && cfg.database.user == cfg.database.name;
message = "services.moodle.database.user must be set to ${user} if services.moodle.database.createLocally is set true";
}
{
assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
message = "a password cannot be specified if services.moodle.database.createLocally is set to true";
}
];
services.mysql = mkIf mysqlLocal {
enable = true;
package = mkDefault pkgs.mariadb;
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{
name = cfg.database.user;
ensurePermissions = {
"${cfg.database.name}.*" =
"SELECT, INSERT, UPDATE, DELETE, CREATE, CREATE TEMPORARY TABLES, DROP, INDEX, ALTER";
};
}
];
};
services.postgresql = mkIf pgsqlLocal {
enable = true;
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{
name = cfg.database.user;
ensureDBOwnership = true;
}
];
};
services.phpfpm.pools.moodle = {
inherit user group;
phpPackage = phpExt;
phpEnv.MOODLE_CONFIG = "${moodleConfig}";
phpOptions = ''
zend_extension = opcache.so
opcache.enable = 1
max_input_vars = 5000
'';
settings = {
"listen.owner" = config.services.httpd.user;
"listen.group" = config.services.httpd.group;
} // cfg.poolConfig;
};
services.httpd = {
enable = true;
adminAddr = mkDefault cfg.virtualHost.adminAddr;
extraModules = [ "proxy_fcgi" ];
virtualHosts.${cfg.virtualHost.hostName} = mkMerge [
cfg.virtualHost
{
documentRoot = mkForce "${cfg.package}/share/moodle";
extraConfig = ''
<Directory "${cfg.package}/share/moodle">
<FilesMatch "\.php$">
<If "-f %{REQUEST_FILENAME}">
SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/"
</If>
</FilesMatch>
Options -Indexes
DirectoryIndex index.php
</Directory>
'';
}
];
};
systemd.tmpfiles.settings."10-moodle".${stateDir}.d = {
inherit user group;
mode = "0750";
};
systemd.services.moodle-init = {
wantedBy = [ "multi-user.target" ];
before = [ "phpfpm-moodle.service" ];
after = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
environment.MOODLE_CONFIG = moodleConfig;
script = ''
${phpExt}/bin/php ${cfg.package}/share/moodle/admin/cli/check_database_schema.php && rc=$? || rc=$?
[ "$rc" == 1 ] && ${phpExt}/bin/php ${cfg.package}/share/moodle/admin/cli/upgrade.php \
--non-interactive \
--allow-unstable
[ "$rc" == 2 ] && ${phpExt}/bin/php ${cfg.package}/share/moodle/admin/cli/install_database.php \
--agree-license \
--adminpass=${cfg.initialPassword}
true
'';
serviceConfig = {
User = user;
Group = group;
Type = "oneshot";
};
};
systemd.services.moodle-cron = {
description = "Moodle cron service";
after = [ "moodle-init.service" ];
environment.MOODLE_CONFIG = moodleConfig;
serviceConfig = {
User = user;
Group = group;
ExecStart = "${phpExt}/bin/php ${cfg.package}/share/moodle/admin/cli/cron.php";
};
};
systemd.timers.moodle-cron = {
description = "Moodle cron timer";
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "minutely";
};
};
systemd.services.httpd.after =
optional mysqlLocal "mysql.service"
++ optional pgsqlLocal "postgresql.service";
users.users.${user} = {
group = group;
isSystemUser = true;
};
};
}