Compare commits

...
Author SHA1 Message Date
renovate[bot] 714ca5a3d8 Update dependency tianon/gosu to v1.20
Signed-off-by: milkmaker <milkmaker@mailcow.de>
2026-10-25 13:00:37 +00:00
milkmakerandciri-cuervo 82b1cf7198 [Web] Updated lang.es-es.json (#7542)
Co-authored-by: ciri-cuervo <nicolascirigliano@gmail.com>
2026-10-22 17:54:52 +02:00
FreddleSpl0it 036607b4bf Merge pull request #7534 from mailcow/feat/php-patch
[PHP] Update base to Alpine 3.24, build PECL extensions from GitHub
2026-10-21 19:29:43 +02:00
FreddleSpl0it 2bad774bcc [PHP] Update to 8.2.35 2026-10-21 16:28:17 +02:00
FreddleSpl0it d833b9696f Merge pull request #7535 from mailcow/feat/nginx-patch
[Nginx] Update to 1.30.6
2026-10-21 14:14:41 +02:00
FreddleSpl0it 7d41484d2a Merge pull request #7536 from mailcow/feat/sogo-patch
[SOGo] Update to 5.12.12
2026-10-21 14:14:13 +02:00
FreddleSpl0it 9e4c7ef038 [SOGo] Update to 5.12.12 2026-10-21 14:08:15 +02:00
FreddleSpl0it 1f04122eca [Nginx] Update to 1.30.6 2026-10-21 13:41:43 +02:00
FreddleSpl0it 015fd04a37 [PHP] Update base to Alpine 3.24, build PECL extensions from GitHub 2026-10-21 11:06:14 +02:00
milkmakerandSchwindelhub ca8546fa4a [Web] Updated lang.de-de.json (#7527)
Co-authored-by: Schwindelhub <github@netbla.de>
2026-10-20 20:34:32 +02:00
milkmakerandMatjaž Tekavec 6f6edfb85e [Web] Updated lang.si-si.json (#7525)
Co-authored-by: Matjaž Tekavec <matjaz@moj-svet.si>
2026-10-19 18:40:37 +02:00
FreddleSpl0it 6c877fc0ac Merge pull request #7520 from mailcow/feat/update-ui
Feat/update UI
2026-10-15 15:31:18 +02:00
FreddleSpl0it 4a29767a31 [Web] Add role color marker and icons to login pages 2026-10-15 15:23:29 +02:00
FreddleSpl0it cd29426933 [Web] Fix minor customize and theme ui issues 2026-10-15 15:15:42 +02:00
FreddleSpl0it 37be306f1c [Web] change default theme label 2026-10-15 15:10:49 +02:00
FreddleSpl0it a351017455 [Web] Update bootstrap-select css to 1.14.0-beta3 2026-10-15 14:47:19 +02:00
FreddleSpl0it 887b8a3594 [Web] Add servercow theme 2026-10-15 14:46:57 +02:00
FreddleSpl0it 571c1b5b29 [Web] Update Bootstrap to 5.3.8, add theme support 2026-10-15 14:43:06 +02:00
FreddleSpl0it 8cd231c9b9 Merge pull request #7505 from mailcow/feat/rspamd-4.2.1
[Rspamd] Update to 4.2.1
2026-10-06 10:02:49 +02:00
FreddleSpl0it 74facc4b5b [Rspamd] update to 4.2.1 2026-10-06 09:41:51 +02:00
FreddleSpl0it ff6329d01b [Rspamd] Fix CTE and boundary prefix in domain wide footer 2026-10-06 09:41:34 +02:00
FreddleSpl0it e8209d6504 Merge pull request #7504 from ralfbergs/fix/typo-resovler
Fixed typo (changing RESOVLER to RESOLVER).
2026-10-06 09:21:33 +02:00
FreddleSpl0it 626d006a3d Merge pull request #7484 from oidipos/feat/tlspol
[postfix-tlspol] bump to v1.14.0
2026-10-06 09:20:47 +02:00
Ralf Bergs e88c734d0a Fixed typo (changing RESOVLER to RESOLVER). 2026-10-03 12:30:32 +02:00
milkmaker 86bba266f6 update postscreen_access.cidr (#7494) 2026-10-01 08:03:05 +02:00
oidipos e3ed8d2fc2 bump postfix-tlspol to v1.14.0 2026-09-25 21:23:58 +02:00
FreddleSpl0it 88d92261e4 Merge pull request #7432 from mailcow/renovate/redis-7.x
Update redis Docker tag to v7.4.11
2026-09-21 09:32:35 +02:00
FreddleSpl0it 017d0c7ff0 Merge pull request #7475 from mailcow/feat/sogo-5.12.11
[SOGo] Update to 5.12.11
2026-09-21 08:46:32 +02:00
FreddleSpl0it a28fa19044 [SOGo] Update to 5.12.11 2026-09-21 08:43:16 +02:00
FreddleSpl0it fa2ad14fc4 Merge pull request #7473 from mailcow/feat/unbound-1.26.1
[Unbound] Update to 1.26.1
2026-09-21 08:29:13 +02:00
FreddleSpl0it a480a96e10 [Unbound] Update to 1.26.1 2026-09-21 08:23:03 +02:00
renovate[bot] 42d387ca42 Update redis Docker tag to v7.4.11
Signed-off-by: milkmaker <milkmaker@mailcow.de>
2026-09-15 16:52:39 +00:00
milkmakerandStefano fe4e675754 [Web] Updated lang.it-it.json (#7460)
Co-authored-by: Stefano <stefano.vassena@gmail.com>
2026-09-09 22:28:28 +02:00
b05f59b705 Translations update from Weblate (#7457)
* [Web] Updated lang.si-si.json

Co-authored-by: Matjaž Tekavec <matjaz@moj-svet.si>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

* [Web] Updated lang.zh-cn.json

Co-authored-by: 雨 <luotianyi@luotianyi.me>

---------

Co-authored-by: Matjaž Tekavec <matjaz@moj-svet.si>
Co-authored-by: 雨 <luotianyi@luotianyi.me>
2026-09-07 21:38:12 +02:00
milkmaker c4d0ca56d2 update postscreen_access.cidr (#7450) 2026-09-01 13:07:54 +02:00
7ec61fde8a Translations update from Weblate (#7431)
* [Web] Updated lang.it-it.json

Co-authored-by: Claudio <cb@bastard.it>

* [Web] Updated lang.si-si.json

Co-authored-by: Matjaž Tekavec <matjaz@moj-svet.si>

---------

Co-authored-by: Claudio <cb@bastard.it>
Co-authored-by: Matjaž Tekavec <matjaz@moj-svet.si>
2026-08-19 20:23:04 +02:00
56 changed files with 5647 additions and 3273 deletions

No files matched your search

+2
View File
@@ -64,6 +64,8 @@ data/web/templates/cache/*
!data/web/templates/cache/.gitkeep
data/web/.well-known/acme-challenge
data/web/css/build/0081-custom-mailcow.css
data/web/css/themes/*/
!data/web/css/themes/servercow/
data/web/inc/vars.local.inc.php
data/web/inc/app_info.inc.php
data/web/nextcloud*/
+1 -1
View File
@@ -3,7 +3,7 @@ FROM alpine:3.21
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^(?<version>.*)$
ARG GOSU_VERSION=1.19
ARG GOSU_VERSION=1.20
ENV LANG=C.UTF-8
ENV LC_ALL=C.UTF-8
+1 -1
View File
@@ -1,4 +1,4 @@
FROM nginx:1.30.4-alpine
FROM nginx:1.30.6-alpine
LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
ENV PIP_BREAK_SYSTEM_PACKAGES=1
+13 -8
View File
@@ -1,4 +1,4 @@
FROM php:8.2-fpm-alpine3.21
FROM php:8.2-fpm-alpine3.24
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
@@ -47,6 +47,7 @@ RUN apk add -U --no-cache autoconf \
libressl-dev \
librsvg \
libtool \
libwebp \
libwebp-dev \
libxml2-dev \
libxpm \
@@ -63,13 +64,16 @@ RUN apk add -U --no-cache autoconf \
samba-client \
zlib-dev \
tzdata \
&& pecl install APCu-${APCU_PECL_VERSION} \
&& pecl install imagick-${IMAGICK_PECL_VERSION} \
&& pecl install mailparse-${MAILPARSE_PECL_VERSION} \
&& pecl install memcached-${MEMCACHED_PECL_VERSION} \
&& pecl install redis-${REDIS_PECL_VERSION} \
&& docker-php-ext-enable apcu imagick memcached mailparse redis \
&& pecl clear-cache \
&& docker-php-source extract \
# PECL extensions from their GitHub tags (pecl.php.net is not reliable enough for builds)
&& mkdir -p /usr/src/php/ext/apcu /usr/src/php/ext/imagick /usr/src/php/ext/mailparse /usr/src/php/ext/memcached /usr/src/php/ext/redis \
&& curl -fsSL https://github.com/krakjoe/apcu/archive/refs/tags/v${APCU_PECL_VERSION}.tar.gz | tar -xz --strip-components=1 -C /usr/src/php/ext/apcu \
&& curl -fsSL https://github.com/Imagick/imagick/archive/refs/tags/${IMAGICK_PECL_VERSION}.tar.gz | tar -xz --strip-components=1 -C /usr/src/php/ext/imagick \
&& sed -i "s/@PACKAGE_VERSION@/${IMAGICK_PECL_VERSION}/" /usr/src/php/ext/imagick/php_imagick.h \
&& curl -fsSL https://github.com/php/pecl-mail-mailparse/archive/refs/tags/v${MAILPARSE_PECL_VERSION}.tar.gz | tar -xz --strip-components=1 -C /usr/src/php/ext/mailparse \
&& curl -fsSL https://github.com/php-memcached-dev/php-memcached/archive/refs/tags/v${MEMCACHED_PECL_VERSION}.tar.gz | tar -xz --strip-components=1 -C /usr/src/php/ext/memcached \
&& curl -fsSL https://github.com/phpredis/phpredis/archive/refs/tags/${REDIS_PECL_VERSION}.tar.gz | tar -xz --strip-components=1 -C /usr/src/php/ext/redis \
&& docker-php-ext-install -j 4 apcu imagick mailparse memcached redis \
&& docker-php-ext-configure intl \
&& docker-php-ext-configure exif \
&& docker-php-ext-configure gd --with-freetype=/usr/include/ \
@@ -83,6 +87,7 @@ RUN apk add -U --no-cache autoconf \
&& curl --silent --show-error https://getcomposer.org/installer | php -- --version=${COMPOSER_VERSION} \
&& mv composer.phar /usr/local/bin/composer \
&& chmod +x /usr/local/bin/composer \
&& docker-php-source delete \
&& apk del --purge autoconf \
aspell-dev \
cyrus-sasl-dev \
+1 -1
View File
@@ -4,7 +4,7 @@ WORKDIR /src
ENV CGO_ENABLED=0 \
GO111MODULE=on \
NOOPT=1 \
VERSION=1.11.0
VERSION=1.14.0
RUN git clone --branch v${VERSION} https://github.com/Zuplu/postfix-tlspol && \
cd /src/postfix-tlspol && \
+1 -1
View File
@@ -2,7 +2,7 @@ FROM debian:trixie-slim
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
ARG DEBIAN_FRONTEND=noninteractive
ARG RSPAMD_VER=rspamd_4.1.4-1~beb659b
ARG RSPAMD_VER=rspamd_4.2.1-1~b7a16ae
ARG CODENAME=trixie
ENV LC_ALL=C
+4 -4
View File
@@ -1,6 +1,6 @@
# SOGo built from source to enable security patch application
# Repository: https://github.com/Alinto/sogo
# Version: SOGo-5.12.9
# Version: SOGo-5.12.12
#
# Applied security patches:
# -
@@ -12,12 +12,12 @@ FROM debian:bookworm
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
ARG DEBIAN_FRONTEND=noninteractive
ARG SOGO_VERSION=SOGo-5.12.10
ARG SOPE_VERSION=SOPE-5.12.10
ARG SOGO_VERSION=SOGo-5.12.12
ARG SOPE_VERSION=SOPE-5.12.12
# Security patches to apply (space-separated commit hashes)
ARG SOGO_SECURITY_PATCHES=""
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^(?<version>.*)$
ARG GOSU_VERSION=1.19
ARG GOSU_VERSION=1.20
ENV LC_ALL=C
# Install dependencies, build SOPE and SOGo, then clean up (all in one layer to minimize image size)
+2 -1
View File
@@ -3,7 +3,8 @@ FROM alpine:3.23
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
# install unbound from alpine:edge to get security patches
RUN apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/edge/main unbound
# minimum version is pinned
RUN apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/edge/main "unbound>=1.26.1-r0"
# install other packages from regular alpine stable repo
RUN apk add --update --no-cache \
+2
View File
@@ -1,3 +1,5 @@
max_execution_time = 3600
max_input_time = 3600
memory_limit = 512M
; not used by mailcow, disabled as hardening (PHP security release 2026-10-21)
session.upload_progress.enabled = Off
+28 -38
View File
@@ -1,6 +1,6 @@
# Whitelist generated by Postwhite v3.4 on Sat Aug 1 00:35:20 UTC 2026
# Whitelist generated by Postwhite v3.4 on Thu Oct 1 00:35:20 UTC 2026
# https://github.com/stevejenkins/postwhite/
# 2256 total rules
# 2246 total rules
2a00:1450:4000::/36 permit
2a00:1450:4864::/56 permit
2a01:111:f400::/48 permit
@@ -125,7 +125,6 @@
23.249.208.0/20 permit
23.251.224.0/19 permit
23.253.141.0/24 permit
23.253.182.0/23 permit
23.253.182.103 permit
23.253.183.145 permit
23.253.183.146 permit
@@ -147,24 +146,7 @@
27.123.206.80/28 permit
31.47.251.17 permit
31.186.239.0/24 permit
34.2.64.0/22 permit
34.2.68.0/23 permit
34.2.70.0/23 permit
34.2.71.64/26 permit
34.2.72.0/22 permit
34.2.75.0/26 permit
34.2.78.0/23 permit
34.2.80.0/23 permit
34.2.82.0/23 permit
34.2.84.0/24 permit
34.2.84.64/26 permit
34.2.85.0/24 permit
34.2.85.64/26 permit
34.2.86.0/23 permit
34.2.88.0/23 permit
34.2.90.0/23 permit
34.2.92.0/23 permit
34.2.94.0/23 permit
34.2.64.0/19 permit
34.70.158.162 permit
34.74.74.140 permit
34.83.159.189 permit
@@ -191,6 +173,7 @@
35.205.92.9 permit
35.228.216.85 permit
35.242.169.159 permit
37.187.220.204 permit
37.188.97.188 permit
37.218.248.47 permit
37.218.249.47 permit
@@ -264,8 +247,8 @@
46.228.39.112/31 permit
46.243.88.174 permit
46.243.88.175 permit
46.243.88.176 permit
46.243.88.177 permit
46.243.88.176/30 permit
46.243.95.96/27 permit
46.243.95.179 permit
46.243.95.180 permit
50.16.246.183 permit
@@ -283,6 +266,7 @@
50.56.130.221 permit
50.56.130.222 permit
50.112.246.219 permit
50.152.146.25 permit
51.83.17.38 permit
52.1.14.157 permit
52.5.230.59 permit
@@ -374,6 +358,7 @@
64.127.115.252 permit
64.132.88.0/23 permit
64.132.92.0/24 permit
64.181.136.241 permit
64.181.194.190 permit
64.181.213.254 permit
64.207.219.7 permit
@@ -654,6 +639,7 @@
79.135.106.0/24 permit
79.135.107.0/24 permit
80.225.160.128/25 permit
81.88.38.0/23 permit
81.169.146.243 permit
81.169.146.245 permit
81.169.146.246 permit
@@ -738,6 +724,7 @@
96.43.148.64/28 permit
96.43.148.64/31 permit
96.43.151.64/28 permit
98.77.0.0/16 permit
98.97.248.0/21 permit
98.136.44.181 permit
98.136.44.182/31 permit
@@ -1233,8 +1220,6 @@
104.43.243.237 permit
104.44.112.128/25 permit
104.47.0.0/17 permit
104.130.96.0/28 permit
104.130.122.0/23 permit
106.10.144.64/27 permit
106.10.144.100/31 permit
106.10.144.103 permit
@@ -1456,6 +1441,7 @@
130.35.116.0/25 permit
130.61.9.72 permit
130.162.39.83 permit
130.162.194.23 permit
130.248.172.0/24 permit
130.248.173.0/24 permit
131.186.12.0/25 permit
@@ -1466,8 +1452,7 @@
132.226.49.32 permit
132.226.56.24 permit
134.98.248.128/25 permit
134.128.64.0/19 permit
134.128.96.0/19 permit
134.128.64.0/18 permit
134.170.27.8 permit
134.170.113.0/26 permit
134.170.141.64/26 permit
@@ -1539,9 +1524,6 @@
146.20.14.105 permit
146.20.14.106 permit
146.20.14.107 permit
146.20.112.0/26 permit
146.20.113.0/24 permit
146.20.191.0/24 permit
146.20.215.0/24 permit
146.20.215.182 permit
146.88.28.0/24 permit
@@ -1564,10 +1546,11 @@
149.97.173.180 permit
149.118.160.128/25 permit
150.136.21.199 permit
150.171.109.72 permit
150.171.109.178 permit
150.230.98.160 permit
151.145.38.14 permit
152.67.105.195 permit
152.69.165.251 permit
152.69.200.236 permit
152.70.155.126 permit
155.248.135.128/25 permit
@@ -1589,6 +1572,8 @@
158.247.16.0/20 permit
158.247.100.0/25 permit
159.13.4.0/25 permit
159.13.33.181 permit
159.26.176.0/20 permit
159.92.154.0/24 permit
159.92.155.0/24 permit
159.92.157.0/24 permit
@@ -1616,6 +1601,8 @@
159.183.121.182 permit
159.183.129.172 permit
160.1.62.192 permit
161.33.94.110 permit
161.33.228.244 permit
161.38.192.0/20 permit
161.38.204.0/22 permit
161.71.32.0/19 permit
@@ -1647,7 +1634,6 @@
165.173.182.0/24 permit
165.173.182.250/31 permit
165.173.189.205 permit
166.78.68.0/22 permit
166.78.68.221 permit
166.78.69.169 permit
166.78.69.170 permit
@@ -1721,6 +1707,8 @@
175.41.215.51 permit
176.32.105.0/24 permit
176.32.127.0/24 permit
176.94.34.32/27 permit
176.94.34.64/27 permit
178.236.10.128/26 permit
182.50.76.0/22 permit
182.50.78.64/28 permit
@@ -1802,9 +1790,9 @@
188.125.85.234/31 permit
188.125.85.236/31 permit
188.125.85.238 permit
188.165.51.139 permit
188.172.128.0/20 permit
192.0.64.0/18 permit
192.9.166.184 permit
192.18.139.154 permit
192.18.145.36 permit
192.18.152.58 permit
@@ -1829,7 +1817,6 @@
192.30.252.0/22 permit
192.161.144.0/20 permit
192.162.87.0/24 permit
192.237.158.0/23 permit
192.237.159.42 permit
192.237.159.43 permit
192.254.112.0/20 permit
@@ -1872,7 +1859,6 @@
198.21.0.0/21 permit
198.37.144.0/20 permit
198.37.152.186 permit
198.61.254.0/23 permit
198.61.254.21 permit
198.61.254.231 permit
198.178.234.57 permit
@@ -1959,6 +1945,7 @@
204.14.232.0/21 permit
204.14.232.64/28 permit
204.14.234.64/28 permit
204.75.18.128/27 permit
204.75.142.0/24 permit
204.92.114.187 permit
204.92.114.203 permit
@@ -1966,7 +1953,9 @@
204.141.32.0/23 permit
204.141.42.0/23 permit
204.216.164.202 permit
204.220.160.0/21 permit
204.220.90.0/23 permit
204.220.92.0/22 permit
204.220.160.0/20 permit
204.220.168.0/21 permit
204.220.176.0/20 permit
204.220.181.105 permit
@@ -2002,6 +1991,8 @@
207.211.31.0/25 permit
207.211.41.113 permit
207.211.132.0/25 permit
207.211.147.87 permit
207.211.159.110 permit
207.218.90.0/24 permit
207.218.90.122 permit
207.250.68.0/24 permit
@@ -2051,7 +2042,6 @@
209.43.22.0/28 permit
209.46.117.168 permit
209.46.117.179 permit
209.61.151.0/24 permit
209.61.151.236 permit
209.61.151.249 permit
209.61.151.251 permit
@@ -2238,7 +2228,7 @@
2001:748:400:3301::4 permit
2404:6800:4000::/36 permit
2404:6800:4864::/56 permit
2603:1061:14:75::1 permit
2603:1061:14:1d0::1 permit
2607:13c0:0001:0000:0000:0000:0000:7000/116 permit
2607:13c0:0002:0000:0000:0000:0000:1000/116 permit
2607:13c0:0004:0000:0000:0000:0000:0000/116 permit
+6 -4
View File
@@ -856,6 +856,8 @@ rspamd_config:register_symbol({
local seen_cte
local newline_s = newline(task)
-- use the CTE add_text_footer actually applied
local new_cte = rewrite.new_cte or 'quoted-printable'
local function rewrite_ct_cb(name, hdr)
if rewrite.need_rewrite_ct then
@@ -877,13 +879,13 @@ rspamd_config:register_symbol({
return
elseif name:lower() == 'content-transfer-encoding' then
out[#out + 1] = string.format('%s: %s',
'Content-Transfer-Encoding', 'quoted-printable')
'Content-Transfer-Encoding', new_cte)
-- update Content-Transfer-Encoding header
task:set_milter_reply({
remove_headers = {['Content-Transfer-Encoding'] = 0},
})
task:set_milter_reply({
add_headers = {['Content-Transfer-Encoding'] = 'quoted-printable'}
add_headers = {['Content-Transfer-Encoding'] = new_cte}
})
seen_cte = true
return
@@ -895,7 +897,7 @@ rspamd_config:register_symbol({
task:headers_foreach(rewrite_ct_cb, {full = true})
if not seen_cte and rewrite.need_rewrite_ct then
out[#out + 1] = string.format('%s: %s', 'Content-Transfer-Encoding', 'quoted-printable')
out[#out + 1] = string.format('%s: %s', 'Content-Transfer-Encoding', new_cte)
end
-- End of headers
@@ -914,7 +916,7 @@ rspamd_config:register_symbol({
out_parts[#out_parts + 1] = o
out_parts[#out_parts + 1] = newline_s
else
local removePrefix = "--\x0D\x0AContent-Type"
local removePrefix = "--\x0D\x0AContent-"
if string.lower(string.sub(tostring(o[1]), 1, string.len(removePrefix))) == string.lower(removePrefix) then
o[1] = string.sub(tostring(o[1]), string.len("--\x0D\x0A") + 1)
end
+7 -7
View File
@@ -153,12 +153,12 @@ foreach ($rcpts as $rcpt) {
// Loop through all found gotos
foreach ($gotos_array as $index => &$goto) {
error_log("RCPT RESOVLER: http pipe: query " . $goto . " as username from mailbox" . PHP_EOL);
error_log("RCPT RESOLVER: http pipe: query " . $goto . " as username from mailbox" . PHP_EOL);
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :goto AND (`active`= '1' OR `active`= '2');");
$stmt->execute(array(':goto' => $goto));
$username = $stmt->fetch(PDO::FETCH_ASSOC)['username'];
if (!empty($username)) {
error_log("RCPT RESOVLER: http pipe: mailbox found: " . $username . PHP_EOL);
error_log("RCPT RESOLVER: http pipe: mailbox found: " . $username . PHP_EOL);
// Current goto is a mailbox, save to rcpt_final_mailboxes if not a duplicate
if (!in_array($username, $rcpt_final_mailboxes)) {
$rcpt_final_mailboxes[] = $username;
@@ -167,21 +167,21 @@ foreach ($rcpts as $rcpt) {
else {
$parsed_goto = parse_email($goto);
if (!$redis->hGet('DOMAIN_MAP', $parsed_goto['domain'])) {
error_log("RCPT RESOVLER:" . $goto . " is not a mailcow handled mailbox or alias address" . PHP_EOL);
error_log("RCPT RESOLVER:" . $goto . " is not a mailcow handled mailbox or alias address" . PHP_EOL);
}
else {
$stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :goto AND `active` = '1'");
$stmt->execute(array(':goto' => $goto));
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
if ($goto_branch) {
error_log("RCPT RESOVLER: http pipe: goto address " . $goto . " is an alias branch for " . $goto_branch . PHP_EOL);
error_log("RCPT RESOLVER: http pipe: goto address " . $goto . " is an alias branch for " . $goto_branch . PHP_EOL);
$goto_branch_array = explode(',', $goto_branch);
} else {
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain AND `active` = '1'");
$stmt->execute(array(':domain' => $parsed_goto['domain']));
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['target_domain'];
if ($goto_branch) {
error_log("RCPT RESOVLER: http pipe: goto domain " . $parsed_goto['domain'] . " is a domain alias branch for " . $goto_branch . PHP_EOL);
error_log("RCPT RESOLVER: http pipe: goto domain " . $parsed_goto['domain'] . " is a domain alias branch for " . $goto_branch . PHP_EOL);
$goto_branch_array = array($parsed_goto['local'] . '@' . $goto_branch);
}
}
@@ -203,11 +203,11 @@ foreach ($rcpts as $rcpt) {
// Force exit if loop cannot be solved
// Postfix does not allow for alias loops, so this should never happen.
$loop_c++;
error_log("RCPT RESOVLER: http pipe: goto array count on loop #". $loop_c . " is " . count($gotos_array) . PHP_EOL);
error_log("RCPT RESOLVER: http pipe: goto array count on loop #". $loop_c . " is " . count($gotos_array) . PHP_EOL);
}
}
catch (PDOException $e) {
error_log("RCPT RESOVLER: " . $e->getMessage() . PHP_EOL);
error_log("RCPT RESOLVER: " . $e->getMessage() . PHP_EOL);
http_response_code(502);
exit;
}
+7 -7
View File
@@ -139,12 +139,12 @@ foreach ($rcpts as $rcpt) {
// Loop through all found gotos
foreach ($gotos_array as $index => &$goto) {
error_log("RCPT RESOVLER: http pipe: query " . $goto . " as username from mailbox" . PHP_EOL);
error_log("RCPT RESOLVER: http pipe: query " . $goto . " as username from mailbox" . PHP_EOL);
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :goto AND (`active`= '1' OR `active`= '2');");
$stmt->execute(array(':goto' => $goto));
$username = $stmt->fetch(PDO::FETCH_ASSOC)['username'];
if (!empty($username)) {
error_log("RCPT RESOVLER: http pipe: mailbox found: " . $username . PHP_EOL);
error_log("RCPT RESOLVER: http pipe: mailbox found: " . $username . PHP_EOL);
// Current goto is a mailbox, save to rcpt_final_mailboxes if not a duplicate
if (!in_array($username, $rcpt_final_mailboxes)) {
$rcpt_final_mailboxes[] = $username;
@@ -153,21 +153,21 @@ foreach ($rcpts as $rcpt) {
else {
$parsed_goto = parse_email($goto);
if (!$redis->hGet('DOMAIN_MAP', $parsed_goto['domain'])) {
error_log("RCPT RESOVLER:" . $goto . " is not a mailcow handled mailbox or alias address" . PHP_EOL);
error_log("RCPT RESOLVER:" . $goto . " is not a mailcow handled mailbox or alias address" . PHP_EOL);
}
else {
$stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :goto AND `active` = '1'");
$stmt->execute(array(':goto' => $goto));
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
if ($goto_branch) {
error_log("RCPT RESOVLER: http pipe: goto address " . $goto . " is an alias branch for " . $goto_branch . PHP_EOL);
error_log("RCPT RESOLVER: http pipe: goto address " . $goto . " is an alias branch for " . $goto_branch . PHP_EOL);
$goto_branch_array = explode(',', $goto_branch);
} else {
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain AND `active` = '1'");
$stmt->execute(array(':domain' => $parsed_goto['domain']));
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['target_domain'];
if ($goto_branch) {
error_log("RCPT RESOVLER: http pipe: goto domain " . $parsed_goto['domain'] . " is a domain alias branch for " . $goto_branch . PHP_EOL);
error_log("RCPT RESOLVER: http pipe: goto domain " . $parsed_goto['domain'] . " is a domain alias branch for " . $goto_branch . PHP_EOL);
$goto_branch_array = array($parsed_goto['local'] . '@' . $goto_branch);
}
}
@@ -189,11 +189,11 @@ foreach ($rcpts as $rcpt) {
// Force exit if loop cannot be solved
// Postfix does not allow for alias loops, so this should never happen.
$loop_c++;
error_log("RCPT RESOVLER: http pipe: goto array count on loop #". $loop_c . " is " . count($gotos_array) . PHP_EOL);
error_log("RCPT RESOLVER: http pipe: goto array count on loop #". $loop_c . " is " . count($gotos_array) . PHP_EOL);
}
}
catch (PDOException $e) {
error_log("RCPT RESOVLER: " . $e->getMessage() . PHP_EOL);
error_log("RCPT RESOLVER: " . $e->getMessage() . PHP_EOL);
http_response_code(502);
exit;
}
+2
View File
@@ -115,6 +115,8 @@ $template_data = [
'logo_dark_specs' => customize('get', 'main_logo_dark_specs'),
'ip_check' => customize('get', 'ip_check'),
'custom_login' => customize('get', 'custom_login'),
'ui_themes' => customize('get', 'ui_themes'),
'ui_theme' => customize('get', 'ui_theme'),
'password_complexity' => password_complexity('get'),
'show_rspamd_global_filters' => @$_SESSION['show_rspamd_global_filters'],
'cors_settings' => $cors_settings,
+8 -9
View File
@@ -1,10 +1,10 @@
/*!
* Bootstrap-select v1.14.0-beta2 (https://developer.snapappointments.com/bootstrap-select)
*
* Copyright 2012-2021 SnapAppointments, LLC
* Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE)
*/
/*!
* Bootstrap-select v1.14.0-beta3 (https://developer.snapappointments.com/bootstrap-select)
*
* Copyright 2012-2022 SnapAppointments, LLC
* Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE)
*/
@-webkit-keyframes bs-notify-fadeOut {
0% {
opacity: 0.9;
@@ -35,8 +35,7 @@ select.selectpicker {
display: none !important;
}
.bootstrap-select {
width: 220px \0;
/*IE9 and below*/
width: 220px;
vertical-align: middle;
}
.bootstrap-select > .dropdown-toggle {
+5 -5
View File
@@ -17,15 +17,15 @@ table.dataTable>tbody>tr.child ul.dtr-details>li {
}
table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control:before:hover,
table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control:before:hover {
background-color: #5e5e5e;
background-color: var(--mc-dtr-hover-bg);
}
table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control:before,
table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control:before,
table.dataTable td.dt-control:before {
background-color: #979797 !important;
border: 1.5px solid #616161 !important;
background-color: var(--mc-dtr-bg) !important;
border: 1.5px solid var(--mc-dtr-border) !important;
border-radius: 2px !important;
color: #fff;
color: var(--mc-dtr-color);
height: 1em;
width: 1em;
line-height: 1.25em;
@@ -37,7 +37,7 @@ table.dataTable td.dt-control:before {
table.dataTable.dtr-inline.collapsed>tbody>tr.parent>td.dtr-control:before,
table.dataTable.dtr-inline.collapsed>tbody>tr.parent>th.dtr-control:before,
table.dataTable td.dt-control:before {
background-color: #979797 !important;
background-color: var(--mc-dtr-active-bg) !important;
}
table.dataTable.table-striped>tbody>tr>td {
vertical-align: middle;
+82 -32
View File
@@ -34,11 +34,44 @@
url('/fonts/noto-sans-v12-latin_greek_cyrillic-700italic.woff') format('woff');
}
/* theme tokens, override in css/themes/<name>/theme.css */
:root {
--mc-font-family: "Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
--mc-font-size: 10.5pt;
--mc-body-bg: #fbfbfb;
--mc-primary: #0F688D;
--mc-primary-border: #0d526d;
--mc-info: #148DBC;
--mc-info-border: #127ea8;
--mc-border: #dfdfdf;
--mc-surface: #fff;
--mc-surface-alt: #f7f7f7;
--mc-muted: #959595;
--mc-hr: #ccc;
--mc-progress-bg: #d5d5d5;
--mc-placeholder: rgb(137 137 137);
--mc-btn-outline-color: #000000;
--mc-btn-outline-border: #cfcfcf;
--mc-btn-outline-hover-bg: #f0f0f0;
--mc-btn-outline-active-bg: #f0f0f0;
--mc-btn-light-active-color: #fff;
--mc-btn-light-active-bg: #555;
--mc-btn-light-active-border: #4d4d4d;
--mc-dtr-bg: #979797;
--mc-dtr-border: #616161;
--mc-dtr-color: #fff;
--mc-dtr-hover-bg: #5e5e5e;
--mc-dtr-active-bg: #979797;
--mc-login-user: #28b62c;
--mc-login-domainadmin: #ffc107;
--mc-login-admin: #ff4136;
}
body {
min-height: 100vh;
display: flex;
flex-direction: column;
background-color: #fbfbfb;
background-color: var(--mc-body-bg);
}
#maxmsgsize { min-width: 80px; }
#slider1 .slider-selection {
@@ -86,17 +119,17 @@ body {
max-height: 1000px
}
.nav-tabs .nav-link, .nav-tabs .nav-link.disabled, .nav-tabs .nav-link.disabled:hover, .nav-tabs .nav-link.disabled:focus {
border-color: #dfdfdf;
border-color: var(--mc-border);
}
.nav-tabs .nav-link.active, .nav-tabs .nav-item.show .nav-link {
border-color: #dfdfdf;
border-bottom: 1px solid #ffffff;
border-color: var(--mc-border);
border-bottom: 1px solid var(--mc-surface);
}
.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus {
border-color: #dfdfdf;
border-color: var(--mc-border);
}
.nav-tabs {
border-bottom: 1px solid #dfdfdf;
border-bottom: 1px solid var(--mc-border);
}
.bi {
display: inline-block;
@@ -164,13 +197,13 @@ body.modal-open {
padding-right: inherit !important;
}
body {
font-family: "Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
font-size: 10.5pt;
font-family: var(--mc-font-family);
font-size: var(--mc-font-size);
line-height: 1.5;
}
html {
font-family: "Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
font-size: 10.5pt;
font-family: var(--mc-font-family);
font-size: var(--mc-font-size);
line-height: 1.5;
}
#mailcow-alert {
@@ -206,7 +239,7 @@ legend {
cursor: not-allowed;
}
.overlay {
background: #fff;
background: var(--mc-surface);
position: absolute;
z-index: 10000;
top: 0; right: 0; bottom: 0; left: 0;
@@ -215,9 +248,6 @@ legend {
.bootstrap-select.btn-group .no-results {
display: none;
}
.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary {
color: rgb(197, 197, 197) !important;
}
.haveibeenpwned {
cursor: pointer;
-webkit-user-select: none;
@@ -239,7 +269,7 @@ legend {
.footer {
margin-top: 27px;
margin-bottom: 20px;
color: #959595;
color: var(--mc-muted);
display: flex;
flex-direction: column;
}
@@ -337,7 +367,7 @@ code {
overflow-x: auto!important;
}
.well {
border: 1px solid #dfdfdf;
border: 1px solid var(--mc-border);
background-color: #f9f9f9;
padding: 10px;
}
@@ -356,47 +386,47 @@ button[aria-expanded='true'] > .caret {
}
.list-group-details {
background: #fff;
background: var(--mc-surface);
}
.list-group-header {
background: #f7f7f7;
background: var(--mc-surface-alt);
}
.bg-primary, .alert-primary, .btn-primary {
background-color: #0F688D !important;
border-color: #0d526d !important;
background-color: var(--mc-primary) !important;
border-color: var(--mc-primary-border) !important;
}
.bg-info, .alert-info, .btn-info {
background-color: #148DBC !important;
border-color: #127ea8 !important;
background-color: var(--mc-info) !important;
border-color: var(--mc-info-border) !important;
}
.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary {
color: rgb(137 137 137)!important;
color: var(--mc-placeholder) !important;
}
.progress {
height: 16px;
background-color: #d5d5d5;
background-color: var(--mc-progress-bg);
}
.btn-outline-secondary:hover {
background-color: #f0f0f0;
background-color: var(--mc-btn-outline-hover-bg);
}
.btn.btn-outline-secondary {
color: #000000 !important;
border-color: #cfcfcf !important;
color: var(--mc-btn-outline-color) !important;
border-color: var(--mc-btn-outline-border) !important;
}
.btn-check:checked+.btn-outline-secondary, .btn-check:active+.btn-outline-secondary, .btn-outline-secondary:active, .btn-outline-secondary.active, .btn-outline-secondary.dropdown-toggle.show {
background-color: #f0f0f0 !important;
background-color: var(--mc-btn-outline-active-bg) !important;
}
.btn-check:checked+.btn-light, .btn-check:active+.btn-light, .btn-light:active, .btn-light.active, .show>.btn-light.dropdown-toggle {
color: #fff;
background-color: #555;
color: var(--mc-btn-light-active-color);
background-color: var(--mc-btn-light-active-bg);
background-image: none;
border-color: #4d4d4d;
border-color: var(--mc-btn-light-active-border);
}
.btn-check:checked+.btn-light:focus, .btn-check:active+.btn-light:focus, .btn-light:active:focus, .btn-light.active:focus, .show>.btn-light.dropdown-toggle:focus,
.btn-check:focus+.btn-light, .btn-light:focus {
@@ -412,6 +442,26 @@ button[aria-expanded='true'] > .caret {
text-decoration: none;
}
/* role marker on login pages, header icons differ too (color blindness) */
.login-card::before {
content: "";
display: block;
height: 5px;
background: var(--mc-login-color);
border-radius: inherit;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.login-user {
--mc-login-color: var(--mc-login-user);
}
.login-domainadmin {
--mc-login-color: var(--mc-login-domainadmin);
}
.login-admin {
--mc-login-color: var(--mc-login-admin);
}
.hr-title {
display: flex;
align-items: center;
@@ -423,7 +473,7 @@ button[aria-expanded='true'] > .caret {
.hr-title::after {
content: "";
flex: 1;
border-bottom: 1px solid #ccc;
border-bottom: 1px solid var(--mc-hr);
}
.hr-title:not(:empty)::before {
File diff suppressed because it is too large. Load diff
+155 -114
View File
@@ -1,62 +1,103 @@
/* theme tokens, override in css/themes/<name>/dark.css */
:root {
--mc-body-bg: #1c1c1e;
--mc-body-color: #f2f2f7;
--mc-surface: #2c2c2e;
--mc-surface-alt: #282828;
--mc-text: #ccc;
--mc-text-soft: #bbb;
--mc-muted: #8e8e93;
--mc-border: #555;
--mc-tab-border: #444444;
--mc-link: #6fc7e9;
--mc-link-hover: #3daedb;
--mc-btn-bg: #5e5e5e;
--mc-btn-border: #4c4c4e;
--mc-input-bg: #424242;
--mc-input-color: #e2e2e2;
--mc-input-border: #999;
--mc-dropdown-bg: #424242;
--mc-table-bg: #3a3a3a;
--mc-table-stripe: #282828;
--mc-table-stripe-row: #424242;
--mc-table-hover: #343434;
--mc-table-border: #5c5c5c;
--mc-progress-bg: #555;
--mc-placeholder: #d4d4d4;
--mc-btn-outline-color: #e0e0e0;
--mc-btn-outline-border: #7a7a7a;
--mc-btn-outline-hover-bg: #5c5c5c;
--mc-btn-outline-active-bg: #7a7a7a;
--mc-btn-light-active-color: #f2f2f7;
--mc-btn-light-active-bg: #242424;
--mc-btn-light-active-border: #1c1c1e;
--mc-btn-dark-bg: #242424;
--mc-input-disabled-bg: #6e6e6e;
--mc-input-disabled-color: #a8a8a8;
--mc-list-header-bg: #444;
--mc-list-details-bg: #555;
--mc-chip-bg: #444;
--mc-table-child-bg: #414141;
--mc-dtr-bg: #7a7a7a;
--mc-dtr-border: #5c5c5c;
--mc-dtr-color: #e0e0e0;
--mc-dtr-hover-bg: #7a7a7a;
--mc-dtr-active-bg: #949494;
}
body {
background-color: #1c1c1e;
color: #f2f2f7;
background-color: var(--mc-body-bg);
color: var(--mc-body-color);
}
.card {
border: 1px solid #2c2c2e;
background-color: #2c2c2e;
border: 1px solid var(--mc-surface);
background-color: var(--mc-surface);
}
legend {
color: #f2f2f7;
color: var(--mc-body-color);
}
.card-header {
color: #8e8e93;
background-color: #1c1c1e;
color: var(--mc-muted);
background-color: var(--mc-body-bg);
border-color: transparent;
}
.card-body {
--bs-card-color: #bbb;
--bs-card-color: var(--mc-text-soft);
}
.btn-secondary, .paginate_button, .page-link, .btn-light {
color: #f2f2f7 !important;
background-color: #5e5e5e !important;
border-color: #4c4c4e !important;
color: var(--mc-body-color) !important;
background-color: var(--mc-btn-bg) !important;
border-color: var(--mc-btn-border) !important;
}
.btn-dark {
color: #f2f2f7 !important;
background-color: #242424 !important;
border-color: #1c1c1e !important;
}
.btn-secondary:focus, .btn-secondary:hover, .btn-group.open .dropdown-toggle.btn-secondary {
background-color: #444444;
border-color: #4c4c4e !important;
color: #f2f2f7;
color: var(--mc-body-color) !important;
background-color: var(--mc-btn-dark-bg) !important;
border-color: var(--mc-body-bg) !important;
}
.btn-check:checked+.btn-secondary, .btn-check:active+.btn-secondary, .btn-secondary:active, .btn-secondary.active, .show>.btn-secondary.dropdown-toggle {
border-color: #5e5e5e !important;
border-color: var(--mc-btn-bg) !important;
}
.alert-secondary {
color: #f2f2f7 !important;
background-color: #5e5e5e !important;
border-color: #4c4c4e !important;
color: var(--mc-body-color) !important;
background-color: var(--mc-btn-bg) !important;
border-color: var(--mc-btn-border) !important;
}
.bg-secondary {
color: #f2f2f7 !important;
background-color: #5e5e5e !important;
color: var(--mc-body-color) !important;
background-color: var(--mc-btn-bg) !important;
}
.alert-secondary, .alert-secondary a, .alert-secondary .alert-link {
color: #f2f2f7;
color: var(--mc-body-color);
}
.page-item.active .page-link {
@@ -65,18 +106,18 @@ legend {
}
.btn-secondary:focus, .btn-secondary:hover, .btn-group.open .dropdown-toggle.btn-secondary {
background-color: #5e5e5e;
border-color: #4c4c4e !important;
color: #f2f2f7;
background-color: var(--mc-btn-bg);
border-color: var(--mc-btn-border) !important;
color: var(--mc-body-color);
}
.btn-secondary:disabled, .btn-secondary.disabled {
border-color: #5e5e5e !important;
border-color: var(--mc-btn-bg) !important;
}
.modal-content {
--bs-modal-color: #bbb;
background-color: #2c2c2e;
--bs-modal-color: var(--mc-text-soft);
background-color: var(--mc-surface);
}
.modal-header {
@@ -84,7 +125,7 @@ legend {
}
.modal-title {
color: #bbb;
color: var(--mc-text-soft);
}
.modal .btn-close {
@@ -92,12 +133,12 @@ legend {
}
.navbar.bg-light {
background-color: #1c1c1e !important;
border-color: #2c2c2e;
background-color: var(--mc-body-bg) !important;
border-color: var(--mc-surface);
}
.nav-link {
color: #8e8e93 !important;
color: var(--mc-muted) !important;
}
.nav-tabs .nav-link.active, .nav-tabs .nav-item.show .nav-link {
@@ -105,23 +146,23 @@ legend {
}
.nav-tabs, .nav-tabs .nav-link {
border-color: #444444 !important;
border-color: var(--mc-tab-border) !important;
}
.nav-tabs .nav-link:not(.disabled):hover, .nav-tabs .nav-link:not(.disabled):focus, .nav-tabs .nav-link.active {
border-bottom-color: #1c1c1e !important;
border-bottom-color: var(--mc-body-bg) !important;
}
.card .nav-tabs .nav-link:not(.disabled):hover, .card .nav-tabs .nav-link:not(.disabled):focus, .card .nav-tabs .nav-link.active {
border-bottom-color: #2c2c2e !important;
border-bottom-color: var(--mc-surface) !important;
}
.table, .table-striped>tbody>tr:nth-of-type(odd)>*, tbody tr {
color: #f2f2f7 !important;
color: var(--mc-body-color) !important;
}
.dropdown-menu {
background-color: #424242;
background-color: var(--mc-dropdown-bg);
border: 1px solid #282828;
}
.dropdown-menu>li>a:focus, .dropdown-menu>li>a:hover {
@@ -132,79 +173,79 @@ legend {
color: #fff;
}
.bootstrap-select>.dropdown-toggle.bs-placeholder, .bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary {
color: #d4d4d4 !important;
color: var(--mc-placeholder) !important;
}
tbody tr {
color: #ccc;
color: var(--mc-text);
}
.navbar-default .navbar-nav>.open>a, .navbar-default .navbar-nav>.open>a:focus, .navbar-default .navbar-nav>.open>a:hover {
color: #ccc;
color: var(--mc-text);
}
.navbar-default .navbar-nav>.active>a, .navbar-default .navbar-nav>.active>a:focus, .navbar-default .navbar-nav>.active>a:hover {
color: #ccc;
color: var(--mc-text);
}
.list-group-item {
background-color: #282828;
border: 1px solid #555;
background-color: var(--mc-surface-alt);
border: 1px solid var(--mc-border);
}
.table-striped>tbody>tr:nth-of-type(odd) {
background-color: #424242;
background-color: var(--mc-table-stripe-row);
}
table.dataTable>tbody>tr.child ul.dtr-details>li {
border-bottom: 1px solid rgba(255, 255, 255, 0.13);
}
.label.label-last-login {
color: #ccc !important;
color: var(--mc-text) !important;
background-color: #555 !important;
}
.progress {
background-color: #555;
background-color: var(--mc-progress-bg);
}
.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
color: #ccc;
color: var(--mc-text);
}
div.numberedtextarea-number {
color: #999;
}
.well {
border: 1px solid #555;
background-color: #282828;
border: 1px solid var(--mc-border);
background-color: var(--mc-surface-alt);
}
pre {
color: #ccc;
background-color: #282828;
border: 1px solid #555;
color: var(--mc-text);
background-color: var(--mc-surface-alt);
border: 1px solid var(--mc-border);
}
.form-control {
background-color: transparent;
}
input.form-control, textarea.form-control {
color: #e2e2e2 !important;
background-color: #424242 !important;
border: 1px solid #999;
color: var(--mc-input-color) !important;
background-color: var(--mc-input-bg) !important;
border: 1px solid var(--mc-input-border);
}
input.form-control:focus, textarea.form-control {
background-color: #424242 !important;
background-color: var(--mc-input-bg) !important;
}
input.form-control:disabled, textarea.form-disabled {
color: #a8a8a8 !important;
background-color: #6e6e6e !important;
color: var(--mc-input-disabled-color) !important;
background-color: var(--mc-input-disabled-bg) !important;
}
.input-group-addon {
color: #ccc;
background-color: #424242 !important;
border: 1px solid #999;
color: var(--mc-text);
background-color: var(--mc-input-bg) !important;
border: 1px solid var(--mc-input-border);
}
.input-group-text {
color: #ccc;
color: var(--mc-text);
background-color: #1c1c1c;
}
.list-group-item {
color: #ccc;
color: var(--mc-text);
}
.dropdown-item {
color: #ccc;
color: var(--mc-text);
}
.dropdown-item:hover {
color: #616161 !important;
@@ -214,9 +255,9 @@ input.form-control:disabled, textarea.form-disabled {
background-color: #007aff;
}
.form-select {
color: #e2e2e2!important;
background-color: #424242!important;
border: 1px solid #999;
color: var(--mc-input-color)!important;
background-color: var(--mc-input-bg)!important;
border: 1px solid var(--mc-input-border);
}
.responsive-tabs .card-header button[data-bs-toggle="collapse"] {
@@ -240,15 +281,15 @@ input.form-control:disabled, textarea.form-disabled {
a {
color: #6fc7e9;
color: var(--mc-link);
text-decoration: underline;
}
a:hover {
color: #3daedb;
color: var(--mc-link-hover);
}
.breadcrumb-item.active {
color: #ccc;
color: var(--mc-text);
}
@@ -273,11 +314,11 @@ a:hover {
background-color: #3a3a3a;
}
.accordion-button {
color: #bbb;
color: var(--mc-text-soft);
background-color: #2c2c2c;
}
.accordion-button:not(.collapsed) {
color: #6fc7e9;
color: var(--mc-link);
background-color: #2c2c2c;
}
.accordion-button::after {
@@ -300,15 +341,15 @@ a:hover {
}
.tag-box {
background-color: #282828;
border: 1px solid #555;
background-color: var(--mc-surface-alt);
border: 1px solid var(--mc-border);
}
.tag-input {
color: #fff;
background-color: #282828;
background-color: var(--mc-surface-alt);
}
.tag-add {
color: #ccc;
color: var(--mc-text);
}
.tag-add:hover {
color: #d1d1d1;
@@ -318,19 +359,19 @@ a:hover {
color: #fff;
}
.btn-outline-secondary:hover {
background-color: #5c5c5c;
background-color: var(--mc-btn-outline-hover-bg);
}
.btn.btn-outline-secondary {
color: #e0e0e0 !important;
border-color: #7a7a7a !important;
color: var(--mc-btn-outline-color) !important;
border-color: var(--mc-btn-outline-border) !important;
}
.btn-check:checked+.btn-outline-secondary, .btn-check:active+.btn-outline-secondary, .btn-outline-secondary:active, .btn-outline-secondary.active, .btn-outline-secondary.dropdown-toggle.show {
background-color: #7a7a7a !important;
background-color: var(--mc-btn-outline-active-bg) !important;
}
.btn-check:checked+.btn-light, .btn-check:active+.btn-light, .btn-light:active, .btn-light.active, .show>.btn-light.dropdown-toggle {
color: #f2f2f7 !important;
background-color: #242424 !important;
border-color: #1c1c1e !important;
color: var(--mc-btn-light-active-color) !important;
background-color: var(--mc-btn-light-active-bg) !important;
border-color: var(--mc-btn-light-active-border) !important;
}
.btn-input-missing,
.btn-input-missing:hover,
@@ -348,16 +389,16 @@ a:hover {
}
.list-group-details {
background: #555;
background: var(--mc-list-details-bg);
}
.list-group-header {
background: #444;
background: var(--mc-list-header-bg);
}
span.mail-address-item {
background-color: #444;
background-color: var(--mc-chip-bg);
border-radius: 4px;
border: 1px solid #555;
border: 1px solid var(--mc-border);
padding: 2px 7px;
display: inline-block;
margin: 2px 6px 2px 0;
@@ -365,52 +406,52 @@ span.mail-address-item {
table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control:before:hover,
table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control:before:hover {
background-color: #7a7a7a !important;
background-color: var(--mc-dtr-hover-bg) !important;
}
table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control:before,
table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control:before {
background-color: #7a7a7a !important;
border: 1.5px solid #5c5c5c !important;
color: #e0e0e0 !important;
background-color: var(--mc-dtr-bg) !important;
border: 1.5px solid var(--mc-dtr-border) !important;
color: var(--mc-dtr-color) !important;
}
table.dataTable.dtr-inline.collapsed>tbody>tr.parent>td.dtr-control:before,
table.dataTable.dtr-inline.collapsed>tbody>tr.parent>th.dtr-control:before {
background-color: #949494;
background-color: var(--mc-dtr-active-bg);
}
table.dataTable.dtr-inline.collapsed>tbody>tr>td.child,
table.dataTable.dtr-inline.collapsed>tbody>tr>th.child,
table.dataTable.dtr-inline.collapsed>tbody>tr>td.dataTables_empty {
background-color: #414141;
background-color: var(--mc-table-child-bg);
}
table.table, .table-striped>tbody>tr:nth-of-type(odd)>*, tbody tr {
color: #ccc !important;
color: var(--mc-text) !important;
}
.table-secondary {
--bs-table-bg: #282828;
--bs-table-striped-bg: #343434;
--bs-table-striped-color: #f2f2f7;
--bs-table-striped-color: var(--mc-body-color);
--bs-table-active-bg: #4c4c4c;
--bs-table-active-color: #f2f2f7;
--bs-table-active-color: var(--mc-body-color);
--bs-table-hover-bg: #3a3a3a;
--bs-table-hover-color: #f2f2f7;
color: #ccc;
--bs-table-hover-color: var(--mc-body-color);
color: var(--mc-text);
border-color: #3a3a3a;
}
.table-light {
--bs-table-bg: #3a3a3a;
--bs-table-striped-bg: #444444;
--bs-table-striped-color: #f2f2f7;
--bs-table-striped-color: var(--mc-body-color);
--bs-table-active-bg: #5c5c5c;
--bs-table-active-color: #f2f2f7;
--bs-table-active-color: var(--mc-body-color);
--bs-table-hover-bg: #4c4c4c;
--bs-table-hover-color: #f2f2f7;
color: #ccc;
--bs-table-hover-color: var(--mc-body-color);
color: var(--mc-text);
border-color: #4c4c4c;
}
@@ -430,23 +471,23 @@ table.table, .table-striped>tbody>tr:nth-of-type(odd)>*, tbody tr {
.table-striped>tbody>tr:nth-of-type(odd)>td,
.table-striped>tbody>tr:nth-of-type(odd)>th {
background-color: #282828;
background-color: var(--mc-table-stripe);
}
.table-hover>tbody>tr:hover {
background-color: #343434;
background-color: var(--mc-table-hover);
}
.table>:not(caption)>*>* {
border-color: #5c5c5c;
--bs-table-color-state:#bbb;
--bs-table-bg: #3a3a3a;
border-color: var(--mc-table-border);
--bs-table-color-state:var(--mc-text-soft);
--bs-table-bg: var(--mc-table-bg);
}
.text-muted {
--bs-secondary-color: #8e8e93;
--bs-secondary-color: var(--mc-muted);
}
input::placeholder {
color: #8e8e93 !important;
color: var(--mc-muted) !important;
}
.form-select {
+212
View File
@@ -0,0 +1,212 @@
/* servercow theme, dark mode, bundled after mailcow core dark mode */
:root {
--sc-brown-hover: #806b55;
--sc-brown-active: #8a745c;
--sc-brown-soft: rgba(201, 180, 154, .18);
--sc-brown-tint: rgba(201, 180, 154, .08);
--sc-link: #c9b49a;
--sc-danger-text: #ff8a8a;
--sc-ink: #ece7e1;
--sc-muted: #a89f94;
--sc-placeholder: #8a8177;
--sc-bg: #171513;
--sc-surface: #211e1b;
--sc-cap: #2a2622;
--sc-addon: #2a2622;
--sc-line: #463f38;
--sc-divider: #34302b;
--sc-cap-line: #3d3732;
--sc-stripe: rgba(255, 255, 255, .025);
--sc-hover: rgba(201, 180, 154, .08);
--sc-focus-border: #8a7864;
--sc-focus: rgba(201, 180, 154, .25);
--sc-shadow: 0 0 10px 0 rgba(0, 0, 0, .5);
--bs-body-color: #ece7e1;
--bs-body-bg: #171513;
/* core dark tokens, see css/themes/mailcow-darkmode.css */
--mc-body-bg: var(--sc-bg);
--mc-body-color: var(--sc-ink);
--mc-surface: var(--sc-surface);
--mc-surface-alt: var(--sc-cap);
--mc-text: var(--sc-ink);
--mc-text-soft: var(--sc-ink);
--mc-muted: var(--sc-muted);
--mc-border: var(--sc-divider);
--mc-tab-border: var(--sc-divider);
--mc-link: var(--sc-link);
--mc-link-hover: #e0cfba;
--mc-btn-bg: var(--sc-cap);
--mc-btn-border: var(--sc-line);
--mc-input-bg: var(--sc-bg);
--mc-input-color: var(--sc-ink);
--mc-input-border: var(--sc-line);
--mc-dropdown-bg: var(--sc-surface);
--mc-table-bg: transparent;
--mc-table-stripe: var(--sc-stripe);
--mc-table-stripe-row: transparent;
--mc-table-hover: var(--sc-hover);
--mc-table-border: var(--sc-divider);
--mc-progress-bg: var(--sc-cap);
--mc-placeholder: var(--sc-placeholder);
--mc-hr: var(--sc-divider);
--mc-btn-outline-color: var(--sc-ink);
--mc-btn-outline-border: var(--sc-line);
--mc-btn-outline-hover-bg: var(--sc-brown-tint);
--mc-btn-outline-active-bg: var(--sc-brown);
--mc-btn-light-active-color: #fff;
--mc-btn-light-active-bg: var(--sc-brown);
--mc-btn-light-active-border: var(--sc-brown);
--mc-btn-dark-bg: var(--sc-brown);
--mc-input-disabled-bg: var(--sc-cap);
--mc-input-disabled-color: var(--sc-muted);
--mc-list-header-bg: var(--sc-cap);
--mc-list-details-bg: var(--sc-surface);
--mc-chip-bg: var(--sc-cap);
--mc-table-child-bg: var(--sc-surface);
--mc-dtr-bg: var(--sc-cap);
--mc-dtr-border: var(--sc-line);
--mc-dtr-color: var(--sc-link);
--mc-dtr-hover-bg: var(--sc-hover);
--mc-dtr-active-bg: var(--sc-brown-soft);
}
/* navbar */
.navbar.bg-light {
background-color: var(--sc-surface) !important;
border-bottom-color: var(--sc-green);
}
.navbar-light .navbar-nav .nav-link {
color: var(--sc-ink) !important;
}
.navbar-light .navbar-nav .nav-link:hover,
.navbar-light .navbar-nav .nav-link:focus,
.navbar-light .navbar-nav .nav-link.active,
.navbar-light .navbar-nav .show > .nav-link {
color: var(--sc-link) !important;
}
.dropdown-menu {
border-color: var(--sc-cap-line);
}
.dropdown-item:hover, .dropdown-item:focus {
color: var(--sc-link) !important;
}
/* cards and modals */
.card-header {
color: var(--sc-ink);
background-color: var(--sc-cap);
border-color: var(--sc-cap-line);
}
.modal-content {
background-color: var(--sc-surface);
}
.modal-header {
border-color: var(--sc-cap-line);
}
.input-group-text {
background-color: var(--sc-addon);
}
.form-control::file-selector-button {
color: var(--sc-ink);
background-color: var(--sc-cap);
border-color: var(--sc-line);
}
.form-control:hover:not(:disabled):not([readonly])::file-selector-button {
color: var(--sc-link);
background-color: var(--sc-line);
}
.form-check-input:not(:checked) {
background-color: var(--sc-bg);
border-color: var(--sc-line);
}
kbd {
color: var(--sc-ink);
background-color: var(--sc-cap);
}
/* buttons */
.btn-secondary:hover, .btn-secondary:focus, .btn-light:hover, .btn-light:focus, .page-link:hover {
color: var(--sc-link) !important;
background-color: var(--sc-line) !important;
}
.btn-check:checked + .btn-secondary, .btn-secondary.active, .show > .btn-secondary.dropdown-toggle,
.page-item.active .page-link {
color: #fff !important;
background-color: var(--sc-brown) !important;
border-color: var(--sc-brown) !important;
}
/* tabs */
.nav-tabs .nav-link {
color: var(--sc-ink) !important;
border-bottom-color: transparent !important;
}
.nav-tabs .nav-link:not(.disabled):hover, .nav-tabs .nav-link:not(.disabled):focus,
.card .nav-tabs .nav-link:not(.disabled):hover, .card .nav-tabs .nav-link:not(.disabled):focus {
color: var(--sc-link) !important;
border-bottom-color: transparent !important;
}
.nav-tabs .nav-link.active, .card .nav-tabs .nav-link.active {
color: var(--sc-link) !important;
border-bottom-color: var(--sc-link) !important;
}
/* semantic text */
.text-success {
color: var(--sc-green) !important;
}
.text-warning {
color: var(--sc-warning) !important;
}
/* alerts, opaque dark tints (also used for floating notifications) */
.alert-primary {
color: #e6d9c9 !important;
background-color: #3c342b !important;
border-color: #5e4f3e !important;
}
.alert-info {
color: #cfe9f6 !important;
background-color: #2f3334 !important;
border-color: #44535a !important;
}
.alert-success {
color: #cdeea0;
background-color: #2e3419;
border-color: #405515;
}
.alert-danger {
color: #f9c4c4;
background-color: #402a28;
border-color: #693a38;
}
.alert-warning {
color: #f5d496;
background-color: #3c2f19;
border-color: #654815;
}
.alert-secondary, .alert-light {
color: var(--sc-ink) !important;
background-color: var(--sc-cap) !important;
border-color: var(--sc-divider) !important;
}
.alert .btn-close {
filter: invert(1) grayscale(100%) brightness(200%);
}
/* slider */
.noUi-target {
background: var(--sc-bg);
border-color: var(--sc-line);
box-shadow: none;
}
.noUi-handle {
background: var(--sc-cap);
border-color: var(--sc-line);
box-shadow: none;
}
.noUi-handle::before, .noUi-handle::after {
background: var(--sc-line);
}
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
@@ -0,0 +1,93 @@
Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
+584
View File
@@ -0,0 +1,584 @@
/* servercow theme for mailcow, based on the servercow customer portal */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 400;
font-display: swap;
src: local(''),
url('/css/themes/servercow/fonts/poppins-v24-latin_latin-ext-regular.woff2') format('woff2'),
url('/css/themes/servercow/fonts/poppins-v24-latin_latin-ext-regular.woff') format('woff');
}
@font-face {
font-family: 'Poppins';
font-style: italic;
font-weight: 400;
font-display: swap;
src: local(''),
url('/css/themes/servercow/fonts/poppins-v24-latin_latin-ext-italic.woff2') format('woff2'),
url('/css/themes/servercow/fonts/poppins-v24-latin_latin-ext-italic.woff') format('woff');
}
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 500;
font-display: swap;
src: local(''),
url('/css/themes/servercow/fonts/poppins-v24-latin_latin-ext-500.woff2') format('woff2'),
url('/css/themes/servercow/fonts/poppins-v24-latin_latin-ext-500.woff') format('woff');
}
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 600;
font-display: swap;
src: local(''),
url('/css/themes/servercow/fonts/poppins-v24-latin_latin-ext-600.woff2') format('woff2'),
url('/css/themes/servercow/fonts/poppins-v24-latin_latin-ext-600.woff') format('woff');
}
@font-face {
font-family: 'Roboto Slab';
font-style: normal;
font-weight: 400;
font-display: swap;
src: local(''),
url('/css/themes/servercow/fonts/roboto-slab-v36-cyrillic_greek_latin-regular.woff2') format('woff2'),
url('/css/themes/servercow/fonts/roboto-slab-v36-cyrillic_greek_latin-regular.woff') format('woff');
}
@font-face {
font-family: 'Roboto Slab';
font-style: normal;
font-weight: 500;
font-display: swap;
src: local(''),
url('/css/themes/servercow/fonts/roboto-slab-v36-cyrillic_greek_latin-500.woff2') format('woff2'),
url('/css/themes/servercow/fonts/roboto-slab-v36-cyrillic_greek_latin-500.woff') format('woff');
}
/* tokens */
:root {
--sc-brown: #6F5D49;
--sc-brown-hover: #5e4f3e;
--sc-brown-active: #594a3a;
--sc-brown-soft: rgba(111, 93, 73, .2);
--sc-brown-tint: rgba(111, 93, 73, .08);
--sc-link: #6F5D49;
--sc-green: #8AD608;
--sc-blue: #96D0EC;
--sc-warning: #e3950b;
--sc-danger: #ef6f6f;
--sc-danger-hover: #e85e5e;
--sc-danger-text: #c43c3c;
--sc-ink: #212529;
--sc-muted: #6c757d;
--sc-placeholder: #6c757d;
--sc-bg: #F6F8F8;
--sc-surface: #ffffff;
--sc-cap: #f7f7f7;
--sc-addon: #e9ecef;
--sc-line: #ced4da;
--sc-divider: #dee2e6;
--sc-cap-line: rgba(0, 0, 0, .175);
--sc-stripe: rgba(111, 93, 73, .04);
--sc-hover: rgba(111, 93, 73, .08);
--sc-focus-border: #b7aea4;
--sc-focus: rgba(111, 93, 73, .25);
--sc-shadow: 0 0 10px 0 rgba(134, 134, 134, .3);
--sc-font: 'Poppins', system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
--sc-font-head: 'Roboto Slab', Georgia, serif;
--bs-body-color: #212529;
--bs-body-bg: #F6F8F8;
--bs-body-font-family: var(--sc-font);
/* core tokens, see css/build/014-mailcow.css */
--mc-font-family: var(--sc-font);
--mc-font-size: 10pt;
--mc-body-bg: var(--sc-bg);
--mc-primary: var(--sc-brown);
--mc-primary-border: var(--sc-brown);
--mc-info: var(--sc-blue);
--mc-info-border: var(--sc-blue);
--mc-border: var(--sc-divider);
--mc-surface: var(--sc-surface);
--mc-surface-alt: var(--sc-cap);
--mc-muted: var(--sc-muted);
--mc-hr: var(--sc-divider);
--mc-progress-bg: var(--sc-addon);
--mc-placeholder: var(--sc-placeholder);
--mc-btn-outline-color: var(--sc-ink);
--mc-btn-outline-border: var(--sc-line);
--mc-btn-outline-hover-bg: var(--sc-brown-tint);
--mc-btn-outline-active-bg: var(--sc-brown);
--mc-btn-light-active-color: #fff;
--mc-btn-light-active-bg: var(--sc-brown);
--mc-btn-light-active-border: var(--sc-brown);
--mc-dtr-bg: var(--sc-surface);
--mc-dtr-border: var(--sc-line);
--mc-dtr-color: var(--sc-brown);
--mc-dtr-hover-bg: var(--sc-brown-tint);
--mc-dtr-active-bg: var(--sc-brown-soft);
--mc-login-user: var(--sc-green);
--mc-login-domainadmin: #f5c400;
--mc-login-admin: var(--sc-danger);
}
/* base */
body {
color: var(--sc-ink);
-webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6, legend, .modal-title {
font-family: var(--sc-font-head);
font-weight: 500;
color: var(--sc-ink);
}
a {
color: var(--sc-link);
}
a:hover {
color: var(--sc-brown-hover);
}
.text-muted, .text-secondary {
color: var(--sc-muted) !important;
}
::selection {
background: var(--sc-brown-soft);
}
hr {
border-color: var(--sc-divider);
opacity: 1;
}
::placeholder {
color: var(--sc-placeholder) !important;
}
/* navbar, white with green line */
.navbar.bg-light {
background-color: var(--sc-surface) !important;
border: 0;
border-bottom: 5px solid var(--sc-green);
}
.navbar-light .navbar-nav .nav-link {
color: var(--sc-ink);
}
.navbar-light .navbar-nav .nav-link:hover,
.navbar-light .navbar-nav .nav-link:focus,
.navbar-light .navbar-nav .nav-link.active,
.navbar-light .navbar-nav .show > .nav-link {
color: var(--sc-brown);
}
@media (min-width: 992px) {
.navbar-nav .nav-link {
height: 60px;
}
}
.dropdown-menu {
border: 1px solid var(--sc-cap-line);
border-radius: 0;
box-shadow: var(--sc-shadow);
}
.dropdown-item {
color: var(--sc-ink);
}
.dropdown-item:hover, .dropdown-item:focus {
color: var(--sc-brown) !important;
background-color: var(--sc-brown-soft);
}
.dropdown-item.active, .dropdown-item:active {
color: #fff;
background-color: var(--sc-brown);
}
/* cards, white with soft shadow */
.card {
border: 0 !important;
border-radius: 0;
background-color: var(--sc-surface);
box-shadow: var(--sc-shadow);
}
.card-header, .card-header:first-child {
font-family: var(--sc-font-head);
font-weight: 500;
font-size: 1.1rem;
color: var(--sc-ink);
background-color: var(--sc-cap);
border-bottom: 1px solid var(--sc-cap-line);
border-radius: 0;
padding: .5rem 1rem;
}
.card-header .btn, .card-header .form-control, .card-header .form-select {
font-family: var(--sc-font);
font-size: .875rem;
}
.card-footer {
background-color: var(--sc-cap);
border-top: 1px solid var(--sc-cap-line);
border-radius: 0;
}
.card .card {
box-shadow: none;
border: 1px solid var(--sc-divider) !important;
}
/* colored headers become a top line */
.card:has(> .card-header.bg-danger) {
border-top: 4px solid var(--sc-danger) !important;
}
.card-header.bg-danger, .card-header.bg-success, .card-header.bg-primary, .card-header.bg-info {
color: var(--sc-ink) !important;
background-color: var(--sc-cap) !important;
}
.list-group-item {
border-color: var(--sc-divider);
}
/* modals */
.modal-content {
border: 0;
border-radius: 0;
box-shadow: 0 8px 40px rgba(0, 0, 0, .2);
}
.modal-header {
background-color: var(--sc-cap);
border-bottom: 1px solid var(--sc-cap-line);
border-radius: 0;
}
.modal-footer {
border-top: 1px solid var(--sc-divider);
}
/* buttons, flat and square */
.btn {
border-width: 1px;
border-radius: 0;
font-weight: 400;
transition: none;
}
.btn:not(.disabled):hover,
.btn:not(.disabled):active {
margin-top: 0;
border-bottom-width: 1px;
}
.btn:focus, .btn-check:focus + .btn {
box-shadow: 0 0 0 .2rem var(--sc-focus);
}
.btn-primary, .btn-primary:hover, .btn-primary:focus, .btn-primary:active {
color: #fff;
}
.btn-primary:hover, .btn-primary:focus, .btn-primary:active {
background-color: var(--sc-brown-hover) !important;
border-color: var(--sc-brown-active) !important;
}
/* main call to action, portal primary */
.btn-success, .btn-success:not(.disabled):hover, .btn-success:not(.disabled):active, .btn-success:focus,
.btn-success:disabled, .btn-success.disabled {
color: #fff;
background-color: var(--sc-brown);
border-color: var(--sc-brown);
}
.btn-success:hover, .btn-success:active, .btn-success:focus {
background-color: var(--sc-brown-hover);
border-color: var(--sc-brown-active);
}
.btn-danger, .btn-danger:not(.disabled):hover, .btn-danger:not(.disabled):active, .btn-danger:focus,
.btn-danger:disabled, .btn-danger.disabled {
color: #000;
background-color: var(--sc-danger);
border-color: var(--sc-danger);
}
.btn-danger:hover, .btn-danger:active, .btn-danger:focus {
background-color: var(--sc-danger-hover);
border-color: var(--sc-danger-hover);
}
.btn-warning, .btn-warning:not(.disabled):hover, .btn-warning:not(.disabled):active, .btn-warning:focus {
color: #000;
background-color: var(--sc-warning);
border-color: var(--sc-warning);
}
.bg-info, .alert-info, .btn-info, .badge.bg-info > a {
color: #000 !important;
}
.btn-secondary, .btn-secondary:not(.disabled):hover, .btn-secondary:not(.disabled):active, .btn-secondary:focus,
.btn-light, .btn-light:not(.disabled):hover, .btn-light:not(.disabled):active, .btn-light:focus {
color: var(--sc-ink);
background-color: var(--sc-surface);
border-color: var(--sc-line);
}
.btn-secondary:hover, .btn-secondary:active, .btn-secondary:focus,
.btn-light:hover, .btn-light:focus {
color: var(--sc-brown);
background-color: var(--sc-brown-tint);
border-color: var(--sc-line);
}
.btn-check:checked + .btn-secondary, .btn-secondary.active, .show > .btn-secondary.dropdown-toggle {
color: #fff;
background-color: var(--sc-brown);
border-color: var(--sc-brown);
}
.btn-check:checked + .btn-outline-secondary, .btn-outline-secondary.active {
color: #fff !important;
border-color: var(--sc-brown) !important;
}
.btn-outline-primary {
color: var(--sc-brown);
border-color: var(--sc-brown);
}
.btn-outline-primary:hover {
color: #fff;
background-color: var(--sc-brown);
border-color: var(--sc-brown);
}
/* active toggle, e.g. tagging options */
.btn-dark, .btn-dark:hover, .btn-dark:focus, .btn-dark:active {
color: #fff;
background-color: var(--sc-brown);
border-color: var(--sc-brown);
}
.btn-xs, .btn-group-xs > .btn {
border-radius: 0;
}
/* forms */
.form-control, .form-select {
border-color: var(--sc-line);
border-radius: 0;
box-shadow: none;
color: var(--sc-ink);
background-color: var(--sc-surface);
}
.form-control:focus, .form-select:focus {
border-color: var(--sc-focus-border);
box-shadow: 0 0 0 .25rem var(--sc-focus);
}
.form-control::file-selector-button {
color: var(--sc-ink);
background-color: var(--sc-addon);
border-color: var(--sc-line);
}
.form-control:hover:not(:disabled):not([readonly])::file-selector-button {
color: var(--sc-brown);
background-color: var(--sc-brown-soft);
}
.input-group-text {
color: var(--sc-ink);
background-color: var(--sc-addon);
border-color: var(--sc-line);
border-radius: 0;
}
.input-group > :not(:first-child), .input-group > :not(:last-child) {
border-radius: 0;
}
.form-check-input {
border-color: var(--sc-line);
}
.form-check-input:checked {
background-color: var(--sc-brown);
border-color: var(--sc-brown);
}
.form-check-input:focus {
border-color: var(--sc-focus-border);
box-shadow: 0 0 0 .25rem var(--sc-focus);
}
.bootstrap-select > .dropdown-toggle {
border-radius: 0;
}
.bootstrap-select > .dropdown-toggle.bs-placeholder,
.bootstrap-select > .dropdown-toggle.bs-placeholder:hover,
.bootstrap-select > .dropdown-toggle.bs-placeholder:focus,
.bootstrap-select > .dropdown-toggle.bs-placeholder:active {
color: var(--mc-placeholder);
}
.bootstrap-select .dropdown-menu li a.active, .bootstrap-select .dropdown-menu li a:active {
color: #fff;
background-color: var(--sc-brown);
}
div.numberedtextarea-number {
color: var(--sc-muted);
}
/* nav tabs */
.nav-tabs {
border-bottom: 1px solid var(--sc-divider);
}
.nav-tabs .nav-link,
.nav-tabs .nav-link.disabled, .nav-tabs .nav-link.disabled:hover, .nav-tabs .nav-link.disabled:focus {
margin-top: 0;
color: var(--sc-ink);
border: 0;
border-bottom: 3px solid transparent;
border-radius: 0;
background: none;
}
.nav-tabs .nav-link:not(.disabled):hover, .nav-tabs .nav-link:not(.disabled):focus {
padding-bottom: .5rem;
color: var(--sc-brown);
}
.nav-tabs .nav-link.active, .nav-tabs .nav-item.show .nav-link,
.nav-tabs .nav-link:not(.disabled).active {
padding-bottom: .5rem;
color: var(--sc-brown);
background: none;
border-bottom: 3px solid var(--sc-brown);
}
.nav-pills .nav-link {
border-radius: 0;
color: var(--sc-ink);
}
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
background-color: var(--sc-brown);
}
/* tables */
.table {
--bs-table-bg: transparent;
--bs-table-striped-bg: var(--sc-stripe);
--bs-table-striped-color: var(--sc-ink);
--bs-table-hover-color: var(--sc-ink);
--bs-table-hover-bg: var(--sc-hover);
color: var(--sc-ink);
border-color: var(--sc-divider);
}
.table > thead > tr > th, table.dataTable thead th {
color: var(--sc-ink);
font-weight: 600;
border-bottom: 1px solid var(--sc-line);
}
table.dataTable.table-striped > tbody > tr.odd > * {
box-shadow: inset 0 0 0 9999px var(--sc-stripe);
}
table.dataTable.table-hover > tbody > tr:hover > * {
box-shadow: inset 0 0 0 9999px var(--sc-hover);
}
.table > :not(:first-child) {
border-top: 0;
}
.dataTables_wrapper .dataTables_filter input,
.dataTables_wrapper .dataTables_length select {
border: 1px solid var(--sc-line);
border-radius: 0;
}
.pagination > li > a, .pagination > li > span, .pagination .page-link,
.pagination > li > a:hover, .pagination > li > a:focus, .pagination > li > a:active {
top: 0;
color: var(--sc-brown);
font-weight: 400;
text-transform: none;
background-color: var(--sc-surface);
border: 1px solid var(--sc-divider);
border-radius: 0 !important;
}
.pagination > li > a:hover, .pagination > li > a:focus {
background-color: var(--sc-brown-tint);
}
.pagination > .disabled > a, .pagination > .disabled > a:hover, .pagination > .disabled > a:active,
.page-item.disabled .page-link {
color: var(--sc-muted);
background-color: var(--sc-surface);
border-width: 1px;
border-color: var(--sc-divider);
}
.page-item.active .page-link {
color: #fff;
background-color: var(--sc-brown);
border-color: var(--sc-brown);
}
.breadcrumb {
border: 1px solid var(--sc-divider);
border-radius: 0;
}
/* alerts like the portal */
.alert {
border: 1px solid;
border-radius: 0;
}
.alert-primary {
color: #43382c !important;
background-color: #e2dfdb !important;
border-color: #d4cec8 !important;
}
.alert-info {
color: #3c535e !important;
background-color: #eaf6fb !important;
border-color: #e0f1f9 !important;
}
.alert-success {
color: #375603;
background-color: #e8f7ce;
border-color: #dcf3b5;
}
.alert-danger {
color: #8f4343;
background-color: #fce2e2;
border-color: #fad4d4;
}
.alert-warning {
color: #885907;
background-color: #f9eace;
border-color: #f7dfb6;
}
.alert-secondary, .alert-light {
color: var(--sc-ink);
background-color: var(--sc-cap);
border-color: var(--sc-divider);
}
.alert .alert-link {
color: inherit;
}
.alert .btn-close {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e");
opacity: .5;
}
/* badges and semantic colors, readable text on top */
.badge {
font-weight: 500;
border-radius: 0;
}
.bg-success {
color: #000 !important;
background-color: var(--sc-green) !important;
}
.bg-warning {
color: #000 !important;
background-color: var(--sc-warning) !important;
}
.bg-danger {
color: #000 !important;
background-color: var(--sc-danger) !important;
}
.badge.bg-secondary, .badge.bg-light {
color: var(--sc-ink);
background-color: var(--sc-addon) !important;
}
.text-success {
color: #4f8a0b !important;
}
.text-danger {
color: var(--sc-danger-text) !important;
}
.text-warning {
color: #9a6100 !important;
}
.progress {
border-radius: 0;
}
.progress-bar {
color: #000;
background-color: var(--sc-green);
}
.progress-bar.bg-danger, .progress-bar-danger {
color: #000;
background-color: var(--sc-danger) !important;
}
.progress-bar-warning {
color: #000;
background-color: var(--sc-warning) !important;
}
/* footer */
.footer, .footer a {
color: var(--sc-muted);
}
.footer hr {
border-color: var(--sc-divider);
}
+4
View File
@@ -0,0 +1,4 @@
{
"name": "servercow",
"description": "Based on the servercow theme"
}
+68
View File
@@ -233,6 +233,38 @@ function customize($_action, $_item, $_data = null) {
'msg' => 'custom_login_modified'
);
break;
case 'ui_theme':
$ui_theme = is_array($_data['ui_theme']) ? (string)reset($_data['ui_theme']) : (string)$_data['ui_theme'];
if ($ui_theme !== '' && !array_key_exists($ui_theme, customize('get', 'ui_themes'))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'ui_theme_invalid'
);
return false;
}
try {
if ($ui_theme === '') {
$redis->del('UI_THEME');
}
else {
$redis->set('UI_THEME', $ui_theme);
}
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'ui_theme_modified'
);
break;
}
break;
case 'delete':
@@ -402,6 +434,42 @@ function customize($_action, $_item, $_data = null) {
return false;
}
break;
case 'ui_themes':
// theme folders in css/themes/<name>/ with a theme.css
$themes = array();
foreach (glob('/web/css/themes/*/theme.css') as $theme_css) {
$theme_dir = dirname($theme_css);
$theme = basename($theme_dir);
if (!preg_match('/^[a-zA-Z0-9_-]+$/', $theme)) {
continue;
}
$meta = json_decode(@file_get_contents($theme_dir . '/theme.json'), true);
$themes[$theme] = array(
'name' => (!empty($meta['name'])) ? $meta['name'] : $theme,
'description' => (!empty($meta['description'])) ? $meta['description'] : '',
'dark' => file_exists($theme_dir . '/dark.css')
);
}
ksort($themes);
return $themes;
break;
case 'ui_theme':
try {
$ui_theme = $redis->get('UI_THEME');
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
if (empty($ui_theme) || !preg_match('/^[a-zA-Z0-9_-]+$/', $ui_theme) || !file_exists('/web/css/themes/' . $ui_theme . '/theme.css')) {
return '';
}
return $ui_theme;
break;
}
break;
}
+15
View File
@@ -22,6 +22,9 @@ if (preg_match("/debug/i", $_SERVER['REQUEST_URI'])) {
if ($_SERVER['REQUEST_URI'] == '/') {
$css_minifier->add('/web/css/site/index.css');
}
if (file_exists('/web/css/themes/'.$UI_THEME.'/theme.css')) {
$css_minifier->add('/web/css/themes/'.$UI_THEME.'/theme.css');
}
$hash = $css_minifier->getDataHash();
$CSSPath = '/tmp/' . $hash . '.css';
@@ -30,6 +33,17 @@ if(!file_exists($CSSPath)) {
cleanupCSS($hash);
}
// dark mode bundle, core dark mode + theme dark.css
$dark_minifier = new CSSminifierExtended('/web/css/themes/mailcow-darkmode.css');
if (file_exists('/web/css/themes/'.$UI_THEME.'/dark.css')) {
$dark_minifier->add('/web/css/themes/'.$UI_THEME.'/dark.css');
}
$DarkCSSPath = '/tmp/' . $hash . '-' . $dark_minifier->getDataHash() . '.css';
if(!file_exists($DarkCSSPath)) {
$dark_minifier->minify($DarkCSSPath);
}
$dark_css_path = '/cache/' . basename($DarkCSSPath);
$mailcow_apps_processed = $MAILCOW_APPS;
$app_links = customize('get', 'app_links');
$app_links_processed = $app_links;
@@ -77,6 +91,7 @@ $globalVariables = [
'dual_login' => @$_SESSION['dual-login'],
'ui_texts' => $UI_TEXTS,
'css_path' => '/cache/'.basename($CSSPath),
'dark_css_path' => $dark_css_path,
'logo' => customize('get', 'main_logo'),
'logo_dark' => customize('get', 'main_logo_dark'),
'available_languages' => $AVAILABLE_LANGUAGES,
+7 -1
View File
@@ -319,10 +319,16 @@ $css_minifier = new CSSminifierExtended();
$css_dir = array_diff(scandir('/web/css/build'), array('..', '.'));
// get customized ui data
$UI_TEXTS = customize('get', 'ui_texts');
// theme selected in admin UI overrides $UI_THEME
if ($ui_theme_selected = customize('get', 'ui_theme')) {
$UI_THEME = $ui_theme_selected;
}
// minify bootstrap theme
if (file_exists('/web/css/themes/'.$UI_THEME.'-bootstrap.css'))
if (file_exists('/web/css/themes/'.$UI_THEME.'/bootstrap.css'))
$css_minifier->add('/web/css/themes/'.$UI_THEME.'/bootstrap.css');
elseif (file_exists('/web/css/themes/'.$UI_THEME.'-bootstrap.css'))
$css_minifier->add('/web/css/themes/'.$UI_THEME.'-bootstrap.css');
else
$css_minifier->add('/web/css/themes/lumen-bootstrap.css');
+2
View File
@@ -121,6 +121,8 @@ $AVAILABLE_LANGUAGES = array(
// default theme is lumen
// additional themes can be found here: https://bootswatch.com/
// copy them to data/web/css/themes/{THEME-NAME}-bootstrap.css
// or use a theme folder data/web/css/themes/{THEME-NAME}/ (theme.css, optional dark.css, bootstrap.css, theme.json)
// theme folders can be selected in System > Configuration > Customize, which overrides $UI_THEME
$UI_THEME = "lumen";
// Show DKIM private keys - false by default
+3 -3
View File
@@ -1,7 +1,7 @@
/*!
* Bootstrap v5.1.3 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Bootstrap v5.3.8 (https://getbootstrap.com/)
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e()}(this,(function(){"use strict";const t="transitionend",e=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let i=t.getAttribute("href");if(!i||!i.includes("#")&&!i.startsWith("."))return null;i.includes("#")&&!i.startsWith("#")&&(i=`#${i.split("#")[1]}`),e=i&&"#"!==i?i.trim():null}return e},i=t=>{const i=e(t);return i&&document.querySelector(i)?i:null},n=t=>{const i=e(t);return i?document.querySelector(i):null},s=e=>{e.dispatchEvent(new Event(t))},o=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),r=t=>o(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(t):null,a=(t,e,i)=>{Object.keys(i).forEach((n=>{const s=i[n],r=e[n],a=r&&o(r)?"element":null==(l=r)?`${l}`:{}.toString.call(l).match(/\s([a-z]+)/i)[1].toLowerCase();var l;if(!new RegExp(s).test(a))throw new TypeError(`${t.toUpperCase()}: Option "${n}" provided type "${a}" but expected type "${s}".`)}))},l=t=>!(!o(t)||0===t.getClientRects().length)&&"visible"===getComputedStyle(t).getPropertyValue("visibility"),c=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),h=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?h(t.parentNode):null},d=()=>{},u=t=>{t.offsetHeight},f=()=>{const{jQuery:t}=window;return t&&!document.body.hasAttribute("data-bs-no-jquery")?t:null},p=[],m=()=>"rtl"===document.documentElement.dir,g=t=>{var e;e=()=>{const e=f();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(p.length||document.addEventListener("DOMContentLoaded",(()=>{p.forEach((t=>t()))})),p.push(e)):e()},_=t=>{"function"==typeof t&&t()},b=(e,i,n=!0)=>{if(!n)return void _(e);const o=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(i)+5;let r=!1;const a=({target:n})=>{n===i&&(r=!0,i.removeEventListener(t,a),_(e))};i.addEventListener(t,a),setTimeout((()=>{r||s(i)}),o)},v=(t,e,i,n)=>{let s=t.indexOf(e);if(-1===s)return t[!i&&n?t.length-1:0];const o=t.length;return s+=i?1:-1,n&&(s=(s+o)%o),t[Math.max(0,Math.min(s,o-1))]},y=/[^.]*(?=\..*)\.|.*/,w=/\..*/,E=/::\d+$/,A={};let T=1;const O={mouseenter:"mouseover",mouseleave:"mouseout"},C=/^(mouseenter|mouseleave)/i,k=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function L(t,e){return e&&`${e}::${T++}`||t.uidEvent||T++}function x(t){const e=L(t);return t.uidEvent=e,A[e]=A[e]||{},A[e]}function D(t,e,i=null){const n=Object.keys(t);for(let s=0,o=n.length;s<o;s++){const o=t[n[s]];if(o.originalHandler===e&&o.delegationSelector===i)return o}return null}function S(t,e,i){const n="string"==typeof e,s=n?i:e;let o=P(t);return k.has(o)||(o=t),[n,s,o]}function N(t,e,i,n,s){if("string"!=typeof e||!t)return;if(i||(i=n,n=null),C.test(e)){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};n?n=t(n):i=t(i)}const[o,r,a]=S(e,i,n),l=x(t),c=l[a]||(l[a]={}),h=D(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=L(r,e.replace(y,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(let a=o.length;a--;)if(o[a]===r)return s.delegateTarget=r,n.oneOff&&j.off(t,s.type,e,i),i.apply(r,[s]);return null}}(t,i,n):function(t,e){return function i(n){return n.delegateTarget=t,i.oneOff&&j.off(t,n.type,e),e.apply(t,[n])}}(t,i);u.delegationSelector=o?i:null,u.originalHandler=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function I(t,e,i,n,s){const o=D(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function P(t){return t=t.replace(w,""),O[t]||t}const j={on(t,e,i,n){N(t,e,i,n,!1)},one(t,e,i,n){N(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=S(e,i,n),a=r!==e,l=x(t),c=e.startsWith(".");if(void 0!==o){if(!Line truncated
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e()}(this,function(){"use strict";const t=new Map,e={set(e,i,n){t.has(e)||t.set(e,new Map);const s=t.get(e);s.has(i)||0===s.size?s.set(i,n):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(s.keys())[0]}.`)},get:(e,i)=>t.has(e)&&t.get(e).get(i)||null,remove(e,i){if(!t.has(e))return;const n=t.get(e);n.delete(i),0===n.size&&t.delete(e)}},i="transitionend",n=t=>(t&&window.CSS&&window.CSS.escape&&(t=t.replace(/#([^\s"#']+)/g,(t,e)=>`#${CSS.escape(e)}`)),t),s=t=>null==t?`${t}`:Object.prototype.toString.call(t).match(/\s([a-z]+)/i)[1].toLowerCase(),o=t=>{t.dispatchEvent(new Event(i))},r=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),a=t=>r(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(n(t)):null,l=t=>{if(!r(t)||0===t.getClientRects().length)return!1;const e="visible"===getComputedStyle(t).getPropertyValue("visibility"),i=t.closest("details:not([open])");if(!i)return e;if(i!==t){const e=t.closest("summary");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},c=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),h=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?h(t.parentNode):null},d=()=>{},u=t=>{t.offsetHeight},f=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,p=[],m=()=>"rtl"===document.documentElement.dir,g=t=>{var e;e=()=>{const e=f();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(p.length||document.addEventListener("DOMContentLoaded",()=>{for(const t of p)t()}),p.push(e)):e()},_=(t,e=[],i=t)=>"function"==typeof t?t.call(...e):i,b=(t,e,n=!0)=>{if(!n)return void _(t);const s=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(e)+5;let r=!1;const a=({target:n})=>{n===e&&(r=!0,e.removeEventListener(i,a),_(t))};e.addEventListener(i,a),setTimeout(()=>{r||o(e)},s)},v=(t,e,i,n)=>{const s=t.length;let o=t.indexOf(e);return-1===o?!i&&n?t[s-1]:t[0]:(o+=i?1:-1,n&&(o=(o+s)%s),t[Math.max(0,Math.min(o,s-1))])},y=/[^.]*(?=\..*)\.|.*/,w=/\..*/,A=/::\d+$/,E={};let T=1;const C={mouseenter:"mouseover",mouseleave:"mouseout"},O=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function x(t,e){return e&&`${e}::${T++}`||t.uidEvent||T++}function k(t){const e=x(t);return t.uidEvent=e,E[e]=E[e]||{},E[e]}function L(t,e,i=null){return Object.values(t).find(t=>t.callable===e&&t.delegationSelector===i)}function S(t,e,i){const n="string"==typeof e,s=n?i:e||i;let o=N(t);return O.has(o)||(o=t),[n,s,o]}function D(t,e,i,n,s){if("string"!=typeof e||!t)return;let[o,r,a]=S(e,i,n);if(e in C){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};r=t(r)}const l=k(t),c=l[a]||(l[a]={}),h=L(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=x(r,e.replace(y,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(const a of o)if(a===r)return j(s,{delegateTarget:r}),n.oneOff&&P.off(t,s.type,e,i),i.apply(r,[s])}}(t,i,r):function(t,e){return function i(n){return j(n,{delegateTarget:t}),i.oneOff&&P.off(t,n.type,e),e.apply(t,[n])}}(t,r);u.delegationSelector=o?i:null,u.callable=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function $(t,e,i,n,s){const o=L(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function I(t,e,i,n){const s=e[i]||{};for(const[o,r]of Object.entries(s))o.includes(n)&&$(t,e,i,r.callable,r.delegationSelector)}function N(t){return t=t.replace(w,""),C[t]||t}const P={on(t,e,i,n){D(t,e,i,n,!1)},one(t,e,i,n){D(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=S(e,i,n),a=r!==e,l=k(t),c=l[rLine truncated
//# sourceMappingURL=bootstrap.bundle.min.js.map
+4 -1
View File
@@ -124,6 +124,9 @@ $(document).ready(function() {
var lastTab = localStorage.getItem(key);
if (lastTab) {
$('[data-bs-target="#' + lastTab + '"]').click();
var dropdown = $('[data-bs-target="#' + lastTab + '"]').closest('.dropdown');
dropdown.find('.dropdown-menu').removeClass('show');
dropdown.find('.dropdown-toggle').attr('aria-expanded', 'false');
var tab = $('[id^="' + lastTab + '"]');
$(tab).find('.card-body.collapse:first').collapse('show');
}
@@ -347,7 +350,7 @@ $(document).ready(function() {
if ($('#rspamd_logo_sm').length) $('#rspamd_logo_sm').attr('src', '/img/rspamd_logo_dark.png');
localStorage.setItem('mailcow_theme', 'light');
}else{
$('head').append('<link id="dark-mode-theme" rel="stylesheet" type="text/css" href="/css/themes/mailcow-darkmode.css">');
$('head').append('<link id="dark-mode-theme" rel="stylesheet" type="text/css" href="' + mailcow_dark_css + '">');
$('#dark-mode-toggle').prop('checked', true);
$('.main-logo').addClass('d-none');
$('.main-logo-dark').removeClass('d-none');
+3
View File
@@ -2038,6 +2038,9 @@ if (isset($_GET['query'])) {
case "custom_login":
process_edit_return(customize('edit', 'custom_login', $attr));
break;
case "ui_theme":
process_edit_return(customize('edit', 'ui_theme', $attr));
break;
case "self":
if ($_SESSION['mailcow_cc_role'] == "domainadmin") {
process_edit_return(domain_admin('edit', $attr));
+20 -12
View File
@@ -14,7 +14,7 @@
"mailbox_relayhost": "Relayhost für eine Mailbox setzen",
"prohibited": "Untersagt durch Richtlinie",
"protocol_access": "Ändern der erlaubten Protokolle",
"pushover": "Pushover",
"pushover": "Pushover-Berechtigung",
"pw_reset": "Verwalten der E-Mail zur Passwortwiederherstellung erlauben",
"quarantine": "Quarantäne-Aktionen",
"quarantine_attachments": "Anhänge aus Quarantäne",
@@ -97,7 +97,7 @@
"relay_transport_info": "<div class=\"badge fs-6 bg-info\">Info</div> Transport-Maps können erstellt werden, um individuelle Ziele für eine Relay-Domain zu definieren.",
"relay_unknown_only": "Nur nicht-lokale Mailboxen relayen. Existente Mailboxen werden weiterhin lokal zugestellt.",
"relayhost_wrapped_tls_info": "Bitte <b>keine</b> \"TLS-wrapped Ports\" verwenden (etwa SMTPS via Port 465/tcp).<br>\r\nDer Transport wird stattdessen STARTTLS anfordern, um TLS zu verwenden. TLS kann unter \"TLS Policy Maps\" erzwungen werden.",
"select": "Bitte auswählen",
"select": "Bitte auswählen...",
"select_domain": "Bitte zuerst eine Domain auswählen",
"sieve_desc": "Kurze Beschreibung",
"sieve_type": "Filtertyp",
@@ -395,6 +395,11 @@
"ui_header_announcement_type_info": "Info",
"ui_header_announcement_type_warning": "Wichtig",
"ui_texts": "UI-Label und Texte",
"ui_theme": "Theme",
"ui_theme_dark": "Enthält eine Darkmode-Variante",
"ui_theme_default": "Standard",
"ui_theme_info": "Themes sind Ordner in <code>data/web/css/themes/&lt;name&gt;/</code> mit folgenden Dateien:<ul class=\"mb-0\"><li><code>theme.css</code> (erforderlich)</li><li><code>dark.css</code> (optional, Darkmode)</li><li><code>bootstrap.css</code> (optional, ersetzt das Bootstrap-Basistheme)</li><li><code>theme.json</code> (optional, <code>name</code> und <code>description</code>)</li></ul>",
"ui_theme_none": "Keine Themes gefunden.",
"unban_pending": "ausstehend",
"unchanged_if_empty": "Unverändert, wenn leer",
"upload": "Hochladen",
@@ -432,7 +437,7 @@
"comment_too_long": "Kommentarfeld darf maximal 160 Zeichen enthalten",
"cors_invalid_method": "Allow-Methods enthält eine ungültige Methode",
"cors_invalid_origin": "Allow-Origins enthält eine ungültige Origin",
"defquota_empty": "Standard-Quota darf nicht 0 sein",
"defquota_empty": "Standard-Quota darf nicht 0 sein.",
"demo_mode_enabled": "Demo Mode ist aktiviert",
"description_invalid": "Ressourcenbeschreibung für %s ist ungültig",
"dkim_domain_or_sel_exists": "Ein DKIM-Key für die Domain \"%s\" existiert und wird nicht überschrieben",
@@ -457,13 +462,13 @@
"global_map_write_error": "Kann globale Map ID %s nicht schreiben: %s",
"goto_empty": "Eine Alias-Adresse muss auf mindestens eine gültige Ziel-Adresse zeigen",
"goto_invalid": "Ziel-Adresse %s ist ungültig",
"ham_learn_error": "Ham Lernfehler: %s",
"ham_learn_error": "Ham-Lernfehler: %s",
"iam_test_connection": "Verbindung fehlgeschlagen",
"imagick_exception": "Fataler Bildverarbeitungsfehler",
"img_dimensions_exceeded": "Grafik überschreitet die maximale Bildgröße",
"img_invalid": "Grafik konnte nicht validiert werden",
"img_size_exceeded": "Grafik überschreitet die maximale Dateigröße",
"img_tmp_missing": "Grafik konnte nicht validiert werden: Erstellung temporärer Datei fehlgeschlagen.",
"img_tmp_missing": "Grafik konnte nicht validiert werden: Temporärer Datei nicht gefunden.",
"invalid_bcc_map_type": "Ungültiger BCC-Map-Typ",
"invalid_destination": "Ziel-Format \"%s\" ist ungültig",
"invalid_filter_type": "Ungültiger Filtertyp",
@@ -476,7 +481,7 @@
"invalid_reset_token": "Ungültiger Rücksetz-Token",
"ip_list_empty": "Liste erlaubter IPs darf nicht leer sein",
"is_alias": "%s lautet bereits eine Alias-Adresse",
"is_alias_or_mailbox": "Eine Mailbox, ein Alias oder eine sich aus einer Alias-Domain ergebende Adresse mit dem Namen %s ist bereits vorhanden",
"is_alias_or_mailbox": "Eine Mailbox, ein Alias oder eine sich aus einer Alias-Domain ergebende Adresse mit dem Namen %s ist bereits vorhanden.",
"is_spam_alias": "%s lautet bereits eine temporäre Alias-Adresse (Spam-Alias-Adresse)",
"last_key": "Letzter Key kann nicht gelöscht werden, bitte stattdessen die 2FA deaktivieren.",
"login_failed": "Anmeldung fehlgeschlagen",
@@ -550,6 +555,7 @@
"webauthn_authenticator_failed": "Der ausgewählte Authenticator wurde nicht gefunden",
"webauthn_publickey_failed": "Zu dem ausgewählten Authenticator wurde kein Publickey hinterlegt",
"webauthn_username_failed": "Der ausgewählte Authenticator gehört zu einem anderen Konto",
"ui_theme_invalid": "Theme nicht gefunden",
"unknown": "Ein unbekannter Fehler trat auf",
"unknown_tfa_method": "Unbekannte TFA-Methode",
"unlimited_quota_acl": "Unendliche Quota untersagt durch ACL",
@@ -661,7 +667,7 @@
"delete1": "Lösche Nachricht nach Übertragung vom Quell-Server",
"delete2": "Lösche Nachrichten von Ziel-Server, die nicht auf Quell-Server vorhanden sind",
"delete2duplicates": "Lösche Duplikate im Ziel",
"delete_ays": "Soll der Löschvorgang wirklich ausgeführt werden?",
"delete_ays": "Bitte den Löschvorgang bestätigen.",
"description": "Beschreibung",
"disable_login": "Login verbieten (Mails werden weiterhin angenommen)",
"domain": "Domain bearbeiten",
@@ -781,7 +787,7 @@
"spam_score": "Einen benutzerdefiniterten Spam-Score festlegen",
"subfolder2": "Ziel-Ordner<br><small>(leer = kein Unterordner)</small>",
"syncjob": "Sync-Job bearbeiten",
"target_address": "Ziel-Adresse(n)",
"target_address": "Ziel-Adresse(n)<small>(kommasepariert)</small>",
"target_domain": "Ziel-Domain",
"timeout1": "Timeout für Verbindung zum Remote-Host",
"timeout2": "Timeout für Verbindung zum lokalen Host",
@@ -963,7 +969,7 @@
"private_comment": "Privater Kommentar",
"public_comment": "Öffentlicher Kommentar",
"q_add_header": "bei Mail in Junk-Ordner",
"q_all": "bei Reject und Mail in Junk-Ordner",
"q_all": " bei Reject und Mail in Junk-Ordner",
"q_reject": "bei Reject",
"quarantine_category": "Quarantäne-Benachrichtigungskategorie",
"quarantine_notification": "Quarantäne-Benachrichtigung",
@@ -1037,7 +1043,8 @@
"syncjob_EXIT_AUTHENTICATION_FAILURE": "Authentifizierungsproblem",
"syncjob_EXIT_OVERQUOTA": "Ziel Mailbox ist über dem Limit",
"syncjob_EXIT_CONNECTION_FAILURE_HOST1": "Kann keine Verbindung zum Zielserver herstellen",
"iam": "Identitätsanbieter"
"iam": "Identitätsanbieter",
"force_tfa": "2-Faktor-Authentifizierung"
},
"oauth2": {
"access_denied": "Bitte als Mailbox-Nutzer einloggen, um den Zugriff via OAuth2 zu erlauben.",
@@ -1206,6 +1213,7 @@
"tls_policy_map_entry_deleted": "TLS-Richtlinie mit der ID %s wurde gelöscht",
"tls_policy_map_entry_saved": "TLS-Richtlinieneintrag \"%s\" wurde gespeichert",
"ui_texts": "Änderungen an UI-Texten",
"ui_theme_modified": "Theme wurde erfolgreich gespeichert",
"upload_success": "Datei wurde erfolgreich hochgeladen",
"verified_fido2_login": "FIDO2-Anmeldung verifiziert",
"verified_totp_login": "TOTP-Anmeldung verifiziert",
@@ -1231,7 +1239,7 @@
"set_tfa": "Konfiguriere Zwei-Faktor-Authentifizierungsmethode",
"start_webauthn_validation": "Starte Validierung",
"tfa": "Zwei-Faktor-Authentifizierung",
"tfa_token_invalid": "TFA-Token ungültig!",
"tfa_token_invalid": "2FA-Token ungültig",
"totp": "Time-based-OTP (Google Authenticator etc.)",
"u2f_deprecated": "Es sieht so aus als wurde der Schlüssel mit der alten U2F Methode registriert. Wir werden die Zwei-Faktor-Authentifizierung deaktivieren und deinen Schlüssel löschen.",
"u2f_deprecated_important": "Bitte registriere den Schlüssel im Adminbereich mit der neuen WebAuthn Methode.",
@@ -1360,7 +1368,7 @@
"shared_aliases_desc": "Geteilte Alias-Adressen werden nicht bei benutzerdefinierten Einstellungen, wie die des Spam-Filters oder der Verschlüsselungsrichtlinie, berücksichtigt. Entsprechende Spam-Filter können lediglich von einem Administrator vorgenommen werden.",
"show_sieve_filters": "Zeige aktiven Filter des Benutzers",
"sogo_profile_reset": "SOGo-Profil zurücksetzen",
"sogo_profile_reset_help": "Das Profil wird inklusive <b>aller</b> Kalender- und Kontaktdaten <b>unwiederbringlich gelöscht</b>.",
"sogo_profile_reset_help": "Das Profil wird inklusive <b>aller Kalender- und Kontaktdaten unwiederbringlich gelöscht</b>.",
"sogo_profile_reset_now": "Profil jetzt zurücksetzen",
"spam_aliases": "Spam E-Mail-Aliasse",
"spam_aliases_info": "Ein Spam-Alias ist eine temporäre E-Mailadresse, die benutzt werden kann, um eine echte E-Mail Adressen zu schützen. <br>Optional kann eine Ablaufzeit gesetzt werden, sodass der Alias nach dem definierten Zeitraum automatisch deaktiviert wird, was missbrauchte oder geleakte Adressen effektiv entsorgt.",
+7
View File
@@ -405,6 +405,11 @@
"ui_header_announcement_type_info": "Info",
"ui_header_announcement_type_warning": "Important",
"ui_texts": "UI labels and texts",
"ui_theme": "Theme",
"ui_theme_dark": "Includes a dark mode variant",
"ui_theme_default": "Default",
"ui_theme_info": "Themes are folders in <code>data/web/css/themes/&lt;name&gt;/</code> with the following files:<ul class=\"mb-0\"><li><code>theme.css</code> (required)</li><li><code>dark.css</code> (optional, dark mode)</li><li><code>bootstrap.css</code> (optional, replaces the Bootstrap base theme)</li><li><code>theme.json</code> (optional, <code>name</code> and <code>description</code>)</li></ul>",
"ui_theme_none": "No themes found.",
"unban_pending": "unban pending",
"unchanged_if_empty": "If unchanged leave blank",
"upload": "Upload",
@@ -554,6 +559,7 @@
"webauthn_authenticator_failed": "The selected authenticator was not found",
"webauthn_publickey_failed": "No public key was stored for the selected authenticator",
"webauthn_username_failed": "The selected authenticator belongs to another account",
"ui_theme_invalid": "Theme not found",
"unknown": "An unknown error occurred",
"unknown_tfa_method": "Unknown TFA method",
"unlimited_quota_acl": "Unlimited quota prohibited by ACL",
@@ -1214,6 +1220,7 @@
"tls_policy_map_entry_deleted": "TLS policy map ID %s has been deleted",
"tls_policy_map_entry_saved": "TLS policy map entry \"%s\" has been saved",
"ui_texts": "Saved changes to UI texts",
"ui_theme_modified": "Theme was saved successfully",
"upload_success": "File uploaded successfully",
"verified_fido2_login": "Verified FIDO2 login",
"verified_totp_login": "Verified TOTP login",
+261 -58
View File
@@ -17,7 +17,7 @@
"spam_policy": "Lista de bloqueo/desbloqueo",
"spam_score": "Puntuación de spam",
"syncjobs": "Trabajos de sincronización",
"tls_policy": "Póliza de TLS",
"tls_policy": "Política de TLS",
"unlimited_quota": "Cuota ilimitada para buzones",
"app_passwds": "Gestionar las contraseñas de aplicaciones",
"domain_desc": "Cambiar descripción del dominio",
@@ -28,7 +28,8 @@
"pw_reset": "Permitir el restablecimiento de la contraseña del usuario mailcow",
"sogo_access": "Permitir la gestión del acceso a SOGo",
"mailbox_relayhost": "Cambiar el host de reenvío para un buzón",
"smtp_ip_access": "Cambiar hosts permitidos para SMTP"
"smtp_ip_access": "Cambiar hosts permitidos para SMTP",
"alias_external_goto": "Permitir alias con dominios de destino externos"
},
"add": {
"activate_filter_warn": "Todos los demás filtros se desactivarán cuando este filtro se active.",
@@ -78,8 +79,8 @@
"relay_all": "Retransmitir todos los destinatarios",
"relay_all_info": "↪ Si se elige <b>no</b> retransmitir todos los destinatarios, será necesario agregar un buzón \"ciego\" por cada destinatario que deba ser retransmitido.",
"relay_domain": "Retransmitir este dominio",
"select": "Por favor selecciona...",
"select_domain": "Por favor elige un dominio primero",
"select": "Seleccione...",
"select_domain": "Elija primero un dominio",
"sieve_desc": "Descripción",
"sieve_type": "Tipo de filtro",
"skipcrossduplicates": "Omitir mensajes duplicados en carpetas (orden de llegada)",
@@ -108,7 +109,9 @@
"domain_matches_hostname": "El dominio %s coincide con el nombre de host",
"relay_unknown_only": "Reenviar sólo los buzones no existentes. Los buzones existentes se entregarán localmente.",
"relayhost_wrapped_tls_info": "Por favor, no utilice puertos con TLS (habitualmente, el puerto 465). <br>Utilice cualquier puerto no cifrado y emita STARTTLS. Se puede crear una política para imponer TLS en \"TLS policy maps\".",
"tags": "Etiquetas"
"tags": "Etiquetas",
"internal_info": "Los alias internos solo son accesibles desde el propio dominio o sus dominios alias.",
"sender_allowed": "Permitir enviar como este alias"
},
"admin": {
"access": "Acceso",
@@ -171,7 +174,7 @@
"f2b_max_ban_time": "Max tiempo de restricción (s)",
"f2b_netban_ipv4": "Tamaño de subred IPv4 para aplicar la restricción (8-32)",
"f2b_netban_ipv6": "Tamaño de subred IPv6 para aplicar la restricción (8-128)",
"f2b_parameters": "Parametros Fail2ban",
"f2b_parameters": "Parámetros de Fail2ban",
"f2b_retry_window": "Ventana de tiempo entre reintentos",
"f2b_whitelist": "Redes y hosts en lista de desbloqueo",
"filter_table": "Filtrar tabla",
@@ -190,7 +193,7 @@
"include_exclude_info": "Por defecto - sin selección - <b>todos los buzones</b> son notificados.",
"includes": "Incluye a estos destinatarios",
"link": "Enlace",
"loading": "Espera por favor...",
"loading": "Espere, por favor...",
"logo_info": "La imagen se escalará a una altura de 40 px para la barra de navegación superior y un máx. de ancho de 250 px para la página de inicio. Un gráfico escalable es muy recomendable.",
"main_name": "Nombre \"mailcow UI\"",
"merged_vars_hint": "Las filas grises fueron incorporadas de <code>vars.(local.)inc.php</code> y no son modificables.",
@@ -281,7 +284,7 @@
"password_length": "Longitud de la contraseña",
"quicklink_text": "Mostrar u ocultar enlaces rápidos a otras páginas de inicio bajo el formulario de inicio de sesión",
"password_policy_lowerupper": "Debe contener caracteres en minúsculas y mayúsculas",
"rspamd_global_filters_regex": "Sus nombres indican su propósito. Todo el contenido debe constar de expresiones regulares válidas con el formato \"/patrón/opciones\" (por ejemplo, <code>/.+@domain\\.tld/i</code>).<br>\n Si bien se llevan a cabo comprobaciones básicas de cada expresión regular, la funcionalidad de Rspamd puede verse inutilizada, si no consigue interpretar correctamente la sintaxis utilizada.<br>\n Rspamd intentará leer el contenido del mapa cuando éste se modifique. En caso de de problemas, <a href=\"\" data-toggle=\"modal\" data-container=\"rspamd-mailcow\" data-target=\"#RestartContainer\">reinicie Rspamd</a> para forzar una recarga del mapa.<br>Los elementos incluidos en listas de bloqueo se excluyen de la cuarentena.",
"rspamd_global_filters_regex": "Sus nombres indican su propósito. Todo el contenido debe constar de expresiones regulares válidas con el formato \"/patrón/opciones\" (por ejemplo, <code>/.+@domain\\.tld/i</code>).<br>\n Si bien se llevan a cabo comprobaciones básicas de cada expresión regular, la funcionalidad de Rspamd puede verse inutilizada, si no consigue interpretar correctamente la sintaxis utilizada.<br>\n Rspamd intentará leer el contenido del mapa cuando este se modifique. En caso de problemas, <a href=\"\" data-toggle=\"modal\" data-container=\"rspamd-mailcow\" data-target=\"#RestartContainer\">reinicie Rspamd</a> para forzar una recarga del mapa.<br>Los elementos incluidos en listas de bloqueo se excluyen de la cuarentena.",
"iam_use_ssl_info": "Si se habilita SSL y el puerto se establece en el 389, se cambiará automáticamente al 636.",
"iam_login_provisioning": "Crear usuarios automáticamente al iniciar sesión",
"iam_periodic_full_sync": "Sincronización completa periódica",
@@ -400,7 +403,20 @@
"user_quicklink": "Ocultar enlace rápido a página de inicio de sesión de usuario",
"validate_license_now": "Validar el GUID contra el servidor de licencias",
"verify": "Verificar",
"yes": "&#10003;"
"yes": "&#10003;",
"iam_auth_flow_info": "Además del Authorization Code Flow (Standard Flow en Keycloak), que se usa para el inicio de sesión único (SSO), mailcow también admite autenticación con credenciales directas. El Mailpassword Flow intenta validar las credenciales del usuario mediante la API REST de administración de Keycloak. mailcow obtiene la contraseña cifrada del atributo <code>mailcow_password</code>, que está mapeado en Keycloak.",
"iam_extra_permission": "Para que los siguientes ajustes funcionen, el cliente de mailcow en Keycloak necesita una <code>Service account</code> y el permiso <code>view-users</code>.",
"iam_bindpass": "Contraseña de enlace (bind)",
"iam_rest_flow": "Mailpassword Flow",
"iam_binddn": "Bind DN",
"oauth2_info": "La implementación de OAuth2 admite el tipo de concesión \"Authorization Code\" y emite tokens de actualización.<br>\r\nEl servidor también emite automáticamente nuevos tokens de actualización después de usar uno.<br><br>\r\n&#8226; El alcance predeterminado es <i>profile</i>. Solo los usuarios de buzones pueden autenticarse mediante OAuth2. Si se omite el parámetro scope, se usa <i>profile</i>.<br>\r\n&#8226; El cliente debe enviar el parámetro <i>state</i> como parte de la solicitud de autorización.<br><br>\r\nRutas para las solicitudes a la API de OAuth2: <br>\r\n<ul>\r\n <li>Endpoint de autorización: <code>/oauth/authorize</code></li>\r\n <li>Endpoint de tokens: <code>/oauth/token</code></li>\r\n <li>Página de recursos: <code>/oauth/profile</code></li>\r\n</ul>\r\nRegenerar el secreto del cliente no invalida los códigos de autorización existentes, pero estos no podrán renovar su token.<br><br>\r\nRevocar los tokens de los clientes termina de inmediato todas las sesiones activas. Todos los clientes deberán volver a autenticarse.",
"rate_name": "Nombre del límite",
"ui_header_announcement_active": "Activar anuncio",
"ui_theme": "Tema",
"ui_theme_dark": "Incluye una variante en modo oscuro",
"ui_theme_default": "Predeterminado",
"ui_theme_info": "Los temas son carpetas en <code>data/web/css/themes/&lt;name&gt;/</code> con los siguientes archivos:<ul class=\"mb-0\"><li><code>theme.css</code> (obligatorio)</li><li><code>dark.css</code> (opcional, modo oscuro)</li><li><code>bootstrap.css</code> (opcional, reemplaza el tema base de Bootstrap)</li><li><code>theme.json</code> (opcional, <code>name</code> y <code>description</code>)</li></ul>",
"ui_theme_none": "No se encontraron temas."
},
"danger": {
"access_denied": "Acceso denegado o datos del formulario inválidos",
@@ -447,7 +463,7 @@
"map_content_empty": "Contenido no puede estar vacío",
"max_mailbox_exceeded": "Máx. de buzones superado (%d de %d)",
"max_quota_in_use": "Cuota del buzón debe ser mayor o igual a %d MiB",
"mysql_error": "MySQL error: %s",
"mysql_error": "Error de MySQL: %s",
"network_host_invalid": "Red o host inválido: %s",
"next_hop_interferes": "%s interfiere con el siguiente destino %s",
"next_hop_interferes_any": "Siguiente destino existente interfiere con %s",
@@ -462,7 +478,7 @@
"private_key_error": "Error en la llave privada: %s",
"quota_not_0_not_numeric": "Cuota debe ser numérica y >= 0",
"recipient_map_entry_exists": "Una regla de destinatario \"%s\" existe",
"redis_error": "Redis error: %s",
"redis_error": "Error de Redis: %s",
"relayhost_invalid": "relayhost %s es invalido",
"release_send_failed": "El mensaje no pudo ser liberado: %s",
"rl_timeframe": "Marco de tiempo del límite de velocidad esta incorrecto",
@@ -471,14 +487,14 @@
"sender_acl_invalid": "Valor %s del ACL del remitente es inválido",
"set_acl_failed": "Error al establecer el ACL",
"settings_map_invalid": "Ajustes de ID %s invalidos",
"sieve_error": "Sieve parser error: %s",
"sieve_error": "Error del analizador de Sieve: %s",
"spam_learn_error": "Error aprendiendo muestra: %s",
"subject_empty": "Asunto no puede estar vacío",
"target_domain_invalid": "El dominio \"goto\" es inválido",
"targetd_not_found": "Dominio destino no encontrado",
"text_empty": "Texto no puede estar vacío",
"tls_policy_map_entry_exists": "Regla de póliza de TLS \"%s\" existe",
"tls_policy_map_parameter_invalid": "El parámetro de póliza no es válido.",
"tls_policy_map_entry_exists": "La regla de política de TLS \"%s\" ya existe",
"tls_policy_map_parameter_invalid": "El parámetro de la política no es válido.",
"totp_verification_failed": "Verificación TOTP fallida",
"transport_dest_exists": "Destino de la regla de transporte ya existe",
"webauthn_verification_failed": "Verificación WebAuthn fallida: %s",
@@ -541,7 +557,12 @@
"tfa_token_invalid": "Token de autenticación de doble factor no válido",
"to_invalid": "El destinatario no puede quedar en blanco",
"webauthn_authenticator_failed": "No se ha localizado el autenticador seleccionado",
"webauthn_username_failed": "El autenticador seleccionado pertenece a otra cuenta"
"webauthn_username_failed": "El autenticador seleccionado pertenece a otra cuenta",
"tfa_removal_blocked": "No se puede quitar la autenticación de dos factores porque es obligatoria para su cuenta.",
"external_goto_denied": "La dirección de destino externa %s no está permitida",
"max_age_invalid": "La antigüedad máxima %s no es válida",
"quarantine_category_invalid": "La categoría de cuarentena debe ser una de: add_header, reject, all",
"ui_theme_invalid": "Tema no encontrado"
},
"debug": {
"containers_info": "Información de los contenedores",
@@ -551,11 +572,11 @@
"log_info": "<p>Los <b>logs en memoria</b> son recopilados en listas de Redis y recortados a LOG_LINES (%d) cada minuto para prevenir sobrecarga en el sistema.\r\n <br>Los logs en memoria no están destinados a ser persistentes. Todas las aplicaciones que logean a la memoria, también logean en el daemon de Docker y, por lo tanto, en el controlador de registro predeterminado.\r\n El log en memoria se debe utilizar para analizar problemas menores con los contenedores.</p>\r\n <p>Los <b>logs externos</b> se recopilan a través de la API de la aplicación dada.</p>\r\n <p>Los <b>logs estáticos</b> son principalmente registros de actividad, que no están registrados en Dockerd pero que aún deben ser persistentes (excepto los registros de API).</p>",
"logs": "Logs",
"restart_container": "Reiniciar",
"docs": "Docs",
"docs": "Documentación",
"last_modified": "Última modificación",
"size": "Tamaño",
"started_at": "Iniciado el",
"uptime": "Uptime",
"uptime": "Tiempo en funcionamiento",
"static_logs": "Logs estáticos",
"system_containers": "Sistema y Contenedores",
"show_ip": "Mostrar IP pública",
@@ -617,8 +638,8 @@
"full_name": "Nombre completo",
"gal": "Lista global de direcciones (GAL)",
"gal_info": "El GAL contiene todos los objetos de un dominio y no puede ser editado por ningún usuario. Falta información de disponibilidad en SOGo, si está desactivada. <b>Reinicia SOGo para aplicar los cambios.</b>",
"grant_types": "Grant types",
"hostname": "Hostname",
"grant_types": "Tipos de concesión",
"hostname": "Nombre de host",
"inactive": "Inactivo",
"kind": "Tipo",
"last_modified": "Última modificación",
@@ -636,9 +657,9 @@
"password_repeat": "Confirmación de contraseña (repetir):",
"previous": "Página anterior",
"quota_mb": "Cuota (MiB)",
"redirect_uri": "Redirect/Callback URL",
"redirect_uri": "URL de redirección/callback",
"relay_all": "Retransmitir todos los destinatarios",
"relay_all_info": "<small>Si eliges <b>no</b> retransmitir a todos los destinatarios, necesitas agregar un buzón \"ciego\" por cada destinatario que debe ser retransmitido.</small>",
"relay_all_info": "<small>Si elige <b>no</b> retransmitir a todos los destinatarios, deberá agregar un buzón \"ciego\" por cada destinatario que deba ser retransmitido.</small>",
"relay_domain": "Dominio de retransmisión",
"relayhost": "Ruta saliente",
"remove": "Eliminar",
@@ -715,12 +736,36 @@
"spam_alias": "Crear o modificar alias temporales (con caducidad)",
"spam_filter": "Filtro de spam",
"spam_policy": "Añadir o eliminar elementos de la lista de bloqueo/desbloqueo",
"spam_score": "Establecer una puntuación de spam personalizada"
"spam_score": "Establecer una puntuación de spam personalizada",
"domain_footer_info_vars": {
"auth_user": "{= auth_user =} - Nombre de usuario autenticado indicado por un MTA",
"from_user": "{= from_user =} - Parte de usuario del remitente del sobre, p. ej. para \"moo@mailcow.tld\" devuelve \"moo\"",
"from_name": "{= from_name =} - Nombre del remitente del sobre, p. ej. para \"Mailcow &lt;moo@mailcow.tld&gt;\" devuelve \"Mailcow\"",
"from_addr": "{= from_addr =} - Dirección del remitente del sobre",
"from_domain": "{= from_domain =} - Dominio del remitente del sobre",
"custom": "{= foo =} - Si el buzón tiene el atributo personalizado \"foo\" con el valor \"bar\", devuelve \"bar\""
},
"domain_footer_plain": "Pie de página en texto plano",
"internal_info": "Los alias internos solo son accesibles desde el propio dominio o sus dominios alias.",
"sender_allowed": "Permitir enviar como este alias",
"sender_allowed_info": "Si se desactiva, este alias solo puede recibir correo. Use la ACL de remitentes para anularlo y dar permiso de envío a buzones concretos.",
"mta_sts": "MTA-STS",
"mta_sts_info": "<a href='https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol#SMTP_MTA_Strict_Transport_Security' target='_blank'>MTA-STS</a> es un estándar que obliga a que la entrega de correo entre servidores use TLS con certificados válidos. <br>Se usa cuando <a target='_blank' href='https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities'>DANE</a> no es posible por falta de DNSSEC o porque no es compatible.<br><b>Nota</b>: si el dominio receptor admite DANE con DNSSEC, DANE <b>siempre</b> tiene preferencia; MTA-STS solo actúa como alternativa.",
"mta_sts_version_info": "Define la versión del estándar MTA-STS; actualmente solo es válida <code>STSv1</code>.",
"mta_sts_mode_info": "Hay tres modos para elegir:<ul><li><em>testing</em>: la política solo se supervisa, las infracciones no tienen efecto.</li><li><em>enforce</em>: la política se aplica estrictamente y se rechazan las conexiones sin TLS válido.</li><li><em>none</em>: la política se publica pero no se aplica.</li></ul>",
"mta_sts_max_age": "Antigüedad máxima",
"mta_sts_max_age_info": "Tiempo en segundos durante el cual los servidores receptores pueden guardar esta política en caché antes de volver a obtenerla.",
"mta_sts_mx_info": "Permite enviar solo a los nombres de servidor de correo indicados explícitamente; el MTA emisor comprueba que el nombre del registro MX coincida con la lista de la política y solo entrega con un certificado TLS válido (protege contra ataques MITM).",
"mta_sts_mx_notice": "Se pueden indicar varios servidores MX (separados por comas).",
"mta_sts_active_info": "Si se desmarca, la política MTA-STS no se publicará y no se solicitará ningún certificado para el subdominio mta-sts mediante ACME.",
"pushover_only_x_prio": "Considerar solo el correo de alta prioridad [<code>X-Priority: 1</code>]",
"pushover_sender_regex": "Considerar la siguiente expresión regular de remitente",
"pushover_vars": "Si no se define ningún filtro de remitente, se considerarán todos los correos.<br>Los filtros por expresión regular y las comprobaciones exactas de remitente pueden definirse por separado y se evalúan en orden. No dependen entre sí.<br>Variables utilizables para el texto y el título (tenga en cuenta las normas de protección de datos)"
},
"footer": {
"hibp_nok": "¡Se encontró coincidencia - esta es una contraseña <b>no segura</b>, selecciona otra!",
"hibp_nok": "¡Se encontró una coincidencia: esta es una contraseña <b>no segura</b>, elija otra!",
"hibp_ok": "No se encontraron coincidencias",
"loading": "Espera por favor...",
"loading": "Espere, por favor...",
"restart_now": "Reiniciar ahora",
"restart_container": "Reiniciar contenedor",
"restart_container_info": "<b>Importante:</b> Un reinicio limpio puede llevar un tiempo. Por favor, espere a que finalice.",
@@ -735,7 +780,7 @@
"header": {
"administration": "Administración",
"debug": "Información",
"email": "E-Mail",
"email": "Correo electrónico",
"mailcow_config": "Configuración",
"quarantine": "Cuarentena",
"restart_sogo": "Reiniciar SOGo",
@@ -781,12 +826,12 @@
"add_bcc_entry": "Añadir regla de BCC",
"add_domain": "Agregar dominio",
"add_domain_alias": "Agregar alias de dominio",
"add_domain_record_first": "Agrega un dominio primero",
"add_domain_record_first": "Agregue primero un dominio",
"add_filter": "Añadir filtro",
"add_mailbox": "Agregar buzón",
"add_recipient_map_entry": "Añadir regla de destinatario",
"add_resource": "Añadir recurso",
"add_tls_policy_map": "Añadir regla de póliza de TLS",
"add_tls_policy_map": "Agregar regla de política de TLS",
"address_rewriting": "Reescritura de direcciones",
"alias": "Alias",
"aliases": "Alias",
@@ -864,8 +909,8 @@
"running": "En marcha",
"set_postfilter": "Marcar como posfiltro",
"set_prefilter": "Marcar como prefiltro",
"sieve_info": "Puedes almacenar múltiples filtros por usuario, pero solo un prefiltro y un postfiltro pueden estar activos al mismo tiempo.<br>\r\nCada filtro será procesado en el orden descrito. Ni un script fallido ni un \"keep;\" emitido detendrá el procesamiento de otros scripts.<br>\r\n<a href=\"https://github.com/mailcow/mailcow-dockerized/blob/master/data/conf/dovecot/global_sieve_before\" target=\"_blank\">Prefiltro sieve global</a> → Prefiltro → Scripts del usuario → Posfiltro → <a href=\"https://github.com/mailcow/mailcow-dockerized/blob/master/data/conf/dovecot/global_sieve_after\" target=\"_blank\">Posfiltro sieve global</a>",
"status": "Status",
"sieve_info": "Puede almacenar varios filtros por usuario, pero solo un prefiltro y un postfiltro pueden estar activos al mismo tiempo.<br>\r\nCada filtro será procesado en el orden descrito. Ni un script fallido ni un \"keep;\" emitido detendrá el procesamiento de otros scripts.<br>\r\n<a href=\"https://github.com/mailcow/mailcow-dockerized/blob/master/data/conf/dovecot/global_sieve_before\" target=\"_blank\">Prefiltro sieve global</a> → Prefiltro → Scripts del usuario → Posfiltro → <a href=\"https://github.com/mailcow/mailcow-dockerized/blob/master/data/conf/dovecot/global_sieve_after\" target=\"_blank\">Posfiltro sieve global</a>",
"status": "Estado",
"sync_jobs": "Trabajos de sincronización",
"target_address": "Dirección destino",
"target_domain": "Dominio destino",
@@ -873,12 +918,12 @@
"tls_enforce_out": "Forzar TLS saliente",
"tls_map_dest": "Destino",
"tls_map_dest_info": "Ejemplos: example.org, .example.org, [mail.example.org]:25",
"tls_map_parameters": "Parametros",
"tls_map_parameters_info": "Vacío o parametros, por ejemplo: protocols=!SSLv2 ciphers=medium exclude=3DES",
"tls_map_policy": "Póliza",
"tls_map_parameters": "Parámetros",
"tls_map_parameters_info": "Vacío o parámetros, por ejemplo: protocols=!SSLv2 ciphers=medium exclude=3DES",
"tls_map_policy": "Política",
"tls_policy_maps": "Políticas de TLS",
"tls_policy_maps_info": "Esta póliza anula las reglas de transporte TLS salientes independientemente de la configuración de la política TLS de los usuarios.<br>\r\n Revisa <a href=\"http://www.postfix.org/postconf.5.html#smtp_tls_policy_maps\" target=\"_blank\">the \"smtp_tls_policy_maps\" docs</a> para mas información.",
"tls_policy_maps_long": "Forzar póliza de TLS saliente",
"tls_policy_maps_info": "Este mapa de políticas anula las reglas de transporte TLS salientes, independientemente de la configuración de la política TLS de los usuarios.<br>\r\n Consulte <a href=\"http://www.postfix.org/postconf.5.html#smtp_tls_policy_maps\" target=\"_blank\">la documentación de \"smtp_tls_policy_maps\"</a> para más información.",
"tls_policy_maps_long": "Forzar política de TLS saliente",
"toggle_all": "Selecionar todo",
"username": "Nombre de usuario",
"waiting": "Esperando",
@@ -942,7 +987,12 @@
"table_size_show_n": "Mostrar %s elementos",
"templates": "Plantillas",
"template": "Plantilla",
"yes": "&#10003;"
"yes": "&#10003;",
"add_alias_expand": "Extender el alias a los dominios alias",
"alias_domain_backupmx": "Dominio alias inactivo para el dominio de retransmisión",
"catch_all": "Recoge-todo (catch-all)",
"force_tfa": "2FA",
"last_run_reset": "Programar la siguiente"
},
"oauth2": {
"access_denied": "Inicie sesión como propietario del buzón para otorgar acceso a través de OAuth2.",
@@ -967,8 +1017,8 @@
"medium_danger": "Peligro medio",
"neutral_danger": "Neutral",
"qhandler_success": "Solicitud enviada con éxito al sistema, puede cerrar la ventana",
"qid": "Rspamd QID",
"qitem": "Quarantine item",
"qid": "QID de Rspamd",
"qitem": "Elemento en cuarentena",
"quarantine": "Cuarentena",
"quick_actions": "Acciones",
"rcpt": "Destinatario",
@@ -990,14 +1040,29 @@
"download_eml": "Descargar (.eml)",
"info": "Información",
"junk_folder": "Carpeta de correo no deseado",
"notified": "Notificado"
"notified": "Notificado",
"qinfo": "El sistema de cuarentena guarda en la base de datos el correo rechazado (al remitente <em>no</em> se le dará la impresión de que el correo fue entregado) y también el correo que se entrega como copia en la carpeta de correo no deseado de un buzón.\r\n <br>\"Aprender como spam y eliminar\" aprende un mensaje como spam mediante el teorema de Bayes y calcula hashes difusos para rechazar mensajes similares en el futuro.\r\n <br>Tenga en cuenta que aprender varios mensajes puede llevar tiempo, según su sistema.<br>Los elementos de la lista de bloqueo se excluyen de la cuarentena.",
"quick_delete_link": "Abrir enlace de eliminación rápida",
"quick_release_link": "Abrir enlace de liberación rápida",
"rewrite_subject": "Reescribir asunto",
"sender_header": "Remitente (encabezado \"From\")",
"settings_info": "Cantidad máxima de elementos en cuarentena (por buzón): %s<br>Tamaño máximo de correo: %s MiB"
},
"queue": {
"queue_manager": "Administrador de cola"
"queue_manager": "Administrador de cola",
"flush": "Vaciar la cola",
"info": "La cola de correo contiene todos los correos que esperan ser entregados. Si un correo permanece mucho tiempo en la cola, el sistema lo elimina automáticamente.<br>El mensaje de error de cada correo indica por qué no se pudo entregar.",
"legend": "Acciones de la cola de correo:",
"deliver_mail_legend": "Intenta volver a entregar los correos seleccionados.",
"hold_mail_legend": "Retiene los correos seleccionados. (Impide nuevos intentos de entrega)",
"show_message": "Mostrar mensaje",
"unban": "desbloquear cola",
"unhold_mail": "Liberar",
"unhold_mail_legend": "Libera los correos seleccionados para su entrega. (Requiere que hayan sido retenidos antes)"
},
"start": {
"help": "Mostrar/Ocultar panel de ayuda",
"imap_smtp_server_auth_info": "Por favor utiliza tu dirección de correo completa y el mecanismo de autenticación PLAIN.<br>\r\nTus datos para iniciar sesión serán cifrados por el cifrado obligatorio del servidor"
"imap_smtp_server_auth_info": "Utilice su dirección de correo completa y el mecanismo de autenticación PLAIN.<br>\r\nSus datos de inicio de sesión se cifran gracias al cifrado obligatorio del servidor"
},
"success": {
"acl_saved": "ACL para el objeto %s guardado",
@@ -1024,8 +1089,8 @@
"domain_admin_removed": "Administrador del dominio %s removido",
"domain_modified": "Cambios al dominio %s guardados",
"domain_removed": "Dominio %s removido",
"forwarding_host_added": "Forwarding host %s has been added",
"forwarding_host_removed": "Forwarding host %s has been removed",
"forwarding_host_added": "Se agregó el host de reenvío %s",
"forwarding_host_removed": "Se eliminó el host de reenvío %s",
"logged_in_as": "Conectado como %s",
"mailbox_added": "Buzón %s agregado",
"mailbox_modified": "Cambios al buzón %s guardados",
@@ -1043,31 +1108,86 @@
"saved_settings": "Ajustes guardados",
"settings_map_added": "Ajustes agregados",
"settings_map_removed": "Ajustes del ID %s removidos",
"tls_policy_map_entry_deleted": "Regla de póliza de TLS con ID %s ha sido elimindada",
"tls_policy_map_entry_saved": "Regla de póliza de TLS \"%s\" ha sido guardada",
"tls_policy_map_entry_deleted": "Se eliminó la regla de política de TLS con ID %s",
"tls_policy_map_entry_saved": "Se guardó la regla de política de TLS \"%s\"",
"verified_totp_login": "Inicio de sesión TOTP verificado",
"verified_webauthn_login": "Inicio de sesión WebAuthn verificado",
"verified_yotp_login": "Inicio de sesión Yubico OTP verificado"
"verified_yotp_login": "Inicio de sesión Yubico OTP verificado",
"admin_added": "Se agregó el administrador %s",
"admin_api_modified": "Se guardaron los cambios en la API",
"admin_removed": "Se eliminó el administrador %s",
"app_links": "Se guardaron los cambios en los enlaces de aplicaciones",
"app_passwd_added": "Se agregó una nueva contraseña de aplicación",
"app_passwd_removed": "Se eliminó la contraseña de aplicación con ID %s",
"cors_headers_edited": "Se guardó la configuración de CORS",
"custom_login_modified": "La personalización del inicio de sesión se guardó correctamente",
"domain_add_dkim_available": "Ya existía una clave DKIM",
"dkim_duplicated": "La clave DKIM del dominio %s se copió a %s",
"domain_footer_modified": "Se guardaron los cambios en el pie de página del dominio %s",
"dovecot_restart_success": "Dovecot se reinició correctamente",
"eas_reset": "Se restablecieron los dispositivos ActiveSync del usuario %s",
"f2b_banlist_refreshed": "El ID de la lista de bloqueo se actualizó correctamente.",
"f2b_modified": "Se guardaron los cambios en los parámetros de Fail2ban",
"global_filter_written": "El filtro se escribió correctamente en el archivo",
"hash_deleted": "Hash eliminado",
"iam_test_connection": "Conexión correcta",
"ip_check_opt_in_modified": "La comprobación de IP se guardó correctamente",
"item_deleted": "Elemento %s eliminado correctamente",
"item_released": "Elemento %s liberado",
"items_deleted": "Elemento %s eliminado correctamente",
"items_released": "Se liberaron los elementos seleccionados",
"learned_ham": "El ID %s se aprendió correctamente como correo legítimo (ham)",
"license_modified": "Se guardaron los cambios en la licencia",
"mailbox_renamed": "El buzón se renombró de %s a %s",
"nginx_reloaded": "Nginx se recargó",
"object_modified": "Se guardaron los cambios en el objeto %s",
"password_policy_saved": "La política de contraseñas se guardó correctamente",
"password_changed_success": "La contraseña se cambió correctamente",
"pushover_settings_edited": "La configuración de Pushover se guardó; verifique las credenciales.",
"recovery_email_sent": "Se envió el correo de recuperación a %s",
"reset_main_logo": "Se restableció el logo predeterminado",
"resource_removed": "Se eliminó el recurso %s",
"sogo_profile_reset": "Se restableció el perfil de SOGo del usuario %s",
"template_added": "Se agregó la plantilla %s",
"template_modified": "Se guardaron los cambios en la plantilla %s",
"template_removed": "Se eliminó la plantilla con ID %s",
"ui_texts": "Se guardaron los cambios en los textos de la interfaz",
"ui_theme_modified": "El tema se guardó correctamente",
"upload_success": "Archivo subido correctamente",
"verified_fido2_login": "Inicio de sesión FIDO2 verificado"
},
"tfa": {
"api_register": "%s utiliza la API de la nube de Yubico. Por favor, obtén una clave API para tu llave <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">aquí</a>.",
"api_register": "%s utiliza la API en la nube de Yubico. Obtenga una clave de API para su llave <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">aquí</a>.",
"confirm": "Confirmar",
"confirm_totp_token": "Por favor confirma los cambios ingresando el token generado",
"confirm_totp_token": "Confirme los cambios ingresando el token generado",
"delete_tfa": "Desactivar TFA",
"disable_tfa": "Desactivar TFA hasta el próximo inicio de sesión exitoso",
"enter_qr_code": "Su código TOTP si su dispositivo no puede escanear códigos QR",
"key_id": "Un identificador para tu YubiKey",
"key_id_totp": "Un identificador para tu llave",
"key_id": "Un identificador para su YubiKey",
"key_id_totp": "Un identificador para su llave",
"none": "Desactivar",
"scan_qr_code": "Escanee el siguiente código con su aplicación de autenticador o ingrese el código manualmente.",
"select": "Selecciona",
"select": "Seleccione",
"set_tfa": "Establecer el método de autenticación de dos factores",
"tfa": "Autenticación de dos factores",
"totp": "OTP basado en tiempo (Google Authenticator, Authy, etc.)",
"webauthn": "Autenticación WebAuthn",
"waiting_usb_auth": "<i>Esperando al dispositivo USB...</i><br><br>Toque el botón en su dispositivo USB WebAuthn ahora.",
"waiting_usb_register": "<i>Esperando al dispositivo USB....</i><br><br>Ingrese su contraseña arriba y confirme su registro WebAuthn tocando el botón en su dispositivo USB WebAuthn.",
"yubi_otp": "Yubico OTP"
"yubi_otp": "Yubico OTP",
"authenticators": "Autenticadores",
"force_tfa": "Exigir configurar 2FA al iniciar sesión",
"force_tfa_info": "El usuario deberá configurar la autenticación de dos factores antes de acceder al panel.",
"setup_title": "Autenticación de dos factores obligatoria",
"setup_required": "Su cuenta requiere autenticación de dos factores. Configure un método de 2FA para continuar.",
"cancel_setup": "Cancelar y cerrar sesión",
"error_code": "Código de error",
"init_webauthn": "Inicializando, espere...",
"reload_retry": "- (recargue el navegador si el error persiste)",
"start_webauthn_validation": "Iniciar validación",
"tfa_token_invalid": "Token de 2FA no válido",
"u2f_deprecated": "Parece que su llave se registró con el método obsoleto U2F. Desactivaremos la autenticación de dos factores y eliminaremos su llave.",
"u2f_deprecated_important": "Registre su llave en el panel de administración con el nuevo método WebAuthn."
},
"user": {
"action": "Acción",
@@ -1075,7 +1195,7 @@
"alias": "Alias",
"alias_create_random": "Generar alias aleatorio",
"alias_extend_all": "Extender alias por 1 hora",
"alias_full_date": "d.m.Y, H:i:s T",
"alias_full_date": "d/m/Y, H:i:s T",
"alias_remove_all": "Eliminar todos los alias",
"alias_select_validity": "Periodo de validez",
"alias_time_left": "Tiempo restante",
@@ -1126,7 +1246,7 @@
"spamfilter_bl": "Lista negra",
"spamfilter_bl_desc": "Direcciones en la lista negra <b>siempre</b> clasificarán como spam. Se puede usar comodines. Filtro solo aplica a los alias directos (alias con un solo buzón de destino) excluyendo los alias catch-all.",
"spamfilter_default_score": "Valores predeterminados:",
"spamfilter_green": "Verde: éste mensaje no es spam",
"spamfilter_green": "Verde: este mensaje no es spam",
"spamfilter_hint": "El primer valor representa la \"calificación baja de spam\", el segundo representa la \"calificación alta de spam\".",
"spamfilter_red": "Rojo: Este mensaje es spam, será rechazado por el servidor y enviado a la quarantena (si esta configurada)",
"spamfilter_table_action": "Acción",
@@ -1136,8 +1256,8 @@
"spamfilter_table_rule": "Regla",
"spamfilter_wl": "Lista blanca",
"spamfilter_wl_desc": "Direcciones en la lista blanca <b>nunca</b> clasificarán como spam. Se puede usar comodines. Filtro solo aplica a los alias directos (alias con un solo buzón de destino) excluyendo los alias catch-all.",
"spamfilter_yellow": "Amarillo: éste mensaje puede ser spam, será etiquetado como spam y trasladado a tu carpeta de correo no deseado",
"status": "Status",
"spamfilter_yellow": "Amarillo: este mensaje puede ser spam, será etiquetado como spam y movido a su carpeta de correo no deseado",
"status": "Estado",
"sync_jobs": "Trabajos de sincronización",
"tag_handling": "Establecer manejo para el correo etiquetado",
"tag_help_example": "Ejemplo de una dirección email etiquetada: mi<b>+Facebook</b>@ejemplo.org",
@@ -1157,13 +1277,89 @@
"weeks": "Semanas",
"with_app_password": "con contraseña de aplicación",
"year": "año",
"years": "años"
"years": "años",
"active_sieve": "Filtro activo",
"advanced_settings": "Configuración avanzada",
"app_hint": "Las contraseñas de aplicación son contraseñas alternativas para iniciar sesión en IMAP, SMTP, CalDAV, CardDAV y EAS. El nombre de usuario no cambia. El webmail SOGo no está disponible con contraseñas de aplicación.",
"allowed_protocols": "Protocolos permitidos",
"app_name": "Nombre de la aplicación",
"app_passwds": "Contraseñas de aplicación",
"apple_connection_profile": "Perfil de conexión de Apple",
"apple_connection_profile_complete": "Este perfil de conexión incluye los parámetros de IMAP y SMTP, y las rutas de CalDAV (calendarios) y CardDAV (contactos) para un dispositivo Apple.",
"apple_connection_profile_mailonly": "Este perfil de conexión incluye los parámetros de configuración de IMAP y SMTP para un dispositivo Apple.",
"apple_connection_profile_with_app_password": "Se genera una nueva contraseña de aplicación y se agrega al perfil, para que no haga falta escribir ninguna contraseña al configurar el dispositivo. No comparta el archivo: da acceso completo a su buzón.",
"attribute": "Atributo",
"authentication": "Autenticación",
"change_password_hint_app_passwords": "Su cuenta tiene %d contraseñas de aplicación que no se cambiarán. Para administrarlas, vaya a la pestaña Contraseñas de aplicación.",
"clear_recent_successful_connections": "Borrar las conexiones correctas registradas",
"client_configuration": "Mostrar guías de configuración para clientes de correo y teléfonos",
"create_app_passwd": "Crear contraseña de aplicación",
"delete_ays": "Confirme la eliminación.",
"direct_protocol_access": "Este usuario de buzón tiene <b>acceso directo y externo</b> a los siguientes protocolos y aplicaciones. Su administrador controla este ajuste. Puede crear contraseñas de aplicación para dar acceso a protocolos y aplicaciones concretos.<br>El botón \"Webmail\" ofrece inicio de sesión único en SOGo y siempre está disponible.",
"email": "Correo electrónico",
"email_and_dav": "Correo, calendarios y contactos",
"empty": "Sin resultados",
"fido2_webauthn": "FIDO2/WebAuthn",
"force_pw_update": "<b>Debe</b> establecer una nueva contraseña para acceder a los servicios de groupware.",
"from": "de",
"generate": "generar",
"in_use": "En uso",
"last_mail_login": "Último inicio de sesión de correo",
"last_pw_change": "Último cambio de contraseña",
"last_ui_login": "Último inicio de sesión en la interfaz",
"loading": "Cargando...",
"login_history": "Historial de inicios de sesión",
"mailbox": "Buzón",
"mailbox_general": "General",
"mailbox_settings": "Configuración",
"messages": "mensajes",
"month": "mes",
"months": "meses",
"no_active_filter": "No hay ningún filtro activo",
"no_last_login": "No hay información del último inicio de sesión en la interfaz",
"open_logs": "Abrir registros",
"open_webmail_sso": "Webmail",
"overview": "Resumen",
"password": "Contraseña",
"pw_update_required": "Su cuenta requiere un cambio de contraseña. Establezca una nueva contraseña para continuar.",
"password_repeat": "Contraseña (repetir)",
"password_reset_info": "Si no se indica un correo de recuperación de contraseña, esta función no se puede usar.",
"protocols": "Protocolos",
"pushover_evaluate_x_prio": "Escalar el correo de alta prioridad [<code>X-Priority: 1</code>]",
"pushover_info": "La configuración de notificaciones push se aplicará a todo el correo limpio (no spam) entregado a <b>%s</b>, incluidos los alias (compartidos, no compartidos, etiquetados).",
"pushover_only_x_prio": "Considerar solo el correo de alta prioridad [<code>X-Priority: 1</code>]",
"pushover_sender_array": "Considerar las siguientes direcciones de remitente <small>(separadas por comas)</small>",
"pushover_sender_regex": "Filtrar remitentes con la siguiente expresión regular",
"pushover_text": "Texto de la notificación",
"pushover_title": "Título de la notificación",
"pushover_sound": "Sonido",
"pushover_vars": "Si no se define ningún filtro de remitente, se considerarán todos los correos.<br>Los filtros por expresión regular y las comprobaciones exactas de remitente pueden definirse por separado y se evalúan en orden. No dependen entre sí.<br>Variables utilizables para el texto y el título (tenga en cuenta las normas de protección de datos)",
"pushover_verify": "Verificar credenciales",
"pw_recovery_email": "Correo de recuperación de contraseña",
"q_add_header": "Carpeta de correo no deseado",
"quarantine_category_info": "La categoría de notificación \"Rechazado\" incluye el correo que fue rechazado, mientras que \"Carpeta de correo no deseado\" avisa al usuario de los correos que se movieron a esa carpeta.",
"recent_successful_connections": "Conexiones correctas registradas",
"sender_acl_disabled": "<span class=\"badge fs-6 bg-danger\">La comprobación de remitente está desactivada</span>",
"show_sieve_filters": "Mostrar el filtro Sieve activo del usuario",
"sogo_profile_reset_help": "Esto destruirá el perfil de SOGo del usuario y <b>eliminará de forma irrecuperable todos sus contactos y datos de calendario</b>.",
"spam_score_reset": "Restablecer al valor predeterminado del servidor",
"spamfilter_table_domain_policy": "n/d (política del dominio)",
"syncjob_check_log": "Revisar registro",
"syncjob_EX_OK": "Correcto",
"syncjob_EXIT_OVERQUOTA": "El buzón de destino superó su cuota",
"syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Usuario o contraseña incorrectos",
"tfa_info": "La autenticación de dos factores ayuda a proteger su cuenta. Si la activa, necesitará contraseñas de aplicación para iniciar sesión en aplicaciones o servicios que no admiten la autenticación de dos factores (p. ej., clientes de correo)."
},
"warning": {
"domain_added_sogo_failed": "Se agregó el dominio pero no se pudo reiniciar SOGo, revisa los logs del servidor.",
"domain_added_sogo_failed": "Se agregó el dominio, pero no se pudo reiniciar SOGo; revise los registros del servidor.",
"fuzzy_learn_error": "Error aprendiendo hash: %s",
"ip_invalid": "IP inválida omitida: %s",
"cannot_delete_self": "No se puede eliminar el usuario conectado"
"cannot_delete_self": "No se puede eliminar el usuario conectado",
"dovecot_restart_failed": "No se pudo reiniciar Dovecot, revise los registros",
"is_not_primary_alias": "Se omitió el alias no principal %s",
"quota_exceeded_scope": "Se superó la cuota del dominio: en este dominio solo se pueden crear buzones ilimitados.",
"session_token": "Token de formulario no válido: los tokens no coinciden",
"session_ua": "Token de formulario no válido: error de validación del User-Agent"
},
"datatables": {
"collapse_all": "Contraer todo",
@@ -1204,5 +1400,12 @@
"register_status": "Estado de registro",
"rename": "Renombrar",
"start_fido2_validation": "Iniciar validación FIDO2"
},
"ratelimit": {
"disabled": "Desactivado",
"second": "mensajes / segundo",
"minute": "mensajes / minuto",
"hour": "mensajes / hora",
"day": "mensajes / día"
}
}
+5 -2
View File
@@ -109,7 +109,10 @@
"bcc_dest_format": "Il destinatario in copia nascosta deve essere un singolo indirizzo email.<br>Se si vuole spedire una copia del messaggio a più destinatari, bisogna creare un alias ed utilizzarlo per questa opzione.",
"app_passwd_protocols": "Protocolli consentiti per la password dell'app",
"tags": "Tag",
"dry": "Simula sincronizzazione"
"dry": "Simula sincronizzazione",
"internal": "Interno",
"internal_info": "Gli alias interni sono accessibili solo dal proprio dominio o dai domini alias.",
"sender_allowed": "Consenti l'invio con questo alias"
},
"admin": {
"access": "Accedi",
@@ -131,7 +134,7 @@
"additional_rows": " righe aggiuntive inserite",
"admin": "Amministratore",
"admin_details": "Modifica impostazioni amministratore",
"admin_domains": "Assengazioni di dominio",
"admin_domains": "Assegnazioni di dominio",
"admins": "Amministratori",
"admins_ldap": "Amministratori LDAP",
"advanced_settings": "Impostazioni avanzate",
+15 -5
View File
@@ -29,7 +29,8 @@
"extend_sender_acl": "Dovoli razširitev pošiljateljevega ACL z zunanjimi e-poštnimi naslovi",
"quarantine_category": "Spremeni kategorijo obvestil o karanteni",
"syncjobs": "Sinhronizacijska opravila",
"pw_reset": "Dovoli ponastavitev uporabniškega gesla mailcow"
"pw_reset": "Dovoli ponastavitev uporabniškega gesla mailcow",
"alias_external_goto": "Dovoli vzdevke z zunanjimi domenami goto"
},
"add": {
"active": "Aktivno",
@@ -412,7 +413,12 @@
"task": "Naloga",
"user_link": "Uporabniška povezava",
"iam_realm": "Realm",
"needs_restart": "potreben je ponovni zagon"
"needs_restart": "potreben je ponovni zagon",
"ui_theme": "Tema",
"ui_theme_dark": "Vključuje različico s temnim načinom",
"ui_theme_default": "Privzeto",
"ui_theme_info": "Teme so mape v <code>data/web/css/themes/&lt;name&gt;/</code>, ki vsebujejo naslednje datoteke:<ul class=\"mb-0\"><li><code>theme.css</code> (obvezna)</li><li><code>dark.css</code> (neobvezna, temni način)</li><li><code>bootstrap.css</code> (neobvezna, nadomesti osnovno temo Bootstrap)</li><li><code>theme.json</code> (neobvezna, <code>name</code> in <code>description</code>)</li></ul>",
"ui_theme_none": "Tem ni bilo mogoče najti."
},
"danger": {
"alias_goto_identical": "Vzdevek in ciljni naslov se ne smeta ujemati",
@@ -559,7 +565,9 @@
"mx_invalid": "Zapis MX %s je neveljaven",
"version_invalid": "Različica %s je neveljavna",
"tfa_removal_blocked": "Dvofaktorske avtentikacije ni mogoče odstraniti, ker je obvezna za vaš račun.",
"quarantine_category_invalid": "Kategorija karantene mora biti ena od: add_header, reject, all"
"quarantine_category_invalid": "Kategorija karantene mora biti ena od: add_header, reject, all",
"external_goto_denied": "Zunanji naslov goto %s ni dovoljen",
"ui_theme_invalid": "Tema ni najdena"
},
"debug": {
"containers_info": "Informacije o zabojniku",
@@ -785,7 +793,8 @@
"internal": "Notranje",
"internal_info": "Notranji vzdevki so dostopni samo iz lastne domene ali vzdevkov domen.",
"sender_allowed": "Dovoli pošiljanje kot ta vzdevek",
"sender_allowed_info": "Če je onemogočeno, lahko ta vzdevek samo prejema pošto. Za preglasitev in dodelitev dovoljenja za pošiljanje določenim poštnim predalom uporabite seznam za nadzor dostopa pošiljatelja."
"sender_allowed_info": "Če je onemogočeno, lahko ta vzdevek samo prejema pošto. Za preglasitev in dodelitev dovoljenja za pošiljanje določenim poštnim predalom uporabite seznam za nadzor dostopa pošiljatelja.",
"mta_sts_active_info": "Če ni označeno, pravilnik MTA-STS ne bo objavljen in za poddomeno mta-sts ne bo zahtevano nobeno potrdilo prek ACME."
},
"footer": {
"restart_container_info": "<b>Pomembno:</b> Eleganten ponovni zagon lahko traja nekaj časa, zato počakajte, da se konča.",
@@ -1203,7 +1212,8 @@
"cors_headers_edited": "Nastavitve CORS so bile shranjene",
"item_released": "Izdan je bil element %s",
"saved_settings": "Shranjene nastavitve",
"learned_ham": "Uspešno naučen ID %s kot zaželjen"
"learned_ham": "Uspešno naučen ID %s kot zaželjen",
"ui_theme_modified": "Tema je bila uspešno shranjena"
},
"start": {
"imap_smtp_server_auth_info": "Prosimo, uporabite svoj celoten e-poštni naslov in mehanizem za preverjanje pristnosti PLAIN.<br>\nVaši podatki za prijavo bodo šifrirani z obveznim šifriranjem na strani strežnika.",
+22 -7
View File
@@ -29,7 +29,8 @@
"syncjobs": "同步任务",
"tls_policy": "TLS 策略",
"unlimited_quota": "无限邮箱容量配额",
"pw_reset": "允许重置mailcow使用者密码"
"pw_reset": "允许重置mailcow使用者密码",
"alias_external_goto": "允许带有外部目标地址的别名"
},
"add": {
"activate_filter_warn": "当“启用”选项被勾选后,其它所有的过滤器都会被禁用。",
@@ -111,7 +112,8 @@
"validation_success": "验证成功",
"dry": "模拟同步(Dry run)",
"internal_info": "内部的别名只能在域内部或者别名域内部访问。",
"internal": "内部的"
"internal": "内部的",
"sender_allowed": "允许以该别名发送"
},
"admin": {
"access": "权限管理",
@@ -555,7 +557,10 @@
"max_age_invalid": "最大有效时间 %s 无效",
"mode_invalid": "模式 %s 无效",
"mx_invalid": "MX 记录 %s 无效",
"version_invalid": "版本 %s 无效"
"version_invalid": "版本 %s 无效",
"tfa_removal_blocked": "无法移除两步验证,你的账户必须使用两步验证。",
"external_goto_denied": "不允许使用外部目标地址 %s",
"quarantine_category_invalid": "隔离分类必须是 add_header, reject, all 的其中一个"
},
"debug": {
"chart_this_server": "图表 (此服务器)",
@@ -754,7 +759,10 @@
"mta_sts_max_age_info": "接收方邮件服务器可缓存该策略的时长(秒),超出后需重新获取策略。",
"mta_sts_mx": "MX 服务器",
"mta_sts_mx_info": "仅允许向明确列出的邮件服务器发送邮件;发送方 MTA 会验证 DNS MX 记录的主机名是否与策略列表匹配,并仅允许携带有效 TLS 证书的投递(可防范中间人攻击)。",
"mta_sts_mx_notice": "可配置多个 MX 服务器(以逗号分隔)。"
"mta_sts_mx_notice": "可配置多个 MX 服务器(以逗号分隔)。",
"sender_allowed": "允许以此别名发送",
"sender_allowed_info": "如果禁用了,此别名只能接收邮件。使用发件者 ACL 来覆盖此选项并赋予指定的邮箱发送权限。",
"mta_sts_active_info": "如果未选中,MTA-STS 策略不会被公布并且不会通过 ACME 为 MTA-STS 子域名请求证书。"
},
"fido2": {
"confirm": "确认",
@@ -1002,7 +1010,8 @@
"templates": "模板",
"template": "模板",
"iam": "身份提供者(IDP)",
"internal": "内部的"
"internal": "内部的",
"force_tfa": "两步验证"
},
"oauth2": {
"access_denied": "请作为邮箱所有者登录以使用 OAuth2 授权。",
@@ -1210,7 +1219,12 @@
"waiting_usb_auth": "<i>等待 USB 设备中...</i><br><br>现在请触碰你的 WebAuthn USB 设备上的按钮。",
"waiting_usb_register": "<i>等待 USB 设备中...</i><br><br>请在上方输入你的密码并请触碰你的 WebAuthn USB 设备上的按钮以确认注册该 WebAuthn 设备。",
"yubi_otp": "Yubico OTP 认证",
"authenticators": "验证器(Authenticators)"
"authenticators": "验证器(Authenticators)",
"force_tfa": "在登录时强制启用 2FA",
"force_tfa_info": "在访问面板前用户会被要求设置两步验证。",
"setup_title": "需要设置两步验证",
"setup_required": "你的账户需要设置两步验证。请添加一个两步验证方法以继续。",
"cancel_setup": "取消并注销"
},
"user": {
"action": "操作",
@@ -1384,7 +1398,8 @@
"overview": "概览",
"expire_never": "永不过期",
"forever": "永久",
"spam_aliases_info": "垃圾邮件别名是一种临时电子邮件地址,可用于保护真实电子邮件地址。<br>还可以选择设置过期时间,以便在设定的时间后自动停用别名,从而有效地销毁被滥用或泄露的地址。"
"spam_aliases_info": "垃圾邮件别名是一种临时电子邮件地址,可用于保护真实电子邮件地址。<br>还可以选择设置过期时间,以便在设定的时间后自动停用别名,从而有效地销毁被滥用或泄露的地址。",
"pw_update_required": "你的账户需要修改一次密码。请设置一个新密码以继续。"
},
"warning": {
"cannot_delete_self": "不能删除已登录的用户",
@@ -7,7 +7,33 @@
<span class="d-none d-md-block">{{ lang.admin.customize }}</span>
</div>
<div id="collapse-tab-config-customize" class="card-body collapse" data-bs-parent="#admin-content">
<legend><i class="bi bi-file-image"></i> {{ lang.admin.change_logo }}</legend><hr />
<legend><i class="bi bi-palette"></i> {{ lang.admin.ui_theme }}</legend><hr />
<div class="text-muted mb-3">{{ lang.admin.ui_theme_info|raw }}</div>
<form class="form" data-id="ui_theme" role="form" method="post">
<div class="row">
<div class="col-12 col-md-6 col-lg-4">
<p><select data-width="100%" id="ui_theme" name="ui_theme" class="selectpicker show-tick">
<option value="" {% if not ui_theme %}selected{% endif %}>{{ lang.admin.ui_theme_default }}</option>
{% for key, theme in ui_themes %}
<option value="{{ key }}" {% if ui_theme == key %}selected{% endif %}>{{ theme.name }}</option>
{% endfor %}
</select></p>
</div>
</div>
{% if ui_themes %}
<ul class="list-unstyled text-muted small mb-4">
{% for key, theme in ui_themes %}
<li><b>{{ theme.name }}</b> <code>{{ key }}</code>{% if theme.dark %} <i class="bi bi-moon-fill" title="{{ lang.admin.ui_theme_dark }}"></i>{% endif %}{% if theme.description %} - {{ theme.description }}{% endif %}</li>
{% endfor %}
</ul>
{% else %}
<p class="text-muted small mb-4">{{ lang.admin.ui_theme_none }}</p>
{% endif %}
<p><div class="btn-group">
<button class="btn btn-sm btn-xs-half d-block d-sm-inline btn-success" data-action="edit_selected" data-item="admin" data-id="ui_theme" data-api-url='edit/ui_theme' data-api-attr='{}' href="#"><i class="bi bi-check-lg"></i> {{ lang.admin.save }}</button>
</div></p>
</form>
<legend style="padding-top:20px"><i class="bi bi-file-image"></i> {{ lang.admin.change_logo }}</legend><hr />
<p class="text-muted">{{ lang.admin.logo_info }}</p>
<form class="form-inline" role="form" method="post" enctype="multipart/form-data">
<div class="mb-4">
+2 -2
View File
@@ -6,9 +6,9 @@
<div class="row mb-4" style="margin-top: 60px">
<div class="col-12 col-md-7 col-lg-6 col-xl-5 ms-auto me-auto">
<div class="card">
<div class="card login-card login-admin">
<div class="card-header d-flex align-items-center text-break">
<i class="bi bi-person-fill me-2"></i> {{ lang.login.login_admin }}
<i class="bi bi-shield-lock-fill me-2"></i> {{ lang.login.login_admin }}
<div class="ms-auto form-check form-switch my-auto d-flex align-items-center">
<label class="form-check-label"><i class="bi bi-moon-fill"></i></label>
<input class="form-check-input ms-2" type="checkbox" id="dark-mode-toggle">
+2 -1
View File
@@ -10,6 +10,7 @@
<link rel="stylesheet" href="{{ css_path }}">
<script>
var mailcow_dark_css = '{{ dark_css_path }}';
// check if darkmode is preferred by OS or set by localStorage
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches && localStorage.getItem("mailcow_theme") !== "light" ||
localStorage.getItem("mailcow_theme") === "dark") {
@@ -18,7 +19,7 @@
link.id = 'dark-mode-theme';
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = '/css/themes/mailcow-darkmode.css';
link.href = mailcow_dark_css;
head.appendChild(link);
}
</script>
+2 -2
View File
@@ -6,9 +6,9 @@
<div class="row mb-4" style="margin-top: 60px">
<div class="col-12 col-md-7 col-lg-6 col-xl-5 ms-auto me-auto">
<div class="card">
<div class="card login-card login-domainadmin">
<div class="card-header d-flex align-items-center text-break">
<i class="bi bi-person-fill me-2"></i> {{ lang.login.login_dadmin }}
<i class="bi bi-globe2 me-2"></i> {{ lang.login.login_dadmin }}
<div class="ms-auto form-check form-switch my-auto d-flex align-items-center">
<label class="form-check-label"><i class="bi bi-moon-fill"></i></label>
<input class="form-check-input ms-2" type="checkbox" id="dark-mode-toggle">
+2 -2
View File
@@ -7,12 +7,12 @@
<li class="nav-item dropdown" role="presentation">
<a class="nav-link dropdown-toggle active" data-bs-toggle="dropdown" href="#">{{ lang.mailbox.domains }}</a>
<ul class="dropdown-menu">
<li><button class="dropdown-item" aria-selected="false" aria-controls="tab-domains" role="tab" data-bs-toggle="tab" data-bs-target="#tab-domains">{{ lang.mailbox.domains }}</button></li>
<li><button class="dropdown-item active" aria-selected="true" aria-controls="tab-domains" role="tab" data-bs-toggle="tab" data-bs-target="#tab-domains">{{ lang.mailbox.domains }}</button></li>
<li><button class="dropdown-item" aria-selected="false" aria-controls="tab-templates-domains" role="tab" data-bs-toggle="tab" data-bs-target="#tab-templates-domains">{{ lang.mailbox.templates }}</button></li>
</ul>
</li>
{% else %}
<li class="nav-item" role="presentation"><button class="nav-link" aria-controls="tab-domains" role="tab" data-bs-toggle="tab" data-bs-target="#tab-domains">{{ lang.mailbox.domains }}</button></li>
<li class="nav-item" role="presentation"><button class="nav-link active" aria-selected="true" aria-controls="tab-domains" role="tab" data-bs-toggle="tab" data-bs-target="#tab-domains">{{ lang.mailbox.domains }}</button></li>
{% endif %}
{% if mailcow_cc_role == 'admin' %}
<li class="nav-item dropdown" role="presentation">
+1 -1
View File
@@ -6,7 +6,7 @@
<div class="row mb-4" style="margin-top: 60px">
<div class="col-12 col-md-7 col-lg-6 col-xl-5 ms-auto me-auto">
<div class="card">
<div class="card login-card login-user">
<div class="card-header d-flex align-items-center text-break">
<i class="bi bi-person-fill me-2"></i> {{ lang.login.login_user }}
<div class="ms-auto form-check form-switch my-auto d-flex align-items-center">
+7 -7
View File
@@ -1,7 +1,7 @@
services:
unbound-mailcow:
image: ghcr.io/mailcow/unbound:1.25.1-1
image: ghcr.io/mailcow/unbound:1.26.1-1
environment:
- TZ=${TZ}
- SKIP_UNBOUND_HEALTHCHECK=${SKIP_UNBOUND_HEALTHCHECK:-n}
@@ -42,7 +42,7 @@ services:
- mysql
redis-mailcow:
image: redis:7.4.10-alpine
image: redis:7.4.11-alpine
entrypoint: ["/bin/sh","/redis-conf.sh"]
volumes:
- redis-vol-1:/data/
@@ -84,7 +84,7 @@ services:
- clamd
rspamd-mailcow:
image: ghcr.io/mailcow/rspamd:4.1.4-1
image: ghcr.io/mailcow/rspamd:4.2.1-1
stop_grace_period: 30s
depends_on:
- dovecot-mailcow
@@ -117,7 +117,7 @@ services:
- rspamd
php-fpm-mailcow:
image: ghcr.io/mailcow/phpfpm:8.2.29-3
image: ghcr.io/mailcow/phpfpm:8.2.35-1
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
depends_on:
- redis-mailcow
@@ -200,7 +200,7 @@ services:
- phpfpm
sogo-mailcow:
image: ghcr.io/mailcow/sogo:5.12.10-1
image: ghcr.io/mailcow/sogo:5.12.12-1
environment:
- DBNAME=${DBNAME}
- DBUSER=${DBUSER}
@@ -382,7 +382,7 @@ services:
- postfix
postfix-tlspol-mailcow:
image: ghcr.io/mailcow/postfix-tlspol:1.11.0
image: ghcr.io/mailcow/postfix-tlspol:1.14.0
depends_on:
unbound-mailcow:
condition: service_healthy
@@ -419,7 +419,7 @@ services:
- php-fpm-mailcow
- sogo-mailcow
- rspamd-mailcow
image: ghcr.io/mailcow/nginx:1.30.4-1
image: ghcr.io/mailcow/nginx:1.30.6-1
dns:
- ${IPV4_NETWORK:-172.22.1}.254
environment: