mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2026-10-06 20:27:12 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
dd50bbca9b | ||
|
|
f3f5471ef7 | ||
|
|
516c8ea66c | ||
|
|
48310034e5 | ||
|
|
be35a88f8c | ||
|
|
e67b512499 | ||
|
|
0cf59159cd | ||
|
|
e7a929a947 | ||
|
|
dabf4d4383 | ||
|
|
13bdd4ad0b | ||
|
|
3281b97ea9 | ||
|
|
8070db96e9 | ||
|
|
82c80a9682 | ||
|
|
136cc2e3ff | ||
|
|
eefce62f01 | ||
|
|
240b2c63f6 | ||
|
|
355da03fba | ||
|
|
55d57c552d | ||
|
|
a56e5eb2fe | ||
|
|
e7817fab78 | ||
|
|
714b8417f4 | ||
|
|
ffb68c8848 | ||
|
|
7a5be0ccbf | ||
|
|
c2927af554 | ||
|
|
24cea0cf22 | ||
|
|
f4351c119f | ||
|
|
f40b6b5b65 | ||
|
|
bff96eb1ae | ||
|
|
de9564c4c9 | ||
|
|
614aa1e49e | ||
|
|
b368c299d9 | ||
|
|
01a61d4e62 | ||
|
|
174fcd7167 | ||
|
|
dca85f2ffb | ||
|
|
8ad5acb020 | ||
|
|
287118c3a7 | ||
|
|
78621a1f50 | ||
|
|
116859e0ba | ||
|
|
c4827e908c | ||
|
|
41d56a867a | ||
|
|
9f4dd1d172 | ||
|
|
948d23f56d | ||
|
|
50e9a3ec8a | ||
|
|
0dbd6be010 | ||
|
|
2b4189b1a4 | ||
|
|
4bf81975dc | ||
|
|
ea040f4412 | ||
|
|
c246648949 | ||
|
|
125aaa5b7d | ||
|
|
aa7888c37d | ||
|
|
f1e1232849 | ||
|
|
bb7c7bcff6 | ||
|
|
e5cf35aff8 | ||
|
|
65585e286d | ||
|
|
99bcfb8c4b | ||
|
|
d98fd74968 | ||
|
|
7875185e1f | ||
|
|
a8d50955ee | ||
|
|
bfd5329363 | ||
|
|
ea1eb48596 | ||
|
|
f1bb23ba2a | ||
|
|
5160eff294 | ||
|
|
118984dfff | ||
|
|
a71cc759f6 | ||
|
|
faf8da1365 | ||
|
|
ce546e8a90 | ||
|
|
f4731eecdb | ||
|
|
6704377402 | ||
|
|
827cb00837 | ||
|
|
299a342a62 | ||
|
|
77f04d10c7 | ||
|
|
d55994b66a | ||
|
|
a96b209e1b | ||
|
|
d7323213b8 | ||
|
|
19fabd0e64 | ||
|
|
ef392ef6ba | ||
|
|
a09661fc83 | ||
|
|
f52ab69a5b | ||
|
|
9d1b620dcf | ||
|
|
3ebd801b3d | ||
|
|
0cdb1e638d | ||
|
|
da415e5c6b | ||
|
|
c8f69ffe77 | ||
|
|
79982e0e8d | ||
|
|
8ca028eb2e | ||
|
|
074e3fcd6e | ||
|
|
3f40fada1b | ||
|
|
a9871d05b2 | ||
|
|
39e46d2e0b | ||
|
|
996b2db514 | ||
|
|
177ebe26de | ||
|
|
da72184fda | ||
|
|
a2b31cb28d | ||
|
|
b6760e19b7 | ||
|
|
6ce25f38e1 | ||
|
|
5cb7f726bc | ||
|
|
a334f33b35 | ||
|
|
b503271aba | ||
|
|
008e5651f8 | ||
|
|
5e3aab12a7 | ||
|
|
51b80f6fa1 | ||
|
|
75fdeb2843 | ||
|
|
2b1d927de4 | ||
|
|
e5d788497a | ||
|
|
b173e2ef86 | ||
|
|
3d48c2427a | ||
|
|
a9046d8f35 | ||
|
|
b4a1b81aec | ||
|
|
90eb0ea27a | ||
|
|
4f01b9fd25 | ||
|
|
174b5c8f7f | ||
|
|
3912fcb238 | ||
|
|
ef70457a48 | ||
|
|
8c4dbaec4f | ||
|
|
645e8f426c | ||
|
|
bae1d1c047 | ||
|
|
b8656763ec | ||
|
|
10e560c5b2 | ||
|
|
ba9f2bc376 | ||
|
|
fb7e234120 | ||
|
|
8c80cecdfb | ||
|
|
e53f431273 | ||
|
|
8e0ee67108 | ||
|
|
3d82d9af1b | ||
|
|
8a0bd23985 | ||
|
|
4387e4764f | ||
|
|
3b8e17c21f | ||
|
|
e74af0db89 | ||
|
|
5ff62d8f22 | ||
|
|
4427173a6c | ||
|
|
db66fe33fa | ||
|
|
cf5fa96a93 | ||
|
|
9e76b6ee70 | ||
|
|
45e97b3753 | ||
|
|
ecc16c69e6 | ||
|
|
77e6124b00 | ||
|
|
a3ddb58566 | ||
|
|
be7252f620 | ||
|
|
db8af3d1e0 | ||
|
|
7f70b0f703 | ||
|
|
2e10cc8e79 | ||
|
|
047d9143c0 | ||
|
|
a7a0eef125 | ||
|
|
5d35af9d69 | ||
|
|
ea21bca7df | ||
|
|
a3c0737ba8 | ||
|
|
9747995510 | ||
|
|
ad9112010f | ||
|
|
a4ec2d1d86 | ||
|
|
b7f07951f6 | ||
|
|
0e3363e61c | ||
|
|
e26d5b8ba5 | ||
|
|
8987ebca36 | ||
|
|
d3cd21956a | ||
|
|
b149da28c8 | ||
|
|
e5cb2dd00e | ||
|
|
c9b883dff5 | ||
|
|
ad43253a90 | ||
|
|
979de67c2b | ||
|
|
8416caf798 | ||
|
|
80d9dfe420 | ||
|
|
8a49b50f33 | ||
|
|
cbd8e40f14 | ||
|
|
2d2c033ba7 | ||
|
|
496c68d2af | ||
|
|
c505943e8b | ||
|
|
2841c09c1f | ||
|
|
18444bd284 | ||
|
|
9d3a89d362 | ||
|
|
2d0ab4a1b8 | ||
|
|
2fec7ccd58 | ||
|
|
052959f435 | ||
|
|
560df58bb4 | ||
|
|
5629d47cb6 | ||
|
|
37b4ff811d | ||
|
|
7384aab2f4 | ||
|
|
4ce05d4d4d | ||
|
|
fdb56de0a8 | ||
|
|
df56d73cec | ||
|
|
4ba0e155f3 | ||
|
|
304655f7ff | ||
|
|
6210f06bc0 | ||
|
|
09ae37410e | ||
|
|
351c803623 | ||
|
|
6a027b70e7 | ||
|
|
cdd2adbc73 | ||
|
|
cb6a5d4069 | ||
|
|
f13530d8a1 | ||
|
|
8a86fa491e | ||
|
|
3e6a241c69 | ||
|
|
160dceff3e | ||
|
|
0ece065cb0 | ||
|
|
fb7e00c158 | ||
|
|
a0567beee5 | ||
|
|
96c8e01a3b | ||
|
|
88bd7bff1e | ||
|
|
7075b9f0c0 | ||
|
|
b19666f7e0 | ||
|
|
e663f3db72 | ||
|
|
fd1ffdba80 | ||
|
|
a12538b3a8 | ||
|
|
cdff1ba37b | ||
|
|
f6a51f6b6f | ||
|
|
45dd0611d9 | ||
|
|
e62069d3db | ||
|
|
5088636d5f | ||
|
|
003d70990e | ||
|
|
051d08b499 | ||
|
|
b980e7af29 | ||
|
|
4d0799dead | ||
|
|
d3dca1ddc2 | ||
|
|
9b8039440c | ||
|
|
eea5c9df2f | ||
|
|
aa9aff800c | ||
|
|
0b3b5e230b | ||
|
|
db0d91beb1 | ||
|
|
4758033445 | ||
|
|
1e48fb8cda | ||
|
|
1d8da117d6 | ||
|
|
635fa795d2 | ||
|
|
c1792df819 | ||
|
|
36944f8073 | ||
|
|
4d59cb0351 |
No files matched your search
@@ -26,21 +26,21 @@ body:
|
||||
attributes:
|
||||
label: Description
|
||||
description: Please provide a brief description of the bug in 1-2 sentences. If applicable, add screenshots to help explain your problem. Very useful for bugs in mailcow UI.
|
||||
render: text
|
||||
render: plain text
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Logs:"
|
||||
description: "Please take a look at the [official documentation](https://docs.mailcow.email/troubleshooting/debug-logs/) and post the last few lines of logs, when the error occurs. For example, docker container logs of affected containers. This will be automatically formatted into code, so no need for backticks."
|
||||
render: text
|
||||
render: plain text
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Steps to reproduce:"
|
||||
description: "Please describe the steps to reproduce the bug. Screenshots can be added, if helpful."
|
||||
render: text
|
||||
render: plain text
|
||||
placeholder: |-
|
||||
1. ...
|
||||
2. ...
|
||||
@@ -117,41 +117,41 @@ body:
|
||||
attributes:
|
||||
label: "Logs of git diff:"
|
||||
description: "#### Output of `git diff origin/master`, any other changes to the code? If so, **please post them**:"
|
||||
render: text
|
||||
render: plain text
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Logs of iptables -L -vn:"
|
||||
description: "#### Output of `iptables -L -vn`"
|
||||
render: text
|
||||
render: plain text
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Logs of ip6tables -L -vn:"
|
||||
description: "#### Output of `ip6tables -L -vn`"
|
||||
render: text
|
||||
render: plain text
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Logs of iptables -L -vn -t nat:"
|
||||
description: "#### Output of `iptables -L -vn -t nat`"
|
||||
render: text
|
||||
render: plain text
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Logs of ip6tables -L -vn -t nat:"
|
||||
description: "#### Output of `ip6tables -L -vn -t nat`"
|
||||
render: text
|
||||
render: plain text
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "DNS check:"
|
||||
description: "#### Output of `docker exec -it $(docker ps -qf name=acme-mailcow) dig +short stackoverflow.com @172.22.1.254` (set the IP accordingly, if you changed the internal mailcow network)"
|
||||
render: text
|
||||
render: plain text
|
||||
validations:
|
||||
required: true
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"enabled": true,
|
||||
"timezone": "Europe/Berlin",
|
||||
"dependencyDashboard": true,
|
||||
"dependencyDashboardTitle": "Renovate Dashboard",
|
||||
"commitBody": "Signed-off-by: milkmaker <milkmaker@mailcow.de>",
|
||||
"rebaseWhen": "auto",
|
||||
"labels": ["renovate"],
|
||||
"assignees": [
|
||||
"@magiccc"
|
||||
],
|
||||
"baseBranches": ["staging"],
|
||||
"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
|
||||
steps:
|
||||
- name: Send message
|
||||
uses: thollander/actions-comment-pull-request@main
|
||||
uses: thollander/actions-comment-pull-request@v2.3.1
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.CHECKIFPRISSTAGING_ACTION_PAT }}
|
||||
message: |
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Mark/Close Stale Issues and Pull Requests 🗑️
|
||||
uses: actions/stale@v6.0.1
|
||||
uses: actions/stale@v7.0.0
|
||||
with:
|
||||
repo-token: ${{ secrets.STALE_ACTION_PAT }}
|
||||
days-before-stale: 60
|
||||
|
||||
@@ -33,13 +33,11 @@ jobs:
|
||||
run: |
|
||||
curl -sSL https://get.docker.com/ | CHANNEL=stable sudo sh
|
||||
sudo service docker start
|
||||
sudo curl -L https://github.com/docker/compose/releases/download/v$(curl -Ls https://www.servercow.de/docker-compose/latest.php)/docker-compose-$(uname -s)-$(uname -m) > /usr/local/bin/docker-compose
|
||||
sudo chmod +x /usr/local/bin/docker-compose
|
||||
- name: Prepair Image Builds
|
||||
run: |
|
||||
cp helper-scripts/docker-compose.override.yml.d/BUILD_FLAGS/docker-compose.override.yml docker-compose.override.yml
|
||||
- name: Build Docker Images
|
||||
run: |
|
||||
docker-compose build ${image}
|
||||
docker compose build ${image}
|
||||
env:
|
||||
image: ${{ matrix.images }}
|
||||
@@ -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'
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Run the Action
|
||||
uses: devops-infra/action-pull-request@v0.5.3
|
||||
uses: devops-infra/action-pull-request@v0.5.5
|
||||
with:
|
||||
github_token: ${{ secrets.PRTONIGHTLY_ACTION_PAT }}
|
||||
title: Automatic PR to nightly from ${{ github.event.repository.updated_at}}
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
password: ${{ secrets.BACKUPIMAGEBUILD_ACTION_DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
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'
|
||||
@@ -1,6 +1,5 @@
|
||||
# mailcow: dockerized - 🐮 + 🐋 = 💕
|
||||
|
||||
[](https://github.com/mailcow/mailcow-dockerized/actions/workflows/integration_tests.yml)
|
||||
[](https://translate.mailcow.email/engage/mailcow-dockerized/)
|
||||
[](https://twitter.com/mailcow_email)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.16
|
||||
FROM alpine:3.17
|
||||
|
||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||
|
||||
|
||||
@@ -213,11 +213,13 @@ while true; do
|
||||
done
|
||||
ADDITIONAL_WC_ARR+=('autodiscover' 'autoconfig')
|
||||
|
||||
if [[ ${SKIP_IP_CHECK} != "y" ]]; then
|
||||
# Start IP detection
|
||||
log_f "Detecting IP addresses..."
|
||||
IPV4=$(get_ipv4)
|
||||
IPV6=$(get_ipv6)
|
||||
log_f "OK: ${IPV4}, ${IPV6:-"0000:0000:0000:0000:0000:0000:0000:0000"}"
|
||||
fi
|
||||
|
||||
#########################################
|
||||
# IP and webroot challenge verification #
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM clamav/clamav:0.105.1_base
|
||||
FROM clamav/clamav:1.0.1-1_base
|
||||
|
||||
LABEL maintainer "André Peters <andre.peters@servercow.de>"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.16
|
||||
FROM alpine:3.17
|
||||
|
||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||
|
||||
@@ -8,11 +8,15 @@ RUN apk add --update --no-cache python3 \
|
||||
py3-pip \
|
||||
openssl \
|
||||
tzdata \
|
||||
py3-psutil \
|
||||
&& pip3 install --upgrade pip \
|
||||
fastapi \
|
||||
uvicorn \
|
||||
aiodocker \
|
||||
docker \
|
||||
flask \
|
||||
flask-restful
|
||||
redis
|
||||
|
||||
COPY docker-entrypoint.sh /app/
|
||||
COPY dockerapi.py /app/
|
||||
|
||||
CMD ["python3", "-u", "/app/dockerapi.py"]
|
||||
ENTRYPOINT ["/bin/sh", "/app/docker-entrypoint.sh"]
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
`openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \
|
||||
-keyout /app/dockerapi_key.pem \
|
||||
-out /app/dockerapi_cert.pem \
|
||||
-subj /CN=dockerapi/O=mailcow \
|
||||
-addext subjectAltName=DNS:dockerapi`
|
||||
|
||||
`uvicorn --host 0.0.0.0 --port 443 --ssl-certfile=/app/dockerapi_cert.pem --ssl-keyfile=/app/dockerapi_key.pem dockerapi:app`
|
||||
@@ -1,233 +1,326 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from flask import Flask
|
||||
from flask_restful import Resource, Api
|
||||
from flask import jsonify
|
||||
from flask import Response
|
||||
from flask import request
|
||||
from threading import Thread
|
||||
from fastapi import FastAPI, Response, Request
|
||||
import aiodocker
|
||||
import docker
|
||||
import uuid
|
||||
import signal
|
||||
import psutil
|
||||
import sys
|
||||
import re
|
||||
import time
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import ssl
|
||||
import socket
|
||||
import subprocess
|
||||
import traceback
|
||||
import json
|
||||
import asyncio
|
||||
import redis
|
||||
from datetime import datetime
|
||||
import logging
|
||||
from logging.config import dictConfig
|
||||
|
||||
docker_client = docker.DockerClient(base_url='unix://var/run/docker.sock', version='auto')
|
||||
app = Flask(__name__)
|
||||
api = Api(app)
|
||||
|
||||
class containers_get(Resource):
|
||||
def get(self):
|
||||
containers = {}
|
||||
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 = []
|
||||
host_stats_isUpdating = False
|
||||
app = FastAPI()
|
||||
logger = logging.getLogger('api-logger')
|
||||
|
||||
|
||||
@app.get("/host/stats")
|
||||
async def get_host_update_stats():
|
||||
global host_stats_isUpdating
|
||||
|
||||
if host_stats_isUpdating == False:
|
||||
asyncio.create_task(get_host_stats())
|
||||
host_stats_isUpdating = True
|
||||
|
||||
while True:
|
||||
if redis_client.exists('host_stats'):
|
||||
break
|
||||
await asyncio.sleep(1.5)
|
||||
|
||||
|
||||
stats = json.loads(redis_client.get('host_stats'))
|
||||
return Response(content=json.dumps(stats, indent=4), media_type="application/json")
|
||||
|
||||
@app.get("/containers/{container_id}/json")
|
||||
async def get_container(container_id : str):
|
||||
if container_id and container_id.isalnum():
|
||||
try:
|
||||
for container in docker_client.containers.list(all=True):
|
||||
containers.update({container.attrs['Id']: container.attrs})
|
||||
return containers
|
||||
for container in (await async_docker_client.containers.list()):
|
||||
if container._id == container_id:
|
||||
container_info = await container.show()
|
||||
return Response(content=json.dumps(container_info, indent=4), media_type="application/json")
|
||||
|
||||
res = {
|
||||
"type": "danger",
|
||||
"msg": "no container found"
|
||||
}
|
||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||
except Exception as e:
|
||||
return jsonify(type='danger', msg=str(e))
|
||||
res = {
|
||||
"type": "danger",
|
||||
"msg": str(e)
|
||||
}
|
||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||
else:
|
||||
res = {
|
||||
"type": "danger",
|
||||
"msg": "no or invalid id defined"
|
||||
}
|
||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||
|
||||
class container_get(Resource):
|
||||
def get(self, container_id):
|
||||
if container_id and container_id.isalnum():
|
||||
try:
|
||||
for container in docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||
return container.attrs
|
||||
except Exception as e:
|
||||
return jsonify(type='danger', msg=str(e))
|
||||
else:
|
||||
return jsonify(type='danger', msg='no or invalid id defined')
|
||||
@app.get("/containers/json")
|
||||
async def get_containers():
|
||||
containers = {}
|
||||
try:
|
||||
for container in (await async_docker_client.containers.list()):
|
||||
container_info = await container.show()
|
||||
containers.update({container_info['Id']: container_info})
|
||||
return Response(content=json.dumps(containers, indent=4), media_type="application/json")
|
||||
except Exception as e:
|
||||
res = {
|
||||
"type": "danger",
|
||||
"msg": str(e)
|
||||
}
|
||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||
|
||||
class container_post(Resource):
|
||||
def post(self, container_id, post_action):
|
||||
if container_id and container_id.isalnum() and post_action:
|
||||
try:
|
||||
"""Dispatch container_post api call"""
|
||||
if post_action == 'exec':
|
||||
if not request.json or not 'cmd' in request.json:
|
||||
return jsonify(type='danger', msg='cmd is missing')
|
||||
if not request.json or not 'task' in request.json:
|
||||
return jsonify(type='danger', msg='task is missing')
|
||||
@app.post("/containers/{container_id}/{post_action}")
|
||||
async def post_containers(container_id : str, post_action : str, request: Request):
|
||||
try :
|
||||
request_json = await request.json()
|
||||
except Exception as err:
|
||||
request_json = {}
|
||||
|
||||
api_call_method_name = '__'.join(['container_post', str(post_action), str(request.json['cmd']), str(request.json['task']) ])
|
||||
else:
|
||||
api_call_method_name = '__'.join(['container_post', str(post_action) ])
|
||||
if container_id and container_id.isalnum() and post_action:
|
||||
try:
|
||||
"""Dispatch container_post api call"""
|
||||
if post_action == 'exec':
|
||||
if not request_json or not 'cmd' in request_json:
|
||||
res = {
|
||||
"type": "danger",
|
||||
"msg": "cmd is missing"
|
||||
}
|
||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||
if not request_json or not 'task' in request_json:
|
||||
res = {
|
||||
"type": "danger",
|
||||
"msg": "task is missing"
|
||||
}
|
||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||
|
||||
api_call_method = getattr(self, api_call_method_name, lambda container_id: jsonify(type='danger', msg='container_post - unknown api call'))
|
||||
api_call_method_name = '__'.join(['container_post', str(post_action), str(request_json['cmd']), str(request_json['task']) ])
|
||||
else:
|
||||
api_call_method_name = '__'.join(['container_post', str(post_action) ])
|
||||
|
||||
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"))
|
||||
|
||||
|
||||
print("api call: %s, container_id: %s" % (api_call_method_name, container_id))
|
||||
return api_call_method(container_id)
|
||||
except Exception as e:
|
||||
print("error - container_post: %s" % str(e))
|
||||
return jsonify(type='danger', msg=str(e))
|
||||
logger.info("api call: %s, container_id: %s" % (api_call_method_name, container_id))
|
||||
return api_call_method(container_id, request_json)
|
||||
except Exception as e:
|
||||
logger.error("error - container_post: %s" % str(e))
|
||||
res = {
|
||||
"type": "danger",
|
||||
"msg": str(e)
|
||||
}
|
||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||
|
||||
else:
|
||||
return jsonify(type='danger', msg='invalid container id or missing action')
|
||||
else:
|
||||
res = {
|
||||
"type": "danger",
|
||||
"msg": "invalid container id or missing action"
|
||||
}
|
||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||
|
||||
@app.post("/container/{container_id}/stats/update")
|
||||
async def post_container_update_stats(container_id : str):
|
||||
global containerIds_to_update
|
||||
|
||||
# start update task for container if no task is running
|
||||
if container_id not in containerIds_to_update:
|
||||
asyncio.create_task(get_container_stats(container_id))
|
||||
containerIds_to_update.append(container_id)
|
||||
|
||||
while True:
|
||||
if redis_client.exists(container_id + '_stats'):
|
||||
break
|
||||
await asyncio.sleep(1.5)
|
||||
|
||||
stats = json.loads(redis_client.get(container_id + '_stats'))
|
||||
return Response(content=json.dumps(stats, indent=4), media_type="application/json")
|
||||
|
||||
|
||||
|
||||
|
||||
class DockerUtils:
|
||||
def __init__(self, docker_client):
|
||||
self.docker_client = docker_client
|
||||
|
||||
# api call: container_post - post_action: stop
|
||||
def container_post__stop(self, container_id):
|
||||
for container in docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||
def container_post__stop(self, container_id, request_json):
|
||||
for container in self.docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||
container.stop()
|
||||
return jsonify(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")
|
||||
# api call: container_post - post_action: start
|
||||
def container_post__start(self, container_id):
|
||||
for container in docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||
def container_post__start(self, container_id, request_json):
|
||||
for container in self.docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||
container.start()
|
||||
return jsonify(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")
|
||||
# api call: container_post - post_action: restart
|
||||
def container_post__restart(self, container_id):
|
||||
for container in docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||
def container_post__restart(self, container_id, request_json):
|
||||
for container in self.docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||
container.restart()
|
||||
return jsonify(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")
|
||||
# api call: container_post - post_action: top
|
||||
def container_post__top(self, container_id):
|
||||
for container in docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||
return jsonify(type='success', msg=container.top())
|
||||
|
||||
|
||||
def container_post__top(self, container_id, request_json):
|
||||
for container in self.docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||
res = { 'type': 'success', 'msg': container.top()}
|
||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||
# api call: container_post - post_action: stats
|
||||
def container_post__stats(self, container_id):
|
||||
for container in docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||
def container_post__stats(self, container_id, request_json):
|
||||
for container in self.docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||
for stat in container.stats(decode=True, stream=True):
|
||||
return jsonify(type='success', msg=stat )
|
||||
|
||||
res = { 'type': 'success', 'msg': stat}
|
||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||
|
||||
# api call: container_post - post_action: exec - cmd: mailq - task: delete
|
||||
def container_post__exec__mailq__delete(self, container_id):
|
||||
if 'items' in request.json:
|
||||
def container_post__exec__mailq__delete(self, container_id, request_json):
|
||||
if 'items' in request_json:
|
||||
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:
|
||||
flagged_qids = ['-d %s' % i for i in filtered_qids]
|
||||
sanitized_string = str(' '.join(flagged_qids));
|
||||
|
||||
for container in docker_client.containers.list(filters={"id": container_id}):
|
||||
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)
|
||||
|
||||
|
||||
# api call: container_post - post_action: exec - cmd: mailq - task: hold
|
||||
def container_post__exec__mailq__hold(self, container_id):
|
||||
if 'items' in request.json:
|
||||
def container_post__exec__mailq__hold(self, container_id, request_json):
|
||||
if 'items' in request_json:
|
||||
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:
|
||||
flagged_qids = ['-h %s' % i for i in filtered_qids]
|
||||
sanitized_string = str(' '.join(flagged_qids));
|
||||
|
||||
for container in docker_client.containers.list(filters={"id": container_id}):
|
||||
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)
|
||||
|
||||
# api call: container_post - post_action: exec - cmd: mailq - task: cat
|
||||
def container_post__exec__mailq__cat(self, container_id):
|
||||
if 'items' in request.json:
|
||||
def container_post__exec__mailq__cat(self, container_id, request_json):
|
||||
if 'items' in request_json:
|
||||
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:
|
||||
sanitized_string = str(' '.join(filtered_qids));
|
||||
|
||||
for container in docker_client.containers.list(filters={"id": container_id}):
|
||||
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||
postcat_return = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postcat -q " + sanitized_string], user='postfix')
|
||||
if not postcat_return:
|
||||
postcat_return = 'err: invalid'
|
||||
return exec_run_handler('utf8_text_only', postcat_return)
|
||||
|
||||
# api call: container_post - post_action: exec - cmd: mailq - task: unhold
|
||||
def container_post__exec__mailq__unhold(self, container_id):
|
||||
if 'items' in request.json:
|
||||
def container_post__exec__mailq__unhold(self, container_id, request_json):
|
||||
if 'items' in request_json:
|
||||
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:
|
||||
flagged_qids = ['-H %s' % i for i in filtered_qids]
|
||||
sanitized_string = str(' '.join(flagged_qids));
|
||||
|
||||
for container in docker_client.containers.list(filters={"id": container_id}):
|
||||
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)
|
||||
|
||||
|
||||
# api call: container_post - post_action: exec - cmd: mailq - task: deliver
|
||||
def container_post__exec__mailq__deliver(self, container_id):
|
||||
if 'items' in request.json:
|
||||
def container_post__exec__mailq__deliver(self, container_id, request_json):
|
||||
if 'items' in request_json:
|
||||
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:
|
||||
flagged_qids = ['-i %s' % i for i in filtered_qids]
|
||||
|
||||
for container in docker_client.containers.list(filters={"id": container_id}):
|
||||
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||
for i in flagged_qids:
|
||||
postqueue_r = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postqueue " + i], user='postfix')
|
||||
# todo: check each exit code
|
||||
return jsonify(type='success', msg=str("Scheduled immediate delivery"))
|
||||
|
||||
|
||||
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
|
||||
def container_post__exec__mailq__list(self, container_id):
|
||||
for container in docker_client.containers.list(filters={"id": container_id}):
|
||||
def container_post__exec__mailq__list(self, container_id, request_json):
|
||||
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||
mailq_return = container.exec_run(["/usr/sbin/postqueue", "-j"], user='postfix')
|
||||
return exec_run_handler('utf8_text_only', mailq_return)
|
||||
|
||||
|
||||
# api call: container_post - post_action: exec - cmd: mailq - task: flush
|
||||
def container_post__exec__mailq__flush(self, container_id):
|
||||
for container in docker_client.containers.list(filters={"id": container_id}):
|
||||
def container_post__exec__mailq__flush(self, container_id, request_json):
|
||||
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||
postqueue_r = container.exec_run(["/usr/sbin/postqueue", "-f"], user='postfix')
|
||||
return exec_run_handler('generic', postqueue_r)
|
||||
|
||||
|
||||
# api call: container_post - post_action: exec - cmd: mailq - task: super_delete
|
||||
def container_post__exec__mailq__super_delete(self, container_id):
|
||||
for container in docker_client.containers.list(filters={"id": container_id}):
|
||||
def container_post__exec__mailq__super_delete(self, container_id, request_json):
|
||||
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||
postsuper_r = container.exec_run(["/usr/sbin/postsuper", "-d", "ALL"])
|
||||
return exec_run_handler('generic', postsuper_r)
|
||||
|
||||
|
||||
# api call: container_post - post_action: exec - cmd: system - task: fts_rescan
|
||||
def container_post__exec__system__fts_rescan(self, container_id):
|
||||
if 'username' in request.json:
|
||||
for container in docker_client.containers.list(filters={"id": container_id}):
|
||||
rescan_return = container.exec_run(["/bin/bash", "-c", "/usr/bin/doveadm fts rescan -u '" + request.json['username'].replace("'", "'\\''") + "'"], user='vmail')
|
||||
def container_post__exec__system__fts_rescan(self, container_id, request_json):
|
||||
if 'username' in request_json:
|
||||
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||
rescan_return = container.exec_run(["/bin/bash", "-c", "/usr/bin/doveadm fts rescan -u '" + request_json['username'].replace("'", "'\\''") + "'"], user='vmail')
|
||||
if rescan_return.exit_code == 0:
|
||||
return jsonify(type='success', msg='fts_rescan: rescan triggered')
|
||||
res = { 'type': 'success', 'msg': 'fts_rescan: rescan triggered'}
|
||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||
else:
|
||||
return jsonify(type='warning', msg='fts_rescan error')
|
||||
|
||||
if 'all' in request.json:
|
||||
for container in docker_client.containers.list(filters={"id": container_id}):
|
||||
res = { 'type': 'warning', 'msg': 'fts_rescan error'}
|
||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||
if 'all' in request_json:
|
||||
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||
rescan_return = container.exec_run(["/bin/bash", "-c", "/usr/bin/doveadm fts rescan -A"], user='vmail')
|
||||
if rescan_return.exit_code == 0:
|
||||
return jsonify(type='success', msg='fts_rescan: rescan triggered')
|
||||
res = { 'type': 'success', 'msg': 'fts_rescan: rescan triggered'}
|
||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||
else:
|
||||
return jsonify(type='warning', msg='fts_rescan error')
|
||||
|
||||
|
||||
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
|
||||
def container_post__exec__system__df(self, container_id):
|
||||
if 'dir' in request.json:
|
||||
for container in docker_client.containers.list(filters={"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')
|
||||
def container_post__exec__system__df(self, container_id, request_json):
|
||||
if 'dir' in request_json:
|
||||
for container in self.docker_client.containers.list(filters={"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')
|
||||
if df_return.exit_code == 0:
|
||||
return df_return.output.decode('utf-8').rstrip()
|
||||
else:
|
||||
return "0,0,0,0,0,0"
|
||||
|
||||
|
||||
# api call: container_post - post_action: exec - cmd: system - task: mysql_upgrade
|
||||
def container_post__exec__system__mysql_upgrade(self, container_id):
|
||||
for container in docker_client.containers.list(filters={"id": container_id}):
|
||||
def container_post__exec__system__mysql_upgrade(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_upgrade -uroot -p'" + os.environ['DBROOT'].replace("'", "'\\''") + "'\n"], user='mysql')
|
||||
if sql_return.exit_code == 0:
|
||||
matched = False
|
||||
@@ -235,103 +328,96 @@ class container_post(Resource):
|
||||
if 'is already upgraded to' in line:
|
||||
matched = True
|
||||
if matched:
|
||||
return jsonify(type='success', msg='mysql_upgrade: already upgraded', text=sql_return.output.decode('utf-8'))
|
||||
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()
|
||||
return jsonify(type='warning', msg='mysql_upgrade: upgrade was applied', text=sql_return.output.decode('utf-8'))
|
||||
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:
|
||||
return jsonify(type='error', msg='mysql_upgrade: error running command', text=sql_return.output.decode('utf-8'))
|
||||
|
||||
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):
|
||||
for container in docker_client.containers.list(filters={"id": container_id}):
|
||||
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:
|
||||
return jsonify(type='info', msg='mysql_tzinfo_to_sql: command completed successfully', text=sql_return.output.decode('utf-8'))
|
||||
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:
|
||||
return jsonify(type='error', msg='mysql_tzinfo_to_sql: error running command', text=sql_return.output.decode('utf-8'))
|
||||
|
||||
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):
|
||||
for container in docker_client.containers.list(filters={"id": container_id}):
|
||||
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):
|
||||
for container in docker_client.containers.list(filters={"id": container_id}):
|
||||
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):
|
||||
for container in docker_client.containers.list(filters={"id": container_id}):
|
||||
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):
|
||||
if 'username' in request.json:
|
||||
for container in 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("'", "'\\''") + "'"])
|
||||
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):
|
||||
if 'username' in request.json and 'script_name' in request.json:
|
||||
for container in 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("'", "'\\''") + "'"]
|
||||
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):
|
||||
if 'maildir' in request.json:
|
||||
for container in docker_client.containers.list(filters={"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"]
|
||||
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'])
|
||||
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 = 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):
|
||||
if 'raw' in request.json:
|
||||
for container in docker_client.containers.list(filters={"id": container_id}):
|
||||
cmd = "/usr/bin/rspamadm pw -e -p '" + request.json['raw'].replace("'", "'\\''") + "' 2> /dev/null"
|
||||
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")
|
||||
|
||||
matched = False
|
||||
for line in cmd_response.split("\n"):
|
||||
if '$2$' in line:
|
||||
hash = line.strip()
|
||||
hash_out = re.search('\$2\$.+$', hash).group(0)
|
||||
rspamd_passphrase_hash = re.sub('[^0-9a-zA-Z\$]+', '', hash_out.rstrip())
|
||||
|
||||
rspamd_password_filename = "/etc/rspamd/override.d/worker-controller-password.inc"
|
||||
cmd = '''/bin/echo 'enable_password = "%s";' > %s && cat %s''' % (rspamd_passphrase_hash, rspamd_password_filename, rspamd_password_filename)
|
||||
cmd_response = exec_cmd_container(container, cmd, user="_rspamd")
|
||||
|
||||
if rspamd_passphrase_hash.startswith("$2$") and rspamd_passphrase_hash in cmd_response:
|
||||
container.restart()
|
||||
matched = True
|
||||
|
||||
if matched:
|
||||
return jsonify(type='success', msg='command completed successfully')
|
||||
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:
|
||||
return jsonify(type='danger', msg='command did not complete')
|
||||
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"):
|
||||
|
||||
def recv_socket_data(c_socket, timeout):
|
||||
c_socket.setblocking(0)
|
||||
total_data=[];
|
||||
data='';
|
||||
total_data=[]
|
||||
data=''
|
||||
begin=time.time()
|
||||
while True:
|
||||
if total_data and time.time()-begin > timeout:
|
||||
@@ -351,6 +437,7 @@ def exec_cmd_container(container, cmd, user, timeout=2, shell_cmd="/bin/bash"):
|
||||
except:
|
||||
pass
|
||||
return ''.join(total_data)
|
||||
|
||||
|
||||
try :
|
||||
socket = container.exec_run([shell_cmd], stdin=True, socket=True, user=user).output._sock
|
||||
@@ -360,60 +447,93 @@ def exec_cmd_container(container, cmd, user, timeout=2, shell_cmd="/bin/bash"):
|
||||
data = recv_socket_data(socket, timeout)
|
||||
socket.close()
|
||||
return data
|
||||
|
||||
except Exception as e:
|
||||
print("error - exec_cmd_container: %s" % str(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 output.exit_code == 0:
|
||||
return jsonify(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")
|
||||
else:
|
||||
return jsonify(type='danger', msg='command failed: ' + output.output.decode('utf-8'))
|
||||
res = { 'type': 'danger', 'msg': 'command failed: ' + output.output.decode('utf-8') }
|
||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||
if type == 'utf8_text_only':
|
||||
r = Response(response=output.output.decode('utf-8'), status=200, mimetype="text/plain")
|
||||
r.headers["Content-Type"] = "text/plain; charset=utf-8"
|
||||
return r
|
||||
return Response(content=output.output.decode('utf-8'), media_type="text/plain")
|
||||
|
||||
class GracefulKiller:
|
||||
kill_now = False
|
||||
def __init__(self):
|
||||
signal.signal(signal.SIGINT, self.exit_gracefully)
|
||||
signal.signal(signal.SIGTERM, self.exit_gracefully)
|
||||
async def get_host_stats(wait=5):
|
||||
global host_stats_isUpdating
|
||||
|
||||
def exit_gracefully(self, signum, frame):
|
||||
self.kill_now = True
|
||||
|
||||
def create_self_signed_cert():
|
||||
process = subprocess.Popen(
|
||||
"openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes -keyout /app/dockerapi_key.pem -out /app/dockerapi_cert.pem -subj /CN=dockerapi/O=mailcow -addext subjectAltName=DNS:dockerapi".split(),
|
||||
stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell=False
|
||||
)
|
||||
process.wait()
|
||||
|
||||
def startFlaskAPI():
|
||||
create_self_signed_cert()
|
||||
try:
|
||||
ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||
ctx.check_hostname = False
|
||||
ctx.load_cert_chain(certfile='/app/dockerapi_cert.pem', keyfile='/app/dockerapi_key.pem')
|
||||
except:
|
||||
print ("Cannot initialize TLS, retrying in 5s...")
|
||||
time.sleep(5)
|
||||
app.run(debug=False, host='0.0.0.0', port=443, threaded=True, ssl_context=ctx)
|
||||
system_time = datetime.now()
|
||||
host_stats = {
|
||||
"cpu": {
|
||||
"cores": psutil.cpu_count(),
|
||||
"usage": psutil.cpu_percent()
|
||||
},
|
||||
"memory": {
|
||||
"total": psutil.virtual_memory().total,
|
||||
"usage": psutil.virtual_memory().percent,
|
||||
"swap": psutil.swap_memory()
|
||||
},
|
||||
"uptime": time.time() - psutil.boot_time(),
|
||||
"system_time": system_time.strftime("%d.%m.%Y %H:%M:%S")
|
||||
}
|
||||
|
||||
api.add_resource(containers_get, '/containers/json')
|
||||
api.add_resource(container_get, '/containers/<string:container_id>/json')
|
||||
api.add_resource(container_post, '/containers/<string:container_id>/<string:post_action>')
|
||||
redis_client.set('host_stats', json.dumps(host_stats), ex=10)
|
||||
except Exception as e:
|
||||
res = {
|
||||
"type": "danger",
|
||||
"msg": str(e)
|
||||
}
|
||||
|
||||
if __name__ == '__main__':
|
||||
api_thread = Thread(target=startFlaskAPI)
|
||||
api_thread.daemon = True
|
||||
api_thread.start()
|
||||
killer = GracefulKiller()
|
||||
while True:
|
||||
time.sleep(1)
|
||||
if killer.kill_now:
|
||||
break
|
||||
print ("Stopping dockerapi-mailcow")
|
||||
await asyncio.sleep(wait)
|
||||
host_stats_isUpdating = False
|
||||
|
||||
async def get_container_stats(container_id, wait=5, stop=False):
|
||||
global containerIds_to_update
|
||||
|
||||
if container_id and container_id.isalnum():
|
||||
try:
|
||||
for container in (await async_docker_client.containers.list()):
|
||||
if container._id == container_id:
|
||||
res = await container.stats(stream=False)
|
||||
|
||||
if redis_client.exists(container_id + '_stats'):
|
||||
stats = json.loads(redis_client.get(container_id + '_stats'))
|
||||
else:
|
||||
stats = []
|
||||
stats.append(res[0])
|
||||
if len(stats) > 3:
|
||||
del stats[0]
|
||||
redis_client.set(container_id + '_stats', json.dumps(stats), ex=60)
|
||||
except Exception as e:
|
||||
res = {
|
||||
"type": "danger",
|
||||
"msg": str(e)
|
||||
}
|
||||
else:
|
||||
res = {
|
||||
"type": "danger",
|
||||
"msg": "no or invalid id defined"
|
||||
}
|
||||
|
||||
await asyncio.sleep(wait)
|
||||
if stop == True:
|
||||
# update task was called second time, stop
|
||||
containerIds_to_update.remove(container_id)
|
||||
else:
|
||||
# call update task a second time
|
||||
await get_container_stats(container_id, wait=0, stop=True)
|
||||
|
||||
|
||||
|
||||
if os.environ['REDIS_SLAVEOF_IP'] != "":
|
||||
redis_client = redis.Redis(host=os.environ['REDIS_SLAVEOF_IP'], port=os.environ['REDIS_SLAVEOF_PORT'], db=0)
|
||||
else:
|
||||
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')
|
||||
|
||||
logger.info('DockerApi started')
|
||||
@@ -2,9 +2,12 @@ FROM debian:bullseye-slim
|
||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||
|
||||
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 GOSU_VERSION 1.14
|
||||
|
||||
|
||||
# Add groups and users before installing Dovecot to not break compatibility
|
||||
RUN groupadd -g 5000 vmail \
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.16
|
||||
FROM alpine:3.17
|
||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||
|
||||
ENV XTABLES_LIBDIR /usr/lib/xtables
|
||||
|
||||
@@ -97,9 +97,9 @@ def refreshF2bregex():
|
||||
f2bregex[3] = 'warning: .*\[([0-9a-f\.:]+)\]: SASL .+ authentication failed: (?!.*Connection lost to authentication server).+'
|
||||
f2bregex[4] = 'warning: non-SMTP command from .*\[([0-9a-f\.:]+)]:.+'
|
||||
f2bregex[5] = 'NOQUEUE: reject: RCPT from \[([0-9a-f\.:]+)].+Protocol error.+'
|
||||
f2bregex[6] = '-login: Disconnected \(auth failed, .+\): user=.*, method=.+, rip=([0-9a-f\.:]+),'
|
||||
f2bregex[7] = '-login: Aborted login \(auth failed .+\): user=.+, rip=([0-9a-f\.:]+), lip.+'
|
||||
f2bregex[8] = '-login: Aborted login \(tried to use disallowed .+\): user=.+, rip=([0-9a-f\.:]+), lip.+'
|
||||
f2bregex[6] = '-login: Disconnected.+ \(auth failed, .+\): user=.*, method=.+, rip=([0-9a-f\.:]+),'
|
||||
f2bregex[7] = '-login: Aborted login.+ \(auth failed .+\): user=.+, rip=([0-9a-f\.:]+), lip.+'
|
||||
f2bregex[8] = '-login: Aborted login.+ \(tried to use disallowed .+\): user=.+, rip=([0-9a-f\.:]+), lip.+'
|
||||
f2bregex[9] = 'SOGo.+ Login from \'([0-9a-f\.:]+)\' for user .+ might not have worked'
|
||||
f2bregex[10] = '([0-9a-f\.:]+) \"GET \/SOGo\/.* HTTP.+\" 403 .+'
|
||||
r.set('F2B_REGEX', json.dumps(f2bregex, ensure_ascii=False))
|
||||
@@ -359,21 +359,28 @@ def snat4(snat_target):
|
||||
chain = iptc.Chain(table, 'POSTROUTING')
|
||||
table.autocommit = False
|
||||
new_rule = get_snat4_rule()
|
||||
for position, rule in enumerate(chain.rules):
|
||||
match = all((
|
||||
new_rule.get_src() == rule.get_src(),
|
||||
new_rule.get_dst() == rule.get_dst(),
|
||||
new_rule.target.parameters == rule.target.parameters,
|
||||
new_rule.target.name == rule.target.name
|
||||
))
|
||||
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)
|
||||
|
||||
if not chain.rules:
|
||||
# if there are no rules in the chain, insert the new rule directly
|
||||
logInfo(f'Added POSTROUTING rule for source network {new_rule.src} to SNAT target {snat_target}')
|
||||
chain.insert_rule(new_rule)
|
||||
else:
|
||||
for position, rule in enumerate(chain.rules):
|
||||
match = all((
|
||||
new_rule.get_src() == rule.get_src(),
|
||||
new_rule.get_dst() == rule.get_dst(),
|
||||
new_rule.target.parameters == rule.target.parameters,
|
||||
new_rule.target.name == rule.target.name
|
||||
))
|
||||
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.autocommit = True
|
||||
except:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.16
|
||||
FROM alpine:3.17
|
||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
FROM php:8.1-fpm-alpine3.16
|
||||
FROM php:8.1-fpm-alpine3.17
|
||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||
|
||||
ENV APCU_PECL 5.1.22
|
||||
ENV IMAGICK_PECL 3.7.0
|
||||
ENV MAILPARSE_PECL 3.1.4
|
||||
ENV MEMCACHED_PECL 3.2.0
|
||||
ENV REDIS_PECL 5.3.7
|
||||
ENV COMPOSER 2.4.4
|
||||
# renovate: datasource=github-tags depName=krakjoe/apcu versioning=semver-coerced
|
||||
ARG APCU_PECL_VERSION=5.1.22
|
||||
# renovate: datasource=github-tags depName=Imagick/imagick versioning=semver-coerced
|
||||
ARG IMAGICK_PECL_VERSION=3.7.0
|
||||
# renovate: datasource=github-tags depName=php/pecl-mail-mailparse versioning=semver-coerced
|
||||
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.4
|
||||
|
||||
RUN apk add -U --no-cache autoconf \
|
||||
aspell-dev \
|
||||
@@ -55,11 +61,11 @@ RUN apk add -U --no-cache autoconf \
|
||||
samba-client \
|
||||
zlib-dev \
|
||||
tzdata \
|
||||
&& pecl install mailparse-${MAILPARSE_PECL} \
|
||||
&& pecl install redis-${REDIS_PECL} \
|
||||
&& pecl install memcached-${MEMCACHED_PECL} \
|
||||
&& pecl install APCu-${APCU_PECL} \
|
||||
&& pecl install imagick-${IMAGICK_PECL} \
|
||||
&& pecl install APCu-${APCU_PECL_VERSION} \
|
||||
&& pecl install imagick-${IMAGICK_PECL_VERSION} \
|
||||
&& pecl install mailparse-${MAILPARSE_PECL_VERSION} \
|
||||
&& pecl install memcached-${MEMCACHED_PECL_VERSION} \
|
||||
&& pecl install redis-${REDIS_PECL_VERSION} \
|
||||
&& docker-php-ext-enable apcu imagick memcached mailparse redis \
|
||||
&& pecl clear-cache \
|
||||
&& docker-php-ext-configure intl \
|
||||
@@ -72,7 +78,7 @@ RUN apk add -U --no-cache autoconf \
|
||||
&& 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-configure imap --with-imap --with-imap-ssl \
|
||||
&& 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 \
|
||||
&& chmod +x /usr/local/bin/composer \
|
||||
&& apk del --purge autoconf \
|
||||
@@ -102,4 +108,4 @@ COPY ./docker-entrypoint.sh /
|
||||
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
|
||||
CMD ["php-fpm"]
|
||||
CMD ["php-fpm"]
|
||||
@@ -26,6 +26,7 @@ RUN apt-get update && apt-get install -y \
|
||||
|
||||
COPY settings.conf /etc/rspamd/settings.conf
|
||||
COPY metadata_exporter.lua /usr/share/rspamd/plugins/metadata_exporter.lua
|
||||
COPY set_worker_password.sh /set_worker_password.sh
|
||||
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
password_file='/etc/rspamd/override.d/worker-controller-password.inc'
|
||||
password_hash=`/usr/bin/rspamadm pw -e -p $1`
|
||||
|
||||
echo 'enable_password = "'$password_hash'";' > $password_file
|
||||
|
||||
if grep -q "$password_hash" "$password_file"; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "ERROR"
|
||||
fi
|
||||
@@ -3,8 +3,9 @@ LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
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 GOSU_VERSION 1.14
|
||||
|
||||
# Prerequisites
|
||||
RUN echo "Building from repository $SOGO_DEBIAN_REPOSITORY" \
|
||||
|
||||
@@ -2,7 +2,8 @@ FROM solr:7.7-slim
|
||||
|
||||
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-config-7.7.0.xml /
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.16
|
||||
FROM alpine:3.17
|
||||
|
||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.16
|
||||
FROM alpine:3.17
|
||||
LABEL maintainer "André Peters <andre.peters@servercow.de>"
|
||||
|
||||
# Installation
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
/.*episerver.*/i
|
||||
/.*supergewinne.*/i
|
||||
/List-Unsubscribe.*nbps\.eu/i
|
||||
/X-Mailer: AWeber.*/i
|
||||
/.*regiofinder.*/i
|
||||
/.*EmailSocket.*/i
|
||||
/List-Unsubscribe:.*respread.*/i
|
||||
|
||||
@@ -175,7 +175,7 @@ BAD_SUBJECT_00 {
|
||||
type = "header";
|
||||
header = "subject";
|
||||
regexp = true;
|
||||
map = "http://nullnull.org/bad-subject-regex.txt";
|
||||
map = "http://fuzzy.mailcow.email/bad-subject-regex.txt";
|
||||
score = 6.0;
|
||||
symbols_set = ["BAD_SUBJECT_00"];
|
||||
}
|
||||
@@ -54,6 +54,7 @@ $rcpts = $headers['X-Rspamd-Rcpt'];
|
||||
$sender = $headers['X-Rspamd-From'];
|
||||
$ip = $headers['X-Rspamd-Ip'];
|
||||
$subject = $headers['X-Rspamd-Subject'];
|
||||
$messageid= $json_body->message_id;
|
||||
$priority = 0;
|
||||
|
||||
$symbols_array = json_decode($headers['X-Rspamd-Symbols'], true);
|
||||
@@ -245,13 +246,13 @@ foreach ($rcpt_final_mailboxes as $rcpt_final) {
|
||||
"token" => $api_data['token'],
|
||||
"user" => $api_data['key'],
|
||||
"title" => sprintf("%s", str_replace(
|
||||
array('{SUBJECT}', '{SENDER}', '{SENDER_NAME}', '{SENDER_ADDRESS}', '{TO_NAME}', '{TO_ADDRESS}'),
|
||||
array($subject, $sender, $sender_name, $sender_address, $to_name, $to_address), $title)
|
||||
array('{SUBJECT}', '{SENDER}', '{SENDER_NAME}', '{SENDER_ADDRESS}', '{TO_NAME}', '{TO_ADDRESS}', '{MSG_ID}'),
|
||||
array($subject, $sender, $sender_name, $sender_address, $to_name, $to_address, $messageid), $title)
|
||||
),
|
||||
"priority" => $priority,
|
||||
"message" => sprintf("%s", str_replace(
|
||||
array('{SUBJECT}', '{SENDER}', '{SENDER_NAME}', '{SENDER_ADDRESS}', '{TO_NAME}', '{TO_ADDRESS}', '\n'),
|
||||
array($subject, $sender, $sender_name, $sender_address, $to_name, $to_address, PHP_EOL), $text)
|
||||
array('{SUBJECT}', '{SENDER}', '{SENDER_NAME}', '{SENDER_ADDRESS}', '{TO_NAME}', '{TO_ADDRESS}', '{MSG_ID}', '\n'),
|
||||
array($subject, $sender, $sender_name, $sender_address, $to_name, $to_address, $messageid, PHP_EOL), $text)
|
||||
),
|
||||
"sound" => $attributes['sound'] ?? "pushover"
|
||||
);
|
||||
|
||||
@@ -13,12 +13,12 @@
|
||||
Please check the logs or contact support if the error persists.</p>
|
||||
<h2>Quick debugging</h2>
|
||||
<p>Check Nginx and PHP logs:</p>
|
||||
<pre>docker-compose logs --tail=200 php-fpm-mailcow nginx-mailcow</pre>
|
||||
<pre>docker compose logs --tail=200 php-fpm-mailcow nginx-mailcow</pre>
|
||||
<p>Make sure your SQL credentials in mailcow.conf (a link to .env) do fit your initialized SQL volume. If you see an access denied, you might have the wrong mailcow.conf:</p>
|
||||
<pre>source mailcow.conf ; docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME}</pre>
|
||||
<pre>source mailcow.conf ; docker compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME}</pre>
|
||||
<p>In case of a previous failed installation, create a backup of your existing data, followed by removing all volumes and starting over (<b>NEVER</b> do this with a production system, it will remove <b>ALL</b> data):</p>
|
||||
<pre>BACKUP_LOCATION=/tmp/ ./helper-scripts/backup_and_restore.sh backup all</pre>
|
||||
<pre>docker-compose down --volumes ; docker-compose up -d</pre>
|
||||
<pre>docker compose down --volumes ; docker compose up -d</pre>
|
||||
<p>Make sure your timezone is correct. Use "America/New_York" for example, do not use spaces. Check <a href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones">here</a> for a list.</p>
|
||||
<br>Click to learn more about <a style="color:red;text-decoration:none;" href="https://mailcow.github.io/mailcow-dockerized-docs/#get-support" target="_blank">getting support.</a>
|
||||
</body>
|
||||
|
||||
+2
-6
@@ -10,9 +10,6 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/header.inc.php';
|
||||
$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
||||
$tfa_data = get_tfa();
|
||||
$fido2_data = fido2(array("action" => "get_friendly_names"));
|
||||
if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CACHE')) {
|
||||
$_SESSION['gal'] = json_decode($license_cache, true);
|
||||
}
|
||||
|
||||
$js_minifier->add('/web/js/site/admin.js');
|
||||
$js_minifier->add('/web/js/presets/rspamd.js');
|
||||
@@ -83,15 +80,12 @@ foreach ($RSPAMD_MAPS['regex'] as $rspamd_regex_desc => $rspamd_regex_map) {
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
$template = 'admin.twig';
|
||||
$template_data = [
|
||||
'tfa_data' => $tfa_data,
|
||||
'tfa_id' => @$_SESSION['tfa_id'],
|
||||
'fido2_cid' => @$_SESSION['fido2_cid'],
|
||||
'fido2_data' => $fido2_data,
|
||||
'gal' => @$_SESSION['gal'],
|
||||
'license_guid' => license('guid'),
|
||||
'api' => [
|
||||
'ro' => admin_api('ro', 'get'),
|
||||
'rw' => admin_api('rw', 'get'),
|
||||
@@ -109,9 +103,11 @@ $template_data = [
|
||||
'rsettings' => $rsettings,
|
||||
'rspamd_regex_maps' => $rspamd_regex_maps,
|
||||
'logo_specs' => customize('get', 'main_logo_specs'),
|
||||
'ip_check' => customize('get', 'ip_check'),
|
||||
'password_complexity' => password_complexity('get'),
|
||||
'show_rspamd_global_filters' => @$_SESSION['show_rspamd_global_filters'],
|
||||
'lang_admin' => json_encode($lang['admin']),
|
||||
'lang_datatables' => json_encode($lang['datatables'])
|
||||
];
|
||||
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/footer.inc.php';
|
||||
@@ -699,6 +699,38 @@ paths:
|
||||
type: string
|
||||
type: object
|
||||
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:
|
||||
post:
|
||||
responses:
|
||||
@@ -1999,7 +2031,7 @@ paths:
|
||||
- domain.tld
|
||||
- domain2.tld
|
||||
properties:
|
||||
items:
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
@@ -2993,7 +3025,7 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
log:
|
||||
@@ -5586,6 +5618,8 @@ tags:
|
||||
description: Manage DKIM keys
|
||||
- name: Domain admin
|
||||
description: Create or udpdate domain admin users
|
||||
- name: Single Sign-On
|
||||
description: Issue tokens for users
|
||||
- name: Address Rewriting
|
||||
description: Create BCC maps or recipient maps
|
||||
- name: Outgoing TLS Policy Map Overrides
|
||||
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
/*!
|
||||
* bootswatch v3.4.1
|
||||
* Homepage: http://bootswatch.com
|
||||
* Copyright 2012-2019 Thomas Park
|
||||
* Licensed under MIT
|
||||
* Based on Bootstrap
|
||||
*//*!
|
||||
* Bootstrap v3.4.1 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2019 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}*{box-sizing:border-box}:after,:before{box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:transparent}body{font-family:"Source Sans Pro","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#158cba;text-decoration:none}a:focus,a:hover{color:#158cba;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:5px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #eee;border-radius:4px;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:400;line-height:1.1;color:#333}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#999}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-Line truncated
|
||||
+1
-1
@@ -1 +1 @@
|
||||
@media (max-width:1050px){.navbar-header{float:none}.navbar-left,.navbar-nav,.navbar-right{float:none!important}.navbar-toggle{display:block}.navbar-collapse{border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-collapse.collapse{display:none!important}.navbar-nav{margin-top:7.5px}.navbar-nav>li{float:none}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px}.collapse.in{display:block!important}.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}}
|
||||
@media (max-width:1050px){.navbar-header{float:none}.navbar-left,.navbar-nav,.navbar-right{float:none!important}.navbar-toggle{display:block}.navbar-collapse{border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-nav{margin-top:7.5px}.navbar-nav>li{float:none}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px}.collapse.in{display:block!important}.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}}
|
||||
@@ -0,0 +1,487 @@
|
||||
/*!
|
||||
* Bootstrap-select v1.14.0-beta2 (https://developer.snapappointments.com/bootstrap-select)
|
||||
*
|
||||
* Copyright 2012-2021 SnapAppointments, LLC
|
||||
* Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
@-webkit-keyframes bs-notify-fadeOut {
|
||||
0% {
|
||||
opacity: 0.9;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@-o-keyframes bs-notify-fadeOut {
|
||||
0% {
|
||||
opacity: 0.9;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@keyframes bs-notify-fadeOut {
|
||||
0% {
|
||||
opacity: 0.9;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
select.bs-select-hidden,
|
||||
.bootstrap-select > select.bs-select-hidden,
|
||||
select.selectpicker {
|
||||
display: none !important;
|
||||
}
|
||||
.bootstrap-select {
|
||||
width: 220px \0;
|
||||
/*IE9 and below*/
|
||||
vertical-align: middle;
|
||||
}
|
||||
.bootstrap-select > .dropdown-toggle {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
display: -webkit-inline-box;
|
||||
display: -webkit-inline-flex;
|
||||
display: -ms-inline-flexbox;
|
||||
display: inline-flex;
|
||||
-webkit-box-align: center;
|
||||
-webkit-align-items: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: justify;
|
||||
-webkit-justify-content: space-between;
|
||||
-ms-flex-pack: justify;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.bootstrap-select > .dropdown-toggle:after {
|
||||
margin-top: -1px;
|
||||
}
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder:hover,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder:focus,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder:active {
|
||||
color: #999;
|
||||
}
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-primary,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-secondary,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-success,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-danger,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-info,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-dark,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-primary:hover,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-secondary:hover,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-success:hover,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-danger:hover,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-info:hover,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-dark:hover,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-primary:focus,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-secondary:focus,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-success:focus,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-danger:focus,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-info:focus,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-dark:focus,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-primary:active,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-secondary:active,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-success:active,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-danger:active,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-info:active,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-dark:active {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
.bootstrap-select > select {
|
||||
position: absolute !important;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
display: block !important;
|
||||
width: 0.5px !important;
|
||||
height: 100% !important;
|
||||
padding: 0 !important;
|
||||
opacity: 0 !important;
|
||||
border: none;
|
||||
z-index: 0 !important;
|
||||
}
|
||||
.bootstrap-select > select.mobile-device {
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: block !important;
|
||||
width: 100% !important;
|
||||
z-index: 2 !important;
|
||||
}
|
||||
.has-error .bootstrap-select .dropdown-toggle,
|
||||
.error .bootstrap-select .dropdown-toggle,
|
||||
.bootstrap-select.is-invalid .dropdown-toggle,
|
||||
.was-validated .bootstrap-select select:invalid + .dropdown-toggle {
|
||||
border-color: #b94a48;
|
||||
}
|
||||
.bootstrap-select.is-valid .dropdown-toggle,
|
||||
.was-validated .bootstrap-select select:valid + .dropdown-toggle {
|
||||
border-color: #28a745;
|
||||
}
|
||||
.bootstrap-select.fit-width {
|
||||
width: auto !important;
|
||||
}
|
||||
.bootstrap-select:not([class*="col-"]):not([class*="form-control"]):not(.input-group-btn) {
|
||||
width: 220px;
|
||||
}
|
||||
.bootstrap-select > select.mobile-device:focus + .dropdown-toggle,
|
||||
.bootstrap-select .dropdown-toggle:focus {
|
||||
outline: thin dotted #333333 !important;
|
||||
outline: 5px auto -webkit-focus-ring-color !important;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
.bootstrap-select.form-control {
|
||||
margin-bottom: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
height: auto;
|
||||
}
|
||||
:not(.input-group) > .bootstrap-select.form-control:not([class*="col-"]) {
|
||||
width: 100%;
|
||||
}
|
||||
.bootstrap-select.form-control.input-group-btn {
|
||||
float: none;
|
||||
z-index: auto;
|
||||
}
|
||||
.form-inline .bootstrap-select,
|
||||
.form-inline .bootstrap-select.form-control:not([class*="col-"]) {
|
||||
width: auto;
|
||||
}
|
||||
.bootstrap-select:not(.input-group-btn),
|
||||
.bootstrap-select[class*="col-"] {
|
||||
float: none;
|
||||
display: inline-block;
|
||||
margin-left: 0;
|
||||
}
|
||||
.bootstrap-select.dropdown-menu-right,
|
||||
.bootstrap-select[class*="col-"].dropdown-menu-right,
|
||||
.row .bootstrap-select[class*="col-"].dropdown-menu-right {
|
||||
float: right;
|
||||
}
|
||||
.form-inline .bootstrap-select,
|
||||
.form-horizontal .bootstrap-select,
|
||||
.form-group .bootstrap-select {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.form-group-lg .bootstrap-select.form-control,
|
||||
.form-group-sm .bootstrap-select.form-control {
|
||||
padding: 0;
|
||||
}
|
||||
.form-group-lg .bootstrap-select.form-control .dropdown-toggle,
|
||||
.form-group-sm .bootstrap-select.form-control .dropdown-toggle {
|
||||
height: 100%;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
border-radius: inherit;
|
||||
}
|
||||
.bootstrap-select.form-control-sm .dropdown-toggle,
|
||||
.bootstrap-select.form-control-lg .dropdown-toggle {
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
border-radius: inherit;
|
||||
}
|
||||
.bootstrap-select.form-control-sm .dropdown-toggle {
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
.bootstrap-select.form-control-lg .dropdown-toggle {
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
.form-inline .bootstrap-select .form-control {
|
||||
width: 100%;
|
||||
}
|
||||
.bootstrap-select.disabled,
|
||||
.bootstrap-select > .disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.bootstrap-select.disabled:focus,
|
||||
.bootstrap-select > .disabled:focus {
|
||||
outline: none !important;
|
||||
}
|
||||
.bootstrap-select.bs-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.bootstrap-select.bs-container .dropdown-menu {
|
||||
z-index: 1060;
|
||||
}
|
||||
.bootstrap-select .dropdown-toggle .filter-option {
|
||||
position: static;
|
||||
top: 0;
|
||||
left: 0;
|
||||
float: left;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
-webkit-box-flex: 0;
|
||||
-webkit-flex: 0 1 auto;
|
||||
-ms-flex: 0 1 auto;
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
.bs3.bootstrap-select .dropdown-toggle .filter-option {
|
||||
padding-right: inherit;
|
||||
}
|
||||
.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option {
|
||||
position: absolute;
|
||||
padding-top: inherit;
|
||||
padding-bottom: inherit;
|
||||
padding-left: inherit;
|
||||
float: none;
|
||||
}
|
||||
.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option .filter-option-inner {
|
||||
padding-right: inherit;
|
||||
}
|
||||
.bootstrap-select .dropdown-toggle .filter-option-inner-inner {
|
||||
overflow: hidden;
|
||||
}
|
||||
.bootstrap-select .dropdown-toggle .filter-expand {
|
||||
width: 0 !important;
|
||||
float: left;
|
||||
opacity: 0 !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
.bootstrap-select .dropdown-toggle .caret {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 12px;
|
||||
margin-top: -2px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.bootstrap-select .dropdown-toggle .bs-select-clear-selected {
|
||||
position: relative;
|
||||
display: block;
|
||||
margin-right: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
.bs3.bootstrap-select .dropdown-toggle .bs-select-clear-selected {
|
||||
padding-right: inherit;
|
||||
}
|
||||
.bootstrap-select .dropdown-toggle .bs-select-clear-selected span {
|
||||
position: relative;
|
||||
top: -webkit-calc(((-1em / 1.5) + 1ex) / 2);
|
||||
top: calc(((-1em / 1.5) + 1ex) / 2);
|
||||
pointer-events: none;
|
||||
}
|
||||
.bs3.bootstrap-select .dropdown-toggle .bs-select-clear-selected span {
|
||||
top: auto;
|
||||
}
|
||||
.bootstrap-select .dropdown-toggle.bs-placeholder .bs-select-clear-selected {
|
||||
display: none;
|
||||
}
|
||||
.input-group .bootstrap-select.form-control .dropdown-toggle {
|
||||
border-radius: inherit;
|
||||
}
|
||||
.bootstrap-select[class*="col-"] .dropdown-toggle {
|
||||
width: 100%;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu {
|
||||
min-width: 100%;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu > .inner:focus {
|
||||
outline: none !important;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu.inner {
|
||||
position: static;
|
||||
float: none;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu li {
|
||||
position: relative;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu li.active small {
|
||||
color: rgba(255, 255, 255, 0.5) !important;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu li.disabled a {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu li a {
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu li a.opt {
|
||||
position: relative;
|
||||
padding-left: 2.25em;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu li a span.check-mark {
|
||||
display: none;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu li a span.text {
|
||||
display: inline-block;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu li small {
|
||||
padding-left: 0.5em;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu .notify {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
width: 96%;
|
||||
margin: 0 2%;
|
||||
min-height: 26px;
|
||||
padding: 3px 5px;
|
||||
background: #f5f5f5;
|
||||
border: 1px solid #e3e3e3;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
|
||||
pointer-events: none;
|
||||
opacity: 0.9;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu .notify.fadeOut {
|
||||
-webkit-animation: 300ms linear 750ms forwards bs-notify-fadeOut;
|
||||
-o-animation: 300ms linear 750ms forwards bs-notify-fadeOut;
|
||||
animation: 300ms linear 750ms forwards bs-notify-fadeOut;
|
||||
}
|
||||
.bootstrap-select .no-results {
|
||||
padding: 3px;
|
||||
background: #f5f5f5;
|
||||
margin: 0 5px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.bootstrap-select.fit-width .dropdown-toggle .filter-option {
|
||||
position: static;
|
||||
display: inline;
|
||||
padding: 0;
|
||||
}
|
||||
.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner,
|
||||
.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner-inner {
|
||||
display: inline;
|
||||
}
|
||||
.bootstrap-select.fit-width .dropdown-toggle .bs-caret:before {
|
||||
content: '\00a0';
|
||||
}
|
||||
.bootstrap-select.fit-width .dropdown-toggle .caret {
|
||||
position: static;
|
||||
top: auto;
|
||||
margin-top: -1px;
|
||||
}
|
||||
.bootstrap-select.show-tick .dropdown-menu .selected span.check-mark {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
right: 15px;
|
||||
top: 5px;
|
||||
}
|
||||
.bootstrap-select.show-tick .dropdown-menu li a span.text {
|
||||
margin-right: 34px;
|
||||
}
|
||||
.bootstrap-select .bs-ok-default:after {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 0.5em;
|
||||
height: 1em;
|
||||
border-style: solid;
|
||||
border-width: 0 0.26em 0.26em 0;
|
||||
-webkit-transform-style: preserve-3d;
|
||||
transform-style: preserve-3d;
|
||||
-webkit-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
-o-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.bootstrap-select.show-menu-arrow.open > .dropdown-toggle,
|
||||
.bootstrap-select.show-menu-arrow.show > .dropdown-toggle {
|
||||
z-index: 1061;
|
||||
}
|
||||
.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:before {
|
||||
content: '';
|
||||
border-left: 7px solid transparent;
|
||||
border-right: 7px solid transparent;
|
||||
border-bottom: 7px solid rgba(204, 204, 204, 0.2);
|
||||
position: absolute;
|
||||
bottom: -4px;
|
||||
left: 9px;
|
||||
display: none;
|
||||
}
|
||||
.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:after {
|
||||
content: '';
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 6px solid white;
|
||||
position: absolute;
|
||||
bottom: -4px;
|
||||
left: 10px;
|
||||
display: none;
|
||||
}
|
||||
.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:before {
|
||||
bottom: auto;
|
||||
top: -4px;
|
||||
border-top: 7px solid rgba(204, 204, 204, 0.2);
|
||||
border-bottom: 0;
|
||||
}
|
||||
.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:after {
|
||||
bottom: auto;
|
||||
top: -4px;
|
||||
border-top: 6px solid white;
|
||||
border-bottom: 0;
|
||||
}
|
||||
.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:before {
|
||||
right: 12px;
|
||||
left: auto;
|
||||
}
|
||||
.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:after {
|
||||
right: 13px;
|
||||
left: auto;
|
||||
}
|
||||
.bootstrap-select.show-menu-arrow.open > .dropdown-toggle .filter-option:before,
|
||||
.bootstrap-select.show-menu-arrow.show > .dropdown-toggle .filter-option:before,
|
||||
.bootstrap-select.show-menu-arrow.open > .dropdown-toggle .filter-option:after,
|
||||
.bootstrap-select.show-menu-arrow.show > .dropdown-toggle .filter-option:after {
|
||||
display: block;
|
||||
}
|
||||
.bs-searchbox,
|
||||
.bs-actionsbox,
|
||||
.bs-donebutton {
|
||||
padding: 4px 8px;
|
||||
}
|
||||
.bs-actionsbox {
|
||||
width: 100%;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.bs-actionsbox .btn-group {
|
||||
display: block;
|
||||
}
|
||||
.bs-actionsbox .btn-group button {
|
||||
width: 50%;
|
||||
}
|
||||
.bs-donebutton {
|
||||
float: left;
|
||||
width: 100%;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.bs-donebutton .btn-group {
|
||||
display: block;
|
||||
}
|
||||
.bs-donebutton .btn-group button {
|
||||
width: 100%;
|
||||
}
|
||||
.bs-searchbox + .bs-actionsbox {
|
||||
padding: 0 8px 4px;
|
||||
}
|
||||
.bs-searchbox .form-control {
|
||||
margin-bottom: 0;
|
||||
width: 100%;
|
||||
float: none;
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-select.css.map */
|
||||
@@ -1,6 +0,0 @@
|
||||
/*!
|
||||
* Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select)
|
||||
*
|
||||
* Copyright 2013-2017 bootstrap-select
|
||||
* Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE)
|
||||
*/select.bs-select-hidden,select.selectpicker{display:none!important}.bootstrap-select{width:220px\9}.bootstrap-select>.dropdown-toggle{width:100%;padding-right:25px;z-index:1}.bootstrap-select>.dropdown-toggle.bs-placeholder,.bootstrap-select>.dropdown-toggle.bs-placeholder:active,.bootstrap-select>.dropdown-toggle.bs-placeholder:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder:hover{color:#999}.bootstrap-select>select{position:absolute!important;bottom:0;left:50%;display:block!important;width:.5px!important;height:100%!important;padding:0!important;opacity:0!important;border:none}.bootstrap-select>select.mobile-device{top:0;left:0;display:block!important;width:100%!important;z-index:2}.error .bootstrap-select .dropdown-toggle,.has-error .bootstrap-select .dropdown-toggle{border-color:#b94a48}.bootstrap-select.fit-width{width:auto!important}.bootstrap-select:not([class*=col-]):not([class*=form-control]):not(.input-group-btn){width:220px}.bootstrap-select .dropdown-toggle:focus{outline:thin dotted #333!important;outline:5px auto -webkit-focus-ring-color!important;outline-offset:-2px}.bootstrap-select.form-control{margin-bottom:0;padding:0;border:none}.bootstrap-select.form-control:not([class*=col-]){width:100%}.bootstrap-select.form-control.input-group-btn{z-index:auto}.bootstrap-select.form-control.input-group-btn:not(:first-child):not(:last-child)>.btn{border-radius:0}.bootstrap-select.btn-group:not(.input-group-btn),.bootstrap-select.btn-group[class*=col-]{float:none;display:inline-block;margin-left:0}.bootstrap-select.btn-group.dropdown-menu-right,.bootstrap-select.btn-group[class*=col-].dropdown-menu-right,.row .bootstrap-select.btn-group[class*=col-].dropdown-menu-right{float:right}.form-group .bootstrap-select.btn-group,.form-horizontal .bootstrap-select.btn-group,.form-inline .bootstrap-select.btn-group{margin-bottom:0}.form-group-lg .bootstrap-select.btn-group.form-control,.form-group-sm .bootstrap-select.btn-group.form-control{padding:0}.form-group-lg .bootstrap-select.btn-group.form-control .dropdown-toggle,.form-group-sm .bootstrap-select.btn-group.form-control .dropdown-toggle{height:100%;font-size:inherit;line-height:inherit;border-radius:inherit}.form-inline .bootstrap-select.btn-group .form-control{width:100%}.bootstrap-select.btn-group.disabled,.bootstrap-select.btn-group>.disabled{cursor:not-allowed}.bootstrap-select.btn-group.disabled:focus,.bootstrap-select.btn-group>.disabled:focus{outline:0!important}.bootstrap-select.btn-group.bs-container{position:absolute;height:0!important;padding:0!important}.bootstrap-select.btn-group.bs-container .dropdown-menu{z-index:1060}.bootstrap-select.btn-group .dropdown-toggle .filter-option{display:inline-block;overflow:hidden;width:100%;text-align:left}.bootstrap-select.btn-group .dropdown-toggle .caret{position:absolute;top:50%;right:12px;margin-top:-2px;vertical-align:middle}.bootstrap-select.btn-group[class*=col-] .dropdown-toggle{width:100%}.bootstrap-select.btn-group .dropdown-menu{min-width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select.btn-group .dropdown-menu.inner{position:static;float:none;border:0;padding:0;margin:0;border-radius:0;-webkit-box-shadow:none;box-shadow:none}.bootstrap-select.btn-group .dropdown-menu li{position:relative}.bootstrap-select.btn-group .dropdown-menu li.active small{color:#fff}.bootstrap-select.btn-group .dropdown-menu li.disabled a{cursor:not-allowed}.bootstrap-select.btn-group .dropdown-menu li a{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.bootstrap-select.btn-group .dropdown-menu li a.opt{position:relative;padding-left:2.25em}.bootstrap-select.btn-group .dropdown-menu li a span.check-mark{display:none}.bootstrap-select.btn-group .dropdown-menu li a span.text{display:inline-block}.bootstrap-select.btn-group .dropdown-menu li small{padding-left:.5em}.bootstrap-select.btn-group .dropdown-menu .notify{position:absolute;bottom:5px;width:96%;margin:0 2%;min-height:26px;padding:3px 5px;background:#f5f5f5;border:1px solid #e3e3e3;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05);pointer-events:none;opacity:.9;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select.btn-group .no-results{padding:3px;background:#f5f5f5;margin:0 5px;white-space:nowrap}.bootstrap-select.btn-group.fit-width .dropdown-toggle .filter-option{position:static}.bootstrap-select.btn-group.fit-width .dropdown-toggle .caret{position:static;top:auto;margin-top:-1px}.bootstrap-select.btn-group.show-tick .dropdown-menu li.selected a span.check-mark{position:absolute;display:inline-block;right:15px;margin-top:5px}.bootstrap-select.btn-group.show-tick .dropdown-menu li a span.text{margin-right:34px}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle{z-index:1061}.bootstrap-select.show-menu-arrow .dropdown-toggle:before{content:'';border-left:7px solidLine truncated
|
||||
-323
@@ -1,323 +0,0 @@
|
||||
table.footable-details,
|
||||
table.footable > thead > tr.footable-filtering > th div.form-group {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
table.footable,
|
||||
table.footable-details {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.footable-hide-fouc {
|
||||
display: none;
|
||||
}
|
||||
table > tbody > tr > td > span.footable-toggle {
|
||||
margin-right: 8px;
|
||||
opacity: 0.3;
|
||||
}
|
||||
table > tbody > tr > td > span.footable-toggle.last-column {
|
||||
margin-left: 8px;
|
||||
float: right;
|
||||
}
|
||||
table.table-condensed > tbody > tr > td > span.footable-toggle {
|
||||
margin-right: 5px;
|
||||
}
|
||||
table.footable-details > tbody > tr > th:nth-child(1) {
|
||||
min-width: 40px;
|
||||
width: 120px;
|
||||
}
|
||||
table.footable-details > tbody > tr > td:nth-child(2) {
|
||||
word-break: break-all;
|
||||
}
|
||||
table.footable-details > tbody > tr:first-child > td,
|
||||
table.footable-details > tbody > tr:first-child > th,
|
||||
table.footable-details > tfoot > tr:first-child > td,
|
||||
table.footable-details > tfoot > tr:first-child > th,
|
||||
table.footable-details > thead > tr:first-child > td,
|
||||
table.footable-details > thead > tr:first-child > th {
|
||||
border-top-width: 0;
|
||||
}
|
||||
table.footable-details.table-bordered > tbody > tr:first-child > td,
|
||||
table.footable-details.table-bordered > tbody > tr:first-child > th,
|
||||
table.footable-details.table-bordered > tfoot > tr:first-child > td,
|
||||
table.footable-details.table-bordered > tfoot > tr:first-child > th,
|
||||
table.footable-details.table-bordered > thead > tr:first-child > td,
|
||||
table.footable-details.table-bordered > thead > tr:first-child > th {
|
||||
border-top-width: 1px;
|
||||
}
|
||||
div.footable-loader {
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
height: 300px;
|
||||
position: relative;
|
||||
}
|
||||
div.footable-loader > span.fooicon {
|
||||
display: inline-block;
|
||||
opacity: 0.3;
|
||||
font-size: 30px;
|
||||
line-height: 32px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-top: -16px;
|
||||
margin-left: -16px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
-webkit-animation: fooicon-spin-r 2s infinite linear;
|
||||
animation: fooicon-spin-r 2s infinite linear;
|
||||
}
|
||||
table.footable > tbody > tr.footable-empty > td {
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
font-size: 30px;
|
||||
}
|
||||
table.footable > tbody > tr > td,
|
||||
table.footable > tbody > tr > th {
|
||||
display: none;
|
||||
}
|
||||
table.footable > tbody > tr.footable-detail-row > td,
|
||||
table.footable > tbody > tr.footable-detail-row > th,
|
||||
table.footable > tbody > tr.footable-empty > td,
|
||||
table.footable > tbody > tr.footable-empty > th {
|
||||
display: table-cell;
|
||||
}
|
||||
@-webkit-keyframes fooicon-spin-r {
|
||||
0% {
|
||||
-webkit-transform: rotate(0);
|
||||
transform: rotate(0);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(359deg);
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
@keyframes fooicon-spin-r {
|
||||
0% {
|
||||
-webkit-transform: rotate(0);
|
||||
transform: rotate(0);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(359deg);
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
.fooicon {
|
||||
position: relative;
|
||||
top: 0px;
|
||||
display: inline-block;
|
||||
font-family: "bootstrap-icons" !important;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
@-moz-document url-prefix() {
|
||||
.fooicon {
|
||||
top: 2px;
|
||||
}
|
||||
}
|
||||
.fooicon:after,
|
||||
.fooicon:before {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.fooicon-loader:before {
|
||||
content: "\f130";
|
||||
}
|
||||
.fooicon-plus:before {
|
||||
content: "\f4fd";
|
||||
}
|
||||
.fooicon-minus:before {
|
||||
content: "\f2e9";
|
||||
}
|
||||
.fooicon-search:before {
|
||||
content: "\f52a";
|
||||
}
|
||||
.fooicon-remove:before {
|
||||
content: "\f62a";
|
||||
}
|
||||
.fooicon-sort:before {
|
||||
content: "\f3c6";
|
||||
}
|
||||
.fooicon-sort-asc:before {
|
||||
content: "\f575";
|
||||
}
|
||||
.fooicon-sort-desc:before {
|
||||
content: "\f57b";
|
||||
}
|
||||
.fooicon-pencil:before {
|
||||
content: "\f4c9";
|
||||
}
|
||||
.fooicon-trash:before {
|
||||
content: "\f62a";
|
||||
}
|
||||
.fooicon-eye-close:before {
|
||||
content: "\f33f";
|
||||
}
|
||||
.fooicon-flash:before {
|
||||
content: "\f46e";
|
||||
}
|
||||
.fooicon-cog:before {
|
||||
content: "\f3e2";
|
||||
}
|
||||
.fooicon-stats:before {
|
||||
content: "\f359";
|
||||
}
|
||||
table.footable > thead > tr.footable-filtering > th {
|
||||
border-bottom-width: 1px;
|
||||
font-weight: 400;
|
||||
}
|
||||
.footable-filtering-external.footable-filtering-right,
|
||||
table.footable.footable-filtering-right > thead > tr.footable-filtering > th,
|
||||
table.footable > thead > tr.footable-filtering > th {
|
||||
text-align: right;
|
||||
}
|
||||
.footable-filtering-external.footable-filtering-left,
|
||||
table.footable.footable-filtering-left > thead > tr.footable-filtering > th {
|
||||
text-align: left;
|
||||
}
|
||||
.footable-filtering-external.footable-filtering-center,
|
||||
.footable-paging-external.footable-paging-center,
|
||||
table.footable-paging-center > tfoot > tr.footable-paging > td,
|
||||
table.footable.footable-filtering-center > thead > tr.footable-filtering > th,
|
||||
table.footable > tfoot > tr.footable-paging > td {
|
||||
text-align: center;
|
||||
}
|
||||
table.footable > thead > tr.footable-filtering > th div.form-group + div.form-group {
|
||||
margin-top: 5px;
|
||||
}
|
||||
table.footable > thead > tr.footable-filtering > th div.input-group {
|
||||
width: 100%;
|
||||
}
|
||||
.footable-filtering-external ul.dropdown-menu > li > a.checkbox,
|
||||
table.footable > thead > tr.footable-filtering > th ul.dropdown-menu > li > a.checkbox {
|
||||
margin: 0;
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
.footable-filtering-external ul.dropdown-menu > li > a.checkbox > label,
|
||||
table.footable > thead > tr.footable-filtering > th ul.dropdown-menu > li > a.checkbox > label {
|
||||
display: block;
|
||||
padding-left: 20px;
|
||||
}
|
||||
.footable-filtering-external ul.dropdown-menu > li > a.checkbox input[type="checkbox"],
|
||||
table.footable > thead > tr.footable-filtering > th ul.dropdown-menu > li > a.checkbox input[type="checkbox"] {
|
||||
position: absolute;
|
||||
margin-left: -20px;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
table.footable > thead > tr.footable-filtering > th div.input-group {
|
||||
width: auto;
|
||||
}
|
||||
table.footable > thead > tr.footable-filtering > th div.form-group {
|
||||
margin-left: 2px;
|
||||
margin-right: 2px;
|
||||
}
|
||||
table.footable > thead > tr.footable-filtering > th div.form-group + div.form-group {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
table.footable > tbody > tr > td.footable-sortable,
|
||||
table.footable > tbody > tr > th.footable-sortable,
|
||||
table.footable > tfoot > tr > td.footable-sortable,
|
||||
table.footable > tfoot > tr > th.footable-sortable,
|
||||
table.footable > thead > tr > td.footable-sortable,
|
||||
table.footable > thead > tr > th.footable-sortable {
|
||||
position: relative;
|
||||
padding-right: 30px;
|
||||
cursor: pointer;
|
||||
}
|
||||
td.footable-sortable > span.fooicon,
|
||||
th.footable-sortable > span.fooicon {
|
||||
position: absolute;
|
||||
right: 6px;
|
||||
top: 50%;
|
||||
margin-top: -7px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease-in;
|
||||
}
|
||||
td.footable-sortable.footable-asc > span.fooicon,
|
||||
td.footable-sortable.footable-desc > span.fooicon,
|
||||
td.footable-sortable:hover > span.fooicon,
|
||||
th.footable-sortable.footable-asc > span.fooicon,
|
||||
th.footable-sortable.footable-desc > span.fooicon,
|
||||
th.footable-sortable:hover > span.fooicon {
|
||||
opacity: 1;
|
||||
}
|
||||
table.footable-sorting-disabled td.footable-sortable.footable-asc > span.fooicon,
|
||||
table.footable-sorting-disabled td.footable-sortable.footable-desc > span.fooicon,
|
||||
table.footable-sorting-disabled td.footable-sortable:hover > span.fooicon,
|
||||
table.footable-sorting-disabled th.footable-sortable.footable-asc > span.fooicon,
|
||||
table.footable-sorting-disabled th.footable-sortable.footable-desc > span.fooicon,
|
||||
table.footable-sorting-disabled th.footable-sortable:hover > span.fooicon {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
.footable-paging-external ul.pagination,
|
||||
table.footable > tfoot > tr.footable-paging > td > ul.pagination {
|
||||
margin: 10px 0 0;
|
||||
}
|
||||
.footable-paging-external span.label,
|
||||
table.footable > tfoot > tr.footable-paging > td > span.label {
|
||||
display: inline-block;
|
||||
margin: 0 0 10px;
|
||||
padding: 4px 10px;
|
||||
}
|
||||
.footable-paging-external.footable-paging-left,
|
||||
table.footable-paging-left > tfoot > tr.footable-paging > td {
|
||||
text-align: left;
|
||||
}
|
||||
.footable-paging-external.footable-paging-right,
|
||||
table.footable-editing-right td.footable-editing,
|
||||
table.footable-editing-right tr.footable-editing,
|
||||
table.footable-paging-right > tfoot > tr.footable-paging > td {
|
||||
text-align: right;
|
||||
}
|
||||
ul.pagination > li.footable-page {
|
||||
display: none;
|
||||
}
|
||||
ul.pagination > li.footable-page.visible {
|
||||
display: inline;
|
||||
}
|
||||
td.footable-editing {
|
||||
width: 90px;
|
||||
max-width: 90px;
|
||||
}
|
||||
table.footable-editing-no-delete td.footable-editing,
|
||||
table.footable-editing-no-edit td.footable-editing,
|
||||
table.footable-editing-no-view td.footable-editing {
|
||||
width: 70px;
|
||||
max-width: 70px;
|
||||
}
|
||||
table.footable-editing-no-delete.footable-editing-no-view td.footable-editing,
|
||||
table.footable-editing-no-edit.footable-editing-no-delete td.footable-editing,
|
||||
table.footable-editing-no-edit.footable-editing-no-view td.footable-editing {
|
||||
width: 50px;
|
||||
max-width: 50px;
|
||||
}
|
||||
table.footable-editing-no-edit.footable-editing-no-delete.footable-editing-no-view td.footable-editing,
|
||||
table.footable-editing-no-edit.footable-editing-no-delete.footable-editing-no-view th.footable-editing {
|
||||
width: 0;
|
||||
max-width: 0;
|
||||
display: none !important;
|
||||
}
|
||||
table.footable-editing-left td.footable-editing,
|
||||
table.footable-editing-left tr.footable-editing {
|
||||
text-align: left;
|
||||
}
|
||||
table.footable-editing button.footable-add,
|
||||
table.footable-editing button.footable-hide,
|
||||
table.footable-editing-show button.footable-show,
|
||||
table.footable-editing.footable-editing-always-show button.footable-hide,
|
||||
table.footable-editing.footable-editing-always-show button.footable-show,
|
||||
table.footable-editing.footable-editing-always-show.footable-editing-no-add tr.footable-editing {
|
||||
display: none;
|
||||
}
|
||||
table.footable-editing.footable-editing-always-show button.footable-add,
|
||||
table.footable-editing.footable-editing-show button.footable-add,
|
||||
table.footable-editing.footable-editing-show button.footable-hide {
|
||||
display: inline-block;
|
||||
}
|
||||
+1
-1
@@ -1 +1 @@
|
||||
.flag-icon-background{background-size:contain;background-position:50%;background-repeat:no-repeat}.flag-icon{background-size:contain;background-position:50%;background-repeat:no-repeat;position:relative;display:inline-block;width:1.33333333em;line-height:1em}.flag-icon:before{content:'\00a0'}.flag-icon.flag-icon-squared{width:1em}.flag-icon-ad{background-image:url(../flags/4x3/ad.svg)}.flag-icon-ad.flag-icon-squared{background-image:url(../flags/1x1/ad.svg)}.flag-icon-ae{background-image:url(../flags/4x3/ae.svg)}.flag-icon-ae.flag-icon-squared{background-image:url(../flags/1x1/ae.svg)}.flag-icon-af{background-image:url(../flags/4x3/af.svg)}.flag-icon-af.flag-icon-squared{background-image:url(../flags/1x1/af.svg)}.flag-icon-ag{background-image:url(../flags/4x3/ag.svg)}.flag-icon-ag.flag-icon-squared{background-image:url(../flags/1x1/ag.svg)}.flag-icon-ai{background-image:url(../flags/4x3/ai.svg)}.flag-icon-ai.flag-icon-squared{background-image:url(../flags/1x1/ai.svg)}.flag-icon-al{background-image:url(../flags/4x3/al.svg)}.flag-icon-al.flag-icon-squared{background-image:url(../flags/1x1/al.svg)}.flag-icon-am{background-image:url(../flags/4x3/am.svg)}.flag-icon-am.flag-icon-squared{background-image:url(../flags/1x1/am.svg)}.flag-icon-ao{background-image:url(../flags/4x3/ao.svg)}.flag-icon-ao.flag-icon-squared{background-image:url(../flags/1x1/ao.svg)}.flag-icon-aq{background-image:url(../flags/4x3/aq.svg)}.flag-icon-aq.flag-icon-squared{background-image:url(../flags/1x1/aq.svg)}.flag-icon-ar{background-image:url(../flags/4x3/ar.svg)}.flag-icon-ar.flag-icon-squared{background-image:url(../flags/1x1/ar.svg)}.flag-icon-as{background-image:url(../flags/4x3/as.svg)}.flag-icon-as.flag-icon-squared{background-image:url(../flags/1x1/as.svg)}.flag-icon-at{background-image:url(../flags/4x3/at.svg)}.flag-icon-at.flag-icon-squared{background-image:url(../flags/1x1/at.svg)}.flag-icon-au{background-image:url(../flags/4x3/au.svg)}.flag-icon-au.flag-icon-squared{background-image:url(../flags/1x1/au.svg)}.flag-icon-aw{background-image:url(../flags/4x3/aw.svg)}.flag-icon-aw.flag-icon-squared{background-image:url(../flags/1x1/aw.svg)}.flag-icon-ax{background-image:url(../flags/4x3/ax.svg)}.flag-icon-ax.flag-icon-squared{background-image:url(../flags/1x1/ax.svg)}.flag-icon-az{background-image:url(../flags/4x3/az.svg)}.flag-icon-az.flag-icon-squared{background-image:url(../flags/1x1/az.svg)}.flag-icon-ba{background-image:url(../flags/4x3/ba.svg)}.flag-icon-ba.flag-icon-squared{background-image:url(../flags/1x1/ba.svg)}.flag-icon-bb{background-image:url(../flags/4x3/bb.svg)}.flag-icon-bb.flag-icon-squared{background-image:url(../flags/1x1/bb.svg)}.flag-icon-bd{background-image:url(../flags/4x3/bd.svg)}.flag-icon-bd.flag-icon-squared{background-image:url(../flags/1x1/bd.svg)}.flag-icon-be{background-image:url(../flags/4x3/be.svg)}.flag-icon-be.flag-icon-squared{background-image:url(../flags/1x1/be.svg)}.flag-icon-bf{background-image:url(../flags/4x3/bf.svg)}.flag-icon-bf.flag-icon-squared{background-image:url(../flags/1x1/bf.svg)}.flag-icon-bg{background-image:url(../flags/4x3/bg.svg)}.flag-icon-bg.flag-icon-squared{background-image:url(../flags/1x1/bg.svg)}.flag-icon-bh{background-image:url(../flags/4x3/bh.svg)}.flag-icon-bh.flag-icon-squared{background-image:url(../flags/1x1/bh.svg)}.flag-icon-bi{background-image:url(../flags/4x3/bi.svg)}.flag-icon-bi.flag-icon-squared{background-image:url(../flags/1x1/bi.svg)}.flag-icon-bj{background-image:url(../flags/4x3/bj.svg)}.flag-icon-bj.flag-icon-squared{background-image:url(../flags/1x1/bj.svg)}.flag-icon-bl{background-image:url(../flags/4x3/bl.svg)}.flag-icon-bl.flag-icon-squared{background-image:url(../flags/1x1/bl.svg)}.flag-icon-bm{background-image:url(../flags/4x3/bm.svg)}.flag-icon-bm.flag-icon-squared{background-image:url(../flags/1x1/bm.svg)}.flag-icon-bn{background-image:url(../flags/4x3/bn.svg)}.flag-icon-bn.flag-icon-squared{background-image:url(../flags/1x1/bn.svg)}.flag-icon-bo{background-image:url(../flags/4x3/bo.svg)}.flag-icon-bo.flag-icon-squared{background-image:url(../flags/1x1/bo.svg)}.flag-icon-bq{background-image:url(../flags/4x3/bq.svg)}.flag-icon-bq.flag-icon-squared{background-image:url(../flags/1x1/bq.svg)}.flag-icon-br{background-image:url(../flags/4x3/br.svg)}.flag-icon-br.flag-icon-squared{background-image:url(../flags/1x1/br.svg)}.flag-icon-bs{background-image:url(../flags/4x3/bs.svg)}.flag-icon-bs.flag-icon-squared{background-image:url(../flags/1x1/bs.svg)}.flag-icon-bt{background-image:url(../flags/4x3/bt.svg)}.flag-icon-bt.flag-icon-squared{background-image:url(../flags/1x1/bt.svg)}.flag-icon-bv{background-image:url(../flags/4x3/bv.svg)}.flag-icon-bv.flag-icon-squared{background-image:url(../flags/1x1/bv.svg)}.flag-icon-bw{background-image:url(../flags/4x3/bw.svg)}.flag-icon-bw.flag-icon-squared{background-image:url(../flags/1x1/bw.svg)}.flag-icon-by{background-image:url(../flags/4x3/by.svg)}.flag-icon-by.flag-icon-squared{background-image:url(../flagsLine truncated
|
||||
.flag-icon-background{background-size:contain;background-position:50%;background-repeat:no-repeat}.flag-icon{background-size:contain;background-position:50%;background-repeat:no-repeat;position:relative;display:inline-block;width:1.33333333em;line-height:1em}.flag-icon:before{content:'\00a0'}.flag-icon.flag-icon-squared{width:1em}.flag-icon-ad{background-image:url(../flags/4x3/ad.svg)}.flag-icon-ad.flag-icon-squared{background-image:url(../flags/1x1/ad.svg)}.flag-icon-ae{background-image:url(../flags/4x3/ae.svg)}.flag-icon-ae.flag-icon-squared{background-image:url(../flags/1x1/ae.svg)}.flag-icon-af{background-image:url(../flags/4x3/af.svg)}.flag-icon-af.flag-icon-squared{background-image:url(../flags/1x1/af.svg)}.flag-icon-ag{background-image:url(../flags/4x3/ag.svg)}.flag-icon-ag.flag-icon-squared{background-image:url(../flags/1x1/ag.svg)}.flag-icon-ai{background-image:url(../flags/4x3/ai.svg)}.flag-icon-ai.flag-icon-squared{background-image:url(../flags/1x1/ai.svg)}.flag-icon-al{background-image:url(../flags/4x3/al.svg)}.flag-icon-al.flag-icon-squared{background-image:url(../flags/1x1/al.svg)}.flag-icon-am{background-image:url(../flags/4x3/am.svg)}.flag-icon-am.flag-icon-squared{background-image:url(../flags/1x1/am.svg)}.flag-icon-ao{background-image:url(../flags/4x3/ao.svg)}.flag-icon-ao.flag-icon-squared{background-image:url(../flags/1x1/ao.svg)}.flag-icon-aq{background-image:url(../flags/4x3/aq.svg)}.flag-icon-aq.flag-icon-squared{background-image:url(../flags/1x1/aq.svg)}.flag-icon-ar{background-image:url(../flags/4x3/ar.svg)}.flag-icon-ar.flag-icon-squared{background-image:url(../flags/1x1/ar.svg)}.flag-icon-as{background-image:url(../flags/4x3/as.svg)}.flag-icon-as.flag-icon-squared{background-image:url(../flags/1x1/as.svg)}.flag-icon-at{background-image:url(../flags/4x3/at.svg)}.flag-icon-at.flag-icon-squared{background-image:url(../flags/1x1/at.svg)}.flag-icon-au{background-image:url(../flags/4x3/au.svg)}.flag-icon-au.flag-icon-squared{background-image:url(../flags/1x1/au.svg)}.flag-icon-aw{background-image:url(../flags/4x3/aw.svg)}.flag-icon-aw.flag-icon-squared{background-image:url(../flags/1x1/aw.svg)}.flag-icon-ax{background-image:url(../flags/4x3/ax.svg)}.flag-icon-ax.flag-icon-squared{background-image:url(../flags/1x1/ax.svg)}.flag-icon-az{background-image:url(../flags/4x3/az.svg)}.flag-icon-az.flag-icon-squared{background-image:url(../flags/1x1/az.svg)}.flag-icon-ba{background-image:url(../flags/4x3/ba.svg)}.flag-icon-ba.flag-icon-squared{background-image:url(../flags/1x1/ba.svg)}.flag-icon-bb{background-image:url(../flags/4x3/bb.svg)}.flag-icon-bb.flag-icon-squared{background-image:url(../flags/1x1/bb.svg)}.flag-icon-bd{background-image:url(../flags/4x3/bd.svg)}.flag-icon-bd.flag-icon-squared{background-image:url(../flags/1x1/bd.svg)}.flag-icon-be{background-image:url(../flags/4x3/be.svg)}.flag-icon-be.flag-icon-squared{background-image:url(../flags/1x1/be.svg)}.flag-icon-bf{background-image:url(../flags/4x3/bf.svg)}.flag-icon-bf.flag-icon-squared{background-image:url(../flags/1x1/bf.svg)}.flag-icon-bg{background-image:url(../flags/4x3/bg.svg)}.flag-icon-bg.flag-icon-squared{background-image:url(../flags/1x1/bg.svg)}.flag-icon-bh{background-image:url(../flags/4x3/bh.svg)}.flag-icon-bh.flag-icon-squared{background-image:url(../flags/1x1/bh.svg)}.flag-icon-bi{background-image:url(../flags/4x3/bi.svg)}.flag-icon-bi.flag-icon-squared{background-image:url(../flags/1x1/bi.svg)}.flag-icon-bj{background-image:url(../flags/4x3/bj.svg)}.flag-icon-bj.flag-icon-squared{background-image:url(../flags/1x1/bj.svg)}.flag-icon-bl{background-image:url(../flags/4x3/bl.svg)}.flag-icon-bl.flag-icon-squared{background-image:url(../flags/1x1/bl.svg)}.flag-icon-bm{background-image:url(../flags/4x3/bm.svg)}.flag-icon-bm.flag-icon-squared{background-image:url(../flags/1x1/bm.svg)}.flag-icon-bn{background-image:url(../flags/4x3/bn.svg)}.flag-icon-bn.flag-icon-squared{background-image:url(../flags/1x1/bn.svg)}.flag-icon-bo{background-image:url(../flags/4x3/bo.svg)}.flag-icon-bo.flag-icon-squared{background-image:url(../flags/1x1/bo.svg)}.flag-icon-bq{background-image:url(../flags/4x3/bq.svg)}.flag-icon-bq.flag-icon-squared{background-image:url(../flags/1x1/bq.svg)}.flag-icon-br{background-image:url(../flags/4x3/br.svg)}.flag-icon-br.flag-icon-squared{background-image:url(../flags/1x1/br.svg)}.flag-icon-bs{background-image:url(../flags/4x3/bs.svg)}.flag-icon-bs.flag-icon-squared{background-image:url(../flags/1x1/bs.svg)}.flag-icon-bt{background-image:url(../flags/4x3/bt.svg)}.flag-icon-bt.flag-icon-squared{background-image:url(../flags/1x1/bt.svg)}.flag-icon-bv{background-image:url(../flags/4x3/bv.svg)}.flag-icon-bv.flag-icon-squared{background-image:url(../flags/1x1/bv.svg)}.flag-icon-bw{background-image:url(../flags/4x3/bw.svg)}.flag-icon-bw.flag-icon-squared{background-image:url(../flags/1x1/bw.svg)}.flag-icon-by{background-image:url(../flags/4x3/by.svg)}.flag-icon-by.flag-icon-squared{background-image:url(../flagsLine truncated
|
||||
+10
-10
@@ -1,11 +1,11 @@
|
||||
@charset "UTF-8";
|
||||
|
||||
/*!
|
||||
* animate.css -http://daneden.me/animate
|
||||
* Version - 3.5.2
|
||||
* Licensed under the MIT license - http://opensource.org/licenses/MIT
|
||||
*
|
||||
* Copyright (c) 2017 Daniel Eden
|
||||
*/
|
||||
|
||||
@charset "UTF-8";
|
||||
|
||||
/*!
|
||||
* animate.css -http://daneden.me/animate
|
||||
* Version - 3.5.2
|
||||
* Licensed under the MIT license - http://opensource.org/licenses/MIT
|
||||
*
|
||||
* Copyright (c) 2017 Daniel Eden
|
||||
*/
|
||||
|
||||
.animated{animation-duration:1s;animation-fill-mode:both}.animated.infinite{animation-iteration-count:infinite}.animated.hinge{animation-duration:2s}.animated.bounceIn,.animated.bounceOut,.animated.flipOutX,.animated.flipOutY{animation-duration:.75s}@keyframes bounce{0%,20%,53%,80%,to{animation-timing-function:cubic-bezier(.215,.61,.355,1);transform:translateZ(0)}40%,43%{animation-timing-function:cubic-bezier(.755,.05,.855,.06);transform:translate3d(0,-30px,0)}70%{animation-timing-function:cubic-bezier(.755,.05,.855,.06);transform:translate3d(0,-15px,0)}90%{transform:translate3d(0,-4px,0)}}.bounce{animation-name:bounce;transform-origin:center bottom}@keyframes flash{0%,50%,to{opacity:1}25%,75%{opacity:0}}.flash{animation-name:flash}@keyframes pulse{0%{transform:scaleX(1)}50%{transform:scale3d(1.05,1.05,1.05)}to{transform:scaleX(1)}}.pulse{animation-name:pulse}@keyframes rubberBand{0%{transform:scaleX(1)}30%{transform:scale3d(1.25,.75,1)}40%{transform:scale3d(.75,1.25,1)}50%{transform:scale3d(1.15,.85,1)}65%{transform:scale3d(.95,1.05,1)}75%{transform:scale3d(1.05,.95,1)}to{transform:scaleX(1)}}.rubberBand{animation-name:rubberBand}@keyframes shake{0%,to{transform:translateZ(0)}10%,30%,50%,70%,90%{transform:translate3d(-10px,0,0)}20%,40%,60%,80%{transform:translate3d(10px,0,0)}}.shake{animation-name:shake}@keyframes headShake{0%{transform:translateX(0)}6.5%{transform:translateX(-6px) rotateY(-9deg)}18.5%{transform:translateX(5px) rotateY(7deg)}31.5%{transform:translateX(-3px) rotateY(-5deg)}43.5%{transform:translateX(2px) rotateY(3deg)}50%{transform:translateX(0)}}.headShake{animation-timing-function:ease-in-out;animation-name:headShake}@keyframes swing{20%{transform:rotate(15deg)}40%{transform:rotate(-10deg)}60%{transform:rotate(5deg)}80%{transform:rotate(-5deg)}to{transform:rotate(0deg)}}.swing{transform-origin:top center;animation-name:swing}@keyframes tada{0%{transform:scaleX(1)}10%,20%{transform:scale3d(.9,.9,.9) rotate(-3deg)}30%,50%,70%,90%{transform:scale3d(1.1,1.1,1.1) rotate(3deg)}40%,60%,80%{transform:scale3d(1.1,1.1,1.1) rotate(-3deg)}to{transform:scaleX(1)}}.tada{animation-name:tada}@keyframes wobble{0%{transform:none}15%{transform:translate3d(-25%,0,0) rotate(-5deg)}30%{transform:translate3d(20%,0,0) rotate(3deg)}45%{transform:translate3d(-15%,0,0) rotate(-3deg)}60%{transform:translate3d(10%,0,0) rotate(2deg)}75%{transform:translate3d(-5%,0,0) rotate(-1deg)}to{transform:none}}.wobble{animation-name:wobble}@keyframes jello{0%,11.1%,to{transform:none}22.2%{transform:skewX(-12.5deg) skewY(-12.5deg)}33.3%{transform:skewX(6.25deg) skewY(6.25deg)}44.4%{transform:skewX(-3.125deg) skewY(-3.125deg)}55.5%{transform:skewX(1.5625deg) skewY(1.5625deg)}66.6%{transform:skewX(-.78125deg) skewY(-.78125deg)}77.7%{transform:skewX(.390625deg) skewY(.390625deg)}88.8%{transform:skewX(-.1953125deg) skewY(-.1953125deg)}}.jello{animation-name:jello;transform-origin:center}@keyframes bounceIn{0%,20%,40%,60%,80%,to{animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;transform:scale3d(.3,.3,.3)}20%{transform:scale3d(1.1,1.1,1.1)}40%{transform:scale3d(.9,.9,.9)}60%{opacity:1;transform:scale3d(1.03,1.03,1.03)}80%{transform:scale3d(.97,.97,.97)}to{opacity:1;transform:scaleX(1)}}.bounceIn{animation-name:bounceIn}@keyframes bounceInDown{0%,60%,75%,90%,to{animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;transform:translate3d(0,-3000px,0)}60%{opacity:1;transform:translate3d(0,25px,0)}75%{transform:translate3d(0,-10px,0)}90%{transform:translate3d(0,5px,0)}to{transform:none}}.bounceInDown{animation-name:bounceInDown}@keyframes bounceInLeft{0%,60%,75%,90%,to{animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;transform:translate3d(-3000px,0,0)}60%{opacity:1;transform:translate3d(25px,0,0)}75%{transform:translate3d(-10px,0,0)}90%{transform:translate3d(5px,0,0)}to{transform:none}}.bounceInLeft{animation-name:bounceInLeft}@keyframes bounceInRight{0%,60%,75%,90%,to{animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;transform:translate3d(3000px,0,0)}60%{opacity:1;transform:translate3d(-25px,0,0)}75%{transform:translate3d(10px,0,0)}90%{transform:translate3d(-5px,0,0)}to{transform:none}}.bounceInRight{animation-name:bounceInRight}@keyframes bounceInUp{0%,60%,75%,90%,to{animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;transform:translate3d(0,3000px,0)}60%{opacity:1;transform:translate3d(0,-20px,0)}75%{transform:translate3d(0,10px,0)}90%{transform:translate3d(0,-5px,0)}to{transform:translateZ(0)}}.bounceInUp{animation-name:bounceInUp}@keyframes bounceOut{20%{transform:scale3d(.9,.9,.9)}50%,55%{opacity:1;transform:scale3d(1.1,1.1,1.1)}to{opacity:0;transform:scale3d(.3,.3,.3)}}.bounceOut{animation-name:bounceOut}@keyframes bounceOutDown{20%{transform:translate3d(0,10px,0)}40%,45%{opacity:1;transform:translate3d(0,-20px,0)}to{opacity:0;transform:translate3d(0,2000px,0)}}.bounceOutDown{animation-name:bounceOutDown}@keyframes bounceOutLeft{20%{opacity:1;Line truncated
|
||||
Vendored
File renamed without changes.
Vendored
File renamed without changes.
@@ -0,0 +1,687 @@
|
||||
/*
|
||||
* This combined file was created by the DataTables downloader builder:
|
||||
* https://datatables.net/download
|
||||
*
|
||||
* To rebuild or modify this file with the latest versions of the included
|
||||
* software please visit:
|
||||
* https://datatables.net/download/#bs5/dt-1.13.1/r-2.4.0/sl-1.5.0
|
||||
*
|
||||
* Included libraries:
|
||||
* DataTables 1.13.1, Responsive 2.4.0, Select 1.5.0
|
||||
*/
|
||||
|
||||
@charset "UTF-8";
|
||||
table.dataTable td.dt-control {
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
table.dataTable td.dt-control:before {
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
margin-top: -9px;
|
||||
display: inline-block;
|
||||
color: white;
|
||||
border: 0.15em solid white;
|
||||
border-radius: 1em;
|
||||
box-shadow: 0 0 0.2em #444;
|
||||
box-sizing: content-box;
|
||||
text-align: center;
|
||||
text-indent: 0 !important;
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
line-height: 1em;
|
||||
content: "+";
|
||||
background-color: #31b131;
|
||||
}
|
||||
table.dataTable tr.dt-hasChild td.dt-control:before {
|
||||
content: "-";
|
||||
background-color: #d33333;
|
||||
}
|
||||
|
||||
table.dataTable thead > tr > th.sorting, table.dataTable thead > tr > th.sorting_asc, table.dataTable thead > tr > th.sorting_desc, table.dataTable thead > tr > th.sorting_asc_disabled, table.dataTable thead > tr > th.sorting_desc_disabled,
|
||||
table.dataTable thead > tr > td.sorting,
|
||||
table.dataTable thead > tr > td.sorting_asc,
|
||||
table.dataTable thead > tr > td.sorting_desc,
|
||||
table.dataTable thead > tr > td.sorting_asc_disabled,
|
||||
table.dataTable thead > tr > td.sorting_desc_disabled {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
padding-right: 26px;
|
||||
}
|
||||
table.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:after,
|
||||
table.dataTable thead > tr > td.sorting:before,
|
||||
table.dataTable thead > tr > td.sorting:after,
|
||||
table.dataTable thead > tr > td.sorting_asc:before,
|
||||
table.dataTable thead > tr > td.sorting_asc:after,
|
||||
table.dataTable thead > tr > td.sorting_desc:before,
|
||||
table.dataTable thead > tr > td.sorting_desc:after,
|
||||
table.dataTable thead > tr > td.sorting_asc_disabled:before,
|
||||
table.dataTable thead > tr > td.sorting_asc_disabled:after,
|
||||
table.dataTable thead > tr > td.sorting_desc_disabled:before,
|
||||
table.dataTable thead > tr > td.sorting_desc_disabled:after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
opacity: 0.125;
|
||||
right: 10px;
|
||||
line-height: 9px;
|
||||
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 > td.sorting:before,
|
||||
table.dataTable thead > tr > td.sorting_asc:before,
|
||||
table.dataTable thead > tr > td.sorting_desc:before,
|
||||
table.dataTable thead > tr > td.sorting_asc_disabled:before,
|
||||
table.dataTable thead > tr > td.sorting_desc_disabled:before {
|
||||
bottom: 50%;
|
||||
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 > td.sorting:after,
|
||||
table.dataTable thead > tr > td.sorting_asc:after,
|
||||
table.dataTable thead > tr > td.sorting_desc:after,
|
||||
table.dataTable thead > tr > td.sorting_asc_disabled:after,
|
||||
table.dataTable thead > tr > td.sorting_desc_disabled:after {
|
||||
top: 50%;
|
||||
content: "▼";
|
||||
}
|
||||
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_desc:after {
|
||||
opacity: 0.6;
|
||||
}
|
||||
table.dataTable thead > tr > th.sorting_desc_disabled:after, table.dataTable thead > tr > th.sorting_asc_disabled:before,
|
||||
table.dataTable thead > tr > td.sorting_desc_disabled:after,
|
||||
table.dataTable thead > tr > td.sorting_asc_disabled:before {
|
||||
display: none;
|
||||
}
|
||||
table.dataTable thead > tr > th:active,
|
||||
table.dataTable thead > tr > td:active {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
div.dataTables_scrollBody table.dataTable thead > tr > th:before, div.dataTables_scrollBody table.dataTable thead > tr > th:after,
|
||||
div.dataTables_scrollBody table.dataTable thead > tr > td:before,
|
||||
div.dataTables_scrollBody table.dataTable thead > tr > td:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.dataTables_processing {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 200px;
|
||||
margin-left: -100px;
|
||||
margin-top: -26px;
|
||||
text-align: center;
|
||||
padding: 2px;
|
||||
}
|
||||
div.dataTables_processing > div:last-child {
|
||||
position: relative;
|
||||
width: 80px;
|
||||
height: 15px;
|
||||
margin: 1em auto;
|
||||
}
|
||||
div.dataTables_processing > div:last-child > div {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
border-radius: 50%;
|
||||
background: rgba(13, 110, 253, 0.9);
|
||||
animation-timing-function: cubic-bezier(0, 1, 1, 0);
|
||||
}
|
||||
div.dataTables_processing > div:last-child > div:nth-child(1) {
|
||||
left: 8px;
|
||||
animation: datatables-loader-1 0.6s infinite;
|
||||
}
|
||||
div.dataTables_processing > div:last-child > div:nth-child(2) {
|
||||
left: 8px;
|
||||
animation: datatables-loader-2 0.6s infinite;
|
||||
}
|
||||
div.dataTables_processing > div:last-child > div:nth-child(3) {
|
||||
left: 32px;
|
||||
animation: datatables-loader-2 0.6s infinite;
|
||||
}
|
||||
div.dataTables_processing > div:last-child > div:nth-child(4) {
|
||||
left: 56px;
|
||||
animation: datatables-loader-3 0.6s infinite;
|
||||
}
|
||||
|
||||
@keyframes datatables-loader-1 {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
@keyframes datatables-loader-3 {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
100% {
|
||||
transform: scale(0);
|
||||
}
|
||||
}
|
||||
@keyframes datatables-loader-2 {
|
||||
0% {
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
100% {
|
||||
transform: translate(24px, 0);
|
||||
}
|
||||
}
|
||||
table.dataTable.nowrap th, table.dataTable.nowrap td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
table.dataTable th.dt-left,
|
||||
table.dataTable td.dt-left {
|
||||
text-align: left;
|
||||
}
|
||||
table.dataTable th.dt-center,
|
||||
table.dataTable td.dt-center,
|
||||
table.dataTable td.dataTables_empty {
|
||||
text-align: center;
|
||||
}
|
||||
table.dataTable th.dt-right,
|
||||
table.dataTable td.dt-right {
|
||||
text-align: right;
|
||||
}
|
||||
table.dataTable th.dt-justify,
|
||||
table.dataTable td.dt-justify {
|
||||
text-align: justify;
|
||||
}
|
||||
table.dataTable th.dt-nowrap,
|
||||
table.dataTable td.dt-nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
table.dataTable thead th,
|
||||
table.dataTable thead td,
|
||||
table.dataTable tfoot th,
|
||||
table.dataTable tfoot td {
|
||||
text-align: left;
|
||||
}
|
||||
table.dataTable thead th.dt-head-left,
|
||||
table.dataTable thead td.dt-head-left,
|
||||
table.dataTable tfoot th.dt-head-left,
|
||||
table.dataTable tfoot td.dt-head-left {
|
||||
text-align: left;
|
||||
}
|
||||
table.dataTable thead th.dt-head-center,
|
||||
table.dataTable thead td.dt-head-center,
|
||||
table.dataTable tfoot th.dt-head-center,
|
||||
table.dataTable tfoot td.dt-head-center {
|
||||
text-align: center;
|
||||
}
|
||||
table.dataTable thead th.dt-head-right,
|
||||
table.dataTable thead td.dt-head-right,
|
||||
table.dataTable tfoot th.dt-head-right,
|
||||
table.dataTable tfoot td.dt-head-right {
|
||||
text-align: right;
|
||||
}
|
||||
table.dataTable thead th.dt-head-justify,
|
||||
table.dataTable thead td.dt-head-justify,
|
||||
table.dataTable tfoot th.dt-head-justify,
|
||||
table.dataTable tfoot td.dt-head-justify {
|
||||
text-align: justify;
|
||||
}
|
||||
table.dataTable thead th.dt-head-nowrap,
|
||||
table.dataTable thead td.dt-head-nowrap,
|
||||
table.dataTable tfoot th.dt-head-nowrap,
|
||||
table.dataTable tfoot td.dt-head-nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
table.dataTable tbody th.dt-body-left,
|
||||
table.dataTable tbody td.dt-body-left {
|
||||
text-align: left;
|
||||
}
|
||||
table.dataTable tbody th.dt-body-center,
|
||||
table.dataTable tbody td.dt-body-center {
|
||||
text-align: center;
|
||||
}
|
||||
table.dataTable tbody th.dt-body-right,
|
||||
table.dataTable tbody td.dt-body-right {
|
||||
text-align: right;
|
||||
}
|
||||
table.dataTable tbody th.dt-body-justify,
|
||||
table.dataTable tbody td.dt-body-justify {
|
||||
text-align: justify;
|
||||
}
|
||||
table.dataTable tbody th.dt-body-nowrap,
|
||||
table.dataTable tbody td.dt-body-nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/*! Bootstrap 5 integration for DataTables
|
||||
*
|
||||
* ©2020 SpryMedia Ltd, all rights reserved.
|
||||
* License: MIT datatables.net/license/mit
|
||||
*/
|
||||
table.dataTable {
|
||||
clear: both;
|
||||
margin-top: 6px !important;
|
||||
margin-bottom: 6px !important;
|
||||
max-width: none !important;
|
||||
border-collapse: separate !important;
|
||||
border-spacing: 0;
|
||||
}
|
||||
table.dataTable td,
|
||||
table.dataTable th {
|
||||
-webkit-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
table.dataTable td.dataTables_empty,
|
||||
table.dataTable th.dataTables_empty {
|
||||
text-align: center;
|
||||
}
|
||||
table.dataTable.nowrap th,
|
||||
table.dataTable.nowrap td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
table.dataTable.table-striped > tbody > tr:nth-of-type(2n+1) > * {
|
||||
box-shadow: none;
|
||||
}
|
||||
table.dataTable > tbody > tr {
|
||||
background-color: transparent;
|
||||
}
|
||||
table.dataTable > tbody > tr.selected > * {
|
||||
box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.9);
|
||||
color: white;
|
||||
}
|
||||
table.dataTable > tbody > tr.selected a {
|
||||
color: #090a0b;
|
||||
}
|
||||
table.dataTable.table-striped > tbody > tr.odd > * {
|
||||
box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
table.dataTable.table-striped > tbody > tr.odd.selected > * {
|
||||
box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.95);
|
||||
}
|
||||
table.dataTable.table-hover > tbody > tr:hover > * {
|
||||
box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
table.dataTable.table-hover > tbody > tr.selected:hover > * {
|
||||
box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.975);
|
||||
}
|
||||
|
||||
div.dataTables_wrapper div.dataTables_length label {
|
||||
font-weight: normal;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
}
|
||||
div.dataTables_wrapper div.dataTables_length select {
|
||||
width: auto;
|
||||
display: inline-block;
|
||||
}
|
||||
div.dataTables_wrapper div.dataTables_filter {
|
||||
text-align: right;
|
||||
}
|
||||
div.dataTables_wrapper div.dataTables_filter label {
|
||||
font-weight: normal;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
}
|
||||
div.dataTables_wrapper div.dataTables_filter input {
|
||||
margin-left: 0.5em;
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
}
|
||||
div.dataTables_wrapper div.dataTables_info {
|
||||
padding-top: 0.85em;
|
||||
}
|
||||
div.dataTables_wrapper div.dataTables_paginate {
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
text-align: right;
|
||||
}
|
||||
div.dataTables_wrapper div.dataTables_paginate ul.pagination {
|
||||
margin: 2px 0;
|
||||
white-space: nowrap;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
div.dataTables_wrapper div.dt-row {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
div.dataTables_scrollHead table.dataTable {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
div.dataTables_scrollBody > table {
|
||||
border-top: none;
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
div.dataTables_scrollBody > table > thead .sorting:before,
|
||||
div.dataTables_scrollBody > table > thead .sorting_asc:before,
|
||||
div.dataTables_scrollBody > table > thead .sorting_desc:before,
|
||||
div.dataTables_scrollBody > table > thead .sorting:after,
|
||||
div.dataTables_scrollBody > table > thead .sorting_asc:after,
|
||||
div.dataTables_scrollBody > table > thead .sorting_desc:after {
|
||||
display: none;
|
||||
}
|
||||
div.dataTables_scrollBody > table > tbody tr:first-child th,
|
||||
div.dataTables_scrollBody > table > tbody tr:first-child td {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
div.dataTables_scrollFoot > .dataTables_scrollFootInner {
|
||||
box-sizing: content-box;
|
||||
}
|
||||
div.dataTables_scrollFoot > .dataTables_scrollFootInner > table {
|
||||
margin-top: 0 !important;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
div.dataTables_wrapper div.dataTables_length,
|
||||
div.dataTables_wrapper div.dataTables_filter,
|
||||
div.dataTables_wrapper div.dataTables_info,
|
||||
div.dataTables_wrapper div.dataTables_paginate {
|
||||
text-align: center;
|
||||
}
|
||||
div.dataTables_wrapper div.dataTables_paginate ul.pagination {
|
||||
justify-content: center !important;
|
||||
}
|
||||
}
|
||||
table.dataTable.table-sm > thead > tr > th:not(.sorting_disabled) {
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
table.table-bordered.dataTable {
|
||||
border-right-width: 0;
|
||||
}
|
||||
table.table-bordered.dataTable thead tr:first-child th,
|
||||
table.table-bordered.dataTable thead tr:first-child td {
|
||||
border-top-width: 1px;
|
||||
}
|
||||
table.table-bordered.dataTable th,
|
||||
table.table-bordered.dataTable td {
|
||||
border-left-width: 0;
|
||||
}
|
||||
table.table-bordered.dataTable th:first-child, table.table-bordered.dataTable th:first-child,
|
||||
table.table-bordered.dataTable td:first-child,
|
||||
table.table-bordered.dataTable td:first-child {
|
||||
border-left-width: 1px;
|
||||
}
|
||||
table.table-bordered.dataTable th:last-child, table.table-bordered.dataTable th:last-child,
|
||||
table.table-bordered.dataTable td:last-child,
|
||||
table.table-bordered.dataTable td:last-child {
|
||||
border-right-width: 1px;
|
||||
}
|
||||
table.table-bordered.dataTable th,
|
||||
table.table-bordered.dataTable td {
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
div.dataTables_scrollHead table.table-bordered {
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
div.table-responsive > div.dataTables_wrapper > div.row {
|
||||
margin: 0;
|
||||
}
|
||||
div.table-responsive > div.dataTables_wrapper > div.row > div[class^=col-]:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
div.table-responsive > div.dataTables_wrapper > div.row > div[class^=col-]:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr > td.child,
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr > th.child,
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr > td.dataTables_empty {
|
||||
cursor: default !important;
|
||||
}
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr > td.child:before,
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr > th.child:before,
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr > td.dataTables_empty:before {
|
||||
display: none !important;
|
||||
}
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr > td.dtr-control,
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr > th.dtr-control {
|
||||
position: relative;
|
||||
padding-left: 30px;
|
||||
cursor: pointer;
|
||||
}
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr > td.dtr-control:before,
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr > th.dtr-control:before {
|
||||
top: 50%;
|
||||
left: 5px;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
margin-top: -9px;
|
||||
display: block;
|
||||
position: absolute;
|
||||
color: white;
|
||||
border: 0.15em solid white;
|
||||
border-radius: 1em;
|
||||
box-shadow: 0 0 0.2em #444;
|
||||
box-sizing: content-box;
|
||||
text-align: center;
|
||||
text-indent: 0 !important;
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
line-height: 1em;
|
||||
content: "+";
|
||||
background-color: #0d6efd;
|
||||
}
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr.parent > td.dtr-control:before,
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr.parent > th.dtr-control:before {
|
||||
content: "-";
|
||||
background-color: #d33333;
|
||||
}
|
||||
table.dataTable.dtr-inline.collapsed.compact > tbody > tr > td.dtr-control,
|
||||
table.dataTable.dtr-inline.collapsed.compact > tbody > tr > th.dtr-control {
|
||||
padding-left: 27px;
|
||||
}
|
||||
table.dataTable.dtr-inline.collapsed.compact > tbody > tr > td.dtr-control:before,
|
||||
table.dataTable.dtr-inline.collapsed.compact > tbody > tr > th.dtr-control:before {
|
||||
left: 4px;
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
border-radius: 14px;
|
||||
line-height: 14px;
|
||||
text-indent: 3px;
|
||||
}
|
||||
table.dataTable.dtr-column > tbody > tr > td.dtr-control,
|
||||
table.dataTable.dtr-column > tbody > tr > th.dtr-control,
|
||||
table.dataTable.dtr-column > tbody > tr > td.control,
|
||||
table.dataTable.dtr-column > tbody > tr > th.control {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
table.dataTable.dtr-column > tbody > tr > td.dtr-control:before,
|
||||
table.dataTable.dtr-column > tbody > tr > th.dtr-control:before,
|
||||
table.dataTable.dtr-column > tbody > tr > td.control:before,
|
||||
table.dataTable.dtr-column > tbody > tr > th.control:before {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
height: 0.8em;
|
||||
width: 0.8em;
|
||||
margin-top: -0.5em;
|
||||
margin-left: -0.5em;
|
||||
display: block;
|
||||
position: absolute;
|
||||
color: white;
|
||||
border: 0.15em solid white;
|
||||
border-radius: 1em;
|
||||
box-shadow: 0 0 0.2em #444;
|
||||
box-sizing: content-box;
|
||||
text-align: center;
|
||||
text-indent: 0 !important;
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
line-height: 1em;
|
||||
content: "+";
|
||||
background-color: #0d6efd;
|
||||
}
|
||||
table.dataTable.dtr-column > tbody > tr.parent td.dtr-control:before,
|
||||
table.dataTable.dtr-column > tbody > tr.parent th.dtr-control:before,
|
||||
table.dataTable.dtr-column > tbody > tr.parent td.control:before,
|
||||
table.dataTable.dtr-column > tbody > tr.parent th.control:before {
|
||||
content: "-";
|
||||
background-color: #d33333;
|
||||
}
|
||||
table.dataTable > tbody > tr.child {
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
table.dataTable > tbody > tr.child:hover {
|
||||
background: transparent !important;
|
||||
}
|
||||
table.dataTable > tbody > tr.child ul.dtr-details {
|
||||
display: inline-block;
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
table.dataTable > tbody > tr.child ul.dtr-details > li {
|
||||
border-bottom: 1px solid #efefef;
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
table.dataTable > tbody > tr.child ul.dtr-details > li:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
table.dataTable > tbody > tr.child ul.dtr-details > li:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
table.dataTable > tbody > tr.child span.dtr-title {
|
||||
display: inline-block;
|
||||
min-width: 75px;
|
||||
font-weight: bold;
|
||||
}
|
||||
div.dtr-modal {
|
||||
position: fixed;
|
||||
box-sizing: border-box;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
z-index: 100;
|
||||
padding: 10em 1em;
|
||||
}
|
||||
div.dtr-modal div.dtr-modal-display {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 50%;
|
||||
height: 50%;
|
||||
overflow: auto;
|
||||
margin: auto;
|
||||
z-index: 102;
|
||||
overflow: auto;
|
||||
background-color: #f5f5f7;
|
||||
border: 1px solid black;
|
||||
border-radius: 0.5em;
|
||||
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
div.dtr-modal div.dtr-modal-content {
|
||||
position: relative;
|
||||
padding: 1em;
|
||||
}
|
||||
div.dtr-modal div.dtr-modal-close {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 6px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border: 1px solid #eaeaea;
|
||||
background-color: #f9f9f9;
|
||||
text-align: center;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
z-index: 12;
|
||||
}
|
||||
div.dtr-modal div.dtr-modal-close:hover {
|
||||
background-color: #eaeaea;
|
||||
}
|
||||
div.dtr-modal div.dtr-modal-background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 101;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
div.dtr-modal div.dtr-modal-display {
|
||||
width: 95%;
|
||||
}
|
||||
}
|
||||
div.dtr-bs-modal table.table tr:first-child td {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
table.dataTable.table-bordered th.dtr-control.dtr-hidden + *,
|
||||
table.dataTable.table-bordered td.dtr-control.dtr-hidden + * {
|
||||
border-left-width: 1px;
|
||||
}
|
||||
|
||||
|
||||
table.dataTable > tbody > tr > .selected {
|
||||
background-color: rgba(13, 110, 253, 0.9);
|
||||
color: white;
|
||||
}
|
||||
table.dataTable > tbody > tr > td.select-checkbox,
|
||||
table.dataTable > tbody > tr > th.select-checkbox {
|
||||
position: relative;
|
||||
}
|
||||
table.dataTable > tbody > tr > td.select-checkbox:before, table.dataTable > tbody > tr > td.select-checkbox:after,
|
||||
table.dataTable > tbody > tr > th.select-checkbox:before,
|
||||
table.dataTable > tbody > tr > th.select-checkbox:after {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 1.2em;
|
||||
left: 50%;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
table.dataTable > tbody > tr > td.select-checkbox:before,
|
||||
table.dataTable > tbody > tr > th.select-checkbox:before {
|
||||
content: " ";
|
||||
margin-top: -5px;
|
||||
margin-left: -6px;
|
||||
border: 1px solid black;
|
||||
border-radius: 3px;
|
||||
}
|
||||
table.dataTable > tbody > tr.selected > td.select-checkbox:before,
|
||||
table.dataTable > tbody > tr.selected > th.select-checkbox:before {
|
||||
border: 1px solid white;
|
||||
}
|
||||
table.dataTable > tbody > tr.selected > td.select-checkbox:after,
|
||||
table.dataTable > tbody > tr.selected > th.select-checkbox:after {
|
||||
content: "✓";
|
||||
font-size: 20px;
|
||||
margin-top: -19px;
|
||||
margin-left: -6px;
|
||||
text-align: center;
|
||||
text-shadow: 1px 1px #B0BED9, -1px -1px #B0BED9, 1px -1px #B0BED9, -1px 1px #B0BED9;
|
||||
}
|
||||
table.dataTable.compact > tbody > tr > td.select-checkbox:before,
|
||||
table.dataTable.compact > tbody > tr > th.select-checkbox:before {
|
||||
margin-top: -12px;
|
||||
}
|
||||
table.dataTable.compact > tbody > tr.selected > td.select-checkbox:after,
|
||||
table.dataTable.compact > tbody > tr.selected > th.select-checkbox:after {
|
||||
margin-top: -16px;
|
||||
}
|
||||
|
||||
div.dataTables_wrapper span.select-info,
|
||||
div.dataTables_wrapper span.select-item {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 640px) {
|
||||
div.dataTables_wrapper span.select-info,
|
||||
div.dataTables_wrapper span.select-item {
|
||||
margin-left: 0;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
table.dataTable.table-sm tbody td.select-checkbox::before {
|
||||
margin-top: -9px;
|
||||
}
|
||||
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
@keyframes chartjs-render-animation{from{opacity:.99}to{opacity:1}}.chartjs-render-monitor{animation:chartjs-render-animation 1ms}.chartjs-size-monitor,.chartjs-size-monitor-expand,.chartjs-size-monitor-shrink{position:absolute;direction:ltr;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1}.chartjs-size-monitor-expand>div{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0}
|
||||
+279
-2
@@ -1,7 +1,7 @@
|
||||
@font-face {
|
||||
font-family: "bootstrap-icons";
|
||||
src: url("/fonts/bootstrap-icons.woff2?45695e8b569b2b0178db2713ca47065c") format("woff2"),
|
||||
url("/fonts/bootstrap-icons.woff?45695e8b569b2b0178db2713ca47065c") format("woff");
|
||||
src: url("/fonts/bootstrap-icons.woff2?524846017b983fc8ded9325d94ed40f3") format("woff2"),
|
||||
url("/fonts/bootstrap-icons.woff?524846017b983fc8ded9325d94ed40f3") format("woff");
|
||||
}
|
||||
|
||||
.bi::before,
|
||||
@@ -19,6 +19,7 @@ url("/fonts/bootstrap-icons.woff?45695e8b569b2b0178db2713ca47065c") format("woff
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.bi-123::before { content: "\f67f"; }
|
||||
.bi-alarm-fill::before { content: "\f101"; }
|
||||
.bi-alarm::before { content: "\f102"; }
|
||||
.bi-align-bottom::before { content: "\f103"; }
|
||||
@@ -1425,3 +1426,279 @@ url("/fonts/bootstrap-icons.woff?45695e8b569b2b0178db2713ca47065c") format("woff
|
||||
.bi-webcam-fill::before { content: "\f67c"; }
|
||||
.bi-webcam::before { content: "\f67d"; }
|
||||
.bi-yin-yang::before { content: "\f67e"; }
|
||||
.bi-bandaid-fill::before { content: "\f680"; }
|
||||
.bi-bandaid::before { content: "\f681"; }
|
||||
.bi-bluetooth::before { content: "\f682"; }
|
||||
.bi-body-text::before { content: "\f683"; }
|
||||
.bi-boombox::before { content: "\f684"; }
|
||||
.bi-boxes::before { content: "\f685"; }
|
||||
.bi-dpad-fill::before { content: "\f686"; }
|
||||
.bi-dpad::before { content: "\f687"; }
|
||||
.bi-ear-fill::before { content: "\f688"; }
|
||||
.bi-ear::before { content: "\f689"; }
|
||||
.bi-envelope-check-1::before { content: "\f68a"; }
|
||||
.bi-envelope-check-fill::before { content: "\f68b"; }
|
||||
.bi-envelope-check::before { content: "\f68c"; }
|
||||
.bi-envelope-dash-1::before { content: "\f68d"; }
|
||||
.bi-envelope-dash-fill::before { content: "\f68e"; }
|
||||
.bi-envelope-dash::before { content: "\f68f"; }
|
||||
.bi-envelope-exclamation-1::before { content: "\f690"; }
|
||||
.bi-envelope-exclamation-fill::before { content: "\f691"; }
|
||||
.bi-envelope-exclamation::before { content: "\f692"; }
|
||||
.bi-envelope-plus-fill::before { content: "\f693"; }
|
||||
.bi-envelope-plus::before { content: "\f694"; }
|
||||
.bi-envelope-slash-1::before { content: "\f695"; }
|
||||
.bi-envelope-slash-fill::before { content: "\f696"; }
|
||||
.bi-envelope-slash::before { content: "\f697"; }
|
||||
.bi-envelope-x-1::before { content: "\f698"; }
|
||||
.bi-envelope-x-fill::before { content: "\f699"; }
|
||||
.bi-envelope-x::before { content: "\f69a"; }
|
||||
.bi-explicit-fill::before { content: "\f69b"; }
|
||||
.bi-explicit::before { content: "\f69c"; }
|
||||
.bi-git::before { content: "\f69d"; }
|
||||
.bi-infinity::before { content: "\f69e"; }
|
||||
.bi-list-columns-reverse::before { content: "\f69f"; }
|
||||
.bi-list-columns::before { content: "\f6a0"; }
|
||||
.bi-meta::before { content: "\f6a1"; }
|
||||
.bi-mortorboard-fill::before { content: "\f6a2"; }
|
||||
.bi-mortorboard::before { content: "\f6a3"; }
|
||||
.bi-nintendo-switch::before { content: "\f6a4"; }
|
||||
.bi-pc-display-horizontal::before { content: "\f6a5"; }
|
||||
.bi-pc-display::before { content: "\f6a6"; }
|
||||
.bi-pc-horizontal::before { content: "\f6a7"; }
|
||||
.bi-pc::before { content: "\f6a8"; }
|
||||
.bi-playstation::before { content: "\f6a9"; }
|
||||
.bi-plus-slash-minus::before { content: "\f6aa"; }
|
||||
.bi-projector-fill::before { content: "\f6ab"; }
|
||||
.bi-projector::before { content: "\f6ac"; }
|
||||
.bi-qr-code-scan::before { content: "\f6ad"; }
|
||||
.bi-qr-code::before { content: "\f6ae"; }
|
||||
.bi-quora::before { content: "\f6af"; }
|
||||
.bi-quote::before { content: "\f6b0"; }
|
||||
.bi-robot::before { content: "\f6b1"; }
|
||||
.bi-send-check-fill::before { content: "\f6b2"; }
|
||||
.bi-send-check::before { content: "\f6b3"; }
|
||||
.bi-send-dash-fill::before { content: "\f6b4"; }
|
||||
.bi-send-dash::before { content: "\f6b5"; }
|
||||
.bi-send-exclamation-1::before { content: "\f6b6"; }
|
||||
.bi-send-exclamation-fill::before { content: "\f6b7"; }
|
||||
.bi-send-exclamation::before { content: "\f6b8"; }
|
||||
.bi-send-fill::before { content: "\f6b9"; }
|
||||
.bi-send-plus-fill::before { content: "\f6ba"; }
|
||||
.bi-send-plus::before { content: "\f6bb"; }
|
||||
.bi-send-slash-fill::before { content: "\f6bc"; }
|
||||
.bi-send-slash::before { content: "\f6bd"; }
|
||||
.bi-send-x-fill::before { content: "\f6be"; }
|
||||
.bi-send-x::before { content: "\f6bf"; }
|
||||
.bi-send::before { content: "\f6c0"; }
|
||||
.bi-steam::before { content: "\f6c1"; }
|
||||
.bi-terminal-dash-1::before { content: "\f6c2"; }
|
||||
.bi-terminal-dash::before { content: "\f6c3"; }
|
||||
.bi-terminal-plus::before { content: "\f6c4"; }
|
||||
.bi-terminal-split::before { content: "\f6c5"; }
|
||||
.bi-ticket-detailed-fill::before { content: "\f6c6"; }
|
||||
.bi-ticket-detailed::before { content: "\f6c7"; }
|
||||
.bi-ticket-fill::before { content: "\f6c8"; }
|
||||
.bi-ticket-perforated-fill::before { content: "\f6c9"; }
|
||||
.bi-ticket-perforated::before { content: "\f6ca"; }
|
||||
.bi-ticket::before { content: "\f6cb"; }
|
||||
.bi-tiktok::before { content: "\f6cc"; }
|
||||
.bi-window-dash::before { content: "\f6cd"; }
|
||||
.bi-window-desktop::before { content: "\f6ce"; }
|
||||
.bi-window-fullscreen::before { content: "\f6cf"; }
|
||||
.bi-window-plus::before { content: "\f6d0"; }
|
||||
.bi-window-split::before { content: "\f6d1"; }
|
||||
.bi-window-stack::before { content: "\f6d2"; }
|
||||
.bi-window-x::before { content: "\f6d3"; }
|
||||
.bi-xbox::before { content: "\f6d4"; }
|
||||
.bi-ethernet::before { content: "\f6d5"; }
|
||||
.bi-hdmi-fill::before { content: "\f6d6"; }
|
||||
.bi-hdmi::before { content: "\f6d7"; }
|
||||
.bi-usb-c-fill::before { content: "\f6d8"; }
|
||||
.bi-usb-c::before { content: "\f6d9"; }
|
||||
.bi-usb-fill::before { content: "\f6da"; }
|
||||
.bi-usb-plug-fill::before { content: "\f6db"; }
|
||||
.bi-usb-plug::before { content: "\f6dc"; }
|
||||
.bi-usb-symbol::before { content: "\f6dd"; }
|
||||
.bi-usb::before { content: "\f6de"; }
|
||||
.bi-boombox-fill::before { content: "\f6df"; }
|
||||
.bi-displayport-1::before { content: "\f6e0"; }
|
||||
.bi-displayport::before { content: "\f6e1"; }
|
||||
.bi-gpu-card::before { content: "\f6e2"; }
|
||||
.bi-memory::before { content: "\f6e3"; }
|
||||
.bi-modem-fill::before { content: "\f6e4"; }
|
||||
.bi-modem::before { content: "\f6e5"; }
|
||||
.bi-motherboard-fill::before { content: "\f6e6"; }
|
||||
.bi-motherboard::before { content: "\f6e7"; }
|
||||
.bi-optical-audio-fill::before { content: "\f6e8"; }
|
||||
.bi-optical-audio::before { content: "\f6e9"; }
|
||||
.bi-pci-card::before { content: "\f6ea"; }
|
||||
.bi-router-fill::before { content: "\f6eb"; }
|
||||
.bi-router::before { content: "\f6ec"; }
|
||||
.bi-ssd-fill::before { content: "\f6ed"; }
|
||||
.bi-ssd::before { content: "\f6ee"; }
|
||||
.bi-thunderbolt-fill::before { content: "\f6ef"; }
|
||||
.bi-thunderbolt::before { content: "\f6f0"; }
|
||||
.bi-usb-drive-fill::before { content: "\f6f1"; }
|
||||
.bi-usb-drive::before { content: "\f6f2"; }
|
||||
.bi-usb-micro-fill::before { content: "\f6f3"; }
|
||||
.bi-usb-micro::before { content: "\f6f4"; }
|
||||
.bi-usb-mini-fill::before { content: "\f6f5"; }
|
||||
.bi-usb-mini::before { content: "\f6f6"; }
|
||||
.bi-cloud-haze2::before { content: "\f6f7"; }
|
||||
.bi-device-hdd-fill::before { content: "\f6f8"; }
|
||||
.bi-device-hdd::before { content: "\f6f9"; }
|
||||
.bi-device-ssd-fill::before { content: "\f6fa"; }
|
||||
.bi-device-ssd::before { content: "\f6fb"; }
|
||||
.bi-displayport-fill::before { content: "\f6fc"; }
|
||||
.bi-mortarboard-fill::before { content: "\f6fd"; }
|
||||
.bi-mortarboard::before { content: "\f6fe"; }
|
||||
.bi-terminal-x::before { content: "\f6ff"; }
|
||||
.bi-arrow-through-heart-fill::before { content: "\f700"; }
|
||||
.bi-arrow-through-heart::before { content: "\f701"; }
|
||||
.bi-badge-sd-fill::before { content: "\f702"; }
|
||||
.bi-badge-sd::before { content: "\f703"; }
|
||||
.bi-bag-heart-fill::before { content: "\f704"; }
|
||||
.bi-bag-heart::before { content: "\f705"; }
|
||||
.bi-balloon-fill::before { content: "\f706"; }
|
||||
.bi-balloon-heart-fill::before { content: "\f707"; }
|
||||
.bi-balloon-heart::before { content: "\f708"; }
|
||||
.bi-balloon::before { content: "\f709"; }
|
||||
.bi-box2-fill::before { content: "\f70a"; }
|
||||
.bi-box2-heart-fill::before { content: "\f70b"; }
|
||||
.bi-box2-heart::before { content: "\f70c"; }
|
||||
.bi-box2::before { content: "\f70d"; }
|
||||
.bi-braces-asterisk::before { content: "\f70e"; }
|
||||
.bi-calendar-heart-fill::before { content: "\f70f"; }
|
||||
.bi-calendar-heart::before { content: "\f710"; }
|
||||
.bi-calendar2-heart-fill::before { content: "\f711"; }
|
||||
.bi-calendar2-heart::before { content: "\f712"; }
|
||||
.bi-chat-heart-fill::before { content: "\f713"; }
|
||||
.bi-chat-heart::before { content: "\f714"; }
|
||||
.bi-chat-left-heart-fill::before { content: "\f715"; }
|
||||
.bi-chat-left-heart::before { content: "\f716"; }
|
||||
.bi-chat-right-heart-fill::before { content: "\f717"; }
|
||||
.bi-chat-right-heart::before { content: "\f718"; }
|
||||
.bi-chat-square-heart-fill::before { content: "\f719"; }
|
||||
.bi-chat-square-heart::before { content: "\f71a"; }
|
||||
.bi-clipboard-check-fill::before { content: "\f71b"; }
|
||||
.bi-clipboard-data-fill::before { content: "\f71c"; }
|
||||
.bi-clipboard-fill::before { content: "\f71d"; }
|
||||
.bi-clipboard-heart-fill::before { content: "\f71e"; }
|
||||
.bi-clipboard-heart::before { content: "\f71f"; }
|
||||
.bi-clipboard-minus-fill::before { content: "\f720"; }
|
||||
.bi-clipboard-plus-fill::before { content: "\f721"; }
|
||||
.bi-clipboard-pulse::before { content: "\f722"; }
|
||||
.bi-clipboard-x-fill::before { content: "\f723"; }
|
||||
.bi-clipboard2-check-fill::before { content: "\f724"; }
|
||||
.bi-clipboard2-check::before { content: "\f725"; }
|
||||
.bi-clipboard2-data-fill::before { content: "\f726"; }
|
||||
.bi-clipboard2-data::before { content: "\f727"; }
|
||||
.bi-clipboard2-fill::before { content: "\f728"; }
|
||||
.bi-clipboard2-heart-fill::before { content: "\f729"; }
|
||||
.bi-clipboard2-heart::before { content: "\f72a"; }
|
||||
.bi-clipboard2-minus-fill::before { content: "\f72b"; }
|
||||
.bi-clipboard2-minus::before { content: "\f72c"; }
|
||||
.bi-clipboard2-plus-fill::before { content: "\f72d"; }
|
||||
.bi-clipboard2-plus::before { content: "\f72e"; }
|
||||
.bi-clipboard2-pulse-fill::before { content: "\f72f"; }
|
||||
.bi-clipboard2-pulse::before { content: "\f730"; }
|
||||
.bi-clipboard2-x-fill::before { content: "\f731"; }
|
||||
.bi-clipboard2-x::before { content: "\f732"; }
|
||||
.bi-clipboard2::before { content: "\f733"; }
|
||||
.bi-emoji-kiss-fill::before { content: "\f734"; }
|
||||
.bi-emoji-kiss::before { content: "\f735"; }
|
||||
.bi-envelope-heart-fill::before { content: "\f736"; }
|
||||
.bi-envelope-heart::before { content: "\f737"; }
|
||||
.bi-envelope-open-heart-fill::before { content: "\f738"; }
|
||||
.bi-envelope-open-heart::before { content: "\f739"; }
|
||||
.bi-envelope-paper-fill::before { content: "\f73a"; }
|
||||
.bi-envelope-paper-heart-fill::before { content: "\f73b"; }
|
||||
.bi-envelope-paper-heart::before { content: "\f73c"; }
|
||||
.bi-envelope-paper::before { content: "\f73d"; }
|
||||
.bi-filetype-aac::before { content: "\f73e"; }
|
||||
.bi-filetype-ai::before { content: "\f73f"; }
|
||||
.bi-filetype-bmp::before { content: "\f740"; }
|
||||
.bi-filetype-cs::before { content: "\f741"; }
|
||||
.bi-filetype-css::before { content: "\f742"; }
|
||||
.bi-filetype-csv::before { content: "\f743"; }
|
||||
.bi-filetype-doc::before { content: "\f744"; }
|
||||
.bi-filetype-docx::before { content: "\f745"; }
|
||||
.bi-filetype-exe::before { content: "\f746"; }
|
||||
.bi-filetype-gif::before { content: "\f747"; }
|
||||
.bi-filetype-heic::before { content: "\f748"; }
|
||||
.bi-filetype-html::before { content: "\f749"; }
|
||||
.bi-filetype-java::before { content: "\f74a"; }
|
||||
.bi-filetype-jpg::before { content: "\f74b"; }
|
||||
.bi-filetype-js::before { content: "\f74c"; }
|
||||
.bi-filetype-jsx::before { content: "\f74d"; }
|
||||
.bi-filetype-key::before { content: "\f74e"; }
|
||||
.bi-filetype-m4p::before { content: "\f74f"; }
|
||||
.bi-filetype-md::before { content: "\f750"; }
|
||||
.bi-filetype-mdx::before { content: "\f751"; }
|
||||
.bi-filetype-mov::before { content: "\f752"; }
|
||||
.bi-filetype-mp3::before { content: "\f753"; }
|
||||
.bi-filetype-mp4::before { content: "\f754"; }
|
||||
.bi-filetype-otf::before { content: "\f755"; }
|
||||
.bi-filetype-pdf::before { content: "\f756"; }
|
||||
.bi-filetype-php::before { content: "\f757"; }
|
||||
.bi-filetype-png::before { content: "\f758"; }
|
||||
.bi-filetype-ppt-1::before { content: "\f759"; }
|
||||
.bi-filetype-ppt::before { content: "\f75a"; }
|
||||
.bi-filetype-psd::before { content: "\f75b"; }
|
||||
.bi-filetype-py::before { content: "\f75c"; }
|
||||
.bi-filetype-raw::before { content: "\f75d"; }
|
||||
.bi-filetype-rb::before { content: "\f75e"; }
|
||||
.bi-filetype-sass::before { content: "\f75f"; }
|
||||
.bi-filetype-scss::before { content: "\f760"; }
|
||||
.bi-filetype-sh::before { content: "\f761"; }
|
||||
.bi-filetype-svg::before { content: "\f762"; }
|
||||
.bi-filetype-tiff::before { content: "\f763"; }
|
||||
.bi-filetype-tsx::before { content: "\f764"; }
|
||||
.bi-filetype-ttf::before { content: "\f765"; }
|
||||
.bi-filetype-txt::before { content: "\f766"; }
|
||||
.bi-filetype-wav::before { content: "\f767"; }
|
||||
.bi-filetype-woff::before { content: "\f768"; }
|
||||
.bi-filetype-xls-1::before { content: "\f769"; }
|
||||
.bi-filetype-xls::before { content: "\f76a"; }
|
||||
.bi-filetype-xml::before { content: "\f76b"; }
|
||||
.bi-filetype-yml::before { content: "\f76c"; }
|
||||
.bi-heart-arrow::before { content: "\f76d"; }
|
||||
.bi-heart-pulse-fill::before { content: "\f76e"; }
|
||||
.bi-heart-pulse::before { content: "\f76f"; }
|
||||
.bi-heartbreak-fill::before { content: "\f770"; }
|
||||
.bi-heartbreak::before { content: "\f771"; }
|
||||
.bi-hearts::before { content: "\f772"; }
|
||||
.bi-hospital-fill::before { content: "\f773"; }
|
||||
.bi-hospital::before { content: "\f774"; }
|
||||
.bi-house-heart-fill::before { content: "\f775"; }
|
||||
.bi-house-heart::before { content: "\f776"; }
|
||||
.bi-incognito::before { content: "\f777"; }
|
||||
.bi-magnet-fill::before { content: "\f778"; }
|
||||
.bi-magnet::before { content: "\f779"; }
|
||||
.bi-person-heart::before { content: "\f77a"; }
|
||||
.bi-person-hearts::before { content: "\f77b"; }
|
||||
.bi-phone-flip::before { content: "\f77c"; }
|
||||
.bi-plugin::before { content: "\f77d"; }
|
||||
.bi-postage-fill::before { content: "\f77e"; }
|
||||
.bi-postage-heart-fill::before { content: "\f77f"; }
|
||||
.bi-postage-heart::before { content: "\f780"; }
|
||||
.bi-postage::before { content: "\f781"; }
|
||||
.bi-postcard-fill::before { content: "\f782"; }
|
||||
.bi-postcard-heart-fill::before { content: "\f783"; }
|
||||
.bi-postcard-heart::before { content: "\f784"; }
|
||||
.bi-postcard::before { content: "\f785"; }
|
||||
.bi-search-heart-fill::before { content: "\f786"; }
|
||||
.bi-search-heart::before { content: "\f787"; }
|
||||
.bi-sliders2-vertical::before { content: "\f788"; }
|
||||
.bi-sliders2::before { content: "\f789"; }
|
||||
.bi-trash3-fill::before { content: "\f78a"; }
|
||||
.bi-trash3::before { content: "\f78b"; }
|
||||
.bi-valentine::before { content: "\f78c"; }
|
||||
.bi-valentine2::before { content: "\f78d"; }
|
||||
.bi-wrench-adjustable-circle-fill::before { content: "\f78e"; }
|
||||
.bi-wrench-adjustable-circle::before { content: "\f78f"; }
|
||||
.bi-wrench-adjustable::before { content: "\f790"; }
|
||||
.bi-filetype-json::before { content: "\f791"; }
|
||||
.bi-filetype-pptx::before { content: "\f792"; }
|
||||
.bi-filetype-xlsx::before { content: "\f793"; }
|
||||
@@ -0,0 +1,98 @@
|
||||
.dataTables_info {
|
||||
margin: 15px 0 !important;
|
||||
padding: 0px !important;
|
||||
}
|
||||
.dataTables_paginate, .dataTables_length, .dataTables_filter {
|
||||
margin: 15px 0 !important;
|
||||
}
|
||||
.dtr-details {
|
||||
width: 100%;
|
||||
}
|
||||
.table-striped>tbody>tr:nth-of-type(odd) {
|
||||
background-color: #F2F2F2;
|
||||
}
|
||||
td.child>ul>li {
|
||||
display: flex;
|
||||
}
|
||||
table.dataTable>tbody>tr.child ul.dtr-details>li {
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.129);
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control:before:hover,
|
||||
table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control:before:hover {
|
||||
background-color: #5e5e5e;
|
||||
}
|
||||
table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control:before,
|
||||
table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control:before,
|
||||
table.dataTable td.dt-control:before {
|
||||
background-color: #979797 !important;
|
||||
border: 1.5px solid #616161 !important;
|
||||
border-radius: 2px !important;
|
||||
color: #fff;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
line-height: 1.25em;
|
||||
border-radius: 0px;
|
||||
box-shadow: none;
|
||||
font-size: 14px;
|
||||
transition: 0.5s all;
|
||||
}
|
||||
table.dataTable.dtr-inline.collapsed>tbody>tr.parent>td.dtr-control:before,
|
||||
table.dataTable.dtr-inline.collapsed>tbody>tr.parent>th.dtr-control:before,
|
||||
table.dataTable td.dt-control:before {
|
||||
background-color: #979797 !important;
|
||||
}
|
||||
table.dataTable.dtr-inline.collapsed>tbody>tr>td.child,
|
||||
table.dataTable.dtr-inline.collapsed>tbody>tr>th.child,
|
||||
table.dataTable.dtr-inline.collapsed>tbody>tr>td.dataTables_empty {
|
||||
background-color: #fbfbfb;
|
||||
}
|
||||
table.dataTable.table-striped>tbody>tr>td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
table.dataTable.table-striped>tbody>tr>td>input[type="checkbox"] {
|
||||
margin-top: 7px;
|
||||
}
|
||||
td.dtr-col-lg {
|
||||
min-width: 350px;
|
||||
word-break: break-word;
|
||||
}
|
||||
td.dtr-col-md {
|
||||
min-width: 250px;
|
||||
word-break: break-word;
|
||||
}
|
||||
td.dtr-col-sm {
|
||||
min-width: 125px;
|
||||
word-break: break-word;
|
||||
}
|
||||
.dt-data-w100 .dtr-data {
|
||||
width: 100%;
|
||||
}
|
||||
li .dtr-data {
|
||||
word-break: break-all;
|
||||
flex: 1;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
table.dataTable>tbody>tr.child span.dtr-title {
|
||||
width: 30%;
|
||||
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;
|
||||
}
|
||||
@@ -63,6 +63,17 @@
|
||||
.navbar-nav {
|
||||
margin: 0;
|
||||
}
|
||||
.navbar-nav .nav-item {
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
padding: 0 10px !important;
|
||||
}
|
||||
.navbar-nav .nav-link {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 10px !important;
|
||||
}
|
||||
.navbar-fixed-bottom .navbar-collapse,
|
||||
.navbar-fixed-top .navbar-collapse {
|
||||
max-height: 1000px
|
||||
@@ -75,6 +86,12 @@
|
||||
display: inline-block;
|
||||
font-size: inherit;
|
||||
}
|
||||
.btn-group-xs > .btn, .btn-xs {
|
||||
padding: .25rem .4rem;
|
||||
font-size: .875rem;
|
||||
line-height: 1rem;
|
||||
border-radius: .2rem;
|
||||
}
|
||||
.icon-spin {
|
||||
animation-name: spin;
|
||||
animation-duration: 2000ms;
|
||||
@@ -105,13 +122,22 @@
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
pre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;}
|
||||
.footable-sortable {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
@keyframes blink {
|
||||
50% {
|
||||
color: transparent
|
||||
}
|
||||
}
|
||||
.loader-dot {
|
||||
animation: 1s blink infinite
|
||||
}
|
||||
.loader-dot:nth-child(2) {
|
||||
animation-delay: 250ms
|
||||
}
|
||||
.loader-dot:nth-child(3) {
|
||||
animation-delay: 500ms
|
||||
}
|
||||
|
||||
pre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;}
|
||||
/* Fix modal moving content left */
|
||||
body.modal-open {
|
||||
overflow: inherit;
|
||||
@@ -166,19 +192,11 @@ legend {
|
||||
top: 0; right: 0; bottom: 0; left: 0;
|
||||
opacity: 0.7;
|
||||
}
|
||||
#top {
|
||||
padding-top: 70px;
|
||||
}
|
||||
.bootstrap-select.btn-group .no-results {
|
||||
display: none;
|
||||
}
|
||||
.dropdown-desc {
|
||||
display: block;
|
||||
padding: 3px 10px;
|
||||
clear: both;
|
||||
font-weight: bold;
|
||||
color: #5a5a5a;
|
||||
white-space: nowrap;
|
||||
.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary {
|
||||
color: rgb(197, 197, 197) !important;
|
||||
}
|
||||
.haveibeenpwned {
|
||||
cursor: pointer;
|
||||
@@ -206,7 +224,7 @@ legend {
|
||||
flex-direction: column;
|
||||
}
|
||||
.footer .version {
|
||||
margin-left: auto;
|
||||
margin-left: auto;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.slave-info {
|
||||
@@ -225,15 +243,8 @@ legend {
|
||||
.btn-input-missing:active:hover,
|
||||
.btn-input-missing:active:focus {
|
||||
color: #000 !important;
|
||||
background-color: #ff4136;
|
||||
border-color: #ff291c;
|
||||
}
|
||||
table.footable>tbody>tr.footable-empty>td {
|
||||
font-style:italic;
|
||||
font-size: 1rem;
|
||||
}
|
||||
table>tbody>tr>td>span.footable-toggle {
|
||||
opacity: 0.75;
|
||||
background-color: #ff2f24 !important;
|
||||
border-color: #e21207 !important;
|
||||
}
|
||||
.navbar-nav > li {
|
||||
font-size: 1rem !important;
|
||||
@@ -260,18 +271,11 @@ code {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.list-group-item.webauthn-authenticator-selection,
|
||||
.list-group-item.totp-authenticator-selection,
|
||||
.list-group-item.yubi_otp-authenticator-selection {
|
||||
border-radius: 0px !important;
|
||||
}
|
||||
.pending-tfa-collapse {
|
||||
padding: 10px;
|
||||
background: #fbfbfb;
|
||||
border: 1px solid #ededed;
|
||||
min-height: 110px;
|
||||
.dropdown-header {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
|
||||
.tag-box {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -295,7 +299,7 @@ code {
|
||||
}
|
||||
.tag-input {
|
||||
margin-left: 10px;
|
||||
border: 0;
|
||||
border: 0 !important;
|
||||
flex: 1;
|
||||
height: 24px;
|
||||
min-width: 150px;
|
||||
@@ -308,3 +312,61 @@ code {
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
#dnstable {
|
||||
overflow-x: auto!important;
|
||||
}
|
||||
.well {
|
||||
border: 1px solid #dfdfdf;
|
||||
background-color: #f9f9f9;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
|
||||
.btn-check-label {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.caret {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
a[aria-expanded='true'] > .caret,
|
||||
button[aria-expanded='true'] > .caret {
|
||||
transform: rotate(-180deg);
|
||||
}
|
||||
|
||||
.list-group-details {
|
||||
background: #fff;
|
||||
}
|
||||
.list-group-header {
|
||||
background: #f7f7f7;
|
||||
}
|
||||
|
||||
|
||||
.bg-primary, .alert-primary, .btn-primary {
|
||||
background-color: #0F688D !important;
|
||||
border-color: #0d526d !important;
|
||||
}
|
||||
.bg-info, .alert-info, .btn-info {
|
||||
background-color: #148DBC !important;
|
||||
border-color: #127ea8 !important;
|
||||
}
|
||||
|
||||
.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary {
|
||||
color: rgb(137 137 137)!important;
|
||||
}
|
||||
|
||||
.progress {
|
||||
background-color: #d5d5d5;
|
||||
}
|
||||
|
||||
|
||||
.btn-outline-secondary:hover {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
.btn.btn-outline-secondary {
|
||||
border-color: #cfcfcf !important;
|
||||
}
|
||||
.btn-check:checked+.btn-outline-secondary, .btn-check:active+.btn-outline-secondary, .btn-outline-secondary:active, .btn-outline-secondary.active, .btn-outline-secondary.dropdown-toggle.show {
|
||||
background-color: #f0f0f0 !important;
|
||||
}
|
||||
@@ -1,308 +0,0 @@
|
||||
.space20 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.btn-xs-lg>.lang-sm:after {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.bootstrap-select {
|
||||
max-width: 350px;
|
||||
}
|
||||
|
||||
.panel-login .apps .btn {
|
||||
width: auto;
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
margin-top: auto;
|
||||
}
|
||||
.panel-login .apps .btn:hover {
|
||||
margin-top: 1px !important;
|
||||
border-bottom-width: 3px;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.panel-login .apps .btn {
|
||||
width: 100%;
|
||||
float: none;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.panel-login .apps .btn {
|
||||
border-bottom-width: 4px;
|
||||
}
|
||||
|
||||
.media-clearfix::after {
|
||||
clear: both;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.media-clearfix::before {
|
||||
display: table;
|
||||
content: " ";
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.xs-show {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.js-tabcollapse-panel-group .panel{
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.js-tabcollapse-panel-group .panel-body {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.js-tabcollapse-panel-group .js-tabcollapse-panel-body .panel-body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.js-tabcollapse-panel-body .panel-heading {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.js-tabcollapse-panel-body .well,
|
||||
.panel-body .form-inline.well {
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.js-tabcollapse-panel-heading {
|
||||
display: block;
|
||||
height: 37px;
|
||||
line-height: 37px;
|
||||
text-indent: 15px;
|
||||
}
|
||||
.js-tabcollapse-panel-heading:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.js-tabcollapse-panel-heading {
|
||||
position: relative;
|
||||
}
|
||||
.js-tabcollapse-panel-heading:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 17px;
|
||||
right: 17px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-left: 2px;
|
||||
vertical-align: middle;
|
||||
border-bottom: 4px dashed;
|
||||
border-right: 4px solid transparent;
|
||||
border-left: 4px solid transparent;
|
||||
}
|
||||
.js-tabcollapse-panel-heading.collapsed:after {
|
||||
border-bottom: none;
|
||||
border-top: 4px dashed;
|
||||
}
|
||||
|
||||
.recent-login-success {
|
||||
font-size: 14px;
|
||||
margin-top: 10px !important;
|
||||
}
|
||||
.pull-xs-right {
|
||||
float: right !important;
|
||||
}
|
||||
.pull-xs-right .dropdown-menu {
|
||||
right: 0;
|
||||
left: auto;
|
||||
}
|
||||
.text-xs-left {
|
||||
text-align: left;
|
||||
}
|
||||
.text-xs-bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.text-xs-bold .small {
|
||||
font-weight: normal;
|
||||
text-align: justify;
|
||||
}
|
||||
.help-block {
|
||||
text-align: justify;
|
||||
}
|
||||
.btn.visible-xs-block {
|
||||
width: 100%;
|
||||
float: none;
|
||||
white-space: normal;
|
||||
}
|
||||
.btn-group.footable-actions .btn.btn-xs-half,
|
||||
.btn.visible-xs-block.btn-xs-half {
|
||||
width: 50%;
|
||||
float: left;
|
||||
}
|
||||
.btn-group.footable-actions .btn.btn-xs-third,
|
||||
.btn.visible-xs-block.btn-xs-third {
|
||||
width: 33.33%;
|
||||
float: left;
|
||||
}
|
||||
.btn-group.footable-actions .btn.btn-xs-quart,
|
||||
.btn.visible-xs-block.btn-xs-quart {
|
||||
width: 25%;
|
||||
float: left;
|
||||
}
|
||||
.btn.visible-xs-block.btn-sm,
|
||||
.btn-xs-lg {
|
||||
padding: 15px 16px 13px;
|
||||
line-height: 15px;
|
||||
}
|
||||
.input-xs-lg {
|
||||
height: 47px;
|
||||
padding: 13px 16px;
|
||||
}
|
||||
.btn-group:not(.input-group-btn) {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.btn-group.nowrap {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
.btn-group.nowrap .dropdown-menu {
|
||||
width: 100%;
|
||||
}
|
||||
.panel-login .btn-group {
|
||||
display: block;
|
||||
}
|
||||
.mass-actions-user .btn-group {
|
||||
float: none;
|
||||
}
|
||||
div[class^='mass-actions'] .dropdown-menu,
|
||||
.panel-xs-lg .dropdown-menu,
|
||||
.dropdown-menu.login {
|
||||
width: 100%;
|
||||
}
|
||||
div[class^='mass-actions'] .btn-group .dropdown-menu {
|
||||
top: 50%;
|
||||
}
|
||||
div[class^='mass-actions'] .btn-group .btn-group .dropdown-menu,
|
||||
div.mass-actions-quarantine .btn-group .dropdown-menu,
|
||||
.panel-xs-lg .dropdown-menu {
|
||||
top: 100%;
|
||||
}
|
||||
div[class^='mass-actions'] .dropdown-menu>li>a,
|
||||
.panel-xs-lg .dropdown-menu>li>a,
|
||||
.dropdown-menu.login>li>a {
|
||||
padding: 8px 20px;
|
||||
}
|
||||
div[class^='mass-actions'] .dropdown-header {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.space20 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.top100 {
|
||||
top: 100% !important;
|
||||
}
|
||||
.top33 {
|
||||
top: 33% !important;
|
||||
}
|
||||
.footable-filtering .form {
|
||||
width: 65%;
|
||||
}
|
||||
.btn-xs-lg>.lang-sm:after {
|
||||
top: 1px;
|
||||
}
|
||||
table.footable>tfoot>tr.footable-paging>td {
|
||||
text-align: left;
|
||||
}
|
||||
.footable-first-visible {
|
||||
min-width: 55px;
|
||||
}
|
||||
table>tbody>tr>td>span.footable-toggle {
|
||||
font-size: 24px;
|
||||
margin-right: 14px !important;
|
||||
}
|
||||
table>tbody>tr>td>span.footable-toggle + input {
|
||||
position: absolute;
|
||||
left: 38px;
|
||||
}
|
||||
.pagination {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
tr.footable-filtering>th>form {
|
||||
width: 270px;
|
||||
}
|
||||
.mass-actions-mailbox {
|
||||
padding: 0;
|
||||
}
|
||||
.panel-xs-lg .panel-heading {
|
||||
height: 66px;
|
||||
line-height: 47px;
|
||||
}
|
||||
.panel-xs-lg .btn-group .btn {
|
||||
padding-right: 5px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
.bootstrap-select:not([class*=col-]):not([class*=form-control]):not(.input-group-btn) {
|
||||
width: 100%;
|
||||
}
|
||||
.btn-group:not(.bootstrap-select) {
|
||||
width: auto !important;
|
||||
}
|
||||
.bootstrap-select {
|
||||
max-width: 100%;
|
||||
}
|
||||
.img-responsive {
|
||||
margin: 0 auto;
|
||||
}
|
||||
.btn-group.footable-actions {
|
||||
position: absolute;
|
||||
width: 90vw !important;
|
||||
left: 0;
|
||||
height: 36px;
|
||||
margin-top: -8px;
|
||||
}
|
||||
.btn-group.footable-actions .btn {
|
||||
padding: 10px 16px 7px;
|
||||
line-height: 15px;
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
.btn-group.footable-actions:after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
.bootstrap-select.btn-group.show-tick .dropdown-menu li a span.text {
|
||||
margin-right: 14px;
|
||||
white-space: normal;
|
||||
}
|
||||
.clearfix {
|
||||
flex-basis: 100%;
|
||||
height: 0;
|
||||
}
|
||||
.btn-group > .btn-group {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
.btn-group .btn {
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.btn-group .btn i {
|
||||
margin-right: 5px;
|
||||
}
|
||||
.btn-group .btn .caret {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.panel-login .btn-group .btn {
|
||||
display: block !important;
|
||||
}
|
||||
.panel-login .clearfix {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 350px) {
|
||||
.mailcow-logo img {
|
||||
max-width: 250px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
.btn-xs-lg>.lang-sm:after {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.bootstrap-select {
|
||||
max-width: 350px;
|
||||
}
|
||||
|
||||
.card-login .apps .btn {
|
||||
width: auto;
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
margin-top: auto;
|
||||
}
|
||||
.card-login .apps .btn:hover {
|
||||
margin-top: 1px !important;
|
||||
border-bottom-width: 3px;
|
||||
}
|
||||
|
||||
.responsive-tabs .nav-tabs {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dataTables_paginate.paging_simple_numbers .pagination {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.responsive-tabs .nav-tabs {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.responsive-tabs .card .card-body.collapse {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.responsive-tabs .tab-pane {
|
||||
display: block !important;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.card-login .apps .btn {
|
||||
width: 100%;
|
||||
float: none;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.card-login .apps .btn {
|
||||
border-bottom-width: 4px;
|
||||
}
|
||||
|
||||
.xs-show {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.recent-login-success {
|
||||
font-size: 14px;
|
||||
margin-top: 10px !important;
|
||||
}
|
||||
.pull-xs-right {
|
||||
float: right !important;
|
||||
}
|
||||
.pull-xs-right .dropdown-menu {
|
||||
right: 0;
|
||||
left: auto;
|
||||
}
|
||||
.text-xs-left {
|
||||
text-align: left;
|
||||
}
|
||||
.text-xs-bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.text-xs-bold .small {
|
||||
font-weight: normal;
|
||||
text-align: justify;
|
||||
}
|
||||
.btn.d-block {
|
||||
width: 100%;
|
||||
white-space: normal;
|
||||
}
|
||||
.btn.btn-xs-half,
|
||||
.btn.d-block.btn-xs-half {
|
||||
width: 50%;
|
||||
}
|
||||
.btn.btn-xs-third,
|
||||
.btn.d-block.btn-xs-third {
|
||||
width: 33.33%;
|
||||
}
|
||||
.btn.btn-xs-quart,
|
||||
.btn.d-block.btn-xs-quart {
|
||||
width: 25%;
|
||||
}
|
||||
.btn.d-block.btn-sm,
|
||||
.btn-xs-lg {
|
||||
padding: .5rem 1rem;
|
||||
line-height: 20px;
|
||||
}
|
||||
.input-xs-lg {
|
||||
height: 47px;
|
||||
padding: 13px 16px;
|
||||
}
|
||||
.btn-group:not(.input-group-btn) {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.btn-group.nowrap {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
.btn-group.nowrap .dropdown-menu {
|
||||
width: 100%;
|
||||
}
|
||||
.card-login .btn-group {
|
||||
display: block;
|
||||
}
|
||||
.mass-actions-user .btn-group {
|
||||
float: none;
|
||||
}
|
||||
div[class^='mass-actions'] .dropdown-menu,
|
||||
.card-xs-lg .dropdown-menu,
|
||||
.dropdown-menu.login {
|
||||
width: 100%;
|
||||
}
|
||||
div[class^='mass-actions'] .btn-group .dropdown-menu {
|
||||
top: 50%;
|
||||
}
|
||||
div[class^='mass-actions'] .btn-group .btn-group .dropdown-menu,
|
||||
div.mass-actions-quarantine .btn-group .dropdown-menu,
|
||||
.card-xs-lg .dropdown-menu {
|
||||
top: 100%;
|
||||
}
|
||||
div[class^='mass-actions'] .dropdown-menu>li>a,
|
||||
.card-xs-lg .dropdown-menu>li>a,
|
||||
.dropdown-menu.login>li>a {
|
||||
padding: 8px 20px;
|
||||
}
|
||||
div[class^='mass-actions'] .dropdown-header {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.top100 {
|
||||
top: 100% !important;
|
||||
}
|
||||
.top33 {
|
||||
top: 33% !important;
|
||||
}
|
||||
.footable-filtering .form {
|
||||
width: 65%;
|
||||
}
|
||||
.btn-xs-lg>.lang-sm:after {
|
||||
top: 1px;
|
||||
}
|
||||
.pagination {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.mass-actions-mailbox {
|
||||
padding: 0;
|
||||
}
|
||||
.card-xs-lg .card-header {
|
||||
height: 66px;
|
||||
line-height: 47px;
|
||||
}
|
||||
.card-xs-lg .btn-group .btn {
|
||||
padding-right: 5px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
.bootstrap-select:not([class*=col-]):not([class*=form-control]):not(.input-group-btn) {
|
||||
width: 100%;
|
||||
}
|
||||
.btn-group:not(.bootstrap-select) {
|
||||
width: auto !important;
|
||||
}
|
||||
.bootstrap-select {
|
||||
max-width: 100%;
|
||||
}
|
||||
.bootstrap-select.btn-group.show-tick .dropdown-menu li a span.text {
|
||||
margin-right: 14px;
|
||||
white-space: normal;
|
||||
}
|
||||
.btn-group > .btn-group {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
.btn-group .btn {
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.btn-group .btn i {
|
||||
margin-right: 5px;
|
||||
}
|
||||
.card-login .btn-group .btn {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.dt-sm-head-hidden .dtr-title {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
div.dataTables_wrapper div.dataTables_length {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.senders-mw220 {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 350px) {
|
||||
.mailcow-logo img {
|
||||
max-width: 250px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1400px) {
|
||||
.container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container {
|
||||
max-width: 1600px;
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,6 @@ body.modal-open {
|
||||
}
|
||||
.mass-actions-admin {
|
||||
user-select: none;
|
||||
padding:10px 0 10px 0;
|
||||
}
|
||||
.inputMissingAttr {
|
||||
border-color: #FF4136;
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
}
|
||||
.mass-actions-debug {
|
||||
user-select: none;
|
||||
padding:10px 0 10px 10px;
|
||||
}
|
||||
.inputMissingAttr {
|
||||
border-color: #FF4136;
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
}
|
||||
.mass-actions-user {
|
||||
user-select: none;
|
||||
padding:10px 0 10px 0;
|
||||
}
|
||||
.inputMissingAttr {
|
||||
border-color: #FF4136;
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
}
|
||||
.mass-actions-mailbox {
|
||||
user-select: none;
|
||||
padding:10px 0 10px 10px;
|
||||
}
|
||||
.inputMissingAttr {
|
||||
border-color: #FF4136;
|
||||
|
||||
+104
-103
@@ -1,103 +1,104 @@
|
||||
.pagination a {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.panel.panel-default {
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.table-responsive {
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.table-responsive {
|
||||
overflow-x: scroll !important;
|
||||
}
|
||||
|
||||
.footer-add-item {
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
padding: 10px;
|
||||
background: #F5F5F5;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.container {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@media (min-width: 1920px) {
|
||||
.container {
|
||||
width: 80%;
|
||||
}
|
||||
}
|
||||
|
||||
.mass-actions-quarantine {
|
||||
user-select: none;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.inputMissingAttr {
|
||||
border-color: #FF4136;
|
||||
}
|
||||
|
||||
.modal#qidDetailModal p {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
span#qid_detail_score {
|
||||
font-weight: 700;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
span.rspamd-symbol {
|
||||
display: inline-block;
|
||||
margin: 2px 6px 2px 0;
|
||||
border-radius: 4px;
|
||||
padding: 0 7px;
|
||||
}
|
||||
|
||||
span.rspamd-symbol.positive {
|
||||
background: #4CAF50;
|
||||
border: 1px solid #4CAF50;
|
||||
color: white;
|
||||
}
|
||||
|
||||
span.rspamd-symbol.negative {
|
||||
background: #ff4136;
|
||||
border: 1px solid #ff4136;
|
||||
color: white;
|
||||
}
|
||||
|
||||
span.rspamd-symbol.neutral {
|
||||
background: #f5f5f5;
|
||||
color: #333;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
span.rspamd-symbol span.score {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
span.mail-address-item {
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ccc;
|
||||
padding: 2px 7px;
|
||||
display: inline-block;
|
||||
margin: 2px 6px 2px 0;
|
||||
}
|
||||
|
||||
table tbody tr {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
table tbody tr td input[type="checkbox"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
.label-rspamd-action {
|
||||
font-size:110%;
|
||||
margin:20px;
|
||||
}
|
||||
|
||||
.pagination a {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.panel.panel-default {
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.table-responsive {
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.table-responsive {
|
||||
overflow-x: scroll !important;
|
||||
}
|
||||
|
||||
.footer-add-item {
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
padding: 10px;
|
||||
background: #F5F5F5;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.container {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@media (min-width: 1920px) {
|
||||
.container {
|
||||
width: 80%;
|
||||
}
|
||||
}
|
||||
|
||||
.mass-actions-quarantine {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.inputMissingAttr {
|
||||
border-color: #FF4136;
|
||||
}
|
||||
|
||||
.modal#qidDetailModal p {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
span#qid_detail_score {
|
||||
font-weight: 700;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
span.rspamd-symbol {
|
||||
display: inline-block;
|
||||
margin: 2px 6px 2px 0;
|
||||
border-radius: 4px;
|
||||
padding: 0 7px;
|
||||
}
|
||||
|
||||
span.rspamd-symbol.positive {
|
||||
background: #4CAF50;
|
||||
border: 1px solid #4CAF50;
|
||||
color: white;
|
||||
}
|
||||
|
||||
span.rspamd-symbol.negative {
|
||||
background: #ff4136;
|
||||
border: 1px solid #ff4136;
|
||||
color: white;
|
||||
}
|
||||
|
||||
span.rspamd-symbol.neutral {
|
||||
background: #f5f5f5;
|
||||
color: #333;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
span.rspamd-symbol span.score {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
span.mail-address-item {
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ccc;
|
||||
padding: 2px 7px;
|
||||
display: inline-block;
|
||||
margin: 2px 6px 2px 0;
|
||||
}
|
||||
|
||||
table tbody tr {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
table tbody tr td input[type="checkbox"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
.label-rspamd-action {
|
||||
font-size:110%;
|
||||
margin:20px;
|
||||
}
|
||||
.senders-mw220 {
|
||||
max-width: 220px;
|
||||
}
|
||||
@@ -21,7 +21,6 @@
|
||||
}
|
||||
.mass-actions-user {
|
||||
user-select: none;
|
||||
padding:10px 0;
|
||||
}
|
||||
.inputMissingAttr {
|
||||
border-color: #FF4136;
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,368 @@
|
||||
body {
|
||||
background-color: #414141;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
.card {
|
||||
border: 1px solid #1c1c1c;
|
||||
background-color: #3a3a3a;
|
||||
}
|
||||
legend {
|
||||
color: #f5f5f5;
|
||||
}
|
||||
.card-header {
|
||||
color: #bbb;
|
||||
background-color: #2c2c2c;
|
||||
border-color: transparent;
|
||||
}
|
||||
.btn-secondary, .paginate_button, .page-link, .btn-light {
|
||||
color: #fff !important;
|
||||
background-color: #7a7a7a !important;
|
||||
border-color: #5c5c5c !important;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
.alert-secondary {
|
||||
color: #fff !important;
|
||||
background-color: #7a7a7a !important;
|
||||
border-color: #5c5c5c !important;
|
||||
}
|
||||
.bg-secondary {
|
||||
color: #fff !important;
|
||||
background-color: #7a7a7a !important;
|
||||
}
|
||||
.alert-secondary, .alert-secondary a, .alert-secondary .alert-link {
|
||||
color: #fff;
|
||||
}
|
||||
.page-item.active .page-link {
|
||||
background-color: #158cba !important;
|
||||
border-color: #127ba3 !important;
|
||||
}
|
||||
.btn-secondary:focus, .btn-secondary:hover, .btn-group.open .dropdown-toggle.btn-secondary {
|
||||
background-color: #7a7a7a;
|
||||
border-color: #5c5c5c !important;
|
||||
color: #fff;
|
||||
}
|
||||
.btn-secondary:disabled, .btn-secondary.disabled {
|
||||
border-color: #7a7a7a !important;
|
||||
}
|
||||
.modal-content {
|
||||
background-color: #414141;
|
||||
}
|
||||
.modal-header {
|
||||
border-bottom: 1px solid #161616;
|
||||
}
|
||||
.modal-title {
|
||||
color: white;
|
||||
}
|
||||
.modal .btn-close {
|
||||
filter: invert(1) grayscale(100%) brightness(200%);
|
||||
}
|
||||
.navbar.bg-light {
|
||||
background-color: #222222 !important;
|
||||
border-color: #181818;
|
||||
}
|
||||
.nav-link {
|
||||
color: #ccc !important;
|
||||
}
|
||||
.nav-tabs .nav-link.active, .nav-tabs .nav-item.show .nav-link {
|
||||
background: none;
|
||||
}
|
||||
.nav-tabs .nav-link:not(.disabled):hover, .nav-tabs .nav-link:not(.disabled):focus, .nav-tabs .nav-link.active {
|
||||
border-bottom-color: #414141;
|
||||
}
|
||||
|
||||
.table, .table-striped>tbody>tr:nth-of-type(odd)>*, tbody tr {
|
||||
color: #ccc !important;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
background-color: #585858;
|
||||
border: 1px solid #333;
|
||||
}
|
||||
.dropdown-menu>li>a:focus, .dropdown-menu>li>a:hover {
|
||||
color: #fafafa;
|
||||
}
|
||||
|
||||
.bootstrap-select>.dropdown-toggle.bs-placeholder, .bootstrap-select>.dropdown-toggle.bs-placeholder:active, .bootstrap-select>.dropdown-toggle.bs-placeholder:focus, .bootstrap-select>.dropdown-toggle.bs-placeholder:hover {
|
||||
color: #fff;
|
||||
}
|
||||
.bootstrap-select>.dropdown-toggle.bs-placeholder, .bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary {
|
||||
color: #d4d4d4 !important;
|
||||
}
|
||||
tbody tr {
|
||||
color: #555;
|
||||
}
|
||||
.navbar-default .navbar-nav>.open>a, .navbar-default .navbar-nav>.open>a:focus, .navbar-default .navbar-nav>.open>a:hover {
|
||||
color: #ccc;
|
||||
}
|
||||
.navbar-default .navbar-nav>.active>a, .navbar-default .navbar-nav>.active>a:focus, .navbar-default .navbar-nav>.active>a:hover {
|
||||
color: #ccc;
|
||||
}
|
||||
.list-group-item {
|
||||
background-color: #333;
|
||||
border: 1px solid #555;
|
||||
}
|
||||
.table-striped>tbody>tr:nth-of-type(odd) {
|
||||
background-color: #333;
|
||||
}
|
||||
table.dataTable>tbody>tr.child ul.dtr-details>li {
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.13);
|
||||
}
|
||||
tbody tr {
|
||||
color: #ccc;
|
||||
}
|
||||
.label.label-last-login {
|
||||
color: #ccc !important;
|
||||
background-color: #555 !important;
|
||||
}
|
||||
.progress {
|
||||
background-color: #555;
|
||||
}
|
||||
.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
|
||||
color: #ccc;
|
||||
}
|
||||
div.numberedtextarea-number {
|
||||
color: #999;
|
||||
}
|
||||
.well {
|
||||
border: 1px solid #555;
|
||||
background-color: #333;
|
||||
}
|
||||
pre {
|
||||
color: #ccc;
|
||||
background-color: #333;
|
||||
border: 1px solid #555;
|
||||
}
|
||||
input.form-control, textarea.form-control {
|
||||
color: #e2e2e2 !important;
|
||||
background-color: #555 !important;
|
||||
border: 1px solid #999;
|
||||
}
|
||||
input.form-control:focus, textarea.form-control {
|
||||
background-color: #555 !important;
|
||||
}
|
||||
input.form-control:disabled, textarea.form-disabled {
|
||||
color: #a8a8a8 !important;
|
||||
background-color: #6e6e6e !important;
|
||||
}
|
||||
.input-group-addon {
|
||||
color: #ccc;
|
||||
background-color: #555 !important;
|
||||
border: 1px solid #999;
|
||||
}
|
||||
.input-group-text {
|
||||
color: #ccc;
|
||||
background-color: #242424;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.list-group-item {
|
||||
color: #ccc;
|
||||
}
|
||||
.dropdown-item {
|
||||
color: #ccc;
|
||||
}
|
||||
.dropdown-item:hover {
|
||||
color: #616161 !important;
|
||||
}
|
||||
.dropdown-item.active:hover {
|
||||
color: #fff !important;
|
||||
background-color: #31b1e4;
|
||||
}
|
||||
.form-select {
|
||||
color: #e2e2e2!important;
|
||||
background-color: #555!important;
|
||||
border: 1px solid #999;
|
||||
}
|
||||
|
||||
.responsive-tabs .card-header button[data-bs-toggle="collapse"] {
|
||||
color: #c7c7c7;
|
||||
}
|
||||
|
||||
.navbar-toggler {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
|
||||
.table-secondary {
|
||||
--bs-table-bg: #7a7a7a;
|
||||
--bs-table-striped-bg: #e4e4e4;
|
||||
--bs-table-striped-color: #000;
|
||||
--bs-table-active-bg: #d8d8d8;
|
||||
--bs-table-active-color: #000;
|
||||
--bs-table-hover-bg: #dedede;
|
||||
--bs-table-hover-color: #000;
|
||||
color: #000;
|
||||
border-color: #d8d8d8;
|
||||
}
|
||||
|
||||
.table-light {
|
||||
--bs-table-bg: #f6f6f6;
|
||||
--bs-table-striped-bg: #eaeaea;
|
||||
--bs-table-striped-color: #000;
|
||||
--bs-table-active-bg: #dddddd;
|
||||
--bs-table-active-color: #000;
|
||||
--bs-table-hover-bg: #e4e4e4;
|
||||
--bs-table-hover-color: #000;
|
||||
color: #000;
|
||||
border-color: #dddddd;
|
||||
}
|
||||
|
||||
.form-control-plaintext {
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
|
||||
.breadcrumb {
|
||||
color: #fff !important;
|
||||
background-color: #7a7a7a !important;
|
||||
border-color: #5c5c5c !important;
|
||||
}
|
||||
|
||||
|
||||
a {
|
||||
color: #6fc7e9;
|
||||
text-decoration: underline;
|
||||
}
|
||||
a:hover {
|
||||
color: #3daedb;
|
||||
}
|
||||
|
||||
.breadcrumb-item.active {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
|
||||
.list-group-item.disabled, .list-group-item:disabled {
|
||||
color: #fff;
|
||||
background-color: #a8a8a8 !important;
|
||||
border-color: #a8a8a8;
|
||||
}
|
||||
|
||||
.card.bg-light .card-title {
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
.card.bg-light .card-text {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.accordion-item {
|
||||
background-color: #3a3a3a;
|
||||
}
|
||||
.accordion-button {
|
||||
color: #bbb;
|
||||
background-color: #2c2c2c;
|
||||
}
|
||||
.accordion-button:not(.collapsed) {
|
||||
color: #6fc7e9;
|
||||
background-color: #2c2c2c;
|
||||
}
|
||||
.accordion-button::after {
|
||||
background-image: none;
|
||||
}
|
||||
.accordion-button:not(.collapsed)::after {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
|
||||
.toast-header {
|
||||
background-color: #4c4c4c;
|
||||
}
|
||||
.toast-body {
|
||||
background-color: #626262;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.tag-box {
|
||||
background-color: #555;
|
||||
border: 1px solid #999;
|
||||
}
|
||||
.tag-input {
|
||||
color: #fff;
|
||||
background-color: #555;
|
||||
}
|
||||
.tag-add {
|
||||
color: #ccc;
|
||||
}
|
||||
.tag-add:hover {
|
||||
color: #d1d1d1;
|
||||
}
|
||||
|
||||
|
||||
table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control:before:hover,
|
||||
table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control:before:hover {
|
||||
background-color: #7a7a7a !important;
|
||||
}
|
||||
table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control:before,
|
||||
table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control:before {
|
||||
background-color: #7a7a7a !important;
|
||||
border: 1.5px solid #5c5c5c !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
table.dataTable.dtr-inline.collapsed>tbody>tr.parent>td.dtr-control:before,
|
||||
table.dataTable.dtr-inline.collapsed>tbody>tr.parent>th.dtr-control:before {
|
||||
background-color: #949494;
|
||||
}
|
||||
table.dataTable.dtr-inline.collapsed>tbody>tr>td.child,
|
||||
table.dataTable.dtr-inline.collapsed>tbody>tr>th.child,
|
||||
table.dataTable.dtr-inline.collapsed>tbody>tr>td.dataTables_empty {
|
||||
background-color: #444444;
|
||||
}
|
||||
|
||||
.btn-check-label {
|
||||
color: #fff;
|
||||
}
|
||||
.btn-outline-secondary:hover {
|
||||
background-color: #c3c3c3;
|
||||
}
|
||||
.btn.btn-outline-secondary {
|
||||
color: #fff !important;
|
||||
border-color: #7a7a7a !important;
|
||||
}
|
||||
.btn-check:checked+.btn-outline-secondary, .btn-check:active+.btn-outline-secondary, .btn-outline-secondary:active, .btn-outline-secondary.active, .btn-outline-secondary.dropdown-toggle.show {
|
||||
background-color: #9b9b9b !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.btn-input-missing,
|
||||
.btn-input-missing:hover,
|
||||
.btn-input-missing:active,
|
||||
.btn-input-missing:focus,
|
||||
.btn-input-missing:active:hover,
|
||||
.btn-input-missing:active:focus {
|
||||
color: #fff !important;
|
||||
background-color: #ff2f24 !important;
|
||||
border-color: #e21207 !important;
|
||||
}
|
||||
|
||||
.inputMissingAttr {
|
||||
border-color: #FF4136 !important;
|
||||
}
|
||||
|
||||
|
||||
.list-group-details {
|
||||
background: #444444;
|
||||
}
|
||||
.list-group-header {
|
||||
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;
|
||||
}
|
||||
@@ -11,6 +11,11 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
||||
$solr_status = (preg_match("/^([yY][eE][sS]|[yY])+$/", $_ENV["SKIP_SOLR"])) ? false : solr_status();
|
||||
$clamd_status = (preg_match("/^([yY][eE][sS]|[yY])+$/", $_ENV["SKIP_CLAMD"])) ? false : true;
|
||||
|
||||
|
||||
if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CACHE')) {
|
||||
$_SESSION['gal'] = json_decode($license_cache, true);
|
||||
}
|
||||
|
||||
$js_minifier->add('/web/js/site/debug.js');
|
||||
|
||||
// vmail df
|
||||
@@ -44,15 +49,26 @@ foreach ($containers as $container => $container_info) {
|
||||
$containers[$container]['State']['StartedAtHR'] = $started;
|
||||
}
|
||||
|
||||
// get mailcow data
|
||||
$hostname = getenv('MAILCOW_HOSTNAME');
|
||||
$timezone = getenv('TZ');
|
||||
|
||||
$template = 'debug.twig';
|
||||
$template_data = [
|
||||
'log_lines' => getenv('LOG_LINES'),
|
||||
'vmail_df' => $vmail_df,
|
||||
'hostname' => $hostname,
|
||||
'timezone' => $timezone,
|
||||
'gal' => @$_SESSION['gal'],
|
||||
'license_guid' => license('guid'),
|
||||
'solr_status' => $solr_status,
|
||||
'solr_uptime' => round($solr_status['status']['dovecot-fts']['uptime'] / 1000 / 60 / 60),
|
||||
'clamd_status' => $clamd_status,
|
||||
'containers' => $containers,
|
||||
'ip_check' => customize('get', 'ip_check'),
|
||||
'lang_admin' => json_encode($lang['admin']),
|
||||
'lang_debug' => json_encode($lang['debug']),
|
||||
'lang_datatables' => json_encode($lang['datatables']),
|
||||
];
|
||||
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/footer.inc.php';
|
||||
|
||||
+65
-39
@@ -38,24 +38,46 @@ if (isset($_SESSION['mailcow_cc_role'])) {
|
||||
$template = 'edit/admin.twig';
|
||||
$template_data = ['admin' => $admin];
|
||||
}
|
||||
elseif (isset($_GET['domain']) &&
|
||||
is_valid_domain_name($_GET["domain"]) &&
|
||||
!empty($_GET["domain"])) {
|
||||
$domain = $_GET["domain"];
|
||||
$result = mailbox('get', 'domain_details', $domain);
|
||||
$quota_notification_bcc = quota_notification_bcc('get', $domain);
|
||||
$rl = ratelimit('get', 'domain', $domain);
|
||||
$rlyhosts = relayhost('get');
|
||||
$template = 'edit/domain.twig';
|
||||
elseif (isset($_GET['domain'])) {
|
||||
if (is_valid_domain_name($_GET["domain"]) &&
|
||||
!empty($_GET["domain"])) {
|
||||
// edit domain
|
||||
$domain = $_GET["domain"];
|
||||
$result = mailbox('get', 'domain_details', $domain);
|
||||
$quota_notification_bcc = quota_notification_bcc('get', $domain);
|
||||
$rl = ratelimit('get', 'domain', $domain);
|
||||
$rlyhosts = relayhost('get');
|
||||
$template = 'edit/domain.twig';
|
||||
$template_data = [
|
||||
'acl' => $_SESSION['acl'],
|
||||
'domain' => $domain,
|
||||
'quota_notification_bcc' => $quota_notification_bcc,
|
||||
'rl' => $rl,
|
||||
'rlyhosts' => $rlyhosts,
|
||||
'dkim' => dkim('details', $domain),
|
||||
'domain_details' => $result,
|
||||
];
|
||||
}
|
||||
}
|
||||
elseif (isset($_GET["template"])){
|
||||
$domain_template = mailbox('get', 'domain_templates', $_GET["template"]);
|
||||
if ($domain_template){
|
||||
$template_data = [
|
||||
'acl' => $_SESSION['acl'],
|
||||
'domain' => $domain,
|
||||
'quota_notification_bcc' => $quota_notification_bcc,
|
||||
'rl' => $rl,
|
||||
'rlyhosts' => $rlyhosts,
|
||||
'dkim' => dkim('details', $domain),
|
||||
'domain_details' => $result,
|
||||
'template' => $domain_template
|
||||
];
|
||||
$template = 'edit/domain-templates.twig';
|
||||
$result = true;
|
||||
}
|
||||
else {
|
||||
$mailbox_template = mailbox('get', 'mailbox_templates', $_GET["template"]);
|
||||
if ($mailbox_template){
|
||||
$template_data = [
|
||||
'template' => $mailbox_template
|
||||
];
|
||||
$template = 'edit/mailbox-templates.twig';
|
||||
$result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif (isset($_GET['oauth2client']) &&
|
||||
is_numeric($_GET["oauth2client"]) &&
|
||||
@@ -79,29 +101,32 @@ if (isset($_SESSION['mailcow_cc_role'])) {
|
||||
'dkim' => dkim('details', $alias_domain),
|
||||
];
|
||||
}
|
||||
elseif (isset($_GET['mailbox']) && filter_var(html_entity_decode(rawurldecode($_GET["mailbox"])), FILTER_VALIDATE_EMAIL) && !empty($_GET["mailbox"])) {
|
||||
$mailbox = html_entity_decode(rawurldecode($_GET["mailbox"]));
|
||||
$result = mailbox('get', 'mailbox_details', $mailbox);
|
||||
$rl = ratelimit('get', 'mailbox', $mailbox);
|
||||
$pushover_data = pushover('get', $mailbox);
|
||||
$quarantine_notification = mailbox('get', 'quarantine_notification', $mailbox);
|
||||
$quarantine_category = mailbox('get', 'quarantine_category', $mailbox);
|
||||
$get_tls_policy = mailbox('get', 'tls_policy', $mailbox);
|
||||
$rlyhosts = relayhost('get');
|
||||
$template = 'edit/mailbox.twig';
|
||||
$template_data = [
|
||||
'acl' => $_SESSION['acl'],
|
||||
'mailbox' => $mailbox,
|
||||
'rl' => $rl,
|
||||
'pushover_data' => $pushover_data,
|
||||
'quarantine_notification' => $quarantine_notification,
|
||||
'quarantine_category' => $quarantine_category,
|
||||
'get_tls_policy' => $get_tls_policy,
|
||||
'rlyhosts' => $rlyhosts,
|
||||
'sender_acl_handles' => mailbox('get', 'sender_acl_handles', $mailbox),
|
||||
'user_acls' => acl('get', 'user', $mailbox),
|
||||
'mailbox_details' => $result
|
||||
];
|
||||
elseif (isset($_GET['mailbox'])){
|
||||
if(filter_var(html_entity_decode(rawurldecode($_GET["mailbox"])), FILTER_VALIDATE_EMAIL) && !empty($_GET["mailbox"])) {
|
||||
// edit mailbox
|
||||
$mailbox = html_entity_decode(rawurldecode($_GET["mailbox"]));
|
||||
$result = mailbox('get', 'mailbox_details', $mailbox);
|
||||
$rl = ratelimit('get', 'mailbox', $mailbox);
|
||||
$pushover_data = pushover('get', $mailbox);
|
||||
$quarantine_notification = mailbox('get', 'quarantine_notification', $mailbox);
|
||||
$quarantine_category = mailbox('get', 'quarantine_category', $mailbox);
|
||||
$get_tls_policy = mailbox('get', 'tls_policy', $mailbox);
|
||||
$rlyhosts = relayhost('get');
|
||||
$template = 'edit/mailbox.twig';
|
||||
$template_data = [
|
||||
'acl' => $_SESSION['acl'],
|
||||
'mailbox' => $mailbox,
|
||||
'rl' => $rl,
|
||||
'pushover_data' => $pushover_data,
|
||||
'quarantine_notification' => $quarantine_notification,
|
||||
'quarantine_category' => $quarantine_category,
|
||||
'get_tls_policy' => $get_tls_policy,
|
||||
'rlyhosts' => $rlyhosts,
|
||||
'sender_acl_handles' => mailbox('get', 'sender_acl_handles', $mailbox),
|
||||
'user_acls' => acl('get', 'user', $mailbox),
|
||||
'mailbox_details' => $result
|
||||
];
|
||||
}
|
||||
}
|
||||
elseif (isset($_GET['relayhost']) && is_numeric($_GET["relayhost"]) && !empty($_GET["relayhost"])) {
|
||||
$relayhost = intval($_GET["relayhost"]);
|
||||
@@ -189,5 +214,6 @@ $js_minifier->add('/web/js/site/pwgen.js');
|
||||
$template_data['result'] = $result;
|
||||
$template_data['return_to'] = $_SESSION['return_to'];
|
||||
$template_data['lang_user'] = json_encode($lang['user']);
|
||||
$template_data['lang_datatables'] = json_encode($lang['datatables']);
|
||||
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/footer.inc.php';
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File renamed without changes.
Binary file not shown.
|
After Width: | Height: | Size: 7.4 KiB |
@@ -436,7 +436,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
||||
}
|
||||
?>
|
||||
</table>
|
||||
<a id='download-zonefile' class="btn btn-sm btn-default visible-xs-block visible-sm-inline visible-md-inline visible-lg-inline" style="margin-top:10px" data-zonefile="<?=base64_encode($dns_data);?>" download='<?=$_GET['domain'];?>.txt' type='text/csv'>Download</a>
|
||||
<a id='download-zonefile' class="btn btn-sm btn-secondary visible-xs-block visible-sm-inline visible-md-inline visible-lg-inline mb-4" style="margin-top:10px" data-zonefile="<?=base64_encode($dns_data);?>" download='<?=$_GET['domain'];?>.txt' type='text/csv'>Download</a>
|
||||
<script>
|
||||
var zonefile_dl_link = document.getElementById('download-zonefile');
|
||||
var zonefile = atob(zonefile_dl_link.getAttribute('data-zonefile'));
|
||||
|
||||
@@ -2,8 +2,18 @@
|
||||
function customize($_action, $_item, $_data = null) {
|
||||
global $redis;
|
||||
global $lang;
|
||||
|
||||
switch ($_action) {
|
||||
case 'add':
|
||||
// disable functionality when demo mode is enabled
|
||||
if ($GLOBALS["DEMO_MODE"]) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_item, $_data),
|
||||
'msg' => 'demo_mode_enabled'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
@@ -72,6 +82,15 @@ function customize($_action, $_item, $_data = null) {
|
||||
}
|
||||
break;
|
||||
case 'edit':
|
||||
// disable functionality when demo mode is enabled
|
||||
if ($GLOBALS["DEMO_MODE"]) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_item, $_data),
|
||||
'msg' => 'demo_mode_enabled'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
@@ -116,6 +135,7 @@ function customize($_action, $_item, $_data = null) {
|
||||
$ui_announcement_text = $_data['ui_announcement_text'];
|
||||
$ui_announcement_type = (in_array($_data['ui_announcement_type'], array('info', 'warning', 'danger'))) ? $_data['ui_announcement_type'] : false;
|
||||
$ui_announcement_active = (!empty($_data['ui_announcement_active']) ? 1 : 0);
|
||||
|
||||
try {
|
||||
$redis->set('TITLE_NAME', htmlspecialchars($title_name));
|
||||
$redis->set('MAIN_NAME', htmlspecialchars($main_name));
|
||||
@@ -140,9 +160,37 @@ function customize($_action, $_item, $_data = null) {
|
||||
'msg' => 'ui_texts'
|
||||
);
|
||||
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;
|
||||
case 'delete':
|
||||
// disable functionality when demo mode is enabled
|
||||
if ($GLOBALS["DEMO_MODE"]) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_item, $_data),
|
||||
'msg' => 'demo_mode_enabled'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
@@ -247,6 +295,20 @@ function customize($_action, $_item, $_data = null) {
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $extra_headers = null) {
|
||||
global $DOCKER_TIMEOUT;
|
||||
global $redis;
|
||||
$curl = curl_init();
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER,array('Content-Type: application/json' ));
|
||||
// We are using our mail certificates for dockerapi, the names will not match, the certs are trusted anyway
|
||||
@@ -32,6 +33,7 @@ function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $ex
|
||||
}
|
||||
}
|
||||
return false;
|
||||
break;
|
||||
case 'containers':
|
||||
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/json');
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||
@@ -51,6 +53,7 @@ function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $ex
|
||||
if (strtolower($container['Config']['Labels']['com.docker.compose.project']) == strtolower(getenv('COMPOSE_PROJECT_NAME'))) {
|
||||
$out[$container['Config']['Labels']['com.docker.compose.service']]['State'] = $container['State'];
|
||||
$out[$container['Config']['Labels']['com.docker.compose.service']]['Config'] = $container['Config'];
|
||||
$out[$container['Config']['Labels']['com.docker.compose.service']]['Id'] = trim($container['Id']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -94,6 +97,7 @@ function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $ex
|
||||
unset($container['Config']['Env']);
|
||||
$out[$container['Config']['Labels']['com.docker.compose.service']]['State'] = $container['State'];
|
||||
$out[$container['Config']['Labels']['com.docker.compose.service']]['Config'] = $container['Config'];
|
||||
$out[$container['Config']['Labels']['com.docker.compose.service']]['Id'] = trim($container['Id']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,6 +107,7 @@ function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $ex
|
||||
unset($container['Config']['Env']);
|
||||
$out[$decoded_response['Config']['Labels']['com.docker.compose.service']]['State'] = $decoded_response['State'];
|
||||
$out[$decoded_response['Config']['Labels']['com.docker.compose.service']]['Config'] = $decoded_response['Config'];
|
||||
$out[$decoded_response['Config']['Labels']['com.docker.compose.service']]['Id'] = trim($decoded_response['Id']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,5 +151,46 @@ function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $ex
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'container_stats':
|
||||
if (empty($service_name)){
|
||||
return false;
|
||||
}
|
||||
|
||||
$container_id = $service_name;
|
||||
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/container/' . $container_id . '/stats/update');
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($curl, CURLOPT_POST, 1);
|
||||
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
|
||||
$response = curl_exec($curl);
|
||||
if ($response === false) {
|
||||
$err = curl_error($curl);
|
||||
curl_close($curl);
|
||||
return $err;
|
||||
}
|
||||
else {
|
||||
curl_close($curl);
|
||||
$stats = json_decode($response, true);
|
||||
if (!empty($stats)) return $stats;
|
||||
}
|
||||
return false;
|
||||
break;
|
||||
case 'host_stats':
|
||||
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/host/stats');
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($curl, CURLOPT_POST, 0);
|
||||
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
|
||||
$response = curl_exec($curl);
|
||||
if ($response === false) {
|
||||
$err = curl_error($curl);
|
||||
curl_close($curl);
|
||||
return $err;
|
||||
}
|
||||
else {
|
||||
curl_close($curl);
|
||||
$stats = json_decode($response, true);
|
||||
if (!empty($stats)) return $stats;
|
||||
}
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1,407 +1,468 @@
|
||||
<?php
|
||||
function domain_admin($_action, $_data = null) {
|
||||
global $pdo;
|
||||
global $lang;
|
||||
$_data_log = $_data;
|
||||
!isset($_data_log['password']) ?: $_data_log['password'] = '*';
|
||||
!isset($_data_log['password2']) ?: $_data_log['password2'] = '*';
|
||||
!isset($_data_log['user_old_pass']) ?: $_data_log['user_old_pass'] = '*';
|
||||
!isset($_data_log['user_new_pass']) ?: $_data_log['user_new_pass'] = '*';
|
||||
!isset($_data_log['user_new_pass2']) ?: $_data_log['user_new_pass2'] = '*';
|
||||
switch ($_action) {
|
||||
case 'add':
|
||||
$username = strtolower(trim($_data['username']));
|
||||
$password = $_data['password'];
|
||||
$password2 = $_data['password2'];
|
||||
$domains = (array)$_data['domains'];
|
||||
$active = intval($_data['active']);
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (empty($domains)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'domain_invalid'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username)) || empty ($username) || $username == 'API') {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('username_invalid', $username)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox`
|
||||
WHERE `username` = :username");
|
||||
$stmt->execute(array(':username' => $username));
|
||||
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||
|
||||
$stmt = $pdo->prepare("SELECT `username` FROM `admin`
|
||||
WHERE `username` = :username");
|
||||
$stmt->execute(array(':username' => $username));
|
||||
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||
|
||||
$stmt = $pdo->prepare("SELECT `username` FROM `domain_admins`
|
||||
WHERE `username` = :username");
|
||||
$stmt->execute(array(':username' => $username));
|
||||
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||
|
||||
foreach ($num_results as $num_results_each) {
|
||||
if ($num_results_each != 0) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('object_exists', htmlspecialchars($username))
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (password_check($password, $password2) !== true) {
|
||||
continue;
|
||||
}
|
||||
$password_hashed = hash_password($password);
|
||||
$valid_domains = 0;
|
||||
foreach ($domains as $domain) {
|
||||
if (!is_valid_domain_name($domain) || mailbox('get', 'domain_details', $domain) === false) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('domain_invalid', htmlspecialchars($domain))
|
||||
);
|
||||
continue;
|
||||
}
|
||||
$valid_domains++;
|
||||
$stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
|
||||
VALUES (:username, :domain, :created, :active)");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
':domain' => $domain,
|
||||
':created' => date('Y-m-d H:i:s'),
|
||||
':active' => $active
|
||||
));
|
||||
}
|
||||
if ($valid_domains != 0) {
|
||||
$stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `active`)
|
||||
VALUES (:username, :password_hashed, '0', :active)");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
':password_hashed' => $password_hashed,
|
||||
':active' => $active
|
||||
));
|
||||
}
|
||||
$stmt = $pdo->prepare("INSERT INTO `da_acl` (`username`) VALUES (:username)");
|
||||
$stmt->execute(array(
|
||||
':username' => $username
|
||||
));
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('domain_admin_added', htmlspecialchars($username))
|
||||
);
|
||||
break;
|
||||
case 'edit':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
// Administrator
|
||||
if ($_SESSION['mailcow_cc_role'] == "admin") {
|
||||
if (!is_array($_data['username'])) {
|
||||
$usernames = array();
|
||||
$usernames[] = $_data['username'];
|
||||
}
|
||||
else {
|
||||
$usernames = $_data['username'];
|
||||
}
|
||||
foreach ($usernames as $username) {
|
||||
$is_now = domain_admin('details', $username);
|
||||
$domains = (isset($_data['domains'])) ? (array)$_data['domains'] : null;
|
||||
if (!empty($is_now)) {
|
||||
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
|
||||
$domains = (!empty($domains)) ? $domains : $is_now['selected_domains'];
|
||||
$username_new = (!empty($_data['username_new'])) ? $_data['username_new'] : $is_now['username'];
|
||||
}
|
||||
else {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
continue;
|
||||
}
|
||||
$password = $_data['password'];
|
||||
$password2 = $_data['password2'];
|
||||
if (!empty($domains)) {
|
||||
foreach ($domains as $domain) {
|
||||
if (!is_valid_domain_name($domain) || mailbox('get', 'domain_details', $domain) === false) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('domain_invalid', htmlspecialchars($domain))
|
||||
);
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username_new))) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('username_invalid', $username_new)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if ($username_new != $username) {
|
||||
if (!empty(domain_admin('details', $username_new)['username'])) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('username_invalid', $username_new)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
));
|
||||
$stmt = $pdo->prepare("UPDATE `da_acl` SET `username` = :username_new WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':username_new' => $username_new,
|
||||
':username' => $username
|
||||
));
|
||||
if (!empty($domains)) {
|
||||
foreach ($domains as $domain) {
|
||||
$stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
|
||||
VALUES (:username_new, :domain, :created, :active)");
|
||||
$stmt->execute(array(
|
||||
':username_new' => $username_new,
|
||||
':domain' => $domain,
|
||||
':created' => date('Y-m-d H:i:s'),
|
||||
':active' => $active
|
||||
));
|
||||
}
|
||||
}
|
||||
if (!empty($password)) {
|
||||
if (password_check($password, $password2) !== true) {
|
||||
return false;
|
||||
}
|
||||
$password_hashed = hash_password($password);
|
||||
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active, `password` = :password_hashed WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':password_hashed' => $password_hashed,
|
||||
':username_new' => $username_new,
|
||||
':username' => $username,
|
||||
':active' => $active
|
||||
));
|
||||
if (isset($_data['disable_tfa'])) {
|
||||
$stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
|
||||
$stmt->execute(array(':username' => $username));
|
||||
}
|
||||
else {
|
||||
$stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
|
||||
$stmt->execute(array(':username_new' => $username_new, ':username' => $username));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':username_new' => $username_new,
|
||||
':username' => $username,
|
||||
':active' => $active
|
||||
));
|
||||
if (isset($_data['disable_tfa'])) {
|
||||
$stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
|
||||
$stmt->execute(array(':username' => $username));
|
||||
}
|
||||
else {
|
||||
$stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
|
||||
$stmt->execute(array(':username_new' => $username_new, ':username' => $username));
|
||||
}
|
||||
}
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('domain_admin_modified', htmlspecialchars($username))
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Domain administrator
|
||||
// Can only edit itself
|
||||
elseif ($_SESSION['mailcow_cc_role'] == "domainadmin") {
|
||||
$username = $_SESSION['mailcow_cc_username'];
|
||||
$password_old = $_data['user_old_pass'];
|
||||
$password_new = $_data['user_new_pass'];
|
||||
$password_new2 = $_data['user_new_pass2'];
|
||||
|
||||
$stmt = $pdo->prepare("SELECT `password` FROM `admin`
|
||||
WHERE `username` = :user");
|
||||
$stmt->execute(array(':user' => $username));
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if (!verify_hash($row['password'], $password_old)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (password_check($password_new, $password_new2) !== true) {
|
||||
return false;
|
||||
}
|
||||
$password_hashed = hash_password($password_new);
|
||||
$stmt = $pdo->prepare("UPDATE `admin` SET `password` = :password_hashed WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':password_hashed' => $password_hashed,
|
||||
':username' => $username
|
||||
));
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('domain_admin_modified', htmlspecialchars($username))
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'delete':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$usernames = (array)$_data['username'];
|
||||
foreach ($usernames as $username) {
|
||||
if (empty(domain_admin('details', $username))) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('username_invalid', $username)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
$stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
));
|
||||
$stmt = $pdo->prepare("DELETE FROM `admin` WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
));
|
||||
$stmt = $pdo->prepare("DELETE FROM `da_acl` WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
));
|
||||
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
));
|
||||
$stmt = $pdo->prepare("DELETE FROM `fido2` WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
));
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('domain_admin_removed', htmlspecialchars($username))
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'get':
|
||||
$domainadmins = array();
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$stmt = $pdo->query("SELECT DISTINCT
|
||||
`username`
|
||||
FROM `domain_admins`
|
||||
WHERE `username` IN (
|
||||
SELECT `username` FROM `admin`
|
||||
WHERE `superadmin`!='1'
|
||||
)");
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while ($row = array_shift($rows)) {
|
||||
$domainadmins[] = $row['username'];
|
||||
}
|
||||
return $domainadmins;
|
||||
break;
|
||||
case 'details':
|
||||
$domainadmindata = array();
|
||||
if ($_SESSION['mailcow_cc_role'] == "domainadmin" && $_data != $_SESSION['mailcow_cc_username']) {
|
||||
return false;
|
||||
}
|
||||
elseif ($_SESSION['mailcow_cc_role'] != "admin" || !isset($_data)) {
|
||||
return false;
|
||||
}
|
||||
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $_data))) {
|
||||
return false;
|
||||
}
|
||||
$stmt = $pdo->prepare("SELECT
|
||||
`tfa`.`active` AS `tfa_active`,
|
||||
`domain_admins`.`username`,
|
||||
`domain_admins`.`created`,
|
||||
`domain_admins`.`active` AS `active`
|
||||
FROM `domain_admins`
|
||||
LEFT OUTER JOIN `tfa` ON `tfa`.`username`=`domain_admins`.`username`
|
||||
WHERE `domain_admins`.`username`= :domain_admin");
|
||||
$stmt->execute(array(
|
||||
':domain_admin' => $_data
|
||||
));
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if (empty($row)) {
|
||||
return false;
|
||||
}
|
||||
$domainadmindata['username'] = $row['username'];
|
||||
$domainadmindata['tfa_active'] = (is_null($row['tfa_active'])) ? 0 : $row['tfa_active'];
|
||||
$domainadmindata['tfa_active_int'] = (is_null($row['tfa_active'])) ? 0 : $row['tfa_active'];
|
||||
$domainadmindata['active'] = $row['active'];
|
||||
$domainadmindata['active_int'] = $row['active'];
|
||||
$domainadmindata['created'] = $row['created'];
|
||||
// GET SELECTED
|
||||
$stmt = $pdo->prepare("SELECT `domain` FROM `domain`
|
||||
WHERE `domain` IN (
|
||||
SELECT `domain` FROM `domain_admins`
|
||||
WHERE `username`= :domain_admin)");
|
||||
$stmt->execute(array(':domain_admin' => $_data));
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while($row = array_shift($rows)) {
|
||||
$domainadmindata['selected_domains'][] = $row['domain'];
|
||||
}
|
||||
// GET UNSELECTED
|
||||
$stmt = $pdo->prepare("SELECT `domain` FROM `domain`
|
||||
WHERE `domain` NOT IN (
|
||||
SELECT `domain` FROM `domain_admins`
|
||||
WHERE `username`= :domain_admin)");
|
||||
$stmt->execute(array(':domain_admin' => $_data));
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while($row = array_shift($rows)) {
|
||||
$domainadmindata['unselected_domains'][] = $row['domain'];
|
||||
}
|
||||
if (!isset($domainadmindata['unselected_domains'])) {
|
||||
$domainadmindata['unselected_domains'] = "";
|
||||
}
|
||||
|
||||
return $domainadmindata;
|
||||
break;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
function domain_admin($_action, $_data = null) {
|
||||
global $pdo;
|
||||
global $lang;
|
||||
$_data_log = $_data;
|
||||
!isset($_data_log['password']) ?: $_data_log['password'] = '*';
|
||||
!isset($_data_log['password2']) ?: $_data_log['password2'] = '*';
|
||||
!isset($_data_log['user_old_pass']) ?: $_data_log['user_old_pass'] = '*';
|
||||
!isset($_data_log['user_new_pass']) ?: $_data_log['user_new_pass'] = '*';
|
||||
!isset($_data_log['user_new_pass2']) ?: $_data_log['user_new_pass2'] = '*';
|
||||
switch ($_action) {
|
||||
case 'add':
|
||||
$username = strtolower(trim($_data['username']));
|
||||
$password = $_data['password'];
|
||||
$password2 = $_data['password2'];
|
||||
$domains = (array)$_data['domains'];
|
||||
$active = intval($_data['active']);
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (empty($domains)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'domain_invalid'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username)) || empty ($username) || $username == 'API') {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('username_invalid', $username)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox`
|
||||
WHERE `username` = :username");
|
||||
$stmt->execute(array(':username' => $username));
|
||||
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||
|
||||
$stmt = $pdo->prepare("SELECT `username` FROM `admin`
|
||||
WHERE `username` = :username");
|
||||
$stmt->execute(array(':username' => $username));
|
||||
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||
|
||||
$stmt = $pdo->prepare("SELECT `username` FROM `domain_admins`
|
||||
WHERE `username` = :username");
|
||||
$stmt->execute(array(':username' => $username));
|
||||
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||
|
||||
foreach ($num_results as $num_results_each) {
|
||||
if ($num_results_each != 0) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('object_exists', htmlspecialchars($username))
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (password_check($password, $password2) !== true) {
|
||||
continue;
|
||||
}
|
||||
$password_hashed = hash_password($password);
|
||||
$valid_domains = 0;
|
||||
foreach ($domains as $domain) {
|
||||
if (!is_valid_domain_name($domain) || mailbox('get', 'domain_details', $domain) === false) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('domain_invalid', htmlspecialchars($domain))
|
||||
);
|
||||
continue;
|
||||
}
|
||||
$valid_domains++;
|
||||
$stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
|
||||
VALUES (:username, :domain, :created, :active)");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
':domain' => $domain,
|
||||
':created' => date('Y-m-d H:i:s'),
|
||||
':active' => $active
|
||||
));
|
||||
}
|
||||
if ($valid_domains != 0) {
|
||||
$stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `active`)
|
||||
VALUES (:username, :password_hashed, '0', :active)");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
':password_hashed' => $password_hashed,
|
||||
':active' => $active
|
||||
));
|
||||
}
|
||||
$stmt = $pdo->prepare("INSERT INTO `da_acl` (`username`) VALUES (:username)");
|
||||
$stmt->execute(array(
|
||||
':username' => $username
|
||||
));
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('domain_admin_added', htmlspecialchars($username))
|
||||
);
|
||||
break;
|
||||
case 'edit':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
// Administrator
|
||||
if ($_SESSION['mailcow_cc_role'] == "admin") {
|
||||
if (!is_array($_data['username'])) {
|
||||
$usernames = array();
|
||||
$usernames[] = $_data['username'];
|
||||
}
|
||||
else {
|
||||
$usernames = $_data['username'];
|
||||
}
|
||||
foreach ($usernames as $username) {
|
||||
$is_now = domain_admin('details', $username);
|
||||
$domains = (isset($_data['domains'])) ? (array)$_data['domains'] : null;
|
||||
if (!empty($is_now)) {
|
||||
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
|
||||
$domains = (!empty($domains)) ? $domains : $is_now['selected_domains'];
|
||||
$username_new = (!empty($_data['username_new'])) ? $_data['username_new'] : $is_now['username'];
|
||||
}
|
||||
else {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
continue;
|
||||
}
|
||||
$password = $_data['password'];
|
||||
$password2 = $_data['password2'];
|
||||
if (!empty($domains)) {
|
||||
foreach ($domains as $domain) {
|
||||
if (!is_valid_domain_name($domain) || mailbox('get', 'domain_details', $domain) === false) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('domain_invalid', htmlspecialchars($domain))
|
||||
);
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username_new))) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('username_invalid', $username_new)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if ($username_new != $username) {
|
||||
if (!empty(domain_admin('details', $username_new)['username'])) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('username_invalid', $username_new)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
));
|
||||
$stmt = $pdo->prepare("UPDATE `da_acl` SET `username` = :username_new WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':username_new' => $username_new,
|
||||
':username' => $username
|
||||
));
|
||||
if (!empty($domains)) {
|
||||
foreach ($domains as $domain) {
|
||||
$stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
|
||||
VALUES (:username_new, :domain, :created, :active)");
|
||||
$stmt->execute(array(
|
||||
':username_new' => $username_new,
|
||||
':domain' => $domain,
|
||||
':created' => date('Y-m-d H:i:s'),
|
||||
':active' => $active
|
||||
));
|
||||
}
|
||||
}
|
||||
if (!empty($password)) {
|
||||
if (password_check($password, $password2) !== true) {
|
||||
return false;
|
||||
}
|
||||
$password_hashed = hash_password($password);
|
||||
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active, `password` = :password_hashed WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':password_hashed' => $password_hashed,
|
||||
':username_new' => $username_new,
|
||||
':username' => $username,
|
||||
':active' => $active
|
||||
));
|
||||
if (isset($_data['disable_tfa'])) {
|
||||
$stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
|
||||
$stmt->execute(array(':username' => $username));
|
||||
}
|
||||
else {
|
||||
$stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
|
||||
$stmt->execute(array(':username_new' => $username_new, ':username' => $username));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':username_new' => $username_new,
|
||||
':username' => $username,
|
||||
':active' => $active
|
||||
));
|
||||
if (isset($_data['disable_tfa'])) {
|
||||
$stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
|
||||
$stmt->execute(array(':username' => $username));
|
||||
}
|
||||
else {
|
||||
$stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
|
||||
$stmt->execute(array(':username_new' => $username_new, ':username' => $username));
|
||||
}
|
||||
}
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('domain_admin_modified', htmlspecialchars($username))
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Domain administrator
|
||||
// Can only edit itself
|
||||
elseif ($_SESSION['mailcow_cc_role'] == "domainadmin") {
|
||||
$username = $_SESSION['mailcow_cc_username'];
|
||||
$password_old = $_data['user_old_pass'];
|
||||
$password_new = $_data['user_new_pass'];
|
||||
$password_new2 = $_data['user_new_pass2'];
|
||||
|
||||
$stmt = $pdo->prepare("SELECT `password` FROM `admin`
|
||||
WHERE `username` = :user");
|
||||
$stmt->execute(array(':user' => $username));
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if (!verify_hash($row['password'], $password_old)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (password_check($password_new, $password_new2) !== true) {
|
||||
return false;
|
||||
}
|
||||
$password_hashed = hash_password($password_new);
|
||||
$stmt = $pdo->prepare("UPDATE `admin` SET `password` = :password_hashed WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':password_hashed' => $password_hashed,
|
||||
':username' => $username
|
||||
));
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('domain_admin_modified', htmlspecialchars($username))
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'delete':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$usernames = (array)$_data['username'];
|
||||
foreach ($usernames as $username) {
|
||||
if (empty(domain_admin('details', $username))) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('username_invalid', $username)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
$stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
));
|
||||
$stmt = $pdo->prepare("DELETE FROM `admin` WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
));
|
||||
$stmt = $pdo->prepare("DELETE FROM `da_acl` WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
));
|
||||
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
));
|
||||
$stmt = $pdo->prepare("DELETE FROM `fido2` WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
));
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('domain_admin_removed', htmlspecialchars($username))
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'get':
|
||||
$domainadmins = array();
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$stmt = $pdo->query("SELECT DISTINCT
|
||||
`username`
|
||||
FROM `domain_admins`
|
||||
WHERE `username` IN (
|
||||
SELECT `username` FROM `admin`
|
||||
WHERE `superadmin`!='1'
|
||||
)");
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while ($row = array_shift($rows)) {
|
||||
$domainadmins[] = $row['username'];
|
||||
}
|
||||
return $domainadmins;
|
||||
break;
|
||||
case 'details':
|
||||
$domainadmindata = array();
|
||||
if ($_SESSION['mailcow_cc_role'] == "domainadmin" && $_data != $_SESSION['mailcow_cc_username']) {
|
||||
return false;
|
||||
}
|
||||
elseif ($_SESSION['mailcow_cc_role'] != "admin" || !isset($_data)) {
|
||||
return false;
|
||||
}
|
||||
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $_data))) {
|
||||
return false;
|
||||
}
|
||||
$stmt = $pdo->prepare("SELECT
|
||||
`tfa`.`active` AS `tfa_active`,
|
||||
`domain_admins`.`username`,
|
||||
`domain_admins`.`created`,
|
||||
`domain_admins`.`active` AS `active`
|
||||
FROM `domain_admins`
|
||||
LEFT OUTER JOIN `tfa` ON `tfa`.`username`=`domain_admins`.`username`
|
||||
WHERE `domain_admins`.`username`= :domain_admin");
|
||||
$stmt->execute(array(
|
||||
':domain_admin' => $_data
|
||||
));
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if (empty($row)) {
|
||||
return false;
|
||||
}
|
||||
$domainadmindata['username'] = $row['username'];
|
||||
$domainadmindata['tfa_active'] = (is_null($row['tfa_active'])) ? 0 : $row['tfa_active'];
|
||||
$domainadmindata['tfa_active_int'] = (is_null($row['tfa_active'])) ? 0 : $row['tfa_active'];
|
||||
$domainadmindata['active'] = $row['active'];
|
||||
$domainadmindata['active_int'] = $row['active'];
|
||||
$domainadmindata['created'] = $row['created'];
|
||||
// GET SELECTED
|
||||
$stmt = $pdo->prepare("SELECT `domain` FROM `domain`
|
||||
WHERE `domain` IN (
|
||||
SELECT `domain` FROM `domain_admins`
|
||||
WHERE `username`= :domain_admin)");
|
||||
$stmt->execute(array(':domain_admin' => $_data));
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while($row = array_shift($rows)) {
|
||||
$domainadmindata['selected_domains'][] = $row['domain'];
|
||||
}
|
||||
// GET UNSELECTED
|
||||
$stmt = $pdo->prepare("SELECT `domain` FROM `domain`
|
||||
WHERE `domain` NOT IN (
|
||||
SELECT `domain` FROM `domain_admins`
|
||||
WHERE `username`= :domain_admin)");
|
||||
$stmt->execute(array(':domain_admin' => $_data));
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while($row = array_shift($rows)) {
|
||||
$domainadmindata['unselected_domains'][] = $row['domain'];
|
||||
}
|
||||
if (!isset($domainadmindata['unselected_domains'])) {
|
||||
$domainadmindata['unselected_domains'] = "";
|
||||
}
|
||||
|
||||
return $domainadmindata;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -251,7 +251,7 @@ function password_check($password1, $password2) {
|
||||
|
||||
return true;
|
||||
}
|
||||
function last_login($action, $username, $sasl_limit_days = 7) {
|
||||
function last_login($action, $username, $sasl_limit_days = 7, $ui_offset = 1) {
|
||||
global $pdo;
|
||||
global $redis;
|
||||
$sasl_limit_days = intval($sasl_limit_days);
|
||||
@@ -319,8 +319,11 @@ function last_login($action, $username, $sasl_limit_days = 7) {
|
||||
$stmt = $pdo->prepare('SELECT `remote`, `time` FROM `logs`
|
||||
WHERE JSON_EXTRACT(`call`, "$[0]") = "check_login"
|
||||
AND JSON_EXTRACT(`call`, "$[1]") = :username
|
||||
AND `type` = "success" ORDER BY `time` DESC LIMIT 1 OFFSET 1');
|
||||
$stmt->execute(array(':username' => $username));
|
||||
AND `type` = "success" ORDER BY `time` DESC LIMIT 1 OFFSET :offset');
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
':offset' => $ui_offset
|
||||
));
|
||||
$ui = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
else {
|
||||
@@ -1736,7 +1739,7 @@ function verify_tfa_login($username, $_data) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => array('webauthn_verification_failed', 'authenticator not found')
|
||||
'msg' => array('webauthn_authenticator_failed')
|
||||
);
|
||||
return false;
|
||||
}
|
||||
@@ -1745,11 +1748,20 @@ function verify_tfa_login($username, $_data) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => array('webauthn_verification_failed', 'publicKey not found')
|
||||
'msg' => array('webauthn_publickey_failed')
|
||||
);
|
||||
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 {
|
||||
$WebAuthn->processGet($clientDataJSON, $authenticatorData, $signature, $process_webauthn['publicKey'], $challenge, null, $GLOBALS['WEBAUTHN_UV_FLAG_LOGIN'], $GLOBALS['WEBAUTHN_USER_PRESENT_FLAG']);
|
||||
}
|
||||
@@ -1781,21 +1793,12 @@ function verify_tfa_login($username, $_data) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => array('webauthn_verification_failed', 'could not determine user role')
|
||||
'msg' => array('webauthn_role_failed')
|
||||
);
|
||||
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['tfa_id'] = $process_webauthn['id'];
|
||||
$_SESSION['authReq'] = null;
|
||||
|
||||
@@ -1020,6 +1020,13 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
if (empty($name)) {
|
||||
$name = $local_part;
|
||||
}
|
||||
if (isset($_data['protocol_access'])) {
|
||||
$_data['protocol_access'] = (array)$_data['protocol_access'];
|
||||
$_data['imap_access'] = (in_array('imap', $_data['protocol_access'])) ? 1 : 0;
|
||||
$_data['pop3_access'] = (in_array('pop3', $_data['protocol_access'])) ? 1 : 0;
|
||||
$_data['smtp_access'] = (in_array('smtp', $_data['protocol_access'])) ? 1 : 0;
|
||||
$_data['sieve_access'] = (in_array('sieve', $_data['protocol_access'])) ? 1 : 0;
|
||||
}
|
||||
$active = intval($_data['active']);
|
||||
$force_pw_update = (isset($_data['force_pw_update'])) ? intval($_data['force_pw_update']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['force_pw_update']);
|
||||
$tls_enforce_in = (isset($_data['tls_enforce_in'])) ? intval($_data['tls_enforce_in']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['tls_enforce_in']);
|
||||
@@ -1200,10 +1207,63 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
':domain' => $domain,
|
||||
':active' => $active
|
||||
));
|
||||
$stmt = $pdo->prepare("INSERT INTO `user_acl` (`username`) VALUES (:username)");
|
||||
$stmt->execute(array(
|
||||
':username' => $username
|
||||
));
|
||||
|
||||
|
||||
if (isset($_data['acl'])) {
|
||||
$_data['acl'] = (array)$_data['acl'];
|
||||
$_data['spam_alias'] = (in_array('spam_alias', $_data['acl'])) ? 1 : 0;
|
||||
$_data['tls_policy'] = (in_array('tls_policy', $_data['acl'])) ? 1 : 0;
|
||||
$_data['spam_score'] = (in_array('spam_score', $_data['acl'])) ? 1 : 0;
|
||||
$_data['spam_policy'] = (in_array('spam_policy', $_data['acl'])) ? 1 : 0;
|
||||
$_data['delimiter_action'] = (in_array('delimiter_action', $_data['acl'])) ? 1 : 0;
|
||||
$_data['syncjobs'] = (in_array('syncjobs', $_data['acl'])) ? 1 : 0;
|
||||
$_data['eas_reset'] = (in_array('eas_reset', $_data['acl'])) ? 1 : 0;
|
||||
$_data['sogo_profile_reset'] = (in_array('sogo_profile_reset', $_data['acl'])) ? 1 : 0;
|
||||
$_data['pushover'] = (in_array('pushover', $_data['acl'])) ? 1 : 0;
|
||||
$_data['quarantine'] = (in_array('quarantine', $_data['acl'])) ? 1 : 0;
|
||||
$_data['quarantine_attachments'] = (in_array('quarantine_attachments', $_data['acl'])) ? 1 : 0;
|
||||
$_data['quarantine_notification'] = (in_array('quarantine_notification', $_data['acl'])) ? 1 : 0;
|
||||
$_data['quarantine_category'] = (in_array('quarantine_category', $_data['acl'])) ? 1 : 0;
|
||||
$_data['app_passwds'] = (in_array('app_passwds', $_data['acl'])) ? 1 : 0;
|
||||
|
||||
$stmt = $pdo->prepare("INSERT INTO `user_acl`
|
||||
(`username`, `spam_alias`, `tls_policy`, `spam_score`, `spam_policy`, `delimiter_action`, `syncjobs`, `eas_reset`, `sogo_profile_reset`,
|
||||
`pushover`, `quarantine`, `quarantine_attachments`, `quarantine_notification`, `quarantine_category`, `app_passwds`)
|
||||
VALUES (:username, :spam_alias, :tls_policy, :spam_score, :spam_policy, :delimiter_action, :syncjobs, :eas_reset, :sogo_profile_reset,
|
||||
:pushover, :quarantine, :quarantine_attachments, :quarantine_notification, :quarantine_category, :app_passwds) ");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
':spam_alias' => $_data['spam_alias'],
|
||||
':tls_policy' => $_data['tls_policy'],
|
||||
':spam_score' => $_data['spam_score'],
|
||||
':spam_policy' => $_data['spam_policy'],
|
||||
':delimiter_action' => $_data['delimiter_action'],
|
||||
':syncjobs' => $_data['syncjobs'],
|
||||
':eas_reset' => $_data['eas_reset'],
|
||||
':sogo_profile_reset' => $_data['sogo_profile_reset'],
|
||||
':pushover' => $_data['pushover'],
|
||||
':quarantine' => $_data['quarantine'],
|
||||
':quarantine_attachments' => $_data['quarantine_attachments'],
|
||||
':quarantine_notification' => $_data['quarantine_notification'],
|
||||
':quarantine_category' => $_data['quarantine_category'],
|
||||
':app_passwds' => $_data['app_passwds']
|
||||
));
|
||||
}
|
||||
else {
|
||||
$stmt = $pdo->prepare("INSERT INTO `user_acl` (`username`) VALUES (:username)");
|
||||
$stmt->execute(array(
|
||||
':username' => $username
|
||||
));
|
||||
}
|
||||
|
||||
if (isset($_data['rl_frame']) && isset($_data['rl_value'])){
|
||||
ratelimit('edit', 'mailbox', array(
|
||||
'object' => $username,
|
||||
'rl_frame' => $_data['rl_frame'],
|
||||
'rl_value' => $_data['rl_value']
|
||||
));
|
||||
}
|
||||
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
@@ -1322,6 +1382,190 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
'msg' => array('resource_added', htmlspecialchars($name))
|
||||
);
|
||||
break;
|
||||
case 'domain_templates':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_extra),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (empty($_data["template"])){
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_extra),
|
||||
'msg' => 'template_name_invalid'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if template name exists, return false
|
||||
$stmt = $pdo->prepare("SELECT id FROM `templates` WHERE `type` = :type AND `template` = :template");
|
||||
$stmt->execute(array(
|
||||
":type" => "domain",
|
||||
":template" => $_data["template"]
|
||||
));
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!empty($row)){
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_extra),
|
||||
'msg' => array('template_exists', $_data["template"])
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// check attributes
|
||||
$attr = array();
|
||||
$attr['tags'] = (isset($_data['tags'])) ? $_data['tags'] : array();
|
||||
$attr['max_num_aliases_for_domain'] = (!empty($_data['max_num_aliases_for_domain'])) ? intval($_data['max_num_aliases_for_domain']) : 400;
|
||||
$attr['max_num_mboxes_for_domain'] = (!empty($_data['max_num_mboxes_for_domain'])) ? intval($_data['max_num_mboxes_for_domain']) : 10;
|
||||
$attr['def_quota_for_mbox'] = (!empty($_data['def_quota_for_mbox'])) ? intval($_data['def_quota_for_mbox']) * 1048576 : 3072 * 1048576;
|
||||
$attr['max_quota_for_mbox'] = (!empty($_data['max_quota_for_mbox'])) ? intval($_data['max_quota_for_mbox']) * 1048576 : 10240 * 1048576;
|
||||
$attr['max_quota_for_domain'] = (!empty($_data['max_quota_for_domain'])) ? intval($_data['max_quota_for_domain']) * 1048576 : 10240 * 1048576;
|
||||
$attr['rl_frame'] = (!empty($_data['rl_frame'])) ? $_data['rl_frame'] : "s";
|
||||
$attr['rl_value'] = (!empty($_data['rl_value'])) ? $_data['rl_value'] : "";
|
||||
$attr['active'] = isset($_data['active']) ? intval($_data['active']) : 1;
|
||||
$attr['gal'] = (isset($_data['gal'])) ? intval($_data['gal']) : 1;
|
||||
$attr['backupmx'] = (isset($_data['backupmx'])) ? intval($_data['backupmx']) : 0;
|
||||
$attr['relay_all_recipients'] = (isset($_data['relay_all_recipients'])) ? intval($_data['relay_all_recipients']) : 0;
|
||||
$attr['relay_unknown_only'] = (isset($_data['relay_unknown_only'])) ? intval($_data['relay_unknown_only']) : 0;
|
||||
$attr['dkim_selector'] = (isset($_data['dkim_selector'])) ? $_data['dkim_selector'] : "dkim";
|
||||
$attr['key_size'] = isset($_data['key_size']) ? intval($_data['key_size']) : 2048;
|
||||
|
||||
// save template
|
||||
$stmt = $pdo->prepare("INSERT INTO `templates` (`type`, `template`, `attributes`)
|
||||
VALUES (:type, :template, :attributes)");
|
||||
$stmt->execute(array(
|
||||
":type" => "domain",
|
||||
":template" => $_data["template"],
|
||||
":attributes" => json_encode($attr)
|
||||
));
|
||||
|
||||
// success
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => array('template_added', $_data["template"])
|
||||
);
|
||||
return true;
|
||||
break;
|
||||
case 'mailbox_templates':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_extra),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (empty($_data["template"])){
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_extra),
|
||||
'msg' => 'template_name_invalid'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if template name exists, return false
|
||||
$stmt = $pdo->prepare("SELECT id FROM `templates` WHERE `type` = :type AND `template` = :template");
|
||||
$stmt->execute(array(
|
||||
":type" => "mailbox",
|
||||
":template" => $_data["template"]
|
||||
));
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if (!empty($row)){
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_extra),
|
||||
'msg' => array('template_exists', $_data["template"])
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// check attributes
|
||||
$attr = array();
|
||||
$attr["quota"] = isset($_data['quota']) ? intval($_data['quota']) * 1048576 : 0;
|
||||
$attr['tags'] = (isset($_data['tags'])) ? $_data['tags'] : array();
|
||||
$attr["quarantine_notification"] = (!empty($_data['quarantine_notification'])) ? $_data['quarantine_notification'] : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_notification']);
|
||||
$attr["quarantine_category"] = (!empty($_data['quarantine_category'])) ? $_data['quarantine_category'] : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_category']);
|
||||
$attr["rl_frame"] = (!empty($_data['rl_frame'])) ? $_data['rl_frame'] : "s";
|
||||
$attr["rl_value"] = (!empty($_data['rl_value'])) ? $_data['rl_value'] : "";
|
||||
$attr["force_pw_update"] = isset($_data['force_pw_update']) ? intval($_data['force_pw_update']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['force_pw_update']);
|
||||
$attr["sogo_access"] = isset($_data['sogo_access']) ? intval($_data['sogo_access']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['sogo_access']);
|
||||
$attr["active"] = isset($_data['active']) ? intval($_data['active']) : 1;
|
||||
$attr["tls_enforce_in"] = isset($_data['tls_enforce_in']) ? intval($_data['tls_enforce_in']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['tls_enforce_in']);
|
||||
$attr["tls_enforce_out"] = isset($_data['tls_enforce_out']) ? intval($_data['tls_enforce_out']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['tls_enforce_out']);
|
||||
if (isset($_data['protocol_access'])) {
|
||||
$_data['protocol_access'] = (array)$_data['protocol_access'];
|
||||
$attr['imap_access'] = (in_array('imap', $_data['protocol_access'])) ? 1 : intval($MAILBOX_DEFAULT_ATTRIBUTES['imap_access']);
|
||||
$attr['pop3_access'] = (in_array('pop3', $_data['protocol_access'])) ? 1 : intval($MAILBOX_DEFAULT_ATTRIBUTES['pop3_access']);
|
||||
$attr['smtp_access'] = (in_array('smtp', $_data['protocol_access'])) ? 1 : intval($MAILBOX_DEFAULT_ATTRIBUTES['smtp_access']);
|
||||
$attr['sieve_access'] = (in_array('sieve', $_data['protocol_access'])) ? 1 : intval($MAILBOX_DEFAULT_ATTRIBUTES['sieve_access']);
|
||||
}
|
||||
else {
|
||||
$attr['imap_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['imap_access']);
|
||||
$attr['pop3_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['pop3_access']);
|
||||
$attr['smtp_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['smtp_access']);
|
||||
$attr['sieve_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['sieve_access']);
|
||||
}
|
||||
if (isset($_data['acl'])) {
|
||||
$_data['acl'] = (array)$_data['acl'];
|
||||
$attr['acl_spam_alias'] = (in_array('spam_alias', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_tls_policy'] = (in_array('tls_policy', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_spam_score'] = (in_array('spam_score', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_spam_policy'] = (in_array('spam_policy', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_delimiter_action'] = (in_array('delimiter_action', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_syncjobs'] = (in_array('syncjobs', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_eas_reset'] = (in_array('eas_reset', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_sogo_profile_reset'] = (in_array('sogo_profile_reset', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_pushover'] = (in_array('pushover', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_quarantine'] = (in_array('quarantine', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_quarantine_attachments'] = (in_array('quarantine_attachments', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_quarantine_notification'] = (in_array('quarantine_notification', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_quarantine_category'] = (in_array('quarantine_category', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_app_passwds'] = (in_array('app_passwds', $_data['acl'])) ? 1 : 0;
|
||||
} else {
|
||||
$_data['acl'] = (array)$_data['acl'];
|
||||
$attr['acl_spam_alias'] = 1;
|
||||
$attr['acl_tls_policy'] = 1;
|
||||
$attr['acl_spam_score'] = 1;
|
||||
$attr['acl_spam_policy'] = 1;
|
||||
$attr['acl_delimiter_action'] = 1;
|
||||
$attr['acl_syncjobs'] = 0;
|
||||
$attr['acl_eas_reset'] = 1;
|
||||
$attr['acl_sogo_profile_reset'] = 0;
|
||||
$attr['acl_pushover'] = 1;
|
||||
$attr['acl_quarantine'] = 1;
|
||||
$attr['acl_quarantine_attachments'] = 1;
|
||||
$attr['acl_quarantine_notification'] = 1;
|
||||
$attr['acl_quarantine_category'] = 1;
|
||||
$attr['acl_app_passwds'] = 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// save template
|
||||
$stmt = $pdo->prepare("INSERT INTO `templates` (`type`, `template`, `attributes`)
|
||||
VALUES (:type, :template, :attributes)");
|
||||
$stmt->execute(array(
|
||||
":type" => "mailbox",
|
||||
":template" => $_data["template"],
|
||||
":attributes" => json_encode($attr)
|
||||
));
|
||||
|
||||
// success
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => array('template_added', $_data["template"])
|
||||
);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'edit':
|
||||
@@ -2472,6 +2716,79 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'domain_templates':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_extra),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (!is_array($_data['ids'])) {
|
||||
$ids = array();
|
||||
$ids[] = $_data['ids'];
|
||||
}
|
||||
else {
|
||||
$ids = $_data['ids'];
|
||||
}
|
||||
foreach ($ids as $id) {
|
||||
$is_now = mailbox("get", "domain_templates", $id);
|
||||
if (empty($is_now) ||
|
||||
$is_now["type"] != "domain"){
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_extra),
|
||||
'msg' => 'template_id_invalid'
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// check name
|
||||
if ($is_now["template"] == "Default" && $is_now["template"] != $_data["template"]){
|
||||
// keep template name of Default template
|
||||
$_data["template"] = $is_now["template"];
|
||||
}
|
||||
else {
|
||||
$_data["template"] = (isset($_data["template"])) ? $_data["template"] : $is_now["template"];
|
||||
}
|
||||
// check attributes
|
||||
$attr = array();
|
||||
$attr['tags'] = (isset($_data['tags'])) ? $_data['tags'] : array();
|
||||
$attr['max_num_aliases_for_domain'] = (isset($_data['max_num_aliases_for_domain'])) ? intval($_data['max_num_aliases_for_domain']) : 0;
|
||||
$attr['max_num_mboxes_for_domain'] = (isset($_data['max_num_mboxes_for_domain'])) ? intval($_data['max_num_mboxes_for_domain']) : 0;
|
||||
$attr['def_quota_for_mbox'] = (isset($_data['def_quota_for_mbox'])) ? intval($_data['def_quota_for_mbox']) * 1048576 : 0;
|
||||
$attr['max_quota_for_mbox'] = (isset($_data['max_quota_for_mbox'])) ? intval($_data['max_quota_for_mbox']) * 1048576 : 0;
|
||||
$attr['max_quota_for_domain'] = (isset($_data['max_quota_for_domain'])) ? intval($_data['max_quota_for_domain']) * 1048576 : 0;
|
||||
$attr['rl_frame'] = (!empty($_data['rl_frame'])) ? $_data['rl_frame'] : "s";
|
||||
$attr['rl_value'] = (!empty($_data['rl_value'])) ? $_data['rl_value'] : "";
|
||||
$attr['active'] = isset($_data['active']) ? intval($_data['active']) : 1;
|
||||
$attr['gal'] = (isset($_data['gal'])) ? intval($_data['gal']) : 1;
|
||||
$attr['backupmx'] = (isset($_data['backupmx'])) ? intval($_data['backupmx']) : 0;
|
||||
$attr['relay_all_recipients'] = (isset($_data['relay_all_recipients'])) ? intval($_data['relay_all_recipients']) : 0;
|
||||
$attr['relay_unknown_only'] = (isset($_data['relay_unknown_only'])) ? intval($_data['relay_unknown_only']) : 0;
|
||||
$attr['dkim_selector'] = (isset($_data['dkim_selector'])) ? $_data['dkim_selector'] : "dkim";
|
||||
$attr['key_size'] = isset($_data['key_size']) ? intval($_data['key_size']) : 2048;
|
||||
|
||||
// update template
|
||||
$stmt = $pdo->prepare("UPDATE `templates`
|
||||
SET `template` = :template, `attributes` = :attributes
|
||||
WHERE id = :id");
|
||||
$stmt->execute(array(
|
||||
":id" => $id ,
|
||||
":template" => $_data["template"] ,
|
||||
":attributes" => json_encode($attr)
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => array('template_modified', $_data["template"])
|
||||
);
|
||||
return true;
|
||||
break;
|
||||
case 'mailbox':
|
||||
if (!is_array($_data['username'])) {
|
||||
$usernames = array();
|
||||
@@ -2562,67 +2879,68 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'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']));
|
||||
foreach ($extra_acls as $i => &$extra_acl) {
|
||||
if (empty($extra_acl)) {
|
||||
continue;
|
||||
}
|
||||
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(
|
||||
'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)) {
|
||||
else {
|
||||
$extra_acls = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['extended_sender_acl']));
|
||||
foreach ($extra_acls as $i => &$extra_acl) {
|
||||
if (empty($extra_acl)) {
|
||||
continue;
|
||||
}
|
||||
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(
|
||||
'type' => 'danger',
|
||||
'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]);
|
||||
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;
|
||||
$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(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => array('extra_acl_invalid_domain', $extra_acl_domain)
|
||||
);
|
||||
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_values($extra_acls);
|
||||
$extra_acls = array_unique($extra_acls);
|
||||
$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)");
|
||||
$extra_acls = array_filter($extra_acls);
|
||||
$extra_acls = array_values($extra_acls);
|
||||
$extra_acls = array_unique($extra_acls);
|
||||
$stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `external` = 1 AND `logged_in_as` = :username");
|
||||
$stmt->execute(array(
|
||||
':sender_acl' => $sender_acl_external,
|
||||
':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'])) {
|
||||
@@ -2814,6 +3132,110 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'mailbox_templates':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_extra),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (!is_array($_data['ids'])) {
|
||||
$ids = array();
|
||||
$ids[] = $_data['ids'];
|
||||
}
|
||||
else {
|
||||
$ids = $_data['ids'];
|
||||
}
|
||||
foreach ($ids as $id) {
|
||||
$is_now = mailbox("get", "mailbox_templates", $id);
|
||||
if (empty($is_now) ||
|
||||
$is_now["type"] != "mailbox"){
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_extra),
|
||||
'msg' => 'template_id_invalid'
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// check name
|
||||
if ($is_now["template"] == "Default" && $is_now["template"] != $_data["template"]){
|
||||
// keep template name of Default template
|
||||
$_data["template"] = $is_now["template"];
|
||||
}
|
||||
else {
|
||||
$_data["template"] = (isset($_data["template"])) ? $_data["template"] : $is_now["template"];
|
||||
}
|
||||
// check attributes
|
||||
$attr = array();
|
||||
$attr["quota"] = isset($_data['quota']) ? intval($_data['quota']) * 1048576 : 0;
|
||||
$attr['tags'] = (isset($_data['tags'])) ? $_data['tags'] : $is_now['tags'];
|
||||
$attr["quarantine_notification"] = (!empty($_data['quarantine_notification'])) ? $_data['quarantine_notification'] : $is_now['quarantine_notification'];
|
||||
$attr["quarantine_category"] = (!empty($_data['quarantine_category'])) ? $_data['quarantine_category'] : $is_now['quarantine_category'];
|
||||
$attr["rl_frame"] = (!empty($_data['rl_frame'])) ? $_data['rl_frame'] : $is_now['rl_frame'];
|
||||
$attr["rl_value"] = (!empty($_data['rl_value'])) ? $_data['rl_value'] : $is_now['rl_value'];
|
||||
$attr["force_pw_update"] = isset($_data['force_pw_update']) ? intval($_data['force_pw_update']) : $is_now['force_pw_update'];
|
||||
$attr["sogo_access"] = isset($_data['sogo_access']) ? intval($_data['sogo_access']) : $is_now['sogo_access'];
|
||||
$attr["active"] = isset($_data['active']) ? intval($_data['active']) : $is_now['active'];
|
||||
$attr["tls_enforce_in"] = isset($_data['tls_enforce_in']) ? intval($_data['tls_enforce_in']) : $is_now['tls_enforce_in'];
|
||||
$attr["tls_enforce_out"] = isset($_data['tls_enforce_out']) ? intval($_data['tls_enforce_out']) : $is_now['tls_enforce_out'];
|
||||
if (isset($_data['protocol_access'])) {
|
||||
$_data['protocol_access'] = (array)$_data['protocol_access'];
|
||||
$attr['imap_access'] = (in_array('imap', $_data['protocol_access'])) ? 1 : 0;
|
||||
$attr['pop3_access'] = (in_array('pop3', $_data['protocol_access'])) ? 1 : 0;
|
||||
$attr['smtp_access'] = (in_array('smtp', $_data['protocol_access'])) ? 1 : 0;
|
||||
$attr['sieve_access'] = (in_array('sieve', $_data['protocol_access'])) ? 1 : 0;
|
||||
}
|
||||
else {
|
||||
foreach ($is_now as $key => $value){
|
||||
$attr[$key] = $is_now[$key];
|
||||
}
|
||||
}
|
||||
if (isset($_data['acl'])) {
|
||||
$_data['acl'] = (array)$_data['acl'];
|
||||
$attr['acl_spam_alias'] = (in_array('spam_alias', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_tls_policy'] = (in_array('tls_policy', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_spam_score'] = (in_array('spam_score', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_spam_policy'] = (in_array('spam_policy', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_delimiter_action'] = (in_array('delimiter_action', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_syncjobs'] = (in_array('syncjobs', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_eas_reset'] = (in_array('eas_reset', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_sogo_profile_reset'] = (in_array('sogo_profile_reset', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_pushover'] = (in_array('pushover', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_quarantine'] = (in_array('quarantine', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_quarantine_attachments'] = (in_array('quarantine_attachments', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_quarantine_notification'] = (in_array('quarantine_notification', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_quarantine_category'] = (in_array('quarantine_category', $_data['acl'])) ? 1 : 0;
|
||||
$attr['acl_app_passwds'] = (in_array('app_passwds', $_data['acl'])) ? 1 : 0;
|
||||
} else {
|
||||
foreach ($is_now as $key => $value){
|
||||
$attr[$key] = $is_now[$key];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// update template
|
||||
$stmt = $pdo->prepare("UPDATE `templates`
|
||||
SET `template` = :template, `attributes` = :attributes
|
||||
WHERE id = :id");
|
||||
$stmt->execute(array(
|
||||
":id" => $id ,
|
||||
":template" => $_data["template"] ,
|
||||
":attributes" => json_encode($attr)
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => array('template_modified', $_data["template"])
|
||||
);
|
||||
return true;
|
||||
break;
|
||||
case 'resource':
|
||||
if (!is_array($_data['name'])) {
|
||||
$names = array();
|
||||
@@ -3606,6 +4028,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
`mailboxes`,
|
||||
`defquota`,
|
||||
`maxquota`,
|
||||
`created`,
|
||||
`modified`,
|
||||
`quota`,
|
||||
`relayhost`,
|
||||
`relay_all_recipients`,
|
||||
@@ -3678,6 +4102,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$domaindata['relay_all_recipients_int'] = $row['relay_all_recipients'];
|
||||
$domaindata['relay_unknown_only'] = $row['relay_unknown_only'];
|
||||
$domaindata['relay_unknown_only_int'] = $row['relay_unknown_only'];
|
||||
$domaindata['created'] = $row['created'];
|
||||
$domaindata['modified'] = $row['modified'];
|
||||
$stmt = $pdo->prepare("SELECT COUNT(`address`) AS `alias_count` FROM `alias`
|
||||
WHERE (`domain`= :domain OR `domain` IN (SELECT `alias_domain` FROM `alias_domain` WHERE `target_domain` = :domain2))
|
||||
AND `address` NOT IN (
|
||||
@@ -3711,6 +4137,43 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
|
||||
return $domaindata;
|
||||
break;
|
||||
case 'domain_templates':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
|
||||
return false;
|
||||
}
|
||||
$_data = (isset($_data)) ? intval($_data) : null;
|
||||
|
||||
if (isset($_data)){
|
||||
$stmt = $pdo->prepare("SELECT * FROM `templates`
|
||||
WHERE `id` = :id AND type = :type");
|
||||
$stmt->execute(array(
|
||||
":id" => $_data,
|
||||
":type" => "domain"
|
||||
));
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (empty($row)){
|
||||
return false;
|
||||
}
|
||||
|
||||
$row["attributes"] = json_decode($row["attributes"], true);
|
||||
return $row;
|
||||
}
|
||||
else {
|
||||
$stmt = $pdo->prepare("SELECT * FROM `templates` WHERE `type` = 'domain'");
|
||||
$stmt->execute();
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if (empty($rows)){
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach($rows as $key => $row){
|
||||
$rows[$key]["attributes"] = json_decode($row["attributes"], true);
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
break;
|
||||
case 'mailbox_details':
|
||||
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
|
||||
return false;
|
||||
@@ -3725,6 +4188,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
`mailbox`.`domain`,
|
||||
`mailbox`.`local_part`,
|
||||
`mailbox`.`quota`,
|
||||
`mailbox`.`created`,
|
||||
`mailbox`.`modified`,
|
||||
`quota2`.`bytes`,
|
||||
`attributes`,
|
||||
`quota2`.`messages`
|
||||
@@ -3743,6 +4208,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
`mailbox`.`domain`,
|
||||
`mailbox`.`local_part`,
|
||||
`mailbox`.`quota`,
|
||||
`mailbox`.`created`,
|
||||
`mailbox`.`modified`,
|
||||
`quota2replica`.`bytes`,
|
||||
`attributes`,
|
||||
`quota2replica`.`messages`
|
||||
@@ -3769,6 +4236,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$mailboxdata['attributes'] = json_decode($row['attributes'], true);
|
||||
$mailboxdata['quota_used'] = intval($row['bytes']);
|
||||
$mailboxdata['percent_in_use'] = ($row['quota'] == 0) ? '- ' : round((intval($row['bytes']) / intval($row['quota'])) * 100);
|
||||
$mailboxdata['created'] = $row['created'];
|
||||
$mailboxdata['modified'] = $row['modified'];
|
||||
|
||||
if ($mailboxdata['percent_in_use'] === '- ') {
|
||||
$mailboxdata['percent_class'] = "info";
|
||||
@@ -3856,6 +4325,43 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
|
||||
return $mailboxdata;
|
||||
break;
|
||||
case 'mailbox_templates':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
|
||||
return false;
|
||||
}
|
||||
$_data = (isset($_data)) ? intval($_data) : null;
|
||||
|
||||
if (isset($_data)){
|
||||
$stmt = $pdo->prepare("SELECT * FROM `templates`
|
||||
WHERE `id` = :id AND type = :type");
|
||||
$stmt->execute(array(
|
||||
":id" => $_data,
|
||||
":type" => "mailbox"
|
||||
));
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (empty($row)){
|
||||
return false;
|
||||
}
|
||||
|
||||
$row["attributes"] = json_decode($row["attributes"], true);
|
||||
return $row;
|
||||
}
|
||||
else {
|
||||
$stmt = $pdo->prepare("SELECT * FROM `templates` WHERE `type` = 'mailbox'");
|
||||
$stmt->execute();
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if (empty($rows)){
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach($rows as $key => $row){
|
||||
$rows[$key]["attributes"] = json_decode($row["attributes"], true);
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
break;
|
||||
case 'resource_details':
|
||||
$resourcedata = array();
|
||||
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
|
||||
@@ -4224,6 +4730,42 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'domain_templates':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (!is_array($_data['ids'])) {
|
||||
$ids = array();
|
||||
$ids[] = $_data['ids'];
|
||||
}
|
||||
else {
|
||||
$ids = $_data['ids'];
|
||||
}
|
||||
|
||||
|
||||
foreach ($ids as $id) {
|
||||
// delete template
|
||||
$stmt = $pdo->prepare("DELETE FROM `templates`
|
||||
WHERE id = :id AND type = :type AND NOT template = :template");
|
||||
$stmt->execute(array(
|
||||
":id" => $id,
|
||||
":type" => "domain",
|
||||
":template" => "Default"
|
||||
));
|
||||
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => array('template_removed', htmlspecialchars($id))
|
||||
);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 'alias':
|
||||
if (!is_array($_data['id'])) {
|
||||
$ids = array();
|
||||
@@ -4518,6 +5060,42 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'mailbox_templates':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (!is_array($_data['ids'])) {
|
||||
$ids = array();
|
||||
$ids[] = $_data['ids'];
|
||||
}
|
||||
else {
|
||||
$ids = $_data['ids'];
|
||||
}
|
||||
|
||||
|
||||
foreach ($ids as $id) {
|
||||
// delete template
|
||||
$stmt = $pdo->prepare("DELETE FROM `templates`
|
||||
WHERE id = :id AND type = :type AND NOT template = :template");
|
||||
$stmt->execute(array(
|
||||
":id" => $id,
|
||||
":type" => "mailbox",
|
||||
":template" => "Default"
|
||||
));
|
||||
}
|
||||
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => 'template_removed'
|
||||
);
|
||||
return true;
|
||||
break;
|
||||
case 'resource':
|
||||
if (!is_array($_data['name'])) {
|
||||
$names = array();
|
||||
@@ -4593,15 +5171,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$tags = $_data['tags'];
|
||||
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;
|
||||
foreach ($domains as $domain) {
|
||||
@@ -4613,7 +5182,15 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
);
|
||||
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){
|
||||
// delete tag
|
||||
$wasModified = true;
|
||||
@@ -4687,7 +5264,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
}
|
||||
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', 'mailbox', 'resource')) && getenv('SKIP_SOGO') != "y") {
|
||||
update_sogo_static_view();
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,6 @@ $globalVariables = [
|
||||
'dual_login' => @$_SESSION['dual-login'],
|
||||
'ui_texts' => $UI_TEXTS,
|
||||
'css_path' => '/cache/'.basename($CSSPath),
|
||||
'theme' => strtolower(trim($DEFAULT_THEME)),
|
||||
'logo' => customize('get', 'main_logo'),
|
||||
'available_languages' => $AVAILABLE_LANGUAGES,
|
||||
'lang' => $lang,
|
||||
@@ -49,6 +48,7 @@ $globalVariables = [
|
||||
'app_links' => customize('get', 'app_links'),
|
||||
'is_root_uri' => (parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) == '/'),
|
||||
'uri' => $_SERVER['REQUEST_URI'],
|
||||
'last_login' => last_login('get', $_SESSION['mailcow_cc_username'], 7, 0)['ui']['time']
|
||||
];
|
||||
|
||||
foreach ($globalVariables as $globalVariableName => $globalVariableValue) {
|
||||
|
||||
+1478
-1360
File diff suppressed because it is too large.
Load diff
@@ -2,6 +2,8 @@
|
||||
|
||||
// check for development mode
|
||||
$DEV_MODE = (getenv('DEV_MODE') == 'y');
|
||||
// check for demo mode
|
||||
$DEMO_MODE = (getenv('DEMO_MODE') == 'y');
|
||||
|
||||
// Slave does not serve UI
|
||||
/* if (!preg_match('/y|yes/i', getenv('MASTER'))) {
|
||||
@@ -44,21 +46,6 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/lib/CSSminifierExtended.php';
|
||||
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/lib/array_merge_real.php';
|
||||
|
||||
// Minify JS
|
||||
use MatthiasMullie\Minify;
|
||||
$js_minifier = new JSminifierExtended();
|
||||
$js_dir = array_diff(scandir('/web/js/build'), array('..', '.'));
|
||||
foreach ($js_dir as $js_file) {
|
||||
$js_minifier->add('/web/js/build/' . $js_file);
|
||||
}
|
||||
|
||||
// Minify CSS
|
||||
$css_minifier = new CSSminifierExtended();
|
||||
$css_dir = array_diff(scandir('/web/css/build'), array('..', '.'));
|
||||
foreach ($css_dir as $css_file) {
|
||||
$css_minifier->add('/web/css/build/' . $css_file);
|
||||
}
|
||||
|
||||
// U2F API + T/HOTP API
|
||||
// u2f - deprecated, should be removed
|
||||
$u2f = new u2flib_server\U2F('https://' . $_SERVER['HTTP_HOST']);
|
||||
@@ -247,7 +234,7 @@ if (!isset($_SESSION['mailcow_locale']) && !isset($_COOKIE['mailcow_locale'])) {
|
||||
|
||||
// Try suggest match
|
||||
// e.g. suggest en-gb when only en-us is provided
|
||||
if (!isset($_COOKIE['mailcow_locale'])) {
|
||||
if (!isset($_SESSION['mailcow_locale'])) {
|
||||
foreach ($lang2pref as $lang => $q) {
|
||||
if (array_key_exists(substr($lang, 0, 2), $AVAILABLE_BASE_LANGUAGES)) {
|
||||
$_SESSION['mailcow_locale'] = $AVAILABLE_BASE_LANGUAGES[substr($lang, 0, 2)];
|
||||
@@ -278,6 +265,7 @@ if(file_exists($langFile)) {
|
||||
$lang = array_merge_real($lang, json_decode(file_get_contents($langFile), true));
|
||||
}
|
||||
|
||||
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.acl.inc.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.address_rewriting.inc.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.admin.inc.php';
|
||||
@@ -312,4 +300,29 @@ if (isset($_SESSION['mailcow_cc_role'])) {
|
||||
// }
|
||||
acl('to_session');
|
||||
}
|
||||
|
||||
// init frontend
|
||||
// Minify JS
|
||||
use MatthiasMullie\Minify;
|
||||
$js_minifier = new JSminifierExtended();
|
||||
$js_dir = array_diff(scandir('/web/js/build'), array('..', '.'));
|
||||
// Minify CSS
|
||||
$css_minifier = new CSSminifierExtended();
|
||||
$css_dir = array_diff(scandir('/web/css/build'), array('..', '.'));
|
||||
// get customized ui data
|
||||
$UI_TEXTS = customize('get', 'ui_texts');
|
||||
|
||||
|
||||
// minify bootstrap theme
|
||||
if (file_exists('/web/css/themes/'.$UI_THEME.'-bootstrap.css'))
|
||||
$css_minifier->add('/web/css/themes/'.$UI_THEME.'-bootstrap.css');
|
||||
else
|
||||
$css_minifier->add('/web/css/themes/lumen-bootstrap.css');
|
||||
// minify css build files
|
||||
foreach ($css_dir as $css_file) {
|
||||
$css_minifier->add('/web/css/build/' . $css_file);
|
||||
}
|
||||
// minify js build files
|
||||
foreach ($js_dir as $js_file) {
|
||||
$js_minifier->add('/web/js/build/' . $js_file);
|
||||
}
|
||||
+140
-140
@@ -1,140 +1,140 @@
|
||||
<?php
|
||||
// Start session
|
||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||
ini_set("session.cookie_httponly", 1);
|
||||
ini_set('session.gc_maxlifetime', $SESSION_LIFETIME);
|
||||
}
|
||||
|
||||
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
|
||||
strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == "https") {
|
||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||
ini_set("session.cookie_secure", 1);
|
||||
}
|
||||
$IS_HTTPS = true;
|
||||
}
|
||||
elseif (isset($_SERVER['HTTPS'])) {
|
||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||
ini_set("session.cookie_secure", 1);
|
||||
}
|
||||
$IS_HTTPS = true;
|
||||
}
|
||||
else {
|
||||
$IS_HTTPS = false;
|
||||
}
|
||||
|
||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
if (!isset($_SESSION['CSRF']['TOKEN'])) {
|
||||
$_SESSION['CSRF']['TOKEN'] = bin2hex(random_bytes(32));
|
||||
}
|
||||
|
||||
// Set session UA
|
||||
if (!isset($_SESSION['SESS_REMOTE_UA'])) {
|
||||
$_SESSION['SESS_REMOTE_UA'] = $_SERVER['HTTP_USER_AGENT'];
|
||||
}
|
||||
|
||||
// Keep session active
|
||||
if (isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY'] > $SESSION_LIFETIME)) {
|
||||
session_unset();
|
||||
session_destroy();
|
||||
}
|
||||
$_SESSION['LAST_ACTIVITY'] = time();
|
||||
|
||||
// API
|
||||
if (!empty($_SERVER['HTTP_X_API_KEY'])) {
|
||||
$stmt = $pdo->prepare("SELECT * FROM `api` WHERE `api_key` = :api_key AND `active` = '1';");
|
||||
$stmt->execute(array(
|
||||
':api_key' => preg_replace('/[^a-zA-Z0-9-]/', '', $_SERVER['HTTP_X_API_KEY'])
|
||||
));
|
||||
$api_return = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if (!empty($api_return['api_key'])) {
|
||||
$skip_ip_check = ($api_return['skip_ip_check'] == 1);
|
||||
$remote = get_remote_ip(false);
|
||||
$allow_from = array_map('trim', preg_split( "/( |,|;|\n)/", $api_return['allow_from']));
|
||||
if ($skip_ip_check === true || ip_acl($remote, $allow_from)) {
|
||||
$_SESSION['mailcow_cc_username'] = 'API';
|
||||
$_SESSION['mailcow_cc_role'] = 'admin';
|
||||
$_SESSION['mailcow_cc_api'] = true;
|
||||
if ($api_return['access'] == 'rw') {
|
||||
$_SESSION['mailcow_cc_api_access'] = 'rw';
|
||||
}
|
||||
else {
|
||||
$_SESSION['mailcow_cc_api_access'] = 'ro';
|
||||
}
|
||||
}
|
||||
else {
|
||||
$redis->publish("F2B_CHANNEL", "mailcow UI: Invalid password for API_USER by " . $_SERVER['REMOTE_ADDR']);
|
||||
error_log("mailcow UI: Invalid password for " . $user . " by " . $_SERVER['REMOTE_ADDR']);
|
||||
http_response_code(401);
|
||||
echo json_encode(array(
|
||||
'type' => 'error',
|
||||
'msg' => 'api access denied for ip ' . $_SERVER['REMOTE_ADDR']
|
||||
));
|
||||
unset($_POST);
|
||||
exit();
|
||||
}
|
||||
}
|
||||
else {
|
||||
$redis->publish("F2B_CHANNEL", "mailcow UI: Invalid password for API_USER by " . $_SERVER['REMOTE_ADDR']);
|
||||
error_log("mailcow UI: Invalid password for " . $user . " by " . $_SERVER['REMOTE_ADDR']);
|
||||
http_response_code(401);
|
||||
echo json_encode(array(
|
||||
'type' => 'error',
|
||||
'msg' => 'authentication failed'
|
||||
));
|
||||
unset($_POST);
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle logouts
|
||||
if (isset($_POST["logout"])) {
|
||||
if (isset($_SESSION["dual-login"])) {
|
||||
$_SESSION["mailcow_cc_username"] = $_SESSION["dual-login"]["username"];
|
||||
$_SESSION["mailcow_cc_role"] = $_SESSION["dual-login"]["role"];
|
||||
unset($_SESSION["dual-login"]);
|
||||
header("Location: /mailbox");
|
||||
exit();
|
||||
}
|
||||
else {
|
||||
session_regenerate_id(true);
|
||||
session_unset();
|
||||
session_destroy();
|
||||
session_write_close();
|
||||
header("Location: /");
|
||||
}
|
||||
}
|
||||
|
||||
// Check session
|
||||
function session_check() {
|
||||
if (isset($_SESSION['mailcow_cc_api']) && $_SESSION['mailcow_cc_api'] === true) {
|
||||
return true;
|
||||
}
|
||||
if (!isset($_SESSION['SESS_REMOTE_UA']) || ($_SESSION['SESS_REMOTE_UA'] != $_SERVER['HTTP_USER_AGENT'])) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'warning',
|
||||
'msg' => 'session_ua'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (!empty($_POST)) {
|
||||
if ($_SESSION['CSRF']['TOKEN'] != $_POST['csrf_token']) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'warning',
|
||||
'msg' => 'session_token'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
unset($_POST['csrf_token']);
|
||||
$_SESSION['CSRF']['TOKEN'] = bin2hex(random_bytes(32));
|
||||
$_SESSION['CSRF']['TIME'] = time();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isset($_SESSION['mailcow_cc_role']) && session_check() === false) {
|
||||
$_POST = array();
|
||||
$_FILES = array();
|
||||
}
|
||||
<?php
|
||||
// Start session
|
||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||
ini_set("session.cookie_httponly", 1);
|
||||
ini_set('session.gc_maxlifetime', $SESSION_LIFETIME);
|
||||
}
|
||||
|
||||
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
|
||||
strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == "https") {
|
||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||
ini_set("session.cookie_secure", 1);
|
||||
}
|
||||
$IS_HTTPS = true;
|
||||
}
|
||||
elseif (isset($_SERVER['HTTPS'])) {
|
||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||
ini_set("session.cookie_secure", 1);
|
||||
}
|
||||
$IS_HTTPS = true;
|
||||
}
|
||||
else {
|
||||
$IS_HTTPS = false;
|
||||
}
|
||||
|
||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
if (!isset($_SESSION['CSRF']['TOKEN'])) {
|
||||
$_SESSION['CSRF']['TOKEN'] = bin2hex(random_bytes(32));
|
||||
}
|
||||
|
||||
// Set session UA
|
||||
if (!isset($_SESSION['SESS_REMOTE_UA'])) {
|
||||
$_SESSION['SESS_REMOTE_UA'] = $_SERVER['HTTP_USER_AGENT'];
|
||||
}
|
||||
|
||||
// Keep session active
|
||||
if (isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY'] > $SESSION_LIFETIME)) {
|
||||
session_unset();
|
||||
session_destroy();
|
||||
}
|
||||
$_SESSION['LAST_ACTIVITY'] = time();
|
||||
|
||||
// API
|
||||
if (!empty($_SERVER['HTTP_X_API_KEY'])) {
|
||||
$stmt = $pdo->prepare("SELECT * FROM `api` WHERE `api_key` = :api_key AND `active` = '1';");
|
||||
$stmt->execute(array(
|
||||
':api_key' => preg_replace('/[^a-zA-Z0-9-]/', '', $_SERVER['HTTP_X_API_KEY'])
|
||||
));
|
||||
$api_return = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if (!empty($api_return['api_key'])) {
|
||||
$skip_ip_check = ($api_return['skip_ip_check'] == 1);
|
||||
$remote = get_remote_ip(false);
|
||||
$allow_from = array_map('trim', preg_split( "/( |,|;|\n)/", $api_return['allow_from']));
|
||||
if ($skip_ip_check === true || ip_acl($remote, $allow_from)) {
|
||||
$_SESSION['mailcow_cc_username'] = 'API';
|
||||
$_SESSION['mailcow_cc_role'] = 'admin';
|
||||
$_SESSION['mailcow_cc_api'] = true;
|
||||
if ($api_return['access'] == 'rw') {
|
||||
$_SESSION['mailcow_cc_api_access'] = 'rw';
|
||||
}
|
||||
else {
|
||||
$_SESSION['mailcow_cc_api_access'] = 'ro';
|
||||
}
|
||||
}
|
||||
else {
|
||||
$redis->publish("F2B_CHANNEL", "mailcow UI: Invalid password for API_USER by " . $_SERVER['REMOTE_ADDR']);
|
||||
error_log("mailcow UI: Invalid password for " . $user . " by " . $_SERVER['REMOTE_ADDR']);
|
||||
http_response_code(401);
|
||||
echo json_encode(array(
|
||||
'type' => 'error',
|
||||
'msg' => 'api access denied for ip ' . $_SERVER['REMOTE_ADDR']
|
||||
));
|
||||
unset($_POST);
|
||||
exit();
|
||||
}
|
||||
}
|
||||
else {
|
||||
$redis->publish("F2B_CHANNEL", "mailcow UI: Invalid password for API_USER by " . $_SERVER['REMOTE_ADDR']);
|
||||
error_log("mailcow UI: Invalid password for " . $user . " by " . $_SERVER['REMOTE_ADDR']);
|
||||
http_response_code(401);
|
||||
echo json_encode(array(
|
||||
'type' => 'error',
|
||||
'msg' => 'authentication failed'
|
||||
));
|
||||
unset($_POST);
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle logouts
|
||||
if (isset($_POST["logout"])) {
|
||||
if (isset($_SESSION["dual-login"])) {
|
||||
$_SESSION["mailcow_cc_username"] = $_SESSION["dual-login"]["username"];
|
||||
$_SESSION["mailcow_cc_role"] = $_SESSION["dual-login"]["role"];
|
||||
unset($_SESSION["dual-login"]);
|
||||
header("Location: /mailbox");
|
||||
exit();
|
||||
}
|
||||
else {
|
||||
session_regenerate_id(true);
|
||||
session_unset();
|
||||
session_destroy();
|
||||
session_write_close();
|
||||
header("Location: /");
|
||||
}
|
||||
}
|
||||
|
||||
// Check session
|
||||
function session_check() {
|
||||
if (isset($_SESSION['mailcow_cc_api']) && $_SESSION['mailcow_cc_api'] === true) {
|
||||
return true;
|
||||
}
|
||||
if (!isset($_SESSION['SESS_REMOTE_UA']) || ($_SESSION['SESS_REMOTE_UA'] != $_SERVER['HTTP_USER_AGENT'])) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'warning',
|
||||
'msg' => 'session_ua'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (!empty($_POST)) {
|
||||
if ($_SESSION['CSRF']['TOKEN'] != $_POST['csrf_token']) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'warning',
|
||||
'msg' => 'session_token'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
unset($_POST['csrf_token']);
|
||||
$_SESSION['CSRF']['TOKEN'] = bin2hex(random_bytes(32));
|
||||
$_SESSION['CSRF']['TIME'] = time();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isset($_SESSION['mailcow_cc_role']) && session_check() === false) {
|
||||
$_POST = array();
|
||||
$_FILES = array();
|
||||
}
|
||||
@@ -1,4 +1,15 @@
|
||||
<?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 (verify_tfa_login($_SESSION['pending_mailcow_cc_username'], $_POST)) {
|
||||
$_SESSION['mailcow_cc_username'] = $_SESSION['pending_mailcow_cc_username'];
|
||||
@@ -6,7 +17,7 @@ if (isset($_POST["verify_tfa_login"])) {
|
||||
unset($_SESSION['pending_mailcow_cc_username']);
|
||||
unset($_SESSION['pending_mailcow_cc_role']);
|
||||
unset($_SESSION['pending_tfa_methods']);
|
||||
|
||||
|
||||
header("Location: /user");
|
||||
} else {
|
||||
unset($_SESSION['pending_mailcow_cc_username']);
|
||||
@@ -34,7 +45,7 @@ if (isset($_POST["quick_delete"])) {
|
||||
if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
|
||||
$login_user = strtolower(trim($_POST["login_user"]));
|
||||
$as = check_login($login_user, $_POST["pass_user"]);
|
||||
|
||||
|
||||
if ($as == "admin") {
|
||||
$_SESSION['mailcow_cc_username'] = $login_user;
|
||||
$_SESSION['mailcow_cc_role'] = "admin";
|
||||
|
||||
@@ -107,12 +107,10 @@ $AVAILABLE_LANGUAGES = array(
|
||||
'zh-tw' => '繁體中文 (Traditional Chinese)',
|
||||
);
|
||||
|
||||
// Change theme (default: lumen)
|
||||
// Needs to be one of those: cerulean, cosmo, cyborg, darkly, flatly, journal, lumen, paper, readable, sandstone,
|
||||
// simplex, slate, spacelab, superhero, united, yeti
|
||||
// See https://bootswatch.com/
|
||||
// WARNING: Only lumen is loaded locally. Enabling any other theme, will download external sources.
|
||||
$DEFAULT_THEME = 'lumen';
|
||||
// default theme is lumen
|
||||
// additional themes can be found here: https://bootswatch.com/
|
||||
// copy them to data/web/css/themes/{THEME-NAME}-bootstrap.css
|
||||
$UI_THEME = "lumen";
|
||||
|
||||
// Show DKIM private keys - false by default
|
||||
$SHOW_DKIM_PRIV_KEYS = false;
|
||||
@@ -126,7 +124,7 @@ $MAILCOW_APPS = array(
|
||||
);
|
||||
|
||||
// Rows until pagination begins
|
||||
$PAGINATION_SIZE = 20;
|
||||
$PAGINATION_SIZE = 25;
|
||||
|
||||
// Default number of rows/lines to display (log table)
|
||||
$LOG_LINES = 1000;
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ if (isset($_SESSION['mailcow_cc_role']) && isset($_SESSION['oauth2_request'])) {
|
||||
exit();
|
||||
}
|
||||
elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'admin') {
|
||||
header('Location: /admin');
|
||||
header('Location: /debug');
|
||||
exit();
|
||||
}
|
||||
elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'domainadmin') {
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */
|
||||
!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}S.fn=S.prototype={jquery:f,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(n){return this.pushStack(S.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(S.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},S.extend=S.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(S.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||S.isPlainObject(n)?n:{},i=!1,a[t]=S.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},S.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==o.call(e))&&(!(t=r(e))||"function"==typeof(n=v.call(t,"constructor")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){b(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(p(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(p(Object(e))?S.merge(n,"string"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(p(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:y}),"function"==typeof Symbol&&(S.fn[Symbol.iterator]=t[Symbol.iterator]),S.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var d=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,S="sizzle"+1*new Date,p=n.document,k=0,r=0,m=ue(),x=ue(),A=ue(),N=ue(),j=function(e,t){return e===t&&(l=!0),0},D={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",I="(?:\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",W="\\["+M+"*("+I+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+I+"))|)"+M+"*\\]",F=":("+I+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+W+")*)|.*)\\)|)",B=new RegExp(M+"+","g"),$=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=new RegExp("^"+M+"*,"+M+"*"),z=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^Line truncated
|
||||
Vendored
-4
@@ -1,4 +0,0 @@
|
||||
/*! jQuery v3.4.1 | (c) JS Foundation and other contributors | jquery.org/license */
|
||||
!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],E=C.document,r=Object.getPrototypeOf,s=t.slice,g=t.concat,u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.4.1",k=function(e,t){return new k.fn.init(e,t)},p=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function d(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}k.fn=k.prototype={jquery:f,constructor:k,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=k.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return k.each(this,e)},map:function(n){return this.pushStack(k.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},k.extend=k.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(k.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||k.isPlainObject(n)?n:{},i=!1,a[t]=k.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},k.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==o.call(e))&&(!(t=r(e))||"function"==typeof(n=v.call(t,"constructor")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t){b(e,{nonce:t&&t.nonce})},each:function(e,t){var n,r=0;if(d(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},trim:function(e){return null==e?"":(e+"").replace(p,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(d(Object(e))?k.merge(n,"string"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(d(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g.apply([],a)},guid:1,support:y}),"function"==typeof Symbol&&(k.fn[Symbol.iterator]=t[Symbol.iterator]),k.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var h=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,k="sizzle"+1*new Date,m=n.document,S=0,r=0,p=ue(),x=ue(),N=ue(),A=ue(),D=function(e,t){return e===t&&(l=!0),0},j={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",I="(?:\\\\.|[\\w-]|[^\0-\\xa0])+",W="\\["+M+"*("+I+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+I+"))|)"+M+"*\\]",$=":("+I+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+W+")*)|.*)\\)|)",F=new RegExp(M+"+","g"),B=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=new RegExp("^"+M+"*,"+M+"*"),z=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp($),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+$),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+Line truncated
|
||||
/*! jQuery Migrate v3.1.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */
|
||||
"undefined"==typeof jQuery.migrateMute&&(jQuery.migrateMute=!0),function(t){"function"==typeof define&&define.amd?define(["jquery"],function(e){return t(e,window)}):"object"==typeof module&&module.exports?module.exports=t(require("jquery"),window):t(jQuery,window)}(function(s,n){"use strict";function e(e){return 0<=function(e,t){for(var r=/^(\d+)\.(\d+)\.(\d+)/,n=r.exec(e)||[],o=r.exec(t)||[],i=1;i<=3;i++){if(+n[i]>+o[i])return 1;if(+n[i]<+o[i])return-1}return 0}(s.fn.jquery,e)}s.migrateVersion="3.1.0",n.console&&n.console.log&&(s&&e("3.0.0")||n.console.log("JQMIGRATE: jQuery 3.0.0+ REQUIRED"),s.migrateWarnings&&n.console.log("JQMIGRATE: Migrate plugin loaded multiple times"),n.console.log("JQMIGRATE: Migrate is installed"+(s.migrateMute?"":" with logging active")+", version "+s.migrateVersion));var r={};function u(e){var t=n.console;r[e]||(r[e]=!0,s.migrateWarnings.push(e),t&&t.warn&&!s.migrateMute&&(t.warn("JQMIGRATE: "+e),s.migrateTrace&&t.trace&&t.trace()))}function t(e,t,r,n){Object.defineProperty(e,t,{configurable:!0,enumerable:!0,get:function(){return u(n),r},set:function(e){u(n),r=e}})}function o(e,t,r,n){e[t]=function(){return u(n),r.apply(this,arguments)}}s.migrateWarnings=[],void 0===s.migrateTrace&&(s.migrateTrace=!0),s.migrateReset=function(){r={},s.migrateWarnings.length=0},"BackCompat"===n.document.compatMode&&u("jQuery is not compatible with Quirks Mode");var i,a=s.fn.init,c=s.isNumeric,d=s.find,l=/\[(\s*[-\w]+\s*)([~|^$*]?=)\s*([-\w#]*?#[-\w#]*)\s*\]/,p=/\[(\s*[-\w]+\s*)([~|^$*]?=)\s*([-\w#]*?#[-\w#]*)\s*\]/g;for(i in s.fn.init=function(e){var t=Array.prototype.slice.call(arguments);return"string"==typeof e&&"#"===e&&(u("jQuery( '#' ) is not a valid selector"),t[0]=[]),a.apply(this,t)},s.fn.init.prototype=s.fn,s.find=function(t){var r=Array.prototype.slice.call(arguments);if("string"==typeof t&&l.test(t))try{n.document.querySelector(t)}catch(e){t=t.replace(p,function(e,t,r,n){return"["+t+r+'"'+n+'"]'});try{n.document.querySelector(t),u("Attribute selector with '#' must be quoted: "+r[0]),r[0]=t}catch(e){u("Attribute selector with '#' was not fixed: "+r[0])}}return d.apply(this,r)},d)Object.prototype.hasOwnProperty.call(d,i)&&(s.find[i]=d[i]);s.fn.size=function(){return u("jQuery.fn.size() is deprecated and removed; use the .length property"),this.length},s.parseJSON=function(){return u("jQuery.parseJSON is deprecated; use JSON.parse"),JSON.parse.apply(null,arguments)},s.isNumeric=function(e){var t,r,n=c(e),o=(r=(t=e)&&t.toString(),!s.isArray(t)&&0<=r-parseFloat(r)+1);return n!==o&&u("jQuery.isNumeric() should not be called on constructed objects"),o},e("3.3.0")&&o(s,"isWindow",function(e){return null!=e&&e===e.window},"jQuery.isWindow() is deprecated"),o(s,"holdReady",s.holdReady,"jQuery.holdReady is deprecated"),o(s,"unique",s.uniqueSort,"jQuery.unique is deprecated; use jQuery.uniqueSort"),t(s.expr,"filters",s.expr.pseudos,"jQuery.expr.filters is deprecated; use jQuery.expr.pseudos"),t(s.expr,":",s.expr.pseudos,"jQuery.expr[':'] is deprecated; use jQuery.expr.pseudos"),e("3.2.0")&&o(s,"nodeName",s.nodeName,"jQuery.nodeName is deprecated");var f=s.ajax;s.ajax=function(){var e=f.apply(this,arguments);return e.promise&&(o(e,"success",e.done,"jQXHR.success is deprecated and removed"),o(e,"error",e.fail,"jQXHR.error is deprecated and removed"),o(e,"complete",e.always,"jQXHR.complete is deprecated and removed")),e};var y=s.fn.removeAttr,m=s.fn.toggleClass,h=/\S+/g;s.fn.removeAttr=function(e){var r=this;return s.each(e.match(h),function(e,t){s.expr.match.bool.test(t)&&(u("jQuery.fn.removeAttr no longer sets boolean properties: "+t),r.prop(t,!1))}),y.apply(this,arguments)};var g=!(s.fn.toggleClass=function(t){return void 0!==t&&"boolean"!=typeof t?m.apply(this,arguments):(u("jQuery.fn.toggleClass( boolean ) is deprecated"),this.each(function(){var e=this.getAttribute&&this.getAttribute("class")||"";e&&s.data(this,"__className__",e),this.setAttribute&&this.setAttribute("class",e||!1===t?"":s.data(this,"__className__")||"")}))});s.swap&&s.each(["height","width","reliableMarginRight"],function(e,t){var r=s.cssHooks[t]&&s.cssHooks[t].get;r&&(s.cssHooks[t].get=function(){var e;return g=!0,e=r.apply(this,arguments),g=!1,e})}),s.swap=function(e,t,r,n){var o,i,a={};for(i in g||u("jQuery.swap() is undocumented and deprecated"),t)a[i]=e.style[i],e.style[i]=t[i];for(i in o=r.apply(e,n||[]),t)e.style[i]=a[i];return o};var v=s.data;s.data=function(e,t,r){var n;if(t&&"object"==typeof t&&2===arguments.length){n=s.hasData(e)&&v.call(this,e);var o={};for(var i in t)i!==s.camelCase(i)?(u("jQuery.data() always sets/gets camelCased names: "+i),n[i]=t[i]):o[i]=t[i];return v.call(this,e,o),t}return t&&"string"==typeof t&&t!==s.camelCase(t)&&(n=s.hasData(e)&&v.call(this,e))&&t in n?(u("jQuery.data() always sets/gets camelCased names: "+t),2<arguments.length&&(n[t]=r),n[t]):v.apply(this,arguments)};function j(e){return e}var Q=s.Tween.prototype.run;s.Tween.prototype.run=function(){1<s.easing[this.easing].length&&(u("'jLine truncated
|
||||
@@ -0,0 +1,7 @@
|
||||
/*!
|
||||
* Bootstrap v5.1.3 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e()}(this,(function(){"use strict";const t="transitionend",e=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let i=t.getAttribute("href");if(!i||!i.includes("#")&&!i.startsWith("."))return null;i.includes("#")&&!i.startsWith("#")&&(i=`#${i.split("#")[1]}`),e=i&&"#"!==i?i.trim():null}return e},i=t=>{const i=e(t);return i&&document.querySelector(i)?i:null},n=t=>{const i=e(t);return i?document.querySelector(i):null},s=e=>{e.dispatchEvent(new Event(t))},o=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),r=t=>o(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(t):null,a=(t,e,i)=>{Object.keys(i).forEach((n=>{const s=i[n],r=e[n],a=r&&o(r)?"element":null==(l=r)?`${l}`:{}.toString.call(l).match(/\s([a-z]+)/i)[1].toLowerCase();var l;if(!new RegExp(s).test(a))throw new TypeError(`${t.toUpperCase()}: Option "${n}" provided type "${a}" but expected type "${s}".`)}))},l=t=>!(!o(t)||0===t.getClientRects().length)&&"visible"===getComputedStyle(t).getPropertyValue("visibility"),c=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),h=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?h(t.parentNode):null},d=()=>{},u=t=>{t.offsetHeight},f=()=>{const{jQuery:t}=window;return t&&!document.body.hasAttribute("data-bs-no-jquery")?t:null},p=[],m=()=>"rtl"===document.documentElement.dir,g=t=>{var e;e=()=>{const e=f();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(p.length||document.addEventListener("DOMContentLoaded",(()=>{p.forEach((t=>t()))})),p.push(e)):e()},_=t=>{"function"==typeof t&&t()},b=(e,i,n=!0)=>{if(!n)return void _(e);const o=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(i)+5;let r=!1;const a=({target:n})=>{n===i&&(r=!0,i.removeEventListener(t,a),_(e))};i.addEventListener(t,a),setTimeout((()=>{r||s(i)}),o)},v=(t,e,i,n)=>{let s=t.indexOf(e);if(-1===s)return t[!i&&n?t.length-1:0];const o=t.length;return s+=i?1:-1,n&&(s=(s+o)%o),t[Math.max(0,Math.min(s,o-1))]},y=/[^.]*(?=\..*)\.|.*/,w=/\..*/,E=/::\d+$/,A={};let T=1;const O={mouseenter:"mouseover",mouseleave:"mouseout"},C=/^(mouseenter|mouseleave)/i,k=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function L(t,e){return e&&`${e}::${T++}`||t.uidEvent||T++}function x(t){const e=L(t);return t.uidEvent=e,A[e]=A[e]||{},A[e]}function D(t,e,i=null){const n=Object.keys(t);for(let s=0,o=n.length;s<o;s++){const o=t[n[s]];if(o.originalHandler===e&&o.delegationSelector===i)return o}return null}function S(t,e,i){const n="string"==typeof e,s=n?i:e;let o=P(t);return k.has(o)||(o=t),[n,s,o]}function N(t,e,i,n,s){if("string"!=typeof e||!t)return;if(i||(i=n,n=null),C.test(e)){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};n?n=t(n):i=t(i)}const[o,r,a]=S(e,i,n),l=x(t),c=l[a]||(l[a]={}),h=D(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=L(r,e.replace(y,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(let a=o.length;a--;)if(o[a]===r)return s.delegateTarget=r,n.oneOff&&j.off(t,s.type,e,i),i.apply(r,[s]);return null}}(t,i,n):function(t,e){return function i(n){return n.delegateTarget=t,i.oneOff&&j.off(t,n.type,e),e.apply(t,[n])}}(t,i);u.delegationSelector=o?i:null,u.originalHandler=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function I(t,e,i,n,s){const o=D(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function P(t){return t=t.replace(w,""),O[t]||t}const j={on(t,e,i,n){N(t,e,i,n,!1)},one(t,e,i,n){N(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=S(e,i,n),a=r!==e,l=x(t),c=e.startsWith(".");if(void 0!==o){if(!Line truncated
|
||||
//# sourceMappingURL=bootstrap.bundle.min.js.map
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
/*!
|
||||
* Bootstrap v3.3.7 (http://getbootstrap.com)
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under the MIT license
|
||||
*/
|
||||
if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>3)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){if(a(b.target).is(this))return b.handleObj.handler.apply(this,arguments)}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.7",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a("#"===f?[]:f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.7",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c).prop(c,!0)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c).prop(c,!1))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target).closest(".btn");b.call(d,"toggle"),a(c.target).is('input[type="radio"], input[type="checkbox"]')||(c.preventDefault(),d.is("input,button")?d.trigger("focus"):d.find("input:visible,button:visible").first().trigger("focus"))}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.7",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:tLine truncated
|
||||
this.activeTarget=b,this.clear();var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")},b.prototype.clear=function(){a(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var d=a.fn.scrollspy;a.fn.scrollspy=c,a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=d,this},a(window).on("load.bs.scrollspy.data-api",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new c(this)),"string"==typeof b&&e[b]()})}var c=function(b){this.element=a(b)};c.VERSION="3.3.7",c.TRANSITION_DURATION=150,c.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a"),f=a.Event("hide.bs.tab",{relatedTarget:b[0]}),g=a.Event("show.bs.tab",{relatedTarget:e[0]});if(e.trigger(f),b.trigger(g),!g.isDefaultPrevented()&&!f.isDefaultPrevented()){var h=a(d);this.activate(b.closest("li"),c),this.activate(h,h.parent(),function(){e.trigger({type:"hidden.bs.tab",relatedTarget:b[0]}),b.trigger({type:"shown.bs.tab",relatedTarget:e[0]})})}}},c.prototype.activate=function(b,d,e){function f(){g.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.7",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return e<c&&"top";if("bottom"==this.affixed)return null!=c?!(e+this.unpin<=f.top)&&"bottom":!(e+g<=a-d)&&"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&e<=c?"top":null!=d&&i+j>=a-d&&"bottom"},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery);
|
||||
File renamed without changes.
File diff suppressed because it is too large.
Load diff
@@ -1,8 +0,0 @@
|
||||
/*!
|
||||
* Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select)
|
||||
*
|
||||
* Copyright 2013-2017 bootstrap-select
|
||||
* Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE)
|
||||
*/
|
||||
!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],function(a){return b(a)}):"object"==typeof module&&module.exports?module.exports=b(require("jquery")):b(a.jQuery)}(this,function(a){!function(a){"use strict";function b(b){var c=[{re:/[\xC0-\xC6]/g,ch:"A"},{re:/[\xE0-\xE6]/g,ch:"a"},{re:/[\xC8-\xCB]/g,ch:"E"},{re:/[\xE8-\xEB]/g,ch:"e"},{re:/[\xCC-\xCF]/g,ch:"I"},{re:/[\xEC-\xEF]/g,ch:"i"},{re:/[\xD2-\xD6]/g,ch:"O"},{re:/[\xF2-\xF6]/g,ch:"o"},{re:/[\xD9-\xDC]/g,ch:"U"},{re:/[\xF9-\xFC]/g,ch:"u"},{re:/[\xC7-\xE7]/g,ch:"c"},{re:/[\xD1]/g,ch:"N"},{re:/[\xF1]/g,ch:"n"}];return a.each(c,function(){b=b?b.replace(this.re,this.ch):""}),b}function c(b){var c=arguments,d=b;[].shift.apply(c);var e,f=this.each(function(){var b=a(this);if(b.is("select")){var f=b.data("selectpicker"),g="object"==typeof d&&d;if(f){if(g)for(var h in g)g.hasOwnProperty(h)&&(f.options[h]=g[h])}else{var i=a.extend({},k.DEFAULTS,a.fn.selectpicker.defaults||{},b.data(),g);i.template=a.extend({},k.DEFAULTS.template,a.fn.selectpicker.defaults?a.fn.selectpicker.defaults.template:{},b.data().template,g.template),b.data("selectpicker",f=new k(this,i))}"string"==typeof d&&(e=f[d]instanceof Function?f[d].apply(f,c):f.options[d])}});return"undefined"!=typeof e?e:f}String.prototype.includes||!function(){var a={}.toString,b=function(){try{var a={},b=Object.defineProperty,c=b(a,a,a)&&b}catch(a){}return c}(),c="".indexOf,d=function(b){if(null==this)throw new TypeError;var d=String(this);if(b&&"[object RegExp]"==a.call(b))throw new TypeError;var e=d.length,f=String(b),g=f.length,h=arguments.length>1?arguments[1]:void 0,i=h?Number(h):0;i!=i&&(i=0);var j=Math.min(Math.max(i,0),e);return!(g+j>e)&&c.call(d,f,i)!=-1};b?b(String.prototype,"includes",{value:d,configurable:!0,writable:!0}):String.prototype.includes=d}(),String.prototype.startsWith||!function(){var a=function(){try{var a={},b=Object.defineProperty,c=b(a,a,a)&&b}catch(a){}return c}(),b={}.toString,c=function(a){if(null==this)throw new TypeError;var c=String(this);if(a&&"[object RegExp]"==b.call(a))throw new TypeError;var d=c.length,e=String(a),f=e.length,g=arguments.length>1?arguments[1]:void 0,h=g?Number(g):0;h!=h&&(h=0);var i=Math.min(Math.max(h,0),d);if(f+i>d)return!1;for(var j=-1;++j<f;)if(c.charCodeAt(i+j)!=e.charCodeAt(j))return!1;return!0};a?a(String.prototype,"startsWith",{value:c,configurable:!0,writable:!0}):String.prototype.startsWith=c}(),Object.keys||(Object.keys=function(a,b,c){c=[];for(b in a)c.hasOwnProperty.call(a,b)&&c.push(b);return c});var d={useDefault:!1,_set:a.valHooks.select.set};a.valHooks.select.set=function(b,c){return c&&!d.useDefault&&a(b).data("selected",!0),d._set.apply(this,arguments)};var e=null;a.fn.triggerNative=function(a){var b,c=this[0];c.dispatchEvent?("function"==typeof Event?b=new Event(a,{bubbles:!0}):(b=document.createEvent("Event"),b.initEvent(a,!0,!1)),c.dispatchEvent(b)):c.fireEvent?(b=document.createEventObject(),b.eventType=a,c.fireEvent("on"+a,b)):this.trigger(a)},a.expr.pseudos.icontains=function(b,c,d){var e=a(b),f=(e.data("tokens")||e.text()).toString().toUpperCase();return f.includes(d[3].toUpperCase())},a.expr.pseudos.ibegins=function(b,c,d){var e=a(b),f=(e.data("tokens")||e.text()).toString().toUpperCase();return f.startsWith(d[3].toUpperCase())},a.expr.pseudos.aicontains=function(b,c,d){var e=a(b),f=(e.data("tokens")||e.data("normalizedText")||e.text()).toString().toUpperCase();return f.includes(d[3].toUpperCase())},a.expr.pseudos.aibegins=function(b,c,d){var e=a(b),f=(e.data("tokens")||e.data("normalizedText")||e.text()).toString().toUpperCase();return f.startsWith(d[3].toUpperCase())};var f={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},g={"&":"&","<":"<",">":">",""":'"',"'":"'","`":"`"},h=function(a){var b=function(b){return a[b]},c="(?:"+Object.keys(a).join("|")+")",d=RegExp(c),e=RegExp(c,"g");return function(a){return a=null==a?"":""+a,d.test(a)?a.replace(e,b):a}},i=h(f),j=h(g),k=function(b,c){d.useDefault||(a.valHooks.select.set=d._set,d.useDefault=!0),this.$element=a(b),this.$newElement=null,this.$button=null,this.$menu=null,this.$lis=null,this.options=c,null===this.options.title&&(this.options.title=this.$element.attr("title"));var e=this.options.windowPadding;"number"==typeof e&&(this.options.windowPadding=[e,e,e,e]),this.val=k.prototype.val,this.render=k.prototype.render,this.refresh=k.prototype.refresh,this.setStyle=k.prototype.setStyle,this.selectAll=k.prototype.selectAll,this.deselectAll=k.prototype.deselectAll,this.destroy=k.prototype.destroy,this.remove=k.prototype.remove,this.show=k.prototype.show,this.hide=k.prototype.hide,this.init()};k.VERSION="1.12.2",k.DEFAULTS={noneSelectedText:"Nothing selected",noneResultsText:"No results matched {0}",countSelectedText:function(a,b){return 1==a?"{0} item selected":"{0} items selected"},maxOptionsText:function(a,b){return[1==a?"Limit reached ({n} item max)":"Limit reached ({n} items max)",1==b?"Group limit reached ({nLine truncated
|
||||
m.focus());else{var u=a(":focus");u.click(),u.focus(),c.preventDefault(),a(document).data("spaceSelect",!0)}a(document).data("keycount",0)}(/(^9$|27)/.test(c.keyCode.toString(10))&&l&&(o.multiple||o.options.liveSearch)||/(27)/.test(c.keyCode.toString(10))&&!l)&&(o.$menu.parent().removeClass("open"),o.options.container&&o.$newElement.removeClass("open"),o.$button.focus())}},mobile:function(){this.$element.addClass("mobile-device")},refresh:function(){this.$lis=null,this.liObj={},this.reloadLi(),this.render(),this.checkDisabled(),this.liHeight(!0),this.setStyle(),this.setWidth(),this.$lis&&this.$searchbox.trigger("propertychange"),this.$element.trigger("refreshed.bs.select")},hide:function(){this.$newElement.hide()},show:function(){this.$newElement.show()},remove:function(){this.$newElement.remove(),this.$element.remove()},destroy:function(){this.$newElement.before(this.$element).remove(),this.$bsContainer?this.$bsContainer.remove():this.$menu.remove(),this.$element.off(".bs.select").removeData("selectpicker").removeClass("bs-select-hidden selectpicker")}};var l=a.fn.selectpicker;a.fn.selectpicker=c,a.fn.selectpicker.Constructor=k,a.fn.selectpicker.noConflict=function(){return a.fn.selectpicker=l,this},a(document).data("keycount",0).on("keydown.bs.select",'.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role="listbox"], .bs-searchbox input',k.prototype.keydown).on("focusin.modal",'.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role="listbox"], .bs-searchbox input',function(a){a.stopPropagation()}),a(window).on("load.bs.select.data-api",function(){a(".selectpicker").each(function(){var b=a(this);c.call(b,b.data())})})}(a)});
|
||||
File diff suppressed because it is too large.
Load diff
+1
@@ -0,0 +1 @@
|
||||
!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):jQuery)}(function(a){function b(h,b,d){var f,e,g={content:{message:"object"==typeof b?b.message:b,title:b.title?b.title:"",icon:b.icon?b.icon:"",url:b.url?b.url:"#",target:b.target?b.target:"-"}};d=a.extend(!0,{},g,d),this.settings=a.extend(!0,{},c,d),this._defaults=c,"-"===this.settings.content.target&&(this.settings.content.target=this.settings.url_target),this.animations={start:"webkitAnimationStart oanimationstart MSAnimationStart animationstart",end:"webkitAnimationEnd oanimationend MSAnimationEnd animationend"},"number"==typeof this.settings.offset&&(this.settings.offset={x:this.settings.offset,y:this.settings.offset}),!this.settings.allow_duplicates&&(this.settings.allow_duplicates||(f=this,e=!1,a('[data-notify="container"]').each(function(k,c){var b=a(c),d=b.find('[data-notify="title"]').text().trim(),g=b.find('[data-notify="message"]').html().trim(),h=d===a("<div>"+f.settings.content.title+"</div>").html().trim(),i=g===a("<div>"+f.settings.content.message+"</div>").html().trim(),j=b.hasClass("alert-"+f.settings.type);return h&&i&&j&&(e=!0),!e}),e))||this.init()}var c={element:"body",position:null,type:"info",allow_dismiss:!0,allow_duplicates:!0,newest_on_top:!1,showProgressbar:!1,placement:{from:"top",align:"right"},offset:20,spacing:10,z_index:1031,delay:5e3,timer:1e3,url_target:"_blank",mouse_over:null,animate:{enter:"animated fadeInDown",exit:"animated fadeOutUp"},onShow:null,onShown:null,onClose:null,onClosed:null,icon_type:"class",template:'<div data-notify="container" class="col-xs-11 col-sm-4 alert alert-{0}" role="alert"><button type="button" aria-hidden="true" class="btn-close" data-notify="dismiss"></button><span data-notify="icon"></span> <span data-notify="title">{1}</span> <span data-notify="message">{2}</span><div class="progress" data-notify="progressbar"><div class="progress-bar progress-bar-{0}" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;"></div></div><a href="{3}" target="{4}" data-notify="url"></a></div>'};String.format=function(){for(var b=arguments[0],a=1;a<arguments.length;a++)b=b.replace(RegExp("\\{"+(a-1)+"\\}","gm"),arguments[a]);return b},a.extend(b.prototype,{init:function(){var a=this;this.buildNotify(),this.settings.content.icon&&this.setIcon(),"#"!=this.settings.content.url&&this.styleURL(),this.styleDismiss(),this.placement(),this.bind(),this.notify={$ele:this.$ele,update:function(e,f){var b={};for(var c in"string"==typeof e?b[e]=f:b=e,b)switch(c){case"type":this.$ele.removeClass("alert-"+a.settings.type),this.$ele.find('[data-notify="progressbar"] > .progress-bar').removeClass("progress-bar-"+a.settings.type),a.settings.type=b[c],this.$ele.addClass("alert-"+b[c]).find('[data-notify="progressbar"] > .progress-bar').addClass("progress-bar-"+b[c]);break;case"icon":var d=this.$ele.find('[data-notify="icon"]');"class"===a.settings.icon_type.toLowerCase()?d.removeClass(a.settings.content.icon).addClass(b[c]):(d.is("img")||d.find("img"),d.attr("src",b[c]));break;case"progress":var g=a.settings.delay-a.settings.delay*(b[c]/100);this.$ele.data("notify-delay",g),this.$ele.find('[data-notify="progressbar"] > div').attr("aria-valuenow",b[c]).css("width",b[c]+"%");break;case"url":this.$ele.find('[data-notify="url"]').attr("href",b[c]);break;case"target":this.$ele.find('[data-notify="url"]').attr("target",b[c]);break;default:this.$ele.find('[data-notify="'+c+'"]').html(b[c])}var h=this.$ele.outerHeight()+parseInt(a.settings.spacing)+parseInt(a.settings.offset.y);a.reposition(h)},close:function(){a.close()}}},buildNotify:function(){var b=this.settings.content;this.$ele=a(String.format(this.settings.template,this.settings.type,b.title,b.message,b.url,b.target)),this.$ele.attr("data-notify-position",this.settings.placement.from+"-"+this.settings.placement.align),this.settings.allow_dismiss||this.$ele.find('[data-notify="dismiss"]').css("display","none"),(!(this.settings.delay<=0)||this.settings.showProgressbar)&&this.settings.showProgressbar||this.$ele.find('[data-notify="progressbar"]').remove()},setIcon:function(){"class"===this.settings.icon_type.toLowerCase()?this.$ele.find('[data-notify="icon"]').addClass(this.settings.content.icon):this.$ele.find('[data-notify="icon"]').is("img")?this.$ele.find('[data-notify="icon"]').attr("src",this.settings.content.icon):this.$ele.find('[data-notify="icon"]').append('<img src="'+this.settings.content.icon+'" alt="Notify Icon" />')},styleDismiss:function(){this.$ele.find('[data-notify="dismiss"]').css({position:"absolute",right:"10px",top:"5px",zIndex:this.settings.z_index+2})},styleURL:function(){this.$ele.find('[data-notify="url"]').css({backgroundImage:"url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)",height:"100%",left:0,position:"absolute",top:0,width:"100%",zIndex:this.settings.z_index+1})},placement:function(){var d=this,b=this.settings.Line truncated
|
||||
File renamed without changes.
-1
@@ -1 +0,0 @@
|
||||
!function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t("object"==typeof exports?require("jquery"):jQuery)}(function(t){function s(s){var e=!1;return t('[data-notify="container"]').each(function(i,n){var a=t(n),o=a.find('[data-notify="title"]').text().trim(),r=a.find('[data-notify="message"]').html().trim(),l=o===t("<div>"+s.settings.content.title+"</div>").html().trim(),d=r===t("<div>"+s.settings.content.message+"</div>").html().trim(),g=a.hasClass("alert-"+s.settings.type);return l&&d&&g&&(e=!0),!e}),e}function e(e,n,a){var o={content:{message:"object"==typeof n?n.message:n,title:n.title?n.title:"",icon:n.icon?n.icon:"",url:n.url?n.url:"#",target:n.target?n.target:"-"}};a=t.extend(!0,{},o,a),this.settings=t.extend(!0,{},i,a),this._defaults=i,"-"===this.settings.content.target&&(this.settings.content.target=this.settings.url_target),this.animations={start:"webkitAnimationStart oanimationstart MSAnimationStart animationstart",end:"webkitAnimationEnd oanimationend MSAnimationEnd animationend"},"number"==typeof this.settings.offset&&(this.settings.offset={x:this.settings.offset,y:this.settings.offset}),(this.settings.allow_duplicates||!this.settings.allow_duplicates&&!s(this))&&this.init()}var i={element:"body",position:null,type:"info",allow_dismiss:!0,allow_duplicates:!0,newest_on_top:!1,showProgressbar:!1,placement:{from:"top",align:"right"},offset:20,spacing:10,z_index:1031,delay:5e3,timer:1e3,url_target:"_blank",mouse_over:null,animate:{enter:"animated fadeInDown",exit:"animated fadeOutUp"},onShow:null,onShown:null,onClose:null,onClosed:null,icon_type:"class",template:'<div data-notify="container" class="col-xs-11 col-sm-4 alert alert-{0}" role="alert"><button type="button" aria-hidden="true" class="close" data-notify="dismiss">×</button><span data-notify="icon"></span> <span data-notify="title">{1}</span> <span data-notify="message">{2}</span><div class="progress" data-notify="progressbar"><div class="progress-bar progress-bar-{0}" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;"></div></div><a href="{3}" target="{4}" data-notify="url"></a></div>'};String.format=function(){for(var t=arguments[0],s=1;s<arguments.length;s++)t=t.replace(RegExp("\\{"+(s-1)+"\\}","gm"),arguments[s]);return t},t.extend(e.prototype,{init:function(){var t=this;this.buildNotify(),this.settings.content.icon&&this.setIcon(),"#"!=this.settings.content.url&&this.styleURL(),this.styleDismiss(),this.placement(),this.bind(),this.notify={$ele:this.$ele,update:function(s,e){var i={};"string"==typeof s?i[s]=e:i=s;for(var n in i)switch(n){case"type":this.$ele.removeClass("alert-"+t.settings.type),this.$ele.find('[data-notify="progressbar"] > .progress-bar').removeClass("progress-bar-"+t.settings.type),t.settings.type=i[n],this.$ele.addClass("alert-"+i[n]).find('[data-notify="progressbar"] > .progress-bar').addClass("progress-bar-"+i[n]);break;case"icon":var a=this.$ele.find('[data-notify="icon"]');"class"===t.settings.icon_type.toLowerCase()?a.removeClass(t.settings.content.icon).addClass(i[n]):(a.is("img")||a.find("img"),a.attr("src",i[n]));break;case"progress":var o=t.settings.delay-t.settings.delay*(i[n]/100);this.$ele.data("notify-delay",o),this.$ele.find('[data-notify="progressbar"] > div').attr("aria-valuenow",i[n]).css("width",i[n]+"%");break;case"url":this.$ele.find('[data-notify="url"]').attr("href",i[n]);break;case"target":this.$ele.find('[data-notify="url"]').attr("target",i[n]);break;default:this.$ele.find('[data-notify="'+n+'"]').html(i[n])}var r=this.$ele.outerHeight()+parseInt(t.settings.spacing)+parseInt(t.settings.offset.y);t.reposition(r)},close:function(){t.close()}}},buildNotify:function(){var s=this.settings.content;this.$ele=t(String.format(this.settings.template,this.settings.type,s.title,s.message,s.url,s.target)),this.$ele.attr("data-notify-position",this.settings.placement.from+"-"+this.settings.placement.align),this.settings.allow_dismiss||this.$ele.find('[data-notify="dismiss"]').css("display","none"),(this.settings.delay<=0&&!this.settings.showProgressbar||!this.settings.showProgressbar)&&this.$ele.find('[data-notify="progressbar"]').remove()},setIcon:function(){"class"===this.settings.icon_type.toLowerCase()?this.$ele.find('[data-notify="icon"]').addClass(this.settings.content.icon):this.$ele.find('[data-notify="icon"]').is("img")?this.$ele.find('[data-notify="icon"]').attr("src",this.settings.content.icon):this.$ele.find('[data-notify="icon"]').append('<img src="'+this.settings.content.icon+'" alt="Notify Icon" />')},styleDismiss:function(){this.$ele.find('[data-notify="dismiss"]').css({position:"absolute",right:"10px",top:"5px",zIndex:this.settings.z_index+2})},styleURL:function(){this.$ele.find('[data-notify="url"]').css({backgroundImage:"url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)",height:"100%",left:0,position:"absolute",top:0,width:"100%",zIndex:this.settings.z_index+1})},placement:function(){varLine truncated
|
||||
File diff suppressed because it is too large.
Load diff
Loaded 100 of 226 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user