diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 9fc036f9213a..db0f0f0870e7 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -1363,6 +1363,7 @@
./services/web-apps/miniflux.nix
./services/web-apps/monica.nix
./services/web-apps/moodle.nix
+ ./services/web-apps/movim.nix
./services/web-apps/netbox.nix
./services/web-apps/nextcloud.nix
./services/web-apps/nextcloud-notify_push.nix
diff --git a/nixos/modules/services/web-apps/movim.nix b/nixos/modules/services/web-apps/movim.nix
new file mode 100644
index 000000000000..c9314e28e949
--- /dev/null
+++ b/nixos/modules/services/web-apps/movim.nix
@@ -0,0 +1,602 @@
+{ config, lib, pkgs, ... }:
+
+let
+ inherit (lib)
+ filterAttrsRecursive
+ generators
+ literalExpression
+ mkDefault
+ mkIf
+ mkOption
+ mkEnableOption
+ mkPackageOption
+ mkMerge
+ pipe
+ types
+ ;
+
+ cfg = config.services.movim;
+
+ defaultPHPCfg = {
+ "output_buffering" = 0;
+ "error_reporting" = "E_ALL & ~E_DEPRECATED & ~E_STRICT";
+ "opcache.enable_cli" = 1;
+ "opcache.interned_strings_buffer" = 8;
+ "opcache.max_accelerated_files" = 6144;
+ "opcache.memory_consumption" = 128;
+ "opcache.revalidate_freq" = 2;
+ "opcache.fast_shutdown" = 1;
+ };
+
+ phpCfg = generators.toKeyValue
+ { mkKeyValue = generators.mkKeyValueDefault { } " = "; }
+ (defaultPHPCfg // cfg.phpCfg);
+
+ podConfigFlags =
+ let
+ bevalue = a: lib.escapeShellArg (generators.mkValueStringDefault { } a);
+ in
+ lib.concatStringsSep " "
+ (lib.attrsets.foldlAttrs
+ (acc: k: v: acc ++ lib.optional (v != null) "--${k}=${bevalue v}")
+ [ ]
+ cfg.podConfig);
+
+ package =
+ let
+ p = cfg.package.override {
+ inherit phpCfg;
+ withPgsql = cfg.database.type == "pgsql";
+ withMysql = cfg.database.type == "mysql";
+ };
+ in
+ p.overrideAttrs (finalAttrs: prevAttrs:
+ let
+ appDir = "$out/share/php/${finalAttrs.pname}";
+
+ stateDirectories = ''
+ # Symlinking in our state directories
+ rm -rf $out/.env $out/cache ${appDir}/public/cache
+ ln -s ${cfg.dataDir}/.env ${appDir}/.env
+ ln -s ${cfg.dataDir}/public/cache ${appDir}/public/cache
+ ln -s ${cfg.logDir} ${appDir}/log
+ ln -s ${cfg.runtimeDir}/cache ${appDir}/cache
+ '';
+
+ exposeComposer = ''
+ # Expose PHP Composer for scripts
+ mkdir -p $out/bin
+ echo "#!${lib.getExe pkgs.dash}" > $out/bin/movim-composer
+ echo "${finalAttrs.php.packages.composer}/bin/composer --working-dir="${appDir}" \"\$@\"" >> $out/bin/movim-composer
+ chmod +x $out/bin/movim-composer
+ '';
+
+ podConfigInputDisableReplace = lib.optionalString (podConfigFlags != "")
+ (lib.concatStringsSep "\n"
+ (lib.attrsets.foldlAttrs
+ (acc: k: v:
+ acc ++ lib.optional (v != null)
+ # Disable all Admin panel options that were set in the
+ # `cfg.podConfig` to prevent confusing situtions where the
+ # values are rewritten on server reboot
+ ''
+ substituteInPlace ${appDir}/app/widgets/AdminMain/adminmain.tpl \
+ --replace-warn 'name="${k}"' 'name="${k}" disabled'
+ '')
+ [ ]
+ cfg.podConfig));
+ in
+ {
+ postInstall = lib.concatStringsSep "\n\n" [
+ prevAttrs.postInstall
+ stateDirectories
+ exposeComposer
+ podConfigInputDisableReplace
+ ];
+ });
+
+ configFile = pipe cfg.settings [
+ (filterAttrsRecursive (_: v: v != null))
+ (generators.toKeyValue { })
+ (pkgs.writeText "movim-env")
+ ];
+
+ pool = "movim";
+ fpm = config.services.phpfpm.pools.${pool};
+ phpExecutionUnit = "phpfpm-${pool}";
+
+ dbService = {
+ "postgresql" = "postgresql.service";
+ "mysql" = "mysql.service";
+ }.${cfg.database.type};
+in
+{
+ options.services = {
+ movim = {
+ enable = mkEnableOption "a Movim instance";
+ package = mkPackageOption pkgs "movim" { };
+ phpPackage = mkPackageOption pkgs "php" { };
+
+ phpCfg = mkOption {
+ type = with types; attrsOf (oneOf [ int str bool ]);
+ defaultText = literalExpression (generators.toPretty { } defaultPHPCfg);
+ default = { };
+ description = "Extra PHP INI options such as `memory_limit`, `max_execution_time`, etc.";
+ };
+
+ user = mkOption {
+ type = types.nonEmptyStr;
+ default = "movim";
+ description = "User running Movim service";
+ };
+
+ group = mkOption {
+ type = types.nonEmptyStr;
+ default = "movim";
+ description = "Group running Movim service";
+ };
+
+ dataDir = mkOption {
+ type = types.nonEmptyStr;
+ default = "/var/lib/movim";
+ description = "State directory of the `movim` user which holds the application’s state & data.";
+ };
+
+ logDir = mkOption {
+ type = types.nonEmptyStr;
+ default = "/var/log/movim";
+ description = "Log directory of the `movim` user which holds the application’s logs.";
+ };
+
+ runtimeDir = mkOption {
+ type = types.nonEmptyStr;
+ default = "/run/movim";
+ description = "Runtime directory of the `movim` user which holds the application’s caches & temporary files.";
+ };
+
+ domain = mkOption {
+ type = types.nonEmptyStr;
+ description = "Fully-qualified domain name (FQDN) for the Movim instance.";
+ };
+
+ port = mkOption {
+ type = types.port;
+ default = 8080;
+ description = "Movim daemon port.";
+ };
+
+ debug = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Debugging logs.";
+ };
+
+ verbose = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Verbose logs.";
+ };
+
+ podConfig = mkOption {
+ type = types.submodule {
+ options = {
+ info = mkOption {
+ type = with types; nullOr str;
+ default = null;
+ description = "Content of the info box on the login page";
+ };
+
+ description = mkOption {
+ type = with types; nullOr str;
+ default = null;
+ description = "General description of the instance";
+ };
+
+ timezone = mkOption {
+ type = with types; nullOr str;
+ default = null;
+ description = "The server timezone";
+ };
+
+ restrictsuggestions = mkOption {
+ type = with types; nullOr bool;
+ default = null;
+ description = "Only suggest chatrooms, Communities and other contents that are available on the user XMPP server and related services";
+ };
+
+ chatonly = mkOption {
+ type = with types; nullOr bool;
+ default = null;
+ description = "Disable all the social feature (Communities, Blog…) and keep only the chat ones";
+ };
+
+ disableregistration = mkOption {
+ type = with types; nullOr bool;
+ default = null;
+ description = "Remove the XMPP registration flow and buttons from the interface";
+ };
+
+ loglevel = mkOption {
+ type = with types; nullOr (ints.between 0 3);
+ default = null;
+ description = "The server loglevel";
+ };
+
+ locale = mkOption {
+ type = with types; nullOr str;
+ default = null;
+ description = "The server main locale";
+ };
+
+ xmppdomain = mkOption {
+ type = with types; nullOr str;
+ default = null;
+ description = "The default XMPP server domain";
+ };
+
+ xmppdescription = mkOption {
+ type = with types; nullOr str;
+ default = null;
+ description = "The default XMPP server description";
+ };
+
+ xmppwhitelist = mkOption {
+ type = with types; nullOr str;
+ default = null;
+ description = "The allowlisted XMPP servers";
+ };
+ };
+ };
+ default = { };
+ description = ''
+ Pod configuration (values from `php daemon.php config --help`).
+ Note that these values will now be disabled in the admin panel.
+ '';
+ };
+
+ settings = mkOption {
+ type = with types; attrsOf (nullOr (oneOf [ int str bool ]));
+ default = { };
+ description = ".env settings for Movim. Secrets should use `secretFile` option instead. `null`s will be culled.";
+ };
+
+ secretFile = mkOption {
+ type = with types; nullOr path;
+ default = null;
+ description = "The secret file to be sourced for the .env settings.";
+ };
+
+ database = {
+ type = mkOption {
+ type = types.enum [ "mysql" "postgresql" ];
+ example = "mysql";
+ default = "postgresql";
+ description = "Database engine to use.";
+ };
+
+ name = mkOption {
+ type = types.str;
+ default = "movim";
+ description = "Database name.";
+ };
+
+ user = mkOption {
+ type = types.str;
+ default = "movim";
+ description = "Database username.";
+ };
+
+ createLocally = mkOption {
+ type = types.bool;
+ default = true;
+ description = "local database using UNIX socket authentication";
+ };
+ };
+
+ nginx = mkOption {
+ type = with types; nullOr (submodule
+ (import ../web-servers/nginx/vhost-options.nix {
+ inherit config lib;
+ }));
+ default = null;
+ example = lib.literalExpression /* nginx */ ''
+ {
+ serverAliases = [
+ "pics.''${config.networking.domain}"
+ ];
+ enableACME = true;
+ forceHttps = true;
+ }
+ '';
+ description = ''
+ With this option, you can customize an nginx virtual host which already has sensible defaults for Movim.
+ Set to `{ }` if you do not need any customization to the virtual host.
+ If enabled, then by default, the {option}`serverName` is `''${domain}`,
+ If this is set to null (the default), no nginx virtualHost will be configured.
+ '';
+ };
+
+ poolConfig = mkOption {
+ type = with types; attrsOf (oneOf [ int str bool ]);
+ default = { };
+ description = "Options for Movim’s PHP-FPM pool.";
+ };
+ };
+ };
+
+ config = mkIf cfg.enable {
+ environment.systemPackages = [ cfg.package ];
+
+ users = {
+ users = {
+ movim = mkIf (cfg.user == "movim") {
+ isSystemUser = true;
+ group = cfg.group;
+ };
+ "${config.services.nginx.user}".extraGroups = [ cfg.group ];
+ };
+ groups = {
+ ${cfg.group} = { };
+ };
+ };
+
+ services = {
+ movim = {
+ settings = mkMerge [
+ {
+ DAEMON_URL = "//${cfg.domain}";
+ DAEMON_PORT = cfg.port;
+ DAEMON_INTERFACE = "127.0.0.1";
+ DAEMON_DEBUG = cfg.debug;
+ DAEMON_VERBOSE = cfg.verbose;
+ }
+ (mkIf cfg.database.createLocally {
+ DB_DRIVER = {
+ "postgresql" = "pgsql";
+ "mysql" = "mysql";
+ }.${cfg.database.type};
+ DB_HOST = "localhost";
+ DB_PORT = config.services.${cfg.database.type}.settings.port;
+ DB_DATABASE = cfg.database.name;
+ DB_USERNAME = cfg.database.user;
+ DB_PASSWORD = "";
+ })
+ ];
+
+ poolConfig = lib.mapAttrs' (n: v: lib.nameValuePair n (lib.mkDefault v)) {
+ "pm" = "dynamic";
+ "php_admin_value[error_log]" = "stderr";
+ "php_admin_flag[log_errors]" = true;
+ "catch_workers_output" = true;
+ "pm.max_children" = 32;
+ "pm.start_servers" = 2;
+ "pm.min_spare_servers" = 2;
+ "pm.max_spare_servers" = 8;
+ "pm.max_requests" = 500;
+ };
+ };
+
+ nginx = mkIf (cfg.nginx != null) {
+ enable = true;
+ recommendedOptimisation = true;
+ recommendedGzipSettings = true;
+ recommendedBrotliSettings = true;
+ recommendedProxySettings = true;
+ # TODO: recommended cache options already in Nginx⁇
+ appendHttpConfig = /* nginx */ ''
+ fastcgi_cache_path /tmp/nginx_cache levels=1:2 keys_zone=nginx_cache:100m inactive=60m;
+ fastcgi_cache_key "$scheme$request_method$host$request_uri";
+ '';
+ virtualHosts."${cfg.domain}" = mkMerge [
+ cfg.nginx
+ {
+ root = lib.mkForce "${package}/share/php/movim/public";
+ locations = {
+ "/favicon.ico" = {
+ priority = 100;
+ extraConfig = /* nginx */ ''
+ access_log off;
+ log_not_found off;
+ '';
+ };
+ "/robots.txt" = {
+ priority = 100;
+ extraConfig = /* nginx */ ''
+ access_log off;
+ log_not_found off;
+ '';
+ };
+ "~ /\\.(?!well-known).*" = {
+ priority = 210;
+ extraConfig = /* nginx */ ''
+ deny all;
+ '';
+ };
+ # Ask nginx to cache every URL starting with "/picture"
+ "/picture" = {
+ priority = 400;
+ tryFiles = "$uri $uri/ /index.php$is_args$args";
+ extraConfig = /* nginx */ ''
+ set $no_cache 0; # Enable cache only there
+ '';
+ };
+ "/" = {
+ priority = 490;
+ tryFiles = "$uri $uri/ /index.php$is_args$args";
+ extraConfig = /* nginx */ ''
+ # https://github.com/movim/movim/issues/314
+ add_header Content-Security-Policy "default-src 'self'; img-src 'self' aesgcm: https:; media-src 'self' aesgcm: https:; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline';";
+ set $no_cache 1;
+ '';
+ };
+ "~ \\.php$" = {
+ priority = 500;
+ tryFiles = "$uri =404";
+ extraConfig = /* nginx */ ''
+ include ${config.services.nginx.package}/conf/fastcgi.conf;
+ add_header X-Cache $upstream_cache_status;
+ fastcgi_ignore_headers "Cache-Control" "Expires" "Set-Cookie";
+ fastcgi_cache nginx_cache;
+ fastcgi_cache_valid any 7d;
+ fastcgi_cache_bypass $no_cache;
+ fastcgi_no_cache $no_cache;
+ fastcgi_split_path_info ^(.+\.php)(/.+)$;
+ fastcgi_index index.php;
+ fastcgi_pass unix:${fpm.socket};
+ '';
+ };
+ "/ws/" = {
+ priority = 900;
+ proxyPass = "http://${cfg.settings.DAEMON_INTERFACE}:${builtins.toString cfg.port}/";
+ proxyWebsockets = true;
+ recommendedProxySettings = true;
+ extraConfig = /* nginx */ ''
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_redirect off;
+ '';
+ };
+ };
+ extraConfig = /* ngnix */ ''
+ index index.php;
+ '';
+ }
+ ];
+ };
+
+ mysql = mkIf (cfg.database.createLocally && cfg.database.type == "mysql") {
+ enable = mkDefault true;
+ package = mkDefault pkgs.mariadb;
+ ensureDatabases = [ cfg.database.name ];
+ ensureUsers = [{
+ name = cfg.user;
+ ensureDBOwnership = true;
+ }];
+ };
+
+ postgresql = mkIf (cfg.database.createLocally && cfg.database.type == "postgresql") {
+ enable = mkDefault true;
+ ensureDatabases = [ cfg.database.name ];
+ ensureUsers = [{
+ name = cfg.user;
+ ensureDBOwnership = true;
+ }];
+ authentication = ''
+ host ${cfg.database.name} ${cfg.database.user} localhost trust
+ '';
+ };
+
+ phpfpm.pools.${pool} =
+ let
+ socketOwner =
+ if (cfg.nginx != null)
+ then config.services.nginx.user
+ else cfg.user;
+ in
+ {
+ phpPackage = package.php;
+ user = cfg.user;
+ group = cfg.group;
+
+ phpOptions = ''
+ error_log = 'stderr'
+ log_errors = on
+ '';
+
+ settings = {
+ "listen.owner" = socketOwner;
+ "listen.group" = cfg.group;
+ "listen.mode" = "0660";
+ "catch_workers_output" = true;
+ } // cfg.poolConfig;
+ };
+ };
+
+ systemd = {
+ services.movim-data-setup = {
+ description = "Movim setup: .env file, databases init, cache reload";
+ wantedBy = [ "multi-user.target" ];
+ requiredBy = [ "${phpExecutionUnit}.service" ];
+ before = [ "${phpExecutionUnit}.service" ];
+ after = lib.optional cfg.database.createLocally dbService;
+ requires = lib.optional cfg.database.createLocally dbService;
+
+ serviceConfig = {
+ Type = "oneshot";
+ User = cfg.user;
+ Group = cfg.group;
+ UMask = "077";
+ } // lib.optionalAttrs (cfg.secretFile != null) {
+ LoadCredential = "env-secrets:${cfg.secretFile}";
+ };
+
+ script = ''
+ # Env vars
+ rm -f ${cfg.dataDir}/.env
+ cp --no-preserve=all ${configFile} ${cfg.dataDir}/.env
+ echo -e '\n' >> ${cfg.dataDir}/.env
+ if [[ -f "$CREDENTIALS_DIRECTORY/env-secrets" ]]; then
+ cat "$CREDENTIALS_DIRECTORY/env-secrets" >> ${cfg.dataDir}/.env
+ echo -e '\n' >> ${cfg.dataDir}/.env
+ fi
+
+ # Caches, logs
+ mkdir -p ${cfg.dataDir}/public/cache ${cfg.logDir} ${cfg.runtimeDir}/cache
+ chmod -R ug+rw ${cfg.dataDir}/public/cache
+ chmod -R ug+rw ${cfg.logDir}
+ chmod -R ug+rwx ${cfg.runtimeDir}/cache
+
+ # Migrations
+ MOVIM_VERSION="${package.version}"
+ if [[ ! -f "${cfg.dataDir}/.migration-version" ]] || [[ "$MOVIM_VERSION" != "$(<${cfg.dataDir}/.migration-version)" ]]; then
+ ${package}/bin/movim-composer movim:migrate && echo $MOVIM_VERSION > ${cfg.dataDir}/.migration-version
+ fi
+ ''
+ + lib.optionalString (podConfigFlags != "") (
+ let
+ flags = lib.concatStringsSep " "
+ ([ "--no-interaction" ]
+ ++ lib.optional cfg.debug "-vvv"
+ ++ lib.optional (!cfg.debug && cfg.verbose) "-v");
+ in
+ ''
+ ${lib.getExe package} config ${podConfigFlags}
+ ''
+ );
+ };
+
+ services.movim = {
+ description = "Movim daemon";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "movim-data-setup.service" ];
+ requires = [ "movim-data-setup.service" ]
+ ++ lib.optional cfg.database.createLocally dbService;
+ environment = {
+ PUBLIC_URL = "//${cfg.domain}";
+ WS_PORT = builtins.toString cfg.port;
+ };
+
+ serviceConfig = {
+ User = cfg.user;
+ Group = cfg.group;
+ WorkingDirectory = "${package}/share/php/movim";
+ ExecStart = "${lib.getExe package} start";
+ };
+ };
+
+ services.${phpExecutionUnit} = {
+ after = [ "movim-data-setup.service" ];
+ requires = [ "movim-data-setup.service" ]
+ ++ lib.optional cfg.database.createLocally dbService;
+ };
+
+ tmpfiles.settings."10-movim" = with cfg; {
+ "${dataDir}".d = { inherit user group; mode = "0710"; };
+ "${dataDir}/public".d = { inherit user group; mode = "0750"; };
+ "${dataDir}/public/cache".d = { inherit user group; mode = "0750"; };
+ "${runtimeDir}".d = { inherit user group; mode = "0700"; };
+ "${runtimeDir}/cache".d = { inherit user group; mode = "0700"; };
+ "${logDir}".d = { inherit user group; mode = "0700"; };
+ };
+ };
+ };
+}
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 7d120d6bc09e..909eea38b35e 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -558,6 +558,7 @@ in {
morty = handleTest ./morty.nix {};
mosquitto = handleTest ./mosquitto.nix {};
moosefs = handleTest ./moosefs.nix {};
+ movim = discoverTests (import ./web-apps/movim { inherit handleTestOn; });
mpd = handleTest ./mpd.nix {};
mpv = handleTest ./mpv.nix {};
mtp = handleTest ./mtp.nix {};
diff --git a/nixos/tests/web-apps/movim/default.nix b/nixos/tests/web-apps/movim/default.nix
new file mode 100644
index 000000000000..5d6314e2b41b
--- /dev/null
+++ b/nixos/tests/web-apps/movim/default.nix
@@ -0,0 +1,8 @@
+{ system ? builtins.currentSystem, handleTestOn }:
+
+let
+ supportedSystems = [ "x86_64-linux" "i686-linux" ];
+in
+{
+ standard = handleTestOn supportedSystems ./standard.nix { inherit system; };
+}
diff --git a/nixos/tests/web-apps/movim/standard.nix b/nixos/tests/web-apps/movim/standard.nix
new file mode 100644
index 000000000000..470d81d8f722
--- /dev/null
+++ b/nixos/tests/web-apps/movim/standard.nix
@@ -0,0 +1,102 @@
+import ../../make-test-python.nix ({ lib, pkgs, ... }:
+
+let
+ movim = {
+ domain = "movim.local";
+ info = "No ToS in tests";
+ description = "NixOS testing server";
+ };
+ xmpp = {
+ domain = "xmpp.local";
+ admin = rec {
+ JID = "${username}@${xmpp.domain}";
+ username = "romeo";
+ password = "juliet";
+ };
+ };
+in
+{
+ name = "movim-standard";
+
+ meta = {
+ maintainers = with pkgs.lib.maintainers; [ toastal ];
+ };
+
+ nodes = {
+ server = { pkgs, ... }: {
+ services.movim = {
+ inherit (movim) domain;
+ enable = true;
+ verbose = true;
+ podConfig = {
+ inherit (movim) description info;
+ xmppdomain = xmpp.domain;
+ };
+ nginx = { };
+ };
+
+ services.prosody = {
+ enable = true;
+ xmppComplianceSuite = false;
+ disco_items = [
+ { url = "upload.${xmpp.domain}"; description = "File Uploads"; }
+ ];
+ virtualHosts."${xmpp.domain}" = {
+ inherit (xmpp) domain;
+ enabled = true;
+ extraConfig = ''
+ Component "pubsub.${xmpp.domain}" "pubsub"
+ pubsub_max_items = 10000
+ expose_publisher = true
+
+ Component "upload.${xmpp.domain}" "http_file_share"
+ http_external_url = "http://upload.${xmpp.domain}"
+ http_file_share_expires_after = 300 * 24 * 60 * 60
+ http_file_share_size_limit = 1024 * 1024 * 1024
+ http_file_share_daily_quota = 4 * 1024 * 1024 * 1024
+ '';
+ };
+ extraConfig = ''
+ pep_max_items = 10000
+
+ http_paths = {
+ file_share = "/";
+ }
+ '';
+ };
+
+ networking.extraHosts = ''
+ 127.0.0.1 ${movim.domain}
+ 127.0.0.1 ${xmpp.domain}
+ '';
+ };
+ };
+
+ testScript = /* python */ ''
+ server.wait_for_unit("phpfpm-movim.service")
+ server.wait_for_unit("nginx.service")
+ server.wait_for_open_port(80)
+
+ server.wait_for_unit("prosody.service")
+ server.succeed('prosodyctl status | grep "Prosody is running"')
+ server.succeed("prosodyctl register ${xmpp.admin.username} ${xmpp.domain} ${xmpp.admin.password}")
+
+ server.wait_for_unit("movim.service")
+
+ # Test unauthenticated
+ server.fail("curl -L --fail-with-body --max-redirs 0 http://${movim.domain}/chat")
+
+ # Test basic Websocket
+ server.succeed("echo \"\" | ${lib.getExe pkgs.websocat} 'ws://${movim.domain}/ws/?path=login&offset=0' --origin 'http://${movim.domain}'")
+
+ # Test login + create cookiejar
+ login_html = server.succeed("curl --fail-with-body -c /tmp/cookies http://${movim.domain}/login")
+ assert "${movim.description}" in login_html
+ assert "${movim.info}" in login_html
+
+ # Test authentication POST
+ server.succeed("curl --fail-with-body -b /tmp/cookies -X POST --data-urlencode 'username=${xmpp.admin.JID}' --data-urlencode 'password=${xmpp.admin.password}' http://${movim.domain}/login")
+
+ server.succeed("curl -L --fail-with-body --max-redirs 1 -b /tmp/cookies http://${movim.domain}/chat")
+ '';
+})
diff --git a/pkgs/by-name/mo/movim/package.nix b/pkgs/by-name/mo/movim/package.nix
index 0225dbc98018..04695835710f 100644
--- a/pkgs/by-name/mo/movim/package.nix
+++ b/pkgs/by-name/mo/movim/package.nix
@@ -1,10 +1,12 @@
{ lib
+, fetchpatch
, fetchFromGitHub
, dash
, php
, phpCfg ? null
, withPgsql ? true # “strongly recommended” according to docs
, withMysql ? false
+, nixosTests
}:
php.buildComposerProject (finalAttrs: {
@@ -36,6 +38,20 @@ php.buildComposerProject (finalAttrs: {
vendorHash = "sha256-RFIi1I+gcagRgkDpgQeR1oGJeBGA7z9q3DCfW+ZDr2Y=";
postPatch = ''
+ # Our modules are already wrapped, removes missing *.so warnings;
+ # replacing `$configuration` with actually-used flags.
+ substituteInPlace src/Movim/Daemon/Session.php \
+ --replace-fail "exec php ' . \$configuration " "exec php -dopcache.enable=1 -dopcache.enable_cli=1 ' "
+
+ # Point to PHP + PHP INI in the Nix store
+ substituteInPlace src/Movim/{Console/DaemonCommand.php,Daemon/Session.php} \
+ --replace-fail "exec php " "exec ${lib.getExe finalAttrs.php} "
+ substituteInPlace src/Movim/Console/DaemonCommand.php \
+ --replace-fail "php vendor/bin/phinx migrate" \
+ "${lib.getBin finalAttrs.php} vendor/bin/phinx migrate" \
+ --replace-fail "php daemon.php setAdmin {jid}" \
+ "${finalAttrs.meta.mainProgram} setAdmin {jid}"
+
# BUGFIX: Imagick API Changes for 7.x+
# See additionally: https://github.com/movim/movim/pull/1122
substituteInPlace src/Movim/Image.php \
@@ -56,6 +72,10 @@ php.buildComposerProject (finalAttrs: {
chmod +x $out/share/{bash-completion/completion/movim.bash,fish/vendor_completions.d/movim.fish,zsh/site-functions/_movim}
'';
+ passthru = {
+ tests = { inherit (nixosTests) movim; };
+ };
+
meta = {
description = "a federated blogging & chat platform that acts as a web front end for the XMPP protocol";
homepage = "https://movim.eu";