Compare commits
251 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
74bcec45f1 | ||
|
9700b3251f | ||
|
88b8d50cd5 | ||
|
55b0191050 | ||
|
33c97fb318 | ||
|
23d33ad5a8 | ||
|
bd6c98047a | ||
|
73d6a29ae1 | ||
|
173e39c859 | ||
|
c0745c5cde | ||
|
1a6f93327e | ||
|
3c68a53170 | ||
|
e38c27ed67 | ||
|
8eaf8bbbde | ||
|
e015c7dbca | ||
|
58452abcdf | ||
|
2cbf0da137 | ||
|
aabcd10539 | ||
|
ee607dc3cc | ||
|
1265302a8e | ||
|
b5acf56e20 | ||
|
fe4a418af4 | ||
|
e5f03e8526 | ||
|
fb60c4a150 | ||
|
fd203abd47 | ||
|
6b65f0fc74 | ||
|
856b3b62f2 | ||
|
0372a2150d | ||
|
f3322c0577 | ||
|
c2bcc4e086 | ||
|
6e79c48640 | ||
|
d7dfa95e1b | ||
|
cf1cc24e33 | ||
|
6824a5650f | ||
|
73570cc8b5 | ||
|
959dcb9980 | ||
|
8f28666916 | ||
|
3eaa5a626c | ||
|
8c79056a94 | ||
|
ed076dc23e | ||
|
be2286c11c | ||
|
0e24c3d300 | ||
|
e1d8df6580 | ||
|
04a08a7d69 | ||
|
3c0c8aa01f | ||
|
026b278357 | ||
|
4121509ceb | ||
|
00ac61f0a4 | ||
|
4bb0dbb2f7 | ||
|
13b6df74af | ||
|
5c025bf865 | ||
|
20fc9eaf84 | ||
|
22a0479fab | ||
|
3510d5617d | ||
|
236d627fbd | ||
|
99739eada0 | ||
|
7bfef57894 | ||
|
d9dfe15253 | ||
|
3fe8aaa719 | ||
|
78a8fac6af | ||
|
6986e7758f | ||
|
b4a9df76b8 | ||
|
d9d958356a | ||
|
96f954a4e2 | ||
|
44585e1c15 | ||
|
c737ff4180 | ||
|
025279009d | ||
|
a9dc13d567 | ||
|
c3ed01c9b5 | ||
|
bd0b4a521e | ||
|
800a0ace71 | ||
|
db97869472 | ||
|
f681fcf154 | ||
|
db1b5956fc | ||
|
bdb07061ed | ||
|
428b917579 | ||
|
469f959e96 | ||
|
b68e189d97 | ||
|
028ef22878 | ||
|
80dacc015a | ||
|
0194c39bd5 | ||
|
f53ca24bb0 | ||
|
ae46a877d3 | ||
|
400939faf6 | ||
|
fd0205aafd | ||
|
e367a8ce24 | ||
|
096e2a41e9 | ||
|
e010f08143 | ||
|
3d2483ca37 | ||
|
535dd23509 | ||
|
4336a99c6a | ||
|
4cd5f93cdf | ||
|
67955779b0 | ||
|
26c34b484a | ||
|
4021613059 | ||
|
e891bf8411 | ||
|
f7798d1aac | ||
|
d11f00261b | ||
|
22cd12f37b | ||
|
db2fb12837 | ||
|
e808e595eb | ||
|
ce6742c676 | ||
|
cf3dc584d0 | ||
|
62f3603588 | ||
|
9fd4aa93e9 | ||
|
5bc3d93545 | ||
|
c28a6b89f0 | ||
|
1233613bea | ||
|
0206e0886c | ||
|
f6d135fbad | ||
|
f7da314dcf | ||
|
e6ce5e88f7 | ||
|
e5e6418be8 | ||
|
6507b53bbb | ||
|
0f59d4952b | ||
|
7225bd2f55 | ||
|
deb2b80352 | ||
|
ad9dee92be | ||
|
f36bc16ca7 | ||
|
bda5f0ed4a | ||
|
cbe1c97a82 | ||
|
81fcbdd104 | ||
|
1a9294b58f | ||
|
310c01aac2 | ||
|
229303c1f8 | ||
|
fc075bc6b7 | ||
|
d04f0257c2 | ||
|
d11d356803 | ||
|
c54750ef8b | ||
|
510ef5196b | ||
|
04e46f9f5b | ||
|
6c0a5028c0 | ||
|
791bbeeb39 | ||
|
a5b8f1b7f7 | ||
|
af267ff706 | ||
|
46cc022590 | ||
|
f77c65411d | ||
|
1052e13af8 | ||
|
11e1502b12 | ||
|
02afc45a15 | ||
|
3e1cfe0d08 | ||
|
d20df7d73e | ||
|
a8c61daeaf | ||
|
1a4f11209a | ||
|
04403aaf70 | ||
|
7f0dd7d0d7 | ||
|
cd29ad883e | ||
|
e1cd719a17 | ||
|
15bb331a7d | ||
|
6f3179bb8d | ||
|
29e5b87207 | ||
|
4403bc2d18 | ||
|
63e92e0897 | ||
|
aa4d8b1f47 | ||
|
9054ca18be | ||
|
38291d123f | ||
|
ca64ff2c0b | ||
|
dc85f49961 | ||
|
5dca4dac81 | ||
|
df8775d4c9 | ||
|
2bc663dcd5 | ||
|
1071bb8230 | ||
|
e437810eca | ||
|
e8fd34d31f | ||
|
6aebb8352e | ||
|
d684e0efc0 | ||
|
64ac6a8891 | ||
|
72e8180c6b | ||
|
d62c275004 | ||
|
aa7f562761 | ||
|
a1f033e4c1 | ||
|
58ddc31db6 | ||
|
5bf62481d5 | ||
|
6ff3f3f044 | ||
|
640f535e99 | ||
|
05d1a974eb | ||
|
99e38d81b1 | ||
|
ed7b384e24 | ||
|
5439ea1010 | ||
|
b719982504 | ||
|
8281d3fa55 | ||
|
9ba65a572e | ||
|
afddcf7f3b | ||
|
294569f5c9 | ||
|
ef6452cf55 | ||
|
9af40eba10 | ||
|
1b3a13ca19 | ||
|
71cc607de6 | ||
|
2ebd8345df | ||
|
f5baeb31c1 | ||
|
5abda44bc6 | ||
|
520d070081 | ||
|
86beba6f5a | ||
|
f0d9948aee | ||
|
8e3d2f7010 | ||
|
fc1c5a505d | ||
|
18cb06fbc7 | ||
|
1af785a94f | ||
|
7626becb38 | ||
|
5d5e959729 | ||
|
49bbdd064e | ||
|
9279ee2e76 | ||
|
a76e6b32f7 | ||
|
4c6f8c4f60 | ||
|
826d32413b | ||
|
b6799d9fcb | ||
|
8782304e8d | ||
|
9c55d46bc6 | ||
|
099db33e44 | ||
|
5c57df4669 | ||
|
152431a7d7 | ||
|
36fa5dc633 | ||
|
814f4aed15 | ||
|
e990856629 | ||
|
c97afbfa0b | ||
|
93b3e0302a | ||
|
27c87de4ed | ||
|
028ad4ceb9 | ||
|
e501642b8e | ||
|
7966f010a2 | ||
|
b22f74cb59 | ||
|
c928948b15 | ||
|
606eaad8f7 | ||
|
c44281f62d | ||
|
1e98784eee | ||
|
dd9296ffc2 | ||
|
fc0e6b6efb | ||
|
68f5fbf65c | ||
|
9727e4084f | ||
|
5c2f48e94c | ||
|
cb098df743 | ||
|
b3c54ed07a | ||
|
c601eca25d | ||
|
48a13255f3 | ||
|
08f93c7d58 | ||
|
e5c9752681 | ||
|
afa1ed1eff | ||
|
072cbe62de | ||
|
9fe8bfadf3 | ||
|
75e4953070 | ||
|
de30650dc7 | ||
|
690c34bc1d | ||
|
4d2e32ee40 | ||
|
02b2988beb | ||
|
3f1a5af88b | ||
|
850fd85d4d | ||
|
24acd42589 | ||
|
eaa0dea63b | ||
|
1606658cb1 | ||
|
54ba66733e | ||
|
f6847e6f8c |
22
.github/renovate.json
vendored
22
.github/renovate.json
vendored
@@ -1,13 +1,31 @@
|
|||||||
{
|
{
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"timezone": "Europe/Berlin",
|
"timezone": "Europe/Berlin",
|
||||||
"dependencyDashboard": false,
|
"dependencyDashboard": true,
|
||||||
"dependencyDashboardTitle": "Renovate Dashboard",
|
"dependencyDashboardTitle": "Renovate Dashboard",
|
||||||
"commitBody": "Signed-off-by: milkmaker <milkmaker@mailcow.de>",
|
"commitBody": "Signed-off-by: milkmaker <milkmaker@mailcow.de>",
|
||||||
"rebaseWhen": "auto",
|
"rebaseWhen": "auto",
|
||||||
|
"labels": ["renovate"],
|
||||||
"assignees": [
|
"assignees": [
|
||||||
"@magiccc"
|
"@magiccc"
|
||||||
],
|
],
|
||||||
"baseBranches": ["staging"],
|
"baseBranches": ["staging"],
|
||||||
"enabledManagers": ["github-actions"]
|
"enabledManagers": ["github-actions", "regex", "docker-compose"],
|
||||||
|
"ignorePaths": [
|
||||||
|
"data\/web\/inc\/lib\/vendor\/matthiasmullie\/minify\/**"
|
||||||
|
],
|
||||||
|
"regexManagers": [
|
||||||
|
{
|
||||||
|
"fileMatch": ["^helper-scripts\/nextcloud.sh$"],
|
||||||
|
"matchStrings": [
|
||||||
|
"#\\srenovate:\\sdatasource=(?<datasource>.*?) depName=(?<depName>.*?)( versioning=(?<versioning>.*?))?( extractVersion=(?<extractVersion>.*?))?\\s.*?_VERSION=(?<currentValue>.*)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fileMatch": ["(^|/)Dockerfile[^/]*$"],
|
||||||
|
"matchStrings": [
|
||||||
|
"#\\srenovate:\\sdatasource=(?<datasource>.*?) depName=(?<depName>.*?)( versioning=(?<versioning>.*?))?\\s(ENV|ARG) .*?_VERSION=(?<currentValue>.*)\\s"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
@@ -10,7 +10,7 @@ jobs:
|
|||||||
if: github.event.pull_request.base.ref != 'staging' #check if the target branch is not staging
|
if: github.event.pull_request.base.ref != 'staging' #check if the target branch is not staging
|
||||||
steps:
|
steps:
|
||||||
- name: Send message
|
- name: Send message
|
||||||
uses: thollander/actions-comment-pull-request@main
|
uses: thollander/actions-comment-pull-request@v2.3.1
|
||||||
with:
|
with:
|
||||||
GITHUB_TOKEN: ${{ secrets.CHECKIFPRISSTAGING_ACTION_PAT }}
|
GITHUB_TOKEN: ${{ secrets.CHECKIFPRISSTAGING_ACTION_PAT }}
|
||||||
message: |
|
message: |
|
||||||
|
@@ -14,7 +14,7 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Mark/Close Stale Issues and Pull Requests 🗑️
|
- name: Mark/Close Stale Issues and Pull Requests 🗑️
|
||||||
uses: actions/stale@v6.0.1
|
uses: actions/stale@v8.0.0
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.STALE_ACTION_PAT }}
|
repo-token: ${{ secrets.STALE_ACTION_PAT }}
|
||||||
days-before-stale: 60
|
days-before-stale: 60
|
||||||
|
63
.github/workflows/integration_tests.yml
vendored
63
.github/workflows/integration_tests.yml
vendored
@@ -1,63 +0,0 @@
|
|||||||
name: mailcow Integration Tests
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "master", "staging" ]
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
integration_tests:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Setup Ansible
|
|
||||||
run: |
|
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install python3 python3-pip git
|
|
||||||
sudo pip3 install ansible
|
|
||||||
- name: Prepair Test Environment
|
|
||||||
run: |
|
|
||||||
git clone https://github.com/mailcow/mailcow-integration-tests.git --branch $(curl -sL https://api.github.com/repos/mailcow/mailcow-integration-tests/releases/latest | jq -r '.tag_name') --single-branch .
|
|
||||||
./fork_check.sh
|
|
||||||
./ci.sh
|
|
||||||
./ci-pip-requirements.sh
|
|
||||||
env:
|
|
||||||
VAULT_PW: ${{ secrets.MAILCOW_TESTS_VAULT_PW }}
|
|
||||||
VAULT_FILE: ${{ secrets.MAILCOW_TESTS_VAULT_FILE }}
|
|
||||||
- name: Start Integration Test Server
|
|
||||||
run: |
|
|
||||||
./fork_check.sh
|
|
||||||
ansible-playbook mailcow-start-server.yml --diff
|
|
||||||
env:
|
|
||||||
PY_COLORS: '1'
|
|
||||||
ANSIBLE_FORCE_COLOR: '1'
|
|
||||||
ANSIBLE_HOST_KEY_CHECKING: 'false'
|
|
||||||
- name: Setup Integration Test Server
|
|
||||||
run: |
|
|
||||||
./fork_check.sh
|
|
||||||
sleep 30
|
|
||||||
ansible-playbook mailcow-setup-server.yml --private-key id_ssh_rsa --diff
|
|
||||||
env:
|
|
||||||
PY_COLORS: '1'
|
|
||||||
ANSIBLE_FORCE_COLOR: '1'
|
|
||||||
ANSIBLE_HOST_KEY_CHECKING: 'false'
|
|
||||||
- name: Run Integration Tests
|
|
||||||
run: |
|
|
||||||
./fork_check.sh
|
|
||||||
ansible-playbook mailcow-integration-tests.yml --private-key id_ssh_rsa --diff
|
|
||||||
env:
|
|
||||||
PY_COLORS: '1'
|
|
||||||
ANSIBLE_FORCE_COLOR: '1'
|
|
||||||
ANSIBLE_HOST_KEY_CHECKING: 'false'
|
|
||||||
- name: Delete Integration Test Server
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
./fork_check.sh
|
|
||||||
ansible-playbook mailcow-delete-server.yml --diff
|
|
||||||
env:
|
|
||||||
PY_COLORS: '1'
|
|
||||||
ANSIBLE_FORCE_COLOR: '1'
|
|
||||||
ANSIBLE_HOST_KEY_CHECKING: 'false'
|
|
2
.github/workflows/pr_to_nightly.yml
vendored
2
.github/workflows/pr_to_nightly.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Run the Action
|
- name: Run the Action
|
||||||
uses: devops-infra/action-pull-request@v0.5.3
|
uses: devops-infra/action-pull-request@v0.5.5
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.PRTONIGHTLY_ACTION_PAT }}
|
github_token: ${{ secrets.PRTONIGHTLY_ACTION_PAT }}
|
||||||
title: Automatic PR to nightly from ${{ github.event.repository.updated_at}}
|
title: Automatic PR to nightly from ${{ github.event.repository.updated_at}}
|
||||||
|
2
.github/workflows/rebuild_backup_image.yml
vendored
2
.github/workflows/rebuild_backup_image.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
|||||||
password: ${{ secrets.BACKUPIMAGEBUILD_ACTION_DOCKERHUB_TOKEN }}
|
password: ${{ secrets.BACKUPIMAGEBUILD_ACTION_DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: data/Dockerfiles/backup/Dockerfile
|
file: data/Dockerfiles/backup/Dockerfile
|
||||||
|
@@ -1,20 +0,0 @@
|
|||||||
name: "Tweet trigger release"
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
tweet:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: "Get Release Tag"
|
|
||||||
run: |
|
|
||||||
RELEASE_TAG=$(curl https://api.github.com/repos/mailcow/mailcow-dockerized/releases/latest | jq -r '.tag_name')
|
|
||||||
- name: Tweet-trigger-publish-release
|
|
||||||
uses: mugi111/tweet-trigger-release@v1.1
|
|
||||||
with:
|
|
||||||
consumer_key: ${{ secrets.CONSUMER_KEY }}
|
|
||||||
consumer_secret: ${{ secrets.CONSUMER_SECRET }}
|
|
||||||
access_token_key: ${{ secrets.ACCESS_TOKEN_KEY }}
|
|
||||||
access_token_secret: ${{ secrets.ACCESS_TOKEN_SECRET }}
|
|
||||||
tweet_body: 'A new mailcow update has just been released! Checkout the GitHub Page for changelog and more informations: https://github.com/mailcow/mailcow-dockerized/releases/latest'
|
|
39
.github/workflows/update_postscreen_access_list.yml
vendored
Normal file
39
.github/workflows/update_postscreen_access_list.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
name: Update postscreen_access.cidr
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# Monthly
|
||||||
|
- cron: "0 0 1 * *"
|
||||||
|
workflow_dispatch: # Allow to run workflow manually
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read # to fetch code (actions/checkout)
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Update-postscreen_access_cidr:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Generate postscreen_access.cidr
|
||||||
|
run: |
|
||||||
|
bash helper-scripts/update_postscreen_whitelist.sh
|
||||||
|
|
||||||
|
- name: Create Pull Request
|
||||||
|
uses: peter-evans/create-pull-request@v5
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.mailcow_action_Update_postscreen_access_cidr_pat }}
|
||||||
|
commit-message: update postscreen_access.cidr
|
||||||
|
committer: milkmaker <milkmaker@mailcow.de>
|
||||||
|
author: milkmaker <milkmaker@mailcow.de>
|
||||||
|
signoff: false
|
||||||
|
branch: update/postscreen_access.cidr
|
||||||
|
base: staging
|
||||||
|
delete-branch: true
|
||||||
|
add-paths: |
|
||||||
|
data/conf/postfix/postscreen_access.cidr
|
||||||
|
title: '[Postfix] update postscreen_access.cidr'
|
||||||
|
body: |
|
||||||
|
This PR updates the postscreen_access.cidr using GitHub Actions and [helper-scripts/update_postscreen_whitelist.sh](https://github.com/mailcow/mailcow-dockerized/blob/master/helper-scripts/update_postscreen_whitelist.sh)
|
@@ -1,6 +1,5 @@
|
|||||||
# mailcow: dockerized - 🐮 + 🐋 = 💕
|
# mailcow: dockerized - 🐮 + 🐋 = 💕
|
||||||
|
|
||||||
[](https://github.com/mailcow/mailcow-dockerized/actions/workflows/integration_tests.yml)
|
|
||||||
[](https://translate.mailcow.email/engage/mailcow-dockerized/)
|
[](https://translate.mailcow.email/engage/mailcow-dockerized/)
|
||||||
[](https://twitter.com/mailcow_email)
|
[](https://twitter.com/mailcow_email)
|
||||||
|
|
||||||
|
@@ -213,11 +213,13 @@ while true; do
|
|||||||
done
|
done
|
||||||
ADDITIONAL_WC_ARR+=('autodiscover' 'autoconfig')
|
ADDITIONAL_WC_ARR+=('autodiscover' 'autoconfig')
|
||||||
|
|
||||||
|
if [[ ${SKIP_IP_CHECK} != "y" ]]; then
|
||||||
# Start IP detection
|
# Start IP detection
|
||||||
log_f "Detecting IP addresses..."
|
log_f "Detecting IP addresses..."
|
||||||
IPV4=$(get_ipv4)
|
IPV4=$(get_ipv4)
|
||||||
IPV6=$(get_ipv6)
|
IPV6=$(get_ipv6)
|
||||||
log_f "OK: ${IPV4}, ${IPV6:-"0000:0000:0000:0000:0000:0000:0000:0000"}"
|
log_f "OK: ${IPV4}, ${IPV6:-"0000:0000:0000:0000:0000:0000:0000:0000"}"
|
||||||
|
fi
|
||||||
|
|
||||||
#########################################
|
#########################################
|
||||||
# IP and webroot challenge verification #
|
# IP and webroot challenge verification #
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
FROM clamav/clamav:1.0_base
|
FROM clamav/clamav:1.0.1-1_base
|
||||||
|
|
||||||
LABEL maintainer "André Peters <andre.peters@servercow.de>"
|
LABEL maintainer "André Peters <andre.peters@servercow.de>"
|
||||||
|
|
||||||
|
@@ -13,6 +13,7 @@ RUN apk add --update --no-cache python3 \
|
|||||||
fastapi \
|
fastapi \
|
||||||
uvicorn \
|
uvicorn \
|
||||||
aiodocker \
|
aiodocker \
|
||||||
|
docker \
|
||||||
redis
|
redis
|
||||||
|
|
||||||
COPY docker-entrypoint.sh /app/
|
COPY docker-entrypoint.sh /app/
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
from fastapi import FastAPI, Response, Request
|
from fastapi import FastAPI, Response, Request
|
||||||
import aiodocker
|
import aiodocker
|
||||||
|
import docker
|
||||||
import psutil
|
import psutil
|
||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
@@ -8,12 +9,40 @@ import os
|
|||||||
import json
|
import json
|
||||||
import asyncio
|
import asyncio
|
||||||
import redis
|
import redis
|
||||||
|
import platform
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import logging
|
||||||
|
from logging.config import dictConfig
|
||||||
|
|
||||||
|
|
||||||
|
log_config = {
|
||||||
|
"version": 1,
|
||||||
|
"disable_existing_loggers": False,
|
||||||
|
"formatters": {
|
||||||
|
"default": {
|
||||||
|
"()": "uvicorn.logging.DefaultFormatter",
|
||||||
|
"fmt": "%(levelprefix)s %(asctime)s %(message)s",
|
||||||
|
"datefmt": "%Y-%m-%d %H:%M:%S",
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"handlers": {
|
||||||
|
"default": {
|
||||||
|
"formatter": "default",
|
||||||
|
"class": "logging.StreamHandler",
|
||||||
|
"stream": "ext://sys.stderr",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"loggers": {
|
||||||
|
"api-logger": {"handlers": ["default"], "level": "INFO"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
dictConfig(log_config)
|
||||||
|
|
||||||
containerIds_to_update = []
|
containerIds_to_update = []
|
||||||
host_stats_isUpdating = False
|
host_stats_isUpdating = False
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
logger = logging.getLogger('api-logger')
|
||||||
|
|
||||||
|
|
||||||
@app.get("/host/stats")
|
@app.get("/host/stats")
|
||||||
@@ -21,18 +50,15 @@ async def get_host_update_stats():
|
|||||||
global host_stats_isUpdating
|
global host_stats_isUpdating
|
||||||
|
|
||||||
if host_stats_isUpdating == False:
|
if host_stats_isUpdating == False:
|
||||||
print("start host stats task")
|
|
||||||
asyncio.create_task(get_host_stats())
|
asyncio.create_task(get_host_stats())
|
||||||
host_stats_isUpdating = True
|
host_stats_isUpdating = True
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
if redis_client.exists('host_stats'):
|
if redis_client.exists('host_stats'):
|
||||||
break
|
break
|
||||||
print("wait for host_stats results")
|
|
||||||
await asyncio.sleep(1.5)
|
await asyncio.sleep(1.5)
|
||||||
|
|
||||||
|
|
||||||
print("host stats pulled")
|
|
||||||
stats = json.loads(redis_client.get('host_stats'))
|
stats = json.loads(redis_client.get('host_stats'))
|
||||||
return Response(content=json.dumps(stats, indent=4), media_type="application/json")
|
return Response(content=json.dumps(stats, indent=4), media_type="application/json")
|
||||||
|
|
||||||
@@ -106,14 +132,14 @@ async def post_containers(container_id : str, post_action : str, request: Reques
|
|||||||
else:
|
else:
|
||||||
api_call_method_name = '__'.join(['container_post', str(post_action) ])
|
api_call_method_name = '__'.join(['container_post', str(post_action) ])
|
||||||
|
|
||||||
docker_utils = DockerUtils(async_docker_client)
|
docker_utils = DockerUtils(sync_docker_client)
|
||||||
api_call_method = getattr(docker_utils, api_call_method_name, lambda container_id: Response(content=json.dumps({'type': 'danger', 'msg':'container_post - unknown api call' }, indent=4), media_type="application/json"))
|
api_call_method = getattr(docker_utils, api_call_method_name, lambda container_id: Response(content=json.dumps({'type': 'danger', 'msg':'container_post - unknown api call' }, indent=4), media_type="application/json"))
|
||||||
|
|
||||||
|
|
||||||
print("api call: %s, container_id: %s" % (api_call_method_name, container_id))
|
logger.info("api call: %s, container_id: %s" % (api_call_method_name, container_id))
|
||||||
return await api_call_method(container_id, request_json)
|
return api_call_method(container_id, request_json)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("error - container_post: %s" % str(e))
|
logger.error("error - container_post: %s" % str(e))
|
||||||
res = {
|
res = {
|
||||||
"type": "danger",
|
"type": "danger",
|
||||||
"msg": str(e)
|
"msg": str(e)
|
||||||
@@ -152,398 +178,297 @@ class DockerUtils:
|
|||||||
self.docker_client = docker_client
|
self.docker_client = docker_client
|
||||||
|
|
||||||
# api call: container_post - post_action: stop
|
# api call: container_post - post_action: stop
|
||||||
async def container_post__stop(self, container_id, request_json):
|
def container_post__stop(self, container_id, request_json):
|
||||||
for container in (await self.docker_client.containers.list()):
|
for container in self.docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||||
if container._id == container_id:
|
container.stop()
|
||||||
await container.stop()
|
|
||||||
res = {
|
|
||||||
'type': 'success',
|
|
||||||
'msg': 'command completed successfully'
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
|
|
||||||
|
res = { 'type': 'success', 'msg': 'command completed successfully'}
|
||||||
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
# api call: container_post - post_action: start
|
# api call: container_post - post_action: start
|
||||||
async def container_post__start(self, container_id, request_json):
|
def container_post__start(self, container_id, request_json):
|
||||||
for container in (await self.docker_client.containers.list()):
|
for container in self.docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||||
if container._id == container_id:
|
container.start()
|
||||||
await container.start()
|
|
||||||
res = {
|
res = { 'type': 'success', 'msg': 'command completed successfully'}
|
||||||
'type': 'success',
|
|
||||||
'msg': 'command completed successfully'
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: restart
|
# api call: container_post - post_action: restart
|
||||||
async def container_post__restart(self, container_id, request_json):
|
def container_post__restart(self, container_id, request_json):
|
||||||
for container in (await self.docker_client.containers.list()):
|
for container in self.docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||||
if container._id == container_id:
|
container.restart()
|
||||||
await container.restart()
|
|
||||||
res = {
|
res = { 'type': 'success', 'msg': 'command completed successfully'}
|
||||||
'type': 'success',
|
|
||||||
'msg': 'command completed successfully'
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: top
|
# api call: container_post - post_action: top
|
||||||
async def container_post__top(self, container_id, request_json):
|
def container_post__top(self, container_id, request_json):
|
||||||
for container in (await self.docker_client.containers.list()):
|
for container in self.docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||||
if container._id == container_id:
|
res = { 'type': 'success', 'msg': container.top()}
|
||||||
ps_exec = await container.exec("ps")
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
async with ps_exec.start(detach=False) as stream:
|
# api call: container_post - post_action: stats
|
||||||
ps_return = await stream.read_out()
|
def container_post__stats(self, container_id, request_json):
|
||||||
|
for container in self.docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||||
exec_details = await ps_exec.inspect()
|
for stat in container.stats(decode=True, stream=True):
|
||||||
if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
|
res = { 'type': 'success', 'msg': stat}
|
||||||
res = {
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
'type': 'success',
|
|
||||||
'msg': ps_return.data.decode('utf-8')
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
else:
|
|
||||||
res = {
|
|
||||||
'type': 'danger',
|
|
||||||
'msg': ''
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: mailq - task: delete
|
# api call: container_post - post_action: exec - cmd: mailq - task: delete
|
||||||
async def container_post__exec__mailq__delete(self, container_id, request_json):
|
def container_post__exec__mailq__delete(self, container_id, request_json):
|
||||||
if 'items' in request_json:
|
if 'items' in request_json:
|
||||||
r = re.compile("^[0-9a-fA-F]+$")
|
r = re.compile("^[0-9a-fA-F]+$")
|
||||||
filtered_qids = filter(r.match, request_json['items'])
|
filtered_qids = filter(r.match, request_json['items'])
|
||||||
if filtered_qids:
|
if filtered_qids:
|
||||||
flagged_qids = ['-d %s' % i for i in filtered_qids]
|
flagged_qids = ['-d %s' % i for i in filtered_qids]
|
||||||
sanitized_string = str(' '.join(flagged_qids))
|
sanitized_string = str(' '.join(flagged_qids));
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
postsuper_r = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string])
|
||||||
|
return exec_run_handler('generic', postsuper_r)
|
||||||
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
postsuper_r_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string])
|
|
||||||
return await exec_run_handler('generic', postsuper_r_exec)
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: mailq - task: hold
|
# api call: container_post - post_action: exec - cmd: mailq - task: hold
|
||||||
async def container_post__exec__mailq__hold(self, container_id, request_json):
|
def container_post__exec__mailq__hold(self, container_id, request_json):
|
||||||
if 'items' in request_json:
|
if 'items' in request_json:
|
||||||
r = re.compile("^[0-9a-fA-F]+$")
|
r = re.compile("^[0-9a-fA-F]+$")
|
||||||
filtered_qids = filter(r.match, request_json['items'])
|
filtered_qids = filter(r.match, request_json['items'])
|
||||||
if filtered_qids:
|
if filtered_qids:
|
||||||
flagged_qids = ['-h %s' % i for i in filtered_qids]
|
flagged_qids = ['-h %s' % i for i in filtered_qids]
|
||||||
sanitized_string = str(' '.join(flagged_qids))
|
sanitized_string = str(' '.join(flagged_qids));
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
for container in (await self.docker_client.containers.list()):
|
postsuper_r = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string])
|
||||||
if container._id == container_id:
|
return exec_run_handler('generic', postsuper_r)
|
||||||
postsuper_r_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string])
|
|
||||||
return await exec_run_handler('generic', postsuper_r_exec)
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: mailq - task: cat
|
# api call: container_post - post_action: exec - cmd: mailq - task: cat
|
||||||
async def container_post__exec__mailq__cat(self, container_id, request_json):
|
def container_post__exec__mailq__cat(self, container_id, request_json):
|
||||||
if 'items' in request_json:
|
if 'items' in request_json:
|
||||||
r = re.compile("^[0-9a-fA-F]+$")
|
r = re.compile("^[0-9a-fA-F]+$")
|
||||||
filtered_qids = filter(r.match, request_json['items'])
|
filtered_qids = filter(r.match, request_json['items'])
|
||||||
if filtered_qids:
|
if filtered_qids:
|
||||||
sanitized_string = str(' '.join(filtered_qids))
|
sanitized_string = str(' '.join(filtered_qids));
|
||||||
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
if container._id == container_id:
|
postcat_return = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postcat -q " + sanitized_string], user='postfix')
|
||||||
postcat_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/postcat -q " + sanitized_string], user='postfix')
|
if not postcat_return:
|
||||||
return await exec_run_handler('utf8_text_only', postcat_exec)
|
postcat_return = 'err: invalid'
|
||||||
|
return exec_run_handler('utf8_text_only', postcat_return)
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: mailq - task: unhold
|
# api call: container_post - post_action: exec - cmd: mailq - task: unhold
|
||||||
async def container_post__exec__mailq__unhold(self, container_id, request_json):
|
def container_post__exec__mailq__unhold(self, container_id, request_json):
|
||||||
if 'items' in request_json:
|
if 'items' in request_json:
|
||||||
r = re.compile("^[0-9a-fA-F]+$")
|
r = re.compile("^[0-9a-fA-F]+$")
|
||||||
filtered_qids = filter(r.match, request_json['items'])
|
filtered_qids = filter(r.match, request_json['items'])
|
||||||
if filtered_qids:
|
if filtered_qids:
|
||||||
flagged_qids = ['-H %s' % i for i in filtered_qids]
|
flagged_qids = ['-H %s' % i for i in filtered_qids]
|
||||||
sanitized_string = str(' '.join(flagged_qids))
|
sanitized_string = str(' '.join(flagged_qids));
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
for container in (await self.docker_client.containers.list()):
|
postsuper_r = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string])
|
||||||
if container._id == container_id:
|
return exec_run_handler('generic', postsuper_r)
|
||||||
postsuper_r_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string])
|
|
||||||
return await exec_run_handler('generic', postsuper_r_exec)
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: mailq - task: deliver
|
# api call: container_post - post_action: exec - cmd: mailq - task: deliver
|
||||||
async def container_post__exec__mailq__deliver(self, container_id, request_json):
|
def container_post__exec__mailq__deliver(self, container_id, request_json):
|
||||||
if 'items' in request_json:
|
if 'items' in request_json:
|
||||||
r = re.compile("^[0-9a-fA-F]+$")
|
r = re.compile("^[0-9a-fA-F]+$")
|
||||||
filtered_qids = filter(r.match, request_json['items'])
|
filtered_qids = filter(r.match, request_json['items'])
|
||||||
if filtered_qids:
|
if filtered_qids:
|
||||||
flagged_qids = ['-i %s' % i for i in filtered_qids]
|
flagged_qids = ['-i %s' % i for i in filtered_qids]
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
for container in (await self.docker_client.containers.list()):
|
for i in flagged_qids:
|
||||||
if container._id == container_id:
|
postqueue_r = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postqueue " + i], user='postfix')
|
||||||
for i in flagged_qids:
|
# todo: check each exit code
|
||||||
postsuper_r_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/postqueue " + i], user='postfix')
|
res = { 'type': 'success', 'msg': 'Scheduled immediate delivery'}
|
||||||
async with postsuper_r_exec.start(detach=False) as stream:
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
postsuper_r_return = await stream.read_out()
|
|
||||||
# todo: check each exit code
|
|
||||||
res = {
|
|
||||||
'type': 'success',
|
|
||||||
'msg': 'Scheduled immediate delivery'
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: mailq - task: list
|
# api call: container_post - post_action: exec - cmd: mailq - task: list
|
||||||
async def container_post__exec__mailq__list(self, container_id, request_json):
|
def container_post__exec__mailq__list(self, container_id, request_json):
|
||||||
for container in (await self.docker_client.containers.list()):
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
if container._id == container_id:
|
mailq_return = container.exec_run(["/usr/sbin/postqueue", "-j"], user='postfix')
|
||||||
mailq_exec = await container.exec(["/usr/sbin/postqueue", "-j"], user='postfix')
|
return exec_run_handler('utf8_text_only', mailq_return)
|
||||||
return await exec_run_handler('utf8_text_only', mailq_exec)
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: mailq - task: flush
|
# api call: container_post - post_action: exec - cmd: mailq - task: flush
|
||||||
async def container_post__exec__mailq__flush(self, container_id, request_json):
|
def container_post__exec__mailq__flush(self, container_id, request_json):
|
||||||
for container in (await self.docker_client.containers.list()):
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
if container._id == container_id:
|
postqueue_r = container.exec_run(["/usr/sbin/postqueue", "-f"], user='postfix')
|
||||||
postsuper_r_exec = await container.exec(["/usr/sbin/postqueue", "-f"], user='postfix')
|
return exec_run_handler('generic', postqueue_r)
|
||||||
return await exec_run_handler('generic', postsuper_r_exec)
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: mailq - task: super_delete
|
# api call: container_post - post_action: exec - cmd: mailq - task: super_delete
|
||||||
async def container_post__exec__mailq__super_delete(self, container_id, request_json):
|
def container_post__exec__mailq__super_delete(self, container_id, request_json):
|
||||||
for container in (await self.docker_client.containers.list()):
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
if container._id == container_id:
|
postsuper_r = container.exec_run(["/usr/sbin/postsuper", "-d", "ALL"])
|
||||||
postsuper_r_exec = await container.exec(["/usr/sbin/postsuper", "-d", "ALL"])
|
return exec_run_handler('generic', postsuper_r)
|
||||||
return await exec_run_handler('generic', postsuper_r_exec)
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: system - task: fts_rescan
|
# api call: container_post - post_action: exec - cmd: system - task: fts_rescan
|
||||||
async def container_post__exec__system__fts_rescan(self, container_id, request_json):
|
def container_post__exec__system__fts_rescan(self, container_id, request_json):
|
||||||
if 'username' in request_json:
|
if 'username' in request_json:
|
||||||
for container in (await self.docker_client.containers.list()):
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
if container._id == container_id:
|
rescan_return = container.exec_run(["/bin/bash", "-c", "/usr/bin/doveadm fts rescan -u '" + request_json['username'].replace("'", "'\\''") + "'"], user='vmail')
|
||||||
rescan_exec = await container.exec(["/bin/bash", "-c", "/usr/bin/doveadm fts rescan -u '" + request_json['username'].replace("'", "'\\''") + "'"], user='vmail')
|
if rescan_return.exit_code == 0:
|
||||||
async with rescan_exec.start(detach=False) as stream:
|
res = { 'type': 'success', 'msg': 'fts_rescan: rescan triggered'}
|
||||||
rescan_return = await stream.read_out()
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
else:
|
||||||
exec_details = await rescan_exec.inspect()
|
res = { 'type': 'warning', 'msg': 'fts_rescan error'}
|
||||||
if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
res = {
|
|
||||||
'type': 'success',
|
|
||||||
'msg': 'fts_rescan: rescan triggered'
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
else:
|
|
||||||
res = {
|
|
||||||
'type': 'warning',
|
|
||||||
'msg': 'fts_rescan error'
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
|
|
||||||
if 'all' in request_json:
|
if 'all' in request_json:
|
||||||
for container in (await self.docker_client.containers.list()):
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
if container._id == container_id:
|
rescan_return = container.exec_run(["/bin/bash", "-c", "/usr/bin/doveadm fts rescan -A"], user='vmail')
|
||||||
rescan_exec = await container.exec(["/bin/bash", "-c", "/usr/bin/doveadm fts rescan -A"], user='vmail')
|
if rescan_return.exit_code == 0:
|
||||||
async with rescan_exec.start(detach=False) as stream:
|
res = { 'type': 'success', 'msg': 'fts_rescan: rescan triggered'}
|
||||||
rescan_return = await stream.read_out()
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
else:
|
||||||
exec_details = await rescan_exec.inspect()
|
res = { 'type': 'warning', 'msg': 'fts_rescan error'}
|
||||||
if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
res = {
|
|
||||||
'type': 'success',
|
|
||||||
'msg': 'fts_rescan: rescan triggered'
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
else:
|
|
||||||
res = {
|
|
||||||
'type': 'warning',
|
|
||||||
'msg': 'fts_rescan error'
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: system - task: df
|
# api call: container_post - post_action: exec - cmd: system - task: df
|
||||||
async def container_post__exec__system__df(self, container_id, request_json):
|
def container_post__exec__system__df(self, container_id, request_json):
|
||||||
if 'dir' in request_json:
|
if 'dir' in request_json:
|
||||||
for container in (await self.docker_client.containers.list()):
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
if container._id == container_id:
|
df_return = container.exec_run(["/bin/bash", "-c", "/bin/df -H '" + request_json['dir'].replace("'", "'\\''") + "' | /usr/bin/tail -n1 | /usr/bin/tr -s [:blank:] | /usr/bin/tr ' ' ','"], user='nobody')
|
||||||
df_exec = await container.exec(["/bin/bash", "-c", "/bin/df -H '" + request_json['dir'].replace("'", "'\\''") + "' | /usr/bin/tail -n1 | /usr/bin/tr -s [:blank:] | /usr/bin/tr ' ' ','"], user='nobody')
|
if df_return.exit_code == 0:
|
||||||
async with df_exec.start(detach=False) as stream:
|
return df_return.output.decode('utf-8').rstrip()
|
||||||
df_return = await stream.read_out()
|
else:
|
||||||
|
return "0,0,0,0,0,0"
|
||||||
print(df_return)
|
|
||||||
print(await df_exec.inspect())
|
|
||||||
exec_details = await df_exec.inspect()
|
|
||||||
if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
|
|
||||||
return df_return.data.decode('utf-8').rstrip()
|
|
||||||
else:
|
|
||||||
return "0,0,0,0,0,0"
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: system - task: mysql_upgrade
|
# api call: container_post - post_action: exec - cmd: system - task: mysql_upgrade
|
||||||
async def container_post__exec__system__mysql_upgrade(self, container_id, request_json):
|
def container_post__exec__system__mysql_upgrade(self, container_id, request_json):
|
||||||
for container in (await self.docker_client.containers.list()):
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
if container._id == container_id:
|
sql_return = container.exec_run(["/bin/bash", "-c", "/usr/bin/mysql_upgrade -uroot -p'" + os.environ['DBROOT'].replace("'", "'\\''") + "'\n"], user='mysql')
|
||||||
sql_exec = await container.exec(["/bin/bash", "-c", "/usr/bin/mysql_upgrade -uroot -p'" + os.environ['DBROOT'].replace("'", "'\\''") + "'\n"], user='mysql')
|
if sql_return.exit_code == 0:
|
||||||
async with sql_exec.start(detach=False) as stream:
|
matched = False
|
||||||
sql_return = await stream.read_out()
|
for line in sql_return.output.decode('utf-8').split("\n"):
|
||||||
|
if 'is already upgraded to' in line:
|
||||||
exec_details = await sql_exec.inspect()
|
|
||||||
if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
|
|
||||||
matched = False
|
|
||||||
for line in sql_return.data.decode('utf-8').split("\n"):
|
|
||||||
if 'is already upgraded to' in line:
|
|
||||||
matched = True
|
|
||||||
if matched:
|
|
||||||
res = {
|
|
||||||
'type': 'success',
|
|
||||||
'msg': 'mysql_upgrade: already upgraded',
|
|
||||||
'text': sql_return.data.decode('utf-8')
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
else:
|
|
||||||
await container.restart()
|
|
||||||
res = {
|
|
||||||
'type': 'warning',
|
|
||||||
'msg': 'mysql_upgrade: upgrade was applied',
|
|
||||||
'text': sql_return.data.decode('utf-8')
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
else:
|
|
||||||
res = {
|
|
||||||
'type': 'error',
|
|
||||||
'msg': 'mysql_upgrade: error running command',
|
|
||||||
'text': sql_return.data.decode('utf-8')
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: system - task: mysql_tzinfo_to_sql
|
|
||||||
async def container_post__exec__system__mysql_tzinfo_to_sql(self, container_id, request_json):
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
sql_exec = await container.exec(["/bin/bash", "-c", "/usr/bin/mysql_tzinfo_to_sql /usr/share/zoneinfo | /bin/sed 's/Local time zone must be set--see zic manual page/FCTY/' | /usr/bin/mysql -uroot -p'" + os.environ['DBROOT'].replace("'", "'\\''") + "' mysql \n"], user='mysql')
|
|
||||||
async with sql_exec.start(detach=False) as stream:
|
|
||||||
sql_return = await stream.read_out()
|
|
||||||
|
|
||||||
exec_details = await sql_exec.inspect()
|
|
||||||
if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
|
|
||||||
res = {
|
|
||||||
'type': 'info',
|
|
||||||
'msg': 'mysql_tzinfo_to_sql: command completed successfully',
|
|
||||||
'text': sql_return.data.decode('utf-8')
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
else:
|
|
||||||
res = {
|
|
||||||
'type': 'error',
|
|
||||||
'msg': 'mysql_tzinfo_to_sql: error running command',
|
|
||||||
'text': sql_return.data.decode('utf-8')
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: reload - task: dovecot
|
|
||||||
async def container_post__exec__reload__dovecot(self, container_id, request_json):
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
reload_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/dovecot reload"])
|
|
||||||
return await exec_run_handler('generic', reload_exec)
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: reload - task: postfix
|
|
||||||
async def container_post__exec__reload__postfix(self, container_id, request_json):
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
reload_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/postfix reload"])
|
|
||||||
return await exec_run_handler('generic', reload_exec)
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: reload - task: nginx
|
|
||||||
async def container_post__exec__reload__nginx(self, container_id, request_json):
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
reload_exec = await container.exec(["/bin/sh", "-c", "/usr/sbin/nginx -s reload"])
|
|
||||||
return await exec_run_handler('generic', reload_exec)
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: sieve - task: list
|
|
||||||
async def container_post__exec__sieve__list(self, container_id, request_json):
|
|
||||||
if 'username' in request_json:
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
sieve_exec = await container.exec(["/bin/bash", "-c", "/usr/bin/doveadm sieve list -u '" + request_json['username'].replace("'", "'\\''") + "'"])
|
|
||||||
return await exec_run_handler('utf8_text_only', sieve_exec)
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: sieve - task: print
|
|
||||||
async def container_post__exec__sieve__print(self, container_id, request_json):
|
|
||||||
if 'username' in request_json and 'script_name' in request_json:
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
cmd = ["/bin/bash", "-c", "/usr/bin/doveadm sieve get -u '" + request_json['username'].replace("'", "'\\''") + "' '" + request_json['script_name'].replace("'", "'\\''") + "'"]
|
|
||||||
sieve_exec = await container.exec(cmd)
|
|
||||||
return await exec_run_handler('utf8_text_only', sieve_exec)
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: maildir - task: cleanup
|
|
||||||
async def container_post__exec__maildir__cleanup(self, container_id, request_json):
|
|
||||||
if 'maildir' in request_json:
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
sane_name = re.sub(r'\W+', '', request_json['maildir'])
|
|
||||||
cmd = ["/bin/bash", "-c", "if [[ -d '/var/vmail/" + request_json['maildir'].replace("'", "'\\''") + "' ]]; then /bin/mv '/var/vmail/" + request_json['maildir'].replace("'", "'\\''") + "' '/var/vmail/_garbage/" + str(int(time.time())) + "_" + sane_name + "'; fi"]
|
|
||||||
maildir_cleanup_exec = await container.exec(cmd, user='vmail')
|
|
||||||
return await exec_run_handler('generic', maildir_cleanup_exec)
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: rspamd - task: worker_password
|
|
||||||
async def container_post__exec__rspamd__worker_password(self, container_id, request_json):
|
|
||||||
if 'raw' in request_json:
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
|
|
||||||
cmd = "./set_worker_password.sh '" + request_json['raw'].replace("'", "'\\''") + "' 2> /dev/null"
|
|
||||||
rspamd_password_exec = await container.exec(cmd, user='_rspamd')
|
|
||||||
async with rspamd_password_exec.start(detach=False) as stream:
|
|
||||||
rspamd_password_return = await stream.read_out()
|
|
||||||
|
|
||||||
matched = False
|
|
||||||
if "OK" in rspamd_password_return.data.decode('utf-8'):
|
|
||||||
matched = True
|
matched = True
|
||||||
await container.restart()
|
if matched:
|
||||||
|
res = { 'type': 'success', 'msg':'mysql_upgrade: already upgraded', 'text': sql_return.output.decode('utf-8')}
|
||||||
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
else:
|
||||||
|
container.restart()
|
||||||
|
res = { 'type': 'warning', 'msg':'mysql_upgrade: upgrade was applied', 'text': sql_return.output.decode('utf-8')}
|
||||||
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
else:
|
||||||
|
res = { 'type': 'error', 'msg': 'mysql_upgrade: error running command', 'text': sql_return.output.decode('utf-8')}
|
||||||
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
# api call: container_post - post_action: exec - cmd: system - task: mysql_tzinfo_to_sql
|
||||||
|
def container_post__exec__system__mysql_tzinfo_to_sql(self, container_id, request_json):
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
sql_return = container.exec_run(["/bin/bash", "-c", "/usr/bin/mysql_tzinfo_to_sql /usr/share/zoneinfo | /bin/sed 's/Local time zone must be set--see zic manual page/FCTY/' | /usr/bin/mysql -uroot -p'" + os.environ['DBROOT'].replace("'", "'\\''") + "' mysql \n"], user='mysql')
|
||||||
|
if sql_return.exit_code == 0:
|
||||||
|
res = { 'type': 'info', 'msg': 'mysql_tzinfo_to_sql: command completed successfully', 'text': sql_return.output.decode('utf-8')}
|
||||||
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
else:
|
||||||
|
res = { 'type': 'error', 'msg': 'mysql_tzinfo_to_sql: error running command', 'text': sql_return.output.decode('utf-8')}
|
||||||
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
# api call: container_post - post_action: exec - cmd: reload - task: dovecot
|
||||||
|
def container_post__exec__reload__dovecot(self, container_id, request_json):
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
reload_return = container.exec_run(["/bin/bash", "-c", "/usr/sbin/dovecot reload"])
|
||||||
|
return exec_run_handler('generic', reload_return)
|
||||||
|
# api call: container_post - post_action: exec - cmd: reload - task: postfix
|
||||||
|
def container_post__exec__reload__postfix(self, container_id, request_json):
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
reload_return = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postfix reload"])
|
||||||
|
return exec_run_handler('generic', reload_return)
|
||||||
|
# api call: container_post - post_action: exec - cmd: reload - task: nginx
|
||||||
|
def container_post__exec__reload__nginx(self, container_id, request_json):
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
reload_return = container.exec_run(["/bin/sh", "-c", "/usr/sbin/nginx -s reload"])
|
||||||
|
return exec_run_handler('generic', reload_return)
|
||||||
|
# api call: container_post - post_action: exec - cmd: sieve - task: list
|
||||||
|
def container_post__exec__sieve__list(self, container_id, request_json):
|
||||||
|
if 'username' in request_json:
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
sieve_return = container.exec_run(["/bin/bash", "-c", "/usr/bin/doveadm sieve list -u '" + request_json['username'].replace("'", "'\\''") + "'"])
|
||||||
|
return exec_run_handler('utf8_text_only', sieve_return)
|
||||||
|
# api call: container_post - post_action: exec - cmd: sieve - task: print
|
||||||
|
def container_post__exec__sieve__print(self, container_id, request_json):
|
||||||
|
if 'username' in request_json and 'script_name' in request_json:
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
cmd = ["/bin/bash", "-c", "/usr/bin/doveadm sieve get -u '" + request_json['username'].replace("'", "'\\''") + "' '" + request_json['script_name'].replace("'", "'\\''") + "'"]
|
||||||
|
sieve_return = container.exec_run(cmd)
|
||||||
|
return exec_run_handler('utf8_text_only', sieve_return)
|
||||||
|
# api call: container_post - post_action: exec - cmd: maildir - task: cleanup
|
||||||
|
def container_post__exec__maildir__cleanup(self, container_id, request_json):
|
||||||
|
if 'maildir' in request_json:
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
sane_name = re.sub(r'\W+', '', request_json['maildir'])
|
||||||
|
vmail_name = request_json['maildir'].replace("'", "'\\''")
|
||||||
|
cmd_vmail = "if [[ -d '/var/vmail/" + vmail_name + "' ]]; then /bin/mv '/var/vmail/" + vmail_name + "' '/var/vmail/_garbage/" + str(int(time.time())) + "_" + sane_name + "'; fi"
|
||||||
|
index_name = request_json['maildir'].split("/")
|
||||||
|
if len(index_name) > 1:
|
||||||
|
index_name = index_name[1].replace("'", "'\\''") + "@" + index_name[0].replace("'", "'\\''")
|
||||||
|
cmd_vmail_index = "if [[ -d '/var/vmail_index/" + index_name + "' ]]; then /bin/mv '/var/vmail_index/" + index_name + "' '/var/vmail/_garbage/" + str(int(time.time())) + "_" + sane_name + "_index'; fi"
|
||||||
|
cmd = ["/bin/bash", "-c", cmd_vmail + " && " + cmd_vmail_index]
|
||||||
|
else:
|
||||||
|
cmd = ["/bin/bash", "-c", cmd_vmail]
|
||||||
|
maildir_cleanup = container.exec_run(cmd, user='vmail')
|
||||||
|
return exec_run_handler('generic', maildir_cleanup)
|
||||||
|
# api call: container_post - post_action: exec - cmd: rspamd - task: worker_password
|
||||||
|
def container_post__exec__rspamd__worker_password(self, container_id, request_json):
|
||||||
|
if 'raw' in request_json:
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
cmd = "/usr/bin/rspamadm pw -e -p '" + request_json['raw'].replace("'", "'\\''") + "' 2> /dev/null"
|
||||||
|
cmd_response = exec_cmd_container(container, cmd, user="_rspamd")
|
||||||
|
|
||||||
if matched:
|
matched = False
|
||||||
res = {
|
for line in cmd_response.split("\n"):
|
||||||
'type': 'success',
|
if '$2$' in line:
|
||||||
'msg': 'command completed successfully'
|
hash = line.strip()
|
||||||
}
|
hash_out = re.search('\$2\$.+$', hash).group(0)
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
rspamd_passphrase_hash = re.sub('[^0-9a-zA-Z\$]+', '', hash_out.rstrip())
|
||||||
else:
|
rspamd_password_filename = "/etc/rspamd/override.d/worker-controller-password.inc"
|
||||||
res = {
|
cmd = '''/bin/echo 'enable_password = "%s";' > %s && cat %s''' % (rspamd_passphrase_hash, rspamd_password_filename, rspamd_password_filename)
|
||||||
'type': 'danger',
|
cmd_response = exec_cmd_container(container, cmd, user="_rspamd")
|
||||||
'msg': 'command did not complete'
|
if rspamd_passphrase_hash.startswith("$2$") and rspamd_passphrase_hash in cmd_response:
|
||||||
}
|
container.restart()
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
matched = True
|
||||||
|
if matched:
|
||||||
|
res = { 'type': 'success', 'msg': 'command completed successfully' }
|
||||||
|
logger.info('success changing Rspamd password')
|
||||||
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
else:
|
||||||
|
logger.error('failed changing Rspamd password')
|
||||||
|
res = { 'type': 'danger', 'msg': 'command did not complete' }
|
||||||
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
|
||||||
|
|
||||||
|
def exec_cmd_container(container, cmd, user, timeout=2, shell_cmd="/bin/bash"):
|
||||||
|
|
||||||
async def exec_run_handler(type, exec_obj):
|
def recv_socket_data(c_socket, timeout):
|
||||||
async with exec_obj.start(detach=False) as stream:
|
c_socket.setblocking(0)
|
||||||
exec_return = await stream.read_out()
|
total_data=[]
|
||||||
|
data=''
|
||||||
|
begin=time.time()
|
||||||
|
while True:
|
||||||
|
if total_data and time.time()-begin > timeout:
|
||||||
|
break
|
||||||
|
elif time.time()-begin > timeout*2:
|
||||||
|
break
|
||||||
|
try:
|
||||||
|
data = c_socket.recv(8192)
|
||||||
|
if data:
|
||||||
|
total_data.append(data.decode('utf-8'))
|
||||||
|
#change the beginning time for measurement
|
||||||
|
begin=time.time()
|
||||||
|
else:
|
||||||
|
#sleep for sometime to indicate a gap
|
||||||
|
time.sleep(0.1)
|
||||||
|
break
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return ''.join(total_data)
|
||||||
|
|
||||||
if exec_return == None:
|
|
||||||
exec_return = ""
|
|
||||||
else:
|
|
||||||
exec_return = exec_return.data.decode('utf-8')
|
|
||||||
|
|
||||||
|
try :
|
||||||
|
socket = container.exec_run([shell_cmd], stdin=True, socket=True, user=user).output._sock
|
||||||
|
if not cmd.endswith("\n"):
|
||||||
|
cmd = cmd + "\n"
|
||||||
|
socket.send(cmd.encode('utf-8'))
|
||||||
|
data = recv_socket_data(socket, timeout)
|
||||||
|
socket.close()
|
||||||
|
return data
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("error - exec_cmd_container: %s" % str(e))
|
||||||
|
traceback.print_exc(file=sys.stdout)
|
||||||
|
def exec_run_handler(type, output):
|
||||||
if type == 'generic':
|
if type == 'generic':
|
||||||
exec_details = await exec_obj.inspect()
|
if output.exit_code == 0:
|
||||||
if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
|
res = { 'type': 'success', 'msg': 'command completed successfully' }
|
||||||
res = {
|
|
||||||
"type": "success",
|
|
||||||
"msg": "command completed successfully"
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
else:
|
else:
|
||||||
res = {
|
res = { 'type': 'danger', 'msg': 'command failed: ' + output.output.decode('utf-8') }
|
||||||
"type": "success",
|
|
||||||
"msg": "'command failed: " + exec_return
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
if type == 'utf8_text_only':
|
if type == 'utf8_text_only':
|
||||||
return Response(content=exec_return, media_type="text/plain")
|
return Response(content=output.output.decode('utf-8'), media_type="text/plain")
|
||||||
|
|
||||||
async def get_host_stats(wait=5):
|
async def get_host_stats(wait=5):
|
||||||
global host_stats_isUpdating
|
global host_stats_isUpdating
|
||||||
@@ -561,7 +486,8 @@ async def get_host_stats(wait=5):
|
|||||||
"swap": psutil.swap_memory()
|
"swap": psutil.swap_memory()
|
||||||
},
|
},
|
||||||
"uptime": time.time() - psutil.boot_time(),
|
"uptime": time.time() - psutil.boot_time(),
|
||||||
"system_time": system_time.strftime("%d.%m.%Y %H:%M:%S")
|
"system_time": system_time.strftime("%d.%m.%Y %H:%M:%S"),
|
||||||
|
"architecture": platform.machine()
|
||||||
}
|
}
|
||||||
|
|
||||||
redis_client.set('host_stats', json.dumps(host_stats), ex=10)
|
redis_client.set('host_stats', json.dumps(host_stats), ex=10)
|
||||||
@@ -570,12 +496,10 @@ async def get_host_stats(wait=5):
|
|||||||
"type": "danger",
|
"type": "danger",
|
||||||
"msg": str(e)
|
"msg": str(e)
|
||||||
}
|
}
|
||||||
print(json.dumps(res, indent=4))
|
|
||||||
|
|
||||||
await asyncio.sleep(wait)
|
await asyncio.sleep(wait)
|
||||||
host_stats_isUpdating = False
|
host_stats_isUpdating = False
|
||||||
|
|
||||||
|
|
||||||
async def get_container_stats(container_id, wait=5, stop=False):
|
async def get_container_stats(container_id, wait=5, stop=False):
|
||||||
global containerIds_to_update
|
global containerIds_to_update
|
||||||
|
|
||||||
@@ -598,13 +522,11 @@ async def get_container_stats(container_id, wait=5, stop=False):
|
|||||||
"type": "danger",
|
"type": "danger",
|
||||||
"msg": str(e)
|
"msg": str(e)
|
||||||
}
|
}
|
||||||
print(json.dumps(res, indent=4))
|
|
||||||
else:
|
else:
|
||||||
res = {
|
res = {
|
||||||
"type": "danger",
|
"type": "danger",
|
||||||
"msg": "no or invalid id defined"
|
"msg": "no or invalid id defined"
|
||||||
}
|
}
|
||||||
print(json.dumps(res, indent=4))
|
|
||||||
|
|
||||||
await asyncio.sleep(wait)
|
await asyncio.sleep(wait)
|
||||||
if stop == True:
|
if stop == True:
|
||||||
@@ -615,9 +537,13 @@ async def get_container_stats(container_id, wait=5, stop=False):
|
|||||||
await get_container_stats(container_id, wait=0, stop=True)
|
await get_container_stats(container_id, wait=0, stop=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if os.environ['REDIS_SLAVEOF_IP'] != "":
|
if os.environ['REDIS_SLAVEOF_IP'] != "":
|
||||||
redis_client = redis.Redis(host=os.environ['REDIS_SLAVEOF_IP'], port=os.environ['REDIS_SLAVEOF_PORT'], db=0)
|
redis_client = redis.Redis(host=os.environ['REDIS_SLAVEOF_IP'], port=os.environ['REDIS_SLAVEOF_PORT'], db=0)
|
||||||
else:
|
else:
|
||||||
redis_client = redis.Redis(host='redis-mailcow', port=6379, db=0)
|
redis_client = redis.Redis(host='redis-mailcow', port=6379, db=0)
|
||||||
|
|
||||||
|
sync_docker_client = docker.DockerClient(base_url='unix://var/run/docker.sock', version='auto')
|
||||||
async_docker_client = aiodocker.Docker(url='unix:///var/run/docker.sock')
|
async_docker_client = aiodocker.Docker(url='unix:///var/run/docker.sock')
|
||||||
|
|
||||||
|
logger.info('DockerApi started')
|
||||||
|
@@ -2,9 +2,12 @@ FROM debian:bullseye-slim
|
|||||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
ARG DOVECOT=2.3.19.1
|
# renovate: datasource=github-tags depName=dovecot/core versioning=semver-coerced
|
||||||
|
ARG DOVECOT=2.3.20
|
||||||
|
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced
|
||||||
|
ARG GOSU_VERSION=1.16
|
||||||
ENV LC_ALL C
|
ENV LC_ALL C
|
||||||
ENV GOSU_VERSION 1.14
|
|
||||||
|
|
||||||
# Add groups and users before installing Dovecot to not break compatibility
|
# Add groups and users before installing Dovecot to not break compatibility
|
||||||
RUN groupadd -g 5000 vmail \
|
RUN groupadd -g 5000 vmail \
|
||||||
@@ -18,6 +21,7 @@ RUN groupadd -g 5000 vmail \
|
|||||||
&& touch /etc/default/locale \
|
&& touch /etc/default/locale \
|
||||||
&& apt-get update \
|
&& apt-get update \
|
||||||
&& apt-get -y --no-install-recommends install \
|
&& apt-get -y --no-install-recommends install \
|
||||||
|
build-essential \
|
||||||
apt-transport-https \
|
apt-transport-https \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
cpanminus \
|
cpanminus \
|
||||||
@@ -58,6 +62,7 @@ RUN groupadd -g 5000 vmail \
|
|||||||
libproc-processtable-perl \
|
libproc-processtable-perl \
|
||||||
libreadonly-perl \
|
libreadonly-perl \
|
||||||
libregexp-common-perl \
|
libregexp-common-perl \
|
||||||
|
libssl-dev \
|
||||||
libsys-meminfo-perl \
|
libsys-meminfo-perl \
|
||||||
libterm-readkey-perl \
|
libterm-readkey-perl \
|
||||||
libtest-deep-perl \
|
libtest-deep-perl \
|
||||||
@@ -107,6 +112,8 @@ RUN groupadd -g 5000 vmail \
|
|||||||
&& apt-get autoclean \
|
&& apt-get autoclean \
|
||||||
&& rm -rf /var/lib/apt/lists/* \
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
&& rm -rf /tmp/* /var/tmp/* /root/.cache/
|
&& rm -rf /tmp/* /var/tmp/* /root/.cache/
|
||||||
|
# imapsync dependencies
|
||||||
|
RUN cpan Crypt::OpenSSL::PKCS12
|
||||||
|
|
||||||
COPY trim_logs.sh /usr/local/bin/trim_logs.sh
|
COPY trim_logs.sh /usr/local/bin/trim_logs.sh
|
||||||
COPY clean_q_aged.sh /usr/local/bin/clean_q_aged.sh
|
COPY clean_q_aged.sh /usr/local/bin/clean_q_aged.sh
|
||||||
|
@@ -8492,6 +8492,7 @@ sub xoauth2
|
|||||||
require HTML::Entities ;
|
require HTML::Entities ;
|
||||||
require JSON ;
|
require JSON ;
|
||||||
require JSON::WebToken::Crypt::RSA ;
|
require JSON::WebToken::Crypt::RSA ;
|
||||||
|
require Crypt::OpenSSL::PKCS12;
|
||||||
require Crypt::OpenSSL::RSA ;
|
require Crypt::OpenSSL::RSA ;
|
||||||
require Encode::Byte ;
|
require Encode::Byte ;
|
||||||
require IO::Socket::SSL ;
|
require IO::Socket::SSL ;
|
||||||
@@ -8532,8 +8533,9 @@ sub xoauth2
|
|||||||
|
|
||||||
$sync->{ debug } and myprint( "Service account: $iss\nKey file: $keyfile\nKey password: $keypass\n");
|
$sync->{ debug } and myprint( "Service account: $iss\nKey file: $keyfile\nKey password: $keypass\n");
|
||||||
|
|
||||||
# Get private key from p12 file (would be better in perl...)
|
# Get private key from p12 file
|
||||||
$key = `openssl pkcs12 -in "$keyfile" -nodes -nocerts -passin pass:$keypass -nomacver`;
|
my $pkcs12 = Crypt::OpenSSL::PKCS12->new_from_file($keyfile);
|
||||||
|
$key = $pkcs12->private_key($keypass);
|
||||||
|
|
||||||
$sync->{ debug } and myprint( "Private key:\n$key\n");
|
$sync->{ debug } and myprint( "Private key:\n$key\n");
|
||||||
}
|
}
|
||||||
|
@@ -64,28 +64,40 @@ def refreshF2boptions():
|
|||||||
global f2boptions
|
global f2boptions
|
||||||
global quit_now
|
global quit_now
|
||||||
global exit_code
|
global exit_code
|
||||||
|
|
||||||
|
f2boptions = {}
|
||||||
|
|
||||||
if not r.get('F2B_OPTIONS'):
|
if not r.get('F2B_OPTIONS'):
|
||||||
f2boptions = {}
|
f2boptions['ban_time'] = r.get('F2B_BAN_TIME')
|
||||||
f2boptions['ban_time'] = int
|
f2boptions['max_ban_time'] = r.get('F2B_MAX_BAN_TIME')
|
||||||
f2boptions['max_attempts'] = int
|
f2boptions['ban_time_increment'] = r.get('F2B_BAN_TIME_INCREMENT')
|
||||||
f2boptions['retry_window'] = int
|
f2boptions['max_attempts'] = r.get('F2B_MAX_ATTEMPTS')
|
||||||
f2boptions['netban_ipv4'] = int
|
f2boptions['retry_window'] = r.get('F2B_RETRY_WINDOW')
|
||||||
f2boptions['netban_ipv6'] = int
|
f2boptions['netban_ipv4'] = r.get('F2B_NETBAN_IPV4')
|
||||||
f2boptions['ban_time'] = r.get('F2B_BAN_TIME') or 1800
|
f2boptions['netban_ipv6'] = r.get('F2B_NETBAN_IPV6')
|
||||||
f2boptions['max_attempts'] = r.get('F2B_MAX_ATTEMPTS') or 10
|
|
||||||
f2boptions['retry_window'] = r.get('F2B_RETRY_WINDOW') or 600
|
|
||||||
f2boptions['netban_ipv4'] = r.get('F2B_NETBAN_IPV4') or 32
|
|
||||||
f2boptions['netban_ipv6'] = r.get('F2B_NETBAN_IPV6') or 128
|
|
||||||
r.set('F2B_OPTIONS', json.dumps(f2boptions, ensure_ascii=False))
|
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
f2boptions = {}
|
|
||||||
f2boptions = json.loads(r.get('F2B_OPTIONS'))
|
f2boptions = json.loads(r.get('F2B_OPTIONS'))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
print('Error loading F2B options: F2B_OPTIONS is not json')
|
print('Error loading F2B options: F2B_OPTIONS is not json')
|
||||||
quit_now = True
|
quit_now = True
|
||||||
exit_code = 2
|
exit_code = 2
|
||||||
|
|
||||||
|
verifyF2boptions(f2boptions)
|
||||||
|
r.set('F2B_OPTIONS', json.dumps(f2boptions, ensure_ascii=False))
|
||||||
|
|
||||||
|
def verifyF2boptions(f2boptions):
|
||||||
|
verifyF2boption(f2boptions,'ban_time', 1800)
|
||||||
|
verifyF2boption(f2boptions,'max_ban_time', 10000)
|
||||||
|
verifyF2boption(f2boptions,'ban_time_increment', True)
|
||||||
|
verifyF2boption(f2boptions,'max_attempts', 10)
|
||||||
|
verifyF2boption(f2boptions,'retry_window', 600)
|
||||||
|
verifyF2boption(f2boptions,'netban_ipv4', 32)
|
||||||
|
verifyF2boption(f2boptions,'netban_ipv6', 128)
|
||||||
|
|
||||||
|
def verifyF2boption(f2boptions, f2boption, f2bdefault):
|
||||||
|
f2boptions[f2boption] = f2boptions[f2boption] if f2boption in f2boptions and f2boptions[f2boption] is not None else f2bdefault
|
||||||
|
|
||||||
def refreshF2bregex():
|
def refreshF2bregex():
|
||||||
global f2bregex
|
global f2bregex
|
||||||
global quit_now
|
global quit_now
|
||||||
@@ -147,6 +159,7 @@ def ban(address):
|
|||||||
global lock
|
global lock
|
||||||
refreshF2boptions()
|
refreshF2boptions()
|
||||||
BAN_TIME = int(f2boptions['ban_time'])
|
BAN_TIME = int(f2boptions['ban_time'])
|
||||||
|
BAN_TIME_INCREMENT = bool(f2boptions['ban_time_increment'])
|
||||||
MAX_ATTEMPTS = int(f2boptions['max_attempts'])
|
MAX_ATTEMPTS = int(f2boptions['max_attempts'])
|
||||||
RETRY_WINDOW = int(f2boptions['retry_window'])
|
RETRY_WINDOW = int(f2boptions['retry_window'])
|
||||||
NETBAN_IPV4 = '/' + str(f2boptions['netban_ipv4'])
|
NETBAN_IPV4 = '/' + str(f2boptions['netban_ipv4'])
|
||||||
@@ -174,20 +187,16 @@ def ban(address):
|
|||||||
net = ipaddress.ip_network((address + (NETBAN_IPV4 if type(ip) is ipaddress.IPv4Address else NETBAN_IPV6)), strict=False)
|
net = ipaddress.ip_network((address + (NETBAN_IPV4 if type(ip) is ipaddress.IPv4Address else NETBAN_IPV6)), strict=False)
|
||||||
net = str(net)
|
net = str(net)
|
||||||
|
|
||||||
if not net in bans or time.time() - bans[net]['last_attempt'] > RETRY_WINDOW:
|
if not net in bans:
|
||||||
bans[net] = { 'attempts': 0 }
|
bans[net] = {'attempts': 0, 'last_attempt': 0, 'ban_counter': 0}
|
||||||
active_window = RETRY_WINDOW
|
|
||||||
else:
|
|
||||||
active_window = time.time() - bans[net]['last_attempt']
|
|
||||||
|
|
||||||
bans[net]['attempts'] += 1
|
bans[net]['attempts'] += 1
|
||||||
bans[net]['last_attempt'] = time.time()
|
bans[net]['last_attempt'] = time.time()
|
||||||
|
|
||||||
active_window = time.time() - bans[net]['last_attempt']
|
|
||||||
|
|
||||||
if bans[net]['attempts'] >= MAX_ATTEMPTS:
|
if bans[net]['attempts'] >= MAX_ATTEMPTS:
|
||||||
cur_time = int(round(time.time()))
|
cur_time = int(round(time.time()))
|
||||||
logCrit('Banning %s for %d minutes' % (net, BAN_TIME / 60))
|
NET_BAN_TIME = BAN_TIME if not BAN_TIME_INCREMENT else BAN_TIME * 2 ** bans[net]['ban_counter']
|
||||||
|
logCrit('Banning %s for %d minutes' % (net, NET_BAN_TIME / 60 ))
|
||||||
if type(ip) is ipaddress.IPv4Address:
|
if type(ip) is ipaddress.IPv4Address:
|
||||||
with lock:
|
with lock:
|
||||||
chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), 'MAILCOW')
|
chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), 'MAILCOW')
|
||||||
@@ -206,7 +215,7 @@ def ban(address):
|
|||||||
rule.target = target
|
rule.target = target
|
||||||
if rule not in chain.rules:
|
if rule not in chain.rules:
|
||||||
chain.insert_rule(rule)
|
chain.insert_rule(rule)
|
||||||
r.hset('F2B_ACTIVE_BANS', '%s' % net, cur_time + BAN_TIME)
|
r.hset('F2B_ACTIVE_BANS', '%s' % net, cur_time + NET_BAN_TIME)
|
||||||
else:
|
else:
|
||||||
logWarn('%d more attempts in the next %d seconds until %s is banned' % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net))
|
logWarn('%d more attempts in the next %d seconds until %s is banned' % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net))
|
||||||
|
|
||||||
@@ -238,7 +247,8 @@ def unban(net):
|
|||||||
r.hdel('F2B_ACTIVE_BANS', '%s' % net)
|
r.hdel('F2B_ACTIVE_BANS', '%s' % net)
|
||||||
r.hdel('F2B_QUEUE_UNBAN', '%s' % net)
|
r.hdel('F2B_QUEUE_UNBAN', '%s' % net)
|
||||||
if net in bans:
|
if net in bans:
|
||||||
del bans[net]
|
bans[net]['attempts'] = 0
|
||||||
|
bans[net]['ban_counter'] += 1
|
||||||
|
|
||||||
def permBan(net, unban=False):
|
def permBan(net, unban=False):
|
||||||
global lock
|
global lock
|
||||||
@@ -332,7 +342,7 @@ def watch():
|
|||||||
logWarn('%s matched rule id %s (%s)' % (addr, rule_id, item['data']))
|
logWarn('%s matched rule id %s (%s)' % (addr, rule_id, item['data']))
|
||||||
ban(addr)
|
ban(addr)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logWarn('Error reading log line from pubsub')
|
logWarn('Error reading log line from pubsub: %s' % ex)
|
||||||
quit_now = True
|
quit_now = True
|
||||||
exit_code = 2
|
exit_code = 2
|
||||||
|
|
||||||
@@ -359,21 +369,30 @@ def snat4(snat_target):
|
|||||||
chain = iptc.Chain(table, 'POSTROUTING')
|
chain = iptc.Chain(table, 'POSTROUTING')
|
||||||
table.autocommit = False
|
table.autocommit = False
|
||||||
new_rule = get_snat4_rule()
|
new_rule = get_snat4_rule()
|
||||||
for position, rule in enumerate(chain.rules):
|
|
||||||
match = all((
|
if not chain.rules:
|
||||||
new_rule.get_src() == rule.get_src(),
|
# if there are no rules in the chain, insert the new rule directly
|
||||||
new_rule.get_dst() == rule.get_dst(),
|
logInfo(f'Added POSTROUTING rule for source network {new_rule.src} to SNAT target {snat_target}')
|
||||||
new_rule.target.parameters == rule.target.parameters,
|
chain.insert_rule(new_rule)
|
||||||
new_rule.target.name == rule.target.name
|
else:
|
||||||
))
|
for position, rule in enumerate(chain.rules):
|
||||||
if position == 0:
|
if not hasattr(rule.target, 'parameter'):
|
||||||
if not match:
|
continue
|
||||||
logInfo(f'Added POSTROUTING rule for source network {new_rule.src} to SNAT target {snat_target}')
|
match = all((
|
||||||
chain.insert_rule(new_rule)
|
new_rule.get_src() == rule.get_src(),
|
||||||
else:
|
new_rule.get_dst() == rule.get_dst(),
|
||||||
if match:
|
new_rule.target.parameters == rule.target.parameters,
|
||||||
logInfo(f'Remove rule for source network {new_rule.src} to SNAT target {snat_target} from POSTROUTING chain at position {position}')
|
new_rule.target.name == rule.target.name
|
||||||
chain.delete_rule(rule)
|
))
|
||||||
|
if position == 0:
|
||||||
|
if not match:
|
||||||
|
logInfo(f'Added POSTROUTING rule for source network {new_rule.src} to SNAT target {snat_target}')
|
||||||
|
chain.insert_rule(new_rule)
|
||||||
|
else:
|
||||||
|
if match:
|
||||||
|
logInfo(f'Remove rule for source network {new_rule.src} to SNAT target {snat_target} from POSTROUTING chain at position {position}')
|
||||||
|
chain.delete_rule(rule)
|
||||||
|
|
||||||
table.commit()
|
table.commit()
|
||||||
table.autocommit = True
|
table.autocommit = True
|
||||||
except:
|
except:
|
||||||
@@ -418,6 +437,8 @@ def autopurge():
|
|||||||
time.sleep(10)
|
time.sleep(10)
|
||||||
refreshF2boptions()
|
refreshF2boptions()
|
||||||
BAN_TIME = int(f2boptions['ban_time'])
|
BAN_TIME = int(f2boptions['ban_time'])
|
||||||
|
MAX_BAN_TIME = int(f2boptions['max_ban_time'])
|
||||||
|
BAN_TIME_INCREMENT = bool(f2boptions['ban_time_increment'])
|
||||||
MAX_ATTEMPTS = int(f2boptions['max_attempts'])
|
MAX_ATTEMPTS = int(f2boptions['max_attempts'])
|
||||||
QUEUE_UNBAN = r.hgetall('F2B_QUEUE_UNBAN')
|
QUEUE_UNBAN = r.hgetall('F2B_QUEUE_UNBAN')
|
||||||
if QUEUE_UNBAN:
|
if QUEUE_UNBAN:
|
||||||
@@ -425,7 +446,9 @@ def autopurge():
|
|||||||
unban(str(net))
|
unban(str(net))
|
||||||
for net in bans.copy():
|
for net in bans.copy():
|
||||||
if bans[net]['attempts'] >= MAX_ATTEMPTS:
|
if bans[net]['attempts'] >= MAX_ATTEMPTS:
|
||||||
if time.time() - bans[net]['last_attempt'] > BAN_TIME:
|
NET_BAN_TIME = BAN_TIME if not BAN_TIME_INCREMENT else BAN_TIME * 2 ** bans[net]['ban_counter']
|
||||||
|
TIME_SINCE_LAST_ATTEMPT = time.time() - bans[net]['last_attempt']
|
||||||
|
if TIME_SINCE_LAST_ATTEMPT > NET_BAN_TIME or TIME_SINCE_LAST_ATTEMPT > MAX_BAN_TIME:
|
||||||
unban(net)
|
unban(net)
|
||||||
|
|
||||||
def isIpNetwork(address):
|
def isIpNetwork(address):
|
||||||
|
@@ -1,12 +1,18 @@
|
|||||||
FROM php:8.1-fpm-alpine3.17
|
FROM php:8.2-fpm-alpine3.17
|
||||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||||
|
|
||||||
ENV APCU_PECL 5.1.22
|
# renovate: datasource=github-tags depName=krakjoe/apcu versioning=semver-coerced
|
||||||
ENV IMAGICK_PECL 3.7.0
|
ARG APCU_PECL_VERSION=5.1.22
|
||||||
ENV MAILPARSE_PECL 3.1.4
|
# renovate: datasource=github-tags depName=Imagick/imagick versioning=semver-coerced
|
||||||
ENV MEMCACHED_PECL 3.2.0
|
ARG IMAGICK_PECL_VERSION=3.7.0
|
||||||
ENV REDIS_PECL 5.3.7
|
# renovate: datasource=github-tags depName=php/pecl-mail-mailparse versioning=semver-coerced
|
||||||
ENV COMPOSER 2.4.4
|
ARG MAILPARSE_PECL_VERSION=3.1.4
|
||||||
|
# renovate: datasource=github-tags depName=php-memcached-dev/php-memcached versioning=semver-coerced
|
||||||
|
ARG MEMCACHED_PECL_VERSION=3.2.0
|
||||||
|
# renovate: datasource=github-tags depName=phpredis/phpredis versioning=semver-coerced
|
||||||
|
ARG REDIS_PECL_VERSION=5.3.7
|
||||||
|
# renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced
|
||||||
|
ARG COMPOSER_VERSION=2.5.5
|
||||||
|
|
||||||
RUN apk add -U --no-cache autoconf \
|
RUN apk add -U --no-cache autoconf \
|
||||||
aspell-dev \
|
aspell-dev \
|
||||||
@@ -46,6 +52,7 @@ RUN apk add -U --no-cache autoconf \
|
|||||||
libxpm-dev \
|
libxpm-dev \
|
||||||
libzip \
|
libzip \
|
||||||
libzip-dev \
|
libzip-dev \
|
||||||
|
linux-headers \
|
||||||
make \
|
make \
|
||||||
mysql-client \
|
mysql-client \
|
||||||
openldap-dev \
|
openldap-dev \
|
||||||
@@ -55,11 +62,11 @@ RUN apk add -U --no-cache autoconf \
|
|||||||
samba-client \
|
samba-client \
|
||||||
zlib-dev \
|
zlib-dev \
|
||||||
tzdata \
|
tzdata \
|
||||||
&& pecl install mailparse-${MAILPARSE_PECL} \
|
&& pecl install APCu-${APCU_PECL_VERSION} \
|
||||||
&& pecl install redis-${REDIS_PECL} \
|
&& pecl install imagick-${IMAGICK_PECL_VERSION} \
|
||||||
&& pecl install memcached-${MEMCACHED_PECL} \
|
&& pecl install mailparse-${MAILPARSE_PECL_VERSION} \
|
||||||
&& pecl install APCu-${APCU_PECL} \
|
&& pecl install memcached-${MEMCACHED_PECL_VERSION} \
|
||||||
&& pecl install imagick-${IMAGICK_PECL} \
|
&& pecl install redis-${REDIS_PECL_VERSION} \
|
||||||
&& docker-php-ext-enable apcu imagick memcached mailparse redis \
|
&& docker-php-ext-enable apcu imagick memcached mailparse redis \
|
||||||
&& pecl clear-cache \
|
&& pecl clear-cache \
|
||||||
&& docker-php-ext-configure intl \
|
&& docker-php-ext-configure intl \
|
||||||
@@ -69,10 +76,10 @@ RUN apk add -U --no-cache autoconf \
|
|||||||
--with-webp \
|
--with-webp \
|
||||||
--with-xpm \
|
--with-xpm \
|
||||||
--with-avif \
|
--with-avif \
|
||||||
&& docker-php-ext-install -j 4 exif gd gettext intl ldap opcache pcntl pdo pdo_mysql pspell soap sockets zip bcmath gmp \
|
&& docker-php-ext-install -j 4 exif gd gettext intl ldap opcache pcntl pdo pdo_mysql pspell soap sockets sysvsem zip bcmath gmp \
|
||||||
&& docker-php-ext-configure imap --with-imap --with-imap-ssl \
|
&& docker-php-ext-configure imap --with-imap --with-imap-ssl \
|
||||||
&& docker-php-ext-install -j 4 imap \
|
&& docker-php-ext-install -j 4 imap \
|
||||||
&& curl --silent --show-error https://getcomposer.org/installer | php -- --version=${COMPOSER} \
|
&& curl --silent --show-error https://getcomposer.org/installer | php -- --version=${COMPOSER_VERSION} \
|
||||||
&& mv composer.phar /usr/local/bin/composer \
|
&& mv composer.phar /usr/local/bin/composer \
|
||||||
&& chmod +x /usr/local/bin/composer \
|
&& chmod +x /usr/local/bin/composer \
|
||||||
&& apk del --purge autoconf \
|
&& apk del --purge autoconf \
|
||||||
@@ -93,6 +100,7 @@ RUN apk add -U --no-cache autoconf \
|
|||||||
libxml2-dev \
|
libxml2-dev \
|
||||||
libxpm-dev \
|
libxpm-dev \
|
||||||
libzip-dev \
|
libzip-dev \
|
||||||
|
linux-headers \
|
||||||
make \
|
make \
|
||||||
openldap-dev \
|
openldap-dev \
|
||||||
pcre-dev \
|
pcre-dev \
|
||||||
|
@@ -172,6 +172,24 @@ BEGIN
|
|||||||
END;
|
END;
|
||||||
//
|
//
|
||||||
DELIMITER ;
|
DELIMITER ;
|
||||||
|
DROP EVENT IF EXISTS clean_sasl_log;
|
||||||
|
DELIMITER //
|
||||||
|
CREATE EVENT clean_sasl_log
|
||||||
|
ON SCHEDULE EVERY 1 DAY DO
|
||||||
|
BEGIN
|
||||||
|
DELETE sasl_log.* FROM sasl_log
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT username, service, MAX(datetime) AS lastdate
|
||||||
|
FROM sasl_log
|
||||||
|
GROUP BY username, service
|
||||||
|
) AS last ON sasl_log.username = last.username AND sasl_log.service = last.service
|
||||||
|
WHERE datetime < DATE_SUB(NOW(), INTERVAL 31 DAY) AND datetime < lastdate;
|
||||||
|
DELETE FROM sasl_log
|
||||||
|
WHERE username NOT IN (SELECT username FROM mailbox) AND
|
||||||
|
datetime < DATE_SUB(NOW(), INTERVAL 31 DAY);
|
||||||
|
END;
|
||||||
|
//
|
||||||
|
DELIMITER ;
|
||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@@ -3,8 +3,9 @@ LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
|||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
ARG SOGO_DEBIAN_REPOSITORY=http://packages.sogo.nu/nightly/5/debian/
|
ARG SOGO_DEBIAN_REPOSITORY=http://packages.sogo.nu/nightly/5/debian/
|
||||||
|
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced
|
||||||
|
ARG GOSU_VERSION=1.16
|
||||||
ENV LC_ALL C
|
ENV LC_ALL C
|
||||||
ENV GOSU_VERSION 1.14
|
|
||||||
|
|
||||||
# Prerequisites
|
# Prerequisites
|
||||||
RUN echo "Building from repository $SOGO_DEBIAN_REPOSITORY" \
|
RUN echo "Building from repository $SOGO_DEBIAN_REPOSITORY" \
|
||||||
|
@@ -2,7 +2,8 @@ FROM solr:7.7-slim
|
|||||||
|
|
||||||
USER root
|
USER root
|
||||||
|
|
||||||
ENV GOSU_VERSION 1.11
|
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced
|
||||||
|
ARG GOSU_VERSION=1.16
|
||||||
|
|
||||||
COPY solr.sh /
|
COPY solr.sh /
|
||||||
COPY solr-config-7.7.0.xml /
|
COPY solr-config-7.7.0.xml /
|
||||||
|
@@ -24,7 +24,7 @@ server {
|
|||||||
add_header X-Download-Options "noopen" always;
|
add_header X-Download-Options "noopen" always;
|
||||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
add_header X-Permitted-Cross-Domain-Policies "none" always;
|
add_header X-Permitted-Cross-Domain-Policies "none" always;
|
||||||
add_header X-Robots-Tag "none" always;
|
add_header X-Robots-Tag "noindex, nofollow" always;
|
||||||
add_header X-XSS-Protection "1; mode=block" always;
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
|
||||||
fastcgi_hide_header X-Powered-By;
|
fastcgi_hide_header X-Powered-By;
|
||||||
|
@@ -24,6 +24,11 @@ mail_plugins = </etc/dovecot/mail_plugins
|
|||||||
mail_attachment_fs = crypt:set_prefix=mail_crypt_global:posix:
|
mail_attachment_fs = crypt:set_prefix=mail_crypt_global:posix:
|
||||||
mail_attachment_dir = /var/attachments
|
mail_attachment_dir = /var/attachments
|
||||||
mail_attachment_min_size = 128k
|
mail_attachment_min_size = 128k
|
||||||
|
# Significantly speeds up very large mailboxes, but is only safe to enable if
|
||||||
|
# you do not manually modify the files in the `cur` directories in
|
||||||
|
# mailcowdockerized_vmail-vol-1.
|
||||||
|
# https://docs.mailcow.email/manual-guides/Dovecot/u_e-dovecot-performance/
|
||||||
|
maildir_very_dirty_syncs = yes
|
||||||
|
|
||||||
# Dovecot 2.2
|
# Dovecot 2.2
|
||||||
#ssl_protocols = !SSLv3
|
#ssl_protocols = !SSLv3
|
||||||
|
@@ -28,3 +28,4 @@
|
|||||||
#197695 2 #Domain names registrar REG.RU Ltd, Russia
|
#197695 2 #Domain names registrar REG.RU Ltd, Russia
|
||||||
#198068 2 #P.A.G.M. OU, Estonia
|
#198068 2 #P.A.G.M. OU, Estonia
|
||||||
#201942 5 #Soltia Consulting SL, Spain
|
#201942 5 #Soltia Consulting SL, Spain
|
||||||
|
#213373 4 #IP Connect Inc
|
@@ -8,7 +8,7 @@ VIRUS_FOUND {
|
|||||||
}
|
}
|
||||||
# Bad policy from free mail providers
|
# Bad policy from free mail providers
|
||||||
FREEMAIL_POLICY_FAILURE {
|
FREEMAIL_POLICY_FAILURE {
|
||||||
expression = "-g+:policies & !DMARC_POLICY_ALLOW & !MAILLIST & ( FREEMAIL_ENVFROM | FREEMAIL_FROM ) & !WHITELISTED_FWD_HOST";
|
expression = "FREEMAIL_FROM & !DMARC_POLICY_ALLOW & !MAILLIST& !WHITELISTED_FWD_HOST & -g+:policies";
|
||||||
score = 16.0;
|
score = 16.0;
|
||||||
}
|
}
|
||||||
# Applies to freemail with undisclosed recipients
|
# Applies to freemail with undisclosed recipients
|
||||||
|
@@ -159,8 +159,8 @@ BAZAAR_ABUSE_CH {
|
|||||||
}
|
}
|
||||||
|
|
||||||
URLHAUS_ABUSE_CH {
|
URLHAUS_ABUSE_CH {
|
||||||
type = "url";
|
type = "selector";
|
||||||
filter = "full";
|
selector = "urls";
|
||||||
map = "https://urlhaus.abuse.ch/downloads/text_online/";
|
map = "https://urlhaus.abuse.ch/downloads/text_online/";
|
||||||
score = 10.0;
|
score = 10.0;
|
||||||
}
|
}
|
||||||
@@ -175,7 +175,7 @@ BAD_SUBJECT_00 {
|
|||||||
type = "header";
|
type = "header";
|
||||||
header = "subject";
|
header = "subject";
|
||||||
regexp = true;
|
regexp = true;
|
||||||
map = "http://nullnull.org/bad-subject-regex.txt";
|
map = "http://fuzzy.mailcow.email/bad-subject-regex.txt";
|
||||||
score = 6.0;
|
score = 6.0;
|
||||||
symbols_set = ["BAD_SUBJECT_00"];
|
symbols_set = ["BAD_SUBJECT_00"];
|
||||||
}
|
}
|
||||||
|
@@ -62,7 +62,7 @@
|
|||||||
SOGoFirstDayOfWeek = "1";
|
SOGoFirstDayOfWeek = "1";
|
||||||
|
|
||||||
SOGoSieveFolderEncoding = "UTF-8";
|
SOGoSieveFolderEncoding = "UTF-8";
|
||||||
SOGoPasswordChangeEnabled = YES;
|
SOGoPasswordChangeEnabled = NO;
|
||||||
SOGoSentFolderName = "Sent";
|
SOGoSentFolderName = "Sent";
|
||||||
SOGoMailShowSubscribedFoldersOnly = NO;
|
SOGoMailShowSubscribedFoldersOnly = NO;
|
||||||
NGImap4ConnectionStringSeparator = "/";
|
NGImap4ConnectionStringSeparator = "/";
|
||||||
|
@@ -103,6 +103,7 @@ $template_data = [
|
|||||||
'rsettings' => $rsettings,
|
'rsettings' => $rsettings,
|
||||||
'rspamd_regex_maps' => $rspamd_regex_maps,
|
'rspamd_regex_maps' => $rspamd_regex_maps,
|
||||||
'logo_specs' => customize('get', 'main_logo_specs'),
|
'logo_specs' => customize('get', 'main_logo_specs'),
|
||||||
|
'ip_check' => customize('get', 'ip_check'),
|
||||||
'password_complexity' => password_complexity('get'),
|
'password_complexity' => password_complexity('get'),
|
||||||
'show_rspamd_global_filters' => @$_SESSION['show_rspamd_global_filters'],
|
'show_rspamd_global_filters' => @$_SESSION['show_rspamd_global_filters'],
|
||||||
'lang_admin' => json_encode($lang['admin']),
|
'lang_admin' => json_encode($lang['admin']),
|
||||||
|
@@ -699,6 +699,38 @@ paths:
|
|||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
summary: Create Domain Admin user
|
summary: Create Domain Admin user
|
||||||
|
/api/v1/add/sso/domain-admin:
|
||||||
|
post:
|
||||||
|
responses:
|
||||||
|
"401":
|
||||||
|
$ref: "#/components/responses/Unauthorized"
|
||||||
|
"200":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
examples:
|
||||||
|
response:
|
||||||
|
value:
|
||||||
|
token: "591F6D-5C3DD2-7455CD-DAF1C1-AA4FCC"
|
||||||
|
description: OK
|
||||||
|
headers: { }
|
||||||
|
tags:
|
||||||
|
- Single Sign-On
|
||||||
|
description: >-
|
||||||
|
Using this endpoint you can issue a token for Domain Admin user. This token can be used for
|
||||||
|
autologin Domain Admin user by using query_string var sso_token={token}. Token expiration time is 30s
|
||||||
|
operationId: Issue Domain Admin SSO token
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
example:
|
||||||
|
username: testadmin
|
||||||
|
properties:
|
||||||
|
username:
|
||||||
|
description: the username for the admin user
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
summary: Issue Domain Admin SSO token
|
||||||
/api/v1/edit/da-acl:
|
/api/v1/edit/da-acl:
|
||||||
post:
|
post:
|
||||||
responses:
|
responses:
|
||||||
@@ -3144,8 +3176,10 @@ paths:
|
|||||||
example:
|
example:
|
||||||
attr:
|
attr:
|
||||||
ban_time: "86400"
|
ban_time: "86400"
|
||||||
|
ban_time_increment: "1"
|
||||||
blacklist: "10.100.6.5/32,10.100.8.4/32"
|
blacklist: "10.100.6.5/32,10.100.8.4/32"
|
||||||
max_attempts: "5"
|
max_attempts: "5"
|
||||||
|
max_ban_time: "86400"
|
||||||
netban_ipv4: "24"
|
netban_ipv4: "24"
|
||||||
netban_ipv6: "64"
|
netban_ipv6: "64"
|
||||||
retry_window: "600"
|
retry_window: "600"
|
||||||
@@ -3159,11 +3193,17 @@ paths:
|
|||||||
description: the backlisted ips or hostnames separated by comma
|
description: the backlisted ips or hostnames separated by comma
|
||||||
type: string
|
type: string
|
||||||
ban_time:
|
ban_time:
|
||||||
description: the time a ip should be banned
|
description: the time an ip should be banned
|
||||||
type: number
|
type: number
|
||||||
|
ban_time_increment:
|
||||||
|
description: if the time of the ban should increase each time
|
||||||
|
type: boolean
|
||||||
max_attempts:
|
max_attempts:
|
||||||
description: the maximum numbe of wrong logins before a ip is banned
|
description: the maximum numbe of wrong logins before a ip is banned
|
||||||
type: number
|
type: number
|
||||||
|
max_ban_time:
|
||||||
|
description: the maximum time an ip should be banned
|
||||||
|
type: number
|
||||||
netban_ipv4:
|
netban_ipv4:
|
||||||
description: the networks mask to ban for ipv4
|
description: the networks mask to ban for ipv4
|
||||||
type: number
|
type: number
|
||||||
@@ -4081,10 +4121,12 @@ paths:
|
|||||||
response:
|
response:
|
||||||
value:
|
value:
|
||||||
ban_time: 604800
|
ban_time: 604800
|
||||||
|
ban_time_increment: 1
|
||||||
blacklist: |-
|
blacklist: |-
|
||||||
45.82.153.37/32
|
45.82.153.37/32
|
||||||
92.118.38.52/32
|
92.118.38.52/32
|
||||||
max_attempts: 1
|
max_attempts: 1
|
||||||
|
max_ban_time: 604800
|
||||||
netban_ipv4: 32
|
netban_ipv4: 32
|
||||||
netban_ipv6: 128
|
netban_ipv6: 128
|
||||||
perm_bans:
|
perm_bans:
|
||||||
@@ -5586,6 +5628,8 @@ tags:
|
|||||||
description: Manage DKIM keys
|
description: Manage DKIM keys
|
||||||
- name: Domain admin
|
- name: Domain admin
|
||||||
description: Create or udpdate domain admin users
|
description: Create or udpdate domain admin users
|
||||||
|
- name: Single Sign-On
|
||||||
|
description: Issue tokens for users
|
||||||
- name: Address Rewriting
|
- name: Address Rewriting
|
||||||
description: Create BCC maps or recipient maps
|
description: Create BCC maps or recipient maps
|
||||||
- name: Outgoing TLS Policy Map Overrides
|
- name: Outgoing TLS Policy Map Overrides
|
||||||
|
@@ -4,10 +4,10 @@
|
|||||||
*
|
*
|
||||||
* To rebuild or modify this file with the latest versions of the included
|
* To rebuild or modify this file with the latest versions of the included
|
||||||
* software please visit:
|
* software please visit:
|
||||||
* https://datatables.net/download/#bs5/dt-1.12.0/r-2.3.0/sl-1.4.0
|
* https://datatables.net/download/#bs5/dt-1.13.1/r-2.4.0/sl-1.5.0
|
||||||
*
|
*
|
||||||
* Included libraries:
|
* Included libraries:
|
||||||
* DataTables 1.12.0, Responsive 2.3.0, Select 1.4.0
|
* DataTables 1.13.1, Responsive 2.4.0, Select 1.5.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@charset "UTF-8";
|
@charset "UTF-8";
|
||||||
@@ -63,7 +63,7 @@ table.dataTable thead > tr > td.sorting_desc_disabled:after {
|
|||||||
opacity: 0.125;
|
opacity: 0.125;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
line-height: 9px;
|
line-height: 9px;
|
||||||
font-size: 0.9em;
|
font-size: 0.8em;
|
||||||
}
|
}
|
||||||
table.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:before,
|
table.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:before,
|
||||||
table.dataTable thead > tr > td.sorting:before,
|
table.dataTable thead > tr > td.sorting:before,
|
||||||
@@ -72,7 +72,7 @@ table.dataTable thead > tr > td.sorting_desc:before,
|
|||||||
table.dataTable thead > tr > td.sorting_asc_disabled:before,
|
table.dataTable thead > tr > td.sorting_asc_disabled:before,
|
||||||
table.dataTable thead > tr > td.sorting_desc_disabled:before {
|
table.dataTable thead > tr > td.sorting_desc_disabled:before {
|
||||||
bottom: 50%;
|
bottom: 50%;
|
||||||
content: "▴";
|
content: "▲";
|
||||||
}
|
}
|
||||||
table.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:after,
|
table.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:after,
|
||||||
table.dataTable thead > tr > td.sorting:after,
|
table.dataTable thead > tr > td.sorting:after,
|
||||||
@@ -81,7 +81,7 @@ table.dataTable thead > tr > td.sorting_desc:after,
|
|||||||
table.dataTable thead > tr > td.sorting_asc_disabled:after,
|
table.dataTable thead > tr > td.sorting_asc_disabled:after,
|
||||||
table.dataTable thead > tr > td.sorting_desc_disabled:after {
|
table.dataTable thead > tr > td.sorting_desc_disabled:after {
|
||||||
top: 50%;
|
top: 50%;
|
||||||
content: "▾";
|
content: "▼";
|
||||||
}
|
}
|
||||||
table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:after,
|
table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:after,
|
||||||
table.dataTable thead > tr > td.sorting_asc:before,
|
table.dataTable thead > tr > td.sorting_asc:before,
|
||||||
@@ -287,6 +287,9 @@ table.dataTable > tbody > tr.selected > * {
|
|||||||
box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.9);
|
box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.9);
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
table.dataTable > tbody > tr.selected a {
|
||||||
|
color: #090a0b;
|
||||||
|
}
|
||||||
table.dataTable.table-striped > tbody > tr.odd > * {
|
table.dataTable.table-striped > tbody > tr.odd > * {
|
||||||
box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.05);
|
box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.05);
|
||||||
}
|
}
|
||||||
@@ -335,6 +338,13 @@ div.dataTables_wrapper div.dataTables_paginate ul.pagination {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
div.dataTables_wrapper div.dt-row {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.dataTables_wrapper span.sorting-value {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
div.dataTables_scrollHead table.dataTable {
|
div.dataTables_scrollHead table.dataTable {
|
||||||
margin-bottom: 0 !important;
|
margin-bottom: 0 !important;
|
||||||
@@ -380,17 +390,6 @@ div.dataTables_wrapper div.dataTables_paginate {
|
|||||||
table.dataTable.table-sm > thead > tr > th:not(.sorting_disabled) {
|
table.dataTable.table-sm > thead > tr > th:not(.sorting_disabled) {
|
||||||
padding-right: 20px;
|
padding-right: 20px;
|
||||||
}
|
}
|
||||||
table.dataTable.table-sm .sorting:before,
|
|
||||||
table.dataTable.table-sm .sorting_asc:before,
|
|
||||||
table.dataTable.table-sm .sorting_desc:before {
|
|
||||||
top: 5px;
|
|
||||||
right: 0.85em;
|
|
||||||
}
|
|
||||||
table.dataTable.table-sm .sorting:after,
|
|
||||||
table.dataTable.table-sm .sorting_asc:after,
|
|
||||||
table.dataTable.table-sm .sorting_desc:after {
|
|
||||||
top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.table-bordered.dataTable {
|
table.table-bordered.dataTable {
|
||||||
border-right-width: 0;
|
border-right-width: 0;
|
||||||
@@ -629,13 +628,13 @@ table.dataTable > tbody > tr > .selected {
|
|||||||
background-color: rgba(13, 110, 253, 0.9);
|
background-color: rgba(13, 110, 253, 0.9);
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
table.dataTable tbody td.select-checkbox,
|
table.dataTable > tbody > tr > td.select-checkbox,
|
||||||
table.dataTable tbody th.select-checkbox {
|
table.dataTable > tbody > tr > th.select-checkbox {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
table.dataTable tbody td.select-checkbox:before, table.dataTable tbody td.select-checkbox:after,
|
table.dataTable > tbody > tr > td.select-checkbox:before, table.dataTable > tbody > tr > td.select-checkbox:after,
|
||||||
table.dataTable tbody th.select-checkbox:before,
|
table.dataTable > tbody > tr > th.select-checkbox:before,
|
||||||
table.dataTable tbody th.select-checkbox:after {
|
table.dataTable > tbody > tr > th.select-checkbox:after {
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 1.2em;
|
top: 1.2em;
|
||||||
@@ -644,20 +643,20 @@ table.dataTable tbody th.select-checkbox:after {
|
|||||||
height: 12px;
|
height: 12px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
table.dataTable tbody td.select-checkbox:before,
|
table.dataTable > tbody > tr > td.select-checkbox:before,
|
||||||
table.dataTable tbody th.select-checkbox:before {
|
table.dataTable > tbody > tr > th.select-checkbox:before {
|
||||||
content: " ";
|
content: " ";
|
||||||
margin-top: -5px;
|
margin-top: -5px;
|
||||||
margin-left: -6px;
|
margin-left: -6px;
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
table.dataTable tr.selected td.select-checkbox:before,
|
table.dataTable > tbody > tr.selected > td.select-checkbox:before,
|
||||||
table.dataTable tr.selected th.select-checkbox:before {
|
table.dataTable > tbody > tr.selected > th.select-checkbox:before {
|
||||||
border: 1px solid white;
|
border: 1px solid white;
|
||||||
}
|
}
|
||||||
table.dataTable tr.selected td.select-checkbox:after,
|
table.dataTable > tbody > tr.selected > td.select-checkbox:after,
|
||||||
table.dataTable tr.selected th.select-checkbox:after {
|
table.dataTable > tbody > tr.selected > th.select-checkbox:after {
|
||||||
content: "✓";
|
content: "✓";
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
margin-top: -19px;
|
margin-top: -19px;
|
||||||
@@ -665,12 +664,12 @@ table.dataTable tr.selected th.select-checkbox:after {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
text-shadow: 1px 1px #B0BED9, -1px -1px #B0BED9, 1px -1px #B0BED9, -1px 1px #B0BED9;
|
text-shadow: 1px 1px #B0BED9, -1px -1px #B0BED9, 1px -1px #B0BED9, -1px 1px #B0BED9;
|
||||||
}
|
}
|
||||||
table.dataTable.compact tbody td.select-checkbox:before,
|
table.dataTable.compact > tbody > tr > td.select-checkbox:before,
|
||||||
table.dataTable.compact tbody th.select-checkbox:before {
|
table.dataTable.compact > tbody > tr > th.select-checkbox:before {
|
||||||
margin-top: -12px;
|
margin-top: -12px;
|
||||||
}
|
}
|
||||||
table.dataTable.compact tr.selected td.select-checkbox:after,
|
table.dataTable.compact > tbody > tr.selected > td.select-checkbox:after,
|
||||||
table.dataTable.compact tr.selected th.select-checkbox:after {
|
table.dataTable.compact > tbody > tr.selected > th.select-checkbox:after {
|
||||||
margin-top: -16px;
|
margin-top: -16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -690,4 +689,3 @@ table.dataTable.table-sm tbody td.select-checkbox::before {
|
|||||||
margin-top: -9px;
|
margin-top: -9px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -78,3 +78,21 @@ table.dataTable>tbody>tr.child span.dtr-title {
|
|||||||
width: 30%;
|
width: 30%;
|
||||||
max-width: 250px;
|
max-width: 250px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
div.dataTables_wrapper div.dataTables_filter {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
div.dataTables_wrapper div.dataTables_length {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.dataTables_paginate, .dataTables_length, .dataTables_filter {
|
||||||
|
margin: 10px 0!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.dt-text-right {
|
||||||
|
text-align: end !important;
|
||||||
|
}
|
||||||
|
th.dt-text-right {
|
||||||
|
text-align: end !important;
|
||||||
|
}
|
@@ -199,6 +199,13 @@
|
|||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.dataTables_wrapper div.dataTables_length {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.senders-mw220 {
|
||||||
|
max-width: 100% !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 350px) {
|
@media (max-width: 350px) {
|
@@ -66,4 +66,6 @@ table tbody tr td input[type="checkbox"] {
|
|||||||
padding: .2em .4em .3em !important;
|
padding: .2em .4em .3em !important;
|
||||||
background-color: #ececec!important;
|
background-color: #ececec!important;
|
||||||
}
|
}
|
||||||
|
.badge.bg-info .bi {
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
@@ -99,4 +99,6 @@ table tbody tr td input[type="checkbox"] {
|
|||||||
font-size:110%;
|
font-size:110%;
|
||||||
margin:20px;
|
margin:20px;
|
||||||
}
|
}
|
||||||
|
.senders-mw220 {
|
||||||
|
max-width: 220px;
|
||||||
|
}
|
||||||
|
@@ -11,7 +11,86 @@
|
|||||||
* Copyright 2011-2021 Twitter, Inc.
|
* Copyright 2011-2021 Twitter, Inc.
|
||||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||||
*/
|
*/
|
||||||
@import url("https://fonts.googleapis.com/css2?family=Source+Sans+Pro:ital,wght@0,300;0,400;0,700;1,400&display=swap");
|
|
||||||
|
/* source-sans-pro-300 - latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
src: url('/fonts/source-sans-pro-v21-latin-300.eot'); /* IE9 Compat Modes */
|
||||||
|
src: local(''),
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-300.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-300.woff2') format('woff2'), /* Super Modern Browsers */
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-300.woff') format('woff'), /* Modern Browsers */
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-300.ttf') format('truetype'), /* Safari, Android, iOS */
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-300.svg#SourceSansPro') format('svg'); /* Legacy iOS */
|
||||||
|
}
|
||||||
|
/* source-sans-pro-300italic - latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
src: url('/fonts/source-sans-pro-v21-latin-300italic.eot'); /* IE9 Compat Modes */
|
||||||
|
src: local(''),
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-300italic.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-300italic.woff2') format('woff2'), /* Super Modern Browsers */
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-300italic.woff') format('woff'), /* Modern Browsers */
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-300italic.ttf') format('truetype'), /* Safari, Android, iOS */
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-300italic.svg#SourceSansPro') format('svg'); /* Legacy iOS */
|
||||||
|
}
|
||||||
|
/* source-sans-pro-regular - latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url('/fonts/source-sans-pro-v21-latin-regular.eot'); /* IE9 Compat Modes */
|
||||||
|
src: local(''),
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-regular.woff') format('woff'), /* Modern Browsers */
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-regular.svg#SourceSansPro') format('svg'); /* Legacy iOS */
|
||||||
|
}
|
||||||
|
/* source-sans-pro-italic - latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url('/fonts/source-sans-pro-v21-latin-italic.eot'); /* IE9 Compat Modes */
|
||||||
|
src: local(''),
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-italic.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-italic.woff2') format('woff2'), /* Super Modern Browsers */
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-italic.woff') format('woff'), /* Modern Browsers */
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-italic.ttf') format('truetype'), /* Safari, Android, iOS */
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-italic.svg#SourceSansPro') format('svg'); /* Legacy iOS */
|
||||||
|
}
|
||||||
|
/* source-sans-pro-700 - latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
src: url('/fonts/source-sans-pro-v21-latin-700.eot'); /* IE9 Compat Modes */
|
||||||
|
src: local(''),
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-700.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-700.woff2') format('woff2'), /* Super Modern Browsers */
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-700.woff') format('woff'), /* Modern Browsers */
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-700.ttf') format('truetype'), /* Safari, Android, iOS */
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-700.svg#SourceSansPro') format('svg'); /* Legacy iOS */
|
||||||
|
}
|
||||||
|
/* source-sans-pro-700italic - latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 700;
|
||||||
|
src: url('/fonts/source-sans-pro-v21-latin-700italic.eot'); /* IE9 Compat Modes */
|
||||||
|
src: local(''),
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-700italic.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-700italic.woff2') format('woff2'), /* Super Modern Browsers */
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-700italic.woff') format('woff'), /* Modern Browsers */
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-700italic.ttf') format('truetype'), /* Safari, Android, iOS */
|
||||||
|
url('/fonts/source-sans-pro-v21-latin-700italic.svg#SourceSansPro') format('svg'); /* Legacy iOS */
|
||||||
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--bs-blue: #158cba;
|
--bs-blue: #158cba;
|
||||||
--bs-indigo: #6610f2;
|
--bs-indigo: #6610f2;
|
||||||
|
@@ -20,6 +20,11 @@ legend {
|
|||||||
background-color: #7a7a7a !important;
|
background-color: #7a7a7a !important;
|
||||||
border-color: #5c5c5c !important;
|
border-color: #5c5c5c !important;
|
||||||
}
|
}
|
||||||
|
.btn-dark {
|
||||||
|
color: #000 !important;;
|
||||||
|
background-color: #f6f6f6 !important;;
|
||||||
|
border-color: #ddd !important;;
|
||||||
|
}
|
||||||
.btn-check:checked+.btn-secondary, .btn-check:active+.btn-secondary, .btn-secondary:active, .btn-secondary.active, .show>.btn-secondary.dropdown-toggle {
|
.btn-check:checked+.btn-secondary, .btn-check:active+.btn-secondary, .btn-secondary:active, .btn-secondary.active, .show>.btn-secondary.dropdown-toggle {
|
||||||
border-color: #7a7a7a !important;
|
border-color: #7a7a7a !important;
|
||||||
}
|
}
|
||||||
@@ -358,3 +363,11 @@ table.dataTable.dtr-inline.collapsed>tbody>tr>td.dataTables_empty {
|
|||||||
background: #333;
|
background: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
span.mail-address-item {
|
||||||
|
background-color: #333;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #555;
|
||||||
|
padding: 2px 7px;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 2px 6px 2px 0;
|
||||||
|
}
|
||||||
|
@@ -65,6 +65,7 @@ $template_data = [
|
|||||||
'solr_uptime' => round($solr_status['status']['dovecot-fts']['uptime'] / 1000 / 60 / 60),
|
'solr_uptime' => round($solr_status['status']['dovecot-fts']['uptime'] / 1000 / 60 / 60),
|
||||||
'clamd_status' => $clamd_status,
|
'clamd_status' => $clamd_status,
|
||||||
'containers' => $containers,
|
'containers' => $containers,
|
||||||
|
'ip_check' => customize('get', 'ip_check'),
|
||||||
'lang_admin' => json_encode($lang['admin']),
|
'lang_admin' => json_encode($lang['admin']),
|
||||||
'lang_debug' => json_encode($lang['debug']),
|
'lang_debug' => json_encode($lang['debug']),
|
||||||
'lang_datatables' => json_encode($lang['datatables']),
|
'lang_datatables' => json_encode($lang['datatables']),
|
||||||
|
BIN
data/web/fonts/source-sans-pro-v21-latin-300.woff
Normal file
BIN
data/web/fonts/source-sans-pro-v21-latin-300.woff
Normal file
Binary file not shown.
BIN
data/web/fonts/source-sans-pro-v21-latin-300.woff2
Normal file
BIN
data/web/fonts/source-sans-pro-v21-latin-300.woff2
Normal file
Binary file not shown.
BIN
data/web/fonts/source-sans-pro-v21-latin-300italic.woff
Normal file
BIN
data/web/fonts/source-sans-pro-v21-latin-300italic.woff
Normal file
Binary file not shown.
BIN
data/web/fonts/source-sans-pro-v21-latin-300italic.woff2
Normal file
BIN
data/web/fonts/source-sans-pro-v21-latin-300italic.woff2
Normal file
Binary file not shown.
BIN
data/web/fonts/source-sans-pro-v21-latin-700.woff
Normal file
BIN
data/web/fonts/source-sans-pro-v21-latin-700.woff
Normal file
Binary file not shown.
BIN
data/web/fonts/source-sans-pro-v21-latin-700.woff2
Normal file
BIN
data/web/fonts/source-sans-pro-v21-latin-700.woff2
Normal file
Binary file not shown.
BIN
data/web/fonts/source-sans-pro-v21-latin-700italic.woff
Normal file
BIN
data/web/fonts/source-sans-pro-v21-latin-700italic.woff
Normal file
Binary file not shown.
BIN
data/web/fonts/source-sans-pro-v21-latin-700italic.woff2
Normal file
BIN
data/web/fonts/source-sans-pro-v21-latin-700italic.woff2
Normal file
Binary file not shown.
BIN
data/web/fonts/source-sans-pro-v21-latin-italic.woff
Normal file
BIN
data/web/fonts/source-sans-pro-v21-latin-italic.woff
Normal file
Binary file not shown.
BIN
data/web/fonts/source-sans-pro-v21-latin-italic.woff2
Normal file
BIN
data/web/fonts/source-sans-pro-v21-latin-italic.woff2
Normal file
Binary file not shown.
BIN
data/web/fonts/source-sans-pro-v21-latin-regular.woff
Normal file
BIN
data/web/fonts/source-sans-pro-v21-latin-regular.woff
Normal file
Binary file not shown.
BIN
data/web/fonts/source-sans-pro-v21-latin-regular.woff2
Normal file
BIN
data/web/fonts/source-sans-pro-v21-latin-regular.woff2
Normal file
Binary file not shown.
@@ -49,7 +49,9 @@ function bcc($_action, $_data = null, $_attr = null) {
|
|||||||
}
|
}
|
||||||
elseif (filter_var($local_dest, FILTER_VALIDATE_EMAIL)) {
|
elseif (filter_var($local_dest, FILTER_VALIDATE_EMAIL)) {
|
||||||
$mailbox = mailbox('get', 'mailbox_details', $local_dest);
|
$mailbox = mailbox('get', 'mailbox_details', $local_dest);
|
||||||
if ($mailbox === false && array_key_exists($local_dest, array_merge($direct_aliases, $shared_aliases)) === false) {
|
$shared_aliases = mailbox('get', 'shared_aliases');
|
||||||
|
$direct_aliases = mailbox('get', 'direct_aliases');
|
||||||
|
if ($mailbox === false && in_array($local_dest, array_merge($direct_aliases, $shared_aliases)) === false) {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
|
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
|
||||||
|
@@ -160,6 +160,25 @@ function customize($_action, $_item, $_data = null) {
|
|||||||
'msg' => 'ui_texts'
|
'msg' => 'ui_texts'
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case 'ip_check':
|
||||||
|
$ip_check = ($_data['ip_check_opt_in'] == "1") ? 1 : 0;
|
||||||
|
try {
|
||||||
|
$redis->set('IP_CHECK', $ip_check);
|
||||||
|
}
|
||||||
|
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' => 'ip_check_opt_in_modified'
|
||||||
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'delete':
|
case 'delete':
|
||||||
@@ -276,6 +295,20 @@ function customize($_action, $_item, $_data = null) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'ip_check':
|
||||||
|
try {
|
||||||
|
$ip_check = ($ip_check = $redis->get('IP_CHECK')) ? $ip_check : 0;
|
||||||
|
return $ip_check;
|
||||||
|
}
|
||||||
|
catch (RedisException $e) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_item, $_data),
|
||||||
|
'msg' => array('redis_error', $e)
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -405,3 +405,64 @@ function domain_admin($_action, $_data = null) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function domain_admin_sso($_action, $_data) {
|
||||||
|
global $pdo;
|
||||||
|
|
||||||
|
switch ($_action) {
|
||||||
|
case 'check':
|
||||||
|
$token = $_data;
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("SELECT `t1`.`username` FROM `da_sso` AS `t1` JOIN `admin` AS `t2` ON `t1`.`username` = `t2`.`username` WHERE `t1`.`token` = :token AND `t1`.`created` > DATE_SUB(NOW(), INTERVAL '30' SECOND) AND `t2`.`active` = 1 AND `t2`.`superadmin` = 0;");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':token' => preg_replace('/[^a-zA-Z0-9-]/', '', $token)
|
||||||
|
));
|
||||||
|
$return = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
return empty($return['username']) ? false : $return['username'];
|
||||||
|
case 'issue':
|
||||||
|
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_data),
|
||||||
|
'msg' => 'access_denied'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$username = $_data['username'];
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("SELECT `username` FROM `domain_admins`
|
||||||
|
WHERE `username` = :username");
|
||||||
|
$stmt->execute(array(':username' => $username));
|
||||||
|
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||||
|
|
||||||
|
if ($num_results < 1) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_data),
|
||||||
|
'msg' => array('object_doesnt_exist', htmlspecialchars($username))
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = implode('-', array(
|
||||||
|
strtoupper(bin2hex(random_bytes(3))),
|
||||||
|
strtoupper(bin2hex(random_bytes(3))),
|
||||||
|
strtoupper(bin2hex(random_bytes(3))),
|
||||||
|
strtoupper(bin2hex(random_bytes(3))),
|
||||||
|
strtoupper(bin2hex(random_bytes(3)))
|
||||||
|
));
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO `da_sso` (`username`, `token`)
|
||||||
|
VALUES (:username, :token)");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':username' => $username,
|
||||||
|
':token' => $token
|
||||||
|
));
|
||||||
|
|
||||||
|
// perform cleanup
|
||||||
|
$pdo->query("DELETE FROM `da_sso` WHERE created < DATE_SUB(NOW(), INTERVAL '30' SECOND);");
|
||||||
|
|
||||||
|
return ['token' => $token];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -239,7 +239,9 @@ function fail2ban($_action, $_data = null) {
|
|||||||
$is_now = fail2ban('get');
|
$is_now = fail2ban('get');
|
||||||
if (!empty($is_now)) {
|
if (!empty($is_now)) {
|
||||||
$ban_time = intval((isset($_data['ban_time'])) ? $_data['ban_time'] : $is_now['ban_time']);
|
$ban_time = intval((isset($_data['ban_time'])) ? $_data['ban_time'] : $is_now['ban_time']);
|
||||||
|
$ban_time_increment = (isset($_data['ban_time_increment']) && $_data['ban_time_increment'] == "1") ? 1 : 0;
|
||||||
$max_attempts = intval((isset($_data['max_attempts'])) ? $_data['max_attempts'] : $is_now['max_attempts']);
|
$max_attempts = intval((isset($_data['max_attempts'])) ? $_data['max_attempts'] : $is_now['max_attempts']);
|
||||||
|
$max_ban_time = intval((isset($_data['max_ban_time'])) ? $_data['max_ban_time'] : $is_now['max_ban_time']);
|
||||||
$retry_window = intval((isset($_data['retry_window'])) ? $_data['retry_window'] : $is_now['retry_window']);
|
$retry_window = intval((isset($_data['retry_window'])) ? $_data['retry_window'] : $is_now['retry_window']);
|
||||||
$netban_ipv4 = intval((isset($_data['netban_ipv4'])) ? $_data['netban_ipv4'] : $is_now['netban_ipv4']);
|
$netban_ipv4 = intval((isset($_data['netban_ipv4'])) ? $_data['netban_ipv4'] : $is_now['netban_ipv4']);
|
||||||
$netban_ipv6 = intval((isset($_data['netban_ipv6'])) ? $_data['netban_ipv6'] : $is_now['netban_ipv6']);
|
$netban_ipv6 = intval((isset($_data['netban_ipv6'])) ? $_data['netban_ipv6'] : $is_now['netban_ipv6']);
|
||||||
@@ -256,6 +258,8 @@ function fail2ban($_action, $_data = null) {
|
|||||||
}
|
}
|
||||||
$f2b_options = array();
|
$f2b_options = array();
|
||||||
$f2b_options['ban_time'] = ($ban_time < 60) ? 60 : $ban_time;
|
$f2b_options['ban_time'] = ($ban_time < 60) ? 60 : $ban_time;
|
||||||
|
$f2b_options['ban_time_increment'] = ($ban_time_increment == 1) ? true : false;
|
||||||
|
$f2b_options['max_ban_time'] = ($max_ban_time < 60) ? 60 : $max_ban_time;
|
||||||
$f2b_options['netban_ipv4'] = ($netban_ipv4 < 8) ? 8 : $netban_ipv4;
|
$f2b_options['netban_ipv4'] = ($netban_ipv4 < 8) ? 8 : $netban_ipv4;
|
||||||
$f2b_options['netban_ipv6'] = ($netban_ipv6 < 8) ? 8 : $netban_ipv6;
|
$f2b_options['netban_ipv6'] = ($netban_ipv6 < 8) ? 8 : $netban_ipv6;
|
||||||
$f2b_options['netban_ipv4'] = ($netban_ipv4 > 32) ? 32 : $netban_ipv4;
|
$f2b_options['netban_ipv4'] = ($netban_ipv4 > 32) ? 32 : $netban_ipv4;
|
||||||
|
@@ -1015,20 +1015,58 @@ function formatBytes($size, $precision = 2) {
|
|||||||
}
|
}
|
||||||
return round(pow(1024, $base - floor($base)), $precision) . $suffixes[floor($base)];
|
return round(pow(1024, $base - floor($base)), $precision) . $suffixes[floor($base)];
|
||||||
}
|
}
|
||||||
function update_sogo_static_view() {
|
function update_sogo_static_view($mailbox = null) {
|
||||||
if (getenv('SKIP_SOGO') == "y") {
|
if (getenv('SKIP_SOGO') == "y") {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
global $pdo;
|
global $pdo;
|
||||||
global $lang;
|
global $lang;
|
||||||
$stmt = $pdo->query("SELECT 'OK' FROM INFORMATION_SCHEMA.TABLES
|
|
||||||
WHERE TABLE_NAME = 'sogo_view'");
|
$mailbox_exists = false;
|
||||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
if ($mailbox !== null) {
|
||||||
if ($num_results != 0) {
|
// Check if the mailbox exists
|
||||||
$stmt = $pdo->query("REPLACE INTO _sogo_static_view (`c_uid`, `domain`, `c_name`, `c_password`, `c_cn`, `mail`, `aliases`, `ad_aliases`, `ext_acl`, `kind`, `multiple_bookings`)
|
$stmt = $pdo->prepare("SELECT username FROM mailbox WHERE username = :mailbox AND active = '1'");
|
||||||
SELECT `c_uid`, `domain`, `c_name`, `c_password`, `c_cn`, `mail`, `aliases`, `ad_aliases`, `ext_acl`, `kind`, `multiple_bookings` from sogo_view");
|
$stmt->execute(array(':mailbox' => $mailbox));
|
||||||
$stmt = $pdo->query("DELETE FROM _sogo_static_view WHERE `c_uid` NOT IN (SELECT `username` FROM `mailbox` WHERE `active` = '1');");
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
if ($row){
|
||||||
|
$mailbox_exists = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$query = "REPLACE INTO _sogo_static_view (`c_uid`, `domain`, `c_name`, `c_password`, `c_cn`, `mail`, `aliases`, `ad_aliases`, `ext_acl`, `kind`, `multiple_bookings`)
|
||||||
|
SELECT
|
||||||
|
mailbox.username,
|
||||||
|
mailbox.domain,
|
||||||
|
mailbox.username,
|
||||||
|
IF(JSON_UNQUOTE(JSON_VALUE(attributes, '$.force_pw_update')) = '0',
|
||||||
|
IF(JSON_UNQUOTE(JSON_VALUE(attributes, '$.sogo_access')) = 1, password, '{SSHA256}A123A123A321A321A321B321B321B123B123B321B432F123E321123123321321'),
|
||||||
|
'{SSHA256}A123A123A321A321A321B321B321B123B123B321B432F123E321123123321321'),
|
||||||
|
mailbox.name,
|
||||||
|
mailbox.username,
|
||||||
|
IFNULL(GROUP_CONCAT(ga.aliases ORDER BY ga.aliases SEPARATOR ' '), ''),
|
||||||
|
IFNULL(gda.ad_alias, ''),
|
||||||
|
IFNULL(external_acl.send_as_acl, ''),
|
||||||
|
mailbox.kind,
|
||||||
|
mailbox.multiple_bookings
|
||||||
|
FROM
|
||||||
|
mailbox
|
||||||
|
LEFT OUTER JOIN grouped_mail_aliases ga ON ga.username REGEXP CONCAT('(^|,)', mailbox.username, '($|,)')
|
||||||
|
LEFT OUTER JOIN grouped_domain_alias_address gda ON gda.username = mailbox.username
|
||||||
|
LEFT OUTER JOIN grouped_sender_acl_external external_acl ON external_acl.username = mailbox.username
|
||||||
|
WHERE
|
||||||
|
mailbox.active = '1'";
|
||||||
|
|
||||||
|
if ($mailbox_exists) {
|
||||||
|
$query .= " AND mailbox.username = :mailbox";
|
||||||
|
$stmt = $pdo->prepare($query);
|
||||||
|
$stmt->execute(array(':mailbox' => $mailbox));
|
||||||
|
} else {
|
||||||
|
$query .= " GROUP BY mailbox.username";
|
||||||
|
$stmt = $pdo->query($query);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->query("DELETE FROM _sogo_static_view WHERE `c_uid` NOT IN (SELECT `username` FROM `mailbox` WHERE `active` = '1');");
|
||||||
|
|
||||||
flush_memcached();
|
flush_memcached();
|
||||||
}
|
}
|
||||||
function edit_user_account($_data) {
|
function edit_user_account($_data) {
|
||||||
@@ -1739,7 +1777,7 @@ function verify_tfa_login($username, $_data) {
|
|||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $username, '*'),
|
'log' => array(__FUNCTION__, $username, '*'),
|
||||||
'msg' => array('webauthn_verification_failed', 'authenticator not found')
|
'msg' => array('webauthn_authenticator_failed')
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1748,11 +1786,20 @@ function verify_tfa_login($username, $_data) {
|
|||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $username, '*'),
|
'log' => array(__FUNCTION__, $username, '*'),
|
||||||
'msg' => array('webauthn_verification_failed', 'publicKey not found')
|
'msg' => array('webauthn_publickey_failed')
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($process_webauthn['username'] != $_SESSION['pending_mailcow_cc_username']){
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $username, '*'),
|
||||||
|
'msg' => array('webauthn_username_failed')
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$WebAuthn->processGet($clientDataJSON, $authenticatorData, $signature, $process_webauthn['publicKey'], $challenge, null, $GLOBALS['WEBAUTHN_UV_FLAG_LOGIN'], $GLOBALS['WEBAUTHN_USER_PRESENT_FLAG']);
|
$WebAuthn->processGet($clientDataJSON, $authenticatorData, $signature, $process_webauthn['publicKey'], $challenge, null, $GLOBALS['WEBAUTHN_UV_FLAG_LOGIN'], $GLOBALS['WEBAUTHN_USER_PRESENT_FLAG']);
|
||||||
}
|
}
|
||||||
@@ -1784,21 +1831,12 @@ function verify_tfa_login($username, $_data) {
|
|||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $username, '*'),
|
'log' => array(__FUNCTION__, $username, '*'),
|
||||||
'msg' => array('webauthn_verification_failed', 'could not determine user role')
|
'msg' => array('webauthn_role_failed')
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($process_webauthn['username'] != $_SESSION['pending_mailcow_cc_username']){
|
|
||||||
$_SESSION['return'][] = array(
|
|
||||||
'type' => 'danger',
|
|
||||||
'log' => array(__FUNCTION__, $username, '*'),
|
|
||||||
'msg' => array('webauthn_verification_failed', 'user who requests does not match with sql entry')
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$_SESSION["mailcow_cc_username"] = $process_webauthn['username'];
|
$_SESSION["mailcow_cc_username"] = $process_webauthn['username'];
|
||||||
$_SESSION['tfa_id'] = $process_webauthn['id'];
|
$_SESSION['tfa_id'] = $process_webauthn['id'];
|
||||||
$_SESSION['authReq'] = null;
|
$_SESSION['authReq'] = null;
|
||||||
|
@@ -1264,11 +1264,13 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update_sogo_static_view($username);
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'success',
|
'type' => 'success',
|
||||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||||
'msg' => array('mailbox_added', htmlspecialchars($username))
|
'msg' => array('mailbox_added', htmlspecialchars($username))
|
||||||
);
|
);
|
||||||
|
return true;
|
||||||
break;
|
break;
|
||||||
case 'resource':
|
case 'resource':
|
||||||
$domain = idn_to_ascii(strtolower(trim($_data['domain'])), 0, INTL_IDNA_VARIANT_UTS46);
|
$domain = idn_to_ascii(strtolower(trim($_data['domain'])), 0, INTL_IDNA_VARIANT_UTS46);
|
||||||
@@ -2879,67 +2881,68 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||||
'msg' => 'access_denied'
|
'msg' => 'extended_sender_acl_denied'
|
||||||
);
|
);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
$extra_acls = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['extended_sender_acl']));
|
else {
|
||||||
foreach ($extra_acls as $i => &$extra_acl) {
|
$extra_acls = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['extended_sender_acl']));
|
||||||
if (empty($extra_acl)) {
|
foreach ($extra_acls as $i => &$extra_acl) {
|
||||||
continue;
|
if (empty($extra_acl)) {
|
||||||
}
|
continue;
|
||||||
if (substr($extra_acl, 0, 1) === "@") {
|
}
|
||||||
$extra_acl = ltrim($extra_acl, '@');
|
if (substr($extra_acl, 0, 1) === "@") {
|
||||||
}
|
$extra_acl = ltrim($extra_acl, '@');
|
||||||
if (!filter_var($extra_acl, FILTER_VALIDATE_EMAIL) && !is_valid_domain_name($extra_acl)) {
|
}
|
||||||
$_SESSION['return'][] = array(
|
if (!filter_var($extra_acl, FILTER_VALIDATE_EMAIL) && !is_valid_domain_name($extra_acl)) {
|
||||||
'type' => 'danger',
|
|
||||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
|
||||||
'msg' => array('extra_acl_invalid', htmlspecialchars($extra_acl))
|
|
||||||
);
|
|
||||||
unset($extra_acls[$i]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$domains = array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains'));
|
|
||||||
if (filter_var($extra_acl, FILTER_VALIDATE_EMAIL)) {
|
|
||||||
$extra_acl_domain = idn_to_ascii(substr(strstr($extra_acl, '@'), 1), 0, INTL_IDNA_VARIANT_UTS46);
|
|
||||||
if (in_array($extra_acl_domain, $domains)) {
|
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||||
'msg' => array('extra_acl_invalid_domain', $extra_acl_domain)
|
'msg' => array('extra_acl_invalid', htmlspecialchars($extra_acl))
|
||||||
);
|
);
|
||||||
unset($extra_acls[$i]);
|
unset($extra_acls[$i]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
$domains = array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains'));
|
||||||
else {
|
if (filter_var($extra_acl, FILTER_VALIDATE_EMAIL)) {
|
||||||
if (in_array($extra_acl, $domains)) {
|
$extra_acl_domain = idn_to_ascii(substr(strstr($extra_acl, '@'), 1), 0, INTL_IDNA_VARIANT_UTS46);
|
||||||
$_SESSION['return'][] = array(
|
if (in_array($extra_acl_domain, $domains)) {
|
||||||
'type' => 'danger',
|
$_SESSION['return'][] = array(
|
||||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
'type' => 'danger',
|
||||||
'msg' => array('extra_acl_invalid_domain', $extra_acl_domain)
|
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||||
);
|
'msg' => array('extra_acl_invalid_domain', $extra_acl_domain)
|
||||||
unset($extra_acls[$i]);
|
);
|
||||||
continue;
|
unset($extra_acls[$i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (in_array($extra_acl, $domains)) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||||
|
'msg' => array('extra_acl_invalid_domain', $extra_acl_domain)
|
||||||
|
);
|
||||||
|
unset($extra_acls[$i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$extra_acl = '@' . $extra_acl;
|
||||||
}
|
}
|
||||||
$extra_acl = '@' . $extra_acl;
|
|
||||||
}
|
}
|
||||||
}
|
$extra_acls = array_filter($extra_acls);
|
||||||
$extra_acls = array_filter($extra_acls);
|
$extra_acls = array_values($extra_acls);
|
||||||
$extra_acls = array_values($extra_acls);
|
$extra_acls = array_unique($extra_acls);
|
||||||
$extra_acls = array_unique($extra_acls);
|
$stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `external` = 1 AND `logged_in_as` = :username");
|
||||||
$stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `external` = 1 AND `logged_in_as` = :username");
|
|
||||||
$stmt->execute(array(
|
|
||||||
':username' => $username
|
|
||||||
));
|
|
||||||
foreach ($extra_acls as $sender_acl_external) {
|
|
||||||
$stmt = $pdo->prepare("INSERT INTO `sender_acl` (`send_as`, `logged_in_as`, `external`)
|
|
||||||
VALUES (:sender_acl, :username, 1)");
|
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':sender_acl' => $sender_acl_external,
|
|
||||||
':username' => $username
|
':username' => $username
|
||||||
));
|
));
|
||||||
|
foreach ($extra_acls as $sender_acl_external) {
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO `sender_acl` (`send_as`, `logged_in_as`, `external`)
|
||||||
|
VALUES (:sender_acl, :username, 1)");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':sender_acl' => $sender_acl_external,
|
||||||
|
':username' => $username
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isset($_data['sender_acl'])) {
|
if (isset($_data['sender_acl'])) {
|
||||||
@@ -3129,7 +3132,10 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||||
'msg' => array('mailbox_modified', $username)
|
'msg' => array('mailbox_modified', $username)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
update_sogo_static_view($username);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
break;
|
break;
|
||||||
case 'mailbox_templates':
|
case 'mailbox_templates':
|
||||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||||
@@ -3959,6 +3965,39 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
}
|
}
|
||||||
return $aliasdomaindata;
|
return $aliasdomaindata;
|
||||||
break;
|
break;
|
||||||
|
case 'shared_aliases':
|
||||||
|
$shared_aliases = array();
|
||||||
|
$stmt = $pdo->query("SELECT `address` FROM `alias`
|
||||||
|
WHERE `goto` REGEXP ','
|
||||||
|
AND `address` NOT LIKE '@%'
|
||||||
|
AND `goto` != `address`");
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
while($row = array_shift($rows)) {
|
||||||
|
$domain = explode("@", $row['address'])[1];
|
||||||
|
if (hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
|
||||||
|
$shared_aliases[] = $row['address'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $shared_aliases;
|
||||||
|
break;
|
||||||
|
case 'direct_aliases':
|
||||||
|
$direct_aliases = array();
|
||||||
|
$stmt = $pdo->query("SELECT `address` FROM `alias`
|
||||||
|
WHERE `goto` NOT LIKE '%,%'
|
||||||
|
AND `address` NOT LIKE '@%'
|
||||||
|
AND `goto` != `address`");
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
while($row = array_shift($rows)) {
|
||||||
|
$domain = explode("@", $row['address'])[1];
|
||||||
|
if (hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
|
||||||
|
$direct_aliases[] = $row['address'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $direct_aliases;
|
||||||
|
break;
|
||||||
case 'domains':
|
case 'domains':
|
||||||
$domains = array();
|
$domains = array();
|
||||||
if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
|
if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
|
||||||
@@ -4950,9 +4989,10 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':username' => $username
|
':username' => $username
|
||||||
));
|
));
|
||||||
$stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `logged_in_as` = :username");
|
$stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `logged_in_as` = :logged_in_as OR `send_as` = :send_as");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':username' => $username
|
':logged_in_as' => $username,
|
||||||
|
':send_as' => $username
|
||||||
));
|
));
|
||||||
// fk, better safe than sorry
|
// fk, better safe than sorry
|
||||||
$stmt = $pdo->prepare("DELETE FROM `user_acl` WHERE `username` = :username");
|
$stmt = $pdo->prepare("DELETE FROM `user_acl` WHERE `username` = :username");
|
||||||
@@ -5052,12 +5092,15 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update_sogo_static_view($username);
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'success',
|
'type' => 'success',
|
||||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||||
'msg' => array('mailbox_removed', htmlspecialchars($username))
|
'msg' => array('mailbox_removed', htmlspecialchars($username))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
break;
|
break;
|
||||||
case 'mailbox_templates':
|
case 'mailbox_templates':
|
||||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||||
@@ -5171,15 +5214,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
if (!is_array($tags)) $tags = array();
|
if (!is_array($tags)) $tags = array();
|
||||||
|
|
||||||
|
|
||||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
|
||||||
$_SESSION['return'][] = array(
|
|
||||||
'type' => 'danger',
|
|
||||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
|
||||||
'msg' => 'access_denied'
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$wasModified = false;
|
$wasModified = false;
|
||||||
foreach ($domains as $domain) {
|
foreach ($domains as $domain) {
|
||||||
if (!is_valid_domain_name($domain)) {
|
if (!is_valid_domain_name($domain)) {
|
||||||
@@ -5190,6 +5224,14 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||||
|
'msg' => 'access_denied'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
foreach($tags as $tag){
|
foreach($tags as $tag){
|
||||||
// delete tag
|
// delete tag
|
||||||
@@ -5264,7 +5306,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ($_action != 'get' && in_array($_type, array('domain', 'alias', 'alias_domain', 'mailbox', 'resource'))) {
|
if ($_action != 'get' && in_array($_type, array('domain', 'alias', 'alias_domain', 'resource')) && getenv('SKIP_SOGO') != "y") {
|
||||||
update_sogo_static_view();
|
update_sogo_static_view();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@ function init_db_schema() {
|
|||||||
try {
|
try {
|
||||||
global $pdo;
|
global $pdo;
|
||||||
|
|
||||||
$db_version = "23122022_1445";
|
$db_version = "14022023_1000";
|
||||||
|
|
||||||
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
|
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
|
||||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||||
@@ -664,6 +664,19 @@ function init_db_schema() {
|
|||||||
),
|
),
|
||||||
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
||||||
),
|
),
|
||||||
|
"da_sso" => array(
|
||||||
|
"cols" => array(
|
||||||
|
"username" => "VARCHAR(255) NOT NULL",
|
||||||
|
"token" => "VARCHAR(255) NOT NULL",
|
||||||
|
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
|
||||||
|
),
|
||||||
|
"keys" => array(
|
||||||
|
"primary" => array(
|
||||||
|
"" => array("token", "created")
|
||||||
|
),
|
||||||
|
),
|
||||||
|
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
||||||
|
),
|
||||||
"imapsync" => array(
|
"imapsync" => array(
|
||||||
"cols" => array(
|
"cols" => array(
|
||||||
"id" => "INT NOT NULL AUTO_INCREMENT",
|
"id" => "INT NOT NULL AUTO_INCREMENT",
|
||||||
@@ -1083,7 +1096,7 @@ function init_db_schema() {
|
|||||||
$stmt = $pdo->query("SHOW TABLES LIKE '" . $table . "'");
|
$stmt = $pdo->query("SHOW TABLES LIKE '" . $table . "'");
|
||||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||||
if ($num_results != 0) {
|
if ($num_results != 0) {
|
||||||
$stmt = $pdo->prepare("SELECT CONCAT('ALTER TABLE ', `table_schema`, '.', `table_name`, ' DROP FOREIGN KEY ', `constraint_name`, ';') AS `FKEY_DROP` FROM `information_schema`.`table_constraints`
|
$stmt = $pdo->prepare("SELECT CONCAT('ALTER TABLE `', `table_schema`, '`.', `table_name`, ' DROP FOREIGN KEY ', `constraint_name`, ';') AS `FKEY_DROP` FROM `information_schema`.`table_constraints`
|
||||||
WHERE `constraint_type` = 'FOREIGN KEY' AND `table_name` = :table;");
|
WHERE `constraint_type` = 'FOREIGN KEY' AND `table_name` = :table;");
|
||||||
$stmt->execute(array(':table' => $table));
|
$stmt->execute(array(':table' => $table));
|
||||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
@@ -1,4 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
|
// SSO Domain Admin
|
||||||
|
if (!empty($_GET['sso_token'])) {
|
||||||
|
$username = domain_admin_sso('check', $_GET['sso_token']);
|
||||||
|
|
||||||
|
if ($username !== false) {
|
||||||
|
$_SESSION['mailcow_cc_username'] = $username;
|
||||||
|
$_SESSION['mailcow_cc_role'] = 'domainadmin';
|
||||||
|
header('Location: /mailbox');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($_POST["verify_tfa_login"])) {
|
if (isset($_POST["verify_tfa_login"])) {
|
||||||
if (verify_tfa_login($_SESSION['pending_mailcow_cc_username'], $_POST)) {
|
if (verify_tfa_login($_SESSION['pending_mailcow_cc_username'], $_POST)) {
|
||||||
$_SESSION['mailcow_cc_username'] = $_SESSION['pending_mailcow_cc_username'];
|
$_SESSION['mailcow_cc_username'] = $_SESSION['pending_mailcow_cc_username'];
|
||||||
@@ -52,7 +63,7 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
|
|||||||
unset($_SESSION['index_query_string']);
|
unset($_SESSION['index_query_string']);
|
||||||
if (in_array('mobileconfig', $http_parameters)) {
|
if (in_array('mobileconfig', $http_parameters)) {
|
||||||
if (in_array('only_email', $http_parameters)) {
|
if (in_array('only_email', $http_parameters)) {
|
||||||
header("Location: /mobileconfig.php?email_only");
|
header("Location: /mobileconfig.php?only_email");
|
||||||
die();
|
die();
|
||||||
}
|
}
|
||||||
header("Location: /mobileconfig.php");
|
header("Location: /mobileconfig.php");
|
||||||
|
@@ -124,7 +124,7 @@ $MAILCOW_APPS = array(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Rows until pagination begins
|
// Rows until pagination begins
|
||||||
$PAGINATION_SIZE = 20;
|
$PAGINATION_SIZE = 25;
|
||||||
|
|
||||||
// Default number of rows/lines to display (log table)
|
// Default number of rows/lines to display (log table)
|
||||||
$LOG_LINES = 1000;
|
$LOG_LINES = 1000;
|
||||||
|
@@ -4,20 +4,20 @@
|
|||||||
*
|
*
|
||||||
* To rebuild or modify this file with the latest versions of the included
|
* To rebuild or modify this file with the latest versions of the included
|
||||||
* software please visit:
|
* software please visit:
|
||||||
* https://datatables.net/download/#bs5/dt-1.12.0/r-2.3.0/sl-1.4.0
|
* https://datatables.net/download/#bs5/dt-1.13.1/r-2.4.0/sl-1.5.0
|
||||||
*
|
*
|
||||||
* Included libraries:
|
* Included libraries:
|
||||||
* DataTables 1.12.0, Responsive 2.3.0, Select 1.4.0
|
* DataTables 1.13.1, Responsive 2.4.0, Select 1.5.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! DataTables 1.12.0
|
/*! DataTables 1.13.1
|
||||||
* ©2008-2022 SpryMedia Ltd - datatables.net/license
|
* ©2008-2022 SpryMedia Ltd - datatables.net/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary DataTables
|
* @summary DataTables
|
||||||
* @description Paginate, search and order HTML tables
|
* @description Paginate, search and order HTML tables
|
||||||
* @version 1.12.0
|
* @version 1.13.1
|
||||||
* @author SpryMedia Ltd
|
* @author SpryMedia Ltd
|
||||||
* @contact www.datatables.net
|
* @contact www.datatables.net
|
||||||
* @copyright SpryMedia Ltd.
|
* @copyright SpryMedia Ltd.
|
||||||
@@ -1162,6 +1162,10 @@
|
|||||||
$( rowOne[0] ).children('th, td').each( function (i, cell) {
|
$( rowOne[0] ).children('th, td').each( function (i, cell) {
|
||||||
var col = oSettings.aoColumns[i];
|
var col = oSettings.aoColumns[i];
|
||||||
|
|
||||||
|
if (! col) {
|
||||||
|
_fnLog( oSettings, 0, 'Incorrect column count', 18 );
|
||||||
|
}
|
||||||
|
|
||||||
if ( col.mData === i ) {
|
if ( col.mData === i ) {
|
||||||
var sort = a( cell, 'sort' ) || a( cell, 'order' );
|
var sort = a( cell, 'sort' ) || a( cell, 'order' );
|
||||||
var filter = a( cell, 'filter' ) || a( cell, 'search' );
|
var filter = a( cell, 'filter' ) || a( cell, 'search' );
|
||||||
@@ -3166,6 +3170,11 @@
|
|||||||
create = nTrIn ? false : true;
|
create = nTrIn ? false : true;
|
||||||
|
|
||||||
nTd = create ? document.createElement( oCol.sCellType ) : anTds[i];
|
nTd = create ? document.createElement( oCol.sCellType ) : anTds[i];
|
||||||
|
|
||||||
|
if (! nTd) {
|
||||||
|
_fnLog( oSettings, 0, 'Incorrect column count', 18 );
|
||||||
|
}
|
||||||
|
|
||||||
nTd._DT_CellIndex = {
|
nTd._DT_CellIndex = {
|
||||||
row: iRow,
|
row: iRow,
|
||||||
column: i
|
column: i
|
||||||
@@ -3316,10 +3325,16 @@
|
|||||||
|
|
||||||
for ( i=0, ien=cells.length ; i<ien ; i++ ) {
|
for ( i=0, ien=cells.length ; i<ien ; i++ ) {
|
||||||
column = columns[i];
|
column = columns[i];
|
||||||
column.nTf = cells[i].cell;
|
|
||||||
|
|
||||||
if ( column.sClass ) {
|
if (column) {
|
||||||
$(column.nTf).addClass( column.sClass );
|
column.nTf = cells[i].cell;
|
||||||
|
|
||||||
|
if ( column.sClass ) {
|
||||||
|
$(column.nTf).addClass( column.sClass );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_fnLog( oSettings, 0, 'Incorrect column count', 18 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5079,6 +5094,10 @@
|
|||||||
_fnDraw( settings );
|
_fnDraw( settings );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
// No change event - paging was called, but no change
|
||||||
|
_fnCallbackFire( settings, null, 'page-nc', [settings] );
|
||||||
|
}
|
||||||
|
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
@@ -5348,6 +5367,7 @@
|
|||||||
footerCopy = footer.clone().prependTo( table );
|
footerCopy = footer.clone().prependTo( table );
|
||||||
footerTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized
|
footerTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized
|
||||||
footerSrcEls = footerCopy.find('tr');
|
footerSrcEls = footerCopy.find('tr');
|
||||||
|
footerCopy.find('[id]').removeAttr('id');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clone the current header and footer elements and then place it into the inner table
|
// Clone the current header and footer elements and then place it into the inner table
|
||||||
@@ -5355,6 +5375,7 @@
|
|||||||
headerTrgEls = header.find('tr'); // original header is in its own table
|
headerTrgEls = header.find('tr'); // original header is in its own table
|
||||||
headerSrcEls = headerCopy.find('tr');
|
headerSrcEls = headerCopy.find('tr');
|
||||||
headerCopy.find('th, td').removeAttr('tabindex');
|
headerCopy.find('th, td').removeAttr('tabindex');
|
||||||
|
headerCopy.find('[id]').removeAttr('id');
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -8333,7 +8354,11 @@
|
|||||||
$(document).on('plugin-init.dt', function (e, context) {
|
$(document).on('plugin-init.dt', function (e, context) {
|
||||||
var api = new _Api( context );
|
var api = new _Api( context );
|
||||||
|
|
||||||
api.on( 'stateSaveParams', function ( e, settings, d ) {
|
const namespace = 'on-plugin-init';
|
||||||
|
const stateSaveParamsEvent = `stateSaveParams.${namespace}`;
|
||||||
|
const destroyEvent = `destroy.${namespace}`;
|
||||||
|
|
||||||
|
api.on( stateSaveParamsEvent, function ( e, settings, d ) {
|
||||||
// This could be more compact with the API, but it is a lot faster as a simple
|
// This could be more compact with the API, but it is a lot faster as a simple
|
||||||
// internal loop
|
// internal loop
|
||||||
var idFn = settings.rowIdFn;
|
var idFn = settings.rowIdFn;
|
||||||
@@ -8347,7 +8372,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
d.childRows = ids;
|
d.childRows = ids;
|
||||||
})
|
});
|
||||||
|
|
||||||
|
api.on( destroyEvent, function () {
|
||||||
|
api.off(`${stateSaveParamsEvent} ${destroyEvent}`);
|
||||||
|
});
|
||||||
|
|
||||||
var loaded = api.state.loaded();
|
var loaded = api.state.loaded();
|
||||||
|
|
||||||
@@ -9668,7 +9697,7 @@
|
|||||||
* @type string
|
* @type string
|
||||||
* @default Version number
|
* @default Version number
|
||||||
*/
|
*/
|
||||||
DataTable.version = "1.12.0";
|
DataTable.version = "1.13.1";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private data store, containing all of the settings objects that are
|
* Private data store, containing all of the settings objects that are
|
||||||
@@ -14092,7 +14121,7 @@
|
|||||||
*
|
*
|
||||||
* @type string
|
* @type string
|
||||||
*/
|
*/
|
||||||
build:"bs5/dt-1.12.0/r-2.3.0/sl-1.4.0",
|
build:"bs5/dt-1.13.1/r-2.4.0/sl-1.5.0",
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -14730,7 +14759,7 @@
|
|||||||
var classes = settings.oClasses;
|
var classes = settings.oClasses;
|
||||||
var lang = settings.oLanguage.oPaginate;
|
var lang = settings.oLanguage.oPaginate;
|
||||||
var aria = settings.oLanguage.oAria.paginate || {};
|
var aria = settings.oLanguage.oAria.paginate || {};
|
||||||
var btnDisplay, btnClass, counter=0;
|
var btnDisplay, btnClass;
|
||||||
|
|
||||||
var attach = function( container, buttons ) {
|
var attach = function( container, buttons ) {
|
||||||
var i, ien, node, button, tabIndex;
|
var i, ien, node, button, tabIndex;
|
||||||
@@ -14805,7 +14834,7 @@
|
|||||||
'class': classes.sPageButton+' '+btnClass,
|
'class': classes.sPageButton+' '+btnClass,
|
||||||
'aria-controls': settings.sTableId,
|
'aria-controls': settings.sTableId,
|
||||||
'aria-label': aria[ button ],
|
'aria-label': aria[ button ],
|
||||||
'data-dt-idx': counter,
|
'data-dt-idx': button,
|
||||||
'tabindex': tabIndex,
|
'tabindex': tabIndex,
|
||||||
'id': idx === 0 && typeof button === 'string' ?
|
'id': idx === 0 && typeof button === 'string' ?
|
||||||
settings.sTableId +'_'+ button :
|
settings.sTableId +'_'+ button :
|
||||||
@@ -14817,8 +14846,6 @@
|
|||||||
_fnBindAction(
|
_fnBindAction(
|
||||||
node, {action: button}, clickHandler
|
node, {action: button}, clickHandler
|
||||||
);
|
);
|
||||||
|
|
||||||
counter++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15163,7 +15190,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (window.luxon) {
|
else if (window.luxon) {
|
||||||
dt = format
|
dt = format && typeof d === 'string'
|
||||||
? window.luxon.DateTime.fromFormat( d, format )
|
? window.luxon.DateTime.fromFormat( d, format )
|
||||||
: window.luxon.DateTime.fromISO( d );
|
: window.luxon.DateTime.fromISO( d );
|
||||||
|
|
||||||
@@ -15303,14 +15330,26 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Based on locale, determine standard number formatting
|
// Based on locale, determine standard number formatting
|
||||||
var __thousands = '';
|
// Fallback for legacy browsers is US English
|
||||||
var __decimal = '';
|
var __thousands = ',';
|
||||||
|
var __decimal = '.';
|
||||||
|
|
||||||
if (Intl) {
|
if (Intl) {
|
||||||
var num = new Intl.NumberFormat().formatToParts(1000.1);
|
try {
|
||||||
|
var num = new Intl.NumberFormat().formatToParts(100000.1);
|
||||||
|
|
||||||
__thousands = num[1].value;
|
for (var i=0 ; i<num.length ; i++) {
|
||||||
__decimal = num[3].value;
|
if (num[i].type === 'group') {
|
||||||
|
__thousands = num[i].value;
|
||||||
|
}
|
||||||
|
else if (num[i].type === 'decimal') {
|
||||||
|
__decimal = num[i].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Formatted date time detection - use by declaring the formats you are going to use
|
// Formatted date time detection - use by declaring the formats you are going to use
|
||||||
@@ -15379,6 +15418,10 @@
|
|||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (d === '' || d === null) {
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
var negative = d < 0 ? '-' : '';
|
var negative = d < 0 ? '-' : '';
|
||||||
var flo = parseFloat( d );
|
var flo = parseFloat( d );
|
||||||
|
|
||||||
@@ -15578,14 +15621,6 @@
|
|||||||
* 2020 SpryMedia Ltd - datatables.net/license
|
* 2020 SpryMedia Ltd - datatables.net/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
* DataTables integration for Bootstrap 4. This requires Bootstrap 5 and
|
|
||||||
* DataTables 1.10 or newer.
|
|
||||||
*
|
|
||||||
* This file sets the defaults and adds options to DataTables to style its
|
|
||||||
* controls using Bootstrap. See http://datatables.net/manual/styling/bootstrap
|
|
||||||
* for further information.
|
|
||||||
*/
|
|
||||||
(function( factory ){
|
(function( factory ){
|
||||||
if ( typeof define === 'function' && define.amd ) {
|
if ( typeof define === 'function' && define.amd ) {
|
||||||
// AMD
|
// AMD
|
||||||
@@ -15597,16 +15632,22 @@
|
|||||||
// CommonJS
|
// CommonJS
|
||||||
module.exports = function (root, $) {
|
module.exports = function (root, $) {
|
||||||
if ( ! root ) {
|
if ( ! root ) {
|
||||||
|
// CommonJS environments without a window global must pass a
|
||||||
|
// root. This will give an error otherwise
|
||||||
root = window;
|
root = window;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! $ || ! $.fn.dataTable ) {
|
if ( ! $ ) {
|
||||||
// Require DataTables, which attaches to jQuery, including
|
$ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window
|
||||||
// jQuery if needed and have a $ property so we can access the
|
require('jquery') :
|
||||||
// jQuery object that is used
|
require('jquery')( root );
|
||||||
$ = require('datatables.net')(root, $).$;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( ! $.fn.dataTable ) {
|
||||||
|
require('datatables.net')(root, $);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return factory( $, root, root.document );
|
return factory( $, root, root.document );
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -15619,11 +15660,21 @@
|
|||||||
var DataTable = $.fn.dataTable;
|
var DataTable = $.fn.dataTable;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DataTables integration for Bootstrap 5. This requires Bootstrap 5 and
|
||||||
|
* DataTables 1.10 or newer.
|
||||||
|
*
|
||||||
|
* This file sets the defaults and adds options to DataTables to style its
|
||||||
|
* controls using Bootstrap. See http://datatables.net/manual/styling/bootstrap
|
||||||
|
* for further information.
|
||||||
|
*/
|
||||||
|
|
||||||
/* Set the defaults for DataTables initialisation */
|
/* Set the defaults for DataTables initialisation */
|
||||||
$.extend( true, DataTable.defaults, {
|
$.extend( true, DataTable.defaults, {
|
||||||
dom:
|
dom:
|
||||||
"<'row'<'col-sm-12 col-md-6'l><'col-sm-12 col-md-6'f>>" +
|
"<'row'<'col-sm-12 col-md-6'l><'col-sm-12 col-md-6'f>>" +
|
||||||
"<'row'<'col-sm-12'tr>>" +
|
"<'row dt-row'<'col-sm-12'tr>>" +
|
||||||
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
renderer: 'bootstrap'
|
renderer: 'bootstrap'
|
||||||
} );
|
} );
|
||||||
@@ -15645,7 +15696,7 @@ DataTable.ext.renderer.pageButton.bootstrap = function ( settings, host, idx, bu
|
|||||||
var classes = settings.oClasses;
|
var classes = settings.oClasses;
|
||||||
var lang = settings.oLanguage.oPaginate;
|
var lang = settings.oLanguage.oPaginate;
|
||||||
var aria = settings.oLanguage.oAria.paginate || {};
|
var aria = settings.oLanguage.oAria.paginate || {};
|
||||||
var btnDisplay, btnClass, counter=0;
|
var btnDisplay, btnClass;
|
||||||
|
|
||||||
var attach = function( container, buttons ) {
|
var attach = function( container, buttons ) {
|
||||||
var i, ien, node, button;
|
var i, ien, node, button;
|
||||||
@@ -15714,7 +15765,7 @@ DataTable.ext.renderer.pageButton.bootstrap = function ( settings, host, idx, bu
|
|||||||
'href': '#',
|
'href': '#',
|
||||||
'aria-controls': settings.sTableId,
|
'aria-controls': settings.sTableId,
|
||||||
'aria-label': aria[ button ],
|
'aria-label': aria[ button ],
|
||||||
'data-dt-idx': counter,
|
'data-dt-idx': button,
|
||||||
'tabindex': settings.iTabIndex,
|
'tabindex': settings.iTabIndex,
|
||||||
'class': 'page-link'
|
'class': 'page-link'
|
||||||
} )
|
} )
|
||||||
@@ -15725,13 +15776,12 @@ DataTable.ext.renderer.pageButton.bootstrap = function ( settings, host, idx, bu
|
|||||||
settings.oApi._fnBindAction(
|
settings.oApi._fnBindAction(
|
||||||
node, {action: button}, clickHandler
|
node, {action: button}, clickHandler
|
||||||
);
|
);
|
||||||
|
|
||||||
counter++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var hostEl = $(host);
|
||||||
// IE9 throws an 'unknown error' if document.activeElement is used
|
// IE9 throws an 'unknown error' if document.activeElement is used
|
||||||
// inside an iframe or frame.
|
// inside an iframe or frame.
|
||||||
var activeEl;
|
var activeEl;
|
||||||
@@ -15741,17 +15791,26 @@ DataTable.ext.renderer.pageButton.bootstrap = function ( settings, host, idx, bu
|
|||||||
// elements, focus is lost on the select button which is bad for
|
// elements, focus is lost on the select button which is bad for
|
||||||
// accessibility. So we want to restore focus once the draw has
|
// accessibility. So we want to restore focus once the draw has
|
||||||
// completed
|
// completed
|
||||||
activeEl = $(host).find(document.activeElement).data('dt-idx');
|
activeEl = hostEl.find(document.activeElement).data('dt-idx');
|
||||||
}
|
}
|
||||||
catch (e) {}
|
catch (e) {}
|
||||||
|
|
||||||
|
var paginationEl = hostEl.children('ul.pagination');
|
||||||
|
|
||||||
|
if (paginationEl.length) {
|
||||||
|
paginationEl.empty();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
paginationEl = hostEl.html('<ul/>').children('ul').addClass('pagination');
|
||||||
|
}
|
||||||
|
|
||||||
attach(
|
attach(
|
||||||
$(host).empty().html('<ul class="pagination"/>').children('ul'),
|
paginationEl,
|
||||||
buttons
|
buttons
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( activeEl !== undefined ) {
|
if ( activeEl !== undefined ) {
|
||||||
$(host).find( '[data-dt-idx='+activeEl+']' ).trigger('focus');
|
hostEl.find('[data-dt-idx='+activeEl+']').trigger('focus');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -15760,14 +15819,54 @@ return DataTable;
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
/*! Responsive 2.3.0
|
/*! Responsive 2.4.0
|
||||||
* 2014-2022 SpryMedia Ltd - datatables.net/license
|
* 2014-2022 SpryMedia Ltd - datatables.net/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
(function( factory ){
|
||||||
|
if ( typeof define === 'function' && define.amd ) {
|
||||||
|
// AMD
|
||||||
|
define( ['jquery', 'datatables.net'], function ( $ ) {
|
||||||
|
return factory( $, window, document );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
else if ( typeof exports === 'object' ) {
|
||||||
|
// CommonJS
|
||||||
|
module.exports = function (root, $) {
|
||||||
|
if ( ! root ) {
|
||||||
|
// CommonJS environments without a window global must pass a
|
||||||
|
// root. This will give an error otherwise
|
||||||
|
root = window;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! $ ) {
|
||||||
|
$ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window
|
||||||
|
require('jquery') :
|
||||||
|
require('jquery')( root );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! $.fn.dataTable ) {
|
||||||
|
require('datatables.net')(root, $);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return factory( $, root, root.document );
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Browser
|
||||||
|
factory( jQuery, window, document );
|
||||||
|
}
|
||||||
|
}(function( $, window, document, undefined ) {
|
||||||
|
'use strict';
|
||||||
|
var DataTable = $.fn.dataTable;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Responsive
|
* @summary Responsive
|
||||||
* @description Responsive tables plug-in for DataTables
|
* @description Responsive tables plug-in for DataTables
|
||||||
* @version 2.3.0
|
* @version 2.4.0
|
||||||
* @author SpryMedia Ltd (www.sprymedia.co.uk)
|
* @author SpryMedia Ltd (www.sprymedia.co.uk)
|
||||||
* @contact www.sprymedia.co.uk/contact
|
* @contact www.sprymedia.co.uk/contact
|
||||||
* @copyright SpryMedia Ltd.
|
* @copyright SpryMedia Ltd.
|
||||||
@@ -15781,35 +15880,6 @@ return DataTable;
|
|||||||
*
|
*
|
||||||
* For details please refer to: http://www.datatables.net
|
* For details please refer to: http://www.datatables.net
|
||||||
*/
|
*/
|
||||||
(function( factory ){
|
|
||||||
if ( typeof define === 'function' && define.amd ) {
|
|
||||||
// AMD
|
|
||||||
define( ['jquery', 'datatables.net'], function ( $ ) {
|
|
||||||
return factory( $, window, document );
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
else if ( typeof exports === 'object' ) {
|
|
||||||
// CommonJS
|
|
||||||
module.exports = function (root, $) {
|
|
||||||
if ( ! root ) {
|
|
||||||
root = window;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! $ || ! $.fn.dataTable ) {
|
|
||||||
$ = require('datatables.net')(root, $).$;
|
|
||||||
}
|
|
||||||
|
|
||||||
return factory( $, root, root.document );
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Browser
|
|
||||||
factory( jQuery, window, document );
|
|
||||||
}
|
|
||||||
}(function( $, window, document, undefined ) {
|
|
||||||
'use strict';
|
|
||||||
var DataTable = $.fn.dataTable;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Responsive is a plug-in for the DataTables library that makes use of
|
* Responsive is a plug-in for the DataTables library that makes use of
|
||||||
@@ -15863,9 +15933,10 @@ var Responsive = function ( settings, opts ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.s = {
|
this.s = {
|
||||||
dt: new DataTable.Api( settings ),
|
childNodeStore: {},
|
||||||
columns: [],
|
columns: [],
|
||||||
current: []
|
current: [],
|
||||||
|
dt: new DataTable.Api( settings )
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if responsive has already been initialised on this table
|
// Check if responsive has already been initialised on this table
|
||||||
@@ -16070,6 +16141,63 @@ $.extend( Responsive.prototype, {
|
|||||||
* Private methods
|
* Private methods
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get and store nodes from a cell - use for node moving renderers
|
||||||
|
*
|
||||||
|
* @param {*} dt DT instance
|
||||||
|
* @param {*} row Row index
|
||||||
|
* @param {*} col Column index
|
||||||
|
*/
|
||||||
|
_childNodes: function( dt, row, col ) {
|
||||||
|
var name = row+'-'+col;
|
||||||
|
|
||||||
|
if ( this.s.childNodeStore[ name ] ) {
|
||||||
|
return this.s.childNodeStore[ name ];
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://jsperf.com/childnodes-array-slice-vs-loop
|
||||||
|
var nodes = [];
|
||||||
|
var children = dt.cell( row, col ).node().childNodes;
|
||||||
|
for ( var i=0, ien=children.length ; i<ien ; i++ ) {
|
||||||
|
nodes.push( children[i] );
|
||||||
|
}
|
||||||
|
|
||||||
|
this.s.childNodeStore[ name ] = nodes;
|
||||||
|
|
||||||
|
return nodes;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore nodes from the cache to a table cell
|
||||||
|
*
|
||||||
|
* @param {*} dt DT instance
|
||||||
|
* @param {*} row Row index
|
||||||
|
* @param {*} col Column index
|
||||||
|
*/
|
||||||
|
_childNodesRestore: function( dt, row, col ) {
|
||||||
|
var name = row+'-'+col;
|
||||||
|
|
||||||
|
if ( ! this.s.childNodeStore[ name ] ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var node = dt.cell( row, col ).node();
|
||||||
|
var store = this.s.childNodeStore[ name ];
|
||||||
|
var parent = store[0].parentNode;
|
||||||
|
var parentChildren = parent.childNodes;
|
||||||
|
var a = [];
|
||||||
|
|
||||||
|
for ( var i=0, ien=parentChildren.length ; i<ien ; i++ ) {
|
||||||
|
a.push( parentChildren[i] );
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( var j=0, jen=a.length ; j<jen ; j++ ) {
|
||||||
|
node.appendChild( a[j] );
|
||||||
|
}
|
||||||
|
|
||||||
|
this.s.childNodeStore[ name ] = undefined;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the visibility for the columns in a table for a given
|
* Calculate the visibility for the columns in a table for a given
|
||||||
* breakpoint. The result is pre-determined based on the class logic if
|
* breakpoint. The result is pre-determined based on the class logic if
|
||||||
@@ -16399,8 +16527,8 @@ $.extend( Responsive.prototype, {
|
|||||||
: details.renderer;
|
: details.renderer;
|
||||||
|
|
||||||
var res = details.display( row, update, function () {
|
var res = details.display( row, update, function () {
|
||||||
return renderer(
|
return renderer.call(
|
||||||
dt, row[0], that._detailsObj(row[0])
|
that, dt, row[0], that._detailsObj(row[0])
|
||||||
);
|
);
|
||||||
} );
|
} );
|
||||||
|
|
||||||
@@ -16622,9 +16750,11 @@ $.extend( Responsive.prototype, {
|
|||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
if ( changed ) {
|
// Always need to update the display, regardless of if it has changed or not, so nodes
|
||||||
this._redrawChildren();
|
// can be re-inserted for listHiddenNodes
|
||||||
|
this._redrawChildren();
|
||||||
|
|
||||||
|
if ( changed ) {
|
||||||
// Inform listeners of the change
|
// Inform listeners of the change
|
||||||
$(dt.table().node()).trigger( 'responsive-resize.dt', [dt, this.s.current] );
|
$(dt.table().node()).trigger( 'responsive-resize.dt', [dt, this.s.current] );
|
||||||
|
|
||||||
@@ -16650,6 +16780,7 @@ $.extend( Responsive.prototype, {
|
|||||||
{
|
{
|
||||||
var dt = this.s.dt;
|
var dt = this.s.dt;
|
||||||
var columns = this.s.columns;
|
var columns = this.s.columns;
|
||||||
|
var that = this;
|
||||||
|
|
||||||
// Are we allowed to do auto sizing?
|
// Are we allowed to do auto sizing?
|
||||||
if ( ! this.c.auto ) {
|
if ( ! this.c.auto ) {
|
||||||
@@ -16663,11 +16794,11 @@ $.extend( Responsive.prototype, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Need to restore all children. They will be reinstated by a re-render
|
// Need to restore all children. They will be reinstated by a re-render
|
||||||
if ( ! $.isEmptyObject( _childNodeStore ) ) {
|
if ( ! $.isEmptyObject( this.s.childNodeStore ) ) {
|
||||||
$.each( _childNodeStore, function ( key ) {
|
$.each( this.s.childNodeStore, function ( key ) {
|
||||||
var idx = key.split('-');
|
var idx = key.split('-');
|
||||||
|
|
||||||
_childNodesRestore( dt, idx[0]*1, idx[1]*1 );
|
that._childNodesRestore( dt, idx[0]*1, idx[1]*1 );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16787,6 +16918,7 @@ $.extend( Responsive.prototype, {
|
|||||||
*/
|
*/
|
||||||
_setColumnVis: function ( col, showHide )
|
_setColumnVis: function ( col, showHide )
|
||||||
{
|
{
|
||||||
|
var that = this;
|
||||||
var dt = this.s.dt;
|
var dt = this.s.dt;
|
||||||
var display = showHide ? '' : 'none'; // empty string will remove the attr
|
var display = showHide ? '' : 'none'; // empty string will remove the attr
|
||||||
|
|
||||||
@@ -16803,9 +16935,9 @@ $.extend( Responsive.prototype, {
|
|||||||
.toggleClass('dtr-hidden', !showHide);
|
.toggleClass('dtr-hidden', !showHide);
|
||||||
|
|
||||||
// If the are child nodes stored, we might need to reinsert them
|
// If the are child nodes stored, we might need to reinsert them
|
||||||
if ( ! $.isEmptyObject( _childNodeStore ) ) {
|
if ( ! $.isEmptyObject( this.s.childNodeStore ) ) {
|
||||||
dt.cells( null, col ).indexes().each( function (idx) {
|
dt.cells( null, col ).indexes().each( function (idx) {
|
||||||
_childNodesRestore( dt, idx.row, idx.column );
|
that._childNodesRestore( dt, idx.row, idx.column );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -16972,52 +17104,6 @@ Responsive.display = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
var _childNodeStore = {};
|
|
||||||
|
|
||||||
function _childNodes( dt, row, col ) {
|
|
||||||
var name = row+'-'+col;
|
|
||||||
|
|
||||||
if ( _childNodeStore[ name ] ) {
|
|
||||||
return _childNodeStore[ name ];
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://jsperf.com/childnodes-array-slice-vs-loop
|
|
||||||
var nodes = [];
|
|
||||||
var children = dt.cell( row, col ).node().childNodes;
|
|
||||||
for ( var i=0, ien=children.length ; i<ien ; i++ ) {
|
|
||||||
nodes.push( children[i] );
|
|
||||||
}
|
|
||||||
|
|
||||||
_childNodeStore[ name ] = nodes;
|
|
||||||
|
|
||||||
return nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _childNodesRestore( dt, row, col ) {
|
|
||||||
var name = row+'-'+col;
|
|
||||||
|
|
||||||
if ( ! _childNodeStore[ name ] ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var node = dt.cell( row, col ).node();
|
|
||||||
var store = _childNodeStore[ name ];
|
|
||||||
var parent = store[0].parentNode;
|
|
||||||
var parentChildren = parent.childNodes;
|
|
||||||
var a = [];
|
|
||||||
|
|
||||||
for ( var i=0, ien=parentChildren.length ; i<ien ; i++ ) {
|
|
||||||
a.push( parentChildren[i] );
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( var j=0, jen=a.length ; j<jen ; j++ ) {
|
|
||||||
node.appendChild( a[j] );
|
|
||||||
}
|
|
||||||
|
|
||||||
_childNodeStore[ name ] = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display methods - functions which define how the hidden data should be shown
|
* Display methods - functions which define how the hidden data should be shown
|
||||||
* in the table.
|
* in the table.
|
||||||
@@ -17029,6 +17115,7 @@ function _childNodesRestore( dt, row, col ) {
|
|||||||
Responsive.renderer = {
|
Responsive.renderer = {
|
||||||
listHiddenNodes: function () {
|
listHiddenNodes: function () {
|
||||||
return function ( api, rowIdx, columns ) {
|
return function ( api, rowIdx, columns ) {
|
||||||
|
var that = this;
|
||||||
var ul = $('<ul data-dtr-index="'+rowIdx+'" class="dtr-details"/>');
|
var ul = $('<ul data-dtr-index="'+rowIdx+'" class="dtr-details"/>');
|
||||||
var found = false;
|
var found = false;
|
||||||
|
|
||||||
@@ -17045,7 +17132,7 @@ Responsive.renderer = {
|
|||||||
'</span> '+
|
'</span> '+
|
||||||
'</li>'
|
'</li>'
|
||||||
)
|
)
|
||||||
.append( $('<span class="dtr-data"/>').append( _childNodes( api, col.rowIndex, col.columnIndex ) ) )// api.cell( col.rowIndex, col.columnIndex ).node().childNodes ) )
|
.append( $('<span class="dtr-data"/>').append( that._childNodes( api, col.rowIndex, col.columnIndex ) ) )// api.cell( col.rowIndex, col.columnIndex ).node().childNodes ) )
|
||||||
.appendTo( ul );
|
.appendTo( ul );
|
||||||
|
|
||||||
found = true;
|
found = true;
|
||||||
@@ -17229,7 +17316,7 @@ Api.registerPlural( 'columns().responsiveHidden()', 'column().responsiveHidden()
|
|||||||
* @name Responsive.version
|
* @name Responsive.version
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
Responsive.version = '2.3.0';
|
Responsive.version = '2.4.0';
|
||||||
|
|
||||||
|
|
||||||
$.fn.dataTable.Responsive = Responsive;
|
$.fn.dataTable.Responsive = Responsive;
|
||||||
@@ -17256,12 +17343,12 @@ $(document).on( 'preInit.dt.dtr', function (e, settings, json) {
|
|||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
||||||
return Responsive;
|
return DataTable;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
/*! Bootstrap 5 integration for DataTables' Responsive
|
/*! Bootstrap 5 integration for DataTables' Responsive
|
||||||
* ©2021 SpryMedia Ltd - datatables.net/license
|
* © SpryMedia Ltd - datatables.net/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function( factory ){
|
(function( factory ){
|
||||||
@@ -17275,17 +17362,26 @@ return Responsive;
|
|||||||
// CommonJS
|
// CommonJS
|
||||||
module.exports = function (root, $) {
|
module.exports = function (root, $) {
|
||||||
if ( ! root ) {
|
if ( ! root ) {
|
||||||
|
// CommonJS environments without a window global must pass a
|
||||||
|
// root. This will give an error otherwise
|
||||||
root = window;
|
root = window;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! $ || ! $.fn.dataTable ) {
|
if ( ! $ ) {
|
||||||
$ = require('datatables.net-bs5')(root, $).$;
|
$ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window
|
||||||
|
require('jquery') :
|
||||||
|
require('jquery')( root );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! $.fn.dataTable.Responsive ) {
|
if ( ! $.fn.dataTable ) {
|
||||||
|
require('datatables.net-bs5')(root, $);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! $.fn.dataTable ) {
|
||||||
require('datatables.net-responsive')(root, $);
|
require('datatables.net-responsive')(root, $);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return factory( $, root, root.document );
|
return factory( $, root, root.document );
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -17298,6 +17394,7 @@ return Responsive;
|
|||||||
var DataTable = $.fn.dataTable;
|
var DataTable = $.fn.dataTable;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var _display = DataTable.Responsive.display;
|
var _display = DataTable.Responsive.display;
|
||||||
var _original = _display.modal;
|
var _original = _display.modal;
|
||||||
var _modal = $(
|
var _modal = $(
|
||||||
@@ -17359,33 +17456,14 @@ _display.modal = function ( options ) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
return DataTable.Responsive;
|
return DataTable;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
/*! Select for DataTables 1.4.0
|
/*! Select for DataTables 1.5.0
|
||||||
* 2015-2021 SpryMedia Ltd - datatables.net/license/mit
|
* 2015-2021 SpryMedia Ltd - datatables.net/license/mit
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Select for DataTables
|
|
||||||
* @description A collection of API methods, events and buttons for DataTables
|
|
||||||
* that provides selection options of the items in a DataTable
|
|
||||||
* @version 1.4.0
|
|
||||||
* @file dataTables.select.js
|
|
||||||
* @author SpryMedia Ltd (www.sprymedia.co.uk)
|
|
||||||
* @contact datatables.net/forums
|
|
||||||
* @copyright Copyright 2015-2021 SpryMedia Ltd.
|
|
||||||
*
|
|
||||||
* This source file is free software, available under the following license:
|
|
||||||
* MIT license - http://datatables.net/license/mit
|
|
||||||
*
|
|
||||||
* This source file is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
|
|
||||||
*
|
|
||||||
* For details please refer to: http://www.datatables.net/extensions/select
|
|
||||||
*/
|
|
||||||
(function( factory ){
|
(function( factory ){
|
||||||
if ( typeof define === 'function' && define.amd ) {
|
if ( typeof define === 'function' && define.amd ) {
|
||||||
// AMD
|
// AMD
|
||||||
@@ -17397,13 +17475,22 @@ return DataTable.Responsive;
|
|||||||
// CommonJS
|
// CommonJS
|
||||||
module.exports = function (root, $) {
|
module.exports = function (root, $) {
|
||||||
if ( ! root ) {
|
if ( ! root ) {
|
||||||
|
// CommonJS environments without a window global must pass a
|
||||||
|
// root. This will give an error otherwise
|
||||||
root = window;
|
root = window;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! $ || ! $.fn.dataTable ) {
|
if ( ! $ ) {
|
||||||
$ = require('datatables.net')(root, $).$;
|
$ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window
|
||||||
|
require('jquery') :
|
||||||
|
require('jquery')( root );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( ! $.fn.dataTable ) {
|
||||||
|
require('datatables.net')(root, $);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return factory( $, root, root.document );
|
return factory( $, root, root.document );
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -17416,10 +17503,11 @@ return DataTable.Responsive;
|
|||||||
var DataTable = $.fn.dataTable;
|
var DataTable = $.fn.dataTable;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Version information for debugger
|
// Version information for debugger
|
||||||
DataTable.select = {};
|
DataTable.select = {};
|
||||||
|
|
||||||
DataTable.select.version = '1.4.0';
|
DataTable.select.version = '1.5.0';
|
||||||
|
|
||||||
DataTable.select.init = function ( dt ) {
|
DataTable.select.init = function ( dt ) {
|
||||||
var ctx = dt.settings()[0];
|
var ctx = dt.settings()[0];
|
||||||
@@ -18688,7 +18776,6 @@ $(document).on( 'preInit.dt.dtSelect', function (e, ctx) {
|
|||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
||||||
return DataTable.select;
|
return DataTable;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,3 +1,13 @@
|
|||||||
|
const LOCALE = undefined;
|
||||||
|
const DATETIME_FORMAT = {
|
||||||
|
year: "numeric",
|
||||||
|
month: "2-digit",
|
||||||
|
day: "2-digit",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
second: "2-digit"
|
||||||
|
};
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
// mailcow alert box generator
|
// mailcow alert box generator
|
||||||
window.mailcow_alert_box = function(message, type) {
|
window.mailcow_alert_box = function(message, type) {
|
||||||
@@ -12,14 +22,22 @@ $(document).ready(function() {
|
|||||||
$.notify({message: msg},{z_index: 20000, delay: auto_hide, type: type,placement: {from: "bottom",align: "right"},animate: {enter: 'animated fadeInUp',exit: 'animated fadeOutDown'}});
|
$.notify({message: msg},{z_index: 20000, delay: auto_hide, type: type,placement: {from: "bottom",align: "right"},animate: {enter: 'animated fadeInUp',exit: 'animated fadeOutDown'}});
|
||||||
}
|
}
|
||||||
|
|
||||||
$(".generate_password").click(function( event ) {
|
$(".generate_password").click(async function( event ) {
|
||||||
|
try {
|
||||||
|
var password_policy = await window.fetch("/api/v1/get/passwordpolicy", { method:'GET', cache:'no-cache' });
|
||||||
|
var password_policy = await password_policy.json();
|
||||||
|
random_passwd_length = password_policy.length;
|
||||||
|
} catch(err) {
|
||||||
|
var random_passwd_length = 8;
|
||||||
|
}
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
$('[data-hibp]').trigger('input');
|
$('[data-hibp]').trigger('input');
|
||||||
if (typeof($(this).closest("form").data('pwgen-length')) == "number") {
|
if (typeof($(this).closest("form").data('pwgen-length')) == "number") {
|
||||||
var random_passwd = GPW.pronounceable($(this).closest("form").data('pwgen-length'))
|
var random_passwd = GPW.pronounceable($(this).closest("form").data('pwgen-length'))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var random_passwd = GPW.pronounceable(8)
|
var random_passwd = GPW.pronounceable(random_passwd_length)
|
||||||
}
|
}
|
||||||
$(this).closest("form").find('[data-pwgen-field]').attr('type', 'text');
|
$(this).closest("form").find('[data-pwgen-field]').attr('type', 'text');
|
||||||
$(this).closest("form").find('[data-pwgen-field]').val(random_passwd);
|
$(this).closest("form").find('[data-pwgen-field]').val(random_passwd);
|
||||||
@@ -278,6 +296,8 @@ $(document).ready(function() {
|
|||||||
$.extend($.fn.dataTable.defaults, {
|
$.extend($.fn.dataTable.defaults, {
|
||||||
responsive: true
|
responsive: true
|
||||||
});
|
});
|
||||||
|
// disable default datatable click listener
|
||||||
|
$(document).off('click', 'tbody>tr');
|
||||||
|
|
||||||
// tag boxes
|
// tag boxes
|
||||||
$('.tag-box .tag-add').click(function(){
|
$('.tag-box .tag-add').click(function(){
|
||||||
|
@@ -47,9 +47,9 @@ jQuery(function($){
|
|||||||
$('button[data-id="' + regex_map_id + '"]').attr({"disabled": false});
|
$('button[data-id="' + regex_map_id + '"]').attr({"disabled": false});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$('.textarea-code').on('keyup', function() {
|
$('.textarea-code').on('keyup', function() {
|
||||||
$('.submit_rspamd_regex').attr({"disabled": true});
|
$('.submit_rspamd_regex').attr({"disabled": true});
|
||||||
});
|
});
|
||||||
$("#show_rspamd_global_filters").click(function() {
|
$("#show_rspamd_global_filters").click(function() {
|
||||||
$.get("inc/ajax/show_rspamd_global_filters.php");
|
$.get("inc/ajax/show_rspamd_global_filters.php");
|
||||||
$("#confirm_show_rspamd_global_filters").hide();
|
$("#confirm_show_rspamd_global_filters").hide();
|
||||||
@@ -70,8 +70,14 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
|
|
||||||
$('#domainadminstable').DataTable({
|
$('#domainadminstable').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
@@ -81,55 +87,55 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
// placeholder, so checkbox will not block child row toggle
|
// placeholder, so checkbox will not block child row toggle
|
||||||
title: '',
|
title: '',
|
||||||
data: null,
|
data: null,
|
||||||
searchable: false,
|
searchable: false,
|
||||||
orderable: false,
|
orderable: false,
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '',
|
title: '',
|
||||||
data: 'chkbox',
|
data: 'chkbox',
|
||||||
searchable: false,
|
searchable: false,
|
||||||
orderable: false,
|
orderable: false,
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.username,
|
title: lang.username,
|
||||||
data: 'username',
|
data: 'username',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.admin_domains,
|
title: lang.admin_domains,
|
||||||
data: 'selected_domains',
|
data: 'selected_domains',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "TFA",
|
title: "TFA",
|
||||||
data: 'tfa_active',
|
data: 'tfa_active',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
if(data == 1) return '<i class="bi bi-check-lg"></i>';
|
if(data == 1) return '<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>';
|
||||||
else return '<i class="bi bi-x-lg"></i>'
|
else return '<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.active,
|
title: lang.active,
|
||||||
data: 'active',
|
data: 'active',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
if(data == 1) return '<i class="bi bi-check-lg"></i>';
|
if(data == 1) return '<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>';
|
||||||
else return '<i class="bi bi-x-lg"></i>'
|
else return '<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.action,
|
title: lang.action,
|
||||||
data: 'action',
|
data: 'action',
|
||||||
className: 'text-md-end dt-sm-head-hidden dt-body-right',
|
className: 'dt-sm-head-hidden dt-text-right',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
initComplete: function(settings, json){
|
initComplete: function(settings, json){
|
||||||
}
|
}
|
||||||
@@ -143,8 +149,14 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
|
|
||||||
$('#oauth2clientstable').DataTable({
|
$('#oauth2clientstable').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
@@ -154,47 +166,47 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
// placeholder, so checkbox will not block child row toggle
|
// placeholder, so checkbox will not block child row toggle
|
||||||
title: '',
|
title: '',
|
||||||
data: null,
|
data: null,
|
||||||
searchable: false,
|
searchable: false,
|
||||||
orderable: false,
|
orderable: false,
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '',
|
title: '',
|
||||||
data: 'chkbox',
|
data: 'chkbox',
|
||||||
searchable: false,
|
searchable: false,
|
||||||
orderable: false,
|
orderable: false,
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'ID',
|
title: 'ID',
|
||||||
data: 'id',
|
data: 'id',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.oauth2_client_id,
|
title: lang.oauth2_client_id,
|
||||||
data: 'client_id',
|
data: 'client_id',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.oauth2_client_secret,
|
title: lang.oauth2_client_secret,
|
||||||
data: 'client_secret',
|
data: 'client_secret',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.oauth2_redirect_uri,
|
title: lang.oauth2_redirect_uri,
|
||||||
data: 'redirect_uri',
|
data: 'redirect_uri',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.action,
|
title: lang.action,
|
||||||
data: 'action',
|
data: 'action',
|
||||||
className: 'text-md-end dt-sm-head-hidden dt-body-right',
|
className: 'dt-sm-head-hidden dt-text-right',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -206,8 +218,14 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
|
|
||||||
$('#adminstable').DataTable({
|
$('#adminstable').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
@@ -217,50 +235,50 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
// placeholder, so checkbox will not block child row toggle
|
// placeholder, so checkbox will not block child row toggle
|
||||||
title: '',
|
title: '',
|
||||||
data: null,
|
data: null,
|
||||||
searchable: false,
|
searchable: false,
|
||||||
orderable: false,
|
orderable: false,
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '',
|
title: '',
|
||||||
data: 'chkbox',
|
data: 'chkbox',
|
||||||
searchable: false,
|
searchable: false,
|
||||||
orderable: false,
|
orderable: false,
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.username,
|
title: lang.username,
|
||||||
data: 'username',
|
data: 'username',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "TFA",
|
title: "TFA",
|
||||||
data: 'tfa_active',
|
data: 'tfa_active',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
if(data == 1) return '<i class="bi bi-check-lg"></i>';
|
if(data == 1) return '<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>';
|
||||||
else return '<i class="bi bi-x-lg"></i>'
|
else return '<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.active,
|
title: lang.active,
|
||||||
data: 'active',
|
data: 'active',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
if(data == 1) return '<i class="bi bi-check-lg"></i>';
|
if(data == 1) return '<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>';
|
||||||
else return '<i class="bi bi-x-lg"></i>'
|
else return '<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.action,
|
title: lang.action,
|
||||||
data: 'action',
|
data: 'action',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
className: 'text-md-end dt-sm-head-hidden dt-body-right'
|
className: 'dt-sm-head-hidden dt-text-right'
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -272,8 +290,14 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
|
|
||||||
$('#forwardinghoststable').DataTable({
|
$('#forwardinghoststable').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
@@ -283,42 +307,45 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
// placeholder, so checkbox will not block child row toggle
|
// placeholder, so checkbox will not block child row toggle
|
||||||
title: '',
|
title: '',
|
||||||
data: null,
|
data: null,
|
||||||
searchable: false,
|
searchable: false,
|
||||||
orderable: false,
|
orderable: false,
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '',
|
title: '',
|
||||||
data: 'chkbox',
|
data: 'chkbox',
|
||||||
searchable: false,
|
searchable: false,
|
||||||
orderable: false,
|
orderable: false,
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.host,
|
title: lang.host,
|
||||||
data: 'host',
|
data: 'host',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.source,
|
title: lang.source,
|
||||||
data: 'source',
|
data: 'source',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.spamfilter,
|
title: lang.spamfilter,
|
||||||
data: 'keep_spam',
|
data: 'keep_spam',
|
||||||
defaultContent: ''
|
defaultContent: '',
|
||||||
},
|
render: function(data, type){
|
||||||
{
|
return 'yes'==data?'<i class="bi bi-x-lg"><span class="sorting-value">yes</span></i>':'no'==data&&'<i class="bi bi-check-lg"><span class="sorting-value">no</span></i>';
|
||||||
title: lang.action,
|
}
|
||||||
data: 'action',
|
},
|
||||||
className: 'text-md-end dt-sm-head-hidden dt-body-right',
|
{
|
||||||
defaultContent: ''
|
title: lang.action,
|
||||||
},
|
data: 'action',
|
||||||
|
className: 'dt-sm-head-hidden dt-text-right',
|
||||||
|
defaultContent: ''
|
||||||
|
},
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -330,8 +357,14 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
|
|
||||||
$('#relayhoststable').DataTable({
|
$('#relayhoststable').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
@@ -341,56 +374,56 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
// placeholder, so checkbox will not block child row toggle
|
// placeholder, so checkbox will not block child row toggle
|
||||||
title: '',
|
title: '',
|
||||||
data: null,
|
data: null,
|
||||||
searchable: false,
|
searchable: false,
|
||||||
orderable: false,
|
orderable: false,
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '',
|
title: '',
|
||||||
data: 'chkbox',
|
data: 'chkbox',
|
||||||
searchable: false,
|
searchable: false,
|
||||||
orderable: false,
|
orderable: false,
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'ID',
|
title: 'ID',
|
||||||
data: 'id',
|
data: 'id',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.host,
|
title: lang.host,
|
||||||
data: 'hostname',
|
data: 'hostname',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.username,
|
title: lang.username,
|
||||||
data: 'username',
|
data: 'username',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.in_use_by,
|
title: lang.in_use_by,
|
||||||
data: 'in_use_by',
|
data: 'in_use_by',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.active,
|
title: lang.active,
|
||||||
data: 'active',
|
data: 'active',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
if(data == 1) return '<i class="bi bi-check-lg"></i>';
|
if(data == 1) return '<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>';
|
||||||
else return '<i class="bi bi-x-lg"></i>'
|
else return '<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.action,
|
title: lang.action,
|
||||||
data: 'action',
|
data: 'action',
|
||||||
className: 'text-md-end dt-sm-head-hidden dt-body-right',
|
className: 'dt-sm-head-hidden dt-text-right',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -402,8 +435,14 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
|
|
||||||
$('#transportstable').DataTable({
|
$('#transportstable').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
@@ -413,56 +452,56 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
// placeholder, so checkbox will not block child row toggle
|
// placeholder, so checkbox will not block child row toggle
|
||||||
title: '',
|
title: '',
|
||||||
data: null,
|
data: null,
|
||||||
searchable: false,
|
searchable: false,
|
||||||
orderable: false,
|
orderable: false,
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '',
|
title: '',
|
||||||
data: 'chkbox',
|
data: 'chkbox',
|
||||||
searchable: false,
|
searchable: false,
|
||||||
orderable: false,
|
orderable: false,
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'ID',
|
title: 'ID',
|
||||||
data: 'id',
|
data: 'id',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.destination,
|
title: lang.destination,
|
||||||
data: 'destination',
|
data: 'destination',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.nexthop,
|
title: lang.nexthop,
|
||||||
data: 'nexthop',
|
data: 'nexthop',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.username,
|
title: lang.username,
|
||||||
data: 'username',
|
data: 'username',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.active,
|
title: lang.active,
|
||||||
data: 'active',
|
data: 'active',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
if(data == 1) return '<i class="bi bi-check-lg"></i>';
|
if(data == 1) return '<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>';
|
||||||
else return '<i class="bi bi-x-lg"></i>'
|
else return '<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.action,
|
title: lang.action,
|
||||||
data: 'action',
|
data: 'action',
|
||||||
className: 'text-md-end dt-sm-head-hidden dt-body-right',
|
className: 'dt-sm-head-hidden dt-text-right',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -612,15 +651,15 @@ jQuery(function($){
|
|||||||
$(this).prop("disabled",true);
|
$(this).prop("disabled",true);
|
||||||
$(this).html('<i class="bi bi-arrow-repeat icon-spin"></i> ');
|
$(this).html('<i class="bi bi-arrow-repeat icon-spin"></i> ');
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
url: 'inc/ajax/relay_check.php',
|
url: 'inc/ajax/relay_check.php',
|
||||||
dataType: 'text',
|
dataType: 'text',
|
||||||
data: $('#test_relayhost_form').serialize(),
|
data: $('#test_relayhost_form').serialize(),
|
||||||
complete: function (data) {
|
complete: function (data) {
|
||||||
$('#test_relayhost_result').html(data.responseText);
|
$('#test_relayhost_result').html(data.responseText);
|
||||||
$('#test_relayhost').prop("disabled",false);
|
$('#test_relayhost').prop("disabled",false);
|
||||||
$('#test_relayhost').text(prev);
|
$('#test_relayhost').text(prev);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
// Transport
|
// Transport
|
||||||
@@ -638,15 +677,15 @@ jQuery(function($){
|
|||||||
$(this).prop("disabled",true);
|
$(this).prop("disabled",true);
|
||||||
$(this).html('<div class="spinner-border" role="status"><span class="visually-hidden">Loading...</span></div> ');
|
$(this).html('<div class="spinner-border" role="status"><span class="visually-hidden">Loading...</span></div> ');
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
url: 'inc/ajax/transport_check.php',
|
url: 'inc/ajax/transport_check.php',
|
||||||
dataType: 'text',
|
dataType: 'text',
|
||||||
data: $('#test_transport_form').serialize(),
|
data: $('#test_transport_form').serialize(),
|
||||||
complete: function (data) {
|
complete: function (data) {
|
||||||
$('#test_transport_result').html(data.responseText);
|
$('#test_transport_result').html(data.responseText);
|
||||||
$('#test_transport').prop("disabled",false);
|
$('#test_transport').prop("disabled",false);
|
||||||
$('#test_transport').text(prev);
|
$('#test_transport').text(prev);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
// DKIM private key modal
|
// DKIM private key modal
|
||||||
@@ -690,9 +729,9 @@ jQuery(function($){
|
|||||||
$(this).parents('tr').remove();
|
$(this).parents('tr').remove();
|
||||||
});
|
});
|
||||||
$('#add_app_link_row').click(function() {
|
$('#add_app_link_row').click(function() {
|
||||||
add_table_row($('#app_link_table'), "app_link");
|
add_table_row($('#app_link_table'), "app_link");
|
||||||
});
|
});
|
||||||
$('#add_f2b_regex_row').click(function() {
|
$('#add_f2b_regex_row').click(function() {
|
||||||
add_table_row($('#f2b_regex_table'), "f2b_regex");
|
add_table_row($('#f2b_regex_table'), "f2b_regex");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,13 +1,3 @@
|
|||||||
const LOCALE = undefined;
|
|
||||||
const DATETIME_FORMAT = {
|
|
||||||
year: "numeric",
|
|
||||||
month: "2-digit",
|
|
||||||
day: "2-digit",
|
|
||||||
hour: "2-digit",
|
|
||||||
minute: "2-digit",
|
|
||||||
second: "2-digit"
|
|
||||||
};
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
// Parse seconds ago to date
|
// Parse seconds ago to date
|
||||||
// Get "now" timestamp
|
// Get "now" timestamp
|
||||||
@@ -34,7 +24,7 @@ $(document).ready(function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// set update loop container list
|
// set update loop container list
|
||||||
containersToUpdate = {}
|
containersToUpdate = {};
|
||||||
// set default ChartJs Font Color
|
// set default ChartJs Font Color
|
||||||
Chart.defaults.color = '#999';
|
Chart.defaults.color = '#999';
|
||||||
// create host cpu and mem charts
|
// create host cpu and mem charts
|
||||||
@@ -43,15 +33,47 @@ $(document).ready(function() {
|
|||||||
if (mailcow_info.branch === "master"){
|
if (mailcow_info.branch === "master"){
|
||||||
check_update(mailcow_info.version_tag, mailcow_info.project_url);
|
check_update(mailcow_info.version_tag, mailcow_info.project_url);
|
||||||
}
|
}
|
||||||
$("#maiclow_version").click(function(){
|
$("#mailcow_version").click(function(){
|
||||||
if (mailcow_cc_role !== "admin" && mailcow_cc_role !== "domainadmin" ||
|
if (mailcow_cc_role !== "admin" && mailcow_cc_role !== "domainadmin" || mailcow_info.branch !== "master")
|
||||||
mailcow_info.branch !== "master")
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
showVersionModal("Version " + mailcow_info.version_tag, mailcow_info.version_tag);
|
showVersionModal("Version " + mailcow_info.version_tag, mailcow_info.version_tag);
|
||||||
})
|
})
|
||||||
// get public ips
|
// get public ips
|
||||||
get_public_ips();
|
$("#host_show_ip").click(function(){
|
||||||
|
$("#host_show_ip").find(".text").addClass("d-none");
|
||||||
|
$("#host_show_ip").find(".spinner-border").removeClass("d-none");
|
||||||
|
|
||||||
|
window.fetch("/api/v1/get/status/host/ip", { method:'GET', cache:'no-cache' }).then(function(response) {
|
||||||
|
return response.json();
|
||||||
|
}).then(function(data) {
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
// display host ips
|
||||||
|
if (data.ipv4)
|
||||||
|
$("#host_ipv4").text(data.ipv4);
|
||||||
|
if (data.ipv6)
|
||||||
|
$("#host_ipv6").text(data.ipv6);
|
||||||
|
|
||||||
|
$("#host_show_ip").addClass("d-none");
|
||||||
|
$("#host_show_ip").find(".text").removeClass("d-none");
|
||||||
|
$("#host_show_ip").find(".spinner-border").addClass("d-none");
|
||||||
|
$("#host_ipv4").removeClass("d-none");
|
||||||
|
$("#host_ipv6").removeClass("d-none");
|
||||||
|
$("#host_ipv6").removeClass("text-danger");
|
||||||
|
$("#host_ipv4").addClass("d-block");
|
||||||
|
$("#host_ipv6").addClass("d-block");
|
||||||
|
}).catch(function(error){
|
||||||
|
console.log(error);
|
||||||
|
|
||||||
|
$("#host_ipv6").removeClass("d-none");
|
||||||
|
$("#host_ipv6").addClass("d-block");
|
||||||
|
$("#host_ipv6").addClass("text-danger");
|
||||||
|
$("#host_ipv6").text(lang_debug.error_show_ip);
|
||||||
|
$("#host_show_ip").find(".text").removeClass("d-none");
|
||||||
|
$("#host_show_ip").find(".spinner-border").addClass("d-none");
|
||||||
|
});
|
||||||
|
});
|
||||||
update_container_stats();
|
update_container_stats();
|
||||||
});
|
});
|
||||||
jQuery(function($){
|
jQuery(function($){
|
||||||
@@ -85,11 +107,20 @@ jQuery(function($){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#autodiscover_log').DataTable({
|
var table = $('#autodiscover_log').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: log_pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
order: [[0, 'desc']],
|
order: [[0, 'desc']],
|
||||||
|
initComplete: function(){
|
||||||
|
hideTableExpandCollapseBtn('#tab-autodiscover-logs', '#autodiscover_log');
|
||||||
|
},
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: "/api/v1/get/logs/autodiscover/100",
|
url: "/api/v1/get/logs/autodiscover/100",
|
||||||
@@ -134,6 +165,10 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
table.on('responsive-resize', function (e, datatable, columns){
|
||||||
|
hideTableExpandCollapseBtn('#tab-autodiscover-logs', '#autodiscover_log');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function draw_postfix_logs() {
|
function draw_postfix_logs() {
|
||||||
// just recalc width if instance already exists
|
// just recalc width if instance already exists
|
||||||
@@ -142,11 +177,20 @@ jQuery(function($){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#postfix_log').DataTable({
|
var table = $('#postfix_log').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: log_pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
order: [[0, 'desc']],
|
order: [[0, 'desc']],
|
||||||
|
initComplete: function(){
|
||||||
|
hideTableExpandCollapseBtn('#tab-postfix-logs', '#postfix_log');
|
||||||
|
},
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: "/api/v1/get/logs/postfix",
|
url: "/api/v1/get/logs/postfix",
|
||||||
@@ -176,6 +220,10 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
table.on('responsive-resize', function (e, datatable, columns){
|
||||||
|
hideTableExpandCollapseBtn('#tab-postfix-logs', '#postfix_log');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function draw_watchdog_logs() {
|
function draw_watchdog_logs() {
|
||||||
// just recalc width if instance already exists
|
// just recalc width if instance already exists
|
||||||
@@ -184,11 +232,20 @@ jQuery(function($){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#watchdog_log').DataTable({
|
var table = $('#watchdog_log').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: log_pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
order: [[0, 'desc']],
|
order: [[0, 'desc']],
|
||||||
|
initComplete: function(){
|
||||||
|
hideTableExpandCollapseBtn('#tab-watchdog-logs', '#watchdog_log');
|
||||||
|
},
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: "/api/v1/get/logs/watchdog",
|
url: "/api/v1/get/logs/watchdog",
|
||||||
@@ -222,6 +279,10 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
table.on('responsive-resize', function (e, datatable, columns){
|
||||||
|
hideTableExpandCollapseBtn('#tab-watchdog-logs', '#watchdog_log');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function draw_api_logs() {
|
function draw_api_logs() {
|
||||||
// just recalc width if instance already exists
|
// just recalc width if instance already exists
|
||||||
@@ -230,11 +291,20 @@ jQuery(function($){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#api_log').DataTable({
|
var table = $('#api_log').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: log_pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
order: [[0, 'desc']],
|
order: [[0, 'desc']],
|
||||||
|
initComplete: function(){
|
||||||
|
hideTableExpandCollapseBtn('#tab-api-logs', '#api_log');
|
||||||
|
},
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: "/api/v1/get/logs/api",
|
url: "/api/v1/get/logs/api",
|
||||||
@@ -275,6 +345,10 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
table.on('responsive-resize', function (e, datatable, columns){
|
||||||
|
hideTableExpandCollapseBtn('#tab-api-logs', '#api_log');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function draw_rl_logs() {
|
function draw_rl_logs() {
|
||||||
// just recalc width if instance already exists
|
// just recalc width if instance already exists
|
||||||
@@ -283,11 +357,20 @@ jQuery(function($){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#rl_log').DataTable({
|
var table = $('#rl_log').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: log_pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
order: [[0, 'desc']],
|
order: [[0, 'desc']],
|
||||||
|
initComplete: function(){
|
||||||
|
hideTableExpandCollapseBtn('#tab-rl-logs', '#rl_log');
|
||||||
|
},
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: "/api/v1/get/logs/ratelimited",
|
url: "/api/v1/get/logs/ratelimited",
|
||||||
@@ -366,6 +449,10 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
table.on('responsive-resize', function (e, datatable, columns){
|
||||||
|
hideTableExpandCollapseBtn('#tab-rl-logs', '#rl_log');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function draw_ui_logs() {
|
function draw_ui_logs() {
|
||||||
// just recalc width if instance already exists
|
// just recalc width if instance already exists
|
||||||
@@ -374,11 +461,20 @@ jQuery(function($){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#ui_logs').DataTable({
|
var table = $('#ui_logs').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: log_pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
order: [[0, 'desc']],
|
order: [[0, 'desc']],
|
||||||
|
initComplete: function(){
|
||||||
|
hideTableExpandCollapseBtn('#tab-ui-logs', '#ui_logs');
|
||||||
|
},
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: "/api/v1/get/logs/ui",
|
url: "/api/v1/get/logs/ui",
|
||||||
@@ -437,6 +533,10 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
table.on('responsive-resize', function (e, datatable, columns){
|
||||||
|
hideTableExpandCollapseBtn('#tab-ui-logs', '#ui_log');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function draw_sasl_logs() {
|
function draw_sasl_logs() {
|
||||||
// just recalc width if instance already exists
|
// just recalc width if instance already exists
|
||||||
@@ -445,11 +545,20 @@ jQuery(function($){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#sasl_logs').DataTable({
|
var table = $('#sasl_logs').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: log_pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
order: [[0, 'desc']],
|
order: [[0, 'desc']],
|
||||||
|
initComplete: function(){
|
||||||
|
hideTableExpandCollapseBtn('#tab-sasl-logs', '#sasl_logs');
|
||||||
|
},
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: "/api/v1/get/logs/sasl",
|
url: "/api/v1/get/logs/sasl",
|
||||||
@@ -479,12 +588,16 @@ jQuery(function($){
|
|||||||
data: 'datetime',
|
data: 'datetime',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
createdCell: function(td, cellData) {
|
createdCell: function(td, cellData) {
|
||||||
cellData = Math.floor((new Date(data.replace(/-/g, "/"))).getTime() / 1000);
|
cellData = Math.floor((new Date(cellData.replace(/-/g, "/"))).getTime() / 1000);
|
||||||
createSortableDate(td, cellData)
|
createSortableDate(td, cellData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
table.on('responsive-resize', function (e, datatable, columns){
|
||||||
|
hideTableExpandCollapseBtn('#tab-sasl-logs', '#sasl_logs');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function draw_acme_logs() {
|
function draw_acme_logs() {
|
||||||
// just recalc width if instance already exists
|
// just recalc width if instance already exists
|
||||||
@@ -493,11 +606,20 @@ jQuery(function($){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#acme_log').DataTable({
|
var table = $('#acme_log').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: log_pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
order: [[0, 'desc']],
|
order: [[0, 'desc']],
|
||||||
|
initComplete: function(){
|
||||||
|
hideTableExpandCollapseBtn('#tab-acme-logs', '#acme_log');
|
||||||
|
},
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: "/api/v1/get/logs/acme",
|
url: "/api/v1/get/logs/acme",
|
||||||
@@ -522,6 +644,10 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
table.on('responsive-resize', function (e, datatable, columns){
|
||||||
|
hideTableExpandCollapseBtn('#tab-acme-logs', '#acme_log');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function draw_netfilter_logs() {
|
function draw_netfilter_logs() {
|
||||||
// just recalc width if instance already exists
|
// just recalc width if instance already exists
|
||||||
@@ -530,11 +656,20 @@ jQuery(function($){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#netfilter_log').DataTable({
|
var table = $('#netfilter_log').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: log_pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
order: [[0, 'desc']],
|
order: [[0, 'desc']],
|
||||||
|
initComplete: function(){
|
||||||
|
hideTableExpandCollapseBtn('#tab-netfilter-logs', '#netfilter_log');
|
||||||
|
},
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: "/api/v1/get/logs/netfilter",
|
url: "/api/v1/get/logs/netfilter",
|
||||||
@@ -564,6 +699,10 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
table.on('responsive-resize', function (e, datatable, columns){
|
||||||
|
hideTableExpandCollapseBtn('#tab-netfilter-logs', '#netfilter_log');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function draw_sogo_logs() {
|
function draw_sogo_logs() {
|
||||||
// just recalc width if instance already exists
|
// just recalc width if instance already exists
|
||||||
@@ -572,11 +711,20 @@ jQuery(function($){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#sogo_log').DataTable({
|
var table = $('#sogo_log').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: log_pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
order: [[0, 'desc']],
|
order: [[0, 'desc']],
|
||||||
|
initComplete: function(){
|
||||||
|
hideTableExpandCollapseBtn('#tab-sogo-logs', '#sogo_log');
|
||||||
|
},
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: "/api/v1/get/logs/sogo",
|
url: "/api/v1/get/logs/sogo",
|
||||||
@@ -606,6 +754,10 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
table.on('responsive-resize', function (e, datatable, columns){
|
||||||
|
hideTableExpandCollapseBtn('#tab-sogo-logs', '#sogo_log');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function draw_dovecot_logs() {
|
function draw_dovecot_logs() {
|
||||||
// just recalc width if instance already exists
|
// just recalc width if instance already exists
|
||||||
@@ -614,11 +766,20 @@ jQuery(function($){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#dovecot_log').DataTable({
|
var table = $('#dovecot_log').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: log_pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
order: [[0, 'desc']],
|
order: [[0, 'desc']],
|
||||||
|
initComplete: function(){
|
||||||
|
hideTableExpandCollapseBtn('#tab-dovecot-logs', '#dovecot_log');
|
||||||
|
},
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: "/api/v1/get/logs/dovecot",
|
url: "/api/v1/get/logs/dovecot",
|
||||||
@@ -648,19 +809,20 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
table.on('responsive-resize', function (e, datatable, columns){
|
||||||
|
hideTableExpandCollapseBtn('#tab-dovecot-logs', '#dovecot_log');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function rspamd_pie_graph() {
|
function rspamd_pie_graph() {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/api/v1/get/rspamd/actions',
|
url: '/api/v1/get/rspamd/actions',
|
||||||
async: true,
|
async: true,
|
||||||
success: function(data){
|
success: function(data){
|
||||||
console.log(data);
|
|
||||||
|
|
||||||
var total = 0;
|
var total = 0;
|
||||||
$(data).map(function(){total += this[1];});
|
$(data).map(function(){total += this[1];});
|
||||||
var labels = $.makeArray($(data).map(function(){return this[0] + ' ' + Math.round(this[1]/total * 100) + '%';}));
|
var labels = $.makeArray($(data).map(function(){return this[0] + ' ' + Math.round(this[1]/total * 100) + '%';}));
|
||||||
var values = $.makeArray($(data).map(function(){return this[1];}));
|
var values = $.makeArray($(data).map(function(){return this[1];}));
|
||||||
console.log(values);
|
|
||||||
|
|
||||||
var graphdata = {
|
var graphdata = {
|
||||||
labels: labels,
|
labels: labels,
|
||||||
@@ -717,11 +879,20 @@ jQuery(function($){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#rspamd_history').DataTable({
|
var table = $('#rspamd_history').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: log_pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
order: [[0, 'desc']],
|
order: [[0, 'desc']],
|
||||||
|
initComplete: function(){
|
||||||
|
hideTableExpandCollapseBtn('#tab-rspamd-logs', '#rspamd_history');
|
||||||
|
},
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: "/api/v1/get/logs/rspamd-history",
|
url: "/api/v1/get/logs/rspamd-history",
|
||||||
@@ -767,12 +938,15 @@ jQuery(function($){
|
|||||||
title: 'Score',
|
title: 'Score',
|
||||||
data: 'score',
|
data: 'score',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
|
class: 'text-nowrap',
|
||||||
createdCell: function(td, cellData) {
|
createdCell: function(td, cellData) {
|
||||||
$(td).attr({
|
$(td).attr({
|
||||||
"data-order": cellData.sortBy,
|
"data-order": cellData.sortBy,
|
||||||
"data-sort": cellData.sortBy
|
"data-sort": cellData.sortBy
|
||||||
});
|
});
|
||||||
$(td).html(cellData.value);
|
},
|
||||||
|
render: function (data) {
|
||||||
|
return data.value;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -795,7 +969,9 @@ jQuery(function($){
|
|||||||
"data-order": cellData.sortBy,
|
"data-order": cellData.sortBy,
|
||||||
"data-sort": cellData.sortBy
|
"data-sort": cellData.sortBy
|
||||||
});
|
});
|
||||||
$(td).html(cellData.value);
|
},
|
||||||
|
render: function (data) {
|
||||||
|
return data.value;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -810,6 +986,10 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
table.on('responsive-resize', function (e, datatable, columns){
|
||||||
|
hideTableExpandCollapseBtn('#tab-rspamd-history', '#rspamd_history');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function process_table_data(data, table) {
|
function process_table_data(data, table) {
|
||||||
if (table == 'rspamd_history') {
|
if (table == 'rspamd_history') {
|
||||||
@@ -821,31 +1001,31 @@ jQuery(function($){
|
|||||||
item.rcpt = escapeHtml(item.rcpt_smtp.join(", "));
|
item.rcpt = escapeHtml(item.rcpt_smtp.join(", "));
|
||||||
}
|
}
|
||||||
item.symbols = Object.keys(item.symbols).sort(function (a, b) {
|
item.symbols = Object.keys(item.symbols).sort(function (a, b) {
|
||||||
if (item.symbols[a].score === 0) return 1
|
if (item.symbols[a].score === 0) return 1;
|
||||||
if (item.symbols[b].score === 0) return -1
|
if (item.symbols[b].score === 0) return -1;
|
||||||
if (item.symbols[b].score < 0 && item.symbols[a].score < 0) {
|
if (item.symbols[b].score < 0 && item.symbols[a].score < 0) {
|
||||||
return item.symbols[a].score - item.symbols[b].score
|
return item.symbols[a].score - item.symbols[b].score;
|
||||||
}
|
}
|
||||||
if (item.symbols[b].score > 0 && item.symbols[a].score > 0) {
|
if (item.symbols[b].score > 0 && item.symbols[a].score > 0) {
|
||||||
return item.symbols[b].score - item.symbols[a].score
|
return item.symbols[b].score - item.symbols[a].score;
|
||||||
}
|
}
|
||||||
return item.symbols[b].score - item.symbols[a].score
|
return item.symbols[b].score - item.symbols[a].score;
|
||||||
}).map(function(key) {
|
}).map(function(key) {
|
||||||
var sym = item.symbols[key];
|
var sym = item.symbols[key];
|
||||||
if (sym.score < 0) {
|
if (sym.score < 0) {
|
||||||
sym.score_formatted = '(<span class="text-success"><b>' + sym.score + '</b></span>)'
|
sym.score_formatted = '(<span class="text-success"><b>' + sym.score + '</b></span>)';
|
||||||
}
|
}
|
||||||
else if (sym.score === 0) {
|
else if (sym.score === 0) {
|
||||||
sym.score_formatted = '(<span><b>' + sym.score + '</b></span>)'
|
sym.score_formatted = '(<span><b>' + sym.score + '</b></span>)';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sym.score_formatted = '(<span class="text-danger"><b>' + sym.score + '</b></span>)'
|
sym.score_formatted = '(<span class="text-danger"><b>' + sym.score + '</b></span>)';
|
||||||
}
|
}
|
||||||
var str = '<strong>' + key + '</strong> ' + sym.score_formatted;
|
var str = '<strong>' + key + '</strong> ' + sym.score_formatted;
|
||||||
if (sym.options) {
|
if (sym.options) {
|
||||||
str += ' [' + escapeHtml(sym.options.join(", ")) + "]";
|
str += ' [' + escapeHtml(sym.options.join(", ")) + "]";
|
||||||
}
|
}
|
||||||
return str
|
return str;
|
||||||
}).join('<br>\n');
|
}).join('<br>\n');
|
||||||
item.subject = escapeHtml(item.subject);
|
item.subject = escapeHtml(item.subject);
|
||||||
var scan_time = item.time_real.toFixed(3);
|
var scan_time = item.time_real.toFixed(3);
|
||||||
@@ -978,14 +1158,14 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return data
|
return data;
|
||||||
};
|
};
|
||||||
$('.add_log_lines').on('click', function (e) {
|
$('.add_log_lines').on('click', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var log_table= $(this).data("table")
|
var log_table= $(this).data("table");
|
||||||
var new_nrows = $(this).data("nrows")
|
var new_nrows = $(this).data("nrows");
|
||||||
var post_process = $(this).data("post-process")
|
var post_process = $(this).data("post-process");
|
||||||
var log_url = $(this).data("log-url")
|
var log_url = $(this).data("log-url");
|
||||||
if (log_table === undefined || new_nrows === undefined || post_process === undefined || log_url === undefined) {
|
if (log_table === undefined || new_nrows === undefined || post_process === undefined || log_url === undefined) {
|
||||||
console.log("no data-table or data-nrows or log_url or data-post-process attr found");
|
console.log("no data-table or data-nrows or log_url or data-post-process attr found");
|
||||||
return;
|
return;
|
||||||
@@ -993,7 +1173,7 @@ jQuery(function($){
|
|||||||
|
|
||||||
if (table = $('#' + log_table).DataTable()) {
|
if (table = $('#' + log_table).DataTable()) {
|
||||||
var heading = $('#' + log_table).closest('.card').find('.card-header');
|
var heading = $('#' + log_table).closest('.card').find('.card-header');
|
||||||
var load_rows = (table.page.len() + 1) + '-' + (table.page.len() + new_nrows)
|
var load_rows = (table.data().length + 1) + '-' + (table.data().length + new_nrows)
|
||||||
|
|
||||||
$.get('/api/v1/get/logs/' + log_url + '/' + load_rows).then(function(data){
|
$.get('/api/v1/get/logs/' + log_url + '/' + load_rows).then(function(data){
|
||||||
if (data.length === undefined) { mailcow_alert_box(lang.no_new_rows, "info"); return; }
|
if (data.length === undefined) { mailcow_alert_box(lang.no_new_rows, "info"); return; }
|
||||||
@@ -1005,6 +1185,12 @@ jQuery(function($){
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
function hideTableExpandCollapseBtn(tab, table){
|
||||||
|
if ($(table).hasClass('collapsed'))
|
||||||
|
$(tab).find(".table_collapse_option").show();
|
||||||
|
else
|
||||||
|
$(tab).find(".table_collapse_option").hide();
|
||||||
|
}
|
||||||
|
|
||||||
// detect element visibility changes
|
// detect element visibility changes
|
||||||
function onVisible(element, callback) {
|
function onVisible(element, callback) {
|
||||||
@@ -1037,7 +1223,6 @@ jQuery(function($){
|
|||||||
onVisible("[id^=rspamd_donut]", () => rspamd_pie_graph());
|
onVisible("[id^=rspamd_donut]", () => rspamd_pie_graph());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// start polling host stats if tab is active
|
// start polling host stats if tab is active
|
||||||
onVisible("[id^=tab-containers]", () => update_stats());
|
onVisible("[id^=tab-containers]", () => update_stats());
|
||||||
// start polling container stats if collapse is active
|
// start polling container stats if collapse is active
|
||||||
@@ -1109,6 +1294,12 @@ function update_stats(timeout=5){
|
|||||||
$("#host_cpu_usage").text(parseInt(data.cpu.usage).toString() + "%");
|
$("#host_cpu_usage").text(parseInt(data.cpu.usage).toString() + "%");
|
||||||
$("#host_memory_total").text((data.memory.total / (1024 ** 3)).toFixed(2).toString() + "GB");
|
$("#host_memory_total").text((data.memory.total / (1024 ** 3)).toFixed(2).toString() + "GB");
|
||||||
$("#host_memory_usage").text(parseInt(data.memory.usage).toString() + "%");
|
$("#host_memory_usage").text(parseInt(data.memory.usage).toString() + "%");
|
||||||
|
if (data.architecture == "aarch64"){
|
||||||
|
$("#host_architecture").html('<span data-bs-toggle="tooltip" data-bs-placement="top" title="' + lang_debug.wip +'">' + data.architecture + ' ⚠️</span>');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$("#host_architecture").html(data.architecture);
|
||||||
|
}
|
||||||
|
|
||||||
// update cpu and mem chart
|
// update cpu and mem chart
|
||||||
var cpu_chart = Chart.getChart("host_cpu_chart");
|
var cpu_chart = Chart.getChart("host_cpu_chart");
|
||||||
@@ -1120,9 +1311,9 @@ function update_stats(timeout=5){
|
|||||||
if (mem_chart.data.labels.length > 30) mem_chart.data.labels.shift();
|
if (mem_chart.data.labels.length > 30) mem_chart.data.labels.shift();
|
||||||
|
|
||||||
cpu_chart.data.datasets[0].data.push(data.cpu.usage);
|
cpu_chart.data.datasets[0].data.push(data.cpu.usage);
|
||||||
if (cpu_chart.data.datasets[0].data.length > 30) cpu_chart.data.datasets[0].data.shift();
|
if (cpu_chart.data.datasets[0].data.length > 30) cpu_chart.data.datasets[0].data.shift();
|
||||||
mem_chart.data.datasets[0].data.push(data.memory.usage);
|
mem_chart.data.datasets[0].data.push(data.memory.usage);
|
||||||
if (mem_chart.data.datasets[0].data.length > 30) mem_chart.data.datasets[0].data.shift();
|
if (mem_chart.data.datasets[0].data.length > 30) mem_chart.data.datasets[0].data.shift();
|
||||||
|
|
||||||
cpu_chart.update();
|
cpu_chart.update();
|
||||||
mem_chart.update();
|
mem_chart.update();
|
||||||
@@ -1224,20 +1415,6 @@ function update_container_stats(timeout=5){
|
|||||||
// run again in n seconds
|
// run again in n seconds
|
||||||
setTimeout(update_container_stats, timeout * 1000);
|
setTimeout(update_container_stats, timeout * 1000);
|
||||||
}
|
}
|
||||||
// get public ips
|
|
||||||
function get_public_ips(){
|
|
||||||
window.fetch("/api/v1/get/status/host/ip", {method:'GET',cache:'no-cache'}).then(function(response) {
|
|
||||||
return response.json();
|
|
||||||
}).then(function(data) {
|
|
||||||
console.log(data);
|
|
||||||
|
|
||||||
// display host ips
|
|
||||||
if (data.ipv4)
|
|
||||||
$("#host_ipv4").text(data.ipv4);
|
|
||||||
if (data.ipv6)
|
|
||||||
$("#host_ipv6").text(data.ipv6);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// format hosts uptime seconds to readable string
|
// format hosts uptime seconds to readable string
|
||||||
function formatUptime(seconds){
|
function formatUptime(seconds){
|
||||||
seconds = Number(seconds);
|
seconds = Number(seconds);
|
||||||
@@ -1295,23 +1472,23 @@ function createReadWriteChart(chart_id, read_lable, write_lable){
|
|||||||
};
|
};
|
||||||
var optionsNet = {
|
var optionsNet = {
|
||||||
interaction: {
|
interaction: {
|
||||||
mode: 'index'
|
mode: 'index'
|
||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
yAxis: {
|
yAxis: {
|
||||||
min: 0,
|
min: 0,
|
||||||
grid: {
|
grid: {
|
||||||
display: false
|
display: false
|
||||||
},
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
callback: function(i, index, ticks) {
|
callback: function(i, index, ticks) {
|
||||||
return formatBytes(i);
|
return formatBytes(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
grid: {
|
grid: {
|
||||||
display: false
|
display: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1359,13 +1536,13 @@ function createHostCpuAndMemChart(){
|
|||||||
};
|
};
|
||||||
var optionsCpu = {
|
var optionsCpu = {
|
||||||
interaction: {
|
interaction: {
|
||||||
mode: 'index'
|
mode: 'index'
|
||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
yAxis: {
|
yAxis: {
|
||||||
min: 0,
|
min: 0,
|
||||||
grid: {
|
grid: {
|
||||||
display: false
|
display: false
|
||||||
},
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
callback: function(i, index, ticks) {
|
callback: function(i, index, ticks) {
|
||||||
@@ -1375,7 +1552,7 @@ function createHostCpuAndMemChart(){
|
|||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
grid: {
|
grid: {
|
||||||
display: false
|
display: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1397,13 +1574,13 @@ function createHostCpuAndMemChart(){
|
|||||||
};
|
};
|
||||||
var optionsMem = {
|
var optionsMem = {
|
||||||
interaction: {
|
interaction: {
|
||||||
mode: 'index'
|
mode: 'index'
|
||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
yAxis: {
|
yAxis: {
|
||||||
min: 0,
|
min: 0,
|
||||||
grid: {
|
grid: {
|
||||||
display: false
|
display: false
|
||||||
},
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
callback: function(i, index, ticks) {
|
callback: function(i, index, ticks) {
|
||||||
@@ -1413,7 +1590,7 @@ function createHostCpuAndMemChart(){
|
|||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
grid: {
|
grid: {
|
||||||
display: false
|
display: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1509,22 +1686,22 @@ function parseGithubMarkdownLinks(inputText) {
|
|||||||
|
|
||||||
replacePattern1 = /(\b(https?):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
|
replacePattern1 = /(\b(https?):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
|
||||||
replacedText = inputText.replace(replacePattern1, (matched, index, original, input_string) => {
|
replacedText = inputText.replace(replacePattern1, (matched, index, original, input_string) => {
|
||||||
if (matched.includes('github.com')){
|
if (matched.includes('github.com')){
|
||||||
// return short link if it's github link
|
// return short link if it's github link
|
||||||
last_uri_path = matched.split('/');
|
last_uri_path = matched.split('/');
|
||||||
last_uri_path = last_uri_path[last_uri_path.length - 1];
|
last_uri_path = last_uri_path[last_uri_path.length - 1];
|
||||||
|
|
||||||
// adjust Full Changelog link to match last git version and new git version, if link is a compare link
|
// adjust Full Changelog link to match last git version and new git version, if link is a compare link
|
||||||
if (matched.includes('/compare/') && mailcow_info.last_version_tag !== ''){
|
if (matched.includes('/compare/') && mailcow_info.last_version_tag !== ''){
|
||||||
matched = matched.replace(last_uri_path, mailcow_info.last_version_tag + '...' + mailcow_info.version_tag);
|
matched = matched.replace(last_uri_path, mailcow_info.last_version_tag + '...' + mailcow_info.version_tag);
|
||||||
last_uri_path = mailcow_info.last_version_tag + '...' + mailcow_info.version_tag;
|
last_uri_path = mailcow_info.last_version_tag + '...' + mailcow_info.version_tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
return '<a href="' + matched + '" target="_blank">' + last_uri_path + '</a><br>';
|
return '<a href="' + matched + '" target="_blank">' + last_uri_path + '</a><br>';
|
||||||
};
|
};
|
||||||
|
|
||||||
// if it's not a github link, return complete link
|
// if it's not a github link, return complete link
|
||||||
return '<a href="' + matched + '" target="_blank">' + matched + '</a>';
|
return '<a href="' + matched + '" target="_blank">' + matched + '</a>';
|
||||||
});
|
});
|
||||||
|
|
||||||
return replacedText;
|
return replacedText;
|
||||||
|
@@ -2,7 +2,7 @@ $(document).ready(function() {
|
|||||||
$(".arrow-toggle").on('click', function(e) { e.preventDefault(); $(this).find('.arrow').toggleClass("animation"); });
|
$(".arrow-toggle").on('click', function(e) { e.preventDefault(); $(this).find('.arrow').toggleClass("animation"); });
|
||||||
$("#pushover_delete").click(function() { return confirm(lang.delete_ays); });
|
$("#pushover_delete").click(function() { return confirm(lang.delete_ays); });
|
||||||
$(".goto_checkbox").click(function( event ) {
|
$(".goto_checkbox").click(function( event ) {
|
||||||
$("form[data-id='editalias'] .goto_checkbox").not(this).prop('checked', false);
|
$("form[data-id='editalias'] .goto_checkbox").not(this).prop('checked', false);
|
||||||
if ($("form[data-id='editalias'] .goto_checkbox:checked").length > 0) {
|
if ($("form[data-id='editalias'] .goto_checkbox:checked").length > 0) {
|
||||||
$('#textarea_alias_goto').prop('disabled', true);
|
$('#textarea_alias_goto').prop('disabled', true);
|
||||||
}
|
}
|
||||||
@@ -78,8 +78,14 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
function draw_wl_policy_domain_table() {
|
function draw_wl_policy_domain_table() {
|
||||||
$('#wl_policy_domain_table').DataTable({
|
$('#wl_policy_domain_table').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
@@ -98,43 +104,49 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
// placeholder, so checkbox will not block child row toggle
|
// placeholder, so checkbox will not block child row toggle
|
||||||
title: '',
|
title: '',
|
||||||
data: null,
|
data: null,
|
||||||
searchable: false,
|
searchable: false,
|
||||||
orderable: false,
|
orderable: false,
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '',
|
title: '',
|
||||||
data: 'chkbox',
|
data: 'chkbox',
|
||||||
searchable: false,
|
searchable: false,
|
||||||
orderable: false,
|
orderable: false,
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'ID',
|
title: 'ID',
|
||||||
data: 'prefid',
|
data: 'prefid',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang_user.spamfilter_table_rule,
|
title: lang_user.spamfilter_table_rule,
|
||||||
data: 'value',
|
data: 'value',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Scope',
|
title: 'Scope',
|
||||||
data: 'object',
|
data: 'object',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function draw_bl_policy_domain_table() {
|
function draw_bl_policy_domain_table() {
|
||||||
$('#bl_policy_domain_table').DataTable({
|
$('#bl_policy_domain_table').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
@@ -153,36 +165,36 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
// placeholder, so checkbox will not block child row toggle
|
// placeholder, so checkbox will not block child row toggle
|
||||||
title: '',
|
title: '',
|
||||||
data: null,
|
data: null,
|
||||||
searchable: false,
|
searchable: false,
|
||||||
orderable: false,
|
orderable: false,
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '',
|
title: '',
|
||||||
data: 'chkbox',
|
data: 'chkbox',
|
||||||
searchable: false,
|
searchable: false,
|
||||||
orderable: false,
|
orderable: false,
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'ID',
|
title: 'ID',
|
||||||
data: 'prefid',
|
data: 'prefid',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang_user.spamfilter_table_rule,
|
title: lang_user.spamfilter_table_rule,
|
||||||
data: 'value',
|
data: 'value',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Scope',
|
title: 'Scope',
|
||||||
data: 'object',
|
data: 'object',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -25,24 +25,24 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
if (typeof data.symbols !== 'undefined') {
|
if (typeof data.symbols !== 'undefined') {
|
||||||
data.symbols.sort(function (a, b) {
|
data.symbols.sort(function (a, b) {
|
||||||
if (a.score === 0) return 1
|
if (a.score === 0) return 1;
|
||||||
if (b.score === 0) return -1
|
if (b.score === 0) return -1;
|
||||||
if (b.score < 0 && a.score < 0) {
|
if (b.score < 0 && a.score < 0) {
|
||||||
return a.score - b.score
|
return a.score - b.score;
|
||||||
}
|
}
|
||||||
if (b.score > 0 && a.score > 0) {
|
if (b.score > 0 && a.score > 0) {
|
||||||
return b.score - a.score
|
return b.score - a.score;
|
||||||
}
|
}
|
||||||
return b.score - a.score
|
return b.score - a.score;
|
||||||
})
|
})
|
||||||
$.each(data.symbols, function (index, value) {
|
$.each(data.symbols, function (index, value) {
|
||||||
var highlightClass = ''
|
var highlightClass = '';
|
||||||
if (value.score > 0) highlightClass = 'negative'
|
if (value.score > 0) highlightClass = 'negative';
|
||||||
else if (value.score < 0) highlightClass = 'positive'
|
else if (value.score < 0) highlightClass = 'positive';
|
||||||
else highlightClass = 'neutral'
|
else highlightClass = 'neutral';
|
||||||
$('#qid_detail_symbols').append('<span data-bs-toggle="tooltip" class="rspamd-symbol ' + highlightClass + '" title="' + (value.options ? value.options.join(', ') : '') + '">' + value.name + ' (<span class="score">' + value.score + '</span>)</span>');
|
$('#qid_detail_symbols').append('<span data-bs-toggle="tooltip" class="rspamd-symbol ' + highlightClass + '" title="' + (value.options ? value.options.join(', ') : '') + '">' + value.name + ' (<span class="score">' + value.score + '</span>)</span>');
|
||||||
});
|
});
|
||||||
$('[data-bs-toggle="tooltip"]').tooltip()
|
$('[data-bs-toggle="tooltip"]').tooltip();
|
||||||
}
|
}
|
||||||
if (typeof data.score !== 'undefined' && typeof data.action !== 'undefined') {
|
if (typeof data.score !== 'undefined' && typeof data.action !== 'undefined') {
|
||||||
if (data.action === "add header") {
|
if (data.action === "add header") {
|
||||||
|
@@ -13,10 +13,29 @@ jQuery(function($){
|
|||||||
$('#' + table_name).DataTable().ajax.reload();
|
$('#' + table_name).DataTable().ajax.reload();
|
||||||
});
|
});
|
||||||
function draw_quarantine_table() {
|
function draw_quarantine_table() {
|
||||||
$('#quarantinetable').DataTable({
|
var table = $('#quarantinetable').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: pagination_size,
|
||||||
|
order: [[2, 'desc']],
|
||||||
|
lengthMenu: [
|
||||||
|
[10, 25, 50, 100, -1],
|
||||||
|
[10, 25, 50, 100, 'all']
|
||||||
|
],
|
||||||
|
pagingType: 'first_last_numbers',
|
||||||
|
aColumns: [
|
||||||
|
{ sWidth: '8.25%' },
|
||||||
|
{ sClass: 'classDataTable' }
|
||||||
|
],
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
|
initComplete: function(){
|
||||||
|
hideTableExpandCollapseBtn('#quarantinetable');
|
||||||
|
},
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: "/api/v1/get/quarantine/all",
|
url: "/api/v1/get/quarantine/all",
|
||||||
@@ -65,83 +84,94 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
// placeholder, so checkbox will not block child row toggle
|
// placeholder, so checkbox will not block child row toggle
|
||||||
title: '',
|
title: '',
|
||||||
data: null,
|
data: null,
|
||||||
searchable: false,
|
searchable: false,
|
||||||
orderable: false,
|
orderable: false,
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '',
|
title: '',
|
||||||
data: 'chkbox',
|
data: 'chkbox',
|
||||||
searchable: false,
|
searchable: false,
|
||||||
orderable: false,
|
orderable: false,
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'ID',
|
title: 'ID',
|
||||||
data: 'id',
|
data: 'id',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.qid,
|
title: lang.qid,
|
||||||
data: 'qid',
|
data: 'qid',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.sender,
|
title: lang.sender,
|
||||||
data: 'sender',
|
data: 'sender',
|
||||||
defaultContent: ''
|
className: 'senders-mw220',
|
||||||
},
|
defaultContent: ''
|
||||||
{
|
},
|
||||||
title: lang.subj,
|
{
|
||||||
data: 'subject',
|
title: lang.subj,
|
||||||
defaultContent: ''
|
data: 'subject',
|
||||||
},
|
defaultContent: ''
|
||||||
{
|
},
|
||||||
title: lang.rspamd_result,
|
{
|
||||||
data: 'rspamdaction',
|
title: lang.rspamd_result,
|
||||||
defaultContent: ''
|
data: 'rspamdaction',
|
||||||
},
|
defaultContent: ''
|
||||||
{
|
},
|
||||||
title: lang.rcpt,
|
{
|
||||||
data: 'rcpt',
|
title: lang.rcpt,
|
||||||
defaultContent: ''
|
data: 'rcpt',
|
||||||
},
|
defaultContent: ''
|
||||||
{
|
},
|
||||||
title: lang.danger,
|
{
|
||||||
data: 'virus',
|
title: lang.danger,
|
||||||
defaultContent: ''
|
data: 'virus',
|
||||||
},
|
defaultContent: ''
|
||||||
{
|
},
|
||||||
title: lang.spam_score,
|
{
|
||||||
data: 'score',
|
title: lang.spam_score,
|
||||||
defaultContent: ''
|
data: 'score',
|
||||||
},
|
defaultContent: ''
|
||||||
{
|
},
|
||||||
title: lang.notified,
|
{
|
||||||
data: 'notified',
|
title: lang.notified,
|
||||||
defaultContent: ''
|
data: 'notified',
|
||||||
},
|
defaultContent: ''
|
||||||
{
|
},
|
||||||
title: lang.received,
|
{
|
||||||
data: 'created',
|
title: lang.received,
|
||||||
defaultContent: '',
|
data: 'created',
|
||||||
render: function (data,type) {
|
defaultContent: '',
|
||||||
var date = new Date(data ? data * 1000 : 0);
|
createdCell: function(td, cellData) {
|
||||||
return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
|
$(td).attr({
|
||||||
}
|
"data-order": cellData,
|
||||||
},
|
"data-sort": cellData
|
||||||
{
|
});
|
||||||
title: lang.action,
|
|
||||||
data: 'action',
|
var date = new Date(cellData ? cellData * 1000 : 0);
|
||||||
className: 'text-md-end dt-sm-head-hidden dt-body-right',
|
var dateString = date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
|
||||||
defaultContent: ''
|
$(td).html(dateString);
|
||||||
},
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: lang.action,
|
||||||
|
data: 'action',
|
||||||
|
className: 'dt-text-right dt-sm-head-hidden',
|
||||||
|
defaultContent: ''
|
||||||
|
},
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
table.on('responsive-resize', function (e, datatable, columns){
|
||||||
|
hideTableExpandCollapseBtn('#quarantinetable');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
$('body').on('click', '.show_qid_info', function (e) {
|
$('body').on('click', '.show_qid_info', function (e) {
|
||||||
@@ -175,24 +205,24 @@ jQuery(function($){
|
|||||||
$('#qid_detail_fuzzy').html('');
|
$('#qid_detail_fuzzy').html('');
|
||||||
if (typeof data.symbols !== 'undefined') {
|
if (typeof data.symbols !== 'undefined') {
|
||||||
data.symbols.sort(function (a, b) {
|
data.symbols.sort(function (a, b) {
|
||||||
if (a.score === 0) return 1
|
if (a.score === 0) return 1;
|
||||||
if (b.score === 0) return -1
|
if (b.score === 0) return -1;
|
||||||
if (b.score < 0 && a.score < 0) {
|
if (b.score < 0 && a.score < 0) {
|
||||||
return a.score - b.score
|
return a.score - b.score;
|
||||||
}
|
}
|
||||||
if (b.score > 0 && a.score > 0) {
|
if (b.score > 0 && a.score > 0) {
|
||||||
return b.score - a.score
|
return b.score - a.score;
|
||||||
}
|
}
|
||||||
return b.score - a.score
|
return b.score - a.score;
|
||||||
})
|
})
|
||||||
$.each(data.symbols, function (index, value) {
|
$.each(data.symbols, function (index, value) {
|
||||||
var highlightClass = ''
|
var highlightClass = '';
|
||||||
if (value.score > 0) highlightClass = 'negative'
|
if (value.score > 0) highlightClass = 'negative';
|
||||||
else if (value.score < 0) highlightClass = 'positive'
|
else if (value.score < 0) highlightClass = 'positive';
|
||||||
else highlightClass = 'neutral'
|
else highlightClass = 'neutral';
|
||||||
$('#qid_detail_symbols').append('<span data-bs-toggle="tooltip" class="rspamd-symbol ' + highlightClass + '" title="' + (value.options ? value.options.join(', ') : '') + '">' + value.name + ' (<span class="score">' + value.score + '</span>)</span>');
|
$('#qid_detail_symbols').append('<span data-bs-toggle="tooltip" class="rspamd-symbol ' + highlightClass + '" title="' + (value.options ? value.options.join(', ') : '') + '">' + value.name + ' (<span class="score">' + value.score + '</span>)</span>');
|
||||||
});
|
});
|
||||||
$('[data-bs-toggle="tooltip"]').tooltip()
|
$('[data-bs-toggle="tooltip"]').tooltip();
|
||||||
}
|
}
|
||||||
if (typeof data.fuzzy_hashes === 'object' && data.fuzzy_hashes !== null && data.fuzzy_hashes.length !== 0) {
|
if (typeof data.fuzzy_hashes === 'object' && data.fuzzy_hashes !== null && data.fuzzy_hashes.length !== 0) {
|
||||||
$.each(data.fuzzy_hashes, function (index, value) {
|
$.each(data.fuzzy_hashes, function (index, value) {
|
||||||
@@ -257,4 +287,11 @@ jQuery(function($){
|
|||||||
|
|
||||||
// Initial table drawings
|
// Initial table drawings
|
||||||
draw_quarantine_table();
|
draw_quarantine_table();
|
||||||
|
|
||||||
|
function hideTableExpandCollapseBtn(table){
|
||||||
|
if ($(table).hasClass('collapsed'))
|
||||||
|
$(".table_collapse_option").show();
|
||||||
|
else
|
||||||
|
$(".table_collapse_option").hide();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
@@ -1,119 +1,124 @@
|
|||||||
jQuery(function($){
|
jQuery(function($){
|
||||||
|
|
||||||
$(".refresh_table").on('click', function(e) {
|
$(".refresh_table").on('click', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var table_name = $(this).data('table');
|
var table_name = $(this).data('table');
|
||||||
$('#' + table_name).DataTable().ajax.reload();
|
$('#' + table_name).DataTable().ajax.reload();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e<B.length-1);return i.toFixed(1)+" "+B[e]}
|
function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e<B.length-1);return i.toFixed(1)+" "+B[e]}
|
||||||
|
|
||||||
// Queue item
|
// Queue item
|
||||||
$('#showQueuedMsg').on('show.bs.modal', function (e) {
|
$('#showQueuedMsg').on('show.bs.modal', function (e) {
|
||||||
$('#queue_msg_content').text(lang.loading);
|
$('#queue_msg_content').text(lang.loading);
|
||||||
button = $(e.relatedTarget)
|
button = $(e.relatedTarget)
|
||||||
if (button != null) {
|
if (button != null) {
|
||||||
$('#queue_id').text(button.data('queue-id'));
|
$('#queue_id').text(button.data('queue-id'));
|
||||||
}
|
|
||||||
$.ajax({
|
|
||||||
type: 'GET',
|
|
||||||
url: '/api/v1/get/postcat/' + button.data('queue-id'),
|
|
||||||
dataType: 'text',
|
|
||||||
complete: function (data) {
|
|
||||||
console.log(data);
|
|
||||||
$('#queue_msg_content').text(data.responseText);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
function draw_queue() {
|
|
||||||
// just recalc width if instance already exists
|
|
||||||
if ($.fn.DataTable.isDataTable('#queuetable') ) {
|
|
||||||
$('#queuetable').DataTable().columns.adjust().responsive.recalc();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
$.ajax({
|
||||||
|
type: 'GET',
|
||||||
|
url: '/api/v1/get/postcat/' + button.data('queue-id'),
|
||||||
|
dataType: 'text',
|
||||||
|
complete: function (data) {
|
||||||
|
$('#queue_msg_content').text(data.responseText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
$('#queuetable').DataTable({
|
function draw_queue() {
|
||||||
processing: true,
|
// just recalc width if instance already exists
|
||||||
serverSide: false,
|
if ($.fn.DataTable.isDataTable('#queuetable') ) {
|
||||||
language: lang_datatables,
|
$('#queuetable').DataTable().columns.adjust().responsive.recalc();
|
||||||
ajax: {
|
return;
|
||||||
type: "GET",
|
}
|
||||||
url: "/api/v1/get/mailq/all",
|
|
||||||
dataSrc: function(data){
|
$('#queuetable').DataTable({
|
||||||
$.each(data, function (i, item) {
|
responsive: true,
|
||||||
item.chkbox = '<input type="checkbox" data-id="mailqitems" name="multi_select" value="' + item.queue_id + '" />';
|
processing: true,
|
||||||
rcpts = $.map(item.recipients, function(i) {
|
serverSide: false,
|
||||||
return escapeHtml(i);
|
stateSave: true,
|
||||||
});
|
pageLength: pagination_size,
|
||||||
item.recipients = rcpts.join('<hr style="margin:1px!important">');
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
item.action = '<div class="btn-group">' +
|
"tr" +
|
||||||
'<a href="#" data-bs-toggle="modal" data-bs-target="#showQueuedMsg" data-queue-id="' + encodeURI(item.queue_id) + '" class="btn btn-xs btn-secondary">' + lang.queue_show_message + '</a>' +
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
|
language: lang_datatables,
|
||||||
|
ajax: {
|
||||||
|
type: "GET",
|
||||||
|
url: "/api/v1/get/mailq/all",
|
||||||
|
dataSrc: function(data){
|
||||||
|
$.each(data, function (i, item) {
|
||||||
|
item.chkbox = '<input type="checkbox" data-id="mailqitems" name="multi_select" value="' + item.queue_id + '" />';
|
||||||
|
rcpts = $.map(item.recipients, function(i) {
|
||||||
|
return escapeHtml(i);
|
||||||
|
});
|
||||||
|
item.recipients = rcpts.join('<hr style="margin:1px!important">');
|
||||||
|
item.action = '<div class="btn-group">' +
|
||||||
|
'<a href="#" data-bs-toggle="modal" data-bs-target="#showQueuedMsg" data-queue-id="' + encodeURI(item.queue_id) + '" class="btn btn-xs btn-secondary">' + lang.show_message + '</a>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
});
|
});
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
// placeholder, so checkbox will not block child row toggle
|
// placeholder, so checkbox will not block child row toggle
|
||||||
title: '',
|
title: '',
|
||||||
data: null,
|
data: null,
|
||||||
searchable: false,
|
searchable: false,
|
||||||
orderable: false,
|
orderable: false,
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '',
|
title: '',
|
||||||
data: 'chkbox',
|
data: 'chkbox',
|
||||||
searchable: false,
|
searchable: false,
|
||||||
orderable: false,
|
orderable: false,
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'QID',
|
title: 'QID',
|
||||||
data: 'queue_id',
|
data: 'queue_id',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Queue',
|
title: 'Queue',
|
||||||
data: 'queue_name',
|
data: 'queue_name',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang_admin.arrival_time,
|
title: lang_admin.arrival_time,
|
||||||
data: 'arrival_time',
|
data: 'arrival_time',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type){
|
render: function (data, type){
|
||||||
var date = new Date(data ? data * 1000 : 0);
|
var date = new Date(data ? data * 1000 : 0);
|
||||||
return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
|
return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang_admin.message_size,
|
title: lang_admin.message_size,
|
||||||
data: 'message_size',
|
data: 'message_size',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type){
|
render: function (data, type){
|
||||||
return humanFileSize(data);
|
return humanFileSize(data);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang_admin.sender,
|
title: lang_admin.sender,
|
||||||
data: 'sender',
|
data: 'sender',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang_admin.recipients,
|
title: lang_admin.recipients,
|
||||||
data: 'recipients',
|
data: 'recipients',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang_admin.action,
|
title: lang_admin.action,
|
||||||
data: 'action',
|
data: 'action',
|
||||||
className: 'text-md-end dt-sm-head-hidden dt-body-right',
|
className: 'dt-sm-head-hidden dt-text-right',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -127,6 +127,20 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function createSortableDate(td, cellData, date_string = false) {
|
||||||
|
if (date_string)
|
||||||
|
var date = new Date(cellData);
|
||||||
|
else
|
||||||
|
var date = new Date(cellData ? cellData * 1000 : 0);
|
||||||
|
|
||||||
|
var timestamp = date.getTime();
|
||||||
|
$(td).attr({
|
||||||
|
"data-order": timestamp,
|
||||||
|
"data-sort": timestamp
|
||||||
|
});
|
||||||
|
$(td).html(date.toLocaleDateString(LOCALE, DATETIME_FORMAT));
|
||||||
|
}
|
||||||
function draw_tla_table() {
|
function draw_tla_table() {
|
||||||
// just recalc width if instance already exists
|
// just recalc width if instance already exists
|
||||||
if ($.fn.DataTable.isDataTable('#tla_table') ) {
|
if ($.fn.DataTable.isDataTable('#tla_table') ) {
|
||||||
@@ -135,9 +149,16 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
|
|
||||||
$('#tla_table').DataTable({
|
$('#tla_table').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
|
order: [[4, 'desc']],
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: "/api/v1/get/time_limited_aliases",
|
url: "/api/v1/get/time_limited_aliases",
|
||||||
@@ -185,24 +206,22 @@ jQuery(function($){
|
|||||||
title: lang.alias_valid_until,
|
title: lang.alias_valid_until,
|
||||||
data: 'validity',
|
data: 'validity',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
createdCell: function(td, cellData) {
|
||||||
var date = new Date(data ? data * 1000 : 0);
|
createSortableDate(td, cellData)
|
||||||
return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.created_on,
|
title: lang.created_on,
|
||||||
data: 'created',
|
data: 'created',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
createdCell: function(td, cellData) {
|
||||||
var date = new Date(data.replace(/-/g, "/"));
|
createSortableDate(td, cellData, true)
|
||||||
return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.action,
|
title: lang.action,
|
||||||
data: 'action',
|
data: 'action',
|
||||||
className: 'text-md-end dt-sm-head-hidden dt-body-right',
|
className: 'dt-sm-head-hidden dt-text-right',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -216,8 +235,14 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
|
|
||||||
$('#sync_job_table').DataTable({
|
$('#sync_job_table').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
@@ -259,9 +284,9 @@ jQuery(function($){
|
|||||||
item.success = '<i class="text-' + (item.success == 1 ? 'success' : 'danger') + ' bi bi-' + (item.success == 1 ? 'check-lg' : 'x-lg') + '"></i>';
|
item.success = '<i class="text-' + (item.success == 1 ? 'success' : 'danger') + ' bi bi-' + (item.success == 1 ? 'check-lg' : 'x-lg') + '"></i>';
|
||||||
}
|
}
|
||||||
if (lang['syncjob_'+item.exit_status]) {
|
if (lang['syncjob_'+item.exit_status]) {
|
||||||
item.exit_status = lang['syncjob_'+item.exit_status];
|
item.exit_status = lang['syncjob_'+item.exit_status];
|
||||||
} else if (item.success != '-') {
|
} else if (item.success != '-') {
|
||||||
item.exit_status = lang.syncjob_check_log;
|
item.exit_status = lang.syncjob_check_log;
|
||||||
}
|
}
|
||||||
item.exit_status = item.success + ' ' + item.exit_status;
|
item.exit_status = item.success + ' ' + item.exit_status;
|
||||||
});
|
});
|
||||||
@@ -351,7 +376,7 @@ jQuery(function($){
|
|||||||
{
|
{
|
||||||
title: lang.action,
|
title: lang.action,
|
||||||
data: 'action',
|
data: 'action',
|
||||||
className: 'text-md-end dt-sm-head-hidden dt-body-right',
|
className: 'dt-sm-head-hidden dt-text-right',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
responsivePriority: 5
|
responsivePriority: 5
|
||||||
}
|
}
|
||||||
@@ -366,8 +391,14 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
|
|
||||||
$('#app_passwd_table').DataTable({
|
$('#app_passwd_table').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
@@ -442,7 +473,7 @@ jQuery(function($){
|
|||||||
{
|
{
|
||||||
title: lang.action,
|
title: lang.action,
|
||||||
data: 'action',
|
data: 'action',
|
||||||
className: 'text-md-end dt-sm-head-hidden dt-body-right',
|
className: 'dt-sm-head-hidden dt-text-right',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -456,8 +487,14 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
|
|
||||||
$('#wl_policy_mailbox_table').DataTable({
|
$('#wl_policy_mailbox_table').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
@@ -521,8 +558,14 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
|
|
||||||
$('#bl_policy_mailbox_table').DataTable({
|
$('#bl_policy_mailbox_table').DataTable({
|
||||||
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
|
stateSave: true,
|
||||||
|
pageLength: pagination_size,
|
||||||
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
|
"tr" +
|
||||||
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
|
@@ -288,6 +288,18 @@ if (isset($_GET['query'])) {
|
|||||||
case "domain-admin":
|
case "domain-admin":
|
||||||
process_add_return(domain_admin('add', $attr));
|
process_add_return(domain_admin('add', $attr));
|
||||||
break;
|
break;
|
||||||
|
case "sso":
|
||||||
|
switch ($object) {
|
||||||
|
case "domain-admin":
|
||||||
|
$data = domain_admin_sso('issue', $attr);
|
||||||
|
if($data) {
|
||||||
|
echo json_encode($data);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
process_add_return($data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "admin":
|
case "admin":
|
||||||
process_add_return(admin('add', $attr));
|
process_add_return(admin('add', $attr));
|
||||||
break;
|
break;
|
||||||
@@ -561,6 +573,15 @@ if (isset($_GET['query'])) {
|
|||||||
echo '{}';
|
echo '{}';
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
$password_complexity_rules = password_complexity('get');
|
||||||
|
if ($password_complexity_rules !== false) {
|
||||||
|
process_get_return($password_complexity_rules);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo '{}';
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1544,14 +1565,15 @@ if (isset($_GET['query'])) {
|
|||||||
}
|
}
|
||||||
else if ($extra == "ip") {
|
else if ($extra == "ip") {
|
||||||
// get public ips
|
// get public ips
|
||||||
|
|
||||||
$curl = curl_init();
|
$curl = curl_init();
|
||||||
curl_setopt($curl, CURLOPT_URL, 'http://ipv4.mailcow.email');
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||||
curl_setopt($curl, CURLOPT_POST, 0);
|
curl_setopt($curl, CURLOPT_POST, 0);
|
||||||
|
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10);
|
||||||
|
curl_setopt($curl, CURLOPT_TIMEOUT, 15);
|
||||||
|
curl_setopt($curl, CURLOPT_URL, 'http://ipv4.mailcow.email');
|
||||||
$ipv4 = curl_exec($curl);
|
$ipv4 = curl_exec($curl);
|
||||||
curl_setopt($curl, CURLOPT_URL, 'http://ipv6.mailcow.email');
|
curl_setopt($curl, CURLOPT_URL, 'http://ipv6.mailcow.email');
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
|
||||||
curl_setopt($curl, CURLOPT_POST, 0);
|
|
||||||
$ipv6 = curl_exec($curl);
|
$ipv6 = curl_exec($curl);
|
||||||
$ips = array(
|
$ips = array(
|
||||||
"ipv4" => $ipv4,
|
"ipv4" => $ipv4,
|
||||||
@@ -1913,6 +1935,9 @@ if (isset($_GET['query'])) {
|
|||||||
case "ui_texts":
|
case "ui_texts":
|
||||||
process_edit_return(customize('edit', 'ui_texts', $attr));
|
process_edit_return(customize('edit', 'ui_texts', $attr));
|
||||||
break;
|
break;
|
||||||
|
case "ip_check":
|
||||||
|
process_edit_return(customize('edit', 'ip_check', $attr));
|
||||||
|
break;
|
||||||
case "self":
|
case "self":
|
||||||
if ($_SESSION['mailcow_cc_role'] == "domainadmin") {
|
if ($_SESSION['mailcow_cc_role'] == "domainadmin") {
|
||||||
process_edit_return(domain_admin('edit', $attr));
|
process_edit_return(domain_admin('edit', $attr));
|
||||||
|
@@ -393,7 +393,6 @@
|
|||||||
"toggle_all": "Marcar tots"
|
"toggle_all": "Marcar tots"
|
||||||
},
|
},
|
||||||
"queue": {
|
"queue": {
|
||||||
"queue_command_success": "Queue command completed successfully",
|
|
||||||
"queue_manager": "Queue Manager"
|
"queue_manager": "Queue Manager"
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
|
@@ -105,7 +105,8 @@
|
|||||||
"timeout2": "Časový limit pro připojení k lokálnímu serveru",
|
"timeout2": "Časový limit pro připojení k lokálnímu serveru",
|
||||||
"username": "Uživatelské jméno",
|
"username": "Uživatelské jméno",
|
||||||
"validate": "Ověřit",
|
"validate": "Ověřit",
|
||||||
"validation_success": "Úspěšně ověřeno"
|
"validation_success": "Úspěšně ověřeno",
|
||||||
|
"tags": "Štítky"
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"access": "Přístupy",
|
"access": "Přístupy",
|
||||||
@@ -333,7 +334,11 @@
|
|||||||
"username": "Uživatelské jméno",
|
"username": "Uživatelské jméno",
|
||||||
"validate_license_now": "Ověřit GUID na licenčním serveru",
|
"validate_license_now": "Ověřit GUID na licenčním serveru",
|
||||||
"verify": "Ověřit",
|
"verify": "Ověřit",
|
||||||
"yes": "✓"
|
"yes": "✓",
|
||||||
|
"f2b_ban_time_increment": "Délka banu je prodlužována s každým dalším banem",
|
||||||
|
"f2b_max_ban_time": "Maximální délka banu (s)",
|
||||||
|
"ip_check": "Kontrola IP",
|
||||||
|
"ip_check_disabled": "Kontrola IP je vypnuta. Můžete ji zapnout v <br> <strong>System > Nastavení > Options > Přizpůsobení</strong>"
|
||||||
},
|
},
|
||||||
"danger": {
|
"danger": {
|
||||||
"access_denied": "Přístup odepřen nebo jsou neplatná data ve formuláři",
|
"access_denied": "Přístup odepřen nebo jsou neplatná data ve formuláři",
|
||||||
@@ -536,7 +541,7 @@
|
|||||||
"inactive": "Neaktivní",
|
"inactive": "Neaktivní",
|
||||||
"kind": "Druh",
|
"kind": "Druh",
|
||||||
"last_modified": "Naposledy změněn",
|
"last_modified": "Naposledy změněn",
|
||||||
"lookup_mx": "Cíl je regulární výraz který se shoduje s MX záznamem (<code>.*google\\.com</code> směřuje veškerou poštu na MX které jsou cílem pro google.com přes tento skok)",
|
"lookup_mx": "Cíl je regulární výraz který se shoduje s MX záznamem (<code>.*\\.google\\.com</code> směřuje veškerou poštu na MX které jsou cílem pro google.com přes tento skok)",
|
||||||
"mailbox": "Úprava mailové schránky",
|
"mailbox": "Úprava mailové schránky",
|
||||||
"mailbox_quota_def": "Výchozí kvóta schránky",
|
"mailbox_quota_def": "Výchozí kvóta schránky",
|
||||||
"mailbox_relayhost_info": "Aplikované jen na uživatelskou schránku a přímé aliasy, přepisuje předávající server domény.",
|
"mailbox_relayhost_info": "Aplikované jen na uživatelskou schránku a přímé aliasy, přepisuje předávající server domény.",
|
||||||
@@ -650,7 +655,7 @@
|
|||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "Přihlášení zpožděno o %s sekund.",
|
"delayed": "Přihlášení zpožděno o %s sekund.",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "Přihlásit",
|
"login": "Přihlásit",
|
||||||
"mobileconfig_info": "Ke stažení profilového souboru se přihlaste jako uživatel schránky.",
|
"mobileconfig_info": "Ke stažení profilového souboru se přihlaste jako uživatel schránky.",
|
||||||
"other_logins": "Přihlášení klíčem",
|
"other_logins": "Přihlášení klíčem",
|
||||||
@@ -889,7 +894,6 @@
|
|||||||
"type": "Typ"
|
"type": "Typ"
|
||||||
},
|
},
|
||||||
"queue": {
|
"queue": {
|
||||||
"queue_deliver_mail": "Doručit",
|
|
||||||
"queue_manager": "Správce fronty"
|
"queue_manager": "Správce fronty"
|
||||||
},
|
},
|
||||||
"ratelimit": {
|
"ratelimit": {
|
||||||
|
@@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"acl": {
|
"acl": {
|
||||||
"alias_domains": "Tilføj kældenavn domæner",
|
"alias_domains": "Tilføj domænealias",
|
||||||
"app_passwds": "Administrer app-adgangskoder",
|
"app_passwds": "Administrer app-adgangskoder",
|
||||||
"bcc_maps": "BCC kort",
|
"bcc_maps": "BCC kort",
|
||||||
"delimiter_action": "Afgrænsning handling",
|
"delimiter_action": "Afgrænsning handling",
|
||||||
"eas_reset": "Nulstil EAS endheder",
|
"eas_reset": "Nulstil EAS enheder",
|
||||||
"extend_sender_acl": "Tillad at udvide afsenderens ACL med eksterne adresser",
|
"extend_sender_acl": "Tillad at udvide afsenderens ACL med eksterne adresser",
|
||||||
"filters": "Filtre",
|
"filters": "Filtre",
|
||||||
"login_as": "Login som mailboks bruger",
|
"login_as": "Login som mailboks bruger",
|
||||||
"prohibited": "Forbudt af ACL",
|
"prohibited": "Nægtet af ACL",
|
||||||
"protocol_access": "Ændre protokol adgang",
|
"protocol_access": "Skift protokol adgang",
|
||||||
"pushover": "Pushover",
|
"pushover": "Pushover",
|
||||||
"quarantine": "Karantæneaktioner",
|
"quarantine": "Karantænehandlinger",
|
||||||
"quarantine_attachments": "Karantæne vedhæftede filer",
|
"quarantine_attachments": "Karantænevedhæftede filer",
|
||||||
"quarantine_notification": "Skift karantænemeddelelser",
|
"quarantine_notification": "Skift karantænemeddelelser",
|
||||||
"ratelimit": "Satsgrænse",
|
"ratelimit": "Satsgrænse",
|
||||||
"recipient_maps": "Modtagerkort",
|
"recipient_maps": "Modtagerkort",
|
||||||
@@ -20,12 +20,15 @@
|
|||||||
"sogo_access": "Tillad styring af SOGo-adgang",
|
"sogo_access": "Tillad styring af SOGo-adgang",
|
||||||
"sogo_profile_reset": "Nulstil SOGo-profil",
|
"sogo_profile_reset": "Nulstil SOGo-profil",
|
||||||
"spam_alias": "Midlertidige aliasser",
|
"spam_alias": "Midlertidige aliasser",
|
||||||
"spam_policy": "Sortliste / hvidliste",
|
"spam_policy": "Sortliste/hvidliste",
|
||||||
"spam_score": "Spam-score",
|
"spam_score": "Spam-score",
|
||||||
"syncjobs": "Synkroniser job",
|
"syncjobs": "Synkroniserings job",
|
||||||
"tls_policy": "TLS politik",
|
"tls_policy": "TLS politik",
|
||||||
"unlimited_quota": "Ubegrænset quote for mailbokse",
|
"unlimited_quota": "Ubegrænset plads for mailbokse",
|
||||||
"domain_desc": "Skift domæne beskrivelse"
|
"domain_desc": "Skift domæne beskrivelse",
|
||||||
|
"domain_relayhost": "Skift relæ host for et domæne",
|
||||||
|
"mailbox_relayhost": "Skift relæ-host for en postkasse",
|
||||||
|
"quarantine_category": "Skift kategorien for karantænemeddelelse"
|
||||||
},
|
},
|
||||||
"add": {
|
"add": {
|
||||||
"activate_filter_warn": "Alle andre filtre deaktiveres, når aktiv er markeret.",
|
"activate_filter_warn": "Alle andre filtre deaktiveres, når aktiv er markeret.",
|
||||||
@@ -33,7 +36,7 @@
|
|||||||
"add": "Tilføj",
|
"add": "Tilføj",
|
||||||
"add_domain_only": "Tilføj kun domæne",
|
"add_domain_only": "Tilføj kun domæne",
|
||||||
"add_domain_restart": "Tilføj domæne og genstart SOGo",
|
"add_domain_restart": "Tilføj domæne og genstart SOGo",
|
||||||
"alias_address": "Alias adresse (r)",
|
"alias_address": "Alias adresse(r)",
|
||||||
"alias_address_info": "<small>Fuld e-mail-adresse eller @ eksempel.com for at fange alle beskeder til et domæne (kommasepareret). <b> kun mailcow-domæner</b>.</small>",
|
"alias_address_info": "<small>Fuld e-mail-adresse eller @ eksempel.com for at fange alle beskeder til et domæne (kommasepareret). <b> kun mailcow-domæner</b>.</small>",
|
||||||
"alias_domain": "Alias-domæne",
|
"alias_domain": "Alias-domæne",
|
||||||
"alias_domain_info": "<small>Kun gyldige domænenavne (kommasepareret).</small>",
|
"alias_domain_info": "<small>Kun gyldige domænenavne (kommasepareret).</small>",
|
||||||
@@ -59,7 +62,7 @@
|
|||||||
"gal": "Global adresseliste",
|
"gal": "Global adresseliste",
|
||||||
"gal_info": "GAL indeholder alle objekter i et domæne og kan ikke redigeres af nogen bruger. Information om ledig / optaget i SOGo mangler, hvis deaktiveret! <b> Genstart SOGo for at anvende ændringer. </b>",
|
"gal_info": "GAL indeholder alle objekter i et domæne og kan ikke redigeres af nogen bruger. Information om ledig / optaget i SOGo mangler, hvis deaktiveret! <b> Genstart SOGo for at anvende ændringer. </b>",
|
||||||
"generate": "generere",
|
"generate": "generere",
|
||||||
"goto_ham": "Lær som <span class=\"text-success\"><b>ham</b></span>",
|
"goto_ham": "Lær som <span class=\"text-success\"><b>ønsket</b></span>",
|
||||||
"goto_null": "Kassér e-mail i stilhed",
|
"goto_null": "Kassér e-mail i stilhed",
|
||||||
"goto_spam": "Lær som <span class=\"text-danger\"><b>spam</b></span>",
|
"goto_spam": "Lær som <span class=\"text-danger\"><b>spam</b></span>",
|
||||||
"hostname": "Vært",
|
"hostname": "Vært",
|
||||||
@@ -80,7 +83,7 @@
|
|||||||
"private_comment": "Privat kommentar",
|
"private_comment": "Privat kommentar",
|
||||||
"public_comment": "Offentlig kommentar",
|
"public_comment": "Offentlig kommentar",
|
||||||
"quota_mb": "Kvota (Mb)",
|
"quota_mb": "Kvota (Mb)",
|
||||||
"relay_all": "Send alle modtagere videre",
|
"relay_all": "Besvar alle modtager",
|
||||||
"relay_all_info": "↪ Hvis du vælger <b> ikke </b> at videresende alle modtagere, skal du tilføje et (\"blind\") postkasse til hver enkelt modtager, der skal videresendes.",
|
"relay_all_info": "↪ Hvis du vælger <b> ikke </b> at videresende alle modtagere, skal du tilføje et (\"blind\") postkasse til hver enkelt modtager, der skal videresendes.",
|
||||||
"relay_domain": "Send dette domæne videre",
|
"relay_domain": "Send dette domæne videre",
|
||||||
"relay_transport_info": "<div class=\"badge fs-6 bg-info\">Info</div> Du kan definere transportkort til en tilpasset destination for dette domæne. Hvis ikke indstillet, foretages der et MX-opslag.",
|
"relay_transport_info": "<div class=\"badge fs-6 bg-info\">Info</div> Du kan definere transportkort til en tilpasset destination for dette domæne. Hvis ikke indstillet, foretages der et MX-opslag.",
|
||||||
@@ -101,7 +104,10 @@
|
|||||||
"timeout2": "Timeout for forbindelse til lokal vært",
|
"timeout2": "Timeout for forbindelse til lokal vært",
|
||||||
"username": "Brugernavn",
|
"username": "Brugernavn",
|
||||||
"validate": "Bekræft",
|
"validate": "Bekræft",
|
||||||
"validation_success": "Valideret med succes"
|
"validation_success": "Valideret med succes",
|
||||||
|
"bcc_dest_format": "BCC-destination skal være en enkelt gyldig e-mail-adresse.<br>Hvis du har brug for at sende en kopi til flere adresser, kan du oprette et alias og bruge det her.",
|
||||||
|
"app_passwd_protocols": "Tilladte protokoller for app adgangskode",
|
||||||
|
"tags": "Tag's"
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"access": "Adgang",
|
"access": "Adgang",
|
||||||
@@ -308,7 +314,10 @@
|
|||||||
"username": "Brugernavn",
|
"username": "Brugernavn",
|
||||||
"validate_license_now": "Valider GUID mod licensserver",
|
"validate_license_now": "Valider GUID mod licensserver",
|
||||||
"verify": "Verificere",
|
"verify": "Verificere",
|
||||||
"yes": "✓"
|
"yes": "✓",
|
||||||
|
"ip_check_opt_in": "Opt-In for brug af tredjepartstjeneste <strong>ipv4.mailcow.email</strong> og <strong>ipv6.mailcow.email</strong> til at finde eksterne IP-adresser.",
|
||||||
|
"queue_unban": "unban",
|
||||||
|
"admins": "Administratorer"
|
||||||
},
|
},
|
||||||
"danger": {
|
"danger": {
|
||||||
"access_denied": "Adgang nægtet eller ugyldig formular data",
|
"access_denied": "Adgang nægtet eller ugyldig formular data",
|
||||||
@@ -425,7 +434,8 @@
|
|||||||
"username_invalid": "Brugernavn %s kan ikke bruges",
|
"username_invalid": "Brugernavn %s kan ikke bruges",
|
||||||
"validity_missing": "Tildel venligst en gyldighedsperiode",
|
"validity_missing": "Tildel venligst en gyldighedsperiode",
|
||||||
"value_missing": "Angiv alle værdier",
|
"value_missing": "Angiv alle værdier",
|
||||||
"yotp_verification_failed": "Yubico OTP verifikationen mislykkedes: %s"
|
"yotp_verification_failed": "Yubico OTP verifikationen mislykkedes: %s",
|
||||||
|
"webauthn_publickey_failed": "Der er ikke gemt nogen offentlig nøgle for den valgte autentifikator"
|
||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"chart_this_server": "Diagram (denne server)",
|
"chart_this_server": "Diagram (denne server)",
|
||||||
@@ -442,7 +452,8 @@
|
|||||||
"solr_status": "Solr-status",
|
"solr_status": "Solr-status",
|
||||||
"started_on": "Startede den",
|
"started_on": "Startede den",
|
||||||
"static_logs": "Statiske logfiler",
|
"static_logs": "Statiske logfiler",
|
||||||
"system_containers": "System og Beholdere"
|
"system_containers": "System og Beholdere",
|
||||||
|
"error_show_ip": "Kunne ikke finde de offentlige IP-adresser"
|
||||||
},
|
},
|
||||||
"diagnostics": {
|
"diagnostics": {
|
||||||
"cname_from_a": "Værdi afledt af A / AAAA-post. Dette understøttes, så længe posten peger på den korrekte ressource.",
|
"cname_from_a": "Værdi afledt af A / AAAA-post. Dette understøttes, så længe posten peger på den korrekte ressource.",
|
||||||
@@ -553,7 +564,11 @@
|
|||||||
"title": "Rediger objekt",
|
"title": "Rediger objekt",
|
||||||
"unchanged_if_empty": "Lad være tomt, hvis uændret",
|
"unchanged_if_empty": "Lad være tomt, hvis uændret",
|
||||||
"username": "Brugernavn",
|
"username": "Brugernavn",
|
||||||
"validate_save": "Valider og gem"
|
"validate_save": "Valider og gem",
|
||||||
|
"admin": "Rediger administrator",
|
||||||
|
"lookup_mx": "Destination er et regulært udtryk, der matcher MX-navnet (<code>.*google\\.dk</code> for at dirigere al e-mail, der er målrettet til en MX, der ender på google.dk, over dette hop)",
|
||||||
|
"mailbox_relayhost_info": "Anvendt på postkassen og kun direkte aliasser, og overskriver et domæne relæ-host.",
|
||||||
|
"quota_warning_bcc": "Kvoteadvarsel BCC"
|
||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"cancel": "Afbestille",
|
"cancel": "Afbestille",
|
||||||
@@ -571,7 +586,7 @@
|
|||||||
"header": {
|
"header": {
|
||||||
"administration": "Konfiguration og detailer",
|
"administration": "Konfiguration og detailer",
|
||||||
"apps": "Apps",
|
"apps": "Apps",
|
||||||
"debug": "Systemoplysninger",
|
"debug": "Information",
|
||||||
"email": "E-Mail",
|
"email": "E-Mail",
|
||||||
"mailcow_config": "Konfiguration",
|
"mailcow_config": "Konfiguration",
|
||||||
"quarantine": "Karantæne",
|
"quarantine": "Karantæne",
|
||||||
@@ -586,7 +601,7 @@
|
|||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "Login blev forsinket med% s sekunder.",
|
"delayed": "Login blev forsinket med% s sekunder.",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
"mobileconfig_info": "Log ind som postkassebruger for at downloade den anmodede Apple-forbindelsesprofil.",
|
"mobileconfig_info": "Log ind som postkassebruger for at downloade den anmodede Apple-forbindelsesprofil.",
|
||||||
"other_logins": "Nøgle login",
|
"other_logins": "Nøgle login",
|
||||||
@@ -739,7 +754,10 @@
|
|||||||
"username": "Brugernavn",
|
"username": "Brugernavn",
|
||||||
"waiting": "Venter",
|
"waiting": "Venter",
|
||||||
"weekly": "Ugentlig",
|
"weekly": "Ugentlig",
|
||||||
"yes": "✓"
|
"yes": "✓",
|
||||||
|
"goto_ham": "Lær som <b>ønsket</b>",
|
||||||
|
"catch_all": "Fang-alt",
|
||||||
|
"open_logs": "Åben logfiler"
|
||||||
},
|
},
|
||||||
"oauth2": {
|
"oauth2": {
|
||||||
"access_denied": "Log ind som mailboks ejer for at give adgang via OAuth2.",
|
"access_denied": "Log ind som mailboks ejer for at give adgang via OAuth2.",
|
||||||
@@ -803,7 +821,6 @@
|
|||||||
"toggle_all": "Skift alt"
|
"toggle_all": "Skift alt"
|
||||||
},
|
},
|
||||||
"queue": {
|
"queue": {
|
||||||
"queue_deliver_mail": "Aflevere",
|
|
||||||
"queue_manager": "Køadministrator"
|
"queue_manager": "Køadministrator"
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
@@ -1031,7 +1048,7 @@
|
|||||||
"spamfilter_table_empty": "Intet data at vise",
|
"spamfilter_table_empty": "Intet data at vise",
|
||||||
"spamfilter_table_remove": "slet",
|
"spamfilter_table_remove": "slet",
|
||||||
"spamfilter_table_rule": "Regl",
|
"spamfilter_table_rule": "Regl",
|
||||||
"spamfilter_wl": "Hvisliste",
|
"spamfilter_wl": "Hvidliste",
|
||||||
"spamfilter_wl_desc": "Hvidlistede e-mail-adresser til <b>aldrig</b> at klassificeres som spam. Wildcards kan bruges. Et filter anvendes kun på direkte aliaser (aliaser med en enkelt målpostkasse) eksklusive catch-aliaser og selve en postkasse.",
|
"spamfilter_wl_desc": "Hvidlistede e-mail-adresser til <b>aldrig</b> at klassificeres som spam. Wildcards kan bruges. Et filter anvendes kun på direkte aliaser (aliaser med en enkelt målpostkasse) eksklusive catch-aliaser og selve en postkasse.",
|
||||||
"spamfilter_yellow": "Gul: denne besked kan være spam, vil blive tagget som spam og flyttes til din junk-mappe",
|
"spamfilter_yellow": "Gul: denne besked kan være spam, vil blive tagget som spam og flyttes til din junk-mappe",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
@@ -1067,5 +1084,11 @@
|
|||||||
"quota_exceeded_scope": "Domænekvote overskredet: Kun ubegrænsede postkasser kan oprettes i dette domæneomfang.",
|
"quota_exceeded_scope": "Domænekvote overskredet: Kun ubegrænsede postkasser kan oprettes i dette domæneomfang.",
|
||||||
"session_token": "Form nøgle ugyldig: Nøgle passer ikke",
|
"session_token": "Form nøgle ugyldig: Nøgle passer ikke",
|
||||||
"session_ua": "Form nøgle ugyldig: Bruger-Agent gyldighedskontrols fejl"
|
"session_ua": "Form nøgle ugyldig: Bruger-Agent gyldighedskontrols fejl"
|
||||||
|
},
|
||||||
|
"datatables": {
|
||||||
|
"lengthMenu": "Vis _MENU_ poster",
|
||||||
|
"paginate": {
|
||||||
|
"first": "Først"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -175,10 +175,12 @@
|
|||||||
"empty": "Keine Einträge vorhanden",
|
"empty": "Keine Einträge vorhanden",
|
||||||
"excludes": "Diese Empfänger ausschließen",
|
"excludes": "Diese Empfänger ausschließen",
|
||||||
"f2b_ban_time": "Bannzeit in Sekunden",
|
"f2b_ban_time": "Bannzeit in Sekunden",
|
||||||
|
"f2b_ban_time_increment": "Bannzeit erhöht sich mit jedem Bann",
|
||||||
"f2b_blacklist": "Blacklist für Netzwerke und Hosts",
|
"f2b_blacklist": "Blacklist für Netzwerke und Hosts",
|
||||||
"f2b_filter": "Regex-Filter",
|
"f2b_filter": "Regex-Filter",
|
||||||
"f2b_list_info": "Ein Host oder Netzwerk auf der Blacklist wird immer eine Whitelist-Einheit überwiegen. <b>Die Aktualisierung der Liste dauert einige Sekunden.</b>",
|
"f2b_list_info": "Ein Host oder Netzwerk auf der Blacklist wird immer eine Whitelist-Einheit überwiegen. <b>Die Aktualisierung der Liste dauert einige Sekunden.</b>",
|
||||||
"f2b_max_attempts": "Max. Versuche",
|
"f2b_max_attempts": "Max. Versuche",
|
||||||
|
"f2b_max_ban_time": "Maximale Bannzeit in Sekunden",
|
||||||
"f2b_netban_ipv4": "Netzbereich für IPv4-Banns (8-32)",
|
"f2b_netban_ipv4": "Netzbereich für IPv4-Banns (8-32)",
|
||||||
"f2b_netban_ipv6": "Netzbereich für IPv6-Banns (8-128)",
|
"f2b_netban_ipv6": "Netzbereich für IPv6-Banns (8-128)",
|
||||||
"f2b_parameters": "Fail2ban-Parameter",
|
"f2b_parameters": "Fail2ban-Parameter",
|
||||||
@@ -204,6 +206,9 @@
|
|||||||
"include_exclude": "Ein- und Ausschlüsse",
|
"include_exclude": "Ein- und Ausschlüsse",
|
||||||
"include_exclude_info": "Ohne Auswahl werden <b>alle Mailboxen</b> adressiert.",
|
"include_exclude_info": "Ohne Auswahl werden <b>alle Mailboxen</b> adressiert.",
|
||||||
"includes": "Diese Empfänger einschließen",
|
"includes": "Diese Empfänger einschließen",
|
||||||
|
"ip_check": "IP Check",
|
||||||
|
"ip_check_disabled": "IP check ist deaktiviert. Unter dem angegebenen Pfad kann es aktiviert werden<br> <strong>System > Konfiguration > Einstellungen > UI-Anpassung</strong>",
|
||||||
|
"ip_check_opt_in": "Opt-In für die Nutzung der Drittanbieter-Dienste <strong>ipv4.mailcow.email</strong> und <strong>ipv6.mailcow.email</strong> zur Auflösung externer IP-Adressen.",
|
||||||
"is_mx_based": "MX-basiert",
|
"is_mx_based": "MX-basiert",
|
||||||
"last_applied": "Zuletzt angewendet",
|
"last_applied": "Zuletzt angewendet",
|
||||||
"license_info": "Eine Lizenz ist nicht erforderlich, hilft jedoch der Entwicklung mailcows.<br><a href=\"https://www.servercow.de/mailcow#sal\" target=\"_blank\" alt=\"SAL Bestellung\">Hier kann die mailcow-GUID registriert werden.</a> Alternativ ist <a href=\"https://www.servercow.de/mailcow#support\" target=\"_blank\" alt=\"SAL Bestellung\">die Bestellung von Support-Paketen möglich</a>.",
|
"license_info": "Eine Lizenz ist nicht erforderlich, hilft jedoch der Entwicklung mailcows.<br><a href=\"https://www.servercow.de/mailcow#sal\" target=\"_blank\" alt=\"SAL Bestellung\">Hier kann die mailcow-GUID registriert werden.</a> Alternativ ist <a href=\"https://www.servercow.de/mailcow#support\" target=\"_blank\" alt=\"SAL Bestellung\">die Bestellung von Support-Paketen möglich</a>.",
|
||||||
@@ -211,7 +216,7 @@
|
|||||||
"loading": "Bitte warten...",
|
"loading": "Bitte warten...",
|
||||||
"login_time": "Zeit",
|
"login_time": "Zeit",
|
||||||
"logo_info": "Die hochgeladene Grafik wird für die Navigationsleiste auf eine Höhe von 40px skaliert. Für die Darstellung auf der Login-Maske beträgt die skalierte Breite maximal 250px. Eine frei skalierbare Grafik (etwa SVG) wird empfohlen.",
|
"logo_info": "Die hochgeladene Grafik wird für die Navigationsleiste auf eine Höhe von 40px skaliert. Für die Darstellung auf der Login-Maske beträgt die skalierte Breite maximal 250px. Eine frei skalierbare Grafik (etwa SVG) wird empfohlen.",
|
||||||
"lookup_mx": "Ziel mit MX vergleichen (Regex, etwa <code>.*google\\.com</code>, um alle Ziele mit MX *google.com zu routen)",
|
"lookup_mx": "Ziel mit MX vergleichen (Regex, etwa <code>.*\\.google\\.com</code>, um alle Ziele mit MX *google.com zu routen)",
|
||||||
"main_name": "\"mailcow UI\" Name",
|
"main_name": "\"mailcow UI\" Name",
|
||||||
"merged_vars_hint": "Ausgegraute Reihen wurden aus der Datei <code>vars.(local.)inc.php</code> gelesen und können hier nicht verändert werden.",
|
"merged_vars_hint": "Ausgegraute Reihen wurden aus der Datei <code>vars.(local.)inc.php</code> gelesen und können hier nicht verändert werden.",
|
||||||
"message": "Nachricht",
|
"message": "Nachricht",
|
||||||
@@ -336,7 +341,8 @@
|
|||||||
"oauth2_add_client": "Füge OAuth2 Client hinzu",
|
"oauth2_add_client": "Füge OAuth2 Client hinzu",
|
||||||
"api_read_only": "Schreibgeschützter Zugriff",
|
"api_read_only": "Schreibgeschützter Zugriff",
|
||||||
"api_read_write": "Lese-Schreib-Zugriff",
|
"api_read_write": "Lese-Schreib-Zugriff",
|
||||||
"oauth2_apps": "OAuth2 Apps"
|
"oauth2_apps": "OAuth2 Apps",
|
||||||
|
"queue_unban": "entsperren"
|
||||||
},
|
},
|
||||||
"danger": {
|
"danger": {
|
||||||
"access_denied": "Zugriff verweigert oder unvollständige/ungültige Daten",
|
"access_denied": "Zugriff verweigert oder unvollständige/ungültige Daten",
|
||||||
@@ -363,6 +369,7 @@
|
|||||||
"domain_not_empty": "Domain %s ist nicht leer",
|
"domain_not_empty": "Domain %s ist nicht leer",
|
||||||
"domain_not_found": "Domain %s nicht gefunden",
|
"domain_not_found": "Domain %s nicht gefunden",
|
||||||
"domain_quota_m_in_use": "Domain-Speicherplatzlimit muss größer oder gleich %d MiB sein",
|
"domain_quota_m_in_use": "Domain-Speicherplatzlimit muss größer oder gleich %d MiB sein",
|
||||||
|
"extended_sender_acl_denied": "Keine Rechte zum Setzen von externen Absenderadressen",
|
||||||
"extra_acl_invalid": "Externe Absenderadresse \"%s\" ist ungültig",
|
"extra_acl_invalid": "Externe Absenderadresse \"%s\" ist ungültig",
|
||||||
"extra_acl_invalid_domain": "Externe Absenderadresse \"%s\" verwendet eine ungültige Domain",
|
"extra_acl_invalid_domain": "Externe Absenderadresse \"%s\" verwendet eine ungültige Domain",
|
||||||
"fido2_verification_failed": "FIDO2-Verifizierung fehlgeschlagen: %s",
|
"fido2_verification_failed": "FIDO2-Verifizierung fehlgeschlagen: %s",
|
||||||
@@ -450,41 +457,48 @@
|
|||||||
"totp_verification_failed": "TOTP-Verifizierung fehlgeschlagen",
|
"totp_verification_failed": "TOTP-Verifizierung fehlgeschlagen",
|
||||||
"transport_dest_exists": "Transport-Maps-Ziel \"%s\" existiert bereits",
|
"transport_dest_exists": "Transport-Maps-Ziel \"%s\" existiert bereits",
|
||||||
"webauthn_verification_failed": "WebAuthn-Verifizierung fehlgeschlagen: %s",
|
"webauthn_verification_failed": "WebAuthn-Verifizierung fehlgeschlagen: %s",
|
||||||
|
"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",
|
||||||
"unknown": "Ein unbekannter Fehler trat auf",
|
"unknown": "Ein unbekannter Fehler trat auf",
|
||||||
"unknown_tfa_method": "Unbekannte TFA-Methode",
|
"unknown_tfa_method": "Unbekannte TFA-Methode",
|
||||||
"unlimited_quota_acl": "Unendliche Quota untersagt durch ACL",
|
"unlimited_quota_acl": "Unendliche Quota untersagt durch ACL",
|
||||||
"username_invalid": "Benutzername %s kann nicht verwendet werden",
|
"username_invalid": "Benutzername %s kann nicht verwendet werden",
|
||||||
"validity_missing": "Bitte geben Sie eine Gültigkeitsdauer an",
|
"validity_missing": "Bitte geben Sie eine Gültigkeitsdauer an",
|
||||||
"value_missing": "Bitte alle Felder ausfüllen",
|
"value_missing": "Bitte alle Felder ausfüllen",
|
||||||
"yotp_verification_failed": "Yubico OTP-Verifizierung fehlgeschlagen: %s"
|
"yotp_verification_failed": "Yubico OTP-Verifizierung fehlgeschlagen: %s",
|
||||||
|
"template_exists": "Vorlage %s existiert bereits",
|
||||||
|
"template_id_invalid": "Vorlagen-ID %s ungültig",
|
||||||
|
"template_name_invalid": "Name der Vorlage ungültig"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
"collapse_all": "Alle Einklappen",
|
"collapse_all": "Alle Einklappen",
|
||||||
"decimal": "",
|
"decimal": ",",
|
||||||
"emptyTable": "Keine Daten in der Tabelle vorhanden",
|
"emptyTable": "Keine Daten in der Tabelle vorhanden",
|
||||||
"expand_all": "Alle Ausklappen",
|
"expand_all": "Alle Ausklappen",
|
||||||
"info": "_START_ bis _END_ von _TOTAL_ Einträgen",
|
"info": "_START_ bis _END_ von _TOTAL_ Einträgen",
|
||||||
"infoEmpty": "0 bis 0 von 0 Einträgen",
|
"infoEmpty": "0 bis 0 von 0 Einträgen",
|
||||||
"infoFiltered": "(gefiltert von _MAX_ Einträgen)",
|
"infoFiltered": "(gefiltert von _MAX_ Einträgen)",
|
||||||
"infoPostFix": "",
|
"infoPostFix": "",
|
||||||
"thousands": ".",
|
"thousands": ".",
|
||||||
"lengthMenu": "_MENU_ Einträge anzeigen",
|
"lengthMenu": "_MENU_ Einträge anzeigen",
|
||||||
"loadingRecords": "Wird geladen...",
|
"loadingRecords": "Wird geladen...",
|
||||||
"processing": "Bitte warten...",
|
"processing": "Bitte warten...",
|
||||||
"search": "Suchen",
|
"search": "Suchen",
|
||||||
"zeroRecords": "Keine Einträge vorhanden.",
|
"zeroRecords": "Keine Einträge vorhanden.",
|
||||||
"paginate": {
|
"paginate": {
|
||||||
"first": "Erste",
|
"first": "Erste",
|
||||||
"previous": "Zurück",
|
"previous": "Zurück",
|
||||||
"next": "Nächste",
|
"next": "Nächste",
|
||||||
"last": "Letzte"
|
"last": "Letzte"
|
||||||
},
|
},
|
||||||
"aria": {
|
"aria": {
|
||||||
"sortAscending": ": aktivieren, um Spalte aufsteigend zu sortieren",
|
"sortAscending": ": aktivieren, um Spalte aufsteigend zu sortieren",
|
||||||
"sortDescending": ": aktivieren, um Spalte absteigend zu sortieren"
|
"sortDescending": ": aktivieren, um Spalte absteigend zu sortieren"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
|
"architecture": "Architektur",
|
||||||
"chart_this_server": "Chart (dieser Server)",
|
"chart_this_server": "Chart (dieser Server)",
|
||||||
"containers_info": "Container-Information",
|
"containers_info": "Container-Information",
|
||||||
"container_running": "Läuft",
|
"container_running": "Läuft",
|
||||||
@@ -494,6 +508,7 @@
|
|||||||
"current_time": "Systemzeit",
|
"current_time": "Systemzeit",
|
||||||
"disk_usage": "Festplattennutzung",
|
"disk_usage": "Festplattennutzung",
|
||||||
"docs": "Dokumente",
|
"docs": "Dokumente",
|
||||||
|
"error_show_ip": "Konnte die öffentlichen IP Adressen nicht auflösen",
|
||||||
"external_logs": "Externe Logs",
|
"external_logs": "Externe Logs",
|
||||||
"history_all_servers": "History (alle Server)",
|
"history_all_servers": "History (alle Server)",
|
||||||
"in_memory_logs": "In-memory Logs",
|
"in_memory_logs": "In-memory Logs",
|
||||||
@@ -506,6 +521,7 @@
|
|||||||
"online_users": "Benutzer online",
|
"online_users": "Benutzer online",
|
||||||
"restart_container": "Neustart",
|
"restart_container": "Neustart",
|
||||||
"service": "Dienst",
|
"service": "Dienst",
|
||||||
|
"show_ip": "Zeige öffentliche IP",
|
||||||
"size": "Größe",
|
"size": "Größe",
|
||||||
"solr_dead": "Solr startet, ist deaktiviert oder temporär nicht erreichbar.",
|
"solr_dead": "Solr startet, ist deaktiviert oder temporär nicht erreichbar.",
|
||||||
"solr_status": "Solr Status",
|
"solr_status": "Solr Status",
|
||||||
@@ -519,7 +535,8 @@
|
|||||||
"update_available": "Es ist ein Update verfügbar",
|
"update_available": "Es ist ein Update verfügbar",
|
||||||
"no_update_available": "Das System ist auf aktuellem Stand",
|
"no_update_available": "Das System ist auf aktuellem Stand",
|
||||||
"update_failed": "Es konnte nicht nach einem Update gesucht werden",
|
"update_failed": "Es konnte nicht nach einem Update gesucht werden",
|
||||||
"username": "Benutzername"
|
"username": "Benutzername",
|
||||||
|
"wip": "Aktuell noch in Arbeit"
|
||||||
},
|
},
|
||||||
"diagnostics": {
|
"diagnostics": {
|
||||||
"cname_from_a": "Wert abgeleitet von A/AAAA-Eintrag. Wird unterstützt, sofern der Eintrag auf die korrekte Ressource zeigt.",
|
"cname_from_a": "Wert abgeleitet von A/AAAA-Eintrag. Wird unterstützt, sofern der Eintrag auf die korrekte Ressource zeigt.",
|
||||||
@@ -578,7 +595,7 @@
|
|||||||
"inactive": "Inaktiv",
|
"inactive": "Inaktiv",
|
||||||
"kind": "Art",
|
"kind": "Art",
|
||||||
"last_modified": "Zuletzt geändert",
|
"last_modified": "Zuletzt geändert",
|
||||||
"lookup_mx": "Ziel mit MX vergleichen (Regex, etwa <code>.*google\\.com</code>, um alle Ziele mit MX *google.com zu routen)",
|
"lookup_mx": "Ziel mit MX vergleichen (Regex, etwa <code>.*\\.google\\.com</code>, um alle Ziele mit MX *google.com zu routen)",
|
||||||
"mailbox": "Mailbox bearbeiten",
|
"mailbox": "Mailbox bearbeiten",
|
||||||
"mailbox_quota_def": "Standard-Quota einer Mailbox",
|
"mailbox_quota_def": "Standard-Quota einer Mailbox",
|
||||||
"mailbox_relayhost_info": "Wird auf eine Mailbox und direkte Alias-Adressen angewendet. Überschreibt die Einstellung einer Domain.",
|
"mailbox_relayhost_info": "Wird auf eine Mailbox und direkte Alias-Adressen angewendet. Überschreibt die Einstellung einer Domain.",
|
||||||
@@ -645,7 +662,8 @@
|
|||||||
"title": "Objekt bearbeiten",
|
"title": "Objekt bearbeiten",
|
||||||
"unchanged_if_empty": "Unverändert, wenn leer",
|
"unchanged_if_empty": "Unverändert, wenn leer",
|
||||||
"username": "Benutzername",
|
"username": "Benutzername",
|
||||||
"validate_save": "Validieren und speichern"
|
"validate_save": "Validieren und speichern",
|
||||||
|
"pushover_sound": "Ton"
|
||||||
},
|
},
|
||||||
"fido2": {
|
"fido2": {
|
||||||
"confirm": "Bestätigen",
|
"confirm": "Bestätigen",
|
||||||
@@ -686,7 +704,8 @@
|
|||||||
"quarantine": "Quarantäne",
|
"quarantine": "Quarantäne",
|
||||||
"restart_netfilter": "Netfilter neustarten",
|
"restart_netfilter": "Netfilter neustarten",
|
||||||
"restart_sogo": "SOGo neustarten",
|
"restart_sogo": "SOGo neustarten",
|
||||||
"user_settings": "Benutzereinstellungen"
|
"user_settings": "Benutzereinstellungen",
|
||||||
|
"mailcow_system": "System"
|
||||||
},
|
},
|
||||||
"info": {
|
"info": {
|
||||||
"awaiting_tfa_confirmation": "Warte auf TFA-Verifizierung",
|
"awaiting_tfa_confirmation": "Warte auf TFA-Verifizierung",
|
||||||
@@ -695,7 +714,7 @@
|
|||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "Login wurde zur Sicherheit um %s Sekunde/n verzögert.",
|
"delayed": "Login wurde zur Sicherheit um %s Sekunde/n verzögert.",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "Anmelden",
|
"login": "Anmelden",
|
||||||
"mobileconfig_info": "Bitte als Mailbox-Benutzer einloggen, um das Verbindungsprofil herunterzuladen.",
|
"mobileconfig_info": "Bitte als Mailbox-Benutzer einloggen, um das Verbindungsprofil herunterzuladen.",
|
||||||
"other_logins": "Key Login",
|
"other_logins": "Key Login",
|
||||||
@@ -942,7 +961,7 @@
|
|||||||
"queue": {
|
"queue": {
|
||||||
"delete": "Queue löschen",
|
"delete": "Queue löschen",
|
||||||
"flush": "Queue flushen",
|
"flush": "Queue flushen",
|
||||||
"info" : "In der Mailqueue befinden sich alle E-Mails, welche auf eine Zustellung warten. Sollte eine E-Mail eine längere Zeit innerhalb der Mailqueue stecken wird diese automatisch vom System gelöscht.<br>Die Fehlermeldung der jeweiligen Mail gibt aufschluss darüber, warum diese nicht zugestellt werden konnte",
|
"info": "In der Mailqueue befinden sich alle E-Mails, welche auf eine Zustellung warten. Sollte eine E-Mail eine längere Zeit innerhalb der Mailqueue stecken wird diese automatisch vom System gelöscht.<br>Die Fehlermeldung der jeweiligen Mail gibt aufschluss darüber, warum diese nicht zugestellt werden konnte",
|
||||||
"legend": "Funktionen der Mailqueue Aktionen:",
|
"legend": "Funktionen der Mailqueue Aktionen:",
|
||||||
"ays": "Soll die derzeitige Queue wirklich komplett bereinigt werden?",
|
"ays": "Soll die derzeitige Queue wirklich komplett bereinigt werden?",
|
||||||
"deliver_mail": "Ausliefern",
|
"deliver_mail": "Ausliefern",
|
||||||
@@ -1001,6 +1020,7 @@
|
|||||||
"forwarding_host_removed": "Weiterleitungs-Host %s wurde entfernt",
|
"forwarding_host_removed": "Weiterleitungs-Host %s wurde entfernt",
|
||||||
"global_filter_written": "Filterdatei wurde erfolgreich geschrieben",
|
"global_filter_written": "Filterdatei wurde erfolgreich geschrieben",
|
||||||
"hash_deleted": "Hash wurde gelöscht",
|
"hash_deleted": "Hash wurde gelöscht",
|
||||||
|
"ip_check_opt_in_modified": "IP Check wurde erfolgreich gespeichert",
|
||||||
"item_deleted": "Objekt %s wurde entfernt",
|
"item_deleted": "Objekt %s wurde entfernt",
|
||||||
"item_released": "Objekt %s freigegeben",
|
"item_released": "Objekt %s freigegeben",
|
||||||
"items_deleted": "Objekt(e) %s wurde(n) erfolgreich entfernt",
|
"items_deleted": "Objekt(e) %s wurde(n) erfolgreich entfernt",
|
||||||
@@ -1229,7 +1249,8 @@
|
|||||||
"syncjob_EXIT_CONNECTION_FAILURE": "Verbindungsproblem",
|
"syncjob_EXIT_CONNECTION_FAILURE": "Verbindungsproblem",
|
||||||
"syncjob_EXIT_TLS_FAILURE": "Problem mit verschlüsselter Verbindung",
|
"syncjob_EXIT_TLS_FAILURE": "Problem mit verschlüsselter Verbindung",
|
||||||
"syncjob_EXIT_AUTHENTICATION_FAILURE": "Authentifizierungsproblem",
|
"syncjob_EXIT_AUTHENTICATION_FAILURE": "Authentifizierungsproblem",
|
||||||
"syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Falscher Benutzername oder Passwort"
|
"syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Falscher Benutzername oder Passwort",
|
||||||
|
"pushover_sound": "Ton"
|
||||||
},
|
},
|
||||||
"warning": {
|
"warning": {
|
||||||
"cannot_delete_self": "Kann derzeit eingeloggten Benutzer nicht entfernen",
|
"cannot_delete_self": "Kann derzeit eingeloggten Benutzer nicht entfernen",
|
||||||
|
@@ -177,10 +177,12 @@
|
|||||||
"empty": "No results",
|
"empty": "No results",
|
||||||
"excludes": "Excludes these recipients",
|
"excludes": "Excludes these recipients",
|
||||||
"f2b_ban_time": "Ban time (s)",
|
"f2b_ban_time": "Ban time (s)",
|
||||||
|
"f2b_ban_time_increment": "Ban time is incremented with each ban",
|
||||||
"f2b_blacklist": "Blacklisted networks/hosts",
|
"f2b_blacklist": "Blacklisted networks/hosts",
|
||||||
"f2b_filter": "Regex filters",
|
"f2b_filter": "Regex filters",
|
||||||
"f2b_list_info": "A blacklisted host or network will always outweigh a whitelist entity. <b>List updates will take a few seconds to be applied.</b>",
|
"f2b_list_info": "A blacklisted host or network will always outweigh a whitelist entity. <b>List updates will take a few seconds to be applied.</b>",
|
||||||
"f2b_max_attempts": "Max. attempts",
|
"f2b_max_attempts": "Max. attempts",
|
||||||
|
"f2b_max_ban_time": "Max. ban time (s)",
|
||||||
"f2b_netban_ipv4": "IPv4 subnet size to apply ban on (8-32)",
|
"f2b_netban_ipv4": "IPv4 subnet size to apply ban on (8-32)",
|
||||||
"f2b_netban_ipv6": "IPv6 subnet size to apply ban on (8-128)",
|
"f2b_netban_ipv6": "IPv6 subnet size to apply ban on (8-128)",
|
||||||
"f2b_parameters": "Fail2ban parameters",
|
"f2b_parameters": "Fail2ban parameters",
|
||||||
@@ -206,6 +208,9 @@
|
|||||||
"include_exclude": "Include/Exclude",
|
"include_exclude": "Include/Exclude",
|
||||||
"include_exclude_info": "By default - with no selection - <b>all mailboxes</b> are addressed",
|
"include_exclude_info": "By default - with no selection - <b>all mailboxes</b> are addressed",
|
||||||
"includes": "Include these recipients",
|
"includes": "Include these recipients",
|
||||||
|
"ip_check": "IP Check",
|
||||||
|
"ip_check_disabled": "IP check is disabled. You can enable it under<br> <strong>System > Configuration > Options > Customize</strong>",
|
||||||
|
"ip_check_opt_in": "Opt-In for using third party service <strong>ipv4.mailcow.email</strong> and <strong>ipv6.mailcow.email</strong> to resolve external IP addresses.",
|
||||||
"is_mx_based": "MX based",
|
"is_mx_based": "MX based",
|
||||||
"last_applied": "Last applied",
|
"last_applied": "Last applied",
|
||||||
"license_info": "A license is not required but helps further development.<br><a href=\"https://www.servercow.de/mailcow?lang=en#sal\" target=\"_blank\" alt=\"SAL order\">Register your GUID here</a> or <a href=\"https://www.servercow.de/mailcow?lang=en#support\" target=\"_blank\" alt=\"Support order\">buy support for your mailcow installation.</a>",
|
"license_info": "A license is not required but helps further development.<br><a href=\"https://www.servercow.de/mailcow?lang=en#sal\" target=\"_blank\" alt=\"SAL order\">Register your GUID here</a> or <a href=\"https://www.servercow.de/mailcow?lang=en#support\" target=\"_blank\" alt=\"Support order\">buy support for your mailcow installation.</a>",
|
||||||
@@ -213,7 +218,7 @@
|
|||||||
"loading": "Please wait...",
|
"loading": "Please wait...",
|
||||||
"login_time": "Login time",
|
"login_time": "Login time",
|
||||||
"logo_info": "Your image will be scaled to a height of 40px for the top navigation bar and a max. width of 250px for the start page. A scalable graphic is highly recommended.",
|
"logo_info": "Your image will be scaled to a height of 40px for the top navigation bar and a max. width of 250px for the start page. A scalable graphic is highly recommended.",
|
||||||
"lookup_mx": "Destination is a regular expression to match against MX name (<code>.*google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
|
"lookup_mx": "Destination is a regular expression to match against MX name (<code>.*\\.google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
|
||||||
"main_name": "\"mailcow UI\" name",
|
"main_name": "\"mailcow UI\" name",
|
||||||
"merged_vars_hint": "Greyed out rows were merged from <code>vars.(local.)inc.php</code> and cannot be modified.",
|
"merged_vars_hint": "Greyed out rows were merged from <code>vars.(local.)inc.php</code> and cannot be modified.",
|
||||||
"message": "Message",
|
"message": "Message",
|
||||||
@@ -264,6 +269,7 @@
|
|||||||
"quota_notifications": "Quota notifications",
|
"quota_notifications": "Quota notifications",
|
||||||
"quota_notifications_info": "Quota notifications are sent to users once when crossing 80% and once when crossing 95% usage.",
|
"quota_notifications_info": "Quota notifications are sent to users once when crossing 80% and once when crossing 95% usage.",
|
||||||
"quota_notifications_vars": "{{percent}} equals the current quota of the user<br>{{username}} is the mailbox name",
|
"quota_notifications_vars": "{{percent}} equals the current quota of the user<br>{{username}} is the mailbox name",
|
||||||
|
"queue_unban": "unban",
|
||||||
"r_active": "Active restrictions",
|
"r_active": "Active restrictions",
|
||||||
"r_inactive": "Inactive restrictions",
|
"r_inactive": "Inactive restrictions",
|
||||||
"r_info": "Greyed out/disabled elements on the list of active restrictions are not known as valid restrictions to mailcow and cannot be moved. Unknown restrictions will be set in order of appearance anyway. <br>You can add new elements in <code>inc/vars.local.inc.php</code> to be able to toggle them.",
|
"r_info": "Greyed out/disabled elements on the list of active restrictions are not known as valid restrictions to mailcow and cannot be moved. Unknown restrictions will be set in order of appearance anyway. <br>You can add new elements in <code>inc/vars.local.inc.php</code> to be able to toggle them.",
|
||||||
@@ -363,6 +369,7 @@
|
|||||||
"domain_not_empty": "Cannot remove non-empty domain %s",
|
"domain_not_empty": "Cannot remove non-empty domain %s",
|
||||||
"domain_not_found": "Domain %s not found",
|
"domain_not_found": "Domain %s not found",
|
||||||
"domain_quota_m_in_use": "Domain quota must be greater or equal to %s MiB",
|
"domain_quota_m_in_use": "Domain quota must be greater or equal to %s MiB",
|
||||||
|
"extended_sender_acl_denied": "missing ACL to set external sender addresses",
|
||||||
"extra_acl_invalid": "External sender address \"%s\" is invalid",
|
"extra_acl_invalid": "External sender address \"%s\" is invalid",
|
||||||
"extra_acl_invalid_domain": "External sender \"%s\" uses an invalid domain",
|
"extra_acl_invalid_domain": "External sender \"%s\" uses an invalid domain",
|
||||||
"fido2_verification_failed": "FIDO2 verification failed: %s",
|
"fido2_verification_failed": "FIDO2 verification failed: %s",
|
||||||
@@ -453,6 +460,9 @@
|
|||||||
"totp_verification_failed": "TOTP verification failed",
|
"totp_verification_failed": "TOTP verification failed",
|
||||||
"transport_dest_exists": "Transport destination \"%s\" exists",
|
"transport_dest_exists": "Transport destination \"%s\" exists",
|
||||||
"webauthn_verification_failed": "WebAuthn verification failed: %s",
|
"webauthn_verification_failed": "WebAuthn verification failed: %s",
|
||||||
|
"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",
|
||||||
"unknown": "An unknown error occurred",
|
"unknown": "An unknown error occurred",
|
||||||
"unknown_tfa_method": "Unknown TFA method",
|
"unknown_tfa_method": "Unknown TFA method",
|
||||||
"unlimited_quota_acl": "Unlimited quota prohibited by ACL",
|
"unlimited_quota_acl": "Unlimited quota prohibited by ACL",
|
||||||
@@ -462,32 +472,33 @@
|
|||||||
"yotp_verification_failed": "Yubico OTP verification failed: %s"
|
"yotp_verification_failed": "Yubico OTP verification failed: %s"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
"collapse_all": "Collapse All",
|
"collapse_all": "Collapse All",
|
||||||
"decimal": "",
|
"decimal": ".",
|
||||||
"emptyTable": "No data available in table",
|
"emptyTable": "No data available in table",
|
||||||
"expand_all": "Expand All",
|
"expand_all": "Expand All",
|
||||||
"info": "Showing _START_ to _END_ of _TOTAL_ entries",
|
"info": "Showing _START_ to _END_ of _TOTAL_ entries",
|
||||||
"infoEmpty": "Showing 0 to 0 of 0 entries",
|
"infoEmpty": "Showing 0 to 0 of 0 entries",
|
||||||
"infoFiltered": "(filtered from _MAX_ total entries)",
|
"infoFiltered": "(filtered from _MAX_ total entries)",
|
||||||
"infoPostFix": "",
|
"infoPostFix": "",
|
||||||
"thousands": ",",
|
"thousands": ",",
|
||||||
"lengthMenu": "Show _MENU_ entries",
|
"lengthMenu": "Show _MENU_ entries",
|
||||||
"loadingRecords": "Loading...",
|
"loadingRecords": "Loading...",
|
||||||
"processing": "Please wait...",
|
"processing": "Please wait...",
|
||||||
"search": "Search:",
|
"search": "Search:",
|
||||||
"zeroRecords": "No matching records found",
|
"zeroRecords": "No matching records found",
|
||||||
"paginate": {
|
"paginate": {
|
||||||
"first": "First",
|
"first": "First",
|
||||||
"last": "Last",
|
"last": "Last",
|
||||||
"next": "Next",
|
"next": "Next",
|
||||||
"previous": "Previous"
|
"previous": "Previous"
|
||||||
},
|
},
|
||||||
"aria": {
|
"aria": {
|
||||||
"sortAscending": ": activate to sort column ascending",
|
"sortAscending": ": activate to sort column ascending",
|
||||||
"sortDescending": ": activate to sort column descending"
|
"sortDescending": ": activate to sort column descending"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
|
"architecture": "Architecture",
|
||||||
"chart_this_server": "Chart (this server)",
|
"chart_this_server": "Chart (this server)",
|
||||||
"containers_info": "Container information",
|
"containers_info": "Container information",
|
||||||
"container_running": "Running",
|
"container_running": "Running",
|
||||||
@@ -497,6 +508,7 @@
|
|||||||
"current_time": "System Time",
|
"current_time": "System Time",
|
||||||
"disk_usage": "Disk usage",
|
"disk_usage": "Disk usage",
|
||||||
"docs": "Docs",
|
"docs": "Docs",
|
||||||
|
"error_show_ip": "Could not resolve the public IP addresses",
|
||||||
"external_logs": "External logs",
|
"external_logs": "External logs",
|
||||||
"history_all_servers": "History (all servers)",
|
"history_all_servers": "History (all servers)",
|
||||||
"in_memory_logs": "In-memory logs",
|
"in_memory_logs": "In-memory logs",
|
||||||
@@ -509,6 +521,7 @@
|
|||||||
"online_users": "Users online",
|
"online_users": "Users online",
|
||||||
"restart_container": "Restart",
|
"restart_container": "Restart",
|
||||||
"service": "Service",
|
"service": "Service",
|
||||||
|
"show_ip": "Show public IP",
|
||||||
"size": "Size",
|
"size": "Size",
|
||||||
"solr_dead": "Solr is starting, disabled or died.",
|
"solr_dead": "Solr is starting, disabled or died.",
|
||||||
"solr_status": "Solr status",
|
"solr_status": "Solr status",
|
||||||
@@ -522,7 +535,8 @@
|
|||||||
"update_available": "There is an update available",
|
"update_available": "There is an update available",
|
||||||
"no_update_available": "The System is on the latest version",
|
"no_update_available": "The System is on the latest version",
|
||||||
"update_failed": "Could not check for an Update",
|
"update_failed": "Could not check for an Update",
|
||||||
"username": "Username"
|
"username": "Username",
|
||||||
|
"wip": "Currently Work in Progress"
|
||||||
},
|
},
|
||||||
"diagnostics": {
|
"diagnostics": {
|
||||||
"cname_from_a": "Value derived from A/AAAA record. This is supported as long as the record points to the correct resource.",
|
"cname_from_a": "Value derived from A/AAAA record. This is supported as long as the record points to the correct resource.",
|
||||||
@@ -581,7 +595,7 @@
|
|||||||
"inactive": "Inactive",
|
"inactive": "Inactive",
|
||||||
"kind": "Kind",
|
"kind": "Kind",
|
||||||
"last_modified": "Last modified",
|
"last_modified": "Last modified",
|
||||||
"lookup_mx": "Destination is a regular expression to match against MX name (<code>.*google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
|
"lookup_mx": "Destination is a regular expression to match against MX name (<code>.*\\.google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
|
||||||
"mailbox": "Edit mailbox",
|
"mailbox": "Edit mailbox",
|
||||||
"mailbox_quota_def": "Default mailbox quota",
|
"mailbox_quota_def": "Default mailbox quota",
|
||||||
"mailbox_relayhost_info": "Applied to the mailbox and direct aliases only, does override a domain relayhost.",
|
"mailbox_relayhost_info": "Applied to the mailbox and direct aliases only, does override a domain relayhost.",
|
||||||
@@ -700,7 +714,7 @@
|
|||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "Login was delayed by %s seconds.",
|
"delayed": "Login was delayed by %s seconds.",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
"mobileconfig_info": "Please login as mailbox user to download the requested Apple connection profile.",
|
"mobileconfig_info": "Please login as mailbox user to download the requested Apple connection profile.",
|
||||||
"other_logins": "Key login",
|
"other_logins": "Key login",
|
||||||
@@ -947,7 +961,7 @@
|
|||||||
"queue": {
|
"queue": {
|
||||||
"delete": "Delete all",
|
"delete": "Delete all",
|
||||||
"flush": "Flush queue",
|
"flush": "Flush queue",
|
||||||
"info" : "The mail queue contains all e-mails that are waiting for delivery. If an email is stuck in the mail queue for a long time, it is automatically deleted by the system.<br>The error message of the respective mail gives information about why the mail could not be delivered.",
|
"info": "The mail queue contains all e-mails that are waiting for delivery. If an email is stuck in the mail queue for a long time, it is automatically deleted by the system.<br>The error message of the respective mail gives information about why the mail could not be delivered.",
|
||||||
"legend": "Mail queue actions functions:",
|
"legend": "Mail queue actions functions:",
|
||||||
"ays": "Please confirm you want to delete all items from the current queue.",
|
"ays": "Please confirm you want to delete all items from the current queue.",
|
||||||
"deliver_mail": "Deliver",
|
"deliver_mail": "Deliver",
|
||||||
@@ -961,11 +975,11 @@
|
|||||||
"unhold_mail_legend": "Releases selected mails for delivery. (Requires prior hold)"
|
"unhold_mail_legend": "Releases selected mails for delivery. (Requires prior hold)"
|
||||||
},
|
},
|
||||||
"ratelimit": {
|
"ratelimit": {
|
||||||
"disabled": "Disabled",
|
"disabled": "Disabled",
|
||||||
"second": "msgs / second",
|
"second": "msgs / second",
|
||||||
"minute": "msgs / minute",
|
"minute": "msgs / minute",
|
||||||
"hour": "msgs / hour",
|
"hour": "msgs / hour",
|
||||||
"day": "msgs / day"
|
"day": "msgs / day"
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
"help": "Show/Hide help panel",
|
"help": "Show/Hide help panel",
|
||||||
@@ -1013,6 +1027,7 @@
|
|||||||
"forwarding_host_removed": "Forwarding host %s has been removed",
|
"forwarding_host_removed": "Forwarding host %s has been removed",
|
||||||
"global_filter_written": "Filter was successfully written to file",
|
"global_filter_written": "Filter was successfully written to file",
|
||||||
"hash_deleted": "Hash deleted",
|
"hash_deleted": "Hash deleted",
|
||||||
|
"ip_check_opt_in_modified": "IP check was saved successfully",
|
||||||
"item_deleted": "Item %s successfully deleted",
|
"item_deleted": "Item %s successfully deleted",
|
||||||
"item_released": "Item %s released",
|
"item_released": "Item %s released",
|
||||||
"items_deleted": "Item %s successfully deleted",
|
"items_deleted": "Item %s successfully deleted",
|
||||||
|
@@ -141,9 +141,11 @@
|
|||||||
"empty": "Sin resultados",
|
"empty": "Sin resultados",
|
||||||
"excludes": "Excluye a estos destinatarios",
|
"excludes": "Excluye a estos destinatarios",
|
||||||
"f2b_ban_time": "Tiempo de restricción (s)",
|
"f2b_ban_time": "Tiempo de restricción (s)",
|
||||||
|
"f2b_ban_time_increment": "Tiempo de restricción se incrementa con cada restricción",
|
||||||
"f2b_blacklist": "Redes y hosts en lista negra",
|
"f2b_blacklist": "Redes y hosts en lista negra",
|
||||||
"f2b_list_info": "Un host o red en lista negra siempre superará a una entidad de la lista blanca. <b>Las actualizaciones de la lista tardarán unos segundos en aplicarse.</b>",
|
"f2b_list_info": "Un host o red en lista negra siempre superará a una entidad de la lista blanca. <b>Las actualizaciones de la lista tardarán unos segundos en aplicarse.</b>",
|
||||||
"f2b_max_attempts": "Max num. de intentos",
|
"f2b_max_attempts": "Max num. de intentos",
|
||||||
|
"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_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_netban_ipv6": "Tamaño de subred IPv6 para aplicar la restricción (8-128)",
|
||||||
"f2b_parameters": "Parametros Fail2ban",
|
"f2b_parameters": "Parametros Fail2ban",
|
||||||
@@ -602,7 +604,6 @@
|
|||||||
"toggle_all": "Seleccionar todos"
|
"toggle_all": "Seleccionar todos"
|
||||||
},
|
},
|
||||||
"queue": {
|
"queue": {
|
||||||
"queue_deliver_mail": "Entregar",
|
|
||||||
"queue_manager": "Administrador de cola"
|
"queue_manager": "Administrador de cola"
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
|
@@ -686,7 +686,6 @@
|
|||||||
"toggle_all": "Valitse kaikki"
|
"toggle_all": "Valitse kaikki"
|
||||||
},
|
},
|
||||||
"queue": {
|
"queue": {
|
||||||
"queue_deliver_mail": "Toimittaa",
|
|
||||||
"queue_manager": "Jonon hallinta"
|
"queue_manager": "Jonon hallinta"
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
|
@@ -24,9 +24,11 @@
|
|||||||
"spam_policy": "Liste Noire/Liste Blanche",
|
"spam_policy": "Liste Noire/Liste Blanche",
|
||||||
"spam_score": "Score SPAM",
|
"spam_score": "Score SPAM",
|
||||||
"syncjobs": "Tâches de synchronisation",
|
"syncjobs": "Tâches de synchronisation",
|
||||||
"tls_policy": "Police TLS",
|
"tls_policy": "Politique TLS",
|
||||||
"unlimited_quota": "Quota illimité pour les boites de courriel",
|
"unlimited_quota": "Quota illimité pour les boites de courriel",
|
||||||
"domain_desc": "Modifier la description du domaine"
|
"domain_desc": "Modifier la description du domaine",
|
||||||
|
"domain_relayhost": "Changer le relais pour un domaine",
|
||||||
|
"mailbox_relayhost": "Changer le relais d’une boîte de réception"
|
||||||
},
|
},
|
||||||
"add": {
|
"add": {
|
||||||
"activate_filter_warn": "Tous les autres filtres seront désactivés, quand activé est coché.",
|
"activate_filter_warn": "Tous les autres filtres seront désactivés, quand activé est coché.",
|
||||||
@@ -103,7 +105,9 @@
|
|||||||
"username": "Nom d'utilisateur",
|
"username": "Nom d'utilisateur",
|
||||||
"validate": "Valider",
|
"validate": "Valider",
|
||||||
"validation_success": "Validation réussie",
|
"validation_success": "Validation réussie",
|
||||||
"bcc_dest_format": "La destination Cci doit être une seule adresse e-mail valide.<br>Si vous avez besoin d'envoyer une copie à plusieurs adresses, créez un alias et utilisez-le ici."
|
"bcc_dest_format": "La destination Cci doit être une seule adresse e-mail valide.<br>Si vous avez besoin d'envoyer une copie à plusieurs adresses, créez un alias et utilisez-le ici.",
|
||||||
|
"tags": "Etiquettes",
|
||||||
|
"app_passwd_protocols": "Protocoles autorisés pour le mot de passe de l'application"
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"access": "Accès",
|
"access": "Accès",
|
||||||
@@ -168,11 +172,13 @@
|
|||||||
"edit": "Editer",
|
"edit": "Editer",
|
||||||
"empty": "Aucun résultat",
|
"empty": "Aucun résultat",
|
||||||
"excludes": "Exclure ces destinataires",
|
"excludes": "Exclure ces destinataires",
|
||||||
"f2b_ban_time": "Durée du bannissement(s)",
|
"f2b_ban_time": "Durée du bannissement (s)",
|
||||||
|
"f2b_ban_time_increment": "Durée du bannissement est augmentée à chaque bannissement",
|
||||||
"f2b_blacklist": "Réseaux/Domaines sur Liste Noire",
|
"f2b_blacklist": "Réseaux/Domaines sur Liste Noire",
|
||||||
"f2b_filter": "Filtre(s) Regex",
|
"f2b_filter": "Filtre(s) Regex",
|
||||||
"f2b_list_info": "Un hôte ou un réseau sur liste noire l'emportera toujours sur une entité de liste blanche. <b>L'application des mises à jour de liste prendra quelques secondes.</b>",
|
"f2b_list_info": "Un hôte ou un réseau sur liste noire l'emportera toujours sur une entité de liste blanche. <b>L'application des mises à jour de liste prendra quelques secondes.</b>",
|
||||||
"f2b_max_attempts": "Nb max. de tentatives",
|
"f2b_max_attempts": "Nb max. de tentatives",
|
||||||
|
"f2b_max_ban_time": "Max. durée du bannissement (s)",
|
||||||
"f2b_netban_ipv4": "Taille du sous-réseau IPv4 pour l'application du bannissement (8-32)",
|
"f2b_netban_ipv4": "Taille du sous-réseau IPv4 pour l'application du bannissement (8-32)",
|
||||||
"f2b_netban_ipv6": "Taille du sous-réseau IPv6 pour l'application du bannissement (8-128)",
|
"f2b_netban_ipv6": "Taille du sous-réseau IPv6 pour l'application du bannissement (8-128)",
|
||||||
"f2b_parameters": "Paramètres Fail2ban",
|
"f2b_parameters": "Paramètres Fail2ban",
|
||||||
@@ -316,7 +322,11 @@
|
|||||||
"oauth2_add_client": "Ajouter un client OAuth2",
|
"oauth2_add_client": "Ajouter un client OAuth2",
|
||||||
"password_policy": "Politique de mots de passe",
|
"password_policy": "Politique de mots de passe",
|
||||||
"admins": "Administrateurs",
|
"admins": "Administrateurs",
|
||||||
"api_read_only": "Accès lecture-seule"
|
"api_read_only": "Accès lecture-seule",
|
||||||
|
"password_policy_lowerupper": "Doit contenir des caractères minuscules et majuscules",
|
||||||
|
"password_policy_numbers": "Doit contenir au moins un chiffre",
|
||||||
|
"ip_check": "Vérification IP",
|
||||||
|
"ip_check_disabled": "La vérification IP est désactivée. Vous pouvez l'activer sous<br> <strong>Système > Configuration > Options > Personnaliser</strong>"
|
||||||
},
|
},
|
||||||
"danger": {
|
"danger": {
|
||||||
"access_denied": "Accès refusé ou données de formulaire non valides",
|
"access_denied": "Accès refusé ou données de formulaire non valides",
|
||||||
@@ -435,7 +445,12 @@
|
|||||||
"username_invalid": "Le nom d'utilisateur %s ne peut pas être utilisé",
|
"username_invalid": "Le nom d'utilisateur %s ne peut pas être utilisé",
|
||||||
"validity_missing": "Veuillez attribuer une période de validité",
|
"validity_missing": "Veuillez attribuer une période de validité",
|
||||||
"value_missing": "Veuillez fournir toutes les valeurs",
|
"value_missing": "Veuillez fournir toutes les valeurs",
|
||||||
"yotp_verification_failed": "La vérification Yubico OTP a échoué : %s"
|
"yotp_verification_failed": "La vérification Yubico OTP a échoué : %s",
|
||||||
|
"webauthn_authenticator_failed": "L'authentificateur selectionné est introuvable",
|
||||||
|
"demo_mode_enabled": "Le mode de démonstration est activé",
|
||||||
|
"template_exists": "La template %s existe déja",
|
||||||
|
"template_id_invalid": "Le numéro de template %s est invalide",
|
||||||
|
"template_name_invalid": "Le nom de la template est invalide"
|
||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"chart_this_server": "Graphique (ce serveur)",
|
"chart_this_server": "Graphique (ce serveur)",
|
||||||
@@ -573,7 +588,7 @@
|
|||||||
"unchanged_if_empty": "Si non modifié, laisser en blanc",
|
"unchanged_if_empty": "Si non modifié, laisser en blanc",
|
||||||
"username": "Nom d'utilisateur",
|
"username": "Nom d'utilisateur",
|
||||||
"validate_save": "Valider et sauver",
|
"validate_save": "Valider et sauver",
|
||||||
"lookup_mx": "La destination est une expression régulière qui doit correspondre avec le nom du MX (<code>.*google\\.com</code> pour acheminer tout le courrier destiné à un MX se terminant par google.com via ce saut).",
|
"lookup_mx": "La destination est une expression régulière qui doit correspondre avec le nom du MX (<code>.*\\.google\\.com</code> pour acheminer tout le courrier destiné à un MX se terminant par google.com via ce saut)",
|
||||||
"mailbox_relayhost_info": "S'applique uniquement à la boîte aux lettres et aux alias directs, remplace le relayhost du domaine."
|
"mailbox_relayhost_info": "S'applique uniquement à la boîte aux lettres et aux alias directs, remplace le relayhost du domaine."
|
||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
@@ -607,7 +622,7 @@
|
|||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "La connexion a été retardée de %s secondes.",
|
"delayed": "La connexion a été retardée de %s secondes.",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "Connexion",
|
"login": "Connexion",
|
||||||
"mobileconfig_info": "Veuillez vous connecter en tant qu’utilisateur de la boîte pour télécharger le profil de connexion Apple demandé.",
|
"mobileconfig_info": "Veuillez vous connecter en tant qu’utilisateur de la boîte pour télécharger le profil de connexion Apple demandé.",
|
||||||
"other_logins": "Clé d'authentification",
|
"other_logins": "Clé d'authentification",
|
||||||
@@ -827,7 +842,6 @@
|
|||||||
"toggle_all": "Tout basculer"
|
"toggle_all": "Tout basculer"
|
||||||
},
|
},
|
||||||
"queue": {
|
"queue": {
|
||||||
"queue_deliver_mail": "Délivrer",
|
|
||||||
"queue_manager": "Gestion de la file d'attente"
|
"queue_manager": "Gestion de la file d'attente"
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
@@ -1077,9 +1091,12 @@
|
|||||||
"username": "Nom d'utilisateur",
|
"username": "Nom d'utilisateur",
|
||||||
"verify": "Vérification",
|
"verify": "Vérification",
|
||||||
"waiting": "En attente",
|
"waiting": "En attente",
|
||||||
"week": "Semaine",
|
"week": "semaine",
|
||||||
"weekly": "Hebdomadaire",
|
"weekly": "Hebdomadaire",
|
||||||
"weeks": "semaines"
|
"weeks": "semaines",
|
||||||
|
"months": "mois",
|
||||||
|
"year": "année",
|
||||||
|
"years": "années"
|
||||||
},
|
},
|
||||||
"warning": {
|
"warning": {
|
||||||
"cannot_delete_self": "Impossible de supprimer l’utilisateur connecté",
|
"cannot_delete_self": "Impossible de supprimer l’utilisateur connecté",
|
||||||
|
@@ -216,7 +216,6 @@
|
|||||||
"toggle_all": "Összes átkapcsolása"
|
"toggle_all": "Összes átkapcsolása"
|
||||||
},
|
},
|
||||||
"queue": {
|
"queue": {
|
||||||
"queue_command_success": "Queue command completed successfully",
|
|
||||||
"queue_manager": "Queue Manager"
|
"queue_manager": "Queue Manager"
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
|
@@ -43,7 +43,7 @@
|
|||||||
"app_name": "Nome app",
|
"app_name": "Nome app",
|
||||||
"app_password": "Aggiungi la password dell'app",
|
"app_password": "Aggiungi la password dell'app",
|
||||||
"automap": "Prova a mappare automaticamente le cartelle (\"Sent items\", \"Sent\" => \"Posta inviata\" ecc.)",
|
"automap": "Prova a mappare automaticamente le cartelle (\"Sent items\", \"Sent\" => \"Posta inviata\" ecc.)",
|
||||||
"backup_mx_options": "Relay options",
|
"backup_mx_options": "Opzioni di inoltro",
|
||||||
"comment_info": "Un commento privato non è visibile all'utente, mentre un commento pubblico viene mostrato come suggerimento quando si passa con il mouse nella panoramica di un utente",
|
"comment_info": "Un commento privato non è visibile all'utente, mentre un commento pubblico viene mostrato come suggerimento quando si passa con il mouse nella panoramica di un utente",
|
||||||
"custom_params": "Parametri personalizzati",
|
"custom_params": "Parametri personalizzati",
|
||||||
"custom_params_hint": "Corretto: --param=xy, errato: --param xy",
|
"custom_params_hint": "Corretto: --param=xy, errato: --param xy",
|
||||||
@@ -175,10 +175,12 @@
|
|||||||
"empty": "Nessun risultato",
|
"empty": "Nessun risultato",
|
||||||
"excludes": "Esclude questi destinatari",
|
"excludes": "Esclude questi destinatari",
|
||||||
"f2b_ban_time": "Tempo di blocco (s)",
|
"f2b_ban_time": "Tempo di blocco (s)",
|
||||||
|
"f2b_ban_time_increment": "Tempo di blocco aumenta ad ogni blocco",
|
||||||
"f2b_blacklist": "Host/reti in blacklist",
|
"f2b_blacklist": "Host/reti in blacklist",
|
||||||
"f2b_filter": "Filtri Regex",
|
"f2b_filter": "Filtri Regex",
|
||||||
"f2b_list_info": "Un host oppure una rete in blacklist, avrà sempre un peso maggiore rispetto ad una in whitelist. <b>L'aggiornamento della lista richiede alcuni secondi per la sua entrata in azione.</b>",
|
"f2b_list_info": "Un host oppure una rete in blacklist, avrà sempre un peso maggiore rispetto ad una in whitelist. <b>L'aggiornamento della lista richiede alcuni secondi per la sua entrata in azione.</b>",
|
||||||
"f2b_max_attempts": "Tentativi massimi",
|
"f2b_max_attempts": "Tentativi massimi",
|
||||||
|
"f2b_max_ban_time": "Tempo massimo di blocco (s)",
|
||||||
"f2b_netban_ipv4": "IPv4 subnet size to apply ban on (8-32)",
|
"f2b_netban_ipv4": "IPv4 subnet size to apply ban on (8-32)",
|
||||||
"f2b_netban_ipv6": "IPv6 subnet size to apply ban on (8-128)",
|
"f2b_netban_ipv6": "IPv6 subnet size to apply ban on (8-128)",
|
||||||
"f2b_parameters": "Parametri Fail2ban",
|
"f2b_parameters": "Parametri Fail2ban",
|
||||||
@@ -211,7 +213,7 @@
|
|||||||
"loading": "Caricamento in corso...",
|
"loading": "Caricamento in corso...",
|
||||||
"login_time": "Ora di accesso",
|
"login_time": "Ora di accesso",
|
||||||
"logo_info": "La tua immagine verrà ridimensionata a 40px di altezza, quando verrà usata nella barra di navigazione in alto, ed ad una larghezza massima di 250px nella schermata iniziale. È altamente consigliato l'utilizzo di un'immagine modulabile.",
|
"logo_info": "La tua immagine verrà ridimensionata a 40px di altezza, quando verrà usata nella barra di navigazione in alto, ed ad una larghezza massima di 250px nella schermata iniziale. È altamente consigliato l'utilizzo di un'immagine modulabile.",
|
||||||
"lookup_mx": "Destination is a regular expression to match against MX name (<code>.*google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
|
"lookup_mx": "Destination is a regular expression to match against MX name (<code>.*\\.google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
|
||||||
"main_name": "Nome \"mailcow UI\"",
|
"main_name": "Nome \"mailcow UI\"",
|
||||||
"merged_vars_hint": "Greyed out rows were merged from <code>vars.(local.)inc.php</code> and cannot be modified.",
|
"merged_vars_hint": "Greyed out rows were merged from <code>vars.(local.)inc.php</code> and cannot be modified.",
|
||||||
"message": "Messaggio",
|
"message": "Messaggio",
|
||||||
@@ -303,7 +305,7 @@
|
|||||||
"spamfilter": "Filtri spam",
|
"spamfilter": "Filtri spam",
|
||||||
"subject": "Oggetto",
|
"subject": "Oggetto",
|
||||||
"success": "Successo",
|
"success": "Successo",
|
||||||
"sys_mails": "System mails",
|
"sys_mails": "Mail di sistema",
|
||||||
"text": "Testo",
|
"text": "Testo",
|
||||||
"time": "Orario",
|
"time": "Orario",
|
||||||
"title": "Titolo",
|
"title": "Titolo",
|
||||||
@@ -335,7 +337,8 @@
|
|||||||
"api_read_write": "Accesso in lettura-scrittura",
|
"api_read_write": "Accesso in lettura-scrittura",
|
||||||
"oauth2_apps": "App OAuth2",
|
"oauth2_apps": "App OAuth2",
|
||||||
"oauth2_add_client": "Aggiungere il client OAuth2",
|
"oauth2_add_client": "Aggiungere il client OAuth2",
|
||||||
"rsettings_preset_4": "Disattivare Rspamd per un dominio"
|
"rsettings_preset_4": "Disattivare Rspamd per un dominio",
|
||||||
|
"options": "Opzioni"
|
||||||
},
|
},
|
||||||
"danger": {
|
"danger": {
|
||||||
"access_denied": "Accesso negato o form di login non corretto",
|
"access_denied": "Accesso negato o form di login non corretto",
|
||||||
@@ -364,7 +367,7 @@
|
|||||||
"extra_acl_invalid": "External sender address \"%s\" is invalid",
|
"extra_acl_invalid": "External sender address \"%s\" is invalid",
|
||||||
"extra_acl_invalid_domain": "External sender \"%s\" uses an invalid domain",
|
"extra_acl_invalid_domain": "External sender \"%s\" uses an invalid domain",
|
||||||
"fido2_verification_failed": "FIDO2 verification failed: %s",
|
"fido2_verification_failed": "FIDO2 verification failed: %s",
|
||||||
"file_open_error": "File cannot be opened for writing",
|
"file_open_error": "Il file non può essere aperto per la scrittura",
|
||||||
"filter_type": "Wrong filter type",
|
"filter_type": "Wrong filter type",
|
||||||
"from_invalid": "Il mittente non può essere vuoto",
|
"from_invalid": "Il mittente non può essere vuoto",
|
||||||
"global_filter_write_error": "Could not write filter file: %s",
|
"global_filter_write_error": "Could not write filter file: %s",
|
||||||
@@ -397,7 +400,7 @@
|
|||||||
"mailbox_quota_exceeds_domain_quota": "Lo spazio massimo supera la spazio del dominio",
|
"mailbox_quota_exceeds_domain_quota": "Lo spazio massimo supera la spazio del dominio",
|
||||||
"mailbox_quota_left_exceeded": "Non c'è abbastanza spazio libero (space left: %d MiB)",
|
"mailbox_quota_left_exceeded": "Non c'è abbastanza spazio libero (space left: %d MiB)",
|
||||||
"mailboxes_in_use": "Lo spazio massimo della casella deve essere maggiore o uguale a %d",
|
"mailboxes_in_use": "Lo spazio massimo della casella deve essere maggiore o uguale a %d",
|
||||||
"malformed_username": "Malformed username",
|
"malformed_username": "Nome utente non valido",
|
||||||
"map_content_empty": "Map content cannot be empty",
|
"map_content_empty": "Map content cannot be empty",
|
||||||
"max_alias_exceeded": "Numero massimo di alias superato",
|
"max_alias_exceeded": "Numero massimo di alias superato",
|
||||||
"max_mailbox_exceeded": "Numero massimo di caselle superato (%d of %d)",
|
"max_mailbox_exceeded": "Numero massimo di caselle superato (%d of %d)",
|
||||||
@@ -429,18 +432,18 @@
|
|||||||
"resource_invalid": "Il nome della risorsa non è valido",
|
"resource_invalid": "Il nome della risorsa non è valido",
|
||||||
"rl_timeframe": "Rate limit time frame is incorrect",
|
"rl_timeframe": "Rate limit time frame is incorrect",
|
||||||
"rspamd_ui_pw_length": "Rspamd UI password should be at least 6 chars long",
|
"rspamd_ui_pw_length": "Rspamd UI password should be at least 6 chars long",
|
||||||
"script_empty": "Script cannot be empty",
|
"script_empty": "Lo script non può essere vuoto",
|
||||||
"sender_acl_invalid": "Il valore di Sender ACL non è valido",
|
"sender_acl_invalid": "Il valore di Sender ACL non è valido",
|
||||||
"set_acl_failed": "Failed to set ACL",
|
"set_acl_failed": "Failed to set ACL",
|
||||||
"settings_map_invalid": "Settings map ID %s invalid",
|
"settings_map_invalid": "Settings map ID %s invalid",
|
||||||
"sieve_error": "Sieve parser error: %s",
|
"sieve_error": "Sieve parser error: %s",
|
||||||
"spam_learn_error": "Spam learn error: %s",
|
"spam_learn_error": "Spam learn error: %s",
|
||||||
"subject_empty": "Subject must not be empty",
|
"subject_empty": "L'oggetto non deve essere vuoto",
|
||||||
"target_domain_invalid": "Goto domain non è valido",
|
"target_domain_invalid": "Goto domain non è valido",
|
||||||
"targetd_not_found": "Il target del dominio non è stato trovato",
|
"targetd_not_found": "Il target del dominio non è stato trovato",
|
||||||
"targetd_relay_domain": "Target domain %s is a relay domain",
|
"targetd_relay_domain": "Target domain %s is a relay domain",
|
||||||
"temp_error": "Temporary error",
|
"temp_error": "Errore temporaneo",
|
||||||
"text_empty": "Text must not be empty",
|
"text_empty": "Il testo non deve essere vuoto",
|
||||||
"tfa_token_invalid": "TFA token invalid",
|
"tfa_token_invalid": "TFA token invalid",
|
||||||
"tls_policy_map_dest_invalid": "Policy destination is invalid",
|
"tls_policy_map_dest_invalid": "Policy destination is invalid",
|
||||||
"tls_policy_map_entry_exists": "A TLS policy map entry \"%s\" exists",
|
"tls_policy_map_entry_exists": "A TLS policy map entry \"%s\" exists",
|
||||||
@@ -448,40 +451,54 @@
|
|||||||
"totp_verification_failed": "TOTP verification failed",
|
"totp_verification_failed": "TOTP verification failed",
|
||||||
"transport_dest_exists": "Transport destination \"%s\" exists",
|
"transport_dest_exists": "Transport destination \"%s\" exists",
|
||||||
"webauthn_verification_failed": "WebAuthn verification failed: %s",
|
"webauthn_verification_failed": "WebAuthn verification failed: %s",
|
||||||
"unknown": "An unknown error occurred",
|
"unknown": "Si è verificato un errore sconosciuto",
|
||||||
"unknown_tfa_method": "Unknown TFA method",
|
"unknown_tfa_method": "Unknown TFA method",
|
||||||
"unlimited_quota_acl": "Unlimited quota prohibited by ACL",
|
"unlimited_quota_acl": "Unlimited quota prohibited by ACL",
|
||||||
"username_invalid": "Username %s non può essere utilizzato",
|
"username_invalid": "Il nome utente %s non può essere utilizzato",
|
||||||
"validity_missing": "Assegnare un periodo di validità",
|
"validity_missing": "Assegnare un periodo di validità",
|
||||||
"value_missing": "Si prega di fornire tutti i valori",
|
"value_missing": "Si prega di fornire tutti i valori",
|
||||||
"yotp_verification_failed": "Verifica OTP Yubico fallita: %s"
|
"yotp_verification_failed": "Verifica OTP Yubico fallita: %s",
|
||||||
|
"demo_mode_enabled": "La modalità demo è abilitata",
|
||||||
|
"template_name_invalid": "Nome template non valido",
|
||||||
|
"template_exists": "Il template %s esiste già",
|
||||||
|
"template_id_invalid": "Il template con ID %s non è valido"
|
||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"chart_this_server": "Grafico (questo server)",
|
"chart_this_server": "Grafico (questo server)",
|
||||||
"containers_info": "Container information",
|
"containers_info": "Informazioni sul container",
|
||||||
"disk_usage": "Uso del disco",
|
"disk_usage": "Uso del disco",
|
||||||
"docs": "Docs",
|
"docs": "Docs",
|
||||||
"external_logs": "External logs",
|
"external_logs": "Log esterni",
|
||||||
"history_all_servers": "History (all servers)",
|
"history_all_servers": "Cronologia (tutti i server)",
|
||||||
"in_memory_logs": "In-memory logs",
|
"in_memory_logs": "In-memory logs",
|
||||||
"jvm_memory_solr": "JVM memory usage",
|
"jvm_memory_solr": "JVM memory usage",
|
||||||
"last_modified": "Ultima modifica",
|
"last_modified": "Ultima modifica",
|
||||||
"log_info": "<p>mailcow <b>in-memory logs</b> are collected in Redis lists and trimmed to LOG_LINES (%d) every minute to reduce hammering.\r\n <br>In-memory logs are not meant to be persistent. All applications that log in-memory, also log to the Docker daemon and therefore to the default logging driver.\r\n <br>The in-memory log type should be used for debugging minor issues with containers.</p>\r\n <p><b>External logs</b> are collected via API of the given application.</p>\r\n <p><b>Static logs</b> are mostly activity logs, that are not logged to the Dockerd but still need to be persistent (except for API logs).</p>",
|
"log_info": "<p>mailcow <b>in-memory logs</b> are collected in Redis lists and trimmed to LOG_LINES (%d) every minute to reduce hammering.\r\n <br>In-memory logs are not meant to be persistent. All applications that log in-memory, also log to the Docker daemon and therefore to the default logging driver.\r\n <br>The in-memory log type should be used for debugging minor issues with containers.</p>\r\n <p><b>External logs</b> are collected via API of the given application.</p>\r\n <p><b>Static logs</b> are mostly activity logs, that are not logged to the Dockerd but still need to be persistent (except for API logs).</p>",
|
||||||
"login_time": "Time",
|
"login_time": "Orario",
|
||||||
"logs": "Logs",
|
"logs": "Logs",
|
||||||
"online_users": "Users online",
|
"online_users": "Utenti online",
|
||||||
"restart_container": "Riavvio",
|
"restart_container": "Riavvio",
|
||||||
"service": "Servizio",
|
"service": "Servizio",
|
||||||
"size": "Size",
|
"size": "Dimensione",
|
||||||
"solr_dead": "Solr is starting, disabled or died.",
|
"solr_dead": "Solr sta partendo, è disabilitato o morto.",
|
||||||
"solr_status": "Stato Solr",
|
"solr_status": "Stato Solr",
|
||||||
"started_at": "Started at",
|
"started_at": "Iniziato alle",
|
||||||
"started_on": "Started on",
|
"started_on": "Iniziato",
|
||||||
"static_logs": "Static logs",
|
"static_logs": "Log statici",
|
||||||
"success": "Successo",
|
"success": "Successo",
|
||||||
"system_containers": "System & Containers",
|
"system_containers": "Sistema & Containers",
|
||||||
"uptime": "Tempo di attività",
|
"uptime": "Tempo di attività",
|
||||||
"username": "Username"
|
"username": "Nome utente",
|
||||||
|
"container_disabled": "Container arrestato o disattivato",
|
||||||
|
"update_available": "È disponibile un aggiornamento",
|
||||||
|
"container_running": "In esecuzione",
|
||||||
|
"container_stopped": "Arrestato",
|
||||||
|
"cores": "Cores",
|
||||||
|
"current_time": "Orario di sistema",
|
||||||
|
"memory": "Memoria",
|
||||||
|
"timezone": "Fuso orario",
|
||||||
|
"no_update_available": "Il sistema è aggiornato all'ultima versione",
|
||||||
|
"update_failed": "Impossibile verificare la presenza di un aggiornamento"
|
||||||
},
|
},
|
||||||
"diagnostics": {
|
"diagnostics": {
|
||||||
"cname_from_a": "Valore letto dal record A/AAAA. Questo è supportato finché il record punta alla risorsa corretta.",
|
"cname_from_a": "Valore letto dal record A/AAAA. Questo è supportato finché il record punta alla risorsa corretta.",
|
||||||
@@ -514,7 +531,7 @@
|
|||||||
"delete1": "Elimina dalla sorgente al termine",
|
"delete1": "Elimina dalla sorgente al termine",
|
||||||
"delete2": "Delete messages on destination that are not on source",
|
"delete2": "Delete messages on destination that are not on source",
|
||||||
"delete2duplicates": "Elimina duplicati nella destinazione",
|
"delete2duplicates": "Elimina duplicati nella destinazione",
|
||||||
"delete_ays": "Please confirm the deletion process.",
|
"delete_ays": "Si prega di confermare il processo di eliminazione.",
|
||||||
"description": "Descrizione",
|
"description": "Descrizione",
|
||||||
"disable_login": "Disabilita l'accesso (la posta in arrivo viene correttamente recapitata)",
|
"disable_login": "Disabilita l'accesso (la posta in arrivo viene correttamente recapitata)",
|
||||||
"domain": "Modifica dominio",
|
"domain": "Modifica dominio",
|
||||||
@@ -527,17 +544,17 @@
|
|||||||
"exclude": "Escludi oggetti (regex)",
|
"exclude": "Escludi oggetti (regex)",
|
||||||
"extended_sender_acl": "External sender addresses",
|
"extended_sender_acl": "External sender addresses",
|
||||||
"extended_sender_acl_info": "A DKIM domain key should be imported, if available.<br>\r\n Remember to add this server to the corresponding SPF TXT record.<br>\r\n Whenever a domain or alias domain is added to this server, that overlaps with an external address, the external address is removed.<br>\r\n Use @domain.tld to allow to send as *@domain.tld.",
|
"extended_sender_acl_info": "A DKIM domain key should be imported, if available.<br>\r\n Remember to add this server to the corresponding SPF TXT record.<br>\r\n Whenever a domain or alias domain is added to this server, that overlaps with an external address, the external address is removed.<br>\r\n Use @domain.tld to allow to send as *@domain.tld.",
|
||||||
"force_pw_update": "Force password update at next login",
|
"force_pw_update": "Forza l'aggiornamento della password al prossimo accesso",
|
||||||
"force_pw_update_info": "Questo utente potrà accedere solo a %s.",
|
"force_pw_update_info": "Questo utente potrà accedere solo a %s.",
|
||||||
"full_name": "Nome completo",
|
"full_name": "Nome completo",
|
||||||
"gal": "Global Address List",
|
"gal": "Global Address List",
|
||||||
"gal_info": "The GAL contains all objects of a domain and cannot be edited by any user. Free/busy information in SOGo is missing, if disabled! <b>Restart SOGo to apply changes.</b>",
|
"gal_info": "The GAL contains all objects of a domain and cannot be edited by any user. Free/busy information in SOGo is missing, if disabled! <b>Restart SOGo to apply changes.</b>",
|
||||||
"generate": "generate",
|
"generate": "crea",
|
||||||
"grant_types": "Grant types",
|
"grant_types": "Grant types",
|
||||||
"hostname": "Hostname",
|
"hostname": "Hostname",
|
||||||
"inactive": "Inattivo",
|
"inactive": "Inattivo",
|
||||||
"kind": "Genere",
|
"kind": "Genere",
|
||||||
"lookup_mx": "Destination is a regular expression to match against MX name (<code>.*google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
|
"lookup_mx": "Destination is a regular expression to match against MX name (<code>.*\\.google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
|
||||||
"mailbox": "Modifica casella di posta",
|
"mailbox": "Modifica casella di posta",
|
||||||
"mailbox_quota_def": "Default mailbox quota",
|
"mailbox_quota_def": "Default mailbox quota",
|
||||||
"mailbox_relayhost_info": "Applied to the mailbox and direct aliases only, does override a domain relayhost.",
|
"mailbox_relayhost_info": "Applied to the mailbox and direct aliases only, does override a domain relayhost.",
|
||||||
@@ -549,7 +566,7 @@
|
|||||||
"mbox_rl_info": "This rate limit is applied on the SASL login name, it matches any \"from\" address used by the logged-in user. A mailbox rate limit overrides a domain-wide rate limit.",
|
"mbox_rl_info": "This rate limit is applied on the SASL login name, it matches any \"from\" address used by the logged-in user. A mailbox rate limit overrides a domain-wide rate limit.",
|
||||||
"mins_interval": "Intervallo (min)",
|
"mins_interval": "Intervallo (min)",
|
||||||
"multiple_bookings": "Prenotazioni multiple",
|
"multiple_bookings": "Prenotazioni multiple",
|
||||||
"nexthop": "Next hop",
|
"nexthop": "Prossimo hop",
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"password_repeat": "Conferma password (riscrivi)",
|
"password_repeat": "Conferma password (riscrivi)",
|
||||||
"previous": "Pagina precedente",
|
"previous": "Pagina precedente",
|
||||||
@@ -561,9 +578,9 @@
|
|||||||
"pushover_sender_array": "Only consider the following sender email addresses <small>(comma-separated)</small>",
|
"pushover_sender_array": "Only consider the following sender email addresses <small>(comma-separated)</small>",
|
||||||
"pushover_sender_regex": "Consider the following sender regex",
|
"pushover_sender_regex": "Consider the following sender regex",
|
||||||
"pushover_text": "Notification text",
|
"pushover_text": "Notification text",
|
||||||
"pushover_title": "Notification title",
|
"pushover_title": "Titolo della notifica",
|
||||||
"pushover_vars": "When no sender filter is defined, all mails will be considered.<br>Regex filters as well as exact sender checks can be defined individually and will be considered sequentially. They do not depend on each other.<br>Useable variables for text and title (please take note of data protection policies)",
|
"pushover_vars": "When no sender filter is defined, all mails will be considered.<br>Regex filters as well as exact sender checks can be defined individually and will be considered sequentially. They do not depend on each other.<br>Useable variables for text and title (please take note of data protection policies)",
|
||||||
"pushover_verify": "Verify credentials",
|
"pushover_verify": "Verifica credenziali",
|
||||||
"quota_mb": "Spazio (MiB)",
|
"quota_mb": "Spazio (MiB)",
|
||||||
"quota_warning_bcc": "Quota warning BCC",
|
"quota_warning_bcc": "Quota warning BCC",
|
||||||
"quota_warning_bcc_info": "Warnings will be sent as separate copies to the following recipients. The subject will be suffixed by the corresponding username in brackets, for example: <code>Quota warning (user@example.com)</code>.",
|
"quota_warning_bcc_info": "Warnings will be sent as separate copies to the following recipients. The subject will be suffixed by the corresponding username in brackets, for example: <code>Quota warning (user@example.com)</code>.",
|
||||||
@@ -582,42 +599,44 @@
|
|||||||
"sender_acl": "Consenti di inviare come",
|
"sender_acl": "Consenti di inviare come",
|
||||||
"sender_acl_disabled": "<span class=\"badge fs-6 bg-danger\">Sender check is disabled</span>",
|
"sender_acl_disabled": "<span class=\"badge fs-6 bg-danger\">Sender check is disabled</span>",
|
||||||
"sender_acl_info": "If mailbox user A is allowed to send as mailbox user B, the sender address is not automatically displayed as selectable \"from\" field in SOGo.<br>\r\n Mailbox user B needs to create a delegation in SOGo to allow mailbox user A to select their address as sender. To delegate a mailbox in SOGo, use the menu (three dots) to the right of your mailbox name in the upper left while in mail view. This behaviour does not apply to alias addresses.",
|
"sender_acl_info": "If mailbox user A is allowed to send as mailbox user B, the sender address is not automatically displayed as selectable \"from\" field in SOGo.<br>\r\n Mailbox user B needs to create a delegation in SOGo to allow mailbox user A to select their address as sender. To delegate a mailbox in SOGo, use the menu (three dots) to the right of your mailbox name in the upper left while in mail view. This behaviour does not apply to alias addresses.",
|
||||||
"sieve_desc": "Short description",
|
"sieve_desc": "Breve descrizione",
|
||||||
"sieve_type": "Filter type",
|
"sieve_type": "Filter type",
|
||||||
"skipcrossduplicates": "Skip duplicate messages across folders (first come, first serve)",
|
"skipcrossduplicates": "Skip duplicate messages across folders (first come, first serve)",
|
||||||
"sogo_visible": "Alias is visible in SOGo",
|
"sogo_visible": "L'alias è visibile in SOGo",
|
||||||
"sogo_visible_info": "This option only affects objects, that can be displayed in SOGo (shared or non-shared alias addresses pointing to at least one local mailbox). If hidden, an alias will not appear as selectable sender in SOGo.",
|
"sogo_visible_info": "This option only affects objects, that can be displayed in SOGo (shared or non-shared alias addresses pointing to at least one local mailbox). If hidden, an alias will not appear as selectable sender in SOGo.",
|
||||||
"spam_alias": "Create or change time limited alias addresses",
|
"spam_alias": "Create or change time limited alias addresses",
|
||||||
"spam_filter": "Spam filter",
|
"spam_filter": "Spam filter",
|
||||||
"spam_policy": "Add or remove items to white-/blacklist",
|
"spam_policy": "Aggiungi o rimuovi elementi dalla whitelist/blacklist",
|
||||||
"spam_score": "Set a custom spam score",
|
"spam_score": "Imposta un punteggio spam personalizzato",
|
||||||
"subfolder2": "Sincronizza in una sottocartella<br /><small>(vuoto = non sincronizzare in sottocartella)</small>",
|
"subfolder2": "Sincronizza in una sottocartella<br /><small>(vuoto = non sincronizzare in sottocartella)</small>",
|
||||||
"syncjob": "Modifica sincronizzazione",
|
"syncjob": "Modifica sincronizzazione",
|
||||||
"target_address": "Vai all'indirizzo/i <small>(separato da virgola)</small>",
|
"target_address": "Vai all'indirizzo/i <small>(separato da virgola)</small>",
|
||||||
"target_domain": "Target dominio",
|
"target_domain": "Target dominio",
|
||||||
"timeout1": "Timeout for connection to remote host",
|
"timeout1": "Timeout per la connessione all'host remoto",
|
||||||
"timeout2": "Timeout for connection to local host",
|
"timeout2": "Timeout per la connessione all'host remoto",
|
||||||
"title": "Modifica oggetto",
|
"title": "Modifica oggetto",
|
||||||
"unchanged_if_empty": "Se immutato lasciare vuoto",
|
"unchanged_if_empty": "Se immutato lasciare vuoto",
|
||||||
"username": "Username",
|
"username": "Nome utente",
|
||||||
"validate_save": "Convalida e salva",
|
"validate_save": "Convalida e salva",
|
||||||
"pushover": "Pushover",
|
"pushover": "Pushover",
|
||||||
"sogo_access_info": "Il single-sign-on dall'interno dell'interfaccia di posta rimane funzionante. Questa impostazione non influisce sull'accesso a tutti gli altri servizi né cancella o modifica il profilo SOGo esistente dell'utente.",
|
"sogo_access_info": "Il single-sign-on dall'interno dell'interfaccia di posta rimane funzionante. Questa impostazione non influisce sull'accesso a tutti gli altri servizi né cancella o modifica il profilo SOGo esistente dell'utente.",
|
||||||
"none_inherit": "Nessuno / Eredita",
|
"none_inherit": "Nessuno / Eredita",
|
||||||
"sogo_access": "Concedere l'accesso diretto a SOGo",
|
"sogo_access": "Concedere l'accesso diretto a SOGo",
|
||||||
"acl": "ACL (autorizzazione)",
|
"acl": "ACL (autorizzazione)",
|
||||||
"app_passwd_protocols": "Protocolli consentiti per la password dell'app"
|
"app_passwd_protocols": "Protocolli consentiti per la password dell'app",
|
||||||
|
"last_modified": "Ultima modifica",
|
||||||
|
"pushover_sound": "Suono"
|
||||||
},
|
},
|
||||||
"fido2": {
|
"fido2": {
|
||||||
"confirm": "Confirm",
|
"confirm": "Conferma",
|
||||||
"fido2_auth": "Login with FIDO2",
|
"fido2_auth": "Login with FIDO2",
|
||||||
"fido2_success": "Device successfully registered",
|
"fido2_success": "Dispositivo registrato con successo",
|
||||||
"fido2_validation_failed": "Validation failed",
|
"fido2_validation_failed": "Validazione fallita",
|
||||||
"fn": "Friendly name",
|
"fn": "Nome descrittivo",
|
||||||
"known_ids": "Known IDs",
|
"known_ids": "ID conosciuti",
|
||||||
"none": "Disabled",
|
"none": "Disabilitato",
|
||||||
"register_status": "Registration status",
|
"register_status": "Stato di registrazione",
|
||||||
"rename": "Rename",
|
"rename": "Rinominare",
|
||||||
"set_fido2": "Register FIDO2 device",
|
"set_fido2": "Register FIDO2 device",
|
||||||
"set_fn": "Set friendly name",
|
"set_fn": "Set friendly name",
|
||||||
"start_fido2_validation": "Start FIDO2 validation",
|
"start_fido2_validation": "Start FIDO2 validation",
|
||||||
@@ -641,13 +660,14 @@
|
|||||||
"header": {
|
"header": {
|
||||||
"administration": "Amministrazione",
|
"administration": "Amministrazione",
|
||||||
"apps": "App",
|
"apps": "App",
|
||||||
"debug": "Informazioni di sistema",
|
"debug": "Informazioni",
|
||||||
"email": "E-Mail",
|
"email": "E-Mail",
|
||||||
"mailcow_config": "Configurazione",
|
"mailcow_config": "Configurazione",
|
||||||
"quarantine": "Quarantena",
|
"quarantine": "Quarantena",
|
||||||
"restart_netfilter": "Riavvia netfilter",
|
"restart_netfilter": "Riavvia netfilter",
|
||||||
"restart_sogo": "Riavvia SOGo",
|
"restart_sogo": "Riavvia SOGo",
|
||||||
"user_settings": "Impostazioni utente"
|
"user_settings": "Impostazioni utente",
|
||||||
|
"mailcow_system": "Sistema"
|
||||||
},
|
},
|
||||||
"info": {
|
"info": {
|
||||||
"awaiting_tfa_confirmation": "In attesa di conferma TFA",
|
"awaiting_tfa_confirmation": "In attesa di conferma TFA",
|
||||||
@@ -656,12 +676,12 @@
|
|||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "L'accesso è stato ritardato di %s secondi.",
|
"delayed": "L'accesso è stato ritardato di %s secondi.",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
"mobileconfig_info": "Please login as mailbox user to download the requested Apple connection profile.",
|
"mobileconfig_info": "Please login as mailbox user to download the requested Apple connection profile.",
|
||||||
"other_logins": "Key login",
|
"other_logins": "Key login",
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"username": "Username"
|
"username": "Nome utente"
|
||||||
},
|
},
|
||||||
"mailbox": {
|
"mailbox": {
|
||||||
"action": "Azione",
|
"action": "Azione",
|
||||||
@@ -733,7 +753,7 @@
|
|||||||
"inactive": "Inattivo",
|
"inactive": "Inattivo",
|
||||||
"insert_preset": "Insert example preset \"%s\"",
|
"insert_preset": "Insert example preset \"%s\"",
|
||||||
"kind": "Tipo",
|
"kind": "Tipo",
|
||||||
"last_mail_login": "Last mail login",
|
"last_mail_login": "Ultimo accesso alla posta",
|
||||||
"last_modified": "Ultima modifica",
|
"last_modified": "Ultima modifica",
|
||||||
"last_pw_change": "Ultima modifica della password",
|
"last_pw_change": "Ultima modifica della password",
|
||||||
"last_run": "Ultima esecuzione",
|
"last_run": "Ultima esecuzione",
|
||||||
@@ -828,7 +848,15 @@
|
|||||||
"sender": "Mittente",
|
"sender": "Mittente",
|
||||||
"all_domains": "Tutti i domini",
|
"all_domains": "Tutti i domini",
|
||||||
"recipient": "Destinatario",
|
"recipient": "Destinatario",
|
||||||
"syncjob_EX_OK": "Successo"
|
"syncjob_EX_OK": "Successo",
|
||||||
|
"add_template": "Aggiungi template",
|
||||||
|
"force_pw_update": "Forza il cambio della password al prossimo accesso",
|
||||||
|
"relay_unknown": "Inoltra a caselle di posta sconosciute",
|
||||||
|
"mailbox_templates": "Template della mailbox",
|
||||||
|
"domain_templates": "Template di dominio",
|
||||||
|
"gal": "Elenco indirizzi globale",
|
||||||
|
"templates": "Template",
|
||||||
|
"template": "Template"
|
||||||
},
|
},
|
||||||
"oauth2": {
|
"oauth2": {
|
||||||
"access_denied": "Effettua il login alla casella di posta per garantire l'accesso tramite OAuth2.",
|
"access_denied": "Effettua il login alla casella di posta per garantire l'accesso tramite OAuth2.",
|
||||||
@@ -847,7 +875,7 @@
|
|||||||
"confirm_delete": "Conferma l'eliminazione di questo elemento.",
|
"confirm_delete": "Conferma l'eliminazione di questo elemento.",
|
||||||
"danger": "Pericolo",
|
"danger": "Pericolo",
|
||||||
"deliver_inbox": "Consegna nella posta in arrivo",
|
"deliver_inbox": "Consegna nella posta in arrivo",
|
||||||
"disabled_by_config": "The current system configuration disables the quarantine functionality. Please set \"retentions per mailbox\" and a \"maximum size\" for quarantine elements.",
|
"disabled_by_config": "L'attuale configurazione del sistema disabilita la funzionalità di quarantena. Imposta \"conservazioni per casella di posta\" e \"dimensione massima\" per gli elementi di quarantena.",
|
||||||
"download_eml": "Download (.eml)",
|
"download_eml": "Download (.eml)",
|
||||||
"empty": "Nessun risultato",
|
"empty": "Nessun risultato",
|
||||||
"high_danger": "Alto",
|
"high_danger": "Alto",
|
||||||
@@ -893,8 +921,18 @@
|
|||||||
"type": "Tipologia"
|
"type": "Tipologia"
|
||||||
},
|
},
|
||||||
"queue": {
|
"queue": {
|
||||||
"queue_deliver_mail": "Consegna",
|
"queue_manager": "Gestore code",
|
||||||
"queue_manager": "Gestore code"
|
"delete": "Cancella tutto",
|
||||||
|
"ays": "Conferma che desideri eliminare tutti gli elementi dalla coda corrente.",
|
||||||
|
"info": "La coda di posta contiene tutte le e-mail in attesa di consegna. Se un'e-mail rimane a lungo nella coda di posta, viene automaticamente cancellata dal sistema.<br>Il messaggio di errore della rispettiva e-mail fornisce informazioni sul motivo per cui non è stato possibile consegnarla.",
|
||||||
|
"deliver_mail_legend": "Tenta di riconsegnare i messaggi selezionati.",
|
||||||
|
"hold_mail": "Blocca",
|
||||||
|
"flush": "Svuota la coda",
|
||||||
|
"deliver_mail": "Consegna",
|
||||||
|
"show_message": "Mostra messaggio",
|
||||||
|
"unhold_mail": "Sblocca",
|
||||||
|
"hold_mail_legend": "Blocca le mail selezionate. (Previene ulteriori tentativi di consegna)",
|
||||||
|
"legend": "Funzioni delle azioni della coda di posta:"
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
"help": "Mostra/Nascondi pannello di aiuto",
|
"help": "Mostra/Nascondi pannello di aiuto",
|
||||||
@@ -979,7 +1017,10 @@
|
|||||||
"verified_totp_login": "Verified TOTP login",
|
"verified_totp_login": "Verified TOTP login",
|
||||||
"verified_webauthn_login": "Verified WebAuthn login",
|
"verified_webauthn_login": "Verified WebAuthn login",
|
||||||
"verified_yotp_login": "Verified Yubico OTP login",
|
"verified_yotp_login": "Verified Yubico OTP login",
|
||||||
"domain_add_dkim_available": "Esisteva già una chiave DKIM"
|
"domain_add_dkim_available": "Esisteva già una chiave DKIM",
|
||||||
|
"template_added": "Aggiunto template %s",
|
||||||
|
"template_modified": "Le modifiche al template %s sono state salvate",
|
||||||
|
"template_removed": "Il template con ID %s è stato cancellato"
|
||||||
},
|
},
|
||||||
"tfa": {
|
"tfa": {
|
||||||
"api_register": "%s usa le API Yubico Cloud. Richiedi una chiave API <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">qui</a>",
|
"api_register": "%s usa le API Yubico Cloud. Richiedi una chiave API <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">qui</a>",
|
||||||
@@ -1143,7 +1184,7 @@
|
|||||||
"tls_enforce_in": "Imponi TLS in ingresso",
|
"tls_enforce_in": "Imponi TLS in ingresso",
|
||||||
"tls_enforce_out": "Imponi TLS in uscita",
|
"tls_enforce_out": "Imponi TLS in uscita",
|
||||||
"tls_policy": "Politica di crittografia",
|
"tls_policy": "Politica di crittografia",
|
||||||
"tls_policy_warning": "<strong>Attenzione:</strong> If you decide to enforce encrypted mail transfer, you may lose emails.<br />Messages to not satisfy the policy will be bounced with a hard fail by the mail system.<br />This option applies to your primary email address (login name), all addresses derived from alias domains as well as alias addresses <b>with only this single mailbox</b> as target.",
|
"tls_policy_warning": "<strong>Attenzione:</strong> Se decidi di applicare il trasferimento di posta crittografato, potresti perdere le email.<br />I messaggi che non soddisfano la politica verranno respinti con un hard fail dal sistema di posta.<br />This option applies to your primary email address (login name), all addresses derived from alias domains as well as alias addresses <b>with only this single mailbox</b> as target.",
|
||||||
"user_settings": "Impostazioni utente",
|
"user_settings": "Impostazioni utente",
|
||||||
"username": "Nome utente",
|
"username": "Nome utente",
|
||||||
"verify": "Verifica",
|
"verify": "Verifica",
|
||||||
@@ -1167,7 +1208,8 @@
|
|||||||
"syncjob_EXIT_CONNECTION_FAILURE_HOST1": "Impossibile connettersi al server remoto",
|
"syncjob_EXIT_CONNECTION_FAILURE_HOST1": "Impossibile connettersi al server remoto",
|
||||||
"syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Nome utente o password errati",
|
"syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Nome utente o password errati",
|
||||||
"with_app_password": "con password dell'app",
|
"with_app_password": "con password dell'app",
|
||||||
"direct_protocol_access": "Questo utente della mailbox ha <b>accesso diretto ed esterno</b> ai seguenti protocolli e applicazioni. Questa impostazione è controllata dal tuo amministratore. Le password delle applicazioni possono essere create per garantire l'accesso ai singoli protocolli e applicazioni.<br>Il pulsante \"Accedi alla webmail\" fornisce un singolo accesso a SOGo ed è sempre disponibile."
|
"direct_protocol_access": "Questo utente della mailbox ha <b>accesso diretto ed esterno</b> ai seguenti protocolli e applicazioni. Questa impostazione è controllata dal tuo amministratore. Le password delle applicazioni possono essere create per garantire l'accesso ai singoli protocolli e applicazioni.<br>Il pulsante \"Accedi alla webmail\" fornisce un singolo accesso a SOGo ed è sempre disponibile.",
|
||||||
|
"pushover_sound": "Suono"
|
||||||
},
|
},
|
||||||
"warning": {
|
"warning": {
|
||||||
"cannot_delete_self": "Cannot delete logged in user",
|
"cannot_delete_self": "Cannot delete logged in user",
|
||||||
@@ -1188,5 +1230,29 @@
|
|||||||
"second": "messaggi / secondo",
|
"second": "messaggi / secondo",
|
||||||
"hour": "messaggi / ora",
|
"hour": "messaggi / ora",
|
||||||
"day": "messaggi / giorno"
|
"day": "messaggi / giorno"
|
||||||
|
},
|
||||||
|
"datatables": {
|
||||||
|
"infoFiltered": "(filtrato da _MAX_ voci totali)",
|
||||||
|
"collapse_all": "Comprimi tutto",
|
||||||
|
"emptyTable": "Nessun dato disponibile nella tabella",
|
||||||
|
"expand_all": "Espandi tutto",
|
||||||
|
"info": "Visualizzazione da _START_ a _END_ di _TOTAL_ voci",
|
||||||
|
"infoEmpty": "Visualizzazione da 0 a 0 di 0 voci",
|
||||||
|
"thousands": ".",
|
||||||
|
"loadingRecords": "Caricamento...",
|
||||||
|
"processing": "Attendere prego...",
|
||||||
|
"search": "Ricerca:",
|
||||||
|
"zeroRecords": "Nessuna corrispondenza trovata",
|
||||||
|
"paginate": {
|
||||||
|
"first": "Prima",
|
||||||
|
"last": "Ultima",
|
||||||
|
"next": "Prossima",
|
||||||
|
"previous": "Precedente"
|
||||||
|
},
|
||||||
|
"lengthMenu": "Mostra _MENU_ voci",
|
||||||
|
"aria": {
|
||||||
|
"sortAscending": ": attivare l'ordinamento crescente delle colonne",
|
||||||
|
"sortDescending": ": attivare l'ordinamento decrescente delle colonne"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -777,7 +777,6 @@
|
|||||||
"toggle_all": "선택 반전"
|
"toggle_all": "선택 반전"
|
||||||
},
|
},
|
||||||
"queue": {
|
"queue": {
|
||||||
"queue_deliver_mail": "Deliver",
|
|
||||||
"queue_manager": "대기열 관리자"
|
"queue_manager": "대기열 관리자"
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
|
@@ -3,7 +3,8 @@
|
|||||||
"bcc_maps": "BCC kartes",
|
"bcc_maps": "BCC kartes",
|
||||||
"filters": "Filtri",
|
"filters": "Filtri",
|
||||||
"recipient_maps": "Saņēmēja kartes",
|
"recipient_maps": "Saņēmēja kartes",
|
||||||
"syncjobs": "Sinhronizācijas uzdevumi"
|
"syncjobs": "Sinhronizācijas uzdevumi",
|
||||||
|
"spam_score": "Mēstules novērtējums"
|
||||||
},
|
},
|
||||||
"add": {
|
"add": {
|
||||||
"activate_filter_warn": "Visi pārējie filtri tiks deaktivizēti, kad aktīvs ir atzīmēts.",
|
"activate_filter_warn": "Visi pārējie filtri tiks deaktivizēti, kad aktīvs ir atzīmēts.",
|
||||||
@@ -104,10 +105,10 @@
|
|||||||
"host": "Hosts",
|
"host": "Hosts",
|
||||||
"import": "Importēt",
|
"import": "Importēt",
|
||||||
"import_private_key": "Importēt privātu atslēgu",
|
"import_private_key": "Importēt privātu atslēgu",
|
||||||
"in_use_by": "Tiek lietots ar",
|
"in_use_by": "Izmanto",
|
||||||
"inactive": "Neaktīvs",
|
"inactive": "Neaktīvs",
|
||||||
"link": "Saite",
|
"link": "Saite",
|
||||||
"loading": "Lūdzu uzgaidiet...",
|
"loading": "Lūgums uzgaidīt...",
|
||||||
"logo_info": "Jūsu attēls augšējā navigācijas joslā tiks palielināts līdz 40 pikseļiem un maks. sākumlapas platums par 250 pikseļi. Ir ļoti ieteicama pielāgojama grafikaYour image will be scaled to a height of 40px for the top navigation bar and a max. width of 250px for the start page. Ir ļoti ieteicama pielāgojamā grafika",
|
"logo_info": "Jūsu attēls augšējā navigācijas joslā tiks palielināts līdz 40 pikseļiem un maks. sākumlapas platums par 250 pikseļi. Ir ļoti ieteicama pielāgojama grafikaYour image will be scaled to a height of 40px for the top navigation bar and a max. width of 250px for the start page. Ir ļoti ieteicama pielāgojamā grafika",
|
||||||
"main_name": "\"mailcow UI\" nosaukums",
|
"main_name": "\"mailcow UI\" nosaukums",
|
||||||
"merged_vars_hint": "Pelēkās rindas tika apvienotas <code>vars.(local.)inc.php</code> un nevar tikt modificētas.",
|
"merged_vars_hint": "Pelēkās rindas tika apvienotas <code>vars.(local.)inc.php</code> un nevar tikt modificētas.",
|
||||||
@@ -144,7 +145,10 @@
|
|||||||
"ui_texts": "UI etiķetes un teksti",
|
"ui_texts": "UI etiķetes un teksti",
|
||||||
"unchanged_if_empty": "Ja nav veiktas izmaiņas, atstājiet tukšu",
|
"unchanged_if_empty": "Ja nav veiktas izmaiņas, atstājiet tukšu",
|
||||||
"upload": "Augšupielādēt",
|
"upload": "Augšupielādēt",
|
||||||
"username": "Lietotājvārds"
|
"username": "Lietotājvārds",
|
||||||
|
"generate": "izveidot",
|
||||||
|
"message": "Ziņojums",
|
||||||
|
"last_applied": "Pēdējoreiz pielietots"
|
||||||
},
|
},
|
||||||
"danger": {
|
"danger": {
|
||||||
"access_denied": "Piekļuve liegta, vai nepareizi dati",
|
"access_denied": "Piekļuve liegta, vai nepareizi dati",
|
||||||
@@ -170,7 +174,7 @@
|
|||||||
"is_alias": "%s jau ir zināms alias",
|
"is_alias": "%s jau ir zināms alias",
|
||||||
"is_alias_or_mailbox": "%s jau ir zināms alias, pastkastes vai alias addrese izvērsta no alias domēna.",
|
"is_alias_or_mailbox": "%s jau ir zināms alias, pastkastes vai alias addrese izvērsta no alias domēna.",
|
||||||
"is_spam_alias": "%s ir jau zināms spam alias",
|
"is_spam_alias": "%s ir jau zināms spam alias",
|
||||||
"last_key": "Pēdējā atslēga nevar būt dzēsta",
|
"last_key": "Pēdējo atslēgu nevar izdzēst, tā vietā jāatspējo divpakāpju pārbaude.",
|
||||||
"login_failed": "Ielogošanās neveiksmīga",
|
"login_failed": "Ielogošanās neveiksmīga",
|
||||||
"mailbox_invalid": "Pastkastes vārds ir nederīgs",
|
"mailbox_invalid": "Pastkastes vārds ir nederīgs",
|
||||||
"mailbox_quota_exceeded": "Kvota pārsniedz domēna limitu (max. %d MiB)",
|
"mailbox_quota_exceeded": "Kvota pārsniedz domēna limitu (max. %d MiB)",
|
||||||
@@ -262,7 +266,8 @@
|
|||||||
"title": "Labot priekšmetu",
|
"title": "Labot priekšmetu",
|
||||||
"unchanged_if_empty": "Ja neizmainīts atstājiet tukšu",
|
"unchanged_if_empty": "Ja neizmainīts atstājiet tukšu",
|
||||||
"username": "Lietotājvārds",
|
"username": "Lietotājvārds",
|
||||||
"validate_save": "Apstiprināt un saglabāt"
|
"validate_save": "Apstiprināt un saglabāt",
|
||||||
|
"last_modified": "Pēdējoreiz mainīts"
|
||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"cancel": "Atcelt",
|
"cancel": "Atcelt",
|
||||||
@@ -314,21 +319,21 @@
|
|||||||
"bcc_destinations": "BCC galamērķi/s",
|
"bcc_destinations": "BCC galamērķi/s",
|
||||||
"bcc_info": "BCC kartes tiek izmantotas, lai klusu pārsūtītu visu ziņojumu kopijas uz citu adresi. Saņēmēja kartes tipa ieraksts tiek izmantots, kad vietējais galamērķis darbojas kā pasta adresāts. Sūtītāja kartes atbilst vienam un tam pašam principam. <br/>\r\n Vietējais galamērķis netiks informēts par piegādes neveiksmi. ",
|
"bcc_info": "BCC kartes tiek izmantotas, lai klusu pārsūtītu visu ziņojumu kopijas uz citu adresi. Saņēmēja kartes tipa ieraksts tiek izmantots, kad vietējais galamērķis darbojas kā pasta adresāts. Sūtītāja kartes atbilst vienam un tam pašam principam. <br/>\r\n Vietējais galamērķis netiks informēts par piegādes neveiksmi. ",
|
||||||
"bcc_local_dest": "Vietējais galamērķis",
|
"bcc_local_dest": "Vietējais galamērķis",
|
||||||
"bcc_map_type": "BCC tips",
|
"bcc_map_type": "BCC veids",
|
||||||
"bcc_maps": "BCC kartes",
|
"bcc_maps": "BCC kartes",
|
||||||
"bcc_rcpt_map": "saņēmēja karte",
|
"bcc_rcpt_map": "saņēmēja karte",
|
||||||
"bcc_sender_map": "Sūtītāja karte",
|
"bcc_sender_map": "Sūtītāja karte",
|
||||||
"bcc_to_rcpt": "Pārslēdzieties uz adresāta kartes tipu",
|
"bcc_to_rcpt": "Pārslēdzieties uz adresāta kartes tipu",
|
||||||
"bcc_to_sender": "Pārslēgties uz sūtītāja kartes tipu",
|
"bcc_to_sender": "Pārslēgties uz sūtītāja kartes tipu",
|
||||||
"bcc_type": "BCC tips",
|
"bcc_type": "BCC tips",
|
||||||
"deactivate": "Deaktivizēt",
|
"deactivate": "Deaktivēt",
|
||||||
"description": "Apraksts",
|
"description": "Apraksts",
|
||||||
"dkim_key_length": "DKIM atslēgas garums (bits)",
|
"dkim_key_length": "DKIM atslēgas garums (bits)",
|
||||||
"domain": "Domēns",
|
"domain": "Domēns",
|
||||||
"domain_admins": "Domēna administratori",
|
"domain_admins": "Domēna administratori",
|
||||||
"domain_aliases": "Domēna aliases",
|
"domain_aliases": "Domēna aliases",
|
||||||
"domain_quota": "Kvota",
|
"domain_quota": "Kvota",
|
||||||
"domain_quota_total": "Kopējā domēna kvota",
|
"domain_quota_total": "Kopējais domēna ierobežojums",
|
||||||
"domains": "Domēns",
|
"domains": "Domēns",
|
||||||
"edit": "Labot",
|
"edit": "Labot",
|
||||||
"empty": "Nav rezultātu",
|
"empty": "Nav rezultātu",
|
||||||
@@ -341,7 +346,7 @@
|
|||||||
"inactive": "Neaktīvs",
|
"inactive": "Neaktīvs",
|
||||||
"kind": "Veids",
|
"kind": "Veids",
|
||||||
"last_run": "Pēdējā norise",
|
"last_run": "Pēdējā norise",
|
||||||
"last_run_reset": "Nākamais grafiks",
|
"last_run_reset": "Ievietot sarakstā kā nākamo",
|
||||||
"mailbox_quota": "Maks. pastkastes izmērs",
|
"mailbox_quota": "Maks. pastkastes izmērs",
|
||||||
"mailboxes": "Pastkaste",
|
"mailboxes": "Pastkaste",
|
||||||
"max_aliases": "Maks. iespejamās aliases",
|
"max_aliases": "Maks. iespejamās aliases",
|
||||||
@@ -374,7 +379,13 @@
|
|||||||
"tls_enforce_out": "Piespiest TLS izejošajiem",
|
"tls_enforce_out": "Piespiest TLS izejošajiem",
|
||||||
"toggle_all": "Pārslēgt visu",
|
"toggle_all": "Pārslēgt visu",
|
||||||
"username": "Lietotājvārds",
|
"username": "Lietotājvārds",
|
||||||
"waiting": "Gaidīšana"
|
"waiting": "Gaidīšana",
|
||||||
|
"last_modified": "Pēdējoreiz mainīts",
|
||||||
|
"booking_0_short": "Vienmēŗ bezmaksas",
|
||||||
|
"daily": "Ik dienu",
|
||||||
|
"hourly": "Ik stundu",
|
||||||
|
"last_mail_login": "Pēdējā pieteikšanās pastkastē",
|
||||||
|
"mailbox": "Pastkaste"
|
||||||
},
|
},
|
||||||
"quarantine": {
|
"quarantine": {
|
||||||
"action": "Darbības",
|
"action": "Darbības",
|
||||||
@@ -400,7 +411,6 @@
|
|||||||
"toggle_all": "Pārslēgt visu"
|
"toggle_all": "Pārslēgt visu"
|
||||||
},
|
},
|
||||||
"queue": {
|
"queue": {
|
||||||
"queue_command_success": "Queue command completed successfully",
|
|
||||||
"queue_manager": "Queue Manager"
|
"queue_manager": "Queue Manager"
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
@@ -548,5 +558,14 @@
|
|||||||
"waiting": "Waiting",
|
"waiting": "Waiting",
|
||||||
"week": "Nedēļa",
|
"week": "Nedēļa",
|
||||||
"weeks": "Nedēļas"
|
"weeks": "Nedēļas"
|
||||||
|
},
|
||||||
|
"datatables": {
|
||||||
|
"paginate": {
|
||||||
|
"first": "Pirmā",
|
||||||
|
"last": "Pēdējā"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
"last_modified": "Pēdējoreiz mainīts"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -168,10 +168,12 @@
|
|||||||
"empty": "Geen resultaten",
|
"empty": "Geen resultaten",
|
||||||
"excludes": "Exclusief",
|
"excludes": "Exclusief",
|
||||||
"f2b_ban_time": "Verbanningstijd (s)",
|
"f2b_ban_time": "Verbanningstijd (s)",
|
||||||
|
"f2b_ban_time_increment": "Verbanningstijd wordt verhoogd met elk verbanning",
|
||||||
"f2b_blacklist": "Netwerken/hosts op de blacklist",
|
"f2b_blacklist": "Netwerken/hosts op de blacklist",
|
||||||
"f2b_filter": "Regex-filters",
|
"f2b_filter": "Regex-filters",
|
||||||
"f2b_list_info": "Een host of netwerk op de blacklist staat altijd boven eenzelfde op de whitelist. <b>Het doorvoeren van wijzigingen kan enkele seconden in beslag nemen.</b>",
|
"f2b_list_info": "Een host of netwerk op de blacklist staat altijd boven eenzelfde op de whitelist. <b>Het doorvoeren van wijzigingen kan enkele seconden in beslag nemen.</b>",
|
||||||
"f2b_max_attempts": "Maximaal aantal pogingen",
|
"f2b_max_attempts": "Maximaal aantal pogingen",
|
||||||
|
"f2b_max_ban_time": "Maximaal verbanningstijd (s)",
|
||||||
"f2b_netban_ipv4": "Voer de IPv4-subnetgrootte in waar de verbanning van kracht moet zijn (8-32)",
|
"f2b_netban_ipv4": "Voer de IPv4-subnetgrootte in waar de verbanning van kracht moet zijn (8-32)",
|
||||||
"f2b_netban_ipv6": "Voer de IPv6-subnetgrootte in waar de verbanning van kracht moet zijn (8-128)",
|
"f2b_netban_ipv6": "Voer de IPv6-subnetgrootte in waar de verbanning van kracht moet zijn (8-128)",
|
||||||
"f2b_parameters": "Fail2ban",
|
"f2b_parameters": "Fail2ban",
|
||||||
@@ -598,7 +600,7 @@
|
|||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "Aanmelding vertraagd met %s seconden.",
|
"delayed": "Aanmelding vertraagd met %s seconden.",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "Aanmelden",
|
"login": "Aanmelden",
|
||||||
"mobileconfig_info": "Log in als mailboxgebruiker om het Apple-verbindingsprofiel te downloaden.",
|
"mobileconfig_info": "Log in als mailboxgebruiker om het Apple-verbindingsprofiel te downloaden.",
|
||||||
"other_logins": "Meld aan met key",
|
"other_logins": "Meld aan met key",
|
||||||
@@ -815,7 +817,6 @@
|
|||||||
"toggle_all": "Selecteer alles"
|
"toggle_all": "Selecteer alles"
|
||||||
},
|
},
|
||||||
"queue": {
|
"queue": {
|
||||||
"queue_deliver_mail": "Lever af",
|
|
||||||
"queue_manager": "Queue manager"
|
"queue_manager": "Queue manager"
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"acl": {
|
"acl": {
|
||||||
"sogo_profile_reset": "Usuń profil SOGo (webmail)",
|
"sogo_profile_reset": "Usuń profil SOGo (webmail)",
|
||||||
"syncjobs": "Polecenie synchronizacji"
|
"syncjobs": "Polecenie synchronizacji",
|
||||||
|
"alias_domains": "Dodaj aliasy domen"
|
||||||
},
|
},
|
||||||
"add": {
|
"add": {
|
||||||
"active": "Aktywny",
|
"active": "Aktywny",
|
||||||
@@ -285,7 +286,6 @@
|
|||||||
"toggle_all": "Zaznacz wszystkie"
|
"toggle_all": "Zaznacz wszystkie"
|
||||||
},
|
},
|
||||||
"queue": {
|
"queue": {
|
||||||
"queue_command_success": "Queue command completed successfully",
|
|
||||||
"queue_manager": "Queue Manager"
|
"queue_manager": "Queue Manager"
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
|
@@ -193,7 +193,6 @@
|
|||||||
"remove": "Remover"
|
"remove": "Remover"
|
||||||
},
|
},
|
||||||
"queue": {
|
"queue": {
|
||||||
"queue_command_success": "Queue command completed successfully",
|
|
||||||
"queue_manager": "Queue Manager"
|
"queue_manager": "Queue Manager"
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
|
@@ -539,7 +539,7 @@
|
|||||||
"inactive": "Inactiv",
|
"inactive": "Inactiv",
|
||||||
"kind": "Fel",
|
"kind": "Fel",
|
||||||
"last_modified": "Ultima modificare",
|
"last_modified": "Ultima modificare",
|
||||||
"lookup_mx": "Destinația este o expresie regulată care potrivită cu numele MX (<code>.*google\\.com</code> pentru a direcționa toate e-mailurile vizate către un MX care se termină în google.com peste acest hop)",
|
"lookup_mx": "Destinația este o expresie regulată care potrivită cu numele MX (<code>.*\\.google\\.com</code> pentru a direcționa toate e-mailurile vizate către un MX care se termină în google.com peste acest hop)",
|
||||||
"mailbox": "Editează căsuța poștală",
|
"mailbox": "Editează căsuța poștală",
|
||||||
"mailbox_quota_def": "Cota implicită a căsuței poștale",
|
"mailbox_quota_def": "Cota implicită a căsuței poștale",
|
||||||
"mailbox_relayhost_info": "Aplicat numai căsuței poștale și aliasurilor directe, suprascrie un transport dependent de domeniu.",
|
"mailbox_relayhost_info": "Aplicat numai căsuței poștale și aliasurilor directe, suprascrie un transport dependent de domeniu.",
|
||||||
@@ -656,7 +656,7 @@
|
|||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "Conectarea a fost întârziată cu %s secunde.",
|
"delayed": "Conectarea a fost întârziată cu %s secunde.",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "Autentificare",
|
"login": "Autentificare",
|
||||||
"mobileconfig_info": "Autentificați-vă cu adresa de email pentru a descărca profilul de conexiune Apple.",
|
"mobileconfig_info": "Autentificați-vă cu adresa de email pentru a descărca profilul de conexiune Apple.",
|
||||||
"other_logins": "Autentificare cu cheie",
|
"other_logins": "Autentificare cu cheie",
|
||||||
@@ -895,7 +895,6 @@
|
|||||||
"toggle_all": "Comută toate"
|
"toggle_all": "Comută toate"
|
||||||
},
|
},
|
||||||
"queue": {
|
"queue": {
|
||||||
"queue_deliver_mail": "Livrează",
|
|
||||||
"queue_manager": "Manager de coadă"
|
"queue_manager": "Manager de coadă"
|
||||||
},
|
},
|
||||||
"ratelimit": {
|
"ratelimit": {
|
||||||
|
@@ -37,7 +37,7 @@
|
|||||||
"add_domain_only": "Только добавить домен",
|
"add_domain_only": "Только добавить домен",
|
||||||
"add_domain_restart": "Добавить домен и перезапустить SOGo",
|
"add_domain_restart": "Добавить домен и перезапустить SOGo",
|
||||||
"alias_address": "Псевдоним/ы",
|
"alias_address": "Псевдоним/ы",
|
||||||
"alias_address_info": "<small>Укажите почтовые адреса разделенные запятыми или, если хотите пересылать все сообщения для домена владельцам псевдонима то: <code>@example.com</code>. <b>Только домены mailcow разрешены</b>.</small>",
|
"alias_address_info": "<small>Адрес(а) электронной почты (через запятую) или @example.com (для перехвата всех писем для домена). <b>только домены mailcow</b>.</small>",
|
||||||
"alias_domain": "Псевдоним домена",
|
"alias_domain": "Псевдоним домена",
|
||||||
"alias_domain_info": "<small>Действительные имена доменов, раздёленные запятыми.</small>",
|
"alias_domain_info": "<small>Действительные имена доменов, раздёленные запятыми.</small>",
|
||||||
"app_name": "Название приложения",
|
"app_name": "Название приложения",
|
||||||
@@ -335,7 +335,10 @@
|
|||||||
"username": "Имя пользователя",
|
"username": "Имя пользователя",
|
||||||
"validate_license_now": "Получить лицензию на основе GUID с сервера лицензий",
|
"validate_license_now": "Получить лицензию на основе GUID с сервера лицензий",
|
||||||
"verify": "Проверить",
|
"verify": "Проверить",
|
||||||
"yes": "✓"
|
"yes": "✓",
|
||||||
|
"queue_unban": "разблокировать",
|
||||||
|
"f2b_ban_time_increment": "Время бана увеличивается с каждым баном",
|
||||||
|
"f2b_max_ban_time": "Максимальное время блокировки"
|
||||||
},
|
},
|
||||||
"danger": {
|
"danger": {
|
||||||
"access_denied": "Доступ запрещён, или указаны неверные данные",
|
"access_denied": "Доступ запрещён, или указаны неверные данные",
|
||||||
@@ -654,7 +657,7 @@
|
|||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "Вход был отложен на %s секунд.",
|
"delayed": "Вход был отложен на %s секунд.",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "Войти",
|
"login": "Войти",
|
||||||
"mobileconfig_info": "Пожалуйста, войдите в систему как пользователь почтового аккаунта для загрузки профиля подключения Apple.",
|
"mobileconfig_info": "Пожалуйста, войдите в систему как пользователь почтового аккаунта для загрузки профиля подключения Apple.",
|
||||||
"other_logins": "Вход с помощью ключа",
|
"other_logins": "Вход с помощью ключа",
|
||||||
@@ -893,7 +896,6 @@
|
|||||||
"type": "Тип"
|
"type": "Тип"
|
||||||
},
|
},
|
||||||
"queue": {
|
"queue": {
|
||||||
"queue_deliver_mail": "Доставить",
|
|
||||||
"queue_manager": "Очередь на отправку"
|
"queue_manager": "Очередь на отправку"
|
||||||
},
|
},
|
||||||
"ratelimit": {
|
"ratelimit": {
|
||||||
|
@@ -106,7 +106,8 @@
|
|||||||
"username": "Používateľské meno",
|
"username": "Používateľské meno",
|
||||||
"validate": "Overiť",
|
"validate": "Overiť",
|
||||||
"validation_success": "Úspešne overené",
|
"validation_success": "Úspešne overené",
|
||||||
"app_passwd_protocols": "Povolené protokoly k heslu aplikácie"
|
"app_passwd_protocols": "Povolené protokoly k heslu aplikácie",
|
||||||
|
"tags": "Štítky"
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"access": "Prístup",
|
"access": "Prístup",
|
||||||
@@ -212,7 +213,7 @@
|
|||||||
"loading": "Čakajte prosím ...",
|
"loading": "Čakajte prosím ...",
|
||||||
"login_time": "Čas prihlásenia",
|
"login_time": "Čas prihlásenia",
|
||||||
"logo_info": "Váš obrázok bude upravený na výšku 40px pre vrchný navigačný riadok a na maximálnu šírku 250px pre úvodnú stránku. Odporúča sa škálovateľná grafika.",
|
"logo_info": "Váš obrázok bude upravený na výšku 40px pre vrchný navigačný riadok a na maximálnu šírku 250px pre úvodnú stránku. Odporúča sa škálovateľná grafika.",
|
||||||
"lookup_mx": "Cieľ je regulárny výraz ktorý sa porovnáva s MX záznamom (<code>.*google\\.com</code> smeruje všetku poštu určenú pre MX ktoré sú cieľom pre google.com cez tento skok)",
|
"lookup_mx": "Cieľ je regulárny výraz ktorý sa porovnáva s MX záznamom (<code>.*\\.google\\.com</code> smeruje všetku poštu určenú pre MX ktoré sú cieľom pre google.com cez tento skok)",
|
||||||
"main_name": "\"mailcow UI\" názov",
|
"main_name": "\"mailcow UI\" názov",
|
||||||
"merged_vars_hint": "Sivé riadky boli načítané z <code>vars.(local.)inc.php</code> a nemôžu byť modifikované cez UI.",
|
"merged_vars_hint": "Sivé riadky boli načítané z <code>vars.(local.)inc.php</code> a nemôžu byť modifikované cez UI.",
|
||||||
"message": "Správa",
|
"message": "Správa",
|
||||||
@@ -538,7 +539,7 @@
|
|||||||
"inactive": "Neaktívny",
|
"inactive": "Neaktívny",
|
||||||
"kind": "Druh",
|
"kind": "Druh",
|
||||||
"last_modified": "Naposledy upravené",
|
"last_modified": "Naposledy upravené",
|
||||||
"lookup_mx": "Cieľ je regulárny výraz ktorý sa zhoduje s MX záznamom (<code>.*google\\.com</code> smeruje všetku poštu na MX ktoré sú cieľom pre google.com cez tento skok)",
|
"lookup_mx": "Cieľ je regulárny výraz ktorý sa zhoduje s MX záznamom (<code>.*\\.google\\.com</code> smeruje všetku poštu na MX ktoré sú cieľom pre google.com cez tento skok)",
|
||||||
"mailbox": "Upraviť mailovú schránku",
|
"mailbox": "Upraviť mailovú schránku",
|
||||||
"mailbox_quota_def": "Predvolená veľkosť mailovej schránky",
|
"mailbox_quota_def": "Predvolená veľkosť mailovej schránky",
|
||||||
"mailbox_relayhost_info": "Aplikované len na používateľské schránky a priame aliasy, prepisuje doménového preposielateľa.",
|
"mailbox_relayhost_info": "Aplikované len na používateľské schránky a priame aliasy, prepisuje doménového preposielateľa.",
|
||||||
@@ -656,7 +657,7 @@
|
|||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "Prihlásenie bolo oneskorené o %s sekúnd.",
|
"delayed": "Prihlásenie bolo oneskorené o %s sekúnd.",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "Prihlásenie",
|
"login": "Prihlásenie",
|
||||||
"mobileconfig_info": "Prosím, prihláste sa ako mailový používateľ pre stiahnutie požadovaného Apple profilu.",
|
"mobileconfig_info": "Prosím, prihláste sa ako mailový používateľ pre stiahnutie požadovaného Apple profilu.",
|
||||||
"other_logins": "Prihlásenie kľúčom",
|
"other_logins": "Prihlásenie kľúčom",
|
||||||
@@ -895,7 +896,6 @@
|
|||||||
"type": "Typ"
|
"type": "Typ"
|
||||||
},
|
},
|
||||||
"queue": {
|
"queue": {
|
||||||
"queue_deliver_mail": "Doručiť",
|
|
||||||
"queue_manager": "Správca fronty"
|
"queue_manager": "Správca fronty"
|
||||||
},
|
},
|
||||||
"ratelimit": {
|
"ratelimit": {
|
||||||
|
@@ -618,7 +618,7 @@
|
|||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "Av säkerhetsskäl har inloggning inaktiverats i %s sekunder.",
|
"delayed": "Av säkerhetsskäl har inloggning inaktiverats i %s sekunder.",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "Logga in",
|
"login": "Logga in",
|
||||||
"mobileconfig_info": "Logga in som en användare av brevlåda för att ladda ner den begärda Apple-anslutningsprofilen.",
|
"mobileconfig_info": "Logga in som en användare av brevlåda för att ladda ner den begärda Apple-anslutningsprofilen.",
|
||||||
"other_logins": "Loggain med nyckel",
|
"other_logins": "Loggain med nyckel",
|
||||||
|
@@ -656,7 +656,7 @@
|
|||||||
"awaiting_tfa_confirmation": "В очікуванні підтвердження TFA"
|
"awaiting_tfa_confirmation": "В очікуванні підтвердження TFA"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "Увійти",
|
"login": "Увійти",
|
||||||
"other_logins": "Вхід за допомогою ключа",
|
"other_logins": "Вхід за допомогою ключа",
|
||||||
"password": "Пароль",
|
"password": "Пароль",
|
||||||
@@ -896,7 +896,6 @@
|
|||||||
"table_size_show_n": "Відображати %s полів"
|
"table_size_show_n": "Відображати %s полів"
|
||||||
},
|
},
|
||||||
"queue": {
|
"queue": {
|
||||||
"queue_hold_mail": "Поставити на утримання",
|
|
||||||
"queue_manager": "Черга на відправлення"
|
"queue_manager": "Черга на відправлення"
|
||||||
},
|
},
|
||||||
"ratelimit": {
|
"ratelimit": {
|
||||||
|
@@ -31,7 +31,7 @@
|
|||||||
"unlimited_quota": "无限邮箱容量配额"
|
"unlimited_quota": "无限邮箱容量配额"
|
||||||
},
|
},
|
||||||
"add": {
|
"add": {
|
||||||
"activate_filter_warn": "当 \"启用\" 选项被勾选后,所有其他过滤器都会被禁用",
|
"activate_filter_warn": "当“启用”选项被勾选后,其它所有的过滤器都会被禁用。",
|
||||||
"active": "启用",
|
"active": "启用",
|
||||||
"add": "添加",
|
"add": "添加",
|
||||||
"add_domain_only": "只添加域名",
|
"add_domain_only": "只添加域名",
|
||||||
@@ -208,12 +208,12 @@
|
|||||||
"includes": "包括这些收件人",
|
"includes": "包括这些收件人",
|
||||||
"is_mx_based": "基于 MX 记录",
|
"is_mx_based": "基于 MX 记录",
|
||||||
"last_applied": "最后应用的条目",
|
"last_applied": "最后应用的条目",
|
||||||
"license_info": "你不需要获取证书便可以使用此项目,但是获取证书可以帮助此项目进一步发展。<br>在这里<a href=\"https://www.servercow.de/mailcow?lang=en#sal\" target=\"_blank\" alt=\"SAL order\">注册</a>你的 GUID或者为你的 Mailcow 安装<a href=\"https://www.servercow.de/mailcow?lang=en#support\" target=\"_blank\" alt=\"Support order\">购买</a>支持服务。",
|
"license_info": "使用并不需要许可证,但获得许可证能够帮助此项目进一步发展。<br><a href=\"https://www.servercow.de/mailcow?lang=en#sal\" target=\"_blank\" alt=\"订购 SAL\">在这里注册你的 GUID </a>或者<a href=\"https://www.servercow.de/mailcow?lang=en#support\" target=\"_blank\" alt=\"订购支持服务\">为你的 Mailcow 安装购买支持服务。</a>",
|
||||||
"link": "链接",
|
"link": "链接",
|
||||||
"loading": "请等待...",
|
"loading": "请等待...",
|
||||||
"login_time": "登录时间",
|
"login_time": "登录时间",
|
||||||
"logo_info": "你的图片将会在顶部导航栏被缩放为 40px 高,在起始页被缩放为最大 250px 高。强烈推荐使用能较好适应缩放的图片。",
|
"logo_info": "你的图片将会在顶部导航栏被缩放为 40px 高,在起始页被缩放为最大 250px 高。强烈推荐使用能较好适应缩放的图片。",
|
||||||
"lookup_mx": "应当为一个正则表达式,用于匹配 MX 记录 (例如 <code>.*google\\.com</code> 将转发所有拥有以 google.com 结尾的 MX 记录的邮件)",
|
"lookup_mx": "应当为一个正则表达式,用于匹配 MX 记录 (例如 <code>.*\\.google\\.com</code> 将转发所有拥有以 google.com 结尾的 MX 记录的邮件)",
|
||||||
"main_name": "Mailcow UI 的名称",
|
"main_name": "Mailcow UI 的名称",
|
||||||
"merged_vars_hint": "灰色行来自 <code>vars.(local.)inc.php</code> 文件并且无法修改。",
|
"merged_vars_hint": "灰色行来自 <code>vars.(local.)inc.php</code> 文件并且无法修改。",
|
||||||
"message": "消息",
|
"message": "消息",
|
||||||
@@ -257,7 +257,6 @@
|
|||||||
"quarantine_release_format_att": "附件",
|
"quarantine_release_format_att": "附件",
|
||||||
"quarantine_release_format_raw": "原件 (未修改)",
|
"quarantine_release_format_raw": "原件 (未修改)",
|
||||||
"quarantine_retention_size": "每个邮箱保留隔离项目数:<br><small>0 表示 <b>禁用</b>。</small>",
|
"quarantine_retention_size": "每个邮箱保留隔离项目数:<br><small>0 表示 <b>禁用</b>。</small>",
|
||||||
"queue_manager": "队列管理",
|
|
||||||
"quota_notification_html": "通知邮件模板:<br><small>留空则恢复默认模板。</small>",
|
"quota_notification_html": "通知邮件模板:<br><small>留空则恢复默认模板。</small>",
|
||||||
"quota_notification_sender": "通知邮件发件人",
|
"quota_notification_sender": "通知邮件发件人",
|
||||||
"quota_notification_subject": "通知邮件主题",
|
"quota_notification_subject": "通知邮件主题",
|
||||||
@@ -336,7 +335,8 @@
|
|||||||
"username": "用户名",
|
"username": "用户名",
|
||||||
"validate_license_now": "通过证书服务器验证 GUID",
|
"validate_license_now": "通过证书服务器验证 GUID",
|
||||||
"verify": "验证",
|
"verify": "验证",
|
||||||
"yes": "✓"
|
"yes": "✓",
|
||||||
|
"options": "选项"
|
||||||
},
|
},
|
||||||
"danger": {
|
"danger": {
|
||||||
"access_denied": "访问被拒绝或者表单数据无效",
|
"access_denied": "访问被拒绝或者表单数据无效",
|
||||||
@@ -403,7 +403,7 @@
|
|||||||
"max_alias_exceeded": "超出最大别名数",
|
"max_alias_exceeded": "超出最大别名数",
|
||||||
"max_mailbox_exceeded": "超出最大邮箱数 (%d / %d)",
|
"max_mailbox_exceeded": "超出最大邮箱数 (%d / %d)",
|
||||||
"max_quota_in_use": "邮箱数必须大于等于 %d MiB",
|
"max_quota_in_use": "邮箱数必须大于等于 %d MiB",
|
||||||
"maxquota_empty": "每个邮箱的最大配额必须不为0",
|
"maxquota_empty": "每个邮箱的最大配额必须不为 0 。",
|
||||||
"mysql_error": "MySQL 错误: %s",
|
"mysql_error": "MySQL 错误: %s",
|
||||||
"network_host_invalid": "网络或主机无效: %s",
|
"network_host_invalid": "网络或主机无效: %s",
|
||||||
"next_hop_interferes": "%s 与下一跳 %s 冲突",
|
"next_hop_interferes": "%s 与下一跳 %s 冲突",
|
||||||
@@ -455,7 +455,8 @@
|
|||||||
"username_invalid": "用户名 %s 无法使用",
|
"username_invalid": "用户名 %s 无法使用",
|
||||||
"validity_missing": "请设置有效期",
|
"validity_missing": "请设置有效期",
|
||||||
"value_missing": "请填入所有值",
|
"value_missing": "请填入所有值",
|
||||||
"yotp_verification_failed": "Yubico OTP 认证失败: %s"
|
"yotp_verification_failed": "Yubico OTP 认证失败: %s",
|
||||||
|
"template_exists": "模板 %s 已存在"
|
||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"chart_this_server": "图表 (此服务器)",
|
"chart_this_server": "图表 (此服务器)",
|
||||||
@@ -474,7 +475,7 @@
|
|||||||
"restart_container": "重启",
|
"restart_container": "重启",
|
||||||
"service": "服务",
|
"service": "服务",
|
||||||
"size": "大小",
|
"size": "大小",
|
||||||
"solr_dead": "Solr 在启动中、已关闭或已停止",
|
"solr_dead": "Solr 在启动中、已关闭或已停止。",
|
||||||
"solr_status": "Solr 状态",
|
"solr_status": "Solr 状态",
|
||||||
"started_at": "开始于",
|
"started_at": "开始于",
|
||||||
"started_on": "启动于",
|
"started_on": "启动于",
|
||||||
@@ -482,10 +483,14 @@
|
|||||||
"success": "成功",
|
"success": "成功",
|
||||||
"system_containers": "系统和容器",
|
"system_containers": "系统和容器",
|
||||||
"uptime": "运行时间",
|
"uptime": "运行时间",
|
||||||
"username": "用户名"
|
"username": "用户名",
|
||||||
|
"container_disabled": "容器已被停止或禁用",
|
||||||
|
"container_running": "运行中",
|
||||||
|
"cores": "核心数",
|
||||||
|
"memory": "内存"
|
||||||
},
|
},
|
||||||
"diagnostics": {
|
"diagnostics": {
|
||||||
"cname_from_a": "虽然此记录为 A/AAAA 类型,但只要此记录指向了正确的资源便可以被支持",
|
"cname_from_a": "来自 A/AAAA 记录的值。但只要记录指向正确的资源即可。",
|
||||||
"dns_records": "DNS 记录",
|
"dns_records": "DNS 记录",
|
||||||
"dns_records_24hours": "请注意 DNS 记录的更改可能需要24小时才可以使此页面的当前状态显示正确。此页面为你提供了一个可以便捷查询如何配置 DNS 记录以及检查你的 DNS 记录是否正确的方式。",
|
"dns_records_24hours": "请注意 DNS 记录的更改可能需要24小时才可以使此页面的当前状态显示正确。此页面为你提供了一个可以便捷查询如何配置 DNS 记录以及检查你的 DNS 记录是否正确的方式。",
|
||||||
"dns_records_data": "正确数据",
|
"dns_records_data": "正确数据",
|
||||||
@@ -502,7 +507,7 @@
|
|||||||
"advanced_settings": "高级设置",
|
"advanced_settings": "高级设置",
|
||||||
"alias": "编辑别名",
|
"alias": "编辑别名",
|
||||||
"allow_from_smtp": "只允许这些 IP 使用 <b>SMTP</b>",
|
"allow_from_smtp": "只允许这些 IP 使用 <b>SMTP</b>",
|
||||||
"allow_from_smtp_info": "留空以允许所有发送者。<br>IPv4/IPv6地址或网络",
|
"allow_from_smtp_info": "留空以允许所有发送者。<br>IPv4/IPv6 地址和网络。",
|
||||||
"allowed_protocols": "允许的协议",
|
"allowed_protocols": "允许的协议",
|
||||||
"app_name": "应用名称",
|
"app_name": "应用名称",
|
||||||
"app_passwd": "应用密码",
|
"app_passwd": "应用密码",
|
||||||
@@ -539,7 +544,7 @@
|
|||||||
"hostname": "主机名",
|
"hostname": "主机名",
|
||||||
"inactive": "禁用",
|
"inactive": "禁用",
|
||||||
"kind": "类型",
|
"kind": "类型",
|
||||||
"lookup_mx": "应当为一个正则表达式,用于匹配 MX 记录 (例如 <code>.*google\\.com</code> 将转发所有拥有以 google.com 结尾的 MX 记录的邮件)",
|
"lookup_mx": "应当为一个正则表达式,用于匹配 MX 记录 (例如 <code>.*\\.google\\.com</code> 将转发所有拥有以 google.com 结尾的 MX 记录的邮件)",
|
||||||
"mailbox": "编辑邮箱",
|
"mailbox": "编辑邮箱",
|
||||||
"mailbox_quota_def": "邮箱默认配额",
|
"mailbox_quota_def": "邮箱默认配额",
|
||||||
"mailbox_relayhost_info": "只适用于邮箱和邮箱别名,不会覆盖域名的中继主机。",
|
"mailbox_relayhost_info": "只适用于邮箱和邮箱别名,不会覆盖域名的中继主机。",
|
||||||
@@ -630,7 +635,7 @@
|
|||||||
"delete_these_items": "请确认对以下对象 ID 的更改",
|
"delete_these_items": "请确认对以下对象 ID 的更改",
|
||||||
"hibp_check": "使用 haveibeenpwned.com 网站检查密码",
|
"hibp_check": "使用 haveibeenpwned.com 网站检查密码",
|
||||||
"hibp_nok": "匹配到密码!存在潜在的使用危险!",
|
"hibp_nok": "匹配到密码!存在潜在的使用危险!",
|
||||||
"hibp_ok": "未匹配到密码",
|
"hibp_ok": "未找到匹配的记录。",
|
||||||
"loading": "请等待...",
|
"loading": "请等待...",
|
||||||
"nothing_selected": "未选择",
|
"nothing_selected": "未选择",
|
||||||
"restart_container": "重启容器",
|
"restart_container": "重启容器",
|
||||||
@@ -656,7 +661,7 @@
|
|||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "请在 %s 秒后重新登录。",
|
"delayed": "请在 %s 秒后重新登录。",
|
||||||
"fido2_webauthn": "使用 FIDO2/WebAuthn 登录",
|
"fido2_webauthn": "使用 FIDO2/WebAuthn Login 登录",
|
||||||
"login": "登录",
|
"login": "登录",
|
||||||
"mobileconfig_info": "请使用邮箱用户登录以下载 Apple 连接描述文件。",
|
"mobileconfig_info": "请使用邮箱用户登录以下载 Apple 连接描述文件。",
|
||||||
"other_logins": "Key 登录",
|
"other_logins": "Key 登录",
|
||||||
@@ -686,7 +691,7 @@
|
|||||||
"aliases": "别名",
|
"aliases": "别名",
|
||||||
"all_domains": "全部域名",
|
"all_domains": "全部域名",
|
||||||
"allow_from_smtp": "只允许这些 IP 使用 <b>SMTP</b>",
|
"allow_from_smtp": "只允许这些 IP 使用 <b>SMTP</b>",
|
||||||
"allow_from_smtp_info": "留空以允许所有发送者,<br>IPv4/IPv6地址或网络",
|
"allow_from_smtp_info": "留空以允许所有发送者。<br>IPv4/IPv6 地址或网络。",
|
||||||
"allowed_protocols": "允许用户直接访问的协议 (不会影响应用的密码协议)",
|
"allowed_protocols": "允许用户直接访问的协议 (不会影响应用的密码协议)",
|
||||||
"backup_mx": "中继域名",
|
"backup_mx": "中继域名",
|
||||||
"bcc": "BCC",
|
"bcc": "BCC",
|
||||||
@@ -740,7 +745,7 @@
|
|||||||
"last_run_reset": "下一次运行",
|
"last_run_reset": "下一次运行",
|
||||||
"mailbox": "邮箱",
|
"mailbox": "邮箱",
|
||||||
"mailbox_defaults": "默认设置",
|
"mailbox_defaults": "默认设置",
|
||||||
"mailbox_defaults_info": "配置新邮箱的默认设置",
|
"mailbox_defaults_info": "配置新邮箱的默认设置。",
|
||||||
"mailbox_defquota": "默认邮箱大小",
|
"mailbox_defquota": "默认邮箱大小",
|
||||||
"mailbox_quota": "最大邮箱大小",
|
"mailbox_quota": "最大邮箱大小",
|
||||||
"mailboxes": "邮箱",
|
"mailboxes": "邮箱",
|
||||||
@@ -821,7 +826,12 @@
|
|||||||
"username": "用户名",
|
"username": "用户名",
|
||||||
"waiting": "等待中",
|
"waiting": "等待中",
|
||||||
"weekly": "每周",
|
"weekly": "每周",
|
||||||
"yes": "✓"
|
"yes": "✓",
|
||||||
|
"domain_templates": "域名模板",
|
||||||
|
"mailbox_templates": "邮箱模板",
|
||||||
|
"gal": "全局地址列表",
|
||||||
|
"max_aliases": "最大别名数",
|
||||||
|
"max_mailboxes": "最大可能的邮箱数"
|
||||||
},
|
},
|
||||||
"oauth2": {
|
"oauth2": {
|
||||||
"access_denied": "请作为邮箱所有者登录以使用 OAuth2 授权",
|
"access_denied": "请作为邮箱所有者登录以使用 OAuth2 授权",
|
||||||
@@ -866,7 +876,7 @@
|
|||||||
"refresh": "刷新",
|
"refresh": "刷新",
|
||||||
"rejected": "已拒绝",
|
"rejected": "已拒绝",
|
||||||
"release": "移除",
|
"release": "移除",
|
||||||
"release_body": "我们已在此消息中将你的消息作为 eml 附件文件",
|
"release_body": "我们已将你的消息作为 eml 文件附在此消息中。",
|
||||||
"release_subject": "存在潜在危险的隔离文件 %s",
|
"release_subject": "存在潜在危险的隔离文件 %s",
|
||||||
"remove": "删除",
|
"remove": "删除",
|
||||||
"rewrite_subject": "重写主题",
|
"rewrite_subject": "重写主题",
|
||||||
@@ -886,8 +896,8 @@
|
|||||||
"type": "类型"
|
"type": "类型"
|
||||||
},
|
},
|
||||||
"queue": {
|
"queue": {
|
||||||
"queue_deliver_mail": "递送",
|
"queue_manager": "队列管理器",
|
||||||
"queue_manager": "队列管理器"
|
"delete": "全部删除"
|
||||||
},
|
},
|
||||||
"ratelimit": {
|
"ratelimit": {
|
||||||
"disabled": "禁用",
|
"disabled": "禁用",
|
||||||
@@ -1181,5 +1191,18 @@
|
|||||||
"quota_exceeded_scope": "域名配额超标: 此域名下现在只能创建无限容量的邮箱。",
|
"quota_exceeded_scope": "域名配额超标: 此域名下现在只能创建无限容量的邮箱。",
|
||||||
"session_token": "表单字段无效: Token 不匹配",
|
"session_token": "表单字段无效: Token 不匹配",
|
||||||
"session_ua": "表单字段无效: User-Agent 校验错误"
|
"session_ua": "表单字段无效: User-Agent 校验错误"
|
||||||
|
},
|
||||||
|
"datatables": {
|
||||||
|
"info": "正从 _TOTAL_ 个条目中显示 _START_ 到 _END_ 条目",
|
||||||
|
"collapse_all": "全部折叠",
|
||||||
|
"expand_all": "全部展开",
|
||||||
|
"infoEmpty": "正从共 0 个条目中显示从 0 到 0 条目",
|
||||||
|
"processing": "请稍等...",
|
||||||
|
"search": "搜索:",
|
||||||
|
"paginate": {
|
||||||
|
"first": "第一页",
|
||||||
|
"last": "最后一页",
|
||||||
|
"previous": "上一页"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -213,7 +213,7 @@
|
|||||||
"loading": "請稍等...",
|
"loading": "請稍等...",
|
||||||
"login_time": "登入時間",
|
"login_time": "登入時間",
|
||||||
"logo_info": "你的起始頁面圖片會在頂部導覽列的限制下被縮放為 40px 高,以及最大 250px 高度。強烈推薦使用能較好縮放的圖片。",
|
"logo_info": "你的起始頁面圖片會在頂部導覽列的限制下被縮放為 40px 高,以及最大 250px 高度。強烈推薦使用能較好縮放的圖片。",
|
||||||
"lookup_mx": "目的地是可以用來匹配 MX 紀錄的正規表達式 (<code>.*google\\.com</code> 會將所有 MX 結尾於 google.com 的郵件轉發到此主機。)",
|
"lookup_mx": "目的地是可以用來匹配 MX 紀錄的正規表達式 (<code>.*\\.google\\.com</code> 會將所有 MX 結尾於 google.com 的郵件轉發到此主機。)",
|
||||||
"main_name": "\"mailcow UI\" 名稱",
|
"main_name": "\"mailcow UI\" 名稱",
|
||||||
"merged_vars_hint": "灰色列來自 <code>vars.(local.)inc.php</code> 並且不能修改。",
|
"merged_vars_hint": "灰色列來自 <code>vars.(local.)inc.php</code> 並且不能修改。",
|
||||||
"message": "訊息",
|
"message": "訊息",
|
||||||
@@ -257,7 +257,6 @@
|
|||||||
"quarantine_release_format_att": "如附件",
|
"quarantine_release_format_att": "如附件",
|
||||||
"quarantine_release_format_raw": "未修改的原始信件",
|
"quarantine_release_format_raw": "未修改的原始信件",
|
||||||
"quarantine_retention_size": "每個信箱的隔離保留上限:<br><small>0 表示 <b>停用</b>。</small>",
|
"quarantine_retention_size": "每個信箱的隔離保留上限:<br><small>0 表示 <b>停用</b>。</small>",
|
||||||
"queue_manager": "佇列管理",
|
|
||||||
"quota_notification_html": "通知信模版:<br><small>留空來重設為預設模版。</small>",
|
"quota_notification_html": "通知信模版:<br><small>留空來重設為預設模版。</small>",
|
||||||
"quota_notification_sender": "通知信寄件人",
|
"quota_notification_sender": "通知信寄件人",
|
||||||
"quota_notification_subject": "通知信主旨",
|
"quota_notification_subject": "通知信主旨",
|
||||||
@@ -541,7 +540,7 @@
|
|||||||
"inactive": "停用",
|
"inactive": "停用",
|
||||||
"kind": "種類",
|
"kind": "種類",
|
||||||
"last_modified": "上次修改時間",
|
"last_modified": "上次修改時間",
|
||||||
"lookup_mx": "目的地是可以用來匹配 MX 紀錄的正規表達式 (<code>.*google\\.com</code> 會將所有 MX 結尾於 google.com 的郵件轉發到此主機。)",
|
"lookup_mx": "目的地是可以用來匹配 MX 紀錄的正規表達式 (<code>.*\\.google\\.com</code> 會將所有 MX 結尾於 google.com 的郵件轉發到此主機。)",
|
||||||
"mailbox": "編輯信箱",
|
"mailbox": "編輯信箱",
|
||||||
"mailbox_quota_def": "預設信箱容量配額",
|
"mailbox_quota_def": "預設信箱容量配額",
|
||||||
"mailbox_relayhost_info": "只會套用於信箱和直接別名,不會覆寫域名中繼主機。",
|
"mailbox_relayhost_info": "只會套用於信箱和直接別名,不會覆寫域名中繼主機。",
|
||||||
@@ -656,7 +655,7 @@
|
|||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "請在 %s 秒後重新登入。",
|
"delayed": "請在 %s 秒後重新登入。",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "登入",
|
"login": "登入",
|
||||||
"mobileconfig_info": "請使用信箱使用者登入以下載 Apple 連接描述檔案。",
|
"mobileconfig_info": "請使用信箱使用者登入以下載 Apple 連接描述檔案。",
|
||||||
"other_logins": "金鑰登入",
|
"other_logins": "金鑰登入",
|
||||||
|
@@ -60,7 +60,7 @@ elseif (isset($_GET['login'])) {
|
|||||||
':remote_addr' => ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'])
|
':remote_addr' => ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'])
|
||||||
));
|
));
|
||||||
// redirect to sogo (sogo will get the correct credentials via nginx auth_request
|
// redirect to sogo (sogo will get the correct credentials via nginx auth_request
|
||||||
header("Location: /SOGo/so/${login}");
|
header("Location: /SOGo/so/{$login}");
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -66,7 +66,7 @@ var lang = {{ lang_admin|raw }};
|
|||||||
var lang_datatables = {{ lang_datatables|raw }};
|
var lang_datatables = {{ lang_datatables|raw }};
|
||||||
var admin_username = '{{ mailcow_cc_username }}';
|
var admin_username = '{{ mailcow_cc_username }}';
|
||||||
var csrf_token = '{{ csrf_token }}';
|
var csrf_token = '{{ csrf_token }}';
|
||||||
var pagination_size = '{{ pagination_size }}';
|
var pagination_size = Math.trunc('{{ pagination_size }}');
|
||||||
var log_pagination_size = '{{ log_pagination_size }}';
|
var log_pagination_size = Math.trunc('{{ log_pagination_size }}');
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@@ -33,6 +33,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<legend style="padding-top:20px" unselectable="on">{{ lang.admin.ip_check }}</legend><hr />
|
||||||
|
<div id="ip_check">
|
||||||
|
<form class="form" data-id="ip_check" role="form" method="post">
|
||||||
|
<div class="mb-4">
|
||||||
|
<input class="form-check-input" type="checkbox" value="1" name="ip_check_opt_in" id="ip_check_opt_in" {% if ip_check == 1 %}checked{% endif %}>
|
||||||
|
<label class="form-check-label" for="ip_check_opt_in">
|
||||||
|
{{ lang.admin.ip_check_opt_in|raw }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<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="ip_check" data-reload="no" data-api-url='edit/ip_check' data-api-attr='{}' href="#"><i class="bi bi-check-lg"></i> {{ lang.admin.save }}</button>
|
||||||
|
</div></p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
<legend>{{ lang.admin.app_links }}</legend><hr />
|
<legend>{{ lang.admin.app_links }}</legend><hr />
|
||||||
<p class="text-muted">{{ lang.admin.merged_vars_hint|raw }}</p>
|
<p class="text-muted">{{ lang.admin.merged_vars_hint|raw }}</p>
|
||||||
<form class="form-inline" data-id="app_links" role="form" method="post">
|
<form class="form-inline" data-id="app_links" role="form" method="post">
|
||||||
|
@@ -12,6 +12,14 @@
|
|||||||
<label for="f2b_ban_time">{{ lang.admin.f2b_ban_time }}:</label>
|
<label for="f2b_ban_time">{{ lang.admin.f2b_ban_time }}:</label>
|
||||||
<input type="number" class="form-control" id="f2b_ban_time" name="ban_time" value="{{ f2b_data.ban_time }}" required>
|
<input type="number" class="form-control" id="f2b_ban_time" name="ban_time" value="{{ f2b_data.ban_time }}" required>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="f2b_max_ban_time">{{ lang.admin.f2b_max_ban_time }}:</label>
|
||||||
|
<input type="number" class="form-control" id="f2b_max_ban_time" name="max_ban_time" value="{{ f2b_data.max_ban_time }}" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<input class="form-check-input" type="checkbox" value="1" name="ban_time_increment" id="f2b_ban_time_increment" {% if f2b_data.ban_time_increment == 1 %}checked{% endif %}>
|
||||||
|
<label class="form-check-label" for="f2b_ban_time_increment">{{ lang.admin.f2b_ban_time_increment }}</label>
|
||||||
|
</div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="f2b_max_attempts">{{ lang.admin.f2b_max_attempts }}:</label>
|
<label for="f2b_max_attempts">{{ lang.admin.f2b_max_attempts }}:</label>
|
||||||
<input type="number" class="form-control" id="f2b_max_attempts" name="max_attempts" value="{{ f2b_data.max_attempts }}" required>
|
<input type="number" class="form-control" id="f2b_max_attempts" name="max_attempts" value="{{ f2b_data.max_attempts }}" required>
|
||||||
|
@@ -36,7 +36,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="rlyhost_password">{{ lang.admin.password }}</label>
|
<label for="rlyhost_password">{{ lang.admin.password }}</label>
|
||||||
<input class="form-control" id="rlyhost_password" name="password">
|
<input class="form-control" id="rlyhost_password" name="password" type="password">
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-sm d-block d-sm-inline btn-success" data-action="add_item" data-id="rlyhost" data-api-url='add/relayhost' data-api-attr='{}' href="#"><i class="bi bi-plus-lg"></i> {{ lang.admin.add }}</button>
|
<button class="btn btn-sm d-block d-sm-inline btn-success" data-action="add_item" data-id="rlyhost" data-api-url='add/relayhost' data-api-attr='{}' href="#"><i class="bi bi-plus-lg"></i> {{ lang.admin.add }}</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -86,7 +86,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="transport_password">{{ lang.admin.password }}</label>
|
<label for="transport_password">{{ lang.admin.password }}</label>
|
||||||
<input class="form-control" id="transport_password" name="password">
|
<input class="form-control" id="transport_password" name="password" type="password">
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
<label>
|
<label>
|
||||||
|
@@ -41,7 +41,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 col-md-8">
|
<div class="col-sm-12 col-md-8">
|
||||||
<div class="table-responsive" style="margin-top: 10px;">
|
<div class="table-responsive" style="margin-top: 10px;">
|
||||||
<table class="table table-striped table-condensed">
|
<table class="table table-striped table-condensed w-100">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Hostname</td>
|
<td>Hostname</td>
|
||||||
@@ -49,18 +49,34 @@
|
|||||||
<p><b>{{ hostname }}</b></p>
|
<p><b>{{ hostname }}</b></p>
|
||||||
</div></td>
|
</div></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ lang.debug.architecture }}</td>
|
||||||
|
<td class="text-break"><div>
|
||||||
|
<p id="host_architecture">-</p>
|
||||||
|
</div></td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>IPs</td>
|
<td>IPs</td>
|
||||||
<td class="text-break">
|
<td class="text-break">
|
||||||
<span class="d-block" id="host_ipv4">-</span>
|
{% if ip_check == 1 %}
|
||||||
<span class="d-block" id="host_ipv6">-</span>
|
<span class="d-none" id="host_ipv4">-</span>
|
||||||
|
<span class="d-none mb-2" id="host_ipv6">-</span>
|
||||||
|
<button class="d-block btn btn-primary btn-sm" id="host_show_ip">
|
||||||
|
<span class="text">{{ lang.debug.show_ip }}</span>
|
||||||
|
<div class="spinner-border spinner-border-sm d-none" role="status">
|
||||||
|
<span class="visually-hidden">Loading...</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
{% else %}
|
||||||
|
<span class="d-block">{{ lang.admin.ip_check_disabled|raw }}</span>
|
||||||
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Version</td>
|
<td>Version</td>
|
||||||
<td class="text-break">
|
<td class="text-break">
|
||||||
<div class="fw-bolder">
|
<div class="fw-bolder">
|
||||||
<p ><a href="#" id="maiclow_version">{{ mailcow_info.version_tag }}</a></p>
|
<p ><a href="#" id="mailcow_version">{{ mailcow_info.version_tag }}</a></p>
|
||||||
<p id="mailcow_update"></p>
|
<p id="mailcow_update"></p>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@@ -340,9 +356,9 @@
|
|||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a class="dropdown-item add_log_lines" data-post-process="general_syslog" data-table="postfix_log" data-log-url="postfix" data-nrows="100" href="#">+ 100</a></li>
|
<li><a class="dropdown-item add_log_lines" data-post-process="general_syslog" data-table="postfix_log" data-log-url="postfix" data-nrows="100" href="#">+ 100</a></li>
|
||||||
<li><a class="dropdown-item add_log_lines" data-post-process="general_syslog" data-table="postfix_log" data-log-url="postfix" data-nrows="1000" href="#">+ 1000</a></li>
|
<li><a class="dropdown-item add_log_lines" data-post-process="general_syslog" data-table="postfix_log" data-log-url="postfix" data-nrows="1000" href="#">+ 1000</a></li>
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li class="table_collapse_option"><hr class="dropdown-divider"></li>
|
||||||
<li><a class="dropdown-item" data-datatables-expand="postfix_log" data-table="postfix_log" href="#">{{ lang.datatables.expand_all }}</a></li>
|
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-expand="postfix_log" data-table="postfix_log" href="#">{{ lang.datatables.expand_all }}</a></li>
|
||||||
<li><a class="dropdown-item" data-datatables-collapse="postfix_log" data-table="postfix_log" href="#">{{ lang.datatables.collapse_all }}</a></li>
|
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-collapse="postfix_log" data-table="postfix_log" href="#">{{ lang.datatables.collapse_all }}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<table id="postfix_log" class="table table-striped dt-responsive w-100"></table>
|
<table id="postfix_log" class="table table-striped dt-responsive w-100"></table>
|
||||||
</div>
|
</div>
|
||||||
@@ -363,9 +379,9 @@
|
|||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a class="dropdown-item add_log_lines" data-post-process="mailcow_ui" data-table="ui_logs" data-log-url="ui" data-nrows="100" href="#">+ 100</a></li>
|
<li><a class="dropdown-item add_log_lines" data-post-process="mailcow_ui" data-table="ui_logs" data-log-url="ui" data-nrows="100" href="#">+ 100</a></li>
|
||||||
<li><a class="dropdown-item add_log_lines" data-post-process="mailcow_ui" data-table="ui_logs" data-log-url="ui" data-nrows="1000" href="#">+ 1000</a></li>
|
<li><a class="dropdown-item add_log_lines" data-post-process="mailcow_ui" data-table="ui_logs" data-log-url="ui" data-nrows="1000" href="#">+ 1000</a></li>
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li class="table_collapse_option"><hr class="dropdown-divider"></li>
|
||||||
<li><a class="dropdown-item" data-datatables-expand="ui_logs" data-table="ui_logs" href="#">{{ lang.datatables.expand_all }}</a></li>
|
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-expand="ui_logs" data-table="ui_logs" href="#">{{ lang.datatables.expand_all }}</a></li>
|
||||||
<li><a class="dropdown-item" data-datatables-collapse="ui_logs" data-table="ui_logs" href="#">{{ lang.datatables.collapse_all }}</a></li>
|
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-collapse="ui_logs" data-table="ui_logs" href="#">{{ lang.datatables.collapse_all }}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<table id="ui_logs" class="table table-striped dt-responsive w-100"></table>
|
<table id="ui_logs" class="table table-striped dt-responsive w-100"></table>
|
||||||
</div>
|
</div>
|
||||||
@@ -386,9 +402,9 @@
|
|||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a class="dropdown-item add_log_lines" data-post-process="sasl_log_table" data-table="sasl_logs" data-log-url="ui" data-nrows="100" href="#">+ 100</a></li>
|
<li><a class="dropdown-item add_log_lines" data-post-process="sasl_log_table" data-table="sasl_logs" data-log-url="ui" data-nrows="100" href="#">+ 100</a></li>
|
||||||
<li><a class="dropdown-item add_log_lines" data-post-process="sasl_log_table" data-table="sasl_logs" data-log-url="ui" data-nrows="1000" href="#">+ 1000</a></li>
|
<li><a class="dropdown-item add_log_lines" data-post-process="sasl_log_table" data-table="sasl_logs" data-log-url="ui" data-nrows="1000" href="#">+ 1000</a></li>
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li class="table_collapse_option"><hr class="dropdown-divider"></li>
|
||||||
<li><a class="dropdown-item" data-datatables-expand="sasl_logs" data-table="sasl_logs" href="#">{{ lang.datatables.expand_all }}</a></li>
|
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-expand="sasl_logs" data-table="sasl_logs" href="#">{{ lang.datatables.expand_all }}</a></li>
|
||||||
<li><a class="dropdown-item" data-datatables-collapse="sasl_logs" data-table="sasl_logs" href="#">{{ lang.datatables.collapse_all }}</a></li>
|
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-collapse="sasl_logs" data-table="sasl_logs" href="#">{{ lang.datatables.collapse_all }}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<table id="sasl_logs" class="table table-striped dt-responsive w-100"></table>
|
<table id="sasl_logs" class="table table-striped dt-responsive w-100"></table>
|
||||||
</div>
|
</div>
|
||||||
@@ -409,9 +425,9 @@
|
|||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a class="dropdown-item add_log_lines" data-post-process="general_syslog" data-table="dovecot_log" data-log-url="dovecot" data-nrows="100" href="#">+ 100</a></li>
|
<li><a class="dropdown-item add_log_lines" data-post-process="general_syslog" data-table="dovecot_log" data-log-url="dovecot" data-nrows="100" href="#">+ 100</a></li>
|
||||||
<li><a class="dropdown-item add_log_lines" data-post-process="general_syslog" data-table="dovecot_log" data-log-url="dovecot" data-nrows="1000" href="#">+ 1000</a></li>
|
<li><a class="dropdown-item add_log_lines" data-post-process="general_syslog" data-table="dovecot_log" data-log-url="dovecot" data-nrows="1000" href="#">+ 1000</a></li>
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li class="table_collapse_option"><hr class="dropdown-divider"></li>
|
||||||
<li><a class="dropdown-item" data-datatables-expand="dovecot_log" data-table="dovecot_log" href="#">{{ lang.datatables.expand_all }}</a></li>
|
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-expand="dovecot_log" data-table="dovecot_log" href="#">{{ lang.datatables.expand_all }}</a></li>
|
||||||
<li><a class="dropdown-item" data-datatables-collapse="dovecot_log" data-table="dovecot_log" href="#">{{ lang.datatables.collapse_all }}</a></li>
|
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-collapse="dovecot_log" data-table="dovecot_log" href="#">{{ lang.datatables.collapse_all }}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<table id="dovecot_log" class="table table-striped dt-responsive w-100"></table>
|
<table id="dovecot_log" class="table table-striped dt-responsive w-100"></table>
|
||||||
</div>
|
</div>
|
||||||
@@ -432,9 +448,9 @@
|
|||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a class="dropdown-item add_log_lines" data-post-process="general_syslog" data-table="sogo_log" data-log-url="sogo" data-nrows="100" href="#">+ 100</a></li>
|
<li><a class="dropdown-item add_log_lines" data-post-process="general_syslog" data-table="sogo_log" data-log-url="sogo" data-nrows="100" href="#">+ 100</a></li>
|
||||||
<li><a class="dropdown-item add_log_lines" data-post-process="general_syslog" data-table="sogo_log" data-log-url="sogo" data-nrows="1000" href="#">+ 1000</a></li>
|
<li><a class="dropdown-item add_log_lines" data-post-process="general_syslog" data-table="sogo_log" data-log-url="sogo" data-nrows="1000" href="#">+ 1000</a></li>
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li class="table_collapse_option"><hr class="dropdown-divider"></li>
|
||||||
<li><a class="dropdown-item" data-datatables-expand="sogo_log" data-table="sogo_log" href="#">{{ lang.datatables.expand_all }}</a></li>
|
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-expand="sogo_log" data-table="sogo_log" href="#">{{ lang.datatables.expand_all }}</a></li>
|
||||||
<li><a class="dropdown-item" data-datatables-collapse="sogo_log" data-table="sogo_log" href="#">{{ lang.datatables.collapse_all }}</a></li>
|
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-collapse="sogo_log" data-table="sogo_log" href="#">{{ lang.datatables.collapse_all }}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<table id="sogo_log" class="table table-striped dt-responsive w-100"></table>
|
<table id="sogo_log" class="table table-striped dt-responsive w-100"></table>
|
||||||
</div>
|
</div>
|
||||||
@@ -455,9 +471,9 @@
|
|||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a class="dropdown-item add_log_lines" data-post-process="general_syslog" data-table="netfilter_log" data-log-url="netfilter" data-nrows="100" href="#">+ 100</a></li>
|
<li><a class="dropdown-item add_log_lines" data-post-process="general_syslog" data-table="netfilter_log" data-log-url="netfilter" data-nrows="100" href="#">+ 100</a></li>
|
||||||
<li><a class="dropdown-item add_log_lines" data-post-process="general_syslog" data-table="netfilter_log" data-log-url="netfilter" data-nrows="1000" href="#">+ 1000</a></li>
|
<li><a class="dropdown-item add_log_lines" data-post-process="general_syslog" data-table="netfilter_log" data-log-url="netfilter" data-nrows="1000" href="#">+ 1000</a></li>
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li class="table_collapse_option"><hr class="dropdown-divider"></li>
|
||||||
<li><a class="dropdown-item" data-datatables-expand="netfilter_log" data-table="netfilter_log" href="#">{{ lang.datatables.expand_all }}</a></li>
|
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-expand="netfilter_log" data-table="netfilter_log" href="#">{{ lang.datatables.expand_all }}</a></li>
|
||||||
<li><a class="dropdown-item" data-datatables-collapse="netfilter_log" data-table="netfilter_log" href="#">{{ lang.datatables.collapse_all }}</a></li>
|
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-collapse="netfilter_log" data-table="netfilter_log" href="#">{{ lang.datatables.collapse_all }}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<table id="netfilter_log" class="table table-striped dt-responsive w-100"></table>
|
<table id="netfilter_log" class="table table-striped dt-responsive w-100"></table>
|
||||||
</div>
|
</div>
|
||||||
@@ -483,9 +499,9 @@
|
|||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a class="dropdown-item add_log_lines" data-post-process="rspamd_history" data-table="rspamd_history" data-log-url="rspamd_history" data-nrows="100" href="#">+ 100</a></li>
|
<li><a class="dropdown-item add_log_lines" data-post-process="rspamd_history" data-table="rspamd_history" data-log-url="rspamd_history" data-nrows="100" href="#">+ 100</a></li>
|
||||||
<li><a class="dropdown-item add_log_lines" data-post-process="rspamd_history" data-table="rspamd_history" data-log-url="rspamd_history" data-nrows="1000" href="#">+ 1000</a></li>
|
<li><a class="dropdown-item add_log_lines" data-post-process="rspamd_history" data-table="rspamd_history" data-log-url="rspamd_history" data-nrows="1000" href="#">+ 1000</a></li>
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li class="table_collapse_option"><hr class="dropdown-divider"></li>
|
||||||
<li><a class="dropdown-item" data-datatables-expand="rspamd_history" data-table="rspamd_history" href="#">{{ lang.datatables.expand_all }}</a></li>
|
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-expand="rspamd_history" data-table="rspamd_history" href="#">{{ lang.datatables.expand_all }}</a></li>
|
||||||
<li><a class="dropdown-item" data-datatables-collapse="rspamd_history" data-table="rspamd_history" href="#">{{ lang.datatables.collapse_all }}</a></li>
|
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-collapse="rspamd_history" data-table="rspamd_history" href="#">{{ lang.datatables.collapse_all }}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<table id="rspamd_history" class="table table-striped dt-responsive w-100"></table>
|
<table id="rspamd_history" class="table table-striped dt-responsive w-100"></table>
|
||||||
</div>
|
</div>
|
||||||
@@ -506,9 +522,9 @@
|
|||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a class="dropdown-item add_log_lines" data-post-process="autodiscover_log" data-table="autodiscover_log" data-log-url="autodiscover" data-nrows="100" href="#">+ 100</a></li>
|
<li><a class="dropdown-item add_log_lines" data-post-process="autodiscover_log" data-table="autodiscover_log" data-log-url="autodiscover" data-nrows="100" href="#">+ 100</a></li>
|
||||||
<li><a class="dropdown-item add_log_lines" data-post-process="autodiscover_log" data-table="autodiscover_log" data-log-url="autodiscover" data-nrows="1000" href="#">+ 1000</a></li>
|
<li><a class="dropdown-item add_log_lines" data-post-process="autodiscover_log" data-table="autodiscover_log" data-log-url="autodiscover" data-nrows="1000" href="#">+ 1000</a></li>
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li class="table_collapse_option"><hr class="dropdown-divider"></li>
|
||||||
<li><a class="dropdown-item" data-datatables-expand="autodiscover_log" data-table="autodiscover_log" href="#">{{ lang.datatables.expand_all }}</a></li>
|
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-expand="autodiscover_log" data-table="autodiscover_log" href="#">{{ lang.datatables.expand_all }}</a></li>
|
||||||
<li><a class="dropdown-item" data-datatables-collapse="autodiscover_log" data-table="autodiscover_log" href="#">{{ lang.datatables.collapse_all }}</a></li>
|
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-collapse="autodiscover_log" data-table="autodiscover_log" href="#">{{ lang.datatables.collapse_all }}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<table id="autodiscover_log" class="table table-striped dt-responsive w-100"></table>
|
<table id="autodiscover_log" class="table table-striped dt-responsive w-100"></table>
|
||||||
</div>
|
</div>
|
||||||
@@ -529,9 +545,9 @@
|
|||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a class="dropdown-item add_log_lines" data-post-process="watchdog" data-table="watchdog_log" data-log-url="watchdog" data-nrows="100" href="#">+ 100</a></li>
|
<li><a class="dropdown-item add_log_lines" data-post-process="watchdog" data-table="watchdog_log" data-log-url="watchdog" data-nrows="100" href="#">+ 100</a></li>
|
||||||
<li><a class="dropdown-item add_log_lines" data-post-process="watchdog" data-table="watchdog_log" data-log-url="watchdog" data-nrows="1000" href="#">+ 1000</a></li>
|
<li><a class="dropdown-item add_log_lines" data-post-process="watchdog" data-table="watchdog_log" data-log-url="watchdog" data-nrows="1000" href="#">+ 1000</a></li>
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li class="table_collapse_option"><hr class="dropdown-divider"></li>
|
||||||
<li><a class="dropdown-item" data-datatables-expand="watchdog_log" data-table="watchdog_log" href="#">{{ lang.datatables.expand_all }}</a></li>
|
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-expand="watchdog_log" data-table="watchdog_log" href="#">{{ lang.datatables.expand_all }}</a></li>
|
||||||
<li><a class="dropdown-item" data-datatables-collapse="watchdog_log" data-table="watchdog_log" href="#">{{ lang.datatables.collapse_all }}</a></li>
|
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-collapse="watchdog_log" data-table="watchdog_log" href="#">{{ lang.datatables.collapse_all }}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<table id="watchdog_log" class="table table-striped dt-responsive w-100"></table>
|
<table id="watchdog_log" class="table table-striped dt-responsive w-100"></table>
|
||||||
</div>
|
</div>
|
||||||
@@ -552,9 +568,9 @@
|
|||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a class="dropdown-item add_log_lines" data-post-process="general_syslog" data-table="acme_log" data-log-url="acme" data-nrows="100" href="#">+ 100</a></li>
|
<li><a class="dropdown-item add_log_lines" data-post-process="general_syslog" data-table="acme_log" data-log-url="acme" data-nrows="100" href="#">+ 100</a></li>
|
||||||
<li><a class="dropdown-item add_log_lines" data-post-process="general_syslog" data-table="acme_log" data-log-url="acme" data-nrows="1000" href="#">+ 1000</a></li>
|
<li><a class="dropdown-item add_log_lines" data-post-process="general_syslog" data-table="acme_log" data-log-url="acme" data-nrows="1000" href="#">+ 1000</a></li>
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li class="table_collapse_option"><hr class="dropdown-divider"></li>
|
||||||
<li><a class="dropdown-item" data-datatables-expand="acme_log" data-table="acme_log" href="#">{{ lang.datatables.expand_all }}</a></li>
|
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-expand="acme_log" data-table="acme_log" href="#">{{ lang.datatables.expand_all }}</a></li>
|
||||||
<li><a class="dropdown-item" data-datatables-collapse="acme_log" data-table="acme_log" href="#">{{ lang.datatables.collapse_all }}</a></li>
|
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-collapse="acme_log" data-table="acme_log" href="#">{{ lang.datatables.collapse_all }}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<table id="acme_log" class="table table-striped dt-responsive w-100"></table>
|
<table id="acme_log" class="table table-striped dt-responsive w-100"></table>
|
||||||
</div>
|
</div>
|
||||||
@@ -575,9 +591,9 @@
|
|||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a class="dropdown-item add_log_lines" data-post-process="apilog" data-table="api_log" data-log-url="api" data-nrows="100" href="#">+ 100</a></li>
|
<li><a class="dropdown-item add_log_lines" data-post-process="apilog" data-table="api_log" data-log-url="api" data-nrows="100" href="#">+ 100</a></li>
|
||||||
<li><a class="dropdown-item add_log_lines" data-post-process="apilog" data-table="api_log" data-log-url="api" data-nrows="1000" href="#">+ 1000</a></li>
|
<li><a class="dropdown-item add_log_lines" data-post-process="apilog" data-table="api_log" data-log-url="api" data-nrows="1000" href="#">+ 1000</a></li>
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li class="table_collapse_option"><hr class="dropdown-divider"></li>
|
||||||
<li><a class="dropdown-item" data-datatables-expand="api_log" data-table="api_log" href="#">{{ lang.datatables.expand_all }}</a></li>
|
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-expand="api_log" data-table="api_log" href="#">{{ lang.datatables.expand_all }}</a></li>
|
||||||
<li><a class="dropdown-item" data-datatables-collapse="api_log" data-table="api_log" href="#">{{ lang.datatables.collapse_all }}</a></li>
|
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-collapse="api_log" data-table="api_log" href="#">{{ lang.datatables.collapse_all }}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<table id="api_log" class="table table-striped dt-responsive w-100"></table>
|
<table id="api_log" class="table table-striped dt-responsive w-100"></table>
|
||||||
</div>
|
</div>
|
||||||
@@ -598,11 +614,11 @@
|
|||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a class="dropdown-item add_log_lines" data-post-process="rllog" data-table="rl_log" data-log-url="ratelimited" data-nrows="100" href="#">+ 100</a></li>
|
<li><a class="dropdown-item add_log_lines" data-post-process="rllog" data-table="rl_log" data-log-url="ratelimited" data-nrows="100" href="#">+ 100</a></li>
|
||||||
<li><a class="dropdown-item add_log_lines" data-post-process="rllog" data-table="rl_log" data-log-url="ratelimited" data-nrows="1000" href="#">+ 1000</a></li>
|
<li><a class="dropdown-item add_log_lines" data-post-process="rllog" data-table="rl_log" data-log-url="ratelimited" data-nrows="1000" href="#">+ 1000</a></li>
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li class="table_collapse_option"><hr class="dropdown-divider"></li>
|
||||||
<li><a class="dropdown-item" data-datatables-expand="rl_log" data-table="rl_log" href="#">{{ lang.datatables.expand_all }}</a></li>
|
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-expand="rl_log" data-table="rl_log" href="#">{{ lang.datatables.expand_all }}</a></li>
|
||||||
<li><a class="dropdown-item" data-datatables-collapse="rl_log" data-table="rl_log" href="#">{{ lang.datatables.collapse_all }}</a></li>
|
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-collapse="rl_log" data-table="rl_log" href="#">{{ lang.datatables.collapse_all }}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<p class="text-muted">{{ lang.admin.hash_remove_info }}</p>
|
<p class="text-muted">{{ lang.admin.hash_remove_info|raw }}</p>
|
||||||
<table id="rl_log" class="table table-striped dt-responsive w-100"></table>
|
<table id="rl_log" class="table table-striped dt-responsive w-100"></table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -617,6 +633,6 @@
|
|||||||
var lang_debug = {{ lang_debug|raw }};
|
var lang_debug = {{ lang_debug|raw }};
|
||||||
var lang_datatables = {{ lang_datatables|raw }};
|
var lang_datatables = {{ lang_datatables|raw }};
|
||||||
var csrf_token = '{{ csrf_token }}';
|
var csrf_token = '{{ csrf_token }}';
|
||||||
var log_pagination_size = '{{ log_pagination_size }}';
|
var log_pagination_size = Math.trunc('{{ log_pagination_size }}');
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@@ -46,7 +46,7 @@
|
|||||||
<div class="col-sm-3 col-5 text-end">{{ lang.fido2.known_ids }}:</div>
|
<div class="col-sm-3 col-5 text-end">{{ lang.fido2.known_ids }}:</div>
|
||||||
<div class="col-sm-9 col-7">
|
<div class="col-sm-9 col-7">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped table-hover table-condensed" id="fido2_keys">
|
<table class="table table-striped table-hover table-condensed w-100" id="fido2_keys">
|
||||||
<tr>
|
<tr>
|
||||||
<th>ID</th>
|
<th>ID</th>
|
||||||
<th style="min-width:240px;text-align: right">{{ lang.admin.action }}</th>
|
<th style="min-width:240px;text-align: right">{{ lang.admin.action }}</th>
|
||||||
|
@@ -26,7 +26,7 @@
|
|||||||
var lang_user = {{ lang_user|raw }};
|
var lang_user = {{ lang_user|raw }};
|
||||||
var lang_datatables = {{ lang_datatables|raw }};
|
var lang_datatables = {{ lang_datatables|raw }};
|
||||||
var csrf_token = '{{ csrf_token }}';
|
var csrf_token = '{{ csrf_token }}';
|
||||||
var pagination_size = '{{ pagination_size }}';
|
var pagination_size = Math.trunc('{{ pagination_size }}');
|
||||||
var table_for_domain = '{{ domain }}';
|
var table_for_domain = '{{ domain }}';
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@@ -109,25 +109,25 @@
|
|||||||
<label class="control-label col-sm-2">{{ lang.user.quarantine_notification }}</label>
|
<label class="control-label col-sm-2">{{ lang.user.quarantine_notification }}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<div class="btn-group" data-acl="{{ acl.quarantine_notification }}">
|
<div class="btn-group" data-acl="{{ acl.quarantine_notification }}">
|
||||||
<button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-secondary{% if quarantine_notification == 'never' %} active{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-light{% if quarantine_notification == 'never' %} btn-dark{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailbox }}"
|
data-item="{{ mailbox }}"
|
||||||
data-id="quarantine_notification"
|
data-id="quarantine_notification"
|
||||||
data-api-url='edit/quarantine_notification'
|
data-api-url='edit/quarantine_notification'
|
||||||
data-api-attr='{"quarantine_notification":"never"}'>{{ lang.user.never }}</button>
|
data-api-attr='{"quarantine_notification":"never"}'>{{ lang.user.never }}</button>
|
||||||
<button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-secondary{% if quarantine_notification == 'hourly' %} active{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-light{% if quarantine_notification == 'hourly' %} btn-dark{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailbox }}"
|
data-item="{{ mailbox }}"
|
||||||
data-id="quarantine_notification"
|
data-id="quarantine_notification"
|
||||||
data-api-url='edit/quarantine_notification'
|
data-api-url='edit/quarantine_notification'
|
||||||
data-api-attr='{"quarantine_notification":"hourly"}'>{{ lang.user.hourly }}</button>
|
data-api-attr='{"quarantine_notification":"hourly"}'>{{ lang.user.hourly }}</button>
|
||||||
<button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-secondary{% if quarantine_notification == 'daily' %} active{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-light{% if quarantine_notification == 'daily' %} btn-dark{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailbox }}"
|
data-item="{{ mailbox }}"
|
||||||
data-id="quarantine_notification"
|
data-id="quarantine_notification"
|
||||||
data-api-url='edit/quarantine_notification'
|
data-api-url='edit/quarantine_notification'
|
||||||
data-api-attr='{"quarantine_notification":"daily"}'>{{ lang.user.daily }}</button>
|
data-api-attr='{"quarantine_notification":"daily"}'>{{ lang.user.daily }}</button>
|
||||||
<button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-secondary{% if quarantine_notification == 'weekly' %} active{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-light{% if quarantine_notification == 'weekly' %} btn-dark{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailbox }}"
|
data-item="{{ mailbox }}"
|
||||||
data-id="quarantine_notification"
|
data-id="quarantine_notification"
|
||||||
@@ -141,19 +141,19 @@
|
|||||||
<label class="control-label col-sm-2">{{ lang.user.quarantine_category }}</label>
|
<label class="control-label col-sm-2">{{ lang.user.quarantine_category }}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<div class="btn-group" data-acl="{{ acl.quarantine_category }}">
|
<div class="btn-group" data-acl="{{ acl.quarantine_category }}">
|
||||||
<button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-secondary{% if quarantine_category == 'reject' %} active{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-light{% if quarantine_category == 'reject' %} btn-dark{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailbox }}"
|
data-item="{{ mailbox }}"
|
||||||
data-id="quarantine_category"
|
data-id="quarantine_category"
|
||||||
data-api-url='edit/quarantine_category'
|
data-api-url='edit/quarantine_category'
|
||||||
data-api-attr='{"quarantine_category":"reject"}'>{{ lang.user.q_reject }}</button>
|
data-api-attr='{"quarantine_category":"reject"}'>{{ lang.user.q_reject }}</button>
|
||||||
<button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-secondary{% if quarantine_category == 'add_header' %} active{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-light{% if quarantine_category == 'add_header' %} btn-dark{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailbox }}"
|
data-item="{{ mailbox }}"
|
||||||
data-id="quarantine_category"
|
data-id="quarantine_category"
|
||||||
data-api-url='edit/quarantine_category'
|
data-api-url='edit/quarantine_category'
|
||||||
data-api-attr='{"quarantine_category":"add_header"}'>{{ lang.user.q_add_header }}</button>
|
data-api-attr='{"quarantine_category":"add_header"}'>{{ lang.user.q_add_header }}</button>
|
||||||
<button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-secondary{% if quarantine_category == 'all' %} active{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-light{% if quarantine_category == 'all' %} btn-dark{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailbox }}"
|
data-item="{{ mailbox }}"
|
||||||
data-id="quarantine_category"
|
data-id="quarantine_category"
|
||||||
@@ -167,13 +167,13 @@
|
|||||||
<label class="control-label col-sm-2" for="sender_acl">{{ lang.user.tls_policy }}</label>
|
<label class="control-label col-sm-2" for="sender_acl">{{ lang.user.tls_policy }}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<div class="btn-group" data-acl="{{ acl.tls_policy }}">
|
<div class="btn-group" data-acl="{{ acl.tls_policy }}">
|
||||||
<button type="button" class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary{% if get_tls_policy.tls_enforce_in == '1' %} active"{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-half d-block d-sm-inline btn-light{% if get_tls_policy.tls_enforce_in == '1' %} btn-dark"{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailbox }}"
|
data-item="{{ mailbox }}"
|
||||||
data-id="tls_policy"
|
data-id="tls_policy"
|
||||||
data-api-url='edit/tls_policy'
|
data-api-url='edit/tls_policy'
|
||||||
data-api-attr='{"tls_enforce_in": {% if get_tls_policy.tls_enforce_in == '1' %}0{% else %}1{% endif %} }'>{{ lang.user.tls_enforce_in }}</button>
|
data-api-attr='{"tls_enforce_in": {% if get_tls_policy.tls_enforce_in == '1' %}0{% else %}1{% endif %} }'>{{ lang.user.tls_enforce_in }}</button>
|
||||||
<button type="button" class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary{% if get_tls_policy.tls_enforce_out == '1' %} active"{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-half d-block d-sm-inline btn-light{% if get_tls_policy.tls_enforce_out == '1' %} btn-dark"{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailbox }}"
|
data-item="{{ mailbox }}"
|
||||||
data-id="tls_policy"
|
data-id="tls_policy"
|
||||||
@@ -200,8 +200,10 @@
|
|||||||
{% if sender_acl_handles.external_sender_aliases %}
|
{% if sender_acl_handles.external_sender_aliases %}
|
||||||
{% set ext_sender_acl = sender_acl_handles.external_sender_aliases|join(', ') %}
|
{% set ext_sender_acl = sender_acl_handles.external_sender_aliases|join(', ') %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<input type="text" class="form-control" name="extended_sender_acl" value="{{ ext_sender_acl }}" placeholder="user1@example.com, user2@example.org, @example.com, ...">
|
{% if acl.extend_sender_acl and acl.extend_sender_acl == 1 %}
|
||||||
<small class="text-muted">{{ lang.edit.extended_sender_acl_info|raw }}</small>
|
<input type="text" class="form-control" name="extended_sender_acl" value="{{ ext_sender_acl }}" placeholder="user1@example.com, user2@example.org, @example.com, ...">
|
||||||
|
<small class="text-muted">{{ lang.edit.extended_sender_acl_info|raw }}</small>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user