Compare commits
81 commits
Author | SHA1 | Date | |
---|---|---|---|
5882a98966 | |||
5b04c476c9 | |||
38b120b03e | |||
f3ef40df0b | |||
a3feb92503 | |||
11c04df074 | |||
7c57b4cc94 | |||
78e06dd2b9 | |||
90795375e7 | |||
c1b226e5a1 | |||
5e527ef10d | |||
3b6d50a62a | |||
4b74f5d04b | |||
ec6a9a7cc1 | |||
f21987158b | |||
2e7dca529a | |||
1d4f070867 | |||
d1494af0a1 | |||
69638b6b6d | |||
ee561a5127 | |||
b69ecbfa2d | |||
bf7b02d5b5 | |||
22945f72bf | |||
d0fe915612 | |||
cb62bd63e2 | |||
affb49a971 | |||
8aadae1c83 | |||
30ffdf1751 | |||
aed8e97dbc | |||
4577ec4896 | |||
8c7af9a246 | |||
175ffd19f4 | |||
27e9d27b2b | |||
3b04595c7a | |||
c7fda0e993 | |||
376cfab88f | |||
be328758e6 | |||
d88745f46b | |||
866f6c8083 | |||
2d05580ed3 | |||
397e479925 | |||
5db97f03d1 | |||
0962891a9b | |||
a136bcc52b | |||
806ee80a85 | |||
10628ee45f | |||
be1ed6ecea | |||
9fbd608837 | |||
8e42cb9c18 | |||
5cf7ef7a23 | |||
0f91587c60 | |||
a65b1574db | |||
9731d4e793 | |||
f9ea88cd24 | |||
4b7e311c62 | |||
37f3ed2506 | |||
e44bdd7be2 | |||
345b32f286 | |||
d499b781d4 | |||
472e272305 | |||
dd67ee91da | |||
a5b65974fb | |||
35aae85c8d | |||
3452e2a8c2 | |||
f89de16f6e | |||
38dae51a7a | |||
d2f94c7bec | |||
fc8f22432c | |||
a8392ef91e | |||
610f8976fc | |||
d6fc29a2b8 | |||
4af1a39ca4 | |||
0f18e59d67 | |||
b3588b5e6c | |||
03250cef00 | |||
0c17f03489 | |||
df3628499d | |||
f220b865ff | |||
a1084ec785 | |||
2328abe8db | |||
b12a891fd7 |
278 changed files with 18852 additions and 8803 deletions
18
.dockerignore
Normal file
18
.dockerignore
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
**/*.pyc
|
||||||
|
**/.*.swp
|
||||||
|
**/.coverage
|
||||||
|
**/__pycache__
|
||||||
|
.dockerignore
|
||||||
|
.env
|
||||||
|
.envrc
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
.idea
|
||||||
|
.isort.cfg
|
||||||
|
.vagrant
|
||||||
|
Dockerfile
|
||||||
|
Vagrantfile
|
||||||
|
docker-compose.yml
|
||||||
|
docs
|
||||||
|
media
|
||||||
|
static
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -50,9 +50,11 @@ coverage-report/
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
.env
|
.env
|
||||||
|
.envrc
|
||||||
|
|
||||||
/docker/django_media
|
/docker/django_media
|
||||||
/docker/django_static
|
/docker/django_static
|
||||||
!/docker/django_media/.empty
|
!/docker/django_media/.empty
|
||||||
!/docker/django_static/.empty
|
!/docker/django_static/.empty
|
||||||
|
/media/
|
||||||
/static/
|
/static/
|
||||||
|
|
7
.isort.cfg
Normal file
7
.isort.cfg
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[tool.isort]
|
||||||
|
multi_line_output = 3
|
||||||
|
include_trailing_comma = True
|
||||||
|
force_grid_wrap = 0
|
||||||
|
use_parentheses = True
|
||||||
|
ensure_newline_before_comments = True
|
||||||
|
line_length = 88
|
84
Dockerfile
84
Dockerfile
|
@ -1,56 +1,70 @@
|
||||||
ARG DEBIAN_RELEASE=buster
|
ARG DEBIAN_RELEASE=bookworm
|
||||||
|
FROM debian:$DEBIAN_RELEASE AS builder
|
||||||
|
|
||||||
|
ARG GVAAPP=gva
|
||||||
|
ARG POETRY_VERSION=1.7.1
|
||||||
|
|
||||||
|
ENV LC_ALL=C.UTF-8
|
||||||
|
ENV LANG=C.UTF-8
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
build-essential \
|
||||||
|
curl \
|
||||||
|
git \
|
||||||
|
libpq-dev \
|
||||||
|
python3-dev \
|
||||||
|
python3-setuptools \
|
||||||
|
python3-virtualenv \
|
||||||
|
python3-wheel
|
||||||
|
|
||||||
|
RUN curl -sSL https://install.python-poetry.org | POETRY_HOME=/root/.local POETRY_VERSION=$POETRY_VERSION python3 - \
|
||||||
|
&& /root/.local/bin/poetry config virtualenvs.in-project true
|
||||||
|
|
||||||
|
WORKDIR /srv/$GVAAPP
|
||||||
|
|
||||||
|
COPY poetry.lock pyproject.toml /srv/$GVAAPP/
|
||||||
|
|
||||||
|
RUN /root/.local/bin/poetry install --only=main --no-root
|
||||||
|
|
||||||
FROM debian:$DEBIAN_RELEASE
|
FROM debian:$DEBIAN_RELEASE
|
||||||
LABEL maintainer="Jan Dittberner <jan@dittberner.info>"
|
LABEL maintainer="Jan Dittberner <jan@dittberner.info>"
|
||||||
|
|
||||||
ENV LC_ALL=C.UTF-8
|
ENV LC_ALL=C.UTF-8
|
||||||
ENV LANG=C.UTF-8
|
ENV LANG=C.UTF-8
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
&& apt-get install -y --no-install-recommends \
|
||||||
build-essential \
|
ca-certificates \
|
||||||
dumb-init \
|
dumb-init \
|
||||||
gettext \
|
gettext \
|
||||||
git \
|
postgresql-client \
|
||||||
python3-dev \
|
python3 \
|
||||||
python3-pip \
|
python3-pip \
|
||||||
python3-setuptools \
|
python3-wheel \
|
||||||
python3-virtualenv \
|
|
||||||
python3-wheel \
|
|
||||||
&& apt-get clean \
|
&& apt-get clean \
|
||||||
&& rm -rf /var/lib/apt/lists/*.*
|
&& rm -rf /var/cache/apt/archives /var/lib/apt/lists/*
|
||||||
|
|
||||||
RUN python3 -m pip install --prefix=/usr/local pipenv
|
|
||||||
|
|
||||||
RUN apt-get update \
|
|
||||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
|
||||||
libpq-dev \
|
|
||||||
postgresql-client \
|
|
||||||
&& apt-get clean \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*.*
|
|
||||||
|
|
||||||
|
ARG GVAAPP=gva
|
||||||
ARG GVAGID=2000
|
ARG GVAGID=2000
|
||||||
ARG GVAUID=2000
|
ARG GVAUID=2000
|
||||||
|
|
||||||
ARG GVAAPP=gva
|
RUN addgroup --gid $GVAGID $GVAAPP ; \
|
||||||
|
adduser --home /home/$GVAAPP --shell /bin/bash --uid $GVAUID --gid $GVAGID --disabled-password \
|
||||||
|
--gecos "User for gnuviechadmin component $GVAAPP" $GVAAPP
|
||||||
|
|
||||||
VOLUME /srv/$GVAAPP/media /srv/$GVAAPP/static
|
COPY --chown=$GVAAPP:$GVAAPP --from=builder /srv/$GVAAPP/.venv /srv/$GVAAPP/.venv
|
||||||
|
|
||||||
WORKDIR /srv/$GVAAPP
|
WORKDIR /srv/$GVAAPP
|
||||||
|
|
||||||
COPY Pipfile Pipfile.lock /srv/$GVAAPP/
|
VOLUME /srv/$GVAAPP/media /srv/$GVAAPP/static
|
||||||
|
|
||||||
RUN addgroup --gid $GVAGID $GVAAPP ; \
|
VOLUME /srv/$GVAAPP/gnuviechadmin
|
||||||
adduser --home /home/$GVAAPP --shell /bin/bash --uid $GVAUID --gid $GVAGID --disabled-password --gecos "User for gnuviechadmin component $GVAAPP" $GVAAPP
|
|
||||||
|
|
||||||
USER $GVAAPP
|
|
||||||
RUN python3 -m virtualenv --python=python3 /home/$GVAAPP/$GVAAPP-venv ; \
|
|
||||||
/home/$GVAAPP/$GVAAPP-venv/bin/python3 -m pip install -U pip ; \
|
|
||||||
VIRTUAL_ENV=/home/$GVAAPP/$GVAAPP-venv pipenv install --deploy --ignore-pipfile --dev
|
|
||||||
|
|
||||||
VOLUME /srv/$GVAAPP
|
|
||||||
|
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
|
|
||||||
COPY gva.sh /srv/
|
COPY ${GVAAPP}.sh entrypoint.sh /srv/
|
||||||
|
|
||||||
ENTRYPOINT ["dumb-init", "/srv/gva.sh"]
|
ENTRYPOINT ["dumb-init", "/srv/entrypoint.sh"]
|
||||||
|
|
33
Pipfile
33
Pipfile
|
@ -1,33 +0,0 @@
|
||||||
[[source]]
|
|
||||||
url = "https://pypi.org/simple"
|
|
||||||
verify_ssl = true
|
|
||||||
name = "pypi"
|
|
||||||
|
|
||||||
[[source]]
|
|
||||||
url = "https://pypi.gnuviech-server.de/simple"
|
|
||||||
verify_ssl = true
|
|
||||||
name = "gnuviech"
|
|
||||||
|
|
||||||
[packages]
|
|
||||||
"psycopg2" = "*"
|
|
||||||
Django = "<3"
|
|
||||||
celery = "*"
|
|
||||||
django-allauth = "*"
|
|
||||||
django-braces = "*"
|
|
||||||
django-crispy-forms = "*"
|
|
||||||
django-model-utils = "*"
|
|
||||||
gvacommon = {version = "*",index = "gnuviech"}
|
|
||||||
passlib = "*"
|
|
||||||
redis = "*"
|
|
||||||
requests-oauthlib = "*"
|
|
||||||
|
|
||||||
[dev-packages]
|
|
||||||
coverage = "*"
|
|
||||||
django-debug-toolbar = "*"
|
|
||||||
sphinx = "*"
|
|
||||||
releases = "*"
|
|
||||||
sphinxcontrib-blockdiag = "*"
|
|
||||||
pylama = "*"
|
|
||||||
|
|
||||||
[requires]
|
|
||||||
python_version = "3.7"
|
|
617
Pipfile.lock
generated
617
Pipfile.lock
generated
|
@ -1,617 +0,0 @@
|
||||||
{
|
|
||||||
"_meta": {
|
|
||||||
"hash": {
|
|
||||||
"sha256": "1c0b7bdab385f10279c852fa7fe7ae2c022dc1c4495d0e55fd407aea947bc976"
|
|
||||||
},
|
|
||||||
"pipfile-spec": 6,
|
|
||||||
"requires": {
|
|
||||||
"python_version": "3.7"
|
|
||||||
},
|
|
||||||
"sources": [
|
|
||||||
{
|
|
||||||
"name": "pypi",
|
|
||||||
"url": "https://pypi.org/simple",
|
|
||||||
"verify_ssl": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "gnuviech",
|
|
||||||
"url": "https://pypi.gnuviech-server.de/simple",
|
|
||||||
"verify_ssl": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"default": {
|
|
||||||
"amqp": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:6e649ca13a7df3faacdc8bbb280aa9a6602d22fd9d545336077e573a1f4ff3b8",
|
|
||||||
"sha256:77f1aef9410698d20eaeac5b73a87817365f457a507d82edf292e12cbb83b08d"
|
|
||||||
],
|
|
||||||
"version": "==2.5.2"
|
|
||||||
},
|
|
||||||
"billiard": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:bff575450859a6e0fbc2f9877d9b715b0bbc07c3565bb7ed2280526a0cdf5ede",
|
|
||||||
"sha256:d91725ce6425f33a97dfa72fb6bfef0e47d4652acd98a032bd1a7fbf06d5fa6a"
|
|
||||||
],
|
|
||||||
"version": "==3.6.3.0"
|
|
||||||
},
|
|
||||||
"celery": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:108a0bf9018a871620936c33a3ee9f6336a89f8ef0a0f567a9001f4aa361415f",
|
|
||||||
"sha256:5b4b37e276033fe47575107a2775469f0b721646a08c96ec2c61531e4fe45f2a"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==4.4.2"
|
|
||||||
},
|
|
||||||
"certifi": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304",
|
|
||||||
"sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519"
|
|
||||||
],
|
|
||||||
"version": "==2020.4.5.1"
|
|
||||||
},
|
|
||||||
"chardet": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
|
|
||||||
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
|
|
||||||
],
|
|
||||||
"version": "==3.0.4"
|
|
||||||
},
|
|
||||||
"defusedxml": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93",
|
|
||||||
"sha256:f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"
|
|
||||||
],
|
|
||||||
"version": "==0.6.0"
|
|
||||||
},
|
|
||||||
"django": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:69897097095f336d5aeef45b4103dceae51c00afa6d3ae198a2a18e519791b7a",
|
|
||||||
"sha256:6ecd229e1815d4fc5240fc98f1cca78c41e7a8cd3e3f2eefadc4735031077916"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==2.2.12"
|
|
||||||
},
|
|
||||||
"django-allauth": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:7ab91485b80d231da191d5c7999ba93170ef1bf14ab6487d886598a1ad03e1d8"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==0.41.0"
|
|
||||||
},
|
|
||||||
"django-braces": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:83705b78948de00804bfacf40c315d001bb39630f35bbdd8588211c2d5b4d43f",
|
|
||||||
"sha256:a6d9b34cf3e4949635e54884097c30410d7964fc7bec7231445ea7079b8c5722"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==1.14.0"
|
|
||||||
},
|
|
||||||
"django-crispy-forms": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:50032184708ce351e3c9f0008ac35d659d9d5973fa2db218066f2e0a76eb41d9",
|
|
||||||
"sha256:67e73ac863d3159500029fbbcdcb788f287a3fd357becebc1a0b51f73896dce3"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==1.9.0"
|
|
||||||
},
|
|
||||||
"django-model-utils": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:9cf882e5b604421b62dbe57ad2b18464dc9c8f963fc3f9831badccae66c1139c",
|
|
||||||
"sha256:adf09e5be15122a7f4e372cb5a6dd512bbf8d78a23a90770ad0983ee9d909061"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==4.0.0"
|
|
||||||
},
|
|
||||||
"gvacommon": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:adf1ebc824433196d112764c61d9ca869481d33f612818c2840069f57ab42c25"
|
|
||||||
],
|
|
||||||
"index": "gnuviech",
|
|
||||||
"version": "==0.5.0"
|
|
||||||
},
|
|
||||||
"idna": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb",
|
|
||||||
"sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"
|
|
||||||
],
|
|
||||||
"version": "==2.9"
|
|
||||||
},
|
|
||||||
"importlib-metadata": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f",
|
|
||||||
"sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e"
|
|
||||||
],
|
|
||||||
"markers": "python_version < '3.8'",
|
|
||||||
"version": "==1.6.0"
|
|
||||||
},
|
|
||||||
"kombu": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:2d1cda774126a044d91a7ff5fa6d09edf99f46924ab332a810760fe6740e9b76",
|
|
||||||
"sha256:598e7e749d6ab54f646b74b2d2df67755dee13894f73ab02a2a9feb8870c7cb2"
|
|
||||||
],
|
|
||||||
"version": "==4.6.8"
|
|
||||||
},
|
|
||||||
"oauthlib": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889",
|
|
||||||
"sha256:df884cd6cbe20e32633f1db1072e9356f53638e4361bef4e8b03c9127c9328ea"
|
|
||||||
],
|
|
||||||
"version": "==3.1.0"
|
|
||||||
},
|
|
||||||
"passlib": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:68c35c98a7968850e17f1b6892720764cc7eed0ef2b7cb3116a89a28e43fe177",
|
|
||||||
"sha256:8d666cef936198bc2ab47ee9b0410c94adf2ba798e5a84bf220be079ae7ab6a8"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==1.7.2"
|
|
||||||
},
|
|
||||||
"psycopg2": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:132efc7ee46a763e68a815f4d26223d9c679953cd190f1f218187cb60decf535",
|
|
||||||
"sha256:2327bf42c1744a434ed8ed0bbaa9168cac7ee5a22a9001f6fc85c33b8a4a14b7",
|
|
||||||
"sha256:27c633f2d5db0fc27b51f1b08f410715b59fa3802987aec91aeb8f562724e95c",
|
|
||||||
"sha256:2c0afb40cfb4d53487ee2ebe128649028c9a78d2476d14a67781e45dc287f080",
|
|
||||||
"sha256:2df2bf1b87305bd95eb3ac666ee1f00a9c83d10927b8144e8e39644218f4cf81",
|
|
||||||
"sha256:440a3ea2c955e89321a138eb7582aa1d22fe286c7d65e26a2c5411af0a88ae72",
|
|
||||||
"sha256:6a471d4d2a6f14c97a882e8d3124869bc623f3df6177eefe02994ea41fd45b52",
|
|
||||||
"sha256:6b306dae53ec7f4f67a10942cf8ac85de930ea90e9903e2df4001f69b7833f7e",
|
|
||||||
"sha256:a0984ff49e176062fcdc8a5a2a670c9bb1704a2f69548bce8f8a7bad41c661bf",
|
|
||||||
"sha256:ac5b23d0199c012ad91ed1bbb971b7666da651c6371529b1be8cbe2a7bf3c3a9",
|
|
||||||
"sha256:acf56d564e443e3dea152efe972b1434058244298a94348fc518d6dd6a9fb0bb",
|
|
||||||
"sha256:d3b29d717d39d3580efd760a9a46a7418408acebbb784717c90d708c9ed5f055",
|
|
||||||
"sha256:f7d46240f7a1ae1dd95aab38bd74f7428d46531f69219954266d669da60c0818"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==2.8.5"
|
|
||||||
},
|
|
||||||
"python3-openid": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:0086da6b6ef3161cfe50fb1ee5cceaf2cda1700019fda03c2c5c440ca6abe4fa",
|
|
||||||
"sha256:628d365d687e12da12d02c6691170f4451db28d6d68d050007e4a40065868502"
|
|
||||||
],
|
|
||||||
"version": "==3.1.0"
|
|
||||||
},
|
|
||||||
"pytz": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d",
|
|
||||||
"sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"
|
|
||||||
],
|
|
||||||
"version": "==2019.3"
|
|
||||||
},
|
|
||||||
"redis": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:0dcfb335921b88a850d461dc255ff4708294943322bd55de6cfd68972490ca1f",
|
|
||||||
"sha256:b205cffd05ebfd0a468db74f0eedbff8df1a7bfc47521516ade4692991bb0833"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==3.4.1"
|
|
||||||
},
|
|
||||||
"requests": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee",
|
|
||||||
"sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"
|
|
||||||
],
|
|
||||||
"version": "==2.23.0"
|
|
||||||
},
|
|
||||||
"requests-oauthlib": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d",
|
|
||||||
"sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==1.3.0"
|
|
||||||
},
|
|
||||||
"six": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
|
|
||||||
"sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
|
|
||||||
],
|
|
||||||
"version": "==1.14.0"
|
|
||||||
},
|
|
||||||
"sqlparse": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:022fb9c87b524d1f7862b3037e541f68597a730a8843245c349fc93e1643dc4e",
|
|
||||||
"sha256:e162203737712307dfe78860cc56c8da8a852ab2ee33750e33aeadf38d12c548"
|
|
||||||
],
|
|
||||||
"version": "==0.3.1"
|
|
||||||
},
|
|
||||||
"urllib3": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc",
|
|
||||||
"sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"
|
|
||||||
],
|
|
||||||
"version": "==1.25.8"
|
|
||||||
},
|
|
||||||
"vine": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:133ee6d7a9016f177ddeaf191c1f58421a1dcc6ee9a42c58b34bed40e1d2cd87",
|
|
||||||
"sha256:ea4947cc56d1fd6f2095c8d543ee25dad966f78692528e68b4fada11ba3f98af"
|
|
||||||
],
|
|
||||||
"version": "==1.3.0"
|
|
||||||
},
|
|
||||||
"zipp": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b",
|
|
||||||
"sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"
|
|
||||||
],
|
|
||||||
"version": "==3.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"develop": {
|
|
||||||
"alabaster": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359",
|
|
||||||
"sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"
|
|
||||||
],
|
|
||||||
"version": "==0.7.12"
|
|
||||||
},
|
|
||||||
"asgiref": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:8036f90603c54e93521e5777b2b9a39ba1bad05773fcf2d208f0299d1df58ce5",
|
|
||||||
"sha256:9ca8b952a0a9afa61d30aa6d3d9b570bb3fd6bafcf7ec9e6bed43b936133db1c"
|
|
||||||
],
|
|
||||||
"version": "==3.2.7"
|
|
||||||
},
|
|
||||||
"babel": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:1aac2ae2d0d8ea368fa90906567f5c08463d98ade155c0c4bfedd6a0f7160e38",
|
|
||||||
"sha256:d670ea0b10f8b723672d3a6abeb87b565b244da220d76b4dba1b66269ec152d4"
|
|
||||||
],
|
|
||||||
"version": "==2.8.0"
|
|
||||||
},
|
|
||||||
"blockdiag": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:16a69dd9f3b44c9e0869999ce82aa968586698febc86ece9ca0c902dba772397",
|
|
||||||
"sha256:fa0b47cf25bfc4d546b7fc284c70c3bac875a066e744b4a6b1d9ba457e4ed077"
|
|
||||||
],
|
|
||||||
"version": "==2.0.1"
|
|
||||||
},
|
|
||||||
"certifi": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304",
|
|
||||||
"sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519"
|
|
||||||
],
|
|
||||||
"version": "==2020.4.5.1"
|
|
||||||
},
|
|
||||||
"chardet": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
|
|
||||||
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
|
|
||||||
],
|
|
||||||
"version": "==3.0.4"
|
|
||||||
},
|
|
||||||
"coverage": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:03f630aba2b9b0d69871c2e8d23a69b7fe94a1e2f5f10df5049c0df99db639a0",
|
|
||||||
"sha256:046a1a742e66d065d16fb564a26c2a15867f17695e7f3d358d7b1ad8a61bca30",
|
|
||||||
"sha256:0a907199566269e1cfa304325cc3b45c72ae341fbb3253ddde19fa820ded7a8b",
|
|
||||||
"sha256:165a48268bfb5a77e2d9dbb80de7ea917332a79c7adb747bd005b3a07ff8caf0",
|
|
||||||
"sha256:1b60a95fc995649464e0cd48cecc8288bac5f4198f21d04b8229dc4097d76823",
|
|
||||||
"sha256:1f66cf263ec77af5b8fe14ef14c5e46e2eb4a795ac495ad7c03adc72ae43fafe",
|
|
||||||
"sha256:2e08c32cbede4a29e2a701822291ae2bc9b5220a971bba9d1e7615312efd3037",
|
|
||||||
"sha256:3844c3dab800ca8536f75ae89f3cf566848a3eb2af4d9f7b1103b4f4f7a5dad6",
|
|
||||||
"sha256:408ce64078398b2ee2ec08199ea3fcf382828d2f8a19c5a5ba2946fe5ddc6c31",
|
|
||||||
"sha256:443be7602c790960b9514567917af538cac7807a7c0c0727c4d2bbd4014920fd",
|
|
||||||
"sha256:4482f69e0701139d0f2c44f3c395d1d1d37abd81bfafbf9b6efbe2542679d892",
|
|
||||||
"sha256:4a8a259bf990044351baf69d3b23e575699dd60b18460c71e81dc565f5819ac1",
|
|
||||||
"sha256:513e6526e0082c59a984448f4104c9bf346c2da9961779ede1fc458e8e8a1f78",
|
|
||||||
"sha256:5f587dfd83cb669933186661a351ad6fc7166273bc3e3a1531ec5c783d997aac",
|
|
||||||
"sha256:62061e87071497951155cbccee487980524d7abea647a1b2a6eb6b9647df9006",
|
|
||||||
"sha256:641e329e7f2c01531c45c687efcec8aeca2a78a4ff26d49184dce3d53fc35014",
|
|
||||||
"sha256:65a7e00c00472cd0f59ae09d2fb8a8aaae7f4a0cf54b2b74f3138d9f9ceb9cb2",
|
|
||||||
"sha256:6ad6ca45e9e92c05295f638e78cd42bfaaf8ee07878c9ed73e93190b26c125f7",
|
|
||||||
"sha256:73aa6e86034dad9f00f4bbf5a666a889d17d79db73bc5af04abd6c20a014d9c8",
|
|
||||||
"sha256:7c9762f80a25d8d0e4ab3cb1af5d9dffbddb3ee5d21c43e3474c84bf5ff941f7",
|
|
||||||
"sha256:85596aa5d9aac1bf39fe39d9fa1051b0f00823982a1de5766e35d495b4a36ca9",
|
|
||||||
"sha256:86a0ea78fd851b313b2e712266f663e13b6bc78c2fb260b079e8b67d970474b1",
|
|
||||||
"sha256:8a620767b8209f3446197c0e29ba895d75a1e272a36af0786ec70fe7834e4307",
|
|
||||||
"sha256:922fb9ef2c67c3ab20e22948dcfd783397e4c043a5c5fa5ff5e9df5529074b0a",
|
|
||||||
"sha256:9fad78c13e71546a76c2f8789623eec8e499f8d2d799f4b4547162ce0a4df435",
|
|
||||||
"sha256:a37c6233b28e5bc340054cf6170e7090a4e85069513320275a4dc929144dccf0",
|
|
||||||
"sha256:c3fc325ce4cbf902d05a80daa47b645d07e796a80682c1c5800d6ac5045193e5",
|
|
||||||
"sha256:cda33311cb9fb9323958a69499a667bd728a39a7aa4718d7622597a44c4f1441",
|
|
||||||
"sha256:db1d4e38c9b15be1521722e946ee24f6db95b189d1447fa9ff18dd16ba89f732",
|
|
||||||
"sha256:eda55e6e9ea258f5e4add23bcf33dc53b2c319e70806e180aecbff8d90ea24de",
|
|
||||||
"sha256:f372cdbb240e09ee855735b9d85e7f50730dcfb6296b74b95a3e5dea0615c4c1"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==5.0.4"
|
|
||||||
},
|
|
||||||
"django": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:69897097095f336d5aeef45b4103dceae51c00afa6d3ae198a2a18e519791b7a",
|
|
||||||
"sha256:6ecd229e1815d4fc5240fc98f1cca78c41e7a8cd3e3f2eefadc4735031077916"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==2.2.12"
|
|
||||||
},
|
|
||||||
"django-debug-toolbar": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:eabbefe89881bbe4ca7c980ff102e3c35c8e8ad6eb725041f538988f2f39a943",
|
|
||||||
"sha256:ff94725e7aae74b133d0599b9bf89bd4eb8f5d2c964106e61d11750228c8774c"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==2.2"
|
|
||||||
},
|
|
||||||
"docutils": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af",
|
|
||||||
"sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"
|
|
||||||
],
|
|
||||||
"version": "==0.16"
|
|
||||||
},
|
|
||||||
"funcparserlib": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:b7992eac1a3eb97b3d91faa342bfda0729e990bd8a43774c1592c091e563c91d"
|
|
||||||
],
|
|
||||||
"version": "==0.3.6"
|
|
||||||
},
|
|
||||||
"idna": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb",
|
|
||||||
"sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"
|
|
||||||
],
|
|
||||||
"version": "==2.9"
|
|
||||||
},
|
|
||||||
"imagesize": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1",
|
|
||||||
"sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"
|
|
||||||
],
|
|
||||||
"version": "==1.2.0"
|
|
||||||
},
|
|
||||||
"jinja2": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:93187ffbc7808079673ef52771baa950426fd664d3aad1d0fa3e95644360e250",
|
|
||||||
"sha256:b0eaf100007721b5c16c1fc1eecb87409464edc10469ddc9a22a27a99123be49"
|
|
||||||
],
|
|
||||||
"version": "==2.11.1"
|
|
||||||
},
|
|
||||||
"markupsafe": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
|
|
||||||
"sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
|
|
||||||
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
|
|
||||||
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
|
|
||||||
"sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42",
|
|
||||||
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
|
|
||||||
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
|
|
||||||
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
|
|
||||||
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
|
|
||||||
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
|
|
||||||
"sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
|
|
||||||
"sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b",
|
|
||||||
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
|
|
||||||
"sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15",
|
|
||||||
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
|
|
||||||
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
|
|
||||||
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
|
|
||||||
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
|
|
||||||
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
|
|
||||||
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
|
|
||||||
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
|
|
||||||
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
|
|
||||||
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
|
|
||||||
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
|
|
||||||
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
|
|
||||||
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
|
|
||||||
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
|
|
||||||
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
|
|
||||||
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
|
|
||||||
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
|
|
||||||
"sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2",
|
|
||||||
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7",
|
|
||||||
"sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"
|
|
||||||
],
|
|
||||||
"version": "==1.1.1"
|
|
||||||
},
|
|
||||||
"mccabe": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
|
|
||||||
"sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
|
|
||||||
],
|
|
||||||
"version": "==0.6.1"
|
|
||||||
},
|
|
||||||
"packaging": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:3c292b474fda1671ec57d46d739d072bfd495a4f51ad01a055121d81e952b7a3",
|
|
||||||
"sha256:82f77b9bee21c1bafbf35a84905d604d5d1223801d639cf3ed140bd651c08752"
|
|
||||||
],
|
|
||||||
"version": "==20.3"
|
|
||||||
},
|
|
||||||
"pillow": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:04a10558320eba9137d6a78ca6fc8f4a5801f1b971152938851dc4629d903579",
|
|
||||||
"sha256:0f89ddc77cf421b8cd34ae852309501458942bf370831b4a9b406156b599a14e",
|
|
||||||
"sha256:251e5618125ec12ac800265d7048f5857a8f8f1979db9ea3e11382e159d17f68",
|
|
||||||
"sha256:291bad7097b06d648222b769bbfcd61e40d0abdfe10df686d20ede36eb8162b6",
|
|
||||||
"sha256:2f0b52a08d175f10c8ea36685115681a484c55d24d0933f9fd911e4111c04144",
|
|
||||||
"sha256:3713386d1e9e79cea1c5e6aaac042841d7eef838cc577a3ca153c8bedf570287",
|
|
||||||
"sha256:433bbc2469a2351bea53666d97bb1eb30f0d56461735be02ea6b27654569f80f",
|
|
||||||
"sha256:4510c6b33277970b1af83c987277f9a08ec2b02cc20ac0f9234e4026136bb137",
|
|
||||||
"sha256:50a10b048f4dd81c092adad99fa5f7ba941edaf2f9590510109ac2a15e706695",
|
|
||||||
"sha256:670e58d3643971f4afd79191abd21623761c2ebe61db1c2cb4797d817c4ba1a7",
|
|
||||||
"sha256:6c1924ed7dbc6ad0636907693bbbdd3fdae1d73072963e71f5644b864bb10b4d",
|
|
||||||
"sha256:721c04d3c77c38086f1f95d1cd8df87f2f9a505a780acf8575912b3206479da1",
|
|
||||||
"sha256:8d5799243050c2833c2662b824dfb16aa98e408d2092805edea4300a408490e7",
|
|
||||||
"sha256:90cd441a1638ae176eab4d8b6b94ab4ec24b212ed4c3fbee2a6e74672481d4f8",
|
|
||||||
"sha256:a5dc9f28c0239ec2742d4273bd85b2aa84655be2564db7ad1eb8f64b1efcdc4c",
|
|
||||||
"sha256:b2f3e8cc52ecd259b94ca880fea0d15f4ebc6da2cd3db515389bb878d800270f",
|
|
||||||
"sha256:b7453750cf911785009423789d2e4e5393aae9cbb8b3f471dab854b85a26cb89",
|
|
||||||
"sha256:b99b2607b6cd58396f363b448cbe71d3c35e28f03e442ab00806463439629c2c",
|
|
||||||
"sha256:cd47793f7bc9285a88c2b5551d3f16a2ddd005789614a34c5f4a598c2a162383",
|
|
||||||
"sha256:d6bf085f6f9ec6a1724c187083b37b58a8048f86036d42d21802ed5d1fae4853",
|
|
||||||
"sha256:da737ab273f4d60ae552f82ad83f7cbd0e173ca30ca20b160f708c92742ee212",
|
|
||||||
"sha256:eb84e7e5b07ff3725ab05977ac56d5eeb0c510795aeb48e8b691491be3c5745b"
|
|
||||||
],
|
|
||||||
"version": "==7.1.1"
|
|
||||||
},
|
|
||||||
"pycodestyle": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56",
|
|
||||||
"sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"
|
|
||||||
],
|
|
||||||
"version": "==2.5.0"
|
|
||||||
},
|
|
||||||
"pydocstyle": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:da7831660b7355307b32778c4a0dbfb137d89254ef31a2b2978f50fc0b4d7586",
|
|
||||||
"sha256:f4f5d210610c2d153fae39093d44224c17429e2ad7da12a8b419aba5c2f614b5"
|
|
||||||
],
|
|
||||||
"version": "==5.0.2"
|
|
||||||
},
|
|
||||||
"pyflakes": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92",
|
|
||||||
"sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"
|
|
||||||
],
|
|
||||||
"version": "==2.2.0"
|
|
||||||
},
|
|
||||||
"pygments": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44",
|
|
||||||
"sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324"
|
|
||||||
],
|
|
||||||
"version": "==2.6.1"
|
|
||||||
},
|
|
||||||
"pylama": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:9bae53ef9c1a431371d6a8dca406816a60d547147b60a4934721898f553b7d8f",
|
|
||||||
"sha256:fd61c11872d6256b019ef1235be37b77c922ef37ac9797df6bd489996dddeb15"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==7.7.1"
|
|
||||||
},
|
|
||||||
"pyparsing": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
|
|
||||||
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
|
|
||||||
],
|
|
||||||
"version": "==2.4.7"
|
|
||||||
},
|
|
||||||
"pytz": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d",
|
|
||||||
"sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"
|
|
||||||
],
|
|
||||||
"version": "==2019.3"
|
|
||||||
},
|
|
||||||
"releases": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:555ae4c97a671a420281c1c782e9236be25157b449fdf20b4c4b293fe93db2f1",
|
|
||||||
"sha256:cb3435ba372a6807433800fbe473760cfa781171513f670f3c4b76983ac80f18"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==1.6.3"
|
|
||||||
},
|
|
||||||
"requests": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee",
|
|
||||||
"sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"
|
|
||||||
],
|
|
||||||
"version": "==2.23.0"
|
|
||||||
},
|
|
||||||
"semantic-version": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:2a4328680073e9b243667b201119772aefc5fc63ae32398d6afafff07c4f54c0",
|
|
||||||
"sha256:2d06ab7372034bcb8b54f2205370f4aa0643c133b7e6dbd129c5200b83ab394b"
|
|
||||||
],
|
|
||||||
"version": "==2.6.0"
|
|
||||||
},
|
|
||||||
"six": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
|
|
||||||
"sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
|
|
||||||
],
|
|
||||||
"version": "==1.14.0"
|
|
||||||
},
|
|
||||||
"snowballstemmer": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0",
|
|
||||||
"sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52"
|
|
||||||
],
|
|
||||||
"version": "==2.0.0"
|
|
||||||
},
|
|
||||||
"sphinx": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:6a099e6faffdc3ceba99ca8c2d09982d43022245e409249375edf111caf79ed3",
|
|
||||||
"sha256:b63a0c879c4ff9a4dffcb05217fa55672ce07abdeb81e33c73303a563f8d8901"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==3.0.0"
|
|
||||||
},
|
|
||||||
"sphinxcontrib-applehelp": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a",
|
|
||||||
"sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"
|
|
||||||
],
|
|
||||||
"version": "==1.0.2"
|
|
||||||
},
|
|
||||||
"sphinxcontrib-blockdiag": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:51ce7cff8d25dfd4c8a753d5aa5491e6dbf280004719c49e8001e583ecda7d91",
|
|
||||||
"sha256:91fd35b64f1f25db59d80b8a5196ed4ffadf57a81f63ee207e34d53ec36d8f97"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==2.0.0"
|
|
||||||
},
|
|
||||||
"sphinxcontrib-devhelp": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e",
|
|
||||||
"sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"
|
|
||||||
],
|
|
||||||
"version": "==1.0.2"
|
|
||||||
},
|
|
||||||
"sphinxcontrib-htmlhelp": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f",
|
|
||||||
"sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b"
|
|
||||||
],
|
|
||||||
"version": "==1.0.3"
|
|
||||||
},
|
|
||||||
"sphinxcontrib-jsmath": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178",
|
|
||||||
"sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"
|
|
||||||
],
|
|
||||||
"version": "==1.0.1"
|
|
||||||
},
|
|
||||||
"sphinxcontrib-qthelp": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72",
|
|
||||||
"sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"
|
|
||||||
],
|
|
||||||
"version": "==1.0.3"
|
|
||||||
},
|
|
||||||
"sphinxcontrib-serializinghtml": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc",
|
|
||||||
"sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a"
|
|
||||||
],
|
|
||||||
"version": "==1.1.4"
|
|
||||||
},
|
|
||||||
"sqlparse": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:022fb9c87b524d1f7862b3037e541f68597a730a8843245c349fc93e1643dc4e",
|
|
||||||
"sha256:e162203737712307dfe78860cc56c8da8a852ab2ee33750e33aeadf38d12c548"
|
|
||||||
],
|
|
||||||
"version": "==0.3.1"
|
|
||||||
},
|
|
||||||
"urllib3": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc",
|
|
||||||
"sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"
|
|
||||||
],
|
|
||||||
"version": "==1.25.8"
|
|
||||||
},
|
|
||||||
"webcolors": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:76f360636957d1c976db7466bc71dcb713bb95ac8911944dffc55c01cb516de6",
|
|
||||||
"sha256:b8cd5d865a25c51ff1218f0c90d0c0781fc64312a49b746b320cf50de1648f6e"
|
|
||||||
],
|
|
||||||
"version": "==1.11.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
---
|
||||||
version: "3"
|
version: "3"
|
||||||
services:
|
services:
|
||||||
db:
|
db:
|
||||||
|
@ -18,7 +19,7 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- "redis_data:/var/lib/redis"
|
- "redis_data:/var/lib/redis"
|
||||||
gva:
|
gva:
|
||||||
image: gnuviech/gva:buster
|
image: gnuviech/gva:bookworm
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
args:
|
args:
|
||||||
|
@ -36,9 +37,9 @@ services:
|
||||||
GVA_DOMAIN_NAME: localhost
|
GVA_DOMAIN_NAME: localhost
|
||||||
GVA_SITE_NAME: localhost
|
GVA_SITE_NAME: localhost
|
||||||
volumes:
|
volumes:
|
||||||
- "./docker/django_media:/srv/gva/media"
|
- "django_media:/srv/gva/media"
|
||||||
- "./docker/django_static:/srv/gva/static"
|
- "django_static:/srv/gva/static"
|
||||||
- ".:/srv/gva"
|
- "./gnuviechadmin:/srv/gva/gnuviechadmin"
|
||||||
web:
|
web:
|
||||||
image: gnuviech/gvaweb:buster
|
image: gnuviech/gvaweb:buster
|
||||||
build:
|
build:
|
||||||
|
@ -51,7 +52,7 @@ services:
|
||||||
- redis
|
- redis
|
||||||
env_file: ../gvaweb/.env
|
env_file: ../gvaweb/.env
|
||||||
volumes:
|
volumes:
|
||||||
- "../gvaweb:/srv/gvaweb"
|
- "../gvaweb/gvaweb:/srv/gvaweb/gvaweb"
|
||||||
ldap:
|
ldap:
|
||||||
image: gnuviech/gvaldap:buster
|
image: gnuviech/gvaldap:buster
|
||||||
build:
|
build:
|
||||||
|
@ -64,9 +65,9 @@ services:
|
||||||
- redis
|
- redis
|
||||||
env_file: ../gvaldap/.env
|
env_file: ../gvaldap/.env
|
||||||
volumes:
|
volumes:
|
||||||
- "../gvaldap:/srv/gvaldap"
|
- "../gvaldap/gvaldap:/srv/gvaldap/gvaldap"
|
||||||
file:
|
file:
|
||||||
image: gnuviech/gvafile:buster
|
image: gnuviech/gvafile:bookworm
|
||||||
build:
|
build:
|
||||||
context: ../gvafile
|
context: ../gvafile
|
||||||
args:
|
args:
|
||||||
|
@ -77,7 +78,7 @@ services:
|
||||||
- redis
|
- redis
|
||||||
env_file: ../gvafile/.env
|
env_file: ../gvafile/.env
|
||||||
volumes:
|
volumes:
|
||||||
- "../gvafile:/srv/gvafile"
|
- "../gvafile/gvafile:/srv/gvafile/gvafile"
|
||||||
pgsql:
|
pgsql:
|
||||||
image: gnuviech/gvapgsql:buster
|
image: gnuviech/gvapgsql:buster
|
||||||
build:
|
build:
|
||||||
|
@ -90,7 +91,7 @@ services:
|
||||||
- redis
|
- redis
|
||||||
env_file: ../gvapgsql/.env
|
env_file: ../gvapgsql/.env
|
||||||
volumes:
|
volumes:
|
||||||
- "../gvapgsql:/srv/gvapgsql"
|
- "../gvapgsql/gvapgsql:/srv/gvapgsql/gvapgsql"
|
||||||
mysql:
|
mysql:
|
||||||
image: gnuviech/gvamysql:buster
|
image: gnuviech/gvamysql:buster
|
||||||
build:
|
build:
|
||||||
|
@ -103,7 +104,7 @@ services:
|
||||||
- redis
|
- redis
|
||||||
env_file: ../gvamysql/.env
|
env_file: ../gvamysql/.env
|
||||||
volumes:
|
volumes:
|
||||||
- "../gvamysql:/srv/gvamysql"
|
- "../gvamysql/gvamysql:/srv/gvamysql/gvamysql"
|
||||||
volumes:
|
volumes:
|
||||||
django_media:
|
django_media:
|
||||||
django_static:
|
django_static:
|
||||||
|
|
|
@ -1,6 +1,41 @@
|
||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
* :release:`0.15.1 <2023-07-23>
|
||||||
|
* :bug:`-` remove stale disk usage stats older than 30 minutes
|
||||||
|
|
||||||
|
* :release:`0.15.0 <2023-07-23>
|
||||||
|
* :feature:`10` add disk usage details for mail and web
|
||||||
|
|
||||||
|
* :release:`0.14.4 <2023-07-22>`
|
||||||
|
* :bug:`-` add customer to disk space detail view
|
||||||
|
|
||||||
|
* :release:`0.14.3 <2023-07-22>`
|
||||||
|
* :bug:`-` fix missing permission check on disk space detail view
|
||||||
|
|
||||||
|
* :release:`0.14.2 <2023-07-22>`
|
||||||
|
* :bug:`-` fix division by zero for hosting packages without disk space allocation
|
||||||
|
|
||||||
|
* :release:`0.14.1 <2023-07-22>`
|
||||||
|
* :bug:`-` fix squashed migration for disk space statistics
|
||||||
|
|
||||||
|
* :release:`0.14.0 <2023-07-22>`
|
||||||
|
* :feature:`-` add disk space statistics
|
||||||
|
|
||||||
|
* :release:`0.13.0 <2023-05-08>`
|
||||||
|
* :feature:`-` add REST API to retrieve and set user information as admin
|
||||||
|
* :feature:`-` add support model for offline account reset codes in new help
|
||||||
|
app
|
||||||
|
* :support:`-` remove unused PowerDNS support tables from domains app
|
||||||
|
* :feature:`-` add impersonation support for superusers
|
||||||
|
* :support:`-` remove django-braces dependency
|
||||||
|
* :support:`-` remove Twitter support
|
||||||
|
* :support:`-` update dependencies
|
||||||
|
|
||||||
|
* :release:`0.12.1 <2020-04-13>`
|
||||||
|
* :bug:`7` fix handling of undefined mail domains in customer hosting package
|
||||||
|
detail template
|
||||||
|
|
||||||
* :release:`0.12.0 <2020-04-10>`
|
* :release:`0.12.0 <2020-04-10>`
|
||||||
* :support:`-` add architecture diagramm for documentation
|
* :support:`-` add architecture diagramm for documentation
|
||||||
* :support:`-` drop environment specific settings
|
* :support:`-` drop environment specific settings
|
||||||
|
|
143
docs/conf.py
143
docs/conf.py
|
@ -20,46 +20,49 @@ import django
|
||||||
# If extensions (or modules to document with autodoc) are in another directory,
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
# add these directories to sys.path here. If the directory is relative to the
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
sys.path.insert(0, os.path.abspath(os.path.join('..', 'gnuviechadmin')))
|
sys.path.insert(0, os.path.abspath(os.path.join("..", "gnuviechadmin")))
|
||||||
|
|
||||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'gnuviechadmin.settings'
|
os.environ["DJANGO_SETTINGS_MODULE"] = "gnuviechadmin.settings"
|
||||||
os.environ['GVA_SITE_ADMINMAIL'] = 'admin@gva.example.org'
|
os.environ["GVA_SITE_ADMINMAIL"] = "admin@gva.example.org"
|
||||||
|
|
||||||
django.setup()
|
django.setup()
|
||||||
|
|
||||||
# -- General configuration -----------------------------------------------------
|
# -- General configuration -----------------------------------------------------
|
||||||
|
|
||||||
# If your documentation needs a minimal Sphinx version, state it here.
|
# If your documentation needs a minimal Sphinx version, state it here.
|
||||||
#needs_sphinx = '1.0'
|
# needs_sphinx = '1.0'
|
||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||||
extensions = [
|
extensions = [
|
||||||
'releases', 'sphinx.ext.autodoc', 'celery.contrib.sphinx',
|
"releases",
|
||||||
'sphinxcontrib.blockdiag']
|
"sphinx.ext.autodoc",
|
||||||
|
"celery.contrib.sphinx",
|
||||||
|
"sphinxcontrib.blockdiag",
|
||||||
|
]
|
||||||
|
|
||||||
# configuration for releases extension
|
# configuration for releases extension
|
||||||
releases_issue_uri = 'https://git.dittberner.info/gnuviech/gva/issues/%s'
|
releases_issue_uri = "https://git.dittberner.info/gnuviech/gva/issues/%s"
|
||||||
releases_release_uri = 'https://git.dittberner.info/gnuviech/gva/src/tag/%s'
|
releases_release_uri = "https://git.dittberner.info/gnuviech/gva/src/tag/%s"
|
||||||
|
|
||||||
# configuration for blockdiag extension
|
# configuration for blockdiag extension
|
||||||
blockdiag_fontpath = '/usr/share/fonts/truetype/dejavu/'
|
blockdiag_fontpath = "/usr/share/fonts/truetype/dejavu/"
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ['_templates']
|
templates_path = ["_templates"]
|
||||||
|
|
||||||
# The suffix of source filenames.
|
# The suffix of source filenames.
|
||||||
source_suffix = '.rst'
|
source_suffix = ".rst"
|
||||||
|
|
||||||
# The encoding of source files.
|
# The encoding of source files.
|
||||||
#source_encoding = 'utf-8-sig'
|
# source_encoding = 'utf-8-sig'
|
||||||
|
|
||||||
# The master toctree document.
|
# The master toctree document.
|
||||||
master_doc = 'index'
|
master_doc = "index"
|
||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = u'gnuviechadmin'
|
project = "gnuviechadmin"
|
||||||
copyright = u'2014-2020, Jan Dittberner'
|
copyright = "2014-2023, Jan Dittberner"
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
# |version| and |release|, also used in various other places throughout the
|
# |version| and |release|, also used in various other places throughout the
|
||||||
|
@ -69,121 +72,121 @@ copyright = u'2014-2020, Jan Dittberner'
|
||||||
from gnuviechadmin import __version__ as release
|
from gnuviechadmin import __version__ as release
|
||||||
|
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = ".".join(release.split('.')[:2])
|
version = ".".join(release.split(".")[:2])
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
#language = None
|
# language = None
|
||||||
|
|
||||||
# There are two options for replacing |today|: either, you set today to some
|
# There are two options for replacing |today|: either, you set today to some
|
||||||
# non-false value, then it is used:
|
# non-false value, then it is used:
|
||||||
#today = ''
|
# today = ''
|
||||||
# Else, today_fmt is used as the format for a strftime call.
|
# Else, today_fmt is used as the format for a strftime call.
|
||||||
#today_fmt = '%B %d, %Y'
|
# today_fmt = '%B %d, %Y'
|
||||||
|
|
||||||
# List of patterns, relative to source directory, that match files and
|
# List of patterns, relative to source directory, that match files and
|
||||||
# directories to ignore when looking for source files.
|
# directories to ignore when looking for source files.
|
||||||
exclude_patterns = ['_build']
|
exclude_patterns = ["_build"]
|
||||||
|
|
||||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||||
#default_role = None
|
# default_role = None
|
||||||
|
|
||||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||||
#add_function_parentheses = True
|
# add_function_parentheses = True
|
||||||
|
|
||||||
# If true, the current module name will be prepended to all description
|
# If true, the current module name will be prepended to all description
|
||||||
# unit titles (such as .. function::).
|
# unit titles (such as .. function::).
|
||||||
#add_module_names = True
|
# add_module_names = True
|
||||||
|
|
||||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||||
# output. They are ignored by default.
|
# output. They are ignored by default.
|
||||||
#show_authors = False
|
# show_authors = False
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
pygments_style = 'sphinx'
|
pygments_style = "sphinx"
|
||||||
|
|
||||||
# A list of ignored prefixes for module index sorting.
|
# A list of ignored prefixes for module index sorting.
|
||||||
#modindex_common_prefix = []
|
# modindex_common_prefix = []
|
||||||
|
|
||||||
|
|
||||||
# -- Options for HTML output ---------------------------------------------------
|
# -- Options for HTML output ---------------------------------------------------
|
||||||
|
|
||||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
# a list of builtin themes.
|
# a list of builtin themes.
|
||||||
html_theme = 'alabaster'
|
html_theme = "alabaster"
|
||||||
|
|
||||||
# Theme options are theme-specific and customize the look and feel of a theme
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
# further. For a list of options available for each theme, see the
|
# further. For a list of options available for each theme, see the
|
||||||
# documentation.
|
# documentation.
|
||||||
#html_theme_options = {}
|
# html_theme_options = {}
|
||||||
|
|
||||||
# Add any paths that contain custom themes here, relative to this directory.
|
# Add any paths that contain custom themes here, relative to this directory.
|
||||||
#html_theme_path = []
|
# html_theme_path = []
|
||||||
|
|
||||||
# The name for this set of Sphinx documents. If None, it defaults to
|
# The name for this set of Sphinx documents. If None, it defaults to
|
||||||
# "<project> v<release> documentation".
|
# "<project> v<release> documentation".
|
||||||
#html_title = None
|
# html_title = None
|
||||||
|
|
||||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||||
#html_short_title = None
|
# html_short_title = None
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top
|
# The name of an image file (relative to this directory) to place at the top
|
||||||
# of the sidebar.
|
# of the sidebar.
|
||||||
#html_logo = None
|
# html_logo = None
|
||||||
|
|
||||||
# The name of an image file (within the static path) to use as favicon of the
|
# The name of an image file (within the static path) to use as favicon of the
|
||||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||||
# pixels large.
|
# pixels large.
|
||||||
#html_favicon = None
|
# html_favicon = None
|
||||||
|
|
||||||
# Add any paths that contain custom static files (such as style sheets) here,
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
# relative to this directory. They are copied after the builtin static files,
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
html_static_path = ['_static']
|
html_static_path = ["_static"]
|
||||||
|
|
||||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||||
# using the given strftime format.
|
# using the given strftime format.
|
||||||
#html_last_updated_fmt = '%b %d, %Y'
|
# html_last_updated_fmt = '%b %d, %Y'
|
||||||
|
|
||||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||||
# typographically correct entities.
|
# typographically correct entities.
|
||||||
#html_use_smartypants = True
|
# html_use_smartypants = True
|
||||||
|
|
||||||
# Custom sidebar templates, maps document names to template names.
|
# Custom sidebar templates, maps document names to template names.
|
||||||
#html_sidebars = {}
|
# html_sidebars = {}
|
||||||
|
|
||||||
# Additional templates that should be rendered to pages, maps page names to
|
# Additional templates that should be rendered to pages, maps page names to
|
||||||
# template names.
|
# template names.
|
||||||
#html_additional_pages = {}
|
# html_additional_pages = {}
|
||||||
|
|
||||||
# If false, no module index is generated.
|
# If false, no module index is generated.
|
||||||
#html_domain_indices = True
|
# html_domain_indices = True
|
||||||
|
|
||||||
# If false, no index is generated.
|
# If false, no index is generated.
|
||||||
#html_use_index = True
|
# html_use_index = True
|
||||||
|
|
||||||
# If true, the index is split into individual pages for each letter.
|
# If true, the index is split into individual pages for each letter.
|
||||||
#html_split_index = False
|
# html_split_index = False
|
||||||
|
|
||||||
# If true, links to the reST sources are added to the pages.
|
# If true, links to the reST sources are added to the pages.
|
||||||
#html_show_sourcelink = True
|
# html_show_sourcelink = True
|
||||||
|
|
||||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||||
#html_show_sphinx = True
|
# html_show_sphinx = True
|
||||||
|
|
||||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||||
#html_show_copyright = True
|
# html_show_copyright = True
|
||||||
|
|
||||||
# If true, an OpenSearch description file will be output, and all pages will
|
# If true, an OpenSearch description file will be output, and all pages will
|
||||||
# contain a <link> tag referring to it. The value of this option must be the
|
# contain a <link> tag referring to it. The value of this option must be the
|
||||||
# base URL from which the finished HTML is served.
|
# base URL from which the finished HTML is served.
|
||||||
#html_use_opensearch = ''
|
# html_use_opensearch = ''
|
||||||
|
|
||||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||||
#html_file_suffix = None
|
# html_file_suffix = None
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
# Output file base name for HTML help builder.
|
||||||
htmlhelp_basename = 'gnuviechadmindoc'
|
htmlhelp_basename = "gnuviechadmindoc"
|
||||||
|
|
||||||
|
|
||||||
# -- Options for LaTeX output --------------------------------------------------
|
# -- Options for LaTeX output --------------------------------------------------
|
||||||
|
@ -191,10 +194,8 @@ htmlhelp_basename = 'gnuviechadmindoc'
|
||||||
latex_elements = {
|
latex_elements = {
|
||||||
# The paper size ('letterpaper' or 'a4paper').
|
# The paper size ('letterpaper' or 'a4paper').
|
||||||
#'papersize': 'letterpaper',
|
#'papersize': 'letterpaper',
|
||||||
|
|
||||||
# The font size ('10pt', '11pt' or '12pt').
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
#'pointsize': '10pt',
|
#'pointsize': '10pt',
|
||||||
|
|
||||||
# Additional stuff for the LaTeX preamble.
|
# Additional stuff for the LaTeX preamble.
|
||||||
#'preamble': '',
|
#'preamble': '',
|
||||||
}
|
}
|
||||||
|
@ -202,29 +203,34 @@ latex_elements = {
|
||||||
# Grouping the document tree into LaTeX files. List of tuples
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||||
latex_documents = [
|
latex_documents = [
|
||||||
('index', 'gnuviechadmin.tex', u'gnuviechadmin Documentation',
|
(
|
||||||
u'Jan Dittberner', 'manual'),
|
"index",
|
||||||
|
"gnuviechadmin.tex",
|
||||||
|
"gnuviechadmin Documentation",
|
||||||
|
"Jan Dittberner",
|
||||||
|
"manual",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top of
|
# The name of an image file (relative to this directory) to place at the top of
|
||||||
# the title page.
|
# the title page.
|
||||||
#latex_logo = None
|
# latex_logo = None
|
||||||
|
|
||||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||||
# not chapters.
|
# not chapters.
|
||||||
#latex_use_parts = False
|
# latex_use_parts = False
|
||||||
|
|
||||||
# If true, show page references after internal links.
|
# If true, show page references after internal links.
|
||||||
#latex_show_pagerefs = False
|
# latex_show_pagerefs = False
|
||||||
|
|
||||||
# If true, show URL addresses after external links.
|
# If true, show URL addresses after external links.
|
||||||
#latex_show_urls = False
|
# latex_show_urls = False
|
||||||
|
|
||||||
# Documents to append as an appendix to all manuals.
|
# Documents to append as an appendix to all manuals.
|
||||||
#latex_appendices = []
|
# latex_appendices = []
|
||||||
|
|
||||||
# If false, no module index is generated.
|
# If false, no module index is generated.
|
||||||
#latex_domain_indices = True
|
# latex_domain_indices = True
|
||||||
|
|
||||||
|
|
||||||
# -- Options for manual page output --------------------------------------------
|
# -- Options for manual page output --------------------------------------------
|
||||||
|
@ -232,12 +238,11 @@ latex_documents = [
|
||||||
# One entry per manual page. List of tuples
|
# One entry per manual page. List of tuples
|
||||||
# (source start file, name, description, authors, manual section).
|
# (source start file, name, description, authors, manual section).
|
||||||
man_pages = [
|
man_pages = [
|
||||||
('index', 'gnuviechadmin', u'gnuviechadmin Documentation',
|
("index", "gnuviechadmin", "gnuviechadmin Documentation", ["Jan Dittberner"], 1)
|
||||||
[u'Jan Dittberner'], 1)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# If true, show URL addresses after external links.
|
# If true, show URL addresses after external links.
|
||||||
#man_show_urls = False
|
# man_show_urls = False
|
||||||
|
|
||||||
|
|
||||||
# -- Options for Texinfo output ------------------------------------------------
|
# -- Options for Texinfo output ------------------------------------------------
|
||||||
|
@ -246,16 +251,22 @@ man_pages = [
|
||||||
# (source start file, target name, title, author,
|
# (source start file, target name, title, author,
|
||||||
# dir menu entry, description, category)
|
# dir menu entry, description, category)
|
||||||
texinfo_documents = [
|
texinfo_documents = [
|
||||||
('index', 'gnuviechadmin', u'gnuviechadmin Documentation',
|
(
|
||||||
u'Jan Dittberner', 'gnuviechadmin', 'Customer center for gnuviech servers.',
|
"index",
|
||||||
'Miscellaneous'),
|
"gnuviechadmin",
|
||||||
|
"gnuviechadmin Documentation",
|
||||||
|
"Jan Dittberner",
|
||||||
|
"gnuviechadmin",
|
||||||
|
"Customer center for gnuviech servers.",
|
||||||
|
"Miscellaneous",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Documents to append as an appendix to all manuals.
|
# Documents to append as an appendix to all manuals.
|
||||||
#texinfo_appendices = []
|
# texinfo_appendices = []
|
||||||
|
|
||||||
# If false, no module index is generated.
|
# If false, no module index is generated.
|
||||||
#texinfo_domain_indices = True
|
# texinfo_domain_indices = True
|
||||||
|
|
||||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||||
#texinfo_show_urls = 'footnote'
|
# texinfo_show_urls = 'footnote'
|
||||||
|
|
7
entrypoint.sh
Executable file
7
entrypoint.sh
Executable file
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
chown -Rc gva.gva /srv/gva/media /srv/gva/static
|
||||||
|
|
||||||
|
su -c /srv/gva.sh gva
|
1
frontend/.gitignore
vendored
Normal file
1
frontend/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
node_modules/
|
46
frontend/package-lock.json
generated
Normal file
46
frontend/package-lock.json
generated
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"name": "frontend",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"dependencies": {
|
||||||
|
"bootstrap": "^5.2.3",
|
||||||
|
"bootstrap-icons": "^1.10.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@popperjs/core": {
|
||||||
|
"version": "2.11.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.7.tgz",
|
||||||
|
"integrity": "sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==",
|
||||||
|
"peer": true,
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/popperjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/bootstrap": {
|
||||||
|
"version": "5.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.3.tgz",
|
||||||
|
"integrity": "sha512-cEKPM+fwb3cT8NzQZYEu4HilJ3anCrWqh3CHAok1p9jXqMPsPTBhU25fBckEJHJ/p+tTxTFTsFQGM+gaHpi3QQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/twbs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/bootstrap"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"peerDependencies": {
|
||||||
|
"@popperjs/core": "^2.11.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/bootstrap-icons": {
|
||||||
|
"version": "1.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.10.4.tgz",
|
||||||
|
"integrity": "sha512-eI3HyIUmpGKRiRv15FCZccV+2sreGE2NnmH8mtxV/nPOzQVu0sPEj8HhF1MwjJ31IhjF0rgMvtYOX5VqIzcb/A=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
frontend/package.json
Normal file
6
frontend/package.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"bootstrap": "^5.2.3",
|
||||||
|
"bootstrap-icons": "^1.10.4"
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,19 +4,17 @@ This module contains the form class for the contact_form app.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
from django import forms
|
|
||||||
from django.conf import settings
|
|
||||||
from django.core.mail import send_mail
|
|
||||||
from django.template import RequestContext
|
|
||||||
from django.template import loader
|
|
||||||
from django.urls import reverse
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
|
|
||||||
from django.contrib.sites.models import Site
|
|
||||||
from django.contrib.sites.requests import RequestSite
|
|
||||||
|
|
||||||
from crispy_forms.helper import FormHelper
|
from crispy_forms.helper import FormHelper
|
||||||
from crispy_forms.layout import Submit
|
from crispy_forms.layout import Submit
|
||||||
|
from django import forms
|
||||||
|
from django.apps import apps
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib.sites.models import Site
|
||||||
|
from django.contrib.sites.requests import RequestSite
|
||||||
|
from django.core.mail import send_mail
|
||||||
|
from django.template import RequestContext, loader
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
class ContactForm(forms.Form):
|
class ContactForm(forms.Form):
|
||||||
|
@ -24,45 +22,42 @@ class ContactForm(forms.Form):
|
||||||
This is the contact form class.
|
This is the contact form class.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
name = forms.CharField(max_length=100, label=_('Your name'))
|
|
||||||
email = forms.EmailField(max_length=200, label=_('Your email address'))
|
name = forms.CharField(max_length=100, label=_("Your name"))
|
||||||
body = forms.CharField(widget=forms.Textarea, label=_('Your message'))
|
email = forms.EmailField(max_length=200, label=_("Your email address"))
|
||||||
|
body = forms.CharField(widget=forms.Textarea, label=_("Your message"))
|
||||||
|
|
||||||
subject_template_name = "contact_form/contact_form_subject.txt"
|
subject_template_name = "contact_form/contact_form_subject.txt"
|
||||||
template_name = 'contact_form/contact_form.txt'
|
template_name = "contact_form/contact_form.txt"
|
||||||
from_email = settings.DEFAULT_FROM_EMAIL
|
from_email = settings.DEFAULT_FROM_EMAIL
|
||||||
recipient_list = [mail_tuple[1] for mail_tuple in settings.MANAGERS]
|
recipient_list = [mail_tuple[1] for mail_tuple in settings.MANAGERS]
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self.request = kwargs.pop('request')
|
self.request = kwargs.pop("request")
|
||||||
super(ContactForm, self).__init__(**kwargs)
|
super(ContactForm, self).__init__(**kwargs)
|
||||||
self.helper = FormHelper()
|
self.helper = FormHelper()
|
||||||
self.helper.form_action = reverse('contact_form')
|
self.helper.form_action = reverse("contact_form")
|
||||||
self.helper.add_input(Submit('submit', _('Send message')))
|
self.helper.add_input(Submit("submit", _("Send message")))
|
||||||
|
|
||||||
def get_context(self):
|
def get_context(self):
|
||||||
if not self.is_valid():
|
if not self.is_valid():
|
||||||
raise ValueError(
|
raise ValueError("Cannot generate context from invalid contact form")
|
||||||
'Cannot generate context from invalid contact form')
|
if apps.is_installed("django.contrib.sites"):
|
||||||
if Site._meta.installed:
|
|
||||||
site = Site.objects.get_current()
|
site = Site.objects.get_current()
|
||||||
else:
|
else:
|
||||||
site = RequestSite(self.request)
|
site = RequestSite(self.request)
|
||||||
return RequestContext(
|
return RequestContext(self.request, dict(self.cleaned_data, site=site))
|
||||||
self.request, dict(self.cleaned_data, site=site))
|
|
||||||
|
|
||||||
def message(self):
|
def message(self):
|
||||||
context = self.get_context()
|
context = self.get_context()
|
||||||
template_context = context.flatten()
|
template_context = context.flatten()
|
||||||
template_context.update({
|
template_context.update({"remote_ip": context.request.META["REMOTE_ADDR"]})
|
||||||
'remote_ip': context.request.META['REMOTE_ADDR']
|
|
||||||
})
|
|
||||||
return loader.render_to_string(self.template_name, template_context)
|
return loader.render_to_string(self.template_name, template_context)
|
||||||
|
|
||||||
def subject(self):
|
def subject(self):
|
||||||
context = self.get_context().flatten()
|
context = self.get_context().flatten()
|
||||||
subject = loader.render_to_string(self.subject_template_name, context)
|
subject = loader.render_to_string(self.subject_template_name, context)
|
||||||
return ''.join(subject.splitlines())
|
return "".join(subject.splitlines())
|
||||||
|
|
||||||
def save(self, fail_silently=False):
|
def save(self, fail_silently=False):
|
||||||
"""
|
"""
|
||||||
|
@ -74,5 +69,5 @@ class ContactForm(forms.Form):
|
||||||
from_email=self.from_email,
|
from_email=self.from_email,
|
||||||
recipient_list=self.recipient_list,
|
recipient_list=self.recipient_list,
|
||||||
subject=self.subject(),
|
subject=self.subject(),
|
||||||
message=self.message()
|
message=self.message(),
|
||||||
)
|
)
|
||||||
|
|
|
@ -7,8 +7,8 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: contact_form\n"
|
"Project-Id-Version: contact_form\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2016-01-29 11:04+0100\n"
|
"POT-Creation-Date: 2023-07-22 19:45+0200\n"
|
||||||
"PO-Revision-Date: 2015-02-01 19:03+0100\n"
|
"PO-Revision-Date: 2023-04-22 13:01+0200\n"
|
||||||
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
|
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
|
||||||
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
|
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
|
||||||
"Language: de\n"
|
"Language: de\n"
|
||||||
|
@ -16,21 +16,32 @@ msgstr ""
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"X-Generator: Poedit 1.6.10\n"
|
"X-Generator: Poedit 3.2.2\n"
|
||||||
"X-Poedit-SourceCharset: UTF-8\n"
|
"X-Poedit-SourceCharset: UTF-8\n"
|
||||||
|
|
||||||
#: contact_form/forms.py:27
|
#: contact_form/forms.py:26
|
||||||
msgid "Your name"
|
msgid "Your name"
|
||||||
msgstr "Ihr Name"
|
msgstr "Ihr Name"
|
||||||
|
|
||||||
#: contact_form/forms.py:28
|
#: contact_form/forms.py:27
|
||||||
msgid "Your email address"
|
msgid "Your email address"
|
||||||
msgstr "Ihre E-Mailadresse"
|
msgstr "Ihre E-Mail-Adresse"
|
||||||
|
|
||||||
#: contact_form/forms.py:29
|
#: contact_form/forms.py:28
|
||||||
msgid "Your message"
|
msgid "Your message"
|
||||||
msgstr "Ihre Nachricht"
|
msgstr "Ihre Nachricht"
|
||||||
|
|
||||||
#: contact_form/forms.py:41
|
#: contact_form/forms.py:40
|
||||||
msgid "Send message"
|
msgid "Send message"
|
||||||
msgstr "Nachricht senden"
|
msgstr "Nachricht senden"
|
||||||
|
|
||||||
|
#: contact_form/templates/contact_form/contact_form.html:4
|
||||||
|
#: contact_form/templates/contact_form/contact_form.html:5
|
||||||
|
#: contact_form/templates/contact_form/contact_success.html:4
|
||||||
|
#: contact_form/templates/contact_form/contact_success.html:5
|
||||||
|
msgid "Contact"
|
||||||
|
msgstr "Kontakt"
|
||||||
|
|
||||||
|
#: contact_form/templates/contact_form/contact_success.html:8
|
||||||
|
msgid "Your message has been sent successfully."
|
||||||
|
msgstr "Ihre Nachricht wurde erfolgreich übermittelt."
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
{% extends "contact_form/base.html" %}
|
||||||
|
{% load i18n crispy_forms_tags %}
|
||||||
|
|
||||||
|
{% block title %}{{ block.super }} - {% translate "Contact" %}{% endblock title %}
|
||||||
|
{% block page_title %}{% translate "Contact" %}{% endblock page_title %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% crispy form %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_js %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
let textFields = document.querySelectorAll("input[type=text]");
|
||||||
|
|
||||||
|
if (textFields[0].val() !== '') {
|
||||||
|
document.getElementsByTagName("textarea")[0].focus();
|
||||||
|
} else {
|
||||||
|
textFields[0].focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock extra_js %}
|
|
@ -0,0 +1,9 @@
|
||||||
|
{% extends "contact_form/base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}{{ block.super }} - {% translate "Contact" %}{% endblock title %}
|
||||||
|
{% block page_title %}{% translate "Contact" %}{% endblock page_title %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<p class="text-success">{% translate "Your message has been sent successfully." %}</p>
|
||||||
|
{% endblock %}
|
|
@ -2,17 +2,13 @@
|
||||||
URL patterns for the contact_form views.
|
URL patterns for the contact_form views.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from django.conf.urls import url
|
from django.urls import re_path
|
||||||
|
|
||||||
from .views import (
|
|
||||||
ContactFormView,
|
|
||||||
ContactSuccessView,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
from .views import ContactFormView, ContactSuccessView
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^$', ContactFormView.as_view(), name='contact_form'),
|
re_path(r"^$", ContactFormView.as_view(), name="contact_form"),
|
||||||
url(r'^success/$', ContactSuccessView.as_view(), name='contact_success'),
|
re_path(r"^success/$", ContactSuccessView.as_view(), name="contact_success"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -2,14 +2,11 @@
|
||||||
This module defines the views of the contact_form app.
|
This module defines the views of the contact_form app.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.views.generic import (
|
from django.views.generic import FormView, TemplateView
|
||||||
FormView,
|
|
||||||
TemplateView,
|
|
||||||
)
|
|
||||||
|
|
||||||
from .forms import ContactForm
|
from .forms import ContactForm
|
||||||
|
|
||||||
|
@ -19,22 +16,22 @@ class ContactFormView(FormView):
|
||||||
This is the contact form view.
|
This is the contact form view.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
form_class = ContactForm
|
form_class = ContactForm
|
||||||
template_name = 'contact_form/contact_form.html'
|
template_name = "contact_form/contact_form.html"
|
||||||
success_url = reverse_lazy('contact_success')
|
success_url = reverse_lazy("contact_success")
|
||||||
|
|
||||||
def get_form_kwargs(self, **kwargs):
|
def get_form_kwargs(self, **kwargs):
|
||||||
kwargs = super(ContactFormView, self).get_form_kwargs(**kwargs)
|
kwargs = super(ContactFormView, self).get_form_kwargs(**kwargs)
|
||||||
kwargs['request'] = self.request
|
kwargs["request"] = self.request
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
initial = super(ContactFormView, self).get_initial()
|
initial = super(ContactFormView, self).get_initial()
|
||||||
currentuser = self.request.user
|
currentuser = self.request.user
|
||||||
if currentuser.is_authenticated:
|
if currentuser.is_authenticated:
|
||||||
initial['name'] = (
|
initial["name"] = currentuser.get_full_name() or currentuser.username
|
||||||
currentuser.get_full_name() or currentuser.username)
|
initial["email"] = currentuser.email
|
||||||
initial['email'] = currentuser.email
|
|
||||||
return initial
|
return initial
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
|
@ -47,4 +44,5 @@ class ContactSuccessView(TemplateView):
|
||||||
This view is shown after successful contact form sending.
|
This view is shown after successful contact form sending.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
template_name = 'contact_form/contact_success.html'
|
|
||||||
|
template_name = "contact_form/contact_success.html"
|
||||||
|
|
|
@ -7,18 +7,53 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: gnuviechadmin dashboard\n"
|
"Project-Id-Version: gnuviechadmin dashboard\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2015-01-17 15:59+0100\n"
|
"POT-Creation-Date: 2023-07-22 19:45+0200\n"
|
||||||
"PO-Revision-Date: 2015-01-17 16:01+0100\n"
|
"PO-Revision-Date: 2023-07-22 19:46+0200\n"
|
||||||
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
|
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
|
||||||
"Language-Team: Jan Dittberner <de@li.org>\n"
|
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
|
||||||
"Language: de\n"
|
"Language: de\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"X-Generator: Poedit 1.6.10\n"
|
"X-Generator: Poedit 3.2.2\n"
|
||||||
"X-Poedit-SourceCharset: UTF-8\n"
|
"X-Poedit-SourceCharset: UTF-8\n"
|
||||||
|
|
||||||
#: dashboard/views.py:43
|
#: dashboard/templates/dashboard/user_dashboard.html:3
|
||||||
msgid "You are not allowed to view this page."
|
#: dashboard/templates/dashboard/user_dashboard.html:6
|
||||||
msgstr "Sie haben nicht die nötigen Berechtigungen um diese Seite zu sehen."
|
#, python-format
|
||||||
|
msgid "Dashboard for %(full_name)s"
|
||||||
|
msgstr "Startseite für %(full_name)s"
|
||||||
|
|
||||||
|
#: dashboard/templates/dashboard/user_dashboard.html:10
|
||||||
|
msgid "Hosting packages"
|
||||||
|
msgstr "Hostingpakete"
|
||||||
|
|
||||||
|
#: dashboard/templates/dashboard/user_dashboard.html:17
|
||||||
|
msgid "Name"
|
||||||
|
msgstr "Name"
|
||||||
|
|
||||||
|
#: dashboard/templates/dashboard/user_dashboard.html:18
|
||||||
|
msgid "Setup date"
|
||||||
|
msgstr "Einrichtungsdatum"
|
||||||
|
|
||||||
|
#: dashboard/templates/dashboard/user_dashboard.html:19
|
||||||
|
msgid "Actions"
|
||||||
|
msgstr "Aktionen"
|
||||||
|
|
||||||
|
#: dashboard/templates/dashboard/user_dashboard.html:26
|
||||||
|
#, python-format
|
||||||
|
msgid "Show details for %(packagename)s"
|
||||||
|
msgstr "Details für %(packagename)s anzeigen"
|
||||||
|
|
||||||
|
#: dashboard/templates/dashboard/user_dashboard.html:38
|
||||||
|
msgid "You have no hosting packages yet."
|
||||||
|
msgstr "Sie haben noch keine Hostingpakete."
|
||||||
|
|
||||||
|
#: dashboard/templates/dashboard/user_dashboard.html:39
|
||||||
|
msgid "This user has no hosting packages assigned yet."
|
||||||
|
msgstr "Diesem Benutzer sind noch keine Hostingpakete zugewiesen."
|
||||||
|
|
||||||
|
#: dashboard/templates/dashboard/user_dashboard.html:43
|
||||||
|
msgid "Add hosting package"
|
||||||
|
msgstr "Hostingpaket anlegen"
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block title %}{{ block.super }} - {% blocktranslate with full_name=request.user.get_full_name trimmed %}
|
||||||
|
Dashboard for {{ full_name }}
|
||||||
|
{% endblocktranslate %}{% endblock title %}
|
||||||
|
{% block page_title %}{% blocktranslate with full_name=request.user.get_full_name trimmed %}
|
||||||
|
Dashboard for {{ full_name }}
|
||||||
|
{% endblocktranslate %}{% endblock page_title %}
|
||||||
|
{% block content %}
|
||||||
|
<h2>{% translate "Hosting packages" %}</h2>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
{% if hosting_packages %}
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% translate "Name" %}</th>
|
||||||
|
<th>{% translate "Setup date" %}</th>
|
||||||
|
<th>{% translate "Actions" %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for package in hosting_packages %}
|
||||||
|
<tr>
|
||||||
|
<td><a href="{{ package.get_absolute_url }}"
|
||||||
|
title="{% blocktranslate with packagename=package.name trimmed %}
|
||||||
|
Show details for {{ packagename }}
|
||||||
|
{% endblocktranslate %}">{{ package.name }}</a>
|
||||||
|
</td>
|
||||||
|
<td>{{ package.created }}</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<p class="text-info">
|
||||||
|
{% if user == object %}{% translate "You have no hosting packages yet." %}{% else %}
|
||||||
|
{% translate "This user has no hosting packages assigned yet." %}{% endif %}</p>
|
||||||
|
{% endif %}
|
||||||
|
{% if user.is_staff %}
|
||||||
|
<a href="{% url "create_customer_hosting_package" user=request.user.username %}"
|
||||||
|
class="btn btn-primary">{% translate "Add hosting package" %}</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock content %}
|
|
@ -2,7 +2,7 @@
|
||||||
Tests for :py:mod:`dashboard.views`.
|
Tests for :py:mod:`dashboard.views`.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from django import http
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
@ -13,55 +13,35 @@ TEST_USER = "test"
|
||||||
TEST_PASSWORD = "secret"
|
TEST_PASSWORD = "secret"
|
||||||
|
|
||||||
|
|
||||||
class IndexViewTest(TestCase):
|
|
||||||
def test_index_view(self):
|
|
||||||
response = self.client.get(reverse("dashboard"))
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
self.assertTemplateUsed(response, "dashboard/index.html")
|
|
||||||
|
|
||||||
|
|
||||||
class UserDashboardViewTest(TestCase):
|
class UserDashboardViewTest(TestCase):
|
||||||
def _create_test_user(self):
|
def _create_test_user(self):
|
||||||
self.user = User.objects.create(username=TEST_USER)
|
self.user = User.objects.create(username=TEST_USER)
|
||||||
self.user.set_password(TEST_PASSWORD)
|
self.user.set_password(TEST_PASSWORD)
|
||||||
self.user.save()
|
self.user.save()
|
||||||
|
|
||||||
def test_user_dashboard_view_no_user(self):
|
|
||||||
response = self.client.get(
|
|
||||||
reverse("customer_dashboard", kwargs={"slug": TEST_USER})
|
|
||||||
)
|
|
||||||
self.assertEqual(response.status_code, 404)
|
|
||||||
|
|
||||||
def test_user_dashboard_view_anonymous(self):
|
def test_user_dashboard_view_anonymous(self):
|
||||||
User.objects.create(username=TEST_USER)
|
User.objects.create(username=TEST_USER)
|
||||||
response = self.client.get(
|
response = self.client.get(reverse("customer_dashboard"))
|
||||||
reverse("customer_dashboard", kwargs={"slug": TEST_USER})
|
self.assertEqual(response.status_code, 302)
|
||||||
)
|
self.assertRedirects(response, "/accounts/login/?next=/")
|
||||||
self.assertEqual(response.status_code, 403)
|
|
||||||
|
|
||||||
def test_user_dashboard_view_logged_in_ok(self):
|
def test_user_dashboard_view_logged_in_ok(self):
|
||||||
self._create_test_user()
|
self._create_test_user()
|
||||||
self.assertTrue(self.client.login(username=TEST_USER, password=TEST_PASSWORD))
|
self.assertTrue(self.client.login(username=TEST_USER, password=TEST_PASSWORD))
|
||||||
response = self.client.get(
|
response = self.client.get(reverse("customer_dashboard"))
|
||||||
reverse("customer_dashboard", kwargs={"slug": TEST_USER})
|
|
||||||
)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
def test_user_dashboard_view_logged_in_template(self):
|
def test_user_dashboard_view_logged_in_template(self):
|
||||||
self._create_test_user()
|
self._create_test_user()
|
||||||
self.assertTrue(self.client.login(username=TEST_USER, password=TEST_PASSWORD))
|
self.assertTrue(self.client.login(username=TEST_USER, password=TEST_PASSWORD))
|
||||||
response = self.client.get(
|
response = self.client.get(
|
||||||
reverse("customer_dashboard", kwargs={"slug": TEST_USER})
|
reverse("customer_dashboard")
|
||||||
)
|
)
|
||||||
self.assertTemplateUsed(response, "dashboard/user_dashboard.html")
|
self.assertTemplateUsed(response, "dashboard/user_dashboard.html")
|
||||||
|
|
||||||
def test_user_dashboard_view_logged_in_context_fresh(self):
|
def test_user_dashboard_view_logged_in_context_fresh(self):
|
||||||
self._create_test_user()
|
self._create_test_user()
|
||||||
self.assertTrue(self.client.login(username=TEST_USER, password=TEST_PASSWORD))
|
self.assertTrue(self.client.login(username=TEST_USER, password=TEST_PASSWORD))
|
||||||
response = self.client.get(
|
response = self.client.get(reverse("customer_dashboard"))
|
||||||
reverse("customer_dashboard", kwargs={"slug": TEST_USER})
|
|
||||||
)
|
|
||||||
self.assertIn("dashboard_user", response.context)
|
|
||||||
self.assertEqual(self.user, response.context["dashboard_user"])
|
|
||||||
self.assertIn("hosting_packages", response.context)
|
self.assertIn("hosting_packages", response.context)
|
||||||
self.assertEqual(len(response.context["hosting_packages"]), 0)
|
self.assertEqual(len(response.context["hosting_packages"]), 0)
|
||||||
|
|
|
@ -1,15 +1,9 @@
|
||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from django.conf.urls import url
|
from django.urls import path
|
||||||
|
|
||||||
from .views import (
|
|
||||||
IndexView,
|
|
||||||
UserDashboardView,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
from .views import UserDashboardView
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^$', IndexView.as_view(), name='dashboard'),
|
path("", UserDashboardView.as_view(), name="customer_dashboard"),
|
||||||
url(r'^user/(?P<slug>[\w0-9@.+-_]+)/$',
|
|
||||||
UserDashboardView.as_view(), name='customer_dashboard'),
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -2,47 +2,26 @@
|
||||||
This module defines the views for the gnuviechadmin customer dashboard.
|
This module defines the views for the gnuviechadmin customer dashboard.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.views.generic import (
|
|
||||||
DetailView,
|
|
||||||
TemplateView,
|
|
||||||
)
|
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from django.shortcuts import redirect
|
||||||
|
from django.views.generic import DetailView, TemplateView
|
||||||
from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin
|
from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin
|
||||||
|
|
||||||
from hostingpackages.models import CustomerHostingPackage
|
from hostingpackages.models import CustomerHostingPackage
|
||||||
|
|
||||||
|
|
||||||
class IndexView(TemplateView):
|
class UserDashboardView(LoginRequiredMixin, TemplateView):
|
||||||
"""
|
|
||||||
This is the dashboard view.
|
|
||||||
|
|
||||||
"""
|
|
||||||
template_name = 'dashboard/index.html'
|
|
||||||
|
|
||||||
|
|
||||||
class UserDashboardView(StaffOrSelfLoginRequiredMixin, DetailView):
|
|
||||||
"""
|
"""
|
||||||
This is the user dashboard view.
|
This is the user dashboard view.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
model = get_user_model()
|
|
||||||
context_object_name = 'dashboard_user'
|
template_name = "dashboard/user_dashboard.html"
|
||||||
slug_field = 'username'
|
|
||||||
template_name = 'dashboard/user_dashboard.html'
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(UserDashboardView, self).get_context_data(**kwargs)
|
context = super(UserDashboardView, self).get_context_data(**kwargs)
|
||||||
context['hosting_packages'] = CustomerHostingPackage.objects.filter(
|
context["hosting_packages"] = CustomerHostingPackage.objects.filter(
|
||||||
customer=self.object
|
customer=self.request.user
|
||||||
)
|
)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get_customer_object(self):
|
|
||||||
"""
|
|
||||||
Returns the customer object.
|
|
||||||
|
|
||||||
"""
|
|
||||||
return self.get_object()
|
|
||||||
|
|
|
@ -2,4 +2,3 @@
|
||||||
This app takes care of domains.
|
This app takes care of domains.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
default_app_config = 'domains.apps.DomainAppConfig'
|
|
||||||
|
|
|
@ -5,24 +5,7 @@ with the django admin site.
|
||||||
"""
|
"""
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from .models import (
|
from domains.models import HostingDomain, MailDomain
|
||||||
DNSComment,
|
|
||||||
DNSCryptoKey,
|
|
||||||
DNSDomain,
|
|
||||||
DNSDomainMetadata,
|
|
||||||
DNSRecord,
|
|
||||||
DNSSupermaster,
|
|
||||||
DNSTSIGKey,
|
|
||||||
HostingDomain,
|
|
||||||
MailDomain,
|
|
||||||
)
|
|
||||||
|
|
||||||
admin.site.register(MailDomain)
|
admin.site.register(MailDomain)
|
||||||
admin.site.register(HostingDomain)
|
admin.site.register(HostingDomain)
|
||||||
admin.site.register(DNSComment)
|
|
||||||
admin.site.register(DNSCryptoKey)
|
|
||||||
admin.site.register(DNSDomain)
|
|
||||||
admin.site.register(DNSDomainMetadata)
|
|
||||||
admin.site.register(DNSRecord)
|
|
||||||
admin.site.register(DNSSupermaster)
|
|
||||||
admin.site.register(DNSTSIGKey)
|
|
||||||
|
|
|
@ -3,9 +3,8 @@ This module contains the :py:class:`django.apps.AppConfig` instance for the
|
||||||
:py:mod:`domains` app.
|
:py:mod:`domains` app.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
class DomainAppConfig(AppConfig):
|
class DomainAppConfig(AppConfig):
|
||||||
|
@ -13,5 +12,6 @@ class DomainAppConfig(AppConfig):
|
||||||
AppConfig for the :py:mod:`domains` app.
|
AppConfig for the :py:mod:`domains` app.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
name = 'domains'
|
|
||||||
verbose_name = _('Domains')
|
name = "domains"
|
||||||
|
verbose_name = _("Domains")
|
||||||
|
|
|
@ -2,19 +2,15 @@
|
||||||
This module defines form classes for domain editing.
|
This module defines form classes for domain editing.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from crispy_forms.helper import FormHelper
|
||||||
|
from crispy_forms.layout import Layout, Submit
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from crispy_forms.helper import FormHelper
|
|
||||||
from crispy_forms.layout import (
|
|
||||||
Layout,
|
|
||||||
Submit,
|
|
||||||
)
|
|
||||||
|
|
||||||
from .models import HostingDomain
|
from .models import HostingDomain
|
||||||
|
|
||||||
|
@ -26,11 +22,10 @@ def relative_domain_validator(value):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if len(value) > 254:
|
if len(value) > 254:
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(_("host name too long"), code="too-long")
|
||||||
_('host name too long'), code='too-long')
|
|
||||||
allowed = re.compile(r"(?!-)[a-z\d-]{1,63}(?<!-)$")
|
allowed = re.compile(r"(?!-)[a-z\d-]{1,63}(?<!-)$")
|
||||||
if not all(allowed.match(x) for x in value.split('.')):
|
if not all(allowed.match(x) for x in value.split(".")):
|
||||||
raise forms.ValidationError(_('invalid domain name'))
|
raise forms.ValidationError(_("invalid domain name"))
|
||||||
|
|
||||||
|
|
||||||
class CreateHostingDomainForm(forms.ModelForm):
|
class CreateHostingDomainForm(forms.ModelForm):
|
||||||
|
@ -38,31 +33,32 @@ class CreateHostingDomainForm(forms.ModelForm):
|
||||||
This form is used to create new HostingDomain instances.
|
This form is used to create new HostingDomain instances.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = HostingDomain
|
model = HostingDomain
|
||||||
fields = ['domain']
|
fields = ["domain"]
|
||||||
|
|
||||||
def __init__(self, instance, *args, **kwargs):
|
def __init__(self, instance, *args, **kwargs):
|
||||||
self.hosting_package = kwargs.pop('hostingpackage')
|
self.hosting_package = kwargs.pop("hostingpackage")
|
||||||
super(CreateHostingDomainForm, self).__init__(*args, **kwargs)
|
super(CreateHostingDomainForm, self).__init__(*args, **kwargs)
|
||||||
self.fields['domain'].validators.append(relative_domain_validator)
|
self.fields["domain"].validators.append(relative_domain_validator)
|
||||||
self.helper = FormHelper()
|
self.helper = FormHelper()
|
||||||
self.helper.form_action = reverse(
|
self.helper.form_action = reverse(
|
||||||
'create_hosting_domain', kwargs={
|
"create_hosting_domain", kwargs={"package": self.hosting_package.id}
|
||||||
'package': self.hosting_package.id
|
)
|
||||||
})
|
|
||||||
self.helper.layout = Layout(
|
self.helper.layout = Layout(
|
||||||
'domain',
|
"domain",
|
||||||
Submit('submit', _('Add Hosting Domain')),
|
Submit("submit", _("Add Hosting Domain")),
|
||||||
)
|
)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
self.cleaned_data = super(CreateHostingDomainForm, self).clean()
|
self.cleaned_data = super(CreateHostingDomainForm, self).clean()
|
||||||
self.cleaned_data['hosting_package'] = self.hosting_package
|
self.cleaned_data["hosting_package"] = self.hosting_package
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
return HostingDomain.objects.create_for_hosting_package(
|
return HostingDomain.objects.create_for_hosting_package(
|
||||||
commit=commit, **self.cleaned_data)
|
commit=commit, **self.cleaned_data
|
||||||
|
)
|
||||||
|
|
||||||
def save_m2m(self):
|
def save_m2m(self):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -7,8 +7,8 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: gnuviechadmin domains\n"
|
"Project-Id-Version: gnuviechadmin domains\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2016-01-29 11:04+0100\n"
|
"POT-Creation-Date: 2023-04-16 22:07+0200\n"
|
||||||
"PO-Revision-Date: 2015-11-08 12:02+0100\n"
|
"PO-Revision-Date: 2023-04-16 18:20+0200\n"
|
||||||
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
|
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
|
||||||
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
|
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
|
||||||
"Language: de\n"
|
"Language: de\n"
|
||||||
|
@ -16,152 +16,60 @@ msgstr ""
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"X-Generator: Poedit 1.8.6\n"
|
"X-Generator: Poedit 3.2.2\n"
|
||||||
"X-Poedit-SourceCharset: UTF-8\n"
|
"X-Poedit-SourceCharset: UTF-8\n"
|
||||||
|
|
||||||
#: domains/apps.py:17
|
#: domains/apps.py:17
|
||||||
msgid "Domains"
|
msgid "Domains"
|
||||||
msgstr "Domains"
|
msgstr "Domains"
|
||||||
|
|
||||||
#: domains/forms.py:30 domains/tests/test_forms.py:24
|
#: domains/forms.py:25 domains/tests/test_forms.py:20
|
||||||
msgid "host name too long"
|
msgid "host name too long"
|
||||||
msgstr "zu langer Hostname"
|
msgstr "zu langer Hostname"
|
||||||
|
|
||||||
#: domains/forms.py:33 domains/tests/test_forms.py:29
|
#: domains/forms.py:28 domains/tests/test_forms.py:24
|
||||||
#: domains/tests/test_forms.py:34 domains/tests/test_forms.py:39
|
#: domains/tests/test_forms.py:28 domains/tests/test_forms.py:32
|
||||||
#: domains/tests/test_forms.py:44
|
#: domains/tests/test_forms.py:36
|
||||||
msgid "invalid domain name"
|
msgid "invalid domain name"
|
||||||
msgstr "ungültiger Domainname"
|
msgstr "ungültiger Domainname"
|
||||||
|
|
||||||
#: domains/forms.py:56
|
#: domains/forms.py:51
|
||||||
msgid "Add Hosting Domain"
|
msgid "Add Hosting Domain"
|
||||||
msgstr "Hostingdomain hinzufügen"
|
msgstr "Hostingdomain hinzufügen"
|
||||||
|
|
||||||
#: domains/models.py:17
|
|
||||||
msgid "Master"
|
|
||||||
msgstr "Master"
|
|
||||||
|
|
||||||
#: domains/models.py:18
|
|
||||||
msgid "Slave"
|
|
||||||
msgstr "Slave"
|
|
||||||
|
|
||||||
#: domains/models.py:19
|
#: domains/models.py:19
|
||||||
msgid "Native"
|
|
||||||
msgstr "Native"
|
|
||||||
|
|
||||||
#: domains/models.py:44
|
|
||||||
msgid "HMAC MD5"
|
|
||||||
msgstr "HMAC MD5"
|
|
||||||
|
|
||||||
#: domains/models.py:45
|
|
||||||
msgid "HMAC SHA1"
|
|
||||||
msgstr "HMAC SHA1"
|
|
||||||
|
|
||||||
#: domains/models.py:46
|
|
||||||
msgid "HMAC SHA224"
|
|
||||||
msgstr "HMAC SHA224"
|
|
||||||
|
|
||||||
#: domains/models.py:47
|
|
||||||
msgid "HMAC SHA256"
|
|
||||||
msgstr "HMAC SHA256"
|
|
||||||
|
|
||||||
#: domains/models.py:48
|
|
||||||
msgid "HMAC SHA384"
|
|
||||||
msgstr "HMAC SHA384"
|
|
||||||
|
|
||||||
#: domains/models.py:49
|
|
||||||
msgid "HMAC SHA512"
|
|
||||||
msgstr "HMAC SHA512"
|
|
||||||
|
|
||||||
#: domains/models.py:58
|
|
||||||
msgid "domain name"
|
msgid "domain name"
|
||||||
msgstr "Domainname"
|
msgstr "Domainname"
|
||||||
|
|
||||||
#: domains/models.py:60 domains/models.py:258 domains/models.py:308
|
#: domains/models.py:22
|
||||||
msgid "customer"
|
msgid "customer"
|
||||||
msgstr "Kunde"
|
msgstr "Kunde"
|
||||||
|
|
||||||
#: domains/models.py:76
|
#: domains/models.py:41
|
||||||
msgid "Mail domain"
|
msgid "Mail domain"
|
||||||
msgstr "E-Maildomain"
|
msgstr "E-Maildomain"
|
||||||
|
|
||||||
#: domains/models.py:77
|
#: domains/models.py:42
|
||||||
msgid "Mail domains"
|
msgid "Mail domains"
|
||||||
msgstr "E-Maildomains"
|
msgstr "E-Maildomains"
|
||||||
|
|
||||||
#: domains/models.py:121
|
#: domains/models.py:91
|
||||||
msgid "mail domain"
|
msgid "mail domain"
|
||||||
msgstr "E-Maildomain"
|
msgstr "E-Maildomain"
|
||||||
|
|
||||||
#: domains/models.py:122
|
#: domains/models.py:94
|
||||||
msgid "assigned mail domain for this domain"
|
msgid "assigned mail domain for this domain"
|
||||||
msgstr "zugeordnete E-Maildomain für diese Domain"
|
msgstr "zugeordnete E-Maildomain für diese Domain"
|
||||||
|
|
||||||
#: domains/models.py:128
|
#: domains/models.py:101
|
||||||
msgid "Hosting domain"
|
msgid "Hosting domain"
|
||||||
msgstr "Hostingdomain"
|
msgstr "Hostingdomain"
|
||||||
|
|
||||||
#: domains/models.py:129
|
#: domains/models.py:102
|
||||||
msgid "Hosting domains"
|
msgid "Hosting domains"
|
||||||
msgstr "Hostingdomains"
|
msgstr "Hostingdomains"
|
||||||
|
|
||||||
#: domains/models.py:169
|
#: domains/views.py:51
|
||||||
msgid "DNS domain"
|
|
||||||
msgstr "DNS-Domain"
|
|
||||||
|
|
||||||
#: domains/models.py:170
|
|
||||||
msgid "DNS domains"
|
|
||||||
msgstr "DNS-Domains"
|
|
||||||
|
|
||||||
#: domains/models.py:226
|
|
||||||
msgid "DNS record"
|
|
||||||
msgstr "DNS-Record"
|
|
||||||
|
|
||||||
#: domains/models.py:227
|
|
||||||
msgid "DNS records"
|
|
||||||
msgstr "DNS-Records"
|
|
||||||
|
|
||||||
#: domains/models.py:261
|
|
||||||
msgid "DNS supermaster"
|
|
||||||
msgstr "DNS-Supermaster"
|
|
||||||
|
|
||||||
#: domains/models.py:262
|
|
||||||
msgid "DNS supermasters"
|
|
||||||
msgstr "DNS-Supermasters"
|
|
||||||
|
|
||||||
#: domains/models.py:313
|
|
||||||
msgid "DNS comment"
|
|
||||||
msgstr "DNS-Kommentar"
|
|
||||||
|
|
||||||
#: domains/models.py:314
|
|
||||||
msgid "DNS comments"
|
|
||||||
msgstr "DNS-Kommentare"
|
|
||||||
|
|
||||||
#: domains/models.py:351
|
|
||||||
msgid "DNS domain metadata item"
|
|
||||||
msgstr "DNS-Domainmetadaten-Eintrag"
|
|
||||||
|
|
||||||
#: domains/models.py:352
|
|
||||||
msgid "DNS domain metadata items"
|
|
||||||
msgstr "DNS-Domainmetadaten-Einträge"
|
|
||||||
|
|
||||||
#: domains/models.py:385
|
|
||||||
msgid "DNS crypto key"
|
|
||||||
msgstr "DNS-Cryposchlüssel"
|
|
||||||
|
|
||||||
#: domains/models.py:386
|
|
||||||
msgid "DNS crypto keys"
|
|
||||||
msgstr "DNS-Cryptoschlüssel"
|
|
||||||
|
|
||||||
#: domains/models.py:420
|
|
||||||
msgid "DNS TSIG key"
|
|
||||||
msgstr "DNS-TSIG-Schlüssel"
|
|
||||||
|
|
||||||
#: domains/models.py:421
|
|
||||||
msgid "DNS TSIG keys"
|
|
||||||
msgstr "DNS-TSIG-Schlüssel"
|
|
||||||
|
|
||||||
#: domains/views.py:58
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Successfully created domain {domainname}"
|
msgid "Successfully created domain {domainname}"
|
||||||
msgstr "Domain {domainname} erfolgreich angelegt"
|
msgstr "Domain {domainname} erfolgreich angelegt"
|
||||||
|
|
|
@ -1,28 +1,46 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import models, migrations
|
|
||||||
import django.utils.timezone
|
import django.utils.timezone
|
||||||
import model_utils.fields
|
import model_utils.fields
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = []
|
||||||
dependencies = [
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='MailDomain',
|
name="MailDomain",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
(
|
||||||
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, verbose_name='created', editable=False)),
|
"id",
|
||||||
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, verbose_name='modified', editable=False)),
|
models.AutoField(
|
||||||
('domain', models.CharField(unique=True, max_length=128)),
|
verbose_name="ID",
|
||||||
|
serialize=False,
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"created",
|
||||||
|
model_utils.fields.AutoCreatedField(
|
||||||
|
default=django.utils.timezone.now,
|
||||||
|
verbose_name="created",
|
||||||
|
editable=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"modified",
|
||||||
|
model_utils.fields.AutoLastModifiedField(
|
||||||
|
default=django.utils.timezone.now,
|
||||||
|
verbose_name="modified",
|
||||||
|
editable=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("domain", models.CharField(unique=True, max_length=128)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Mail domain',
|
"verbose_name": "Mail domain",
|
||||||
'verbose_name_plural': 'Mail domains',
|
"verbose_name_plural": "Mail domains",
|
||||||
},
|
},
|
||||||
bases=(models.Model,),
|
bases=(models.Model,),
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,68 +1,97 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import models, migrations
|
|
||||||
import django.utils.timezone
|
import django.utils.timezone
|
||||||
from django.conf import settings
|
|
||||||
import model_utils.fields
|
import model_utils.fields
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
dependencies = [
|
dependencies = [
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
('domains', '0001_initial'),
|
("domains", "0001_initial"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='HostingDomain',
|
name="HostingDomain",
|
||||||
fields=[
|
fields=[
|
||||||
('id',
|
(
|
||||||
models.AutoField(verbose_name='ID', serialize=False,
|
"id",
|
||||||
auto_created=True, primary_key=True)),
|
models.AutoField(
|
||||||
('created',
|
verbose_name="ID",
|
||||||
model_utils.fields.AutoCreatedField(
|
serialize=False,
|
||||||
default=django.utils.timezone.now, verbose_name='created',
|
auto_created=True,
|
||||||
editable=False)),
|
primary_key=True,
|
||||||
('modified',
|
),
|
||||||
model_utils.fields.AutoLastModifiedField(
|
),
|
||||||
default=django.utils.timezone.now, verbose_name='modified',
|
(
|
||||||
editable=False)),
|
"created",
|
||||||
('domain',
|
model_utils.fields.AutoCreatedField(
|
||||||
models.CharField(
|
default=django.utils.timezone.now,
|
||||||
unique=True, max_length=128, verbose_name='domain name')),
|
verbose_name="created",
|
||||||
('customer',
|
editable=False,
|
||||||
models.ForeignKey(
|
),
|
||||||
verbose_name='customer', blank=True,
|
),
|
||||||
to=settings.AUTH_USER_MODEL, null=True,
|
(
|
||||||
on_delete=models.CASCADE)),
|
"modified",
|
||||||
('maildomain',
|
model_utils.fields.AutoLastModifiedField(
|
||||||
models.OneToOneField(
|
default=django.utils.timezone.now,
|
||||||
null=True, to='domains.MailDomain', blank=True,
|
verbose_name="modified",
|
||||||
help_text='assigned mail domain for this domain',
|
editable=False,
|
||||||
verbose_name='mail domain',
|
),
|
||||||
on_delete=models.CASCADE)),
|
),
|
||||||
|
(
|
||||||
|
"domain",
|
||||||
|
models.CharField(
|
||||||
|
unique=True, max_length=128, verbose_name="domain name"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"customer",
|
||||||
|
models.ForeignKey(
|
||||||
|
verbose_name="customer",
|
||||||
|
blank=True,
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
null=True,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"maildomain",
|
||||||
|
models.OneToOneField(
|
||||||
|
null=True,
|
||||||
|
to="domains.MailDomain",
|
||||||
|
blank=True,
|
||||||
|
help_text="assigned mail domain for this domain",
|
||||||
|
verbose_name="mail domain",
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Hosting domain',
|
"verbose_name": "Hosting domain",
|
||||||
'verbose_name_plural': 'Hosting domains',
|
"verbose_name_plural": "Hosting domains",
|
||||||
},
|
},
|
||||||
bases=(models.Model,),
|
bases=(models.Model,),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='maildomain',
|
model_name="maildomain",
|
||||||
name='customer',
|
name="customer",
|
||||||
field=models.ForeignKey(
|
field=models.ForeignKey(
|
||||||
verbose_name='customer', blank=True,
|
verbose_name="customer",
|
||||||
to=settings.AUTH_USER_MODEL, null=True,
|
blank=True,
|
||||||
on_delete=models.CASCADE),
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
null=True,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
preserve_default=True,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='maildomain',
|
model_name="maildomain",
|
||||||
name='domain',
|
name="domain",
|
||||||
field=models.CharField(
|
field=models.CharField(
|
||||||
unique=True, max_length=128, verbose_name='domain name'),
|
unique=True, max_length=128, verbose_name="domain name"
|
||||||
|
),
|
||||||
preserve_default=True,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,199 +1,285 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.utils.timezone
|
import django.utils.timezone
|
||||||
from django.conf import settings
|
|
||||||
import model_utils.fields
|
import model_utils.fields
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
dependencies = [
|
dependencies = [
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
('domains', '0002_auto_20150124_1909'),
|
("domains", "0002_auto_20150124_1909"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='DNSComment',
|
name="DNSComment",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(
|
(
|
||||||
verbose_name='ID', serialize=False,
|
"id",
|
||||||
auto_created=True, primary_key=True)),
|
models.AutoField(
|
||||||
('name', models.CharField(max_length=255)),
|
verbose_name="ID",
|
||||||
('commenttype',
|
serialize=False,
|
||||||
models.CharField(max_length=10, db_column='type')),
|
auto_created=True,
|
||||||
('modified_at', models.IntegerField()),
|
primary_key=True,
|
||||||
('comment', models.CharField(max_length=65535)),
|
),
|
||||||
('customer', models.ForeignKey(
|
),
|
||||||
verbose_name='customer',
|
("name", models.CharField(max_length=255)),
|
||||||
to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
|
("commenttype", models.CharField(max_length=10, db_column="type")),
|
||||||
|
("modified_at", models.IntegerField()),
|
||||||
|
("comment", models.CharField(max_length=65535)),
|
||||||
|
(
|
||||||
|
"customer",
|
||||||
|
models.ForeignKey(
|
||||||
|
verbose_name="customer",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.RunSQL(
|
migrations.RunSQL(
|
||||||
'''ALTER TABLE domains_dnscomment ADD CONSTRAINT c_lowercase_name
|
"""ALTER TABLE domains_dnscomment ADD CONSTRAINT c_lowercase_name
|
||||||
CHECK (((name)::TEXT = LOWER((name)::TEXT)))'''
|
CHECK (((name)::TEXT = LOWER((name)::TEXT)))"""
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='DNSCryptoKey',
|
name="DNSCryptoKey",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(
|
(
|
||||||
verbose_name='ID', serialize=False,
|
"id",
|
||||||
auto_created=True, primary_key=True)),
|
models.AutoField(
|
||||||
('flags', models.IntegerField()),
|
verbose_name="ID",
|
||||||
('active', models.BooleanField(default=True)),
|
serialize=False,
|
||||||
('content', models.TextField()),
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("flags", models.IntegerField()),
|
||||||
|
("active", models.BooleanField(default=True)),
|
||||||
|
("content", models.TextField()),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='DNSDomain',
|
name="DNSDomain",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(
|
(
|
||||||
verbose_name='ID', serialize=False,
|
"id",
|
||||||
auto_created=True, primary_key=True)),
|
models.AutoField(
|
||||||
('created', model_utils.fields.AutoCreatedField(
|
verbose_name="ID",
|
||||||
default=django.utils.timezone.now, verbose_name='created',
|
serialize=False,
|
||||||
editable=False)),
|
auto_created=True,
|
||||||
('modified', model_utils.fields.AutoLastModifiedField(
|
primary_key=True,
|
||||||
default=django.utils.timezone.now, verbose_name='modified',
|
),
|
||||||
editable=False)),
|
),
|
||||||
('domain', models.CharField(
|
(
|
||||||
unique=True, max_length=255, verbose_name='domain name')),
|
"created",
|
||||||
('master',
|
model_utils.fields.AutoCreatedField(
|
||||||
models.CharField(max_length=128, null=True, blank=True)),
|
default=django.utils.timezone.now,
|
||||||
('last_check', models.IntegerField(null=True)),
|
verbose_name="created",
|
||||||
('domaintype', models.CharField(
|
editable=False,
|
||||||
max_length=6, db_column='type',
|
),
|
||||||
choices=[('MASTER', 'Master'),
|
),
|
||||||
('SLAVE', 'Slave'),
|
(
|
||||||
('NATIVE', 'Native')])),
|
"modified",
|
||||||
('notified_serial', models.IntegerField(null=True)),
|
model_utils.fields.AutoLastModifiedField(
|
||||||
('customer', models.ForeignKey(
|
default=django.utils.timezone.now,
|
||||||
verbose_name='customer', blank=True,
|
verbose_name="modified",
|
||||||
to=settings.AUTH_USER_MODEL, null=True,
|
editable=False,
|
||||||
on_delete=models.CASCADE)),
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"domain",
|
||||||
|
models.CharField(
|
||||||
|
unique=True, max_length=255, verbose_name="domain name"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("master", models.CharField(max_length=128, null=True, blank=True)),
|
||||||
|
("last_check", models.IntegerField(null=True)),
|
||||||
|
(
|
||||||
|
"domaintype",
|
||||||
|
models.CharField(
|
||||||
|
max_length=6,
|
||||||
|
db_column="type",
|
||||||
|
choices=[
|
||||||
|
("MASTER", "Master"),
|
||||||
|
("SLAVE", "Slave"),
|
||||||
|
("NATIVE", "Native"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("notified_serial", models.IntegerField(null=True)),
|
||||||
|
(
|
||||||
|
"customer",
|
||||||
|
models.ForeignKey(
|
||||||
|
verbose_name="customer",
|
||||||
|
blank=True,
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
null=True,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'DNS domain',
|
"verbose_name": "DNS domain",
|
||||||
'verbose_name_plural': 'DNS domains',
|
"verbose_name_plural": "DNS domains",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.RunSQL(
|
migrations.RunSQL(
|
||||||
'''ALTER TABLE domains_dnsdomain ADD CONSTRAINT c_lowercase_name
|
"""ALTER TABLE domains_dnsdomain ADD CONSTRAINT c_lowercase_name
|
||||||
CHECK (((domain)::TEXT = LOWER((domain)::TEXT)))'''
|
CHECK (((domain)::TEXT = LOWER((domain)::TEXT)))"""
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='DNSDomainMetadata',
|
name="DNSDomainMetadata",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(
|
(
|
||||||
verbose_name='ID', serialize=False,
|
"id",
|
||||||
auto_created=True, primary_key=True)),
|
models.AutoField(
|
||||||
('kind', models.CharField(max_length=32)),
|
verbose_name="ID",
|
||||||
('content', models.TextField()),
|
serialize=False,
|
||||||
('domain', models.ForeignKey(
|
auto_created=True,
|
||||||
to='domains.DNSDomain', on_delete=models.CASCADE)),
|
primary_key=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("kind", models.CharField(max_length=32)),
|
||||||
|
("content", models.TextField()),
|
||||||
|
(
|
||||||
|
"domain",
|
||||||
|
models.ForeignKey(to="domains.DNSDomain", on_delete=models.CASCADE),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='DNSRecord',
|
name="DNSRecord",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(
|
(
|
||||||
verbose_name='ID', serialize=False,
|
"id",
|
||||||
auto_created=True, primary_key=True)),
|
models.AutoField(
|
||||||
('name', models.CharField(
|
verbose_name="ID",
|
||||||
db_index=True, max_length=255, null=True, blank=True)),
|
serialize=False,
|
||||||
('recordtype', models.CharField(
|
auto_created=True,
|
||||||
max_length=10, null=True, db_column='type', blank=True)),
|
primary_key=True,
|
||||||
('content', models.CharField(
|
),
|
||||||
max_length=65535, null=True, blank=True)),
|
),
|
||||||
('ttl', models.IntegerField(null=True)),
|
(
|
||||||
('prio', models.IntegerField(null=True)),
|
"name",
|
||||||
('change_date', models.IntegerField(null=True)),
|
models.CharField(
|
||||||
('disabled', models.BooleanField(default=False)),
|
db_index=True, max_length=255, null=True, blank=True
|
||||||
('ordername', models.CharField(max_length=255)),
|
),
|
||||||
('auth', models.BooleanField(default=True)),
|
),
|
||||||
('domain', models.ForeignKey(
|
(
|
||||||
to='domains.DNSDomain', on_delete=models.CASCADE)),
|
"recordtype",
|
||||||
|
models.CharField(
|
||||||
|
max_length=10, null=True, db_column="type", blank=True
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("content", models.CharField(max_length=65535, null=True, blank=True)),
|
||||||
|
("ttl", models.IntegerField(null=True)),
|
||||||
|
("prio", models.IntegerField(null=True)),
|
||||||
|
("change_date", models.IntegerField(null=True)),
|
||||||
|
("disabled", models.BooleanField(default=False)),
|
||||||
|
("ordername", models.CharField(max_length=255)),
|
||||||
|
("auth", models.BooleanField(default=True)),
|
||||||
|
(
|
||||||
|
"domain",
|
||||||
|
models.ForeignKey(to="domains.DNSDomain", on_delete=models.CASCADE),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'DNS record',
|
"verbose_name": "DNS record",
|
||||||
'verbose_name_plural': 'DNS records',
|
"verbose_name_plural": "DNS records",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.RunSQL(
|
migrations.RunSQL(
|
||||||
'''ALTER TABLE domains_dnsrecord ADD CONSTRAINT c_lowercase_name
|
"""ALTER TABLE domains_dnsrecord ADD CONSTRAINT c_lowercase_name
|
||||||
CHECK (((name)::TEXT = LOWER((name)::TEXT)))'''
|
CHECK (((name)::TEXT = LOWER((name)::TEXT)))"""
|
||||||
),
|
),
|
||||||
migrations.RunSQL(
|
migrations.RunSQL(
|
||||||
'''CREATE INDEX recordorder ON domains_dnsrecord (domain_id,
|
"""CREATE INDEX recordorder ON domains_dnsrecord (domain_id,
|
||||||
ordername text_pattern_ops)'''
|
ordername text_pattern_ops)"""
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='DNSSupermaster',
|
name="DNSSupermaster",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(
|
(
|
||||||
verbose_name='ID', serialize=False,
|
"id",
|
||||||
auto_created=True, primary_key=True)),
|
models.AutoField(
|
||||||
('ip', models.GenericIPAddressField()),
|
verbose_name="ID",
|
||||||
('nameserver', models.CharField(max_length=255)),
|
serialize=False,
|
||||||
('customer', models.ForeignKey(
|
auto_created=True,
|
||||||
verbose_name='customer', to=settings.AUTH_USER_MODEL,
|
primary_key=True,
|
||||||
on_delete=models.CASCADE)),
|
),
|
||||||
|
),
|
||||||
|
("ip", models.GenericIPAddressField()),
|
||||||
|
("nameserver", models.CharField(max_length=255)),
|
||||||
|
(
|
||||||
|
"customer",
|
||||||
|
models.ForeignKey(
|
||||||
|
verbose_name="customer",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='DNSTSIGKey',
|
name="DNSTSIGKey",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(
|
(
|
||||||
verbose_name='ID', serialize=False,
|
"id",
|
||||||
auto_created=True, primary_key=True)),
|
models.AutoField(
|
||||||
('name', models.CharField(max_length=255)),
|
verbose_name="ID",
|
||||||
('algorithm', models.CharField(max_length=50)),
|
serialize=False,
|
||||||
('secret', models.CharField(max_length=255)),
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("name", models.CharField(max_length=255)),
|
||||||
|
("algorithm", models.CharField(max_length=50)),
|
||||||
|
("secret", models.CharField(max_length=255)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.RunSQL(
|
migrations.RunSQL(
|
||||||
'''ALTER TABLE domains_dnstsigkey ADD CONSTRAINT c_lowercase_name
|
"""ALTER TABLE domains_dnstsigkey ADD CONSTRAINT c_lowercase_name
|
||||||
CHECK (((name)::TEXT = LOWER((name)::TEXT)))'''
|
CHECK (((name)::TEXT = LOWER((name)::TEXT)))"""
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='hostingdomain',
|
model_name="hostingdomain",
|
||||||
name='domain',
|
name="domain",
|
||||||
field=models.CharField(
|
field=models.CharField(
|
||||||
unique=True, max_length=255, verbose_name='domain name'),
|
unique=True, max_length=255, verbose_name="domain name"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='maildomain',
|
model_name="maildomain",
|
||||||
name='domain',
|
name="domain",
|
||||||
field=models.CharField(
|
field=models.CharField(
|
||||||
unique=True, max_length=255, verbose_name='domain name'),
|
unique=True, max_length=255, verbose_name="domain name"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='dnscryptokey',
|
model_name="dnscryptokey",
|
||||||
name='domain',
|
name="domain",
|
||||||
field=models.ForeignKey(
|
field=models.ForeignKey(to="domains.DNSDomain", on_delete=models.CASCADE),
|
||||||
to='domains.DNSDomain', on_delete=models.CASCADE),
|
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='dnscomment',
|
model_name="dnscomment",
|
||||||
name='domain',
|
name="domain",
|
||||||
field=models.ForeignKey(
|
field=models.ForeignKey(to="domains.DNSDomain", on_delete=models.CASCADE),
|
||||||
to='domains.DNSDomain', on_delete=models.CASCADE),
|
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='dnssupermaster',
|
name="dnssupermaster",
|
||||||
unique_together=set([('ip', 'nameserver')]),
|
unique_together=set([("ip", "nameserver")]),
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='dnstsigkey',
|
name="dnstsigkey",
|
||||||
unique_together=set([('name', 'algorithm')]),
|
unique_together=set([("name", "algorithm")]),
|
||||||
),
|
),
|
||||||
migrations.AlterIndexTogether(
|
migrations.AlterIndexTogether(
|
||||||
name='dnsrecord',
|
name="dnsrecord",
|
||||||
index_together=set([('name', 'recordtype')]),
|
index_together=set([("name", "recordtype")]),
|
||||||
),
|
),
|
||||||
migrations.AlterIndexTogether(
|
migrations.AlterIndexTogether(
|
||||||
name='dnscomment',
|
name="dnscomment",
|
||||||
index_together={('name', 'commenttype'), ('domain', 'modified_at')},
|
index_together={("name", "commenttype"), ("domain", "modified_at")},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,44 +1,87 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('domains', '0003_auto_20151105_2133'),
|
("domains", "0003_auto_20151105_2133"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name='dnscomment',
|
name="dnscomment",
|
||||||
options={'verbose_name': 'DNS comment', 'verbose_name_plural': 'DNS comments'},
|
options={
|
||||||
|
"verbose_name": "DNS comment",
|
||||||
|
"verbose_name_plural": "DNS comments",
|
||||||
|
},
|
||||||
),
|
),
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name='dnscryptokey',
|
name="dnscryptokey",
|
||||||
options={'verbose_name': 'DNS crypto key', 'verbose_name_plural': 'DNS crypto keys'},
|
options={
|
||||||
|
"verbose_name": "DNS crypto key",
|
||||||
|
"verbose_name_plural": "DNS crypto keys",
|
||||||
|
},
|
||||||
),
|
),
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name='dnsdomainmetadata',
|
name="dnsdomainmetadata",
|
||||||
options={'verbose_name': 'DNS domain metadata item', 'verbose_name_plural': 'DNS domain metadata items'},
|
options={
|
||||||
|
"verbose_name": "DNS domain metadata item",
|
||||||
|
"verbose_name_plural": "DNS domain metadata items",
|
||||||
|
},
|
||||||
),
|
),
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name='dnssupermaster',
|
name="dnssupermaster",
|
||||||
options={'verbose_name': 'DNS supermaster', 'verbose_name_plural': 'DNS supermasters'},
|
options={
|
||||||
|
"verbose_name": "DNS supermaster",
|
||||||
|
"verbose_name_plural": "DNS supermasters",
|
||||||
|
},
|
||||||
),
|
),
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name='dnstsigkey',
|
name="dnstsigkey",
|
||||||
options={'verbose_name': 'DNS TSIG key', 'verbose_name_plural': 'DNS TSIG keys'},
|
options={
|
||||||
|
"verbose_name": "DNS TSIG key",
|
||||||
|
"verbose_name_plural": "DNS TSIG keys",
|
||||||
|
},
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='dnsdomainmetadata',
|
model_name="dnsdomainmetadata",
|
||||||
name='kind',
|
name="kind",
|
||||||
field=models.CharField(max_length=32, choices=[('ALLOW-DNSUPDATE-FROM', 'ALLOW-DNSUPDATE-FROM'), ('ALSO-NOTIFY', 'ALSO-NOTIFY'), ('AXFR-MASTER-TSIG', 'AXFR-MASTER-TSIG'), ('AXFR-SOURCE', 'AXFR-SOURCE'), ('FORWARD-DNSUPDATE', 'FORWARD-DNSUPDATE'), ('GSS-ACCEPTOR-PRINCIPAL', 'GSS-ACCEPTOR-PRINCIPAL'), ('GSS-ALLOW-AXFR-PRINCIPAL', 'GSS-ALLOW-AXFR-PRINCIPAL'), ('LUA-AXFR-SCRIPT', 'LUA-AXFR-SCRIPT'), ('NSEC3NARROW', 'NSEC3NARROW'), ('NSEC3PARAM', 'NSEC3PARAM'), ('PRESIGNED', 'PRESIGNED'), ('PUBLISH_CDNSKEY', 'PUBLISH_CDNSKEY'), ('PUBLISH_CDS', 'PUBLISH_CDS'), ('SOA-EDIT', 'SOA-EDIT'), ('SOA-EDIT-DNSUPDATE', 'SOA-EDIT-DNSUPDATE'), ('TSIG-ALLOW-AXFR', 'TSIG-ALLOW-AXFR'), ('TSIG-ALLOW-DNSUPDATE', 'TSIG-ALLOW-DNSUPDATE')]),
|
field=models.CharField(
|
||||||
|
max_length=32,
|
||||||
|
choices=[
|
||||||
|
("ALLOW-DNSUPDATE-FROM", "ALLOW-DNSUPDATE-FROM"),
|
||||||
|
("ALSO-NOTIFY", "ALSO-NOTIFY"),
|
||||||
|
("AXFR-MASTER-TSIG", "AXFR-MASTER-TSIG"),
|
||||||
|
("AXFR-SOURCE", "AXFR-SOURCE"),
|
||||||
|
("FORWARD-DNSUPDATE", "FORWARD-DNSUPDATE"),
|
||||||
|
("GSS-ACCEPTOR-PRINCIPAL", "GSS-ACCEPTOR-PRINCIPAL"),
|
||||||
|
("GSS-ALLOW-AXFR-PRINCIPAL", "GSS-ALLOW-AXFR-PRINCIPAL"),
|
||||||
|
("LUA-AXFR-SCRIPT", "LUA-AXFR-SCRIPT"),
|
||||||
|
("NSEC3NARROW", "NSEC3NARROW"),
|
||||||
|
("NSEC3PARAM", "NSEC3PARAM"),
|
||||||
|
("PRESIGNED", "PRESIGNED"),
|
||||||
|
("PUBLISH_CDNSKEY", "PUBLISH_CDNSKEY"),
|
||||||
|
("PUBLISH_CDS", "PUBLISH_CDS"),
|
||||||
|
("SOA-EDIT", "SOA-EDIT"),
|
||||||
|
("SOA-EDIT-DNSUPDATE", "SOA-EDIT-DNSUPDATE"),
|
||||||
|
("TSIG-ALLOW-AXFR", "TSIG-ALLOW-AXFR"),
|
||||||
|
("TSIG-ALLOW-DNSUPDATE", "TSIG-ALLOW-DNSUPDATE"),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='dnstsigkey',
|
model_name="dnstsigkey",
|
||||||
name='algorithm',
|
name="algorithm",
|
||||||
field=models.CharField(max_length=50, choices=[('hmac-md5', 'HMAC MD5'), ('hmac-sha1', 'HMAC SHA1'), ('hmac-sha224', 'HMAC SHA224'), ('hmac-sha256', 'HMAC SHA256'), ('hmac-sha384', 'HMAC SHA384'), ('hmac-sha512', 'HMAC SHA512')]),
|
field=models.CharField(
|
||||||
|
max_length=50,
|
||||||
|
choices=[
|
||||||
|
("hmac-md5", "HMAC MD5"),
|
||||||
|
("hmac-sha1", "HMAC SHA1"),
|
||||||
|
("hmac-sha224", "HMAC SHA224"),
|
||||||
|
("hmac-sha256", "HMAC SHA256"),
|
||||||
|
("hmac-sha384", "HMAC SHA384"),
|
||||||
|
("hmac-sha512", "HMAC SHA512"),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
# Generated by Django 3.2.18 on 2023-04-15 09:53
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('domains', '0004_auto_20151107_1708'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterIndexTogether(
|
||||||
|
name='dnscomment',
|
||||||
|
index_together=None,
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='dnscomment',
|
||||||
|
name='customer',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='dnscomment',
|
||||||
|
name='domain',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='dnscryptokey',
|
||||||
|
name='domain',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='dnsdomain',
|
||||||
|
name='customer',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='dnsdomainmetadata',
|
||||||
|
name='domain',
|
||||||
|
),
|
||||||
|
migrations.AlterIndexTogether(
|
||||||
|
name='dnsrecord',
|
||||||
|
index_together=None,
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='dnsrecord',
|
||||||
|
name='domain',
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='dnssupermaster',
|
||||||
|
unique_together=None,
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='dnssupermaster',
|
||||||
|
name='customer',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='DNSTSIGKey',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='DNSComment',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='DNSCryptoKey',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='DNSDomain',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='DNSDomainMetadata',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='DNSRecord',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='DNSSupermaster',
|
||||||
|
),
|
||||||
|
]
|
|
@ -2,52 +2,12 @@
|
||||||
This module contains models related to domain names.
|
This module contains models related to domain names.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from django.db import models, transaction
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
from django.db import models, transaction
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from model_utils.models import TimeStampedModel
|
from model_utils.models import TimeStampedModel
|
||||||
from model_utils import Choices
|
|
||||||
|
|
||||||
|
|
||||||
DNS_DOMAIN_TYPES = Choices(
|
|
||||||
('MASTER', _('Master')),
|
|
||||||
('SLAVE', _('Slave')),
|
|
||||||
('NATIVE', _('Native')),
|
|
||||||
)
|
|
||||||
|
|
||||||
# see https://doc.powerdns.com/md/authoritative/domainmetadata/
|
|
||||||
DNS_DOMAIN_METADATA_KINDS = Choices(
|
|
||||||
'ALLOW-DNSUPDATE-FROM',
|
|
||||||
'ALSO-NOTIFY',
|
|
||||||
'AXFR-MASTER-TSIG',
|
|
||||||
'AXFR-SOURCE',
|
|
||||||
'FORWARD-DNSUPDATE',
|
|
||||||
'GSS-ACCEPTOR-PRINCIPAL',
|
|
||||||
'GSS-ALLOW-AXFR-PRINCIPAL',
|
|
||||||
'LUA-AXFR-SCRIPT',
|
|
||||||
'NSEC3NARROW',
|
|
||||||
'NSEC3PARAM',
|
|
||||||
'PRESIGNED',
|
|
||||||
'PUBLISH_CDNSKEY',
|
|
||||||
'PUBLISH_CDS',
|
|
||||||
'SOA-EDIT',
|
|
||||||
'SOA-EDIT-DNSUPDATE',
|
|
||||||
'TSIG-ALLOW-AXFR',
|
|
||||||
'TSIG-ALLOW-DNSUPDATE',
|
|
||||||
)
|
|
||||||
|
|
||||||
DNS_TSIG_KEY_ALGORITHMS = Choices(
|
|
||||||
('hmac-md5', _('HMAC MD5')),
|
|
||||||
('hmac-sha1', _('HMAC SHA1')),
|
|
||||||
('hmac-sha224', _('HMAC SHA224')),
|
|
||||||
('hmac-sha256', _('HMAC SHA256')),
|
|
||||||
('hmac-sha384', _('HMAC SHA384')),
|
|
||||||
('hmac-sha512', _('HMAC SHA512')),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class DomainBase(TimeStampedModel):
|
class DomainBase(TimeStampedModel):
|
||||||
|
@ -55,16 +15,20 @@ class DomainBase(TimeStampedModel):
|
||||||
This is the base model for domains.
|
This is the base model for domains.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
domain = models.CharField(_('domain name'), max_length=255, unique=True)
|
|
||||||
|
domain = models.CharField(_("domain name"), max_length=255, unique=True)
|
||||||
customer = models.ForeignKey(
|
customer = models.ForeignKey(
|
||||||
settings.AUTH_USER_MODEL, verbose_name=_('customer'), blank=True,
|
settings.AUTH_USER_MODEL,
|
||||||
null=True, on_delete=models.CASCADE)
|
verbose_name=_("customer"),
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class MailDomain(DomainBase):
|
class MailDomain(DomainBase):
|
||||||
"""
|
"""
|
||||||
This is the model for mail domains. Mail domains are used to configure the
|
This is the model for mail domains. Mail domains are used to configure the
|
||||||
|
@ -72,9 +36,10 @@ class MailDomain(DomainBase):
|
||||||
domains.
|
domains.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
class Meta:
|
|
||||||
verbose_name = _('Mail domain')
|
class Meta(DomainBase.Meta):
|
||||||
verbose_name_plural = _('Mail domains')
|
verbose_name = _("Mail domain")
|
||||||
|
verbose_name_plural = _("Mail domains")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.domain
|
return self.domain
|
||||||
|
@ -85,6 +50,7 @@ class MailDomain(DomainBase):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return self.mailaddress_set.all()
|
return self.mailaddress_set.all()
|
||||||
|
|
||||||
mailaddresses = property(get_mailaddresses)
|
mailaddresses = property(get_mailaddresses)
|
||||||
|
|
||||||
|
|
||||||
|
@ -93,339 +59,47 @@ class HostingDomainManager(models.Manager):
|
||||||
Default Manager for :py:class:`HostingDomain`.
|
Default Manager for :py:class:`HostingDomain`.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def create_for_hosting_package(
|
def create_for_hosting_package(self, hosting_package, domain, commit, **kwargs):
|
||||||
self, hosting_package, domain, commit, **kwargs
|
|
||||||
):
|
|
||||||
from hostingpackages.models import CustomerHostingPackageDomain
|
from hostingpackages.models import CustomerHostingPackageDomain
|
||||||
|
|
||||||
hostingdomain = self.create(
|
hostingdomain = self.create(
|
||||||
customer=hosting_package.customer, domain=domain, **kwargs)
|
customer=hosting_package.customer, domain=domain, **kwargs
|
||||||
|
)
|
||||||
hostingdomain.maildomain = MailDomain.objects.create(
|
hostingdomain.maildomain = MailDomain.objects.create(
|
||||||
customer=hosting_package.customer, domain=domain)
|
customer=hosting_package.customer, domain=domain
|
||||||
|
)
|
||||||
custdomain = CustomerHostingPackageDomain.objects.create(
|
custdomain = CustomerHostingPackageDomain.objects.create(
|
||||||
hosting_package=hosting_package, domain=hostingdomain)
|
hosting_package=hosting_package, domain=hostingdomain
|
||||||
|
)
|
||||||
if commit:
|
if commit:
|
||||||
hostingdomain.save()
|
hostingdomain.save()
|
||||||
custdomain.save()
|
custdomain.save()
|
||||||
return hostingdomain
|
return hostingdomain
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class HostingDomain(DomainBase):
|
class HostingDomain(DomainBase):
|
||||||
"""
|
"""
|
||||||
This is the model for hosting domains. A hosting domain is linked to a
|
This is the model for hosting domains. A hosting domain is linked to a
|
||||||
customer hosting account.
|
customer hosting account.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
maildomain = models.OneToOneField(
|
maildomain = models.OneToOneField(
|
||||||
MailDomain, verbose_name=_('mail domain'), blank=True, null=True,
|
MailDomain,
|
||||||
help_text=_('assigned mail domain for this domain'),
|
verbose_name=_("mail domain"),
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
help_text=_("assigned mail domain for this domain"),
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = HostingDomainManager()
|
objects = HostingDomainManager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('Hosting domain')
|
verbose_name = _("Hosting domain")
|
||||||
verbose_name_plural = _('Hosting domains')
|
verbose_name_plural = _("Hosting domains")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.domain
|
return self.domain
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class DNSDomain(DomainBase):
|
|
||||||
"""
|
|
||||||
This model represents a DNS zone. The model is similar to the domain table
|
|
||||||
in the PowerDNS schema specified in
|
|
||||||
https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/.
|
|
||||||
|
|
||||||
.. code-block:: sql
|
|
||||||
|
|
||||||
CREATE TABLE domains (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
name VARCHAR(255) NOT NULL,
|
|
||||||
master VARCHAR(128) DEFAULT NULL,
|
|
||||||
last_check INT DEFAULT NULL,
|
|
||||||
type VARCHAR(6) NOT NULL,
|
|
||||||
notified_serial INT DEFAULT NULL,
|
|
||||||
account VARCHAR(40) DEFAULT NULL,
|
|
||||||
CONSTRAINT c_lowercase_name CHECK (
|
|
||||||
((name)::TEXT = LOWER((name)::TEXT)))
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX name_index ON domains(name);
|
|
||||||
|
|
||||||
"""
|
|
||||||
# name is represented by domain
|
|
||||||
master = models.CharField(max_length=128, blank=True, null=True)
|
|
||||||
last_check = models.IntegerField(null=True)
|
|
||||||
domaintype = models.CharField(
|
|
||||||
max_length=6, choices=DNS_DOMAIN_TYPES, db_column='type')
|
|
||||||
notified_serial = models.IntegerField(null=True)
|
|
||||||
# account is represented by customer_id
|
|
||||||
# check constraint is added via RunSQL in migration
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('DNS domain')
|
|
||||||
verbose_name_plural = _('DNS domains')
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.domain
|
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class DNSRecord(models.Model):
|
|
||||||
"""
|
|
||||||
This model represents a DNS record. The model is similar to the record
|
|
||||||
table in the PowerDNS schema specified in
|
|
||||||
https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/.
|
|
||||||
|
|
||||||
.. code-block:: sql
|
|
||||||
|
|
||||||
CREATE TABLE records (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
domain_id INT DEFAULT NULL,
|
|
||||||
name VARCHAR(255) DEFAULT NULL,
|
|
||||||
type VARCHAR(10) DEFAULT NULL,
|
|
||||||
content VARCHAR(65535) DEFAULT NULL,
|
|
||||||
ttl INT DEFAULT NULL,
|
|
||||||
prio INT DEFAULT NULL,
|
|
||||||
change_date INT DEFAULT NULL,
|
|
||||||
disabled BOOL DEFAULT 'f',
|
|
||||||
ordername VARCHAR(255),
|
|
||||||
auth BOOL DEFAULT 't',
|
|
||||||
CONSTRAINT domain_exists
|
|
||||||
FOREIGN KEY(domain_id) REFERENCES domains(id)
|
|
||||||
ON DELETE CASCADE,
|
|
||||||
CONSTRAINT c_lowercase_name CHECK (
|
|
||||||
((name)::TEXT = LOWER((name)::TEXT)))
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX rec_name_index ON records(name);
|
|
||||||
CREATE INDEX nametype_index ON records(name,type);
|
|
||||||
CREATE INDEX domain_id ON records(domain_id);
|
|
||||||
CREATE INDEX recordorder ON records (
|
|
||||||
domain_id, ordername text_pattern_ops);
|
|
||||||
|
|
||||||
"""
|
|
||||||
domain = models.ForeignKey('DNSDomain', on_delete=models.CASCADE)
|
|
||||||
name = models.CharField(
|
|
||||||
max_length=255, blank=True, null=True, db_index=True)
|
|
||||||
recordtype = models.CharField(
|
|
||||||
max_length=10, blank=True, null=True, db_column='type')
|
|
||||||
content = models.CharField(max_length=65535, blank=True, null=True)
|
|
||||||
ttl = models.IntegerField(null=True)
|
|
||||||
prio = models.IntegerField(null=True)
|
|
||||||
change_date = models.IntegerField(null=True)
|
|
||||||
disabled = models.BooleanField(default=False)
|
|
||||||
ordername = models.CharField(max_length=255)
|
|
||||||
auth = models.BooleanField(default=True)
|
|
||||||
# check constraint and index recordorder are added via RunSQL in migration
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('DNS record')
|
|
||||||
verbose_name_plural = _('DNS records')
|
|
||||||
index_together = [
|
|
||||||
['name', 'recordtype']
|
|
||||||
]
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "{name} IN {type} {content}".format(
|
|
||||||
name=self.name, type=self.recordtype, content=self.content)
|
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class DNSSupermaster(models.Model):
|
|
||||||
"""
|
|
||||||
This model represents the supermasters table in the PowerDNS schema
|
|
||||||
specified in
|
|
||||||
https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/.
|
|
||||||
|
|
||||||
.. code-block:: sql
|
|
||||||
|
|
||||||
CREATE TABLE supermasters (
|
|
||||||
ip INET NOT NULL,
|
|
||||||
nameserver VARCHAR(255) NOT NULL,
|
|
||||||
account VARCHAR(40) NOT NULL,
|
|
||||||
PRIMARY KEY(ip, nameserver)
|
|
||||||
);
|
|
||||||
|
|
||||||
"""
|
|
||||||
ip = models.GenericIPAddressField()
|
|
||||||
nameserver = models.CharField(max_length=255)
|
|
||||||
# account is replaced by customer
|
|
||||||
customer = models.ForeignKey(
|
|
||||||
settings.AUTH_USER_MODEL, verbose_name=_('customer'),
|
|
||||||
on_delete=models.CASCADE)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('DNS supermaster')
|
|
||||||
verbose_name_plural = _('DNS supermasters')
|
|
||||||
unique_together = (
|
|
||||||
('ip', 'nameserver')
|
|
||||||
)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "{ip} {nameserver}".format(
|
|
||||||
ip=self.ip, nameserver=self.nameserver)
|
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class DNSComment(models.Model):
|
|
||||||
"""
|
|
||||||
This model represents the comments table in the PowerDNS schema specified
|
|
||||||
in https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/. The
|
|
||||||
comments table is used to store user comments related to individual DNS
|
|
||||||
records.
|
|
||||||
|
|
||||||
.. code-block:: sql
|
|
||||||
|
|
||||||
CREATE TABLE comments (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
domain_id INT NOT NULL,
|
|
||||||
name VARCHAR(255) NOT NULL,
|
|
||||||
type VARCHAR(10) NOT NULL,
|
|
||||||
modified_at INT NOT NULL,
|
|
||||||
account VARCHAR(40) DEFAULT NULL,
|
|
||||||
comment VARCHAR(65535) NOT NULL,
|
|
||||||
CONSTRAINT domain_exists
|
|
||||||
FOREIGN KEY(domain_id) REFERENCES domains(id)
|
|
||||||
ON DELETE CASCADE,
|
|
||||||
CONSTRAINT c_lowercase_name CHECK (
|
|
||||||
((name)::TEXT = LOWER((name)::TEXT)))
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX comments_domain_id_idx ON comments (domain_id);
|
|
||||||
CREATE INDEX comments_name_type_idx ON comments (name, type);
|
|
||||||
CREATE INDEX comments_order_idx ON comments (domain_id, modified_at);
|
|
||||||
|
|
||||||
"""
|
|
||||||
domain = models.ForeignKey('DNSDomain', on_delete=models.CASCADE)
|
|
||||||
name = models.CharField(max_length=255)
|
|
||||||
commenttype = models.CharField(max_length=10, db_column='type')
|
|
||||||
modified_at = models.IntegerField()
|
|
||||||
# account is replaced by customer
|
|
||||||
customer = models.ForeignKey(
|
|
||||||
settings.AUTH_USER_MODEL, verbose_name=_('customer'),
|
|
||||||
on_delete=models.CASCADE)
|
|
||||||
comment = models.CharField(max_length=65535)
|
|
||||||
# check constraint is added via RunSQL in migration
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('DNS comment')
|
|
||||||
verbose_name_plural = _('DNS comments')
|
|
||||||
index_together = [
|
|
||||||
['name', 'commenttype'],
|
|
||||||
['domain', 'modified_at']
|
|
||||||
]
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "{name} IN {type}: {comment}".format(
|
|
||||||
name=self.name, type=self.commenttype, comment=self.comment)
|
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class DNSDomainMetadata(models.Model):
|
|
||||||
"""
|
|
||||||
This model represents the domainmetadata table in the PowerDNS schema
|
|
||||||
specified in
|
|
||||||
https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/.
|
|
||||||
The domainmetadata table is used to store domain meta data as described in
|
|
||||||
https://doc.powerdns.com/md/authoritative/domainmetadata/.
|
|
||||||
|
|
||||||
.. code-block:: sql
|
|
||||||
|
|
||||||
CREATE TABLE domainmetadata (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
domain_id INT REFERENCES domains(id) ON DELETE CASCADE,
|
|
||||||
kind VARCHAR(32),
|
|
||||||
content TEXT
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX domainidmetaindex ON domainmetadata(domain_id);
|
|
||||||
|
|
||||||
"""
|
|
||||||
domain = models.ForeignKey('DNSDomain', on_delete=models.CASCADE)
|
|
||||||
kind = models.CharField(max_length=32, choices=DNS_DOMAIN_METADATA_KINDS)
|
|
||||||
content = models.TextField()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('DNS domain metadata item')
|
|
||||||
verbose_name_plural = _('DNS domain metadata items')
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "{domain} {kind} {content}".format(
|
|
||||||
domain=self.domain.domain, kind=self.kind, content=self.content)
|
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class DNSCryptoKey(models.Model):
|
|
||||||
"""
|
|
||||||
This model represents the cryptokeys table in the PowerDNS schema
|
|
||||||
specified in
|
|
||||||
https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/.
|
|
||||||
|
|
||||||
.. code-block:: sql
|
|
||||||
|
|
||||||
CREATE TABLE cryptokeys (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
domain_id INT REFERENCES domains(id) ON DELETE CASCADE,
|
|
||||||
flags INT NOT NULL,
|
|
||||||
active BOOL,
|
|
||||||
content TEXT
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX domainidindex ON cryptokeys(domain_id);
|
|
||||||
|
|
||||||
"""
|
|
||||||
domain = models.ForeignKey('DNSDomain', on_delete=models.CASCADE)
|
|
||||||
flags = models.IntegerField()
|
|
||||||
active = models.BooleanField(default=True)
|
|
||||||
content = models.TextField()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('DNS crypto key')
|
|
||||||
verbose_name_plural = _('DNS crypto keys')
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "{domain} {content}".format(
|
|
||||||
domain=self.domain.domain, content=self.content)
|
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class DNSTSIGKey(models.Model):
|
|
||||||
"""
|
|
||||||
This model represents the tsigkeys table in the PowerDNS schema specified
|
|
||||||
in https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/.
|
|
||||||
|
|
||||||
.. code-block:: sql
|
|
||||||
|
|
||||||
CREATE TABLE tsigkeys (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
name VARCHAR(255),
|
|
||||||
algorithm VARCHAR(50),
|
|
||||||
secret VARCHAR(255),
|
|
||||||
CONSTRAINT c_lowercase_name CHECK (
|
|
||||||
((name)::TEXT = LOWER((name)::TEXT)))
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX namealgoindex ON tsigkeys(name, algorithm);
|
|
||||||
|
|
||||||
"""
|
|
||||||
name = models.CharField(max_length=255)
|
|
||||||
algorithm = models.CharField(
|
|
||||||
max_length=50, choices=DNS_TSIG_KEY_ALGORITHMS)
|
|
||||||
secret = models.CharField(max_length=255)
|
|
||||||
# check constraint is added via RunSQL in migration
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('DNS TSIG key')
|
|
||||||
verbose_name_plural = _('DNS TSIG keys')
|
|
||||||
unique_together = [
|
|
||||||
['name', 'algorithm']
|
|
||||||
]
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "{name} {algorithm} XXXX".format(
|
|
||||||
name=self.name, algorithm=self.algorithm)
|
|
||||||
|
|
|
@ -7,9 +7,9 @@ from unittest.mock import MagicMock, Mock, patch
|
||||||
from django.forms import ValidationError
|
from django.forms import ValidationError
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from domains.forms import relative_domain_validator, CreateHostingDomainForm
|
from domains.forms import CreateHostingDomainForm, relative_domain_validator
|
||||||
|
|
||||||
|
|
||||||
class RelativeDomainValidatorTest(TestCase):
|
class RelativeDomainValidatorTest(TestCase):
|
||||||
|
|
|
@ -7,19 +7,9 @@ from unittest.mock import patch
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
from domains.models import (
|
from domains.models import HostingDomain, MailDomain
|
||||||
DNSComment,
|
|
||||||
DNSCryptoKey,
|
|
||||||
DNSDomain,
|
|
||||||
DNSDomainMetadata,
|
|
||||||
DNSRecord,
|
|
||||||
DNSSupermaster,
|
|
||||||
DNSTSIGKey,
|
|
||||||
HostingDomain,
|
|
||||||
MailDomain,
|
|
||||||
)
|
|
||||||
from hostingpackages.models import CustomerHostingPackage, HostingPackageTemplate
|
|
||||||
|
|
||||||
|
from hostingpackages.models import CustomerHostingPackage, HostingPackageTemplate
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
|
@ -77,49 +67,3 @@ class HostingDomainTest(TestCase):
|
||||||
def test___str__(self):
|
def test___str__(self):
|
||||||
hostingdomain = HostingDomain(domain="test")
|
hostingdomain = HostingDomain(domain="test")
|
||||||
self.assertEqual(str(hostingdomain), "test")
|
self.assertEqual(str(hostingdomain), "test")
|
||||||
|
|
||||||
|
|
||||||
class DNSDomainTest(TestCase):
|
|
||||||
def test___str__(self):
|
|
||||||
dnsdomain = DNSDomain(domain="test")
|
|
||||||
self.assertEqual(str(dnsdomain), "test")
|
|
||||||
|
|
||||||
|
|
||||||
class DNSRecordTest(TestCase):
|
|
||||||
def test___str__(self):
|
|
||||||
dnsrecord = DNSRecord(name="localhost", recordtype="A", content="127.0.0.1")
|
|
||||||
self.assertEqual(str(dnsrecord), "localhost IN A 127.0.0.1")
|
|
||||||
|
|
||||||
|
|
||||||
class DNSSupermasterTest(TestCase):
|
|
||||||
def test___str__(self):
|
|
||||||
dnssupermaster = DNSSupermaster(ip="127.0.0.1", nameserver="dns.example.org")
|
|
||||||
self.assertEqual(str(dnssupermaster), "127.0.0.1 dns.example.org")
|
|
||||||
|
|
||||||
|
|
||||||
class DNSCommentTest(TestCase):
|
|
||||||
def test___str__(self):
|
|
||||||
dnscomment = DNSComment(name="localhost", commenttype="A", comment="good stuff")
|
|
||||||
self.assertEqual(str(dnscomment), "localhost IN A: good stuff")
|
|
||||||
|
|
||||||
|
|
||||||
class DNSDomainMetadataTest(TestCase):
|
|
||||||
def test___str__(self):
|
|
||||||
dnsdomain = DNSDomain(domain="test")
|
|
||||||
dnsdomainmetadata = DNSDomainMetadata(
|
|
||||||
domain=dnsdomain, kind="SOA-EDIT", content="INCEPTION"
|
|
||||||
)
|
|
||||||
self.assertEqual(str(dnsdomainmetadata), "test SOA-EDIT INCEPTION")
|
|
||||||
|
|
||||||
|
|
||||||
class DNSCryptoKeyTest(TestCase):
|
|
||||||
def test___str__(self):
|
|
||||||
dnsdomain = DNSDomain(domain="test")
|
|
||||||
dnscryptokey = DNSCryptoKey(domain=dnsdomain, content="testvalue")
|
|
||||||
self.assertEqual(str(dnscryptokey), "test testvalue")
|
|
||||||
|
|
||||||
|
|
||||||
class DNSTSIGKeyTest(TestCase):
|
|
||||||
def test___str__(self):
|
|
||||||
dnstsigkey = DNSTSIGKey(name="testkey", algorithm="hmac-md5", secret="dummykey")
|
|
||||||
self.assertEqual(str(dnstsigkey), "testkey hmac-md5 XXXX")
|
|
||||||
|
|
|
@ -2,14 +2,16 @@
|
||||||
This module defines the URL patterns for domain related views.
|
This module defines the URL patterns for domain related views.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from django.conf.urls import url
|
from django.urls import re_path
|
||||||
|
|
||||||
from .views import CreateHostingDomain
|
from .views import CreateHostingDomain
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^(?P<package>\d+)/create$', CreateHostingDomain.as_view(),
|
re_path(
|
||||||
name='create_hosting_domain'),
|
r"^(?P<package>\d+)/create$",
|
||||||
|
CreateHostingDomain.as_view(),
|
||||||
|
name="create_hosting_domain",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -2,20 +2,21 @@
|
||||||
This module defines views related to domains.
|
This module defines views related to domains.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from braces.views import StaffuserRequiredMixin
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
|
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||||
from django.shortcuts import get_object_or_404, redirect
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import gettext as _
|
||||||
from django.views.generic.edit import CreateView
|
from django.views.generic.edit import CreateView
|
||||||
|
|
||||||
from hostingpackages.models import CustomerHostingPackage
|
from hostingpackages.models import CustomerHostingPackage
|
||||||
|
|
||||||
from .forms import CreateHostingDomainForm
|
from .forms import CreateHostingDomainForm
|
||||||
from .models import HostingDomain
|
from .models import HostingDomain
|
||||||
|
|
||||||
|
|
||||||
class CreateHostingDomain(StaffuserRequiredMixin, CreateView):
|
class CreateHostingDomain(PermissionRequiredMixin, CreateView):
|
||||||
"""
|
"""
|
||||||
This view is used for creating a new HostingDomain instance for an existing
|
This view is used for creating a new HostingDomain instance for an existing
|
||||||
hosting package.
|
hosting package.
|
||||||
|
@ -23,6 +24,7 @@ class CreateHostingDomain(StaffuserRequiredMixin, CreateView):
|
||||||
|
|
||||||
model = HostingDomain
|
model = HostingDomain
|
||||||
raise_exception = True
|
raise_exception = True
|
||||||
|
permission_required = 'domains.add_hostingdomain'
|
||||||
template_name_suffix = "_create"
|
template_name_suffix = "_create"
|
||||||
form_class = CreateHostingDomainForm
|
form_class = CreateHostingDomainForm
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# import celery_app to initialize it
|
# import celery_app to initialize it
|
||||||
from gnuviechadmin.celery import app as celery_app # NOQA
|
from gnuviechadmin.celery import app as celery_app # NOQA
|
||||||
|
|
||||||
__version__ = '0.12.0'
|
__version__ = "0.15.1"
|
||||||
|
|
11
gnuviechadmin/gnuviechadmin/auth.py
Normal file
11
gnuviechadmin/gnuviechadmin/auth.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
from allauth.account.adapter import DefaultAccountAdapter
|
||||||
|
|
||||||
|
|
||||||
|
class NoNewUsersAccountAdapter(DefaultAccountAdapter):
|
||||||
|
"""
|
||||||
|
Adapter to disable allauth new signups
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def is_open_for_signup(self, request):
|
||||||
|
return False
|
|
@ -6,15 +6,15 @@ from celery import Celery
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE',
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gnuviechadmin.settings")
|
||||||
'gnuviechadmin.settings.production')
|
|
||||||
|
|
||||||
|
|
||||||
app = Celery('gnuviechadmin')
|
app = Celery("gnuviechadmin")
|
||||||
|
|
||||||
|
|
||||||
def get_installed_apps():
|
def get_installed_apps():
|
||||||
return settings.INSTALLED_APPS
|
return settings.INSTALLED_APPS
|
||||||
|
|
||||||
app.config_from_object('django.conf:settings')
|
|
||||||
|
app.config_from_object("django.conf:settings")
|
||||||
app.autodiscover_tasks(get_installed_apps)
|
app.autodiscover_tasks(get_installed_apps)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
This module provides context processor implementations for gnuviechadmin.
|
This module provides context processor implementations for gnuviechadmin.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -22,38 +22,42 @@ def navigation(request):
|
||||||
:rtype: dict
|
:rtype: dict
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if request.is_ajax():
|
if request.headers.get("x-requested-with") == "XMLHttpRequest":
|
||||||
return {}
|
return {}
|
||||||
context = {
|
context = {
|
||||||
'webmail_url': settings.GVA_LINK_WEBMAIL,
|
"webmail_url": settings.GVA_LINK_WEBMAIL,
|
||||||
'phpmyadmin_url': settings.GVA_LINK_PHPMYADMIN,
|
"phpmyadmin_url": settings.GVA_LINK_PHPMYADMIN,
|
||||||
'phppgadmin_url': settings.GVA_LINK_PHPPGADMIN,
|
"phppgadmin_url": settings.GVA_LINK_PHPPGADMIN,
|
||||||
'active_item': 'dashboard',
|
"active_item": "dashboard",
|
||||||
}
|
}
|
||||||
if request.resolver_match:
|
if request.resolver_match:
|
||||||
viewfunc = request.resolver_match.func
|
viewfunc = request.resolver_match.func
|
||||||
viewmodule = viewfunc.__module__
|
viewmodule = viewfunc.__module__
|
||||||
if viewmodule == 'contact_form.views':
|
if viewmodule == "contact_form.views":
|
||||||
context['active_item'] = 'contact'
|
context["active_item"] = "contact"
|
||||||
elif viewmodule in (
|
elif viewmodule in (
|
||||||
'hostingpackages.views', 'osusers.views', 'userdbs.views',
|
"hostingpackages.views",
|
||||||
'managemails.views', 'websites.views', 'domains.views',
|
"osusers.views",
|
||||||
|
"userdbs.views",
|
||||||
|
"managemails.views",
|
||||||
|
"websites.views",
|
||||||
|
"domains.views",
|
||||||
):
|
):
|
||||||
context['active_item'] = 'hostingpackage'
|
context["active_item"] = "hostingpackage"
|
||||||
elif viewmodule in (
|
elif viewmodule in ("allauth.account.views", "allauth.socialaccount.views"):
|
||||||
'allauth.account.views', 'allauth.socialaccount.views'
|
context["active_item"] = "account"
|
||||||
|
elif viewmodule == "django.contrib.flatpages.views" and request.path.endswith(
|
||||||
|
"/impressum/"
|
||||||
):
|
):
|
||||||
context['active_item'] = 'account'
|
context["active_item"] = "imprint"
|
||||||
elif (
|
elif not viewmodule.startswith("django.contrib.admin"):
|
||||||
viewmodule == 'django.contrib.flatpages.views' and
|
|
||||||
request.path.endswith('/impressum/')
|
|
||||||
):
|
|
||||||
context['active_item'] = 'imprint'
|
|
||||||
elif not viewmodule.startswith('django.contrib.admin'):
|
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
'no special handling for view %s in module %s, fallback to '
|
"no special handling for view %s in module %s, fallback to "
|
||||||
'default active menu item %s',
|
"default active menu item %s",
|
||||||
viewfunc.__name__, viewmodule, context['active_item'])
|
viewfunc.__name__,
|
||||||
|
viewmodule,
|
||||||
|
context["active_item"],
|
||||||
|
)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@ -64,6 +68,6 @@ def version_info(request):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
context = {
|
context = {
|
||||||
'gnuviechadmin_version': gvaversion,
|
"gnuviechadmin_version": gvaversion,
|
||||||
}
|
}
|
||||||
return context
|
return context
|
||||||
|
|
|
@ -6,11 +6,12 @@ Common settings and globals.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from os.path import abspath, basename, dirname, join, normpath
|
from os.path import abspath, basename, dirname, join, normpath
|
||||||
|
from environs import Env
|
||||||
|
|
||||||
from django.contrib.messages import constants as messages
|
from django.contrib.messages import constants as messages
|
||||||
|
|
||||||
from gvacommon.settings_utils import get_env_variable
|
env = Env()
|
||||||
|
env.read_env()
|
||||||
|
|
||||||
# ######### PATH CONFIGURATION
|
# ######### PATH CONFIGURATION
|
||||||
# Absolute filesystem path to the Django project directory:
|
# Absolute filesystem path to the Django project directory:
|
||||||
|
@ -19,15 +20,18 @@ DJANGO_ROOT = dirname(dirname(abspath(__file__)))
|
||||||
# Absolute filesystem path to the top-level project folder:
|
# Absolute filesystem path to the top-level project folder:
|
||||||
SITE_ROOT = dirname(DJANGO_ROOT)
|
SITE_ROOT = dirname(DJANGO_ROOT)
|
||||||
|
|
||||||
|
ROOT_DIR = dirname(DJANGO_ROOT)
|
||||||
|
|
||||||
# Site name:
|
# Site name:
|
||||||
SITE_NAME = basename(DJANGO_ROOT)
|
SITE_NAME = basename(DJANGO_ROOT)
|
||||||
|
|
||||||
# ######### END PATH CONFIGURATION
|
# ######### END PATH CONFIGURATION
|
||||||
|
|
||||||
|
GVA_ENVIRONMENT = env.str("GVA_ENVIRONMENT", default="prod")
|
||||||
|
|
||||||
# ######### DEBUG CONFIGURATION
|
# ######### DEBUG CONFIGURATION
|
||||||
# See: https://docs.djangoproject.com/en/dev/ref/settings/#debug
|
# See: https://docs.djangoproject.com/en/dev/ref/settings/#debug
|
||||||
DEBUG = False
|
DEBUG = GVA_ENVIRONMENT == "local"
|
||||||
# ######### END DEBUG CONFIGURATION
|
# ######### END DEBUG CONFIGURATION
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,8 +39,8 @@ DEBUG = False
|
||||||
# See: https://docs.djangoproject.com/en/dev/ref/settings/#admins
|
# See: https://docs.djangoproject.com/en/dev/ref/settings/#admins
|
||||||
ADMINS = (
|
ADMINS = (
|
||||||
(
|
(
|
||||||
get_env_variable("GVA_ADMIN_NAME", default="Admin"),
|
env.str("GVA_ADMIN_NAME", default="Admin"),
|
||||||
get_env_variable("GVA_ADMIN_EMAIL", default="admin@example.org"),
|
env.str("GVA_ADMIN_EMAIL", default="admin@example.org"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -48,15 +52,10 @@ MANAGERS = ADMINS
|
||||||
# ######### DATABASE CONFIGURATION
|
# ######### DATABASE CONFIGURATION
|
||||||
# See: https://docs.djangoproject.com/en/dev/ref/settings/#databases
|
# See: https://docs.djangoproject.com/en/dev/ref/settings/#databases
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
"default": {
|
"default": env.dj_db_url("GVA_DATABASE_URL"),
|
||||||
"ENGINE": "django.db.backends.postgresql",
|
|
||||||
"NAME": get_env_variable("GVA_PGSQL_DATABASE", default="gnuviechadmin"),
|
|
||||||
"USER": get_env_variable("GVA_PGSQL_USER", default="gnuviechadmin"),
|
|
||||||
"PASSWORD": get_env_variable("GVA_PGSQL_PASSWORD"),
|
|
||||||
"HOST": get_env_variable("GVA_PGSQL_HOSTNAME", default="db"),
|
|
||||||
"PORT": get_env_variable("GVA_PGSQL_PORT", int, default=5432),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
||||||
# ######### END DATABASE CONFIGURATION
|
# ######### END DATABASE CONFIGURATION
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,15 +68,12 @@ LANGUAGE_CODE = "en-us"
|
||||||
|
|
||||||
# See: https://docs.djangoproject.com/en/dev/ref/settings/#site-id
|
# See: https://docs.djangoproject.com/en/dev/ref/settings/#site-id
|
||||||
SITE_ID = 1
|
SITE_ID = 1
|
||||||
SITES_DOMAIN_NAME = get_env_variable("GVA_DOMAIN_NAME")
|
SITES_DOMAIN_NAME = env.str("GVA_DOMAIN_NAME")
|
||||||
SITES_SITE_NAME = get_env_variable("GVA_SITE_NAME")
|
SITES_SITE_NAME = env.str("GVA_SITE_NAME")
|
||||||
|
|
||||||
# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-i18n
|
# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-i18n
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
|
|
||||||
# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-l10n
|
|
||||||
USE_L10N = True
|
|
||||||
|
|
||||||
# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-tz
|
# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-tz
|
||||||
USE_TZ = True
|
USE_TZ = True
|
||||||
# ######### END GENERAL CONFIGURATION
|
# ######### END GENERAL CONFIGURATION
|
||||||
|
@ -85,7 +81,6 @@ USE_TZ = True
|
||||||
|
|
||||||
LOCALE_PATHS = (normpath(join(SITE_ROOT, "gnuviechadmin", "locale")),)
|
LOCALE_PATHS = (normpath(join(SITE_ROOT, "gnuviechadmin", "locale")),)
|
||||||
|
|
||||||
|
|
||||||
# ######### MEDIA CONFIGURATION
|
# ######### MEDIA CONFIGURATION
|
||||||
# See: https://docs.djangoproject.com/en/dev/ref/settings/#media-root
|
# See: https://docs.djangoproject.com/en/dev/ref/settings/#media-root
|
||||||
MEDIA_ROOT = normpath(join(SITE_ROOT, "media"))
|
MEDIA_ROOT = normpath(join(SITE_ROOT, "media"))
|
||||||
|
@ -112,7 +107,7 @@ STATICFILES_FINDERS = (
|
||||||
# ######### SECRET CONFIGURATION
|
# ######### SECRET CONFIGURATION
|
||||||
# See: https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
|
# See: https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
|
||||||
# Note: This key should only be used for development and testing.
|
# Note: This key should only be used for development and testing.
|
||||||
SECRET_KEY = get_env_variable("GVA_SITE_SECRET")
|
SECRET_KEY = env.str("GVA_SITE_SECRET")
|
||||||
# ######### END SECRET CONFIGURATION
|
# ######### END SECRET CONFIGURATION
|
||||||
|
|
||||||
|
|
||||||
|
@ -164,9 +159,9 @@ MIDDLEWARE = [
|
||||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||||
"django.middleware.csrf.CsrfViewMiddleware",
|
"django.middleware.csrf.CsrfViewMiddleware",
|
||||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||||
|
"allauth.account.middleware.AccountMiddleware",
|
||||||
"django.middleware.locale.LocaleMiddleware",
|
"django.middleware.locale.LocaleMiddleware",
|
||||||
"django.contrib.messages.middleware.MessageMiddleware",
|
"django.contrib.messages.middleware.MessageMiddleware",
|
||||||
# uncomment next line to enable translation to browser locale
|
|
||||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||||
]
|
]
|
||||||
# ######### END MIDDLEWARE CONFIGURATION
|
# ######### END MIDDLEWARE CONFIGURATION
|
||||||
|
@ -179,7 +174,6 @@ AUTHENTICATION_BACKENDS = (
|
||||||
"allauth.account.auth_backends.AuthenticationBackend",
|
"allauth.account.auth_backends.AuthenticationBackend",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# ######### URL CONFIGURATION
|
# ######### URL CONFIGURATION
|
||||||
# See: https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf
|
# See: https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf
|
||||||
ROOT_URLCONF = "%s.urls" % SITE_NAME
|
ROOT_URLCONF = "%s.urls" % SITE_NAME
|
||||||
|
@ -207,6 +201,10 @@ DJANGO_APPS = (
|
||||||
# Flatpages for about page
|
# Flatpages for about page
|
||||||
"django.contrib.flatpages",
|
"django.contrib.flatpages",
|
||||||
"crispy_forms",
|
"crispy_forms",
|
||||||
|
"crispy_bootstrap5",
|
||||||
|
"impersonate",
|
||||||
|
"rest_framework",
|
||||||
|
"rest_framework.authtoken",
|
||||||
)
|
)
|
||||||
|
|
||||||
ALLAUTH_APPS = (
|
ALLAUTH_APPS = (
|
||||||
|
@ -215,7 +213,6 @@ ALLAUTH_APPS = (
|
||||||
"allauth.socialaccount",
|
"allauth.socialaccount",
|
||||||
"allauth.socialaccount.providers.google",
|
"allauth.socialaccount.providers.google",
|
||||||
"allauth.socialaccount.providers.linkedin_oauth2",
|
"allauth.socialaccount.providers.linkedin_oauth2",
|
||||||
"allauth.socialaccount.providers.twitter",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Apps specific for this project go here.
|
# Apps specific for this project go here.
|
||||||
|
@ -233,6 +230,8 @@ LOCAL_APPS = (
|
||||||
"userdbs",
|
"userdbs",
|
||||||
"hostingpackages",
|
"hostingpackages",
|
||||||
"websites",
|
"websites",
|
||||||
|
"help",
|
||||||
|
"invoices",
|
||||||
"contact_form",
|
"contact_form",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -250,18 +249,38 @@ MESSAGE_TAGS = {
|
||||||
|
|
||||||
|
|
||||||
# ######### ALLAUTH CONFIGURATION
|
# ######### ALLAUTH CONFIGURATION
|
||||||
|
ACCOUNT_ADAPTER = "gnuviechadmin.auth.NoNewUsersAccountAdapter"
|
||||||
ACCOUNT_EMAIL_REQUIRED = True
|
ACCOUNT_EMAIL_REQUIRED = True
|
||||||
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
|
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
|
||||||
LOGIN_REDIRECT_URL = "/"
|
LOGIN_REDIRECT_URL = "/"
|
||||||
|
SOCIALACCOUNT_AUTO_SIGNUP = False
|
||||||
SOCIALACCOUNT_QUERY_EMAIL = True
|
SOCIALACCOUNT_QUERY_EMAIL = True
|
||||||
# ######### END ALLAUTH CONFIGURATION
|
# ######### END ALLAUTH CONFIGURATION
|
||||||
|
|
||||||
|
|
||||||
# ######### CRISPY FORMS CONFIGURATION
|
# ######### CRISPY FORMS CONFIGURATION
|
||||||
CRISPY_TEMPLATE_PACK = "bootstrap3"
|
CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
|
||||||
|
CRISPY_TEMPLATE_PACK = "bootstrap5"
|
||||||
# ######### END CRISPY_FORMS CONFIGURATION
|
# ######### END CRISPY_FORMS CONFIGURATION
|
||||||
|
|
||||||
|
|
||||||
|
# ######### REST FRAMEWORK CONFIGURATION
|
||||||
|
REST_FRAMEWORK = {
|
||||||
|
"DEFAULT_AUTHENTICATION_CLASSES": [
|
||||||
|
"rest_framework.authentication.BasicAuthentication",
|
||||||
|
"rest_framework.authentication.SessionAuthentication",
|
||||||
|
"rest_framework.authentication.TokenAuthentication",
|
||||||
|
],
|
||||||
|
"DEFAULT_RENDERER_CLASSES": [
|
||||||
|
"rest_framework.renderers.JSONRenderer",
|
||||||
|
],
|
||||||
|
"DEFAULT_PERMISSION_CLASSES": [
|
||||||
|
"rest_framework.permissions.IsAdminUser",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
# ######### END REST FRAMEWORK CONFIGURATION
|
||||||
|
|
||||||
|
|
||||||
# ######### LOGGING CONFIGURATION
|
# ######### LOGGING CONFIGURATION
|
||||||
# See: https://docs.djangoproject.com/en/dev/ref/settings/#logging
|
# See: https://docs.djangoproject.com/en/dev/ref/settings/#logging
|
||||||
# A sample logging configuration. The only tangible logging
|
# A sample logging configuration. The only tangible logging
|
||||||
|
@ -281,20 +300,45 @@ LOGGING = {
|
||||||
},
|
},
|
||||||
"filters": {"require_debug_false": {"()": "django.utils.log.RequireDebugFalse"}},
|
"filters": {"require_debug_false": {"()": "django.utils.log.RequireDebugFalse"}},
|
||||||
"handlers": {
|
"handlers": {
|
||||||
|
"console": {
|
||||||
|
"class": "logging.StreamHandler",
|
||||||
|
},
|
||||||
|
"logfile": {
|
||||||
|
"level": "INFO",
|
||||||
|
"class": "logging.FileHandler",
|
||||||
|
"filename": env.str("GVA_LOG_FILE", default="gva.log"),
|
||||||
|
"formatter": "verbose",
|
||||||
|
},
|
||||||
"mail_admins": {
|
"mail_admins": {
|
||||||
"level": "ERROR",
|
"level": "ERROR",
|
||||||
"filters": ["require_debug_false"],
|
"filters": ["require_debug_false"],
|
||||||
"class": "django.utils.log.AdminEmailHandler",
|
"class": "django.utils.log.AdminEmailHandler",
|
||||||
}
|
},
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"handlers": ["console"],
|
||||||
|
"level": "WARNING",
|
||||||
},
|
},
|
||||||
"loggers": {
|
"loggers": {
|
||||||
"django.request": {
|
"django.request": {
|
||||||
"handlers": ["mail_admins"],
|
"handlers": ["mail_admins"],
|
||||||
"level": "ERROR",
|
"level": "ERROR",
|
||||||
"propagate": True,
|
"propagate": True,
|
||||||
}
|
},
|
||||||
|
"django": {
|
||||||
|
"handlers": ["logfile"],
|
||||||
|
"level": "INFO",
|
||||||
|
"propagate": False,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for app in LOCAL_APPS:
|
||||||
|
LOGGING["loggers"][app] = {
|
||||||
|
"handlers": ["logfile"],
|
||||||
|
"level": "INFO",
|
||||||
|
"propagate": False,
|
||||||
|
}
|
||||||
# ######### END LOGGING CONFIGURATION
|
# ######### END LOGGING CONFIGURATION
|
||||||
|
|
||||||
|
|
||||||
|
@ -305,7 +349,7 @@ WSGI_APPLICATION = "%s.wsgi.application" % SITE_NAME
|
||||||
|
|
||||||
|
|
||||||
# ######### CELERY CONFIGURATION
|
# ######### CELERY CONFIGURATION
|
||||||
BROKER_URL = get_env_variable(
|
BROKER_URL = env.str(
|
||||||
"GVA_BROKER_URL", default="amqp://gnuviechadmin:gnuviechadmin@mq/gnuviechadmin"
|
"GVA_BROKER_URL", default="amqp://gnuviechadmin:gnuviechadmin@mq/gnuviechadmin"
|
||||||
)
|
)
|
||||||
BROKER_TRANSPORT_OPTIONS = {
|
BROKER_TRANSPORT_OPTIONS = {
|
||||||
|
@ -314,7 +358,7 @@ BROKER_TRANSPORT_OPTIONS = {
|
||||||
"interval_step": 0.2,
|
"interval_step": 0.2,
|
||||||
"interval_max": 0.2,
|
"interval_max": 0.2,
|
||||||
}
|
}
|
||||||
CELERY_RESULT_BACKEND = get_env_variable(
|
CELERY_RESULT_BACKEND = env.str(
|
||||||
"GVA_RESULTS_REDIS_URL", default="redis://:gnuviechadmin@redis:6379/0"
|
"GVA_RESULTS_REDIS_URL", default="redis://:gnuviechadmin@redis:6379/0"
|
||||||
)
|
)
|
||||||
CELERY_TASK_RESULT_EXPIRES = None
|
CELERY_TASK_RESULT_EXPIRES = None
|
||||||
|
@ -328,45 +372,50 @@ CELERY_RESULT_SERIALIZER = "json"
|
||||||
|
|
||||||
|
|
||||||
# ######### CUSTOM APP CONFIGURATION
|
# ######### CUSTOM APP CONFIGURATION
|
||||||
OSUSER_MINUID = get_env_variable("GVA_MIN_OS_UID", int, default=10000)
|
OSUSER_MINUID = env.int("GVA_MIN_OS_UID", default=10000)
|
||||||
OSUSER_MINGID = get_env_variable("GVA_MIN_OS_GID", int, default=10000)
|
OSUSER_MINGID = env.int("GVA_MIN_OS_GID", default=10000)
|
||||||
OSUSER_USERNAME_PREFIX = get_env_variable("GVA_OSUSER_PREFIX", default="usr")
|
OSUSER_USERNAME_PREFIX = env.str("GVA_OSUSER_PREFIX", default="usr")
|
||||||
OSUSER_HOME_BASEPATH = get_env_variable("GVA_OSUSER_HOME_BASEPATH", default="/home")
|
OSUSER_HOME_BASEPATH = env.str("GVA_OSUSER_HOME_BASEPATH", default="/home")
|
||||||
OSUSER_DEFAULT_SHELL = get_env_variable(
|
OSUSER_DEFAULT_SHELL = env.str("GVA_OSUSER_DEFAULT_SHELL", default="/usr/bin/rssh")
|
||||||
"GVA_OSUSER_DEFAULT_SHELL", default="/usr/bin/rssh"
|
|
||||||
)
|
|
||||||
OSUSER_SFTP_GROUP = "sftponly"
|
OSUSER_SFTP_GROUP = "sftponly"
|
||||||
OSUSER_SSH_GROUP = "sshusers"
|
OSUSER_SSH_GROUP = "sshusers"
|
||||||
OSUSER_DEFAULT_GROUPS = [OSUSER_SFTP_GROUP]
|
OSUSER_DEFAULT_GROUPS = [OSUSER_SFTP_GROUP]
|
||||||
OSUSER_UPLOAD_SERVER = get_env_variable("GVA_OSUSER_UPLOADSERVER", default="file")
|
OSUSER_UPLOAD_SERVER = env.str("GVA_OSUSER_UPLOADSERVER", default="file")
|
||||||
|
|
||||||
GVA_LINK_WEBMAIL = get_env_variable(
|
GVA_LINK_WEBMAIL = env.str("GVA_WEBMAIL_URL", default="https://webmail.example.org/")
|
||||||
"GVA_WEBMAIL_URL", default="https://webmail.example.org/"
|
GVA_LINK_PHPMYADMIN = env.str(
|
||||||
)
|
|
||||||
GVA_LINK_PHPMYADMIN = get_env_variable(
|
|
||||||
"GVA_PHPMYADMIN_URL", default="https://phpmyadmin.example.org/"
|
"GVA_PHPMYADMIN_URL", default="https://phpmyadmin.example.org/"
|
||||||
)
|
)
|
||||||
GVA_LINK_PHPPGADMIN = get_env_variable(
|
GVA_LINK_PHPPGADMIN = env.str(
|
||||||
"GVA_PHPPGADMIN_URL", default="https://phppgadmin.example.org/"
|
"GVA_PHPPGADMIN_URL", default="https://phppgadmin.example.org/"
|
||||||
)
|
)
|
||||||
# ######### END CUSTOM APP CONFIGURATION
|
# ######### END CUSTOM APP CONFIGURATION
|
||||||
|
|
||||||
GVA_ENVIRONMENT = get_env_variable("GVA_ENVIRONMENT", default="prod")
|
|
||||||
|
|
||||||
# ######### STATIC FILE CONFIGURATION
|
# ######### STATIC FILE CONFIGURATION
|
||||||
# See: https://docs.djangoproject.com/en/dev/ref/settings/#static-root
|
# See: https://docs.djangoproject.com/en/dev/ref/settings/#static-root
|
||||||
STATIC_ROOT = "/srv/gva/static/"
|
STATIC_ROOT = env.str("GVA_STATIC_PATH", default=normpath(join(ROOT_DIR, "static")))
|
||||||
|
|
||||||
|
|
||||||
def show_debug_toolbar(request):
|
def show_debug_toolbar(request):
|
||||||
return DEBUG and GVA_ENVIRONMENT == "local"
|
return DEBUG and GVA_ENVIRONMENT == "local"
|
||||||
|
|
||||||
|
|
||||||
if GVA_ENVIRONMENT == "local":
|
# ######### TOOLBAR CONFIGURATION
|
||||||
# ######### DEBUG CONFIGURATION
|
# See: http://django-debug-toolbar.readthedocs.org/en/latest/installation.html#explicit-setup # noqa
|
||||||
# See: https://docs.djangoproject.com/en/dev/ref/settings/#debug
|
INSTALLED_APPS += ("debug_toolbar",)
|
||||||
DEBUG = True
|
|
||||||
|
|
||||||
|
MIDDLEWARE += [
|
||||||
|
"impersonate.middleware.ImpersonateMiddleware",
|
||||||
|
"debug_toolbar.middleware.DebugToolbarMiddleware",
|
||||||
|
]
|
||||||
|
|
||||||
|
DEBUG_TOOLBAR_CONFIG = {
|
||||||
|
"SHOW_TOOLBAR_CALLBACK": "gnuviechadmin.settings.show_debug_toolbar"
|
||||||
|
}
|
||||||
|
# ######### END TOOLBAR CONFIGURATION
|
||||||
|
|
||||||
|
|
||||||
|
if GVA_ENVIRONMENT == "local":
|
||||||
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-debug
|
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-debug
|
||||||
TEMPLATES[0]["OPTIONS"]["debug"] = DEBUG
|
TEMPLATES[0]["OPTIONS"]["debug"] = DEBUG
|
||||||
# ######### END DEBUG CONFIGURATION
|
# ######### END DEBUG CONFIGURATION
|
||||||
|
@ -381,12 +430,6 @@ if GVA_ENVIRONMENT == "local":
|
||||||
CACHES = {"default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}}
|
CACHES = {"default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}}
|
||||||
# ######### END CACHE CONFIGURATION
|
# ######### END CACHE CONFIGURATION
|
||||||
|
|
||||||
# ######### TOOLBAR CONFIGURATION
|
|
||||||
# See: http://django-debug-toolbar.readthedocs.org/en/latest/installation.html#explicit-setup # noqa
|
|
||||||
INSTALLED_APPS += ("debug_toolbar",)
|
|
||||||
|
|
||||||
MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"]
|
|
||||||
|
|
||||||
LOGGING["handlers"].update(
|
LOGGING["handlers"].update(
|
||||||
{
|
{
|
||||||
"console": {
|
"console": {
|
||||||
|
@ -400,32 +443,10 @@ if GVA_ENVIRONMENT == "local":
|
||||||
dict(
|
dict(
|
||||||
[
|
[
|
||||||
(key, {"handlers": ["console"], "level": "DEBUG", "propagate": True})
|
(key, {"handlers": ["console"], "level": "DEBUG", "propagate": True})
|
||||||
for key in [
|
for key in LOCAL_APPS
|
||||||
"dashboard",
|
],
|
||||||
"domains",
|
|
||||||
"fileservertasks",
|
|
||||||
"gvacommon",
|
|
||||||
"gvawebcore",
|
|
||||||
"hostingpackages",
|
|
||||||
"ldaptasks",
|
|
||||||
"managemails",
|
|
||||||
"mysqltasks",
|
|
||||||
"osusers",
|
|
||||||
"pgsqltasks",
|
|
||||||
"taskresults",
|
|
||||||
"userdbs",
|
|
||||||
"websites",
|
|
||||||
]
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
DEBUG_TOOLBAR_PATCH_SETTINGS = False
|
|
||||||
DEBUG_TOOLBAR_CONFIG = {
|
|
||||||
"SHOW_TOOLBAR_CALLBACK": "gnuviechadmin.settings.show_debug_toolbar"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ######### END TOOLBAR CONFIGURATION
|
|
||||||
elif GVA_ENVIRONMENT == "test":
|
elif GVA_ENVIRONMENT == "test":
|
||||||
ALLOWED_HOSTS = ["localhost"]
|
ALLOWED_HOSTS = ["localhost"]
|
||||||
PASSWORD_HASHERS = ("django.contrib.auth.hashers.MD5PasswordHasher",)
|
PASSWORD_HASHERS = ("django.contrib.auth.hashers.MD5PasswordHasher",)
|
||||||
|
@ -442,25 +463,15 @@ elif GVA_ENVIRONMENT == "test":
|
||||||
dict(
|
dict(
|
||||||
[
|
[
|
||||||
(key, {"handlers": ["console"], "level": "ERROR", "propagate": True})
|
(key, {"handlers": ["console"], "level": "ERROR", "propagate": True})
|
||||||
for key in [
|
for key in LOCAL_APPS
|
||||||
"dashboard",
|
|
||||||
"domains",
|
|
||||||
"fileservertasks",
|
|
||||||
"gvacommon",
|
|
||||||
"gvawebcore",
|
|
||||||
"hostingpackages",
|
|
||||||
"ldaptasks",
|
|
||||||
"managemails",
|
|
||||||
"mysqltasks",
|
|
||||||
"osusers",
|
|
||||||
"pgsqltasks",
|
|
||||||
"taskresults",
|
|
||||||
"userdbs",
|
|
||||||
"websites",
|
|
||||||
]
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
LOGGING["loggers"]["django"] = {
|
||||||
|
"handlers": ["console"],
|
||||||
|
"level": "CRITICAL",
|
||||||
|
"propagate": True,
|
||||||
|
}
|
||||||
BROKER_URL = BROKER_URL + "_test"
|
BROKER_URL = BROKER_URL + "_test"
|
||||||
CELERY_RESULT_PERSISTENT = False
|
CELERY_RESULT_PERSISTENT = False
|
||||||
else:
|
else:
|
||||||
|
@ -477,12 +488,10 @@ else:
|
||||||
EMAIL_SUBJECT_PREFIX = "[%s] " % SITE_NAME
|
EMAIL_SUBJECT_PREFIX = "[%s] " % SITE_NAME
|
||||||
|
|
||||||
# See: https://docs.djangoproject.com/en/dev/ref/settings/#default-from-email
|
# See: https://docs.djangoproject.com/en/dev/ref/settings/#default-from-email
|
||||||
DEFAULT_FROM_EMAIL = get_env_variable(
|
DEFAULT_FROM_EMAIL = env.str("GVA_SITE_ADMINMAIL", default="admin@example.org")
|
||||||
"GVA_SITE_ADMINMAIL", default="admin@example.org"
|
|
||||||
)
|
|
||||||
|
|
||||||
# See: https://docs.djangoproject.com/en/dev/ref/settings/#server-email
|
# See: https://docs.djangoproject.com/en/dev/ref/settings/#server-email
|
||||||
SERVER_EMAIL = get_env_variable("GVA_SITE_ADMINMAIL", default="admin@example.org")
|
SERVER_EMAIL = env.str("GVA_SITE_ADMINMAIL", default="admin@example.org")
|
||||||
# ######### END EMAIL CONFIGURATION
|
# ######### END EMAIL CONFIGURATION
|
||||||
|
|
||||||
# ######### CACHE CONFIGURATION
|
# ######### CACHE CONFIGURATION
|
||||||
|
|
|
@ -18,13 +18,16 @@ from gnuviechadmin.context_processors import navigation
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
|
TEST_USER = "test"
|
||||||
|
TEST_PASSWORD = "secret"
|
||||||
|
|
||||||
|
|
||||||
class NavigationContextProcessorTest(TestCase):
|
class NavigationContextProcessorTest(TestCase):
|
||||||
|
|
||||||
EXPECTED_ITEMS = ("webmail_url", "phpmyadmin_url", "phppgadmin_url", "active_item")
|
EXPECTED_ITEMS = ("webmail_url", "phpmyadmin_url", "phppgadmin_url", "active_item")
|
||||||
|
|
||||||
def test_ajax_request(self):
|
def test_ajax_request(self):
|
||||||
response = self.client.get("/", HTTP_X_REQUESTED_WITH="XMLHttpRequest")
|
response = self.client.get("/accounts/login/", HTTP_X_REQUESTED_WITH="XMLHttpRequest")
|
||||||
for item in self.EXPECTED_ITEMS:
|
for item in self.EXPECTED_ITEMS:
|
||||||
self.assertNotIn(item, response.context)
|
self.assertNotIn(item, response.context)
|
||||||
|
|
||||||
|
@ -34,6 +37,12 @@ class NavigationContextProcessorTest(TestCase):
|
||||||
self.assertEqual(context["phppgadmin_url"], settings.GVA_LINK_PHPPGADMIN)
|
self.assertEqual(context["phppgadmin_url"], settings.GVA_LINK_PHPPGADMIN)
|
||||||
|
|
||||||
def test_index_page_context(self):
|
def test_index_page_context(self):
|
||||||
|
user = User.objects.create(username=TEST_USER)
|
||||||
|
user.set_password(TEST_PASSWORD)
|
||||||
|
user.save()
|
||||||
|
|
||||||
|
self.client.login(username=TEST_USER, password=TEST_PASSWORD)
|
||||||
|
|
||||||
response = self.client.get("/")
|
response = self.client.get("/")
|
||||||
for item in self.EXPECTED_ITEMS:
|
for item in self.EXPECTED_ITEMS:
|
||||||
self.assertIn(item, response.context)
|
self.assertIn(item, response.context)
|
||||||
|
@ -47,15 +56,6 @@ class NavigationContextProcessorTest(TestCase):
|
||||||
self._check_static_urls(response.context)
|
self._check_static_urls(response.context)
|
||||||
self.assertEqual(response.context["active_item"], "contact")
|
self.assertEqual(response.context["active_item"], "contact")
|
||||||
|
|
||||||
def test_hostingpackage_page_context(self):
|
|
||||||
User.objects.create_user("test", password="test")
|
|
||||||
self.client.login(username="test", password="test")
|
|
||||||
response = self.client.get(reverse("hosting_packages", kwargs={"user": "test"}))
|
|
||||||
for item in self.EXPECTED_ITEMS:
|
|
||||||
self.assertIn(item, response.context)
|
|
||||||
self._check_static_urls(response.context)
|
|
||||||
self.assertEqual(response.context["active_item"], "hostingpackage")
|
|
||||||
|
|
||||||
def _test_page_context_by_viewmodule(self, viewmodule, expecteditem):
|
def _test_page_context_by_viewmodule(self, viewmodule, expecteditem):
|
||||||
request = HttpRequest()
|
request = HttpRequest()
|
||||||
request.resolver_match = MagicMock()
|
request.resolver_match = MagicMock()
|
||||||
|
@ -106,6 +106,6 @@ class NavigationContextProcessorTest(TestCase):
|
||||||
|
|
||||||
class VersionInfoContextProcessorTest(TestCase):
|
class VersionInfoContextProcessorTest(TestCase):
|
||||||
def test_version_info_in_context(self):
|
def test_version_info_in_context(self):
|
||||||
response = self.client.get("/")
|
response = self.client.get("/accounts/login/")
|
||||||
self.assertIn("gnuviechadmin_version", response.context)
|
self.assertIn("gnuviechadmin_version", response.context)
|
||||||
self.assertEqual(response.context["gnuviechadmin_version"], gvaversion)
|
self.assertEqual(response.context["gnuviechadmin_version"], gvaversion)
|
||||||
|
|
|
@ -1,36 +1,50 @@
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from django.conf.urls import include, url
|
import debug_toolbar
|
||||||
from django.conf import settings
|
from django.conf.urls import include
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.contrib.flatpages import views
|
from django.contrib.flatpages import views
|
||||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from help import views as help_views
|
||||||
|
from invoices import views as invoice_views
|
||||||
|
|
||||||
admin.autodiscover()
|
admin.autodiscover()
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'', include('dashboard.urls')),
|
path("", include("dashboard.urls")),
|
||||||
url(r'^accounts/', include('allauth.urls')),
|
path("api/users/", help_views.ListHelpUserAPIView.as_view()),
|
||||||
url(r'^database/', include('userdbs.urls')),
|
path(
|
||||||
url(r'^domains/', include('domains.urls')),
|
"api/users/<int:pk>/",
|
||||||
url(r'^hosting/', include('hostingpackages.urls')),
|
help_views.HelpUserAPIView.as_view(),
|
||||||
url(r'^website/', include('websites.urls')),
|
name="helpuser-detail",
|
||||||
url(r'^mail/', include('managemails.urls')),
|
),
|
||||||
url(r'^osuser/', include('osusers.urls')),
|
path("api/invoices/", invoice_views.ListInvoiceAPIView.as_view()),
|
||||||
url(r'^admin/', admin.site.urls),
|
path(
|
||||||
url(r'^contact/', include('contact_form.urls')),
|
"api/invoices/<invoice_number>/",
|
||||||
url(r'^impressum/$', views.flatpage, {
|
invoice_views.InvoiceAPIView.as_view(),
|
||||||
'url': '/impressum/'
|
name="invoice-detail",
|
||||||
}, name='imprint'),
|
),
|
||||||
|
path("admin/", admin.site.urls),
|
||||||
|
path("impersonate/", include("impersonate.urls")),
|
||||||
|
path("accounts/", include("allauth.urls")),
|
||||||
|
path("database/", include("userdbs.urls")),
|
||||||
|
path("domains/", include("domains.urls")),
|
||||||
|
path("hosting/", include("hostingpackages.urls")),
|
||||||
|
path("website/", include("websites.urls")),
|
||||||
|
path("mail/", include("managemails.urls")),
|
||||||
|
path("osuser/", include("osusers.urls")),
|
||||||
|
path("contact/", include("contact_form.urls")),
|
||||||
|
path("impressum/", views.flatpage, {"url": "/impressum/"}, name="imprint"),
|
||||||
|
path("datenschutz/", views.flatpage, {"url": "/datenschutz/"}, name="privacy"),
|
||||||
|
path("issues/", views.flatpage, {"url": "/issues/"}, name="support"),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Uncomment the next line to serve media files in dev.
|
# Uncomment the next line to serve media files in dev.
|
||||||
# urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
# urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
|
||||||
if settings.DEBUG: # pragma: no cover
|
urlpatterns += staticfiles_urlpatterns()
|
||||||
import debug_toolbar
|
urlpatterns += [
|
||||||
|
path("__debug__/", include(debug_toolbar.urls)),
|
||||||
urlpatterns = [
|
]
|
||||||
url(r'^__debug__/', include(debug_toolbar.urls)),
|
|
||||||
] + staticfiles_urlpatterns() + urlpatterns
|
|
||||||
|
|
|
@ -3,11 +3,10 @@ This module defines form classes that can be extended by other gnuviechadmin
|
||||||
apps' forms.
|
apps' forms.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
PASSWORD_MISMATCH_ERROR = _("Passwords don't match")
|
PASSWORD_MISMATCH_ERROR = _("Passwords don't match")
|
||||||
"""
|
"""
|
||||||
|
@ -21,11 +20,14 @@ class PasswordModelFormMixin(forms.Form):
|
||||||
whether both fields contain the same string.
|
whether both fields contain the same string.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
password1 = forms.CharField(
|
password1 = forms.CharField(
|
||||||
label=_('Password'), widget=forms.PasswordInput,
|
label=_("Password"),
|
||||||
|
widget=forms.PasswordInput,
|
||||||
)
|
)
|
||||||
password2 = forms.CharField(
|
password2 = forms.CharField(
|
||||||
label=_('Password (again)'), widget=forms.PasswordInput,
|
label=_("Password (again)"),
|
||||||
|
widget=forms.PasswordInput,
|
||||||
)
|
)
|
||||||
|
|
||||||
def clean_password2(self):
|
def clean_password2(self):
|
||||||
|
@ -36,8 +38,8 @@ class PasswordModelFormMixin(forms.Form):
|
||||||
:rtype: str or None
|
:rtype: str or None
|
||||||
|
|
||||||
"""
|
"""
|
||||||
password1 = self.cleaned_data.get('password1')
|
password1 = self.cleaned_data.get("password1")
|
||||||
password2 = self.cleaned_data.get('password2')
|
password2 = self.cleaned_data.get("password2")
|
||||||
if password1 and password2 and password1 != password2:
|
if password1 and password2 and password1 != password2:
|
||||||
raise forms.ValidationError(PASSWORD_MISMATCH_ERROR)
|
raise forms.ValidationError(PASSWORD_MISMATCH_ERROR)
|
||||||
return password2
|
return password2
|
||||||
|
|
|
@ -7,8 +7,8 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: gvawebcore\n"
|
"Project-Id-Version: gvawebcore\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2016-01-29 11:04+0100\n"
|
"POT-Creation-Date: 2023-04-16 22:07+0200\n"
|
||||||
"PO-Revision-Date: 2015-01-25 11:49+0100\n"
|
"PO-Revision-Date: 2023-04-16 18:21+0200\n"
|
||||||
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
|
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
|
||||||
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
|
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
|
||||||
"Language: de\n"
|
"Language: de\n"
|
||||||
|
@ -16,17 +16,17 @@ msgstr ""
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"X-Generator: Poedit 1.6.10\n"
|
"X-Generator: Poedit 3.2.2\n"
|
||||||
"X-Poedit-SourceCharset: UTF-8\n"
|
"X-Poedit-SourceCharset: UTF-8\n"
|
||||||
|
|
||||||
#: gvawebcore/forms.py:12
|
#: gvawebcore/forms.py:11
|
||||||
msgid "Passwords don't match"
|
msgid "Passwords don't match"
|
||||||
msgstr "Passwörter stimmen nicht überein"
|
msgstr "Passwörter stimmen nicht überein"
|
||||||
|
|
||||||
#: gvawebcore/forms.py:25
|
#: gvawebcore/forms.py:25
|
||||||
msgid "Password"
|
msgid "Password"
|
||||||
msgstr "Passwort: "
|
msgstr "Passwort"
|
||||||
|
|
||||||
#: gvawebcore/forms.py:28
|
#: gvawebcore/forms.py:29
|
||||||
msgid "Password (again)"
|
msgid "Password (again)"
|
||||||
msgstr "Passwortwiederholung"
|
msgstr "Passwortwiederholung"
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
This module defines common view code to be used by multiple gnuviechadmin apps.
|
This module defines common view code to be used by multiple gnuviechadmin apps.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
from hostingpackages.models import CustomerHostingPackage
|
from hostingpackages.models import CustomerHostingPackage
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,7 +15,8 @@ class HostingPackageAndCustomerMixin(object):
|
||||||
keyword argument 'package'.
|
keyword argument 'package'.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
hosting_package_kwarg = 'package'
|
|
||||||
|
hosting_package_kwarg = "package"
|
||||||
"""Keyword argument used to find the hosting package in the URL."""
|
"""Keyword argument used to find the hosting package in the URL."""
|
||||||
|
|
||||||
hostingpackage = None
|
hostingpackage = None
|
||||||
|
@ -22,8 +24,8 @@ class HostingPackageAndCustomerMixin(object):
|
||||||
def get_hosting_package(self):
|
def get_hosting_package(self):
|
||||||
if self.hostingpackage is None:
|
if self.hostingpackage is None:
|
||||||
self.hostingpackage = get_object_or_404(
|
self.hostingpackage = get_object_or_404(
|
||||||
CustomerHostingPackage,
|
CustomerHostingPackage, pk=int(self.kwargs[self.hosting_package_kwarg])
|
||||||
pk=int(self.kwargs[self.hosting_package_kwarg]))
|
)
|
||||||
return self.hostingpackage
|
return self.hostingpackage
|
||||||
|
|
||||||
def get_customer_object(self):
|
def get_customer_object(self):
|
||||||
|
|
22
gnuviechadmin/help/admin.py
Normal file
22
gnuviechadmin/help/admin.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from help.models import HelpUser
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class HelpUserInline(admin.StackedInline):
|
||||||
|
model = HelpUser
|
||||||
|
can_delete = False
|
||||||
|
readonly_fields = ("offline_account_code",)
|
||||||
|
|
||||||
|
|
||||||
|
class UserAdmin(BaseUserAdmin):
|
||||||
|
inlines = (HelpUserInline,)
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.unregister(User)
|
||||||
|
admin.site.register(User, UserAdmin)
|
8
gnuviechadmin/help/apps.py
Normal file
8
gnuviechadmin/help/apps.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class HelpConfig(AppConfig):
|
||||||
|
default_auto_field = "django.db.models.BigAutoField"
|
||||||
|
name = "help"
|
||||||
|
verbose_name = _("User self help")
|
36
gnuviechadmin/help/locale/de/LC_MESSAGES/django.po
Normal file
36
gnuviechadmin/help/locale/de/LC_MESSAGES/django.po
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: help\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2023-04-16 22:07+0200\n"
|
||||||
|
"PO-Revision-Date: 2023-04-16 18:21+0200\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
|
||||||
|
"Language: de\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"X-Generator: Poedit 3.2.2\n"
|
||||||
|
"X-Poedit-SourceCharset: UTF-8\n"
|
||||||
|
|
||||||
|
#: help/apps.py:8
|
||||||
|
msgid "User self help"
|
||||||
|
msgstr "Selbsthilfe für Nutzer"
|
||||||
|
|
||||||
|
#: help/models.py:10
|
||||||
|
msgid "Contact email address"
|
||||||
|
msgstr "Kontakt-E-Mail-Adresse"
|
||||||
|
|
||||||
|
#: help/models.py:11
|
||||||
|
msgid "Contact postal address"
|
||||||
|
msgstr "Kontakt-Postanschrift"
|
||||||
|
|
||||||
|
#: help/models.py:13
|
||||||
|
msgid "Offline account reset code"
|
||||||
|
msgstr "Offline-Code für die Konto-Rücksetzung"
|
0
gnuviechadmin/help/management/commands/__init__.py
Normal file
0
gnuviechadmin/help/management/commands/__init__.py
Normal file
17
gnuviechadmin/help/management/commands/populate.py
Normal file
17
gnuviechadmin/help/management/commands/populate.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.core.management import BaseCommand
|
||||||
|
|
||||||
|
from help.models import HelpUser
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = "Populate help user information for existing users"
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
for user in User.objects.filter(helpuser=None):
|
||||||
|
help_user = HelpUser.objects.create(user_id=user.id, email_address=user.email)
|
||||||
|
help_user.generate_offline_account_code()
|
||||||
|
help_user.save()
|
||||||
|
self.stdout.write(f"created offline account code {help_user.offline_account_code} for {user}.")
|
29
gnuviechadmin/help/management/commands/reset_offline_code.py
Normal file
29
gnuviechadmin/help/management/commands/reset_offline_code.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.core.management import BaseCommand, CommandError
|
||||||
|
|
||||||
|
from help.models import HelpUser
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = "Reset offline account reset code for existing users"
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
parser.add_argument("users", nargs='+', type=str)
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
for name in options["users"]:
|
||||||
|
try:
|
||||||
|
user = User.objects.get(username=name)
|
||||||
|
except User.DoesNotExist:
|
||||||
|
raise CommandError(f'User {name} does not exist')
|
||||||
|
|
||||||
|
help_user = user.helpuser
|
||||||
|
if help_user is None:
|
||||||
|
help_user = HelpUser.objects.create(email_address=user.email)
|
||||||
|
|
||||||
|
help_user.generate_offline_account_code()
|
||||||
|
help_user.save()
|
||||||
|
|
||||||
|
self.stdout.write(f"generated new offline account reset code {help_user.offline_account_code} for {name}")
|
|
@ -0,0 +1,55 @@
|
||||||
|
# Generated by Django 3.2.18 on 2023-04-16 09:32
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="HelpUser",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"email_address",
|
||||||
|
models.EmailField(
|
||||||
|
help_text="Contact email address", max_length=254
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"postal_address",
|
||||||
|
models.TextField(blank=True, help_text="Contact postal address"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"offline_account_code",
|
||||||
|
models.CharField(
|
||||||
|
default="",
|
||||||
|
help_text="Offline account reset code",
|
||||||
|
max_length=36,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"user",
|
||||||
|
models.OneToOneField(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
0
gnuviechadmin/help/migrations/__init__.py
Normal file
0
gnuviechadmin/help/migrations/__init__.py
Normal file
17
gnuviechadmin/help/models.py
Normal file
17
gnuviechadmin/help/models.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import models
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class HelpUser(models.Model):
|
||||||
|
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
|
||||||
|
email_address = models.EmailField(help_text=_("Contact email address"))
|
||||||
|
postal_address = models.TextField(help_text=_("Contact postal address"), blank=True)
|
||||||
|
offline_account_code = models.CharField(
|
||||||
|
help_text=_("Offline account reset code"), max_length=36, default=""
|
||||||
|
)
|
||||||
|
|
||||||
|
def generate_offline_account_code(self):
|
||||||
|
self.offline_account_code = str(uuid.uuid4())
|
22
gnuviechadmin/help/serializers.py
Normal file
22
gnuviechadmin/help/serializers.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
"""
|
||||||
|
Serializers for the REST API
|
||||||
|
"""
|
||||||
|
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from help.models import HelpUser
|
||||||
|
|
||||||
|
|
||||||
|
class HelpUserSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
user = serializers.StringRelatedField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = HelpUser
|
||||||
|
fields = [
|
||||||
|
"url",
|
||||||
|
"user",
|
||||||
|
"email_address",
|
||||||
|
"postal_address",
|
||||||
|
"offline_account_code",
|
||||||
|
]
|
||||||
|
read_only_fields = ["user", "offline_account_code"]
|
3
gnuviechadmin/help/tests.py
Normal file
3
gnuviechadmin/help/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
26
gnuviechadmin/help/views.py
Normal file
26
gnuviechadmin/help/views.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
from rest_framework import generics
|
||||||
|
|
||||||
|
from help.models import HelpUser
|
||||||
|
from help.serializers import HelpUserSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class ListHelpUserAPIView(generics.ListAPIView):
|
||||||
|
"""
|
||||||
|
API endpoint that allows user help profile to be viewed or edited.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
queryset = (
|
||||||
|
HelpUser.objects.all().prefetch_related("user").order_by("user__username")
|
||||||
|
)
|
||||||
|
serializer_class = HelpUserSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class HelpUserAPIView(generics.RetrieveUpdateAPIView):
|
||||||
|
"""
|
||||||
|
API endpoint that allows user help profile to be viewed or edited.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
queryset = HelpUser.objects.all()
|
||||||
|
serializer_class = HelpUserSerializer
|
|
@ -2,4 +2,3 @@
|
||||||
This app takes care of hosting packages.
|
This app takes care of hosting packages.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
default_app_config = 'hostingpackages.apps.HostingPackagesAppConfig'
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
This module contains the admin site interface for hosting packages.
|
This module contains the admin site interface for hosting packages.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
@ -12,6 +12,7 @@ from .models import (
|
||||||
CustomerHostingPackage,
|
CustomerHostingPackage,
|
||||||
CustomerHostingPackageDomain,
|
CustomerHostingPackageDomain,
|
||||||
CustomerMailboxOption,
|
CustomerMailboxOption,
|
||||||
|
CustomerPackageDiskUsage,
|
||||||
CustomerUserDatabaseOption,
|
CustomerUserDatabaseOption,
|
||||||
DiskSpaceOption,
|
DiskSpaceOption,
|
||||||
HostingPackageTemplate,
|
HostingPackageTemplate,
|
||||||
|
@ -25,9 +26,10 @@ class CustomerHostingPackageCreateForm(forms.ModelForm):
|
||||||
This is the form class for creating new customer hosting packages.
|
This is the form class for creating new customer hosting packages.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CustomerHostingPackage
|
model = CustomerHostingPackage
|
||||||
fields = ['customer', 'template', 'name']
|
fields = ["customer", "template", "name"]
|
||||||
|
|
||||||
def save(self, **kwargs):
|
def save(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -39,10 +41,11 @@ class CustomerHostingPackageCreateForm(forms.ModelForm):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
hostinpackages = CustomerHostingPackage.objects.create_from_template(
|
hostinpackages = CustomerHostingPackage.objects.create_from_template(
|
||||||
customer=self.cleaned_data['customer'],
|
customer=self.cleaned_data["customer"],
|
||||||
template=self.cleaned_data['template'],
|
template=self.cleaned_data["template"],
|
||||||
name=self.cleaned_data['name'],
|
name=self.cleaned_data["name"],
|
||||||
**kwargs)
|
**kwargs
|
||||||
|
)
|
||||||
return hostinpackages
|
return hostinpackages
|
||||||
|
|
||||||
def save_m2m(self):
|
def save_m2m(self):
|
||||||
|
@ -55,6 +58,7 @@ class CustomerDiskSpaceOptionInline(admin.TabularInline):
|
||||||
space options.
|
space options.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
model = CustomerDiskSpaceOption
|
model = CustomerDiskSpaceOption
|
||||||
extra = 0
|
extra = 0
|
||||||
|
|
||||||
|
@ -65,6 +69,7 @@ class CustomerMailboxOptionInline(admin.TabularInline):
|
||||||
mailbox options.
|
mailbox options.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
model = CustomerMailboxOption
|
model = CustomerMailboxOption
|
||||||
extra = 0
|
extra = 0
|
||||||
|
|
||||||
|
@ -75,6 +80,7 @@ class CustomerUserDatabaseOptionInline(admin.TabularInline):
|
||||||
database options.
|
database options.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
model = CustomerUserDatabaseOption
|
model = CustomerUserDatabaseOption
|
||||||
extra = 0
|
extra = 0
|
||||||
|
|
||||||
|
@ -85,30 +91,41 @@ class CustomerHostingPackageDomainInline(admin.TabularInline):
|
||||||
hosting packages.
|
hosting packages.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
model = CustomerHostingPackageDomain
|
model = CustomerHostingPackageDomain
|
||||||
extra = 0
|
extra = 0
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerPackageDiskUsageInline(admin.TabularInline):
|
||||||
|
model = CustomerPackageDiskUsage
|
||||||
|
ordering = ["-used_kb", "source", "item"]
|
||||||
|
fields = ["source", "item", "used_kb"]
|
||||||
|
readonly_fields = ["source", "item", "used_kb"]
|
||||||
|
extra = 0
|
||||||
|
can_delete = False
|
||||||
|
|
||||||
|
def has_add_permission(self, request, obj):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class CustomerHostingPackageAdmin(admin.ModelAdmin):
|
class CustomerHostingPackageAdmin(admin.ModelAdmin):
|
||||||
"""
|
"""
|
||||||
This class implements the admin interface for
|
This class implements the admin interface for
|
||||||
:py:class:`CustomerHostingPackage`.
|
:py:class:`CustomerHostingPackage`.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
add_form = CustomerHostingPackageCreateForm
|
add_form = CustomerHostingPackageCreateForm
|
||||||
add_fieldsets = (
|
add_fieldsets = ((None, {"fields": ("customer", "template", "name")}),)
|
||||||
(None, {
|
|
||||||
'fields': ('customer', 'template', 'name')
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
inlines = [
|
inlines = [
|
||||||
CustomerDiskSpaceOptionInline,
|
CustomerDiskSpaceOptionInline,
|
||||||
CustomerMailboxOptionInline,
|
CustomerMailboxOptionInline,
|
||||||
CustomerUserDatabaseOptionInline,
|
CustomerUserDatabaseOptionInline,
|
||||||
CustomerHostingPackageDomainInline,
|
CustomerHostingPackageDomainInline,
|
||||||
|
CustomerPackageDiskUsageInline,
|
||||||
]
|
]
|
||||||
list_display = ['name', 'customer', 'osuser']
|
list_display = ["name", "customer", "osuser"]
|
||||||
|
|
||||||
def get_form(self, request, obj=None, **kwargs):
|
def get_form(self, request, obj=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -125,13 +142,16 @@ class CustomerHostingPackageAdmin(admin.ModelAdmin):
|
||||||
"""
|
"""
|
||||||
defaults = {}
|
defaults = {}
|
||||||
if obj is None:
|
if obj is None:
|
||||||
defaults.update({
|
defaults.update(
|
||||||
'form': self.add_form,
|
{
|
||||||
'fields': admin.options.flatten_fieldsets(self.add_fieldsets),
|
"form": self.add_form,
|
||||||
})
|
"fields": admin.options.flatten_fieldsets(self.add_fieldsets),
|
||||||
|
}
|
||||||
|
)
|
||||||
defaults.update(kwargs)
|
defaults.update(kwargs)
|
||||||
return super(CustomerHostingPackageAdmin, self).get_form(
|
return super(CustomerHostingPackageAdmin, self).get_form(
|
||||||
request, obj, **defaults)
|
request, obj, **defaults
|
||||||
|
)
|
||||||
|
|
||||||
def get_readonly_fields(self, request, obj=None):
|
def get_readonly_fields(self, request, obj=None):
|
||||||
"""
|
"""
|
||||||
|
@ -147,7 +167,7 @@ class CustomerHostingPackageAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if obj:
|
if obj:
|
||||||
return ['customer', 'template']
|
return ["customer", "template"]
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,8 @@ This module contains the :py:class:`django.apps.AppConfig` instance for the
|
||||||
:py:mod:`hostingpackages` app.
|
:py:mod:`hostingpackages` app.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
class HostingPackagesAppConfig(AppConfig):
|
class HostingPackagesAppConfig(AppConfig):
|
||||||
|
@ -13,5 +12,6 @@ class HostingPackagesAppConfig(AppConfig):
|
||||||
AppConfig for the :py:mod:`hostingpackages` app.
|
AppConfig for the :py:mod:`hostingpackages` app.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
name = 'hostingpackages'
|
|
||||||
verbose_name = _('Hosting Packages and Options')
|
name = "hostingpackages"
|
||||||
|
verbose_name = _("Hosting Packages and Options")
|
||||||
|
|
|
@ -2,17 +2,13 @@
|
||||||
This module contains the form classes related to hosting packages.
|
This module contains the form classes related to hosting packages.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from django import forms
|
|
||||||
from django.urls import reverse
|
|
||||||
from django.utils.translation import ugettext as _
|
|
||||||
|
|
||||||
from crispy_forms.helper import FormHelper
|
from crispy_forms.helper import FormHelper
|
||||||
from crispy_forms.layout import (
|
from crispy_forms.layout import Layout, Submit
|
||||||
Layout,
|
from django import forms
|
||||||
Submit,
|
from django.urls import reverse
|
||||||
)
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
CustomerDiskSpaceOption,
|
CustomerDiskSpaceOption,
|
||||||
|
@ -28,25 +24,24 @@ class CreateCustomerHostingPackageForm(forms.ModelForm):
|
||||||
a preselected customer.
|
a preselected customer.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CustomerHostingPackage
|
model = CustomerHostingPackage
|
||||||
fields = ['template', 'name', 'description']
|
fields = ["template", "name", "description"]
|
||||||
|
|
||||||
def __init__(self, instance, *args, **kwargs):
|
def __init__(self, instance, *args, **kwargs):
|
||||||
username = kwargs.pop('user')
|
username = kwargs.pop("user")
|
||||||
super(CreateCustomerHostingPackageForm, self).__init__(
|
super(CreateCustomerHostingPackageForm, self).__init__(*args, **kwargs)
|
||||||
*args, **kwargs
|
self.fields["description"].widget.attrs["rows"] = 2
|
||||||
)
|
|
||||||
self.fields['description'].widget.attrs['rows'] = 2
|
|
||||||
self.helper = FormHelper()
|
self.helper = FormHelper()
|
||||||
self.helper.form_action = reverse(
|
self.helper.form_action = reverse(
|
||||||
'create_customer_hosting_package', kwargs={'user': username}
|
"create_customer_hosting_package", kwargs={"user": username}
|
||||||
)
|
)
|
||||||
self.helper.layout = Layout(
|
self.helper.layout = Layout(
|
||||||
'template',
|
"template",
|
||||||
'name',
|
"name",
|
||||||
'description',
|
"description",
|
||||||
Submit('submit', _('Add Hosting Package')),
|
Submit("submit", _("Add Hosting Package")),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,44 +50,44 @@ class CreateHostingPackageForm(forms.ModelForm):
|
||||||
This form class is used for creating new customer hosting packages.
|
This form class is used for creating new customer hosting packages.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CustomerHostingPackage
|
model = CustomerHostingPackage
|
||||||
fields = ['customer', 'template', 'name', 'description']
|
fields = ["customer", "template", "name", "description"]
|
||||||
|
|
||||||
def __init__(self, instance, *args, **kwargs):
|
def __init__(self, instance, *args, **kwargs):
|
||||||
super(CreateHostingPackageForm, self).__init__(
|
super(CreateHostingPackageForm, self).__init__(*args, **kwargs)
|
||||||
*args, **kwargs
|
self.fields["description"].widget.attrs["rows"] = 2
|
||||||
)
|
|
||||||
self.fields['description'].widget.attrs['rows'] = 2
|
|
||||||
self.helper = FormHelper()
|
self.helper = FormHelper()
|
||||||
self.helper.form_action = reverse('create_hosting_package')
|
self.helper.form_action = reverse("create_hosting_package")
|
||||||
self.helper.layout = Layout(
|
self.helper.layout = Layout(
|
||||||
'customer',
|
"customer",
|
||||||
'template',
|
"template",
|
||||||
'name',
|
"name",
|
||||||
'description',
|
"description",
|
||||||
Submit('submit', _('Add Hosting Package')),
|
Submit("submit", _("Add Hosting Package")),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class AddDiskspaceOptionForm(forms.ModelForm):
|
class AddDiskspaceOptionForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CustomerDiskSpaceOption
|
model = CustomerDiskSpaceOption
|
||||||
fields = ['diskspace', 'diskspace_unit']
|
fields = ["diskspace", "diskspace_unit"]
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.hostingpackage = kwargs.pop('hostingpackage')
|
self.hostingpackage = kwargs.pop("hostingpackage")
|
||||||
self.option_template = kwargs.pop('option_template')
|
self.option_template = kwargs.pop("option_template")
|
||||||
super(AddDiskspaceOptionForm, self).__init__(*args, **kwargs)
|
super(AddDiskspaceOptionForm, self).__init__(*args, **kwargs)
|
||||||
self.helper = FormHelper()
|
self.helper = FormHelper()
|
||||||
self.helper.form_action = reverse(
|
self.helper.form_action = reverse(
|
||||||
'add_hosting_option',
|
"add_hosting_option",
|
||||||
kwargs={
|
kwargs={
|
||||||
'package': self.hostingpackage.id,
|
"package": self.hostingpackage.id,
|
||||||
'type': 'diskspace',
|
"type": "diskspace",
|
||||||
'optionid': self.option_template.id,
|
"optionid": self.option_template.id,
|
||||||
})
|
},
|
||||||
self.helper.add_input(Submit('submit', _('Add disk space option')))
|
)
|
||||||
|
self.helper.add_input(Submit("submit", _("Add disk space option")))
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
self.instance.hosting_package = self.hostingpackage
|
self.instance.hosting_package = self.hostingpackage
|
||||||
|
@ -103,21 +98,22 @@ class AddDiskspaceOptionForm(forms.ModelForm):
|
||||||
class AddMailboxOptionForm(forms.ModelForm):
|
class AddMailboxOptionForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CustomerMailboxOption
|
model = CustomerMailboxOption
|
||||||
fields = ['number']
|
fields = ["number"]
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.hostingpackage = kwargs.pop('hostingpackage')
|
self.hostingpackage = kwargs.pop("hostingpackage")
|
||||||
self.option_template = kwargs.pop('option_template')
|
self.option_template = kwargs.pop("option_template")
|
||||||
super(AddMailboxOptionForm, self).__init__(*args, **kwargs)
|
super(AddMailboxOptionForm, self).__init__(*args, **kwargs)
|
||||||
self.helper = FormHelper()
|
self.helper = FormHelper()
|
||||||
self.helper.form_action = reverse(
|
self.helper.form_action = reverse(
|
||||||
'add_hosting_option',
|
"add_hosting_option",
|
||||||
kwargs={
|
kwargs={
|
||||||
'package': self.hostingpackage.id,
|
"package": self.hostingpackage.id,
|
||||||
'type': 'mailboxes',
|
"type": "mailboxes",
|
||||||
'optionid': self.option_template.id,
|
"optionid": self.option_template.id,
|
||||||
})
|
},
|
||||||
self.helper.add_input(Submit('submit', _('Add mailbox option')))
|
)
|
||||||
|
self.helper.add_input(Submit("submit", _("Add mailbox option")))
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
self.instance.hosting_package = self.hostingpackage
|
self.instance.hosting_package = self.hostingpackage
|
||||||
|
@ -128,21 +124,22 @@ class AddMailboxOptionForm(forms.ModelForm):
|
||||||
class AddUserDatabaseOptionForm(forms.ModelForm):
|
class AddUserDatabaseOptionForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CustomerUserDatabaseOption
|
model = CustomerUserDatabaseOption
|
||||||
fields = ['number']
|
fields = ["number"]
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.hostingpackage = kwargs.pop('hostingpackage')
|
self.hostingpackage = kwargs.pop("hostingpackage")
|
||||||
self.option_template = kwargs.pop('option_template')
|
self.option_template = kwargs.pop("option_template")
|
||||||
super(AddUserDatabaseOptionForm, self).__init__(*args, **kwargs)
|
super(AddUserDatabaseOptionForm, self).__init__(*args, **kwargs)
|
||||||
self.helper = FormHelper()
|
self.helper = FormHelper()
|
||||||
self.helper.form_action = reverse(
|
self.helper.form_action = reverse(
|
||||||
'add_hosting_option',
|
"add_hosting_option",
|
||||||
kwargs={
|
kwargs={
|
||||||
'package': self.hostingpackage.id,
|
"package": self.hostingpackage.id,
|
||||||
'type': 'databases',
|
"type": "databases",
|
||||||
'optionid': self.option_template.id,
|
"optionid": self.option_template.id,
|
||||||
})
|
},
|
||||||
self.helper.add_input(Submit('submit', _('Add database option')))
|
)
|
||||||
|
self.helper.add_input(Submit("submit", _("Add database option")))
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
self.instance.hosting_package = self.hostingpackage
|
self.instance.hosting_package = self.hostingpackage
|
||||||
|
|
|
@ -7,8 +7,8 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: gnuviechadmin hostingpackages\n"
|
"Project-Id-Version: gnuviechadmin hostingpackages\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2016-01-29 11:04+0100\n"
|
"POT-Creation-Date: 2023-07-23 10:23+0200\n"
|
||||||
"PO-Revision-Date: 2015-01-25 15:49+0100\n"
|
"PO-Revision-Date: 2023-07-23 10:24+0200\n"
|
||||||
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
|
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
|
||||||
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
|
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
|
||||||
"Language: de\n"
|
"Language: de\n"
|
||||||
|
@ -16,221 +16,682 @@ msgstr ""
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"X-Generator: Poedit 1.6.10\n"
|
"X-Generator: Poedit 3.2.2\n"
|
||||||
"X-Poedit-SourceCharset: UTF-8\n"
|
"X-Poedit-SourceCharset: UTF-8\n"
|
||||||
|
|
||||||
#: hostingpackages/apps.py:17
|
#: hostingpackages/apps.py:17
|
||||||
msgid "Hosting Packages and Options"
|
msgid "Hosting Packages and Options"
|
||||||
msgstr "Hostingpakete und -Optionen"
|
msgstr "Hostingpakete und -Optionen"
|
||||||
|
|
||||||
#: hostingpackages/forms.py:49 hostingpackages/forms.py:74
|
#: hostingpackages/forms.py:44 hostingpackages/forms.py:68
|
||||||
msgid "Add Hosting Package"
|
msgid "Add Hosting Package"
|
||||||
msgstr "Hostingpaket anlegen"
|
msgstr "Hostingpaket anlegen"
|
||||||
|
|
||||||
#: hostingpackages/forms.py:95
|
#: hostingpackages/forms.py:90
|
||||||
msgid "Add disk space option"
|
msgid "Add disk space option"
|
||||||
msgstr "Speicherplatzoption hinzufügen"
|
msgstr "Speicherplatzoption hinzufügen"
|
||||||
|
|
||||||
#: hostingpackages/forms.py:120
|
#: hostingpackages/forms.py:116
|
||||||
msgid "Add mailbox option"
|
msgid "Add mailbox option"
|
||||||
msgstr "Postfachoption hinzufügen"
|
msgstr "Postfachoption hinzufügen"
|
||||||
|
|
||||||
#: hostingpackages/forms.py:145
|
#: hostingpackages/forms.py:142
|
||||||
msgid "Add database option"
|
msgid "Add database option"
|
||||||
msgstr "Datenbankoption hinzufügen"
|
msgstr "Datenbankoption hinzufügen"
|
||||||
|
|
||||||
#: hostingpackages/models.py:31
|
#: hostingpackages/models.py:22
|
||||||
msgid "MiB"
|
msgid "MiB"
|
||||||
msgstr "MiB"
|
msgstr "MiB"
|
||||||
|
|
||||||
#: hostingpackages/models.py:32
|
#: hostingpackages/models.py:22
|
||||||
msgid "GiB"
|
msgid "GiB"
|
||||||
msgstr "GiB"
|
msgstr "GiB"
|
||||||
|
|
||||||
#: hostingpackages/models.py:33
|
#: hostingpackages/models.py:22
|
||||||
msgid "TiB"
|
msgid "TiB"
|
||||||
msgstr "TiB"
|
msgstr "TiB"
|
||||||
|
|
||||||
#: hostingpackages/models.py:45
|
#: hostingpackages/models.py:28
|
||||||
msgid "description"
|
msgid "description"
|
||||||
msgstr "Beschreibung"
|
msgstr "Beschreibung"
|
||||||
|
|
||||||
#: hostingpackages/models.py:46
|
#: hostingpackages/models.py:29
|
||||||
msgid "mailbox count"
|
msgid "mailbox count"
|
||||||
msgstr "Anzahl Postfächer"
|
msgstr "Anzahl Postfächer"
|
||||||
|
|
||||||
#: hostingpackages/models.py:48 hostingpackages/models.py:76
|
#: hostingpackages/models.py:31 hostingpackages/models.py:60
|
||||||
msgid "disk space"
|
msgid "disk space"
|
||||||
msgstr "Speicherplatz"
|
msgstr "Speicherplatz"
|
||||||
|
|
||||||
#: hostingpackages/models.py:48
|
#: hostingpackages/models.py:31
|
||||||
msgid "disk space for the hosting package"
|
msgid "disk space for the hosting package"
|
||||||
msgstr "Speicherplatz für das Hostingpaket"
|
msgstr "Speicherplatz für das Hostingpaket"
|
||||||
|
|
||||||
#: hostingpackages/models.py:50 hostingpackages/models.py:78
|
#: hostingpackages/models.py:34 hostingpackages/models.py:62
|
||||||
msgid "unit of disk space"
|
msgid "unit of disk space"
|
||||||
msgstr "Maßeinheit für den Speicherplatz"
|
msgstr "Maßeinheit für den Speicherplatz"
|
||||||
|
|
||||||
#: hostingpackages/models.py:60 hostingpackages/models.py:213
|
#: hostingpackages/models.py:45 hostingpackages/models.py:193
|
||||||
msgid "name"
|
msgid "name"
|
||||||
msgstr "Name"
|
msgstr "Name"
|
||||||
|
|
||||||
#: hostingpackages/models.py:63
|
#: hostingpackages/models.py:48
|
||||||
msgid "Hosting package"
|
msgid "Hosting package"
|
||||||
msgstr "Hostingpaket"
|
msgstr "Hostingpaket"
|
||||||
|
|
||||||
#: hostingpackages/models.py:64
|
#: hostingpackages/models.py:49
|
||||||
msgid "Hosting packages"
|
msgid "Hosting packages"
|
||||||
msgstr "Hostingpakete"
|
msgstr "Hostingpakete"
|
||||||
|
|
||||||
#: hostingpackages/models.py:83
|
#: hostingpackages/models.py:68
|
||||||
msgid "Disk space option"
|
msgid "Disk space option"
|
||||||
msgstr "Speicherplatzoption"
|
msgstr "Speicherplatzoption"
|
||||||
|
|
||||||
#: hostingpackages/models.py:84
|
#: hostingpackages/models.py:69
|
||||||
msgid "Disk space options"
|
msgid "Disk space options"
|
||||||
msgstr "Speicherplatzoptionen"
|
msgstr "Speicherplatzoptionen"
|
||||||
|
|
||||||
#: hostingpackages/models.py:87
|
#: hostingpackages/models.py:72
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Additional disk space {space} {unit}"
|
msgid "Additional disk space {space} {unit}"
|
||||||
msgstr "Zusätzlicher Speicherplatz {space} {unit}"
|
msgstr "Zusätzlicher Speicherplatz {space} {unit}"
|
||||||
|
|
||||||
#: hostingpackages/models.py:104
|
#: hostingpackages/models.py:89
|
||||||
msgid "number of databases"
|
msgid "number of databases"
|
||||||
msgstr "Anzahl von Datenbanken"
|
msgstr "Anzahl von Datenbanken"
|
||||||
|
|
||||||
#: hostingpackages/models.py:106
|
#: hostingpackages/models.py:90
|
||||||
msgid "database type"
|
msgid "database type"
|
||||||
msgstr "Datenbanktyp"
|
msgstr "Datenbanktyp"
|
||||||
|
|
||||||
#: hostingpackages/models.py:111
|
#: hostingpackages/models.py:95
|
||||||
msgid "Database option"
|
msgid "Database option"
|
||||||
msgstr "Datenbankoption"
|
msgstr "Datenbankoption"
|
||||||
|
|
||||||
#: hostingpackages/models.py:112
|
#: hostingpackages/models.py:96
|
||||||
msgid "Database options"
|
msgid "Database options"
|
||||||
msgstr "Datenbankoptionen"
|
msgstr "Datenbankoptionen"
|
||||||
|
|
||||||
#: hostingpackages/models.py:116
|
#: hostingpackages/models.py:100
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{type} database"
|
msgid "{type} database"
|
||||||
msgid_plural "{count} {type} databases"
|
msgid_plural "{count} {type} databases"
|
||||||
msgstr[0] "{type}-Datenbank"
|
msgstr[0] "{type}-Datenbank"
|
||||||
msgstr[1] "{count} {type}-Datenbanken"
|
msgstr[1] "{count} {type}-Datenbanken"
|
||||||
|
|
||||||
#: hostingpackages/models.py:141
|
#: hostingpackages/models.py:121
|
||||||
msgid "number of mailboxes"
|
msgid "number of mailboxes"
|
||||||
msgstr "Anzahl von Postfächern"
|
msgstr "Anzahl von Postfächern"
|
||||||
|
|
||||||
#: hostingpackages/models.py:146
|
#: hostingpackages/models.py:126
|
||||||
msgid "Mailbox option"
|
msgid "Mailbox option"
|
||||||
msgstr "Postfachoption"
|
msgstr "Postfachoption"
|
||||||
|
|
||||||
#: hostingpackages/models.py:147
|
#: hostingpackages/models.py:127
|
||||||
msgid "Mailbox options"
|
msgid "Mailbox options"
|
||||||
msgstr "Postfachoptionen"
|
msgstr "Postfachoptionen"
|
||||||
|
|
||||||
#: hostingpackages/models.py:151
|
#: hostingpackages/models.py:131
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{count} additional mailbox"
|
msgid "{count} additional mailbox"
|
||||||
msgid_plural "{count} additional mailboxes"
|
msgid_plural "{count} additional mailboxes"
|
||||||
msgstr[0] "{count} zusätzliches Postfach"
|
msgstr[0] "{count} zusätzliches Postfach"
|
||||||
msgstr[1] "{count} zusätzliche Postfächer"
|
msgstr[1] "{count} zusätzliche Postfächer"
|
||||||
|
|
||||||
#: hostingpackages/models.py:206
|
#: hostingpackages/models.py:183
|
||||||
msgid "customer"
|
msgid "customer"
|
||||||
msgstr "Kunde"
|
msgstr "Kunde"
|
||||||
|
|
||||||
#: hostingpackages/models.py:208
|
#: hostingpackages/models.py:187
|
||||||
msgid "hosting package template"
|
msgid "hosting package template"
|
||||||
msgstr "Hostingpaketvorlage"
|
msgstr "Hostingpaketvorlage"
|
||||||
|
|
||||||
#: hostingpackages/models.py:210
|
#: hostingpackages/models.py:189
|
||||||
msgid "The hosting package template that this hosting package is based on"
|
msgid "The hosting package template that this hosting package is based on"
|
||||||
msgstr "Die Hostingpaketvorlage, auf der dieses Hostingpaket aufgebaut ist"
|
msgstr "Die Hostingpaketvorlage, auf der dieses Hostingpaket aufgebaut ist"
|
||||||
|
|
||||||
#: hostingpackages/models.py:215
|
#: hostingpackages/models.py:196
|
||||||
msgid "Operating system user"
|
msgid "Operating system user"
|
||||||
msgstr "Betriebssystemnutzer"
|
msgstr "Betriebssystemnutzer"
|
||||||
|
|
||||||
#: hostingpackages/models.py:222
|
#: hostingpackages/models.py:206
|
||||||
msgid "customer hosting package"
|
msgid "customer hosting package"
|
||||||
msgstr "Kundenhostingpaket"
|
msgstr "Kundenhostingpaket"
|
||||||
|
|
||||||
#: hostingpackages/models.py:223
|
#: hostingpackages/models.py:207
|
||||||
msgid "customer hosting packages"
|
msgid "customer hosting packages"
|
||||||
msgstr "Kundenhostingpakete"
|
msgstr "Kundenhostingpakete"
|
||||||
|
|
||||||
#: hostingpackages/models.py:226
|
#: hostingpackages/models.py:210
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} for {customer}"
|
msgid "{name} for {customer}"
|
||||||
msgstr "{name} für {customer}"
|
msgstr "{name} für {customer}"
|
||||||
|
|
||||||
#: hostingpackages/models.py:404 hostingpackages/models.py:426
|
#: hostingpackages/models.py:421 hostingpackages/models.py:448
|
||||||
|
#: hostingpackages/models.py:513
|
||||||
msgid "hosting package"
|
msgid "hosting package"
|
||||||
msgstr "Hostingpaket"
|
msgstr "Hostingpaket"
|
||||||
|
|
||||||
#: hostingpackages/models.py:407
|
#: hostingpackages/models.py:426
|
||||||
msgid "hosting domain"
|
msgid "hosting domain"
|
||||||
msgstr "Hostingdomain"
|
msgstr "Hostingdomain"
|
||||||
|
|
||||||
#: hostingpackages/models.py:429
|
#: hostingpackages/models.py:453
|
||||||
msgid "customer hosting option"
|
msgid "customer hosting option"
|
||||||
msgstr "kundenspezifische Hostingoption"
|
msgstr "kundenspezifische Hostingoption"
|
||||||
|
|
||||||
#: hostingpackages/models.py:430
|
#: hostingpackages/models.py:454
|
||||||
msgid "customer hosting options"
|
msgid "customer hosting options"
|
||||||
msgstr "kundenspezifische Hostingoptionen"
|
msgstr "kundenspezifische Hostingoptionen"
|
||||||
|
|
||||||
#: hostingpackages/models.py:442
|
#: hostingpackages/models.py:466
|
||||||
msgid "disk space option template"
|
msgid "disk space option template"
|
||||||
msgstr "Speicherplatzoptionsvorlage"
|
msgstr "Speicherplatzoptionsvorlage"
|
||||||
|
|
||||||
#: hostingpackages/models.py:444
|
#: hostingpackages/models.py:468
|
||||||
msgid "The disk space option template that this disk space option is based on"
|
msgid "The disk space option template that this disk space option is based on"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Die Speicherplatzoptionsvorlage auf der diese Speicherplatzoption aufgebaut "
|
"Die Speicherplatzoptionsvorlage auf der diese Speicherplatzoption aufgebaut "
|
||||||
"ist"
|
"ist"
|
||||||
|
|
||||||
#: hostingpackages/models.py:458
|
#: hostingpackages/models.py:483
|
||||||
msgid "user database option template"
|
msgid "user database option template"
|
||||||
msgstr "Nutzerdatenbankoptionsvorlage"
|
msgstr "Nutzerdatenbankoptionsvorlage"
|
||||||
|
|
||||||
#: hostingpackages/models.py:460
|
#: hostingpackages/models.py:485
|
||||||
msgid "The user database option template that this database option is based on"
|
msgid "The user database option template that this database option is based on"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Die Nutzerdatenbankoptionsvorlage auf der diese Datenbankoption aufgebaut ist"
|
"Die Nutzerdatenbankoptionsvorlage auf der diese Datenbankoption aufgebaut ist"
|
||||||
|
|
||||||
#: hostingpackages/models.py:474
|
#: hostingpackages/models.py:500
|
||||||
msgid "mailbox option template"
|
msgid "mailbox option template"
|
||||||
msgstr "Postfachoptionsvorlage"
|
msgstr "Postfachoptionsvorlage"
|
||||||
|
|
||||||
#: hostingpackages/models.py:476
|
#: hostingpackages/models.py:501
|
||||||
msgid "The mailbox option template that this mailbox option is based on"
|
msgid "The mailbox option template that this mailbox option is based on"
|
||||||
msgstr "Die Postfachoptionsvorlage auf der diese Postfachoption aufgebaut ist"
|
msgstr "Die Postfachoptionsvorlage auf der diese Postfachoption aufgebaut ist"
|
||||||
|
|
||||||
#: hostingpackages/views.py:60 hostingpackages/views.py:94
|
#: hostingpackages/models.py:514
|
||||||
|
msgid "The hosting package"
|
||||||
|
msgstr "Das Hostingpaket"
|
||||||
|
|
||||||
|
#: hostingpackages/models.py:518
|
||||||
|
msgid "data source"
|
||||||
|
msgstr "Datenquelle"
|
||||||
|
|
||||||
|
#: hostingpackages/models.py:520
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:202
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:42
|
||||||
|
msgid "Mailbox"
|
||||||
|
msgstr "Postfach"
|
||||||
|
|
||||||
|
#: hostingpackages/models.py:521
|
||||||
|
msgid "Website"
|
||||||
|
msgstr "Webauftritt"
|
||||||
|
|
||||||
|
#: hostingpackages/models.py:522
|
||||||
|
msgid "MariaDB database"
|
||||||
|
msgstr "MariaDB-Datenbank"
|
||||||
|
|
||||||
|
#: hostingpackages/models.py:523
|
||||||
|
msgid "PostgreSQL database"
|
||||||
|
msgstr "PostgreSQL-Datenbank"
|
||||||
|
|
||||||
|
#: hostingpackages/models.py:526
|
||||||
|
msgid "data item"
|
||||||
|
msgstr "Dateneinheit"
|
||||||
|
|
||||||
|
#: hostingpackages/models.py:528
|
||||||
|
msgid "space used in KiB"
|
||||||
|
msgstr "genutzter Platz in KiB"
|
||||||
|
|
||||||
|
#: hostingpackages/models.py:532
|
||||||
|
msgid "mail address"
|
||||||
|
msgstr "E-Mailadresse"
|
||||||
|
|
||||||
|
#: hostingpackages/models.py:533
|
||||||
|
msgid "Assigned mail address"
|
||||||
|
msgstr "Zugeordnete E-Mailadresse"
|
||||||
|
|
||||||
|
#: hostingpackages/models.py:539
|
||||||
|
msgid "website"
|
||||||
|
msgstr "Webauftritt"
|
||||||
|
|
||||||
|
#: hostingpackages/models.py:540
|
||||||
|
msgid "Assigned web site"
|
||||||
|
msgstr "Zugeordneter Webauftritt"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/add_hosting_option.html:4
|
||||||
|
#: hostingpackages/templates/hostingpackages/add_hosting_option.html:7
|
||||||
|
#, python-format
|
||||||
|
msgid "Add Option to Hosting Package %(package)s of Customer %(full_name)s"
|
||||||
|
msgstr ""
|
||||||
|
"Option zum Hostingpaket %(package)s des Kunden %(full_name)s hinzufügen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:3
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:4
|
||||||
|
msgid "All hosting packages"
|
||||||
|
msgstr "Alle Hostingpakete"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:11
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:36
|
||||||
|
msgid "Name"
|
||||||
|
msgstr "Name"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:12
|
||||||
|
msgid "Customer"
|
||||||
|
msgstr "Kunde"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:13
|
||||||
|
msgid "OS User"
|
||||||
|
msgstr "OS-Nutzer"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:14
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:40
|
||||||
|
#: hostingpackages/views.py:184
|
||||||
|
msgid "Disk space"
|
||||||
|
msgstr "Speicherplatz"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:15
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:54
|
||||||
|
#: hostingpackages/views.py:191
|
||||||
|
msgid "Mailboxes"
|
||||||
|
msgstr "Postfächer"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:16
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:239
|
||||||
|
#: hostingpackages/views.py:198
|
||||||
|
msgid "Databases"
|
||||||
|
msgstr "Datenbanken"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:17
|
||||||
|
msgid "Setup date"
|
||||||
|
msgstr "Einrichtungsdatum"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:28
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"The reserved disk space for your hosting package is %(diskspace)s bytes."
|
||||||
|
msgstr ""
|
||||||
|
"Der für Ihr Hostingpaket reservierte Speicherplatz beträgt %(diskspace)s "
|
||||||
|
"Bytes."
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:34
|
||||||
|
#, python-format
|
||||||
|
msgid "used %(num)s of %(total)s"
|
||||||
|
msgstr "used %(num)s of %(total)s"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:48
|
||||||
|
msgid "No hosting packages have been setup yet."
|
||||||
|
msgstr "Es wurden noch keine Hostingpakete eingerichtet."
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:51
|
||||||
|
msgid "Add hosting package"
|
||||||
|
msgstr "Hostingpaket anlegen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_create.html:3
|
||||||
|
#, python-format
|
||||||
|
msgid "Add hosting package for Customer %(full_name)s"
|
||||||
|
msgstr "Hostingpaket für Kunde %(full_name)s hinzufügen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_create.html:6
|
||||||
|
#, python-format
|
||||||
|
msgid "Add Hosting Package for Customer %(full_name)s"
|
||||||
|
msgstr "Hosting Paket für Kunde %(full_name)s"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:6
|
||||||
|
#, python-format
|
||||||
|
msgid "Details for your Hosting Package %(package)s"
|
||||||
|
msgstr "Details zu Ihrem Hostingpaket %(package)s"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:10
|
||||||
|
#, python-format
|
||||||
|
msgid "Details for Hosting Package %(package)s of %(full_name)s"
|
||||||
|
msgstr "Details zum Hostingpaket %(package)s von %(full_name)s"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:16
|
||||||
|
#, python-format
|
||||||
|
msgid "Details of Hosting Package %(package)s"
|
||||||
|
msgstr "Details zum Hostingpaket %(package)s"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:25
|
||||||
|
msgid "Hosting Package Information"
|
||||||
|
msgstr "Informationen zum Hostingpaket"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:29
|
||||||
|
msgid "Edit Hosting Package Information"
|
||||||
|
msgstr "Informationen zum Hostingpaket ändern"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:38
|
||||||
|
msgid "Description"
|
||||||
|
msgstr "Beschreibung"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:43
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"You use %(used_space)s of the reserved disk space of %(disk_space)s for your "
|
||||||
|
"hosting package"
|
||||||
|
msgstr ""
|
||||||
|
"Sie nutzen aktuell %(used_space)s des reservierten Speicherplatzes von "
|
||||||
|
"%(disk_space)s für Ihr Hostingpaket"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:45
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:28
|
||||||
|
#, python-format
|
||||||
|
msgid "%(used_space)s of %(disk_space)s (%(space_level_percent)s%%)"
|
||||||
|
msgstr "%(used_space)s von %(disk_space)s (%(space_level_percent)s%%)"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:47
|
||||||
|
msgid "Disk usage details"
|
||||||
|
msgstr "Details zur Speicherplatznutzung"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:47
|
||||||
|
msgid "Details"
|
||||||
|
msgstr "Details"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:49
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:32
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"The package contributes %(package_space)s the difference comes from disk "
|
||||||
|
"space options"
|
||||||
|
msgstr ""
|
||||||
|
"Das Paket trägt %(package_space)s zur Gesamtgröße bei, der Unterschied "
|
||||||
|
"ergibt sich aus Speicherplatzoptionen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:56
|
||||||
|
#, python-format
|
||||||
|
msgid "%(num)s of %(total)s in use"
|
||||||
|
msgstr "%(num)s von %(total)s genutzt"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:59
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"The package provides %(mailboxcount)s mailboxes the difference comes from "
|
||||||
|
"mailbox options."
|
||||||
|
msgstr ""
|
||||||
|
"Das Paket bietet %(mailboxcount)s Postfächer, der Unterschied ergibt sich "
|
||||||
|
"durch die Postfachoptionen."
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:61
|
||||||
|
msgid "SFTP username"
|
||||||
|
msgstr "SFTP-Benutzername"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:62
|
||||||
|
msgid "SSH/SFTP username"
|
||||||
|
msgstr "SSH/SFTP-Benutzername"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:65
|
||||||
|
#, python-format
|
||||||
|
msgid "There is an SSH public key set for this user."
|
||||||
|
msgid_plural "There are %(counter)s SSH public keys set for this user."
|
||||||
|
msgstr[0] "Es wurde ein SSH-Schlüssel für diesen Nutzer hinterlegt."
|
||||||
|
msgstr[1] "Es wurden %(counter)s SSH-Schlüssel für diesen Nutzer hinterlegt."
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:67
|
||||||
|
msgid "Upload server"
|
||||||
|
msgstr "Uploadserver"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:75
|
||||||
|
msgid "Hosting Package Options"
|
||||||
|
msgstr "Hostingpaketoptionen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:83
|
||||||
|
msgid "No options booked"
|
||||||
|
msgstr "Keine Optionen gebucht"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:89
|
||||||
|
msgid "Add another hosting option"
|
||||||
|
msgstr "Eine weitere Hostingoption hinzufügen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:89
|
||||||
|
msgid "Add option"
|
||||||
|
msgstr "Option hinzufügen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:96
|
||||||
|
msgid "Hosting Package Actions"
|
||||||
|
msgstr "Aktionen zum Hostingpaket"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:100
|
||||||
|
msgid "Edit Hosting Package Description"
|
||||||
|
msgstr "Beschreibung des Hostingpakets bearbeiten"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:100
|
||||||
|
msgid "Edit description"
|
||||||
|
msgstr "Beschreibung bearbeiten"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:103
|
||||||
|
msgid "Set SFTP password"
|
||||||
|
msgstr "SFTP-Passwort setzen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:104
|
||||||
|
msgid "Set SSH/SFTP password"
|
||||||
|
msgstr "SSH/SFTP-Passwort setzen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:107
|
||||||
|
msgid "Add an SSH public key that can be used as an alternative for password"
|
||||||
|
msgstr ""
|
||||||
|
"Einen SSH-Schlüssel, der als Alternative zum Passwort genutzt werden kann, "
|
||||||
|
"hinzufügen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:109
|
||||||
|
msgid "Add SSH public key"
|
||||||
|
msgstr "SSH-Schlüssel hinzufügen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:118
|
||||||
|
msgid "Domains"
|
||||||
|
msgstr "Domains"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:123
|
||||||
|
msgid "Domain name"
|
||||||
|
msgstr "Domainname"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:124
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:203
|
||||||
|
msgid "Mail addresses"
|
||||||
|
msgstr "E-Mailadressen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:125
|
||||||
|
msgid "Websites"
|
||||||
|
msgstr "Webauftritte"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:126
|
||||||
|
msgid "Domain actions"
|
||||||
|
msgstr "Domainaktionen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:127
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:206
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:249
|
||||||
|
msgid "Actions"
|
||||||
|
msgstr "Aktionen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:139
|
||||||
|
msgid "Edit mail address targets"
|
||||||
|
msgstr "E-Mailadressziele bearbeiten"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:141
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:143
|
||||||
|
msgid "Delete mail address"
|
||||||
|
msgstr "E-Mailadresse löschen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:148
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:163
|
||||||
|
msgid "None"
|
||||||
|
msgstr "Keine"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:156
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:158
|
||||||
|
msgid "Delete website"
|
||||||
|
msgstr "Webauftritt löschen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:169
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:171
|
||||||
|
msgid "Add mail address"
|
||||||
|
msgstr "E-Mailadresse hinzufügen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:176
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:177
|
||||||
|
msgid "Add website"
|
||||||
|
msgstr "Webauftritt anlegen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:185
|
||||||
|
msgid "There are no domains assigned to this hosting package yet."
|
||||||
|
msgstr "Diesem Paket sind noch keine Domains zugeordnet."
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:189
|
||||||
|
msgid "Add domain"
|
||||||
|
msgstr "Domain hinzufügen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:197
|
||||||
|
msgid "E-Mail-Accounts"
|
||||||
|
msgstr "E-Mail-Konten"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:204
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:216
|
||||||
|
msgid "Active"
|
||||||
|
msgstr "Aktiv"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:205
|
||||||
|
msgid "Mailbox actions"
|
||||||
|
msgstr "Postfachaktionen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:217
|
||||||
|
msgid "inactive"
|
||||||
|
msgstr "inaktiv"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:220
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:221
|
||||||
|
msgid "Set mailbox password"
|
||||||
|
msgstr "Postfachpasswort setzen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:227
|
||||||
|
msgid "There are no mailboxes assigned to this hosting package yet."
|
||||||
|
msgstr "Diesem Hostingpaket sind noch keine Postfächer zugeordnet."
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:232
|
||||||
|
msgid "Add mailbox"
|
||||||
|
msgstr "Postfach hinzufügen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:244
|
||||||
|
msgid "Database name"
|
||||||
|
msgstr "Datenbankname"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:245
|
||||||
|
msgid "Database user"
|
||||||
|
msgstr "Datenbanknutzer"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:246
|
||||||
|
msgid "Database type"
|
||||||
|
msgstr "Datenbanktyp"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:247
|
||||||
|
msgid "Type"
|
||||||
|
msgstr "Typ"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:248
|
||||||
|
msgid "Database actions"
|
||||||
|
msgstr "Datenbankaktionen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:260
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:262
|
||||||
|
msgid "Set database user password"
|
||||||
|
msgstr "Datenbanknutzerpasswort setzen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:264
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:265
|
||||||
|
msgid "Delete database"
|
||||||
|
msgstr "Datenbank löschen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:272
|
||||||
|
msgid "There are no databases assigned to this hosting package yet."
|
||||||
|
msgstr "Diesem Hostingpaket sind noch keine Datenbanken zugeordnet."
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:277
|
||||||
|
msgid "Add database"
|
||||||
|
msgstr "Datenbank hinzufügen"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:6
|
||||||
|
#, python-format
|
||||||
|
msgid "Disk usage of your Hosting Package %(package)s"
|
||||||
|
msgstr "Speicherplatznutzung Ihres Hostingpakets %(package)s"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:10
|
||||||
|
#, python-format
|
||||||
|
msgid "Disk usage of Hosting Package %(package)s of %(full_name)s"
|
||||||
|
msgstr "Speicherplatznutzung des Hostingpakets %(package)s von %(full_name)s"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:17
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"Disk usage of Hosting Package <a href=\"%(package_url)s\">%(package)s</a>"
|
||||||
|
msgstr ""
|
||||||
|
"Speicherplatznutzung des Hostingpakets <a "
|
||||||
|
"href=\"%(package_url)s\">%(package)s</a>"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:23
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"You use %(used_space)s of the reserved disk space of %(disk_space)s for your "
|
||||||
|
"hosting package."
|
||||||
|
msgstr ""
|
||||||
|
"Sie nutzen %(used_space)s des reservierten Speicherplatzes von "
|
||||||
|
"%(disk_space)s für Ihr Hostingpaket."
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:36
|
||||||
|
msgid "Breakdown by usage"
|
||||||
|
msgstr "Aufgliederung nach Nutzung"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:38
|
||||||
|
msgid "Mailbox usage"
|
||||||
|
msgstr "Postfachnutzung"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:43
|
||||||
|
msgid "Primary email address"
|
||||||
|
msgstr "Primäre E-Mailadresse"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:44
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:64
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:83
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:102
|
||||||
|
msgid "Used space"
|
||||||
|
msgstr "Genutzter Speicherplatz"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:59
|
||||||
|
msgid "Website usage"
|
||||||
|
msgstr "Nutzung für Webauftritte"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:63
|
||||||
|
msgid "Website / Directory"
|
||||||
|
msgstr "Webauftritt / Verzeichnis"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:78
|
||||||
|
msgid "MySQL/MariaDB database usage"
|
||||||
|
msgstr "MySQL/MariaDB-Datenbanknutzung"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:82
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:101
|
||||||
|
msgid "Database"
|
||||||
|
msgstr "Datenbank"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:97
|
||||||
|
msgid "PostgreSQL database usage"
|
||||||
|
msgstr "PostgreSQL-Datenbanknutzung"
|
||||||
|
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_option_choices.html:5
|
||||||
|
#: hostingpackages/templates/hostingpackages/customerhostingpackage_option_choices.html:8
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"Choose new Option for Hosting Package %(package)s of Customer %(full_name)s"
|
||||||
|
msgstr ""
|
||||||
|
"Wählen Sie eine neue Option für das Hostingpaket %(package)s des Kunden "
|
||||||
|
"%(full_name)s"
|
||||||
|
|
||||||
|
#: hostingpackages/views.py:63 hostingpackages/views.py:97
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Started setup of new hosting package {name}."
|
msgid "Started setup of new hosting package {name}."
|
||||||
msgstr "Einrichtung des Hostingpakets {name} wurde gestartet."
|
msgstr "Einrichtung des Hostingpakets {name} wurde gestartet."
|
||||||
|
|
||||||
#: hostingpackages/views.py:186
|
#: hostingpackages/views.py:272
|
||||||
msgid "Disk space"
|
|
||||||
msgstr "Speicherplatz"
|
|
||||||
|
|
||||||
#: hostingpackages/views.py:189
|
|
||||||
msgid "Mailboxes"
|
|
||||||
msgstr "Postfächer"
|
|
||||||
|
|
||||||
#: hostingpackages/views.py:192
|
|
||||||
msgid "Databases"
|
|
||||||
msgstr "Datenbanken"
|
|
||||||
|
|
||||||
#: hostingpackages/views.py:262
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Successfully added option {option} to hosting package {package}."
|
msgid "Successfully added option {option} to hosting package {package}."
|
||||||
msgstr "Option {option} erfolgreich zum Hostingpaket {package} hinzugefügt."
|
msgstr "Option {option} erfolgreich zum Hostingpaket {package} hinzugefügt."
|
||||||
|
|
||||||
#~ msgid "Hosting options"
|
|
||||||
#~ msgstr "Hostingoptionen"
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import django.utils.timezone
|
import django.utils.timezone
|
||||||
import model_utils.fields
|
import model_utils.fields
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -14,301 +12,469 @@ class Migration(migrations.Migration):
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='CustomerHostingPackage',
|
name="CustomerHostingPackage",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(
|
(
|
||||||
verbose_name='ID', serialize=False, auto_created=True,
|
"id",
|
||||||
primary_key=True)),
|
models.AutoField(
|
||||||
('created', model_utils.fields.AutoCreatedField(
|
verbose_name="ID",
|
||||||
default=django.utils.timezone.now, verbose_name='created',
|
serialize=False,
|
||||||
editable=False)),
|
auto_created=True,
|
||||||
('modified', model_utils.fields.AutoLastModifiedField(
|
primary_key=True,
|
||||||
default=django.utils.timezone.now, verbose_name='modified',
|
),
|
||||||
editable=False)),
|
),
|
||||||
('name', models.CharField(
|
(
|
||||||
unique=True, max_length=128, verbose_name='name')),
|
"created",
|
||||||
('description', models.TextField(
|
model_utils.fields.AutoCreatedField(
|
||||||
verbose_name='description', blank=True)),
|
default=django.utils.timezone.now,
|
||||||
('mailboxcount', models.PositiveIntegerField(
|
verbose_name="created",
|
||||||
verbose_name='mailbox count')),
|
editable=False,
|
||||||
('diskspace', models.PositiveIntegerField(
|
),
|
||||||
help_text='disk space for the hosting package',
|
),
|
||||||
verbose_name='disk space')),
|
(
|
||||||
('diskspace_unit', models.PositiveSmallIntegerField(
|
"modified",
|
||||||
verbose_name='unit of disk space',
|
model_utils.fields.AutoLastModifiedField(
|
||||||
choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])),
|
default=django.utils.timezone.now,
|
||||||
('customer', models.ForeignKey(
|
verbose_name="modified",
|
||||||
verbose_name='customer', to=settings.AUTH_USER_MODEL,
|
editable=False,
|
||||||
on_delete=models.CASCADE)),
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"name",
|
||||||
|
models.CharField(unique=True, max_length=128, verbose_name="name"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"description",
|
||||||
|
models.TextField(verbose_name="description", blank=True),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"mailboxcount",
|
||||||
|
models.PositiveIntegerField(verbose_name="mailbox count"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"diskspace",
|
||||||
|
models.PositiveIntegerField(
|
||||||
|
help_text="disk space for the hosting package",
|
||||||
|
verbose_name="disk space",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"diskspace_unit",
|
||||||
|
models.PositiveSmallIntegerField(
|
||||||
|
verbose_name="unit of disk space",
|
||||||
|
choices=[(0, "MiB"), (1, "GiB"), (2, "TiB")],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"customer",
|
||||||
|
models.ForeignKey(
|
||||||
|
verbose_name="customer",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'customer hosting package',
|
"verbose_name": "customer hosting package",
|
||||||
'verbose_name_plural': 'customer hosting packages',
|
"verbose_name_plural": "customer hosting packages",
|
||||||
},
|
},
|
||||||
bases=(models.Model,),
|
bases=(models.Model,),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='CustomerHostingPackageOption',
|
name="CustomerHostingPackageOption",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(
|
(
|
||||||
verbose_name='ID', serialize=False, auto_created=True,
|
"id",
|
||||||
primary_key=True)),
|
models.AutoField(
|
||||||
('created', model_utils.fields.AutoCreatedField(
|
verbose_name="ID",
|
||||||
default=django.utils.timezone.now, verbose_name='created',
|
serialize=False,
|
||||||
editable=False)),
|
auto_created=True,
|
||||||
('modified', model_utils.fields.AutoLastModifiedField(
|
primary_key=True,
|
||||||
default=django.utils.timezone.now, verbose_name='modified',
|
),
|
||||||
editable=False)),
|
),
|
||||||
|
(
|
||||||
|
"created",
|
||||||
|
model_utils.fields.AutoCreatedField(
|
||||||
|
default=django.utils.timezone.now,
|
||||||
|
verbose_name="created",
|
||||||
|
editable=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"modified",
|
||||||
|
model_utils.fields.AutoLastModifiedField(
|
||||||
|
default=django.utils.timezone.now,
|
||||||
|
verbose_name="modified",
|
||||||
|
editable=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'customer hosting option',
|
"verbose_name": "customer hosting option",
|
||||||
'verbose_name_plural': 'customer hosting options',
|
"verbose_name_plural": "customer hosting options",
|
||||||
},
|
},
|
||||||
bases=(models.Model,),
|
bases=(models.Model,),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='CustomerDiskSpaceOption',
|
name="CustomerDiskSpaceOption",
|
||||||
fields=[
|
fields=[
|
||||||
('customerhostingpackageoption_ptr', models.OneToOneField(
|
(
|
||||||
parent_link=True, auto_created=True, primary_key=True,
|
"customerhostingpackageoption_ptr",
|
||||||
serialize=False,
|
models.OneToOneField(
|
||||||
to='hostingpackages.CustomerHostingPackageOption',
|
parent_link=True,
|
||||||
on_delete=models.CASCADE)),
|
auto_created=True,
|
||||||
('diskspace', models.PositiveIntegerField(
|
primary_key=True,
|
||||||
verbose_name='disk space')),
|
serialize=False,
|
||||||
('diskspace_unit', models.PositiveSmallIntegerField(
|
to="hostingpackages.CustomerHostingPackageOption",
|
||||||
verbose_name='unit of disk space',
|
on_delete=models.CASCADE,
|
||||||
choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])),
|
),
|
||||||
|
),
|
||||||
|
("diskspace", models.PositiveIntegerField(verbose_name="disk space")),
|
||||||
|
(
|
||||||
|
"diskspace_unit",
|
||||||
|
models.PositiveSmallIntegerField(
|
||||||
|
verbose_name="unit of disk space",
|
||||||
|
choices=[(0, "MiB"), (1, "GiB"), (2, "TiB")],
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ['diskspace_unit', 'diskspace'],
|
"ordering": ["diskspace_unit", "diskspace"],
|
||||||
'abstract': False,
|
"abstract": False,
|
||||||
'verbose_name': 'Disk space option',
|
"verbose_name": "Disk space option",
|
||||||
'verbose_name_plural': 'Disk space options',
|
"verbose_name_plural": "Disk space options",
|
||||||
},
|
},
|
||||||
bases=(
|
bases=("hostingpackages.customerhostingpackageoption", models.Model),
|
||||||
'hostingpackages.customerhostingpackageoption', models.Model),
|
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='CustomerMailboxOption',
|
name="CustomerMailboxOption",
|
||||||
fields=[
|
fields=[
|
||||||
('customerhostingpackageoption_ptr', models.OneToOneField(
|
(
|
||||||
parent_link=True, auto_created=True, primary_key=True,
|
"customerhostingpackageoption_ptr",
|
||||||
serialize=False,
|
models.OneToOneField(
|
||||||
to='hostingpackages.CustomerHostingPackageOption',
|
parent_link=True,
|
||||||
on_delete=models.CASCADE)),
|
auto_created=True,
|
||||||
('number', models.PositiveIntegerField(
|
primary_key=True,
|
||||||
unique=True, verbose_name='number of mailboxes')),
|
serialize=False,
|
||||||
|
to="hostingpackages.CustomerHostingPackageOption",
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"number",
|
||||||
|
models.PositiveIntegerField(
|
||||||
|
unique=True, verbose_name="number of mailboxes"
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ['number'],
|
"ordering": ["number"],
|
||||||
'abstract': False,
|
"abstract": False,
|
||||||
'verbose_name': 'Mailbox option',
|
"verbose_name": "Mailbox option",
|
||||||
'verbose_name_plural': 'Mailbox options',
|
"verbose_name_plural": "Mailbox options",
|
||||||
},
|
},
|
||||||
bases=(
|
bases=("hostingpackages.customerhostingpackageoption", models.Model),
|
||||||
'hostingpackages.customerhostingpackageoption', models.Model),
|
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='CustomerUserDatabaseOption',
|
name="CustomerUserDatabaseOption",
|
||||||
fields=[
|
fields=[
|
||||||
('customerhostingpackageoption_ptr', models.OneToOneField(
|
(
|
||||||
parent_link=True, auto_created=True, primary_key=True,
|
"customerhostingpackageoption_ptr",
|
||||||
serialize=False,
|
models.OneToOneField(
|
||||||
to='hostingpackages.CustomerHostingPackageOption',
|
parent_link=True,
|
||||||
on_delete=models.CASCADE)),
|
auto_created=True,
|
||||||
('number', models.PositiveIntegerField(
|
primary_key=True,
|
||||||
default=1, verbose_name='number of databases')),
|
serialize=False,
|
||||||
('db_type', models.PositiveSmallIntegerField(
|
to="hostingpackages.CustomerHostingPackageOption",
|
||||||
verbose_name='database type',
|
on_delete=models.CASCADE,
|
||||||
choices=[(0, 'PostgreSQL'), (1, 'MySQL')])),
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"number",
|
||||||
|
models.PositiveIntegerField(
|
||||||
|
default=1, verbose_name="number of databases"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"db_type",
|
||||||
|
models.PositiveSmallIntegerField(
|
||||||
|
verbose_name="database type",
|
||||||
|
choices=[(0, "PostgreSQL"), (1, "MySQL")],
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ['db_type', 'number'],
|
"ordering": ["db_type", "number"],
|
||||||
'abstract': False,
|
"abstract": False,
|
||||||
'verbose_name': 'Database option',
|
"verbose_name": "Database option",
|
||||||
'verbose_name_plural': 'Database options',
|
"verbose_name_plural": "Database options",
|
||||||
},
|
},
|
||||||
bases=(
|
bases=("hostingpackages.customerhostingpackageoption", models.Model),
|
||||||
'hostingpackages.customerhostingpackageoption', models.Model),
|
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='HostingOption',
|
name="HostingOption",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(
|
(
|
||||||
verbose_name='ID', serialize=False, auto_created=True,
|
"id",
|
||||||
primary_key=True)),
|
models.AutoField(
|
||||||
('created', model_utils.fields.AutoCreatedField(
|
verbose_name="ID",
|
||||||
default=django.utils.timezone.now, verbose_name='created',
|
serialize=False,
|
||||||
editable=False)),
|
auto_created=True,
|
||||||
('modified', model_utils.fields.AutoLastModifiedField(
|
primary_key=True,
|
||||||
default=django.utils.timezone.now, verbose_name='modified',
|
),
|
||||||
editable=False)),
|
),
|
||||||
|
(
|
||||||
|
"created",
|
||||||
|
model_utils.fields.AutoCreatedField(
|
||||||
|
default=django.utils.timezone.now,
|
||||||
|
verbose_name="created",
|
||||||
|
editable=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"modified",
|
||||||
|
model_utils.fields.AutoLastModifiedField(
|
||||||
|
default=django.utils.timezone.now,
|
||||||
|
verbose_name="modified",
|
||||||
|
editable=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Hosting option',
|
"verbose_name": "Hosting option",
|
||||||
'verbose_name_plural': 'Hosting options',
|
"verbose_name_plural": "Hosting options",
|
||||||
},
|
},
|
||||||
bases=(models.Model,),
|
bases=(models.Model,),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='DiskSpaceOption',
|
name="DiskSpaceOption",
|
||||||
fields=[
|
fields=[
|
||||||
('hostingoption_ptr', models.OneToOneField(
|
(
|
||||||
parent_link=True, auto_created=True, primary_key=True,
|
"hostingoption_ptr",
|
||||||
serialize=False, to='hostingpackages.HostingOption',
|
models.OneToOneField(
|
||||||
on_delete=models.CASCADE)),
|
parent_link=True,
|
||||||
('diskspace', models.PositiveIntegerField(
|
auto_created=True,
|
||||||
verbose_name='disk space')),
|
primary_key=True,
|
||||||
('diskspace_unit', models.PositiveSmallIntegerField(
|
serialize=False,
|
||||||
verbose_name='unit of disk space',
|
to="hostingpackages.HostingOption",
|
||||||
choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])),
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("diskspace", models.PositiveIntegerField(verbose_name="disk space")),
|
||||||
|
(
|
||||||
|
"diskspace_unit",
|
||||||
|
models.PositiveSmallIntegerField(
|
||||||
|
verbose_name="unit of disk space",
|
||||||
|
choices=[(0, "MiB"), (1, "GiB"), (2, "TiB")],
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ['diskspace_unit', 'diskspace'],
|
"ordering": ["diskspace_unit", "diskspace"],
|
||||||
'abstract': False,
|
"abstract": False,
|
||||||
'verbose_name': 'Disk space option',
|
"verbose_name": "Disk space option",
|
||||||
'verbose_name_plural': 'Disk space options',
|
"verbose_name_plural": "Disk space options",
|
||||||
},
|
},
|
||||||
bases=('hostingpackages.hostingoption', models.Model),
|
bases=("hostingpackages.hostingoption", models.Model),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='HostingPackageTemplate',
|
name="HostingPackageTemplate",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(
|
(
|
||||||
verbose_name='ID', serialize=False, auto_created=True,
|
"id",
|
||||||
primary_key=True)),
|
models.AutoField(
|
||||||
('created', model_utils.fields.AutoCreatedField(
|
verbose_name="ID",
|
||||||
default=django.utils.timezone.now, verbose_name='created',
|
serialize=False,
|
||||||
editable=False)),
|
auto_created=True,
|
||||||
('modified', model_utils.fields.AutoLastModifiedField(
|
primary_key=True,
|
||||||
default=django.utils.timezone.now, verbose_name='modified',
|
),
|
||||||
editable=False)),
|
),
|
||||||
('name', models.CharField(
|
(
|
||||||
unique=True, max_length=128, verbose_name='name')),
|
"created",
|
||||||
('description', models.TextField(
|
model_utils.fields.AutoCreatedField(
|
||||||
verbose_name='description', blank=True)),
|
default=django.utils.timezone.now,
|
||||||
('mailboxcount', models.PositiveIntegerField(
|
verbose_name="created",
|
||||||
verbose_name='mailbox count')),
|
editable=False,
|
||||||
('diskspace', models.PositiveIntegerField(
|
),
|
||||||
help_text='disk space for the hosting package',
|
),
|
||||||
verbose_name='disk space')),
|
(
|
||||||
('diskspace_unit', models.PositiveSmallIntegerField(
|
"modified",
|
||||||
verbose_name='unit of disk space',
|
model_utils.fields.AutoLastModifiedField(
|
||||||
choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])),
|
default=django.utils.timezone.now,
|
||||||
|
verbose_name="modified",
|
||||||
|
editable=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"name",
|
||||||
|
models.CharField(unique=True, max_length=128, verbose_name="name"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"description",
|
||||||
|
models.TextField(verbose_name="description", blank=True),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"mailboxcount",
|
||||||
|
models.PositiveIntegerField(verbose_name="mailbox count"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"diskspace",
|
||||||
|
models.PositiveIntegerField(
|
||||||
|
help_text="disk space for the hosting package",
|
||||||
|
verbose_name="disk space",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"diskspace_unit",
|
||||||
|
models.PositiveSmallIntegerField(
|
||||||
|
verbose_name="unit of disk space",
|
||||||
|
choices=[(0, "MiB"), (1, "GiB"), (2, "TiB")],
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Hosting package',
|
"verbose_name": "Hosting package",
|
||||||
'verbose_name_plural': 'Hosting packages',
|
"verbose_name_plural": "Hosting packages",
|
||||||
},
|
},
|
||||||
bases=(models.Model,),
|
bases=(models.Model,),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='MailboxOption',
|
name="MailboxOption",
|
||||||
fields=[
|
fields=[
|
||||||
('hostingoption_ptr', models.OneToOneField(
|
(
|
||||||
parent_link=True, auto_created=True, primary_key=True,
|
"hostingoption_ptr",
|
||||||
serialize=False, to='hostingpackages.HostingOption',
|
models.OneToOneField(
|
||||||
on_delete=models.CASCADE)),
|
parent_link=True,
|
||||||
('number', models.PositiveIntegerField(
|
auto_created=True,
|
||||||
unique=True, verbose_name='number of mailboxes')),
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
to="hostingpackages.HostingOption",
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"number",
|
||||||
|
models.PositiveIntegerField(
|
||||||
|
unique=True, verbose_name="number of mailboxes"
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ['number'],
|
"ordering": ["number"],
|
||||||
'abstract': False,
|
"abstract": False,
|
||||||
'verbose_name': 'Mailbox option',
|
"verbose_name": "Mailbox option",
|
||||||
'verbose_name_plural': 'Mailbox options',
|
"verbose_name_plural": "Mailbox options",
|
||||||
},
|
},
|
||||||
bases=('hostingpackages.hostingoption', models.Model),
|
bases=("hostingpackages.hostingoption", models.Model),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='UserDatabaseOption',
|
name="UserDatabaseOption",
|
||||||
fields=[
|
fields=[
|
||||||
('hostingoption_ptr', models.OneToOneField(
|
(
|
||||||
parent_link=True, auto_created=True, primary_key=True,
|
"hostingoption_ptr",
|
||||||
serialize=False, to='hostingpackages.HostingOption',
|
models.OneToOneField(
|
||||||
on_delete=models.CASCADE)),
|
parent_link=True,
|
||||||
('number', models.PositiveIntegerField(
|
auto_created=True,
|
||||||
default=1, verbose_name='number of databases')),
|
primary_key=True,
|
||||||
('db_type', models.PositiveSmallIntegerField(
|
serialize=False,
|
||||||
verbose_name='database type',
|
to="hostingpackages.HostingOption",
|
||||||
choices=[(0, 'PostgreSQL'), (1, 'MySQL')])),
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"number",
|
||||||
|
models.PositiveIntegerField(
|
||||||
|
default=1, verbose_name="number of databases"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"db_type",
|
||||||
|
models.PositiveSmallIntegerField(
|
||||||
|
verbose_name="database type",
|
||||||
|
choices=[(0, "PostgreSQL"), (1, "MySQL")],
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ['db_type', 'number'],
|
"ordering": ["db_type", "number"],
|
||||||
'abstract': False,
|
"abstract": False,
|
||||||
'verbose_name': 'Database option',
|
"verbose_name": "Database option",
|
||||||
'verbose_name_plural': 'Database options',
|
"verbose_name_plural": "Database options",
|
||||||
},
|
},
|
||||||
bases=('hostingpackages.hostingoption', models.Model),
|
bases=("hostingpackages.hostingoption", models.Model),
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='userdatabaseoption',
|
name="userdatabaseoption",
|
||||||
unique_together={('number', 'db_type')},
|
unique_together={("number", "db_type")},
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='diskspaceoption',
|
name="diskspaceoption",
|
||||||
unique_together={('diskspace', 'diskspace_unit')},
|
unique_together={("diskspace", "diskspace_unit")},
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='customeruserdatabaseoption',
|
model_name="customeruserdatabaseoption",
|
||||||
name='template',
|
name="template",
|
||||||
field=models.ForeignKey(
|
field=models.ForeignKey(
|
||||||
verbose_name='user database option template',
|
verbose_name="user database option template",
|
||||||
to='hostingpackages.UserDatabaseOption',
|
to="hostingpackages.UserDatabaseOption",
|
||||||
help_text='The user database option template that this '
|
help_text="The user database option template that this "
|
||||||
'hosting option is based on',
|
"hosting option is based on",
|
||||||
on_delete=models.CASCADE),
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
preserve_default=True,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='customeruserdatabaseoption',
|
name="customeruserdatabaseoption",
|
||||||
unique_together={('number', 'db_type')},
|
unique_together={("number", "db_type")},
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='customermailboxoption',
|
model_name="customermailboxoption",
|
||||||
name='template',
|
name="template",
|
||||||
field=models.ForeignKey(
|
field=models.ForeignKey(
|
||||||
verbose_name='mailbox option template',
|
verbose_name="mailbox option template",
|
||||||
to='hostingpackages.UserDatabaseOption',
|
to="hostingpackages.UserDatabaseOption",
|
||||||
help_text='The mailbox option template that this hosting '
|
help_text="The mailbox option template that this hosting "
|
||||||
'option is based on',
|
"option is based on",
|
||||||
on_delete=models.CASCADE),
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
preserve_default=True,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='customerhostingpackageoption',
|
model_name="customerhostingpackageoption",
|
||||||
name='hosting_package',
|
name="hosting_package",
|
||||||
field=models.ForeignKey(
|
field=models.ForeignKey(
|
||||||
verbose_name='hosting package',
|
verbose_name="hosting package",
|
||||||
to='hostingpackages.CustomerHostingPackage',
|
to="hostingpackages.CustomerHostingPackage",
|
||||||
on_delete=models.CASCADE),
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
preserve_default=True,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='customerhostingpackage',
|
model_name="customerhostingpackage",
|
||||||
name='template',
|
name="template",
|
||||||
field=models.ForeignKey(
|
field=models.ForeignKey(
|
||||||
verbose_name='hosting package template',
|
verbose_name="hosting package template",
|
||||||
to='hostingpackages.HostingPackageTemplate',
|
to="hostingpackages.HostingPackageTemplate",
|
||||||
help_text='The hosting package template that this hosting '
|
help_text="The hosting package template that this hosting "
|
||||||
'package is based on.',
|
"package is based on.",
|
||||||
on_delete=models.CASCADE),
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
preserve_default=True,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='customerdiskspaceoption',
|
model_name="customerdiskspaceoption",
|
||||||
name='template',
|
name="template",
|
||||||
field=models.ForeignKey(
|
field=models.ForeignKey(
|
||||||
verbose_name='disk space option template',
|
verbose_name="disk space option template",
|
||||||
to='hostingpackages.DiskSpaceOption',
|
to="hostingpackages.DiskSpaceOption",
|
||||||
help_text='The disk space option template that this hosting '
|
help_text="The disk space option template that this hosting "
|
||||||
'option is based on',
|
"option is based on",
|
||||||
on_delete=models.CASCADE),
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
preserve_default=True,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='customerdiskspaceoption',
|
name="customerdiskspaceoption",
|
||||||
unique_together={('diskspace', 'diskspace_unit')},
|
unique_together={("diskspace", "diskspace_unit")},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import django.utils.timezone
|
import django.utils.timezone
|
||||||
import model_utils.fields
|
import model_utils.fields
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -8,361 +6,530 @@ from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
replaces = [('hostingpackages', '0001_initial'),
|
replaces = [
|
||||||
('hostingpackages', '0002_auto_20150118_1149'),
|
("hostingpackages", "0001_initial"),
|
||||||
('hostingpackages', '0003_auto_20150118_1221'),
|
("hostingpackages", "0002_auto_20150118_1149"),
|
||||||
('hostingpackages', '0004_customerhostingpackage_osuser'),
|
("hostingpackages", "0003_auto_20150118_1221"),
|
||||||
('hostingpackages', '0005_auto_20150118_1303')]
|
("hostingpackages", "0004_customerhostingpackage_osuser"),
|
||||||
|
("hostingpackages", "0005_auto_20150118_1303"),
|
||||||
|
]
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
('osusers', '0004_auto_20150104_1751'),
|
("osusers", "0004_auto_20150104_1751"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='CustomerHostingPackage',
|
name="CustomerHostingPackage",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(
|
(
|
||||||
verbose_name='ID', serialize=False, auto_created=True,
|
"id",
|
||||||
primary_key=True)),
|
models.AutoField(
|
||||||
('created', model_utils.fields.AutoCreatedField(
|
verbose_name="ID",
|
||||||
default=django.utils.timezone.now, verbose_name='created',
|
serialize=False,
|
||||||
editable=False)),
|
auto_created=True,
|
||||||
('modified', model_utils.fields.AutoLastModifiedField(
|
primary_key=True,
|
||||||
default=django.utils.timezone.now, verbose_name='modified',
|
),
|
||||||
editable=False)),
|
),
|
||||||
('name', models.CharField(
|
(
|
||||||
unique=True, max_length=128, verbose_name='name')),
|
"created",
|
||||||
('description', models.TextField(
|
model_utils.fields.AutoCreatedField(
|
||||||
verbose_name='description', blank=True)),
|
default=django.utils.timezone.now,
|
||||||
('mailboxcount', models.PositiveIntegerField(
|
verbose_name="created",
|
||||||
verbose_name='mailbox count')),
|
editable=False,
|
||||||
('diskspace', models.PositiveIntegerField(
|
),
|
||||||
help_text='disk space for the hosting package',
|
),
|
||||||
verbose_name='disk space')),
|
(
|
||||||
('diskspace_unit', models.PositiveSmallIntegerField(
|
"modified",
|
||||||
verbose_name='unit of disk space',
|
model_utils.fields.AutoLastModifiedField(
|
||||||
choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])),
|
default=django.utils.timezone.now,
|
||||||
('customer', models.ForeignKey(
|
verbose_name="modified",
|
||||||
verbose_name='customer',
|
editable=False,
|
||||||
to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"name",
|
||||||
|
models.CharField(unique=True, max_length=128, verbose_name="name"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"description",
|
||||||
|
models.TextField(verbose_name="description", blank=True),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"mailboxcount",
|
||||||
|
models.PositiveIntegerField(verbose_name="mailbox count"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"diskspace",
|
||||||
|
models.PositiveIntegerField(
|
||||||
|
help_text="disk space for the hosting package",
|
||||||
|
verbose_name="disk space",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"diskspace_unit",
|
||||||
|
models.PositiveSmallIntegerField(
|
||||||
|
verbose_name="unit of disk space",
|
||||||
|
choices=[(0, "MiB"), (1, "GiB"), (2, "TiB")],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"customer",
|
||||||
|
models.ForeignKey(
|
||||||
|
verbose_name="customer",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'customer hosting package',
|
"verbose_name": "customer hosting package",
|
||||||
'verbose_name_plural': 'customer hosting packages',
|
"verbose_name_plural": "customer hosting packages",
|
||||||
},
|
},
|
||||||
bases=(models.Model,),
|
bases=(models.Model,),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='CustomerHostingPackageOption',
|
name="CustomerHostingPackageOption",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(
|
(
|
||||||
verbose_name='ID', serialize=False, auto_created=True,
|
"id",
|
||||||
primary_key=True)),
|
models.AutoField(
|
||||||
('created', model_utils.fields.AutoCreatedField(
|
verbose_name="ID",
|
||||||
default=django.utils.timezone.now, verbose_name='created',
|
serialize=False,
|
||||||
editable=False)),
|
auto_created=True,
|
||||||
('modified', model_utils.fields.AutoLastModifiedField(
|
primary_key=True,
|
||||||
default=django.utils.timezone.now, verbose_name='modified',
|
),
|
||||||
editable=False)),
|
),
|
||||||
|
(
|
||||||
|
"created",
|
||||||
|
model_utils.fields.AutoCreatedField(
|
||||||
|
default=django.utils.timezone.now,
|
||||||
|
verbose_name="created",
|
||||||
|
editable=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"modified",
|
||||||
|
model_utils.fields.AutoLastModifiedField(
|
||||||
|
default=django.utils.timezone.now,
|
||||||
|
verbose_name="modified",
|
||||||
|
editable=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'customer hosting option',
|
"verbose_name": "customer hosting option",
|
||||||
'verbose_name_plural': 'customer hosting options',
|
"verbose_name_plural": "customer hosting options",
|
||||||
},
|
},
|
||||||
bases=(models.Model,),
|
bases=(models.Model,),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='CustomerDiskSpaceOption',
|
name="CustomerDiskSpaceOption",
|
||||||
fields=[
|
fields=[
|
||||||
('customerhostingpackageoption_ptr',
|
(
|
||||||
models.OneToOneField(
|
"customerhostingpackageoption_ptr",
|
||||||
parent_link=True, auto_created=True, primary_key=True,
|
models.OneToOneField(
|
||||||
serialize=False,
|
parent_link=True,
|
||||||
to='hostingpackages.CustomerHostingPackageOption',
|
auto_created=True,
|
||||||
on_delete=models.CASCADE)),
|
primary_key=True,
|
||||||
('diskspace', models.PositiveIntegerField(
|
serialize=False,
|
||||||
verbose_name='disk space')),
|
to="hostingpackages.CustomerHostingPackageOption",
|
||||||
('diskspace_unit', models.PositiveSmallIntegerField(
|
on_delete=models.CASCADE,
|
||||||
verbose_name='unit of disk space',
|
),
|
||||||
choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])),
|
),
|
||||||
|
("diskspace", models.PositiveIntegerField(verbose_name="disk space")),
|
||||||
|
(
|
||||||
|
"diskspace_unit",
|
||||||
|
models.PositiveSmallIntegerField(
|
||||||
|
verbose_name="unit of disk space",
|
||||||
|
choices=[(0, "MiB"), (1, "GiB"), (2, "TiB")],
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ['diskspace_unit', 'diskspace'],
|
"ordering": ["diskspace_unit", "diskspace"],
|
||||||
'abstract': False,
|
"abstract": False,
|
||||||
'verbose_name': 'Disk space option',
|
"verbose_name": "Disk space option",
|
||||||
'verbose_name_plural': 'Disk space options',
|
"verbose_name_plural": "Disk space options",
|
||||||
},
|
},
|
||||||
bases=(
|
bases=("hostingpackages.customerhostingpackageoption", models.Model),
|
||||||
'hostingpackages.customerhostingpackageoption', models.Model),
|
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='CustomerMailboxOption',
|
name="CustomerMailboxOption",
|
||||||
fields=[
|
fields=[
|
||||||
('customerhostingpackageoption_ptr',
|
(
|
||||||
models.OneToOneField(
|
"customerhostingpackageoption_ptr",
|
||||||
parent_link=True, auto_created=True, primary_key=True,
|
models.OneToOneField(
|
||||||
serialize=False,
|
parent_link=True,
|
||||||
to='hostingpackages.CustomerHostingPackageOption',
|
auto_created=True,
|
||||||
on_delete=models.CASCADE)),
|
primary_key=True,
|
||||||
('number', models.PositiveIntegerField(
|
serialize=False,
|
||||||
unique=True, verbose_name='number of mailboxes')),
|
to="hostingpackages.CustomerHostingPackageOption",
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"number",
|
||||||
|
models.PositiveIntegerField(
|
||||||
|
unique=True, verbose_name="number of mailboxes"
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ['number'],
|
"ordering": ["number"],
|
||||||
'abstract': False,
|
"abstract": False,
|
||||||
'verbose_name': 'Mailbox option',
|
"verbose_name": "Mailbox option",
|
||||||
'verbose_name_plural': 'Mailbox options',
|
"verbose_name_plural": "Mailbox options",
|
||||||
},
|
},
|
||||||
bases=(
|
bases=("hostingpackages.customerhostingpackageoption", models.Model),
|
||||||
'hostingpackages.customerhostingpackageoption', models.Model),
|
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='CustomerUserDatabaseOption',
|
name="CustomerUserDatabaseOption",
|
||||||
fields=[
|
fields=[
|
||||||
('customerhostingpackageoption_ptr',
|
(
|
||||||
models.OneToOneField(
|
"customerhostingpackageoption_ptr",
|
||||||
parent_link=True, auto_created=True, primary_key=True,
|
models.OneToOneField(
|
||||||
serialize=False,
|
parent_link=True,
|
||||||
to='hostingpackages.CustomerHostingPackageOption',
|
auto_created=True,
|
||||||
on_delete=models.CASCADE)),
|
primary_key=True,
|
||||||
('number', models.PositiveIntegerField(
|
serialize=False,
|
||||||
default=1, verbose_name='number of databases')),
|
to="hostingpackages.CustomerHostingPackageOption",
|
||||||
('db_type', models.PositiveSmallIntegerField(
|
on_delete=models.CASCADE,
|
||||||
verbose_name='database type',
|
),
|
||||||
choices=[(0, 'PostgreSQL'), (1, 'MySQL')])),
|
),
|
||||||
|
(
|
||||||
|
"number",
|
||||||
|
models.PositiveIntegerField(
|
||||||
|
default=1, verbose_name="number of databases"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"db_type",
|
||||||
|
models.PositiveSmallIntegerField(
|
||||||
|
verbose_name="database type",
|
||||||
|
choices=[(0, "PostgreSQL"), (1, "MySQL")],
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ['db_type', 'number'],
|
"ordering": ["db_type", "number"],
|
||||||
'abstract': False,
|
"abstract": False,
|
||||||
'verbose_name': 'Database option',
|
"verbose_name": "Database option",
|
||||||
'verbose_name_plural': 'Database options',
|
"verbose_name_plural": "Database options",
|
||||||
},
|
},
|
||||||
bases=(
|
bases=("hostingpackages.customerhostingpackageoption", models.Model),
|
||||||
'hostingpackages.customerhostingpackageoption', models.Model),
|
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='HostingOption',
|
name="HostingOption",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(
|
(
|
||||||
verbose_name='ID', serialize=False, auto_created=True,
|
"id",
|
||||||
primary_key=True)),
|
models.AutoField(
|
||||||
('created', model_utils.fields.AutoCreatedField(
|
verbose_name="ID",
|
||||||
default=django.utils.timezone.now, verbose_name='created',
|
serialize=False,
|
||||||
editable=False)),
|
auto_created=True,
|
||||||
('modified', model_utils.fields.AutoLastModifiedField(
|
primary_key=True,
|
||||||
default=django.utils.timezone.now, verbose_name='modified',
|
),
|
||||||
editable=False)),
|
),
|
||||||
|
(
|
||||||
|
"created",
|
||||||
|
model_utils.fields.AutoCreatedField(
|
||||||
|
default=django.utils.timezone.now,
|
||||||
|
verbose_name="created",
|
||||||
|
editable=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"modified",
|
||||||
|
model_utils.fields.AutoLastModifiedField(
|
||||||
|
default=django.utils.timezone.now,
|
||||||
|
verbose_name="modified",
|
||||||
|
editable=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Hosting option',
|
"verbose_name": "Hosting option",
|
||||||
'verbose_name_plural': 'Hosting options',
|
"verbose_name_plural": "Hosting options",
|
||||||
},
|
},
|
||||||
bases=(models.Model,),
|
bases=(models.Model,),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='DiskSpaceOption',
|
name="DiskSpaceOption",
|
||||||
fields=[
|
fields=[
|
||||||
('hostingoption_ptr',
|
(
|
||||||
models.OneToOneField(
|
"hostingoption_ptr",
|
||||||
parent_link=True, auto_created=True, primary_key=True,
|
models.OneToOneField(
|
||||||
serialize=False, to='hostingpackages.HostingOption',
|
parent_link=True,
|
||||||
on_delete=models.CASCADE)),
|
auto_created=True,
|
||||||
('diskspace', models.PositiveIntegerField(
|
primary_key=True,
|
||||||
verbose_name='disk space')),
|
serialize=False,
|
||||||
('diskspace_unit', models.PositiveSmallIntegerField(
|
to="hostingpackages.HostingOption",
|
||||||
verbose_name='unit of disk space',
|
on_delete=models.CASCADE,
|
||||||
choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])),
|
),
|
||||||
|
),
|
||||||
|
("diskspace", models.PositiveIntegerField(verbose_name="disk space")),
|
||||||
|
(
|
||||||
|
"diskspace_unit",
|
||||||
|
models.PositiveSmallIntegerField(
|
||||||
|
verbose_name="unit of disk space",
|
||||||
|
choices=[(0, "MiB"), (1, "GiB"), (2, "TiB")],
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ['diskspace_unit', 'diskspace'],
|
"ordering": ["diskspace_unit", "diskspace"],
|
||||||
'abstract': False,
|
"abstract": False,
|
||||||
'verbose_name': 'Disk space option',
|
"verbose_name": "Disk space option",
|
||||||
'verbose_name_plural': 'Disk space options',
|
"verbose_name_plural": "Disk space options",
|
||||||
},
|
},
|
||||||
bases=('hostingpackages.hostingoption', models.Model),
|
bases=("hostingpackages.hostingoption", models.Model),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='HostingPackageTemplate',
|
name="HostingPackageTemplate",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(
|
(
|
||||||
verbose_name='ID', serialize=False, auto_created=True,
|
"id",
|
||||||
primary_key=True)),
|
models.AutoField(
|
||||||
('created', model_utils.fields.AutoCreatedField(
|
verbose_name="ID",
|
||||||
default=django.utils.timezone.now, verbose_name='created',
|
serialize=False,
|
||||||
editable=False)),
|
auto_created=True,
|
||||||
('modified', model_utils.fields.AutoLastModifiedField(
|
primary_key=True,
|
||||||
default=django.utils.timezone.now, verbose_name='modified',
|
),
|
||||||
editable=False)),
|
),
|
||||||
('name', models.CharField(
|
(
|
||||||
unique=True, max_length=128, verbose_name='name')),
|
"created",
|
||||||
('description', models.TextField(
|
model_utils.fields.AutoCreatedField(
|
||||||
verbose_name='description', blank=True)),
|
default=django.utils.timezone.now,
|
||||||
('mailboxcount', models.PositiveIntegerField(
|
verbose_name="created",
|
||||||
verbose_name='mailbox count')),
|
editable=False,
|
||||||
('diskspace', models.PositiveIntegerField(
|
),
|
||||||
help_text='disk space for the hosting package',
|
),
|
||||||
verbose_name='disk space')),
|
(
|
||||||
('diskspace_unit', models.PositiveSmallIntegerField(
|
"modified",
|
||||||
verbose_name='unit of disk space',
|
model_utils.fields.AutoLastModifiedField(
|
||||||
choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])),
|
default=django.utils.timezone.now,
|
||||||
|
verbose_name="modified",
|
||||||
|
editable=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"name",
|
||||||
|
models.CharField(unique=True, max_length=128, verbose_name="name"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"description",
|
||||||
|
models.TextField(verbose_name="description", blank=True),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"mailboxcount",
|
||||||
|
models.PositiveIntegerField(verbose_name="mailbox count"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"diskspace",
|
||||||
|
models.PositiveIntegerField(
|
||||||
|
help_text="disk space for the hosting package",
|
||||||
|
verbose_name="disk space",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"diskspace_unit",
|
||||||
|
models.PositiveSmallIntegerField(
|
||||||
|
verbose_name="unit of disk space",
|
||||||
|
choices=[(0, "MiB"), (1, "GiB"), (2, "TiB")],
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Hosting package',
|
"verbose_name": "Hosting package",
|
||||||
'verbose_name_plural': 'Hosting packages',
|
"verbose_name_plural": "Hosting packages",
|
||||||
},
|
},
|
||||||
bases=(models.Model,),
|
bases=(models.Model,),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='MailboxOption',
|
name="MailboxOption",
|
||||||
fields=[
|
fields=[
|
||||||
('hostingoption_ptr',
|
(
|
||||||
models.OneToOneField(
|
"hostingoption_ptr",
|
||||||
parent_link=True, auto_created=True, primary_key=True,
|
models.OneToOneField(
|
||||||
serialize=False, to='hostingpackages.HostingOption',
|
parent_link=True,
|
||||||
on_delete=models.CASCADE)),
|
auto_created=True,
|
||||||
('number', models.PositiveIntegerField(
|
primary_key=True,
|
||||||
unique=True, verbose_name='number of mailboxes')),
|
serialize=False,
|
||||||
|
to="hostingpackages.HostingOption",
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"number",
|
||||||
|
models.PositiveIntegerField(
|
||||||
|
unique=True, verbose_name="number of mailboxes"
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ['number'],
|
"ordering": ["number"],
|
||||||
'abstract': False,
|
"abstract": False,
|
||||||
'verbose_name': 'Mailbox option',
|
"verbose_name": "Mailbox option",
|
||||||
'verbose_name_plural': 'Mailbox options',
|
"verbose_name_plural": "Mailbox options",
|
||||||
},
|
},
|
||||||
bases=('hostingpackages.hostingoption', models.Model),
|
bases=("hostingpackages.hostingoption", models.Model),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='UserDatabaseOption',
|
name="UserDatabaseOption",
|
||||||
fields=[
|
fields=[
|
||||||
('hostingoption_ptr',
|
(
|
||||||
models.OneToOneField(
|
"hostingoption_ptr",
|
||||||
parent_link=True, auto_created=True, primary_key=True,
|
models.OneToOneField(
|
||||||
serialize=False, to='hostingpackages.HostingOption',
|
parent_link=True,
|
||||||
on_delete=models.CASCADE)),
|
auto_created=True,
|
||||||
('number', models.PositiveIntegerField(
|
primary_key=True,
|
||||||
default=1, verbose_name='number of databases')),
|
serialize=False,
|
||||||
('db_type',
|
to="hostingpackages.HostingOption",
|
||||||
models.PositiveSmallIntegerField(
|
on_delete=models.CASCADE,
|
||||||
verbose_name='database type',
|
),
|
||||||
choices=[(0, 'PostgreSQL'), (1, 'MySQL')])),
|
),
|
||||||
|
(
|
||||||
|
"number",
|
||||||
|
models.PositiveIntegerField(
|
||||||
|
default=1, verbose_name="number of databases"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"db_type",
|
||||||
|
models.PositiveSmallIntegerField(
|
||||||
|
verbose_name="database type",
|
||||||
|
choices=[(0, "PostgreSQL"), (1, "MySQL")],
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ['db_type', 'number'],
|
"ordering": ["db_type", "number"],
|
||||||
'abstract': False,
|
"abstract": False,
|
||||||
'verbose_name': 'Database option',
|
"verbose_name": "Database option",
|
||||||
'verbose_name_plural': 'Database options',
|
"verbose_name_plural": "Database options",
|
||||||
},
|
},
|
||||||
bases=('hostingpackages.hostingoption', models.Model),
|
bases=("hostingpackages.hostingoption", models.Model),
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='userdatabaseoption',
|
name="userdatabaseoption",
|
||||||
unique_together={('number', 'db_type')},
|
unique_together={("number", "db_type")},
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='diskspaceoption',
|
name="diskspaceoption",
|
||||||
unique_together={('diskspace', 'diskspace_unit')},
|
unique_together={("diskspace", "diskspace_unit")},
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='customeruserdatabaseoption',
|
model_name="customeruserdatabaseoption",
|
||||||
name='template',
|
name="template",
|
||||||
field=models.ForeignKey(
|
field=models.ForeignKey(
|
||||||
verbose_name='user database option template',
|
verbose_name="user database option template",
|
||||||
to='hostingpackages.UserDatabaseOption',
|
to="hostingpackages.UserDatabaseOption",
|
||||||
help_text='The user database option template that this '
|
help_text="The user database option template that this "
|
||||||
'hosting option is based on',
|
"hosting option is based on",
|
||||||
on_delete=models.CASCADE),
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
preserve_default=True,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='customeruserdatabaseoption',
|
name="customeruserdatabaseoption",
|
||||||
unique_together={('number', 'db_type')},
|
unique_together={("number", "db_type")},
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='customermailboxoption',
|
model_name="customermailboxoption",
|
||||||
name='template',
|
name="template",
|
||||||
field=models.ForeignKey(
|
field=models.ForeignKey(
|
||||||
verbose_name='mailbox option template',
|
verbose_name="mailbox option template",
|
||||||
to='hostingpackages.UserDatabaseOption',
|
to="hostingpackages.UserDatabaseOption",
|
||||||
help_text='The mailbox option template that this mailbox '
|
help_text="The mailbox option template that this mailbox "
|
||||||
'option is based on',
|
"option is based on",
|
||||||
on_delete=models.CASCADE),
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
preserve_default=True,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='customerhostingpackageoption',
|
model_name="customerhostingpackageoption",
|
||||||
name='hosting_package',
|
name="hosting_package",
|
||||||
field=models.ForeignKey(
|
field=models.ForeignKey(
|
||||||
verbose_name='hosting package',
|
verbose_name="hosting package",
|
||||||
to='hostingpackages.CustomerHostingPackage',
|
to="hostingpackages.CustomerHostingPackage",
|
||||||
on_delete=models.CASCADE),
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
preserve_default=True,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='customerhostingpackage',
|
model_name="customerhostingpackage",
|
||||||
name='template',
|
name="template",
|
||||||
field=models.ForeignKey(
|
field=models.ForeignKey(
|
||||||
verbose_name='hosting package template',
|
verbose_name="hosting package template",
|
||||||
to='hostingpackages.HostingPackageTemplate',
|
to="hostingpackages.HostingPackageTemplate",
|
||||||
help_text='The hosting package template that this hosting '
|
help_text="The hosting package template that this hosting "
|
||||||
'package is based on',
|
"package is based on",
|
||||||
on_delete=models.CASCADE),
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
preserve_default=True,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='customerdiskspaceoption',
|
model_name="customerdiskspaceoption",
|
||||||
name='template',
|
name="template",
|
||||||
field=models.ForeignKey(
|
field=models.ForeignKey(
|
||||||
verbose_name='disk space option template',
|
verbose_name="disk space option template",
|
||||||
to='hostingpackages.DiskSpaceOption',
|
to="hostingpackages.DiskSpaceOption",
|
||||||
help_text='The disk space option template that this hosting '
|
help_text="The disk space option template that this hosting "
|
||||||
'option is based on',
|
"option is based on",
|
||||||
on_delete=models.CASCADE),
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
preserve_default=True,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='customerdiskspaceoption',
|
name="customerdiskspaceoption",
|
||||||
unique_together={('diskspace', 'diskspace_unit')},
|
unique_together={("diskspace", "diskspace_unit")},
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='customerdiskspaceoption',
|
model_name="customerdiskspaceoption",
|
||||||
name='template',
|
name="template",
|
||||||
field=models.ForeignKey(
|
field=models.ForeignKey(
|
||||||
verbose_name='disk space option template',
|
verbose_name="disk space option template",
|
||||||
to='hostingpackages.DiskSpaceOption',
|
to="hostingpackages.DiskSpaceOption",
|
||||||
help_text='The disk space option template that this disk '
|
help_text="The disk space option template that this disk "
|
||||||
'space option is based on',
|
"space option is based on",
|
||||||
on_delete=models.CASCADE),
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
preserve_default=True,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='customeruserdatabaseoption',
|
model_name="customeruserdatabaseoption",
|
||||||
name='template',
|
name="template",
|
||||||
field=models.ForeignKey(
|
field=models.ForeignKey(
|
||||||
verbose_name='user database option template',
|
verbose_name="user database option template",
|
||||||
to='hostingpackages.UserDatabaseOption',
|
to="hostingpackages.UserDatabaseOption",
|
||||||
help_text='The user database option template that this '
|
help_text="The user database option template that this "
|
||||||
'database option is based on',
|
"database option is based on",
|
||||||
on_delete=models.CASCADE),
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
preserve_default=True,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='customerhostingpackage',
|
model_name="customerhostingpackage",
|
||||||
name='name',
|
name="name",
|
||||||
field=models.CharField(max_length=128, verbose_name='name'),
|
field=models.CharField(max_length=128, verbose_name="name"),
|
||||||
preserve_default=True,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='customerhostingpackage',
|
name="customerhostingpackage",
|
||||||
unique_together={('customer', 'name')},
|
unique_together={("customer", "name")},
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='customerhostingpackage',
|
model_name="customerhostingpackage",
|
||||||
name='osuser',
|
name="osuser",
|
||||||
field=models.OneToOneField(
|
field=models.OneToOneField(
|
||||||
null=True, blank=True, to='osusers.User',
|
null=True,
|
||||||
verbose_name='Operating system user', on_delete=models.CASCADE),
|
blank=True,
|
||||||
|
to="osusers.User",
|
||||||
|
verbose_name="Operating system user",
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
preserve_default=True,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,57 +1,59 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('hostingpackages', '0001_initial'),
|
("hostingpackages", "0001_initial"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='customerdiskspaceoption',
|
model_name="customerdiskspaceoption",
|
||||||
name='template',
|
name="template",
|
||||||
field=models.ForeignKey(
|
field=models.ForeignKey(
|
||||||
verbose_name='disk space option template',
|
verbose_name="disk space option template",
|
||||||
to='hostingpackages.DiskSpaceOption',
|
to="hostingpackages.DiskSpaceOption",
|
||||||
help_text='The disk space option template that this disk '
|
help_text="The disk space option template that this disk "
|
||||||
'space option is based on',
|
"space option is based on",
|
||||||
on_delete=models.CASCADE),
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
preserve_default=True,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='customerhostingpackage',
|
model_name="customerhostingpackage",
|
||||||
name='template',
|
name="template",
|
||||||
field=models.ForeignKey(
|
field=models.ForeignKey(
|
||||||
verbose_name='hosting package template',
|
verbose_name="hosting package template",
|
||||||
to='hostingpackages.HostingPackageTemplate',
|
to="hostingpackages.HostingPackageTemplate",
|
||||||
help_text='The hosting package template that this hosting '
|
help_text="The hosting package template that this hosting "
|
||||||
'package is based on',
|
"package is based on",
|
||||||
on_delete=models.CASCADE),
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
preserve_default=True,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='customermailboxoption',
|
model_name="customermailboxoption",
|
||||||
name='template',
|
name="template",
|
||||||
field=models.ForeignKey(
|
field=models.ForeignKey(
|
||||||
verbose_name='mailbox option template',
|
verbose_name="mailbox option template",
|
||||||
to='hostingpackages.UserDatabaseOption',
|
to="hostingpackages.UserDatabaseOption",
|
||||||
help_text='The mailbox option template that this mailbox '
|
help_text="The mailbox option template that this mailbox "
|
||||||
'option is based on',
|
"option is based on",
|
||||||
on_delete=models.CASCADE),
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
preserve_default=True,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='customeruserdatabaseoption',
|
model_name="customeruserdatabaseoption",
|
||||||
name='template',
|
name="template",
|
||||||
field=models.ForeignKey(
|
field=models.ForeignKey(
|
||||||
verbose_name='user database option template',
|
verbose_name="user database option template",
|
||||||
to='hostingpackages.UserDatabaseOption',
|
to="hostingpackages.UserDatabaseOption",
|
||||||
help_text='The user database option template that this '
|
help_text="The user database option template that this "
|
||||||
'database option is based on',
|
"database option is based on",
|
||||||
on_delete=models.CASCADE),
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
preserve_default=True,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,18 +1,15 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from django.db import migrations
|
||||||
|
|
||||||
from django.db import models, migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('hostingpackages', '0001_squashed_0005_auto_20150118_1303'),
|
("hostingpackages", "0001_squashed_0005_auto_20150118_1303"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name='hostingoption',
|
name="hostingoption",
|
||||||
options={},
|
options={},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,24 +1,21 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from django.db import migrations, models
|
||||||
|
|
||||||
from django.db import models, migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('hostingpackages', '0002_auto_20150118_1149'),
|
("hostingpackages", "0002_auto_20150118_1149"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='customerhostingpackage',
|
model_name="customerhostingpackage",
|
||||||
name='name',
|
name="name",
|
||||||
field=models.CharField(max_length=128, verbose_name='name'),
|
field=models.CharField(max_length=128, verbose_name="name"),
|
||||||
preserve_default=True,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='customerhostingpackage',
|
name="customerhostingpackage",
|
||||||
unique_together=set([('customer', 'name')]),
|
unique_together=set([("customer", "name")]),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,24 +1,23 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('hostingpackages', '0002_auto_20150118_1319'),
|
("hostingpackages", "0002_auto_20150118_1319"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='customermailboxoption',
|
model_name="customermailboxoption",
|
||||||
name='template',
|
name="template",
|
||||||
field=models.ForeignKey(
|
field=models.ForeignKey(
|
||||||
verbose_name='mailbox option template',
|
verbose_name="mailbox option template",
|
||||||
to='hostingpackages.MailboxOption',
|
to="hostingpackages.MailboxOption",
|
||||||
help_text='The mailbox option template that this mailbox '
|
help_text="The mailbox option template that this mailbox "
|
||||||
'option is based on',
|
"option is based on",
|
||||||
on_delete=models.CASCADE),
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
preserve_default=True,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,22 +1,24 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('osusers', '0004_auto_20150104_1751'),
|
("osusers", "0004_auto_20150104_1751"),
|
||||||
('hostingpackages', '0003_auto_20150118_1221'),
|
("hostingpackages", "0003_auto_20150118_1221"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='customerhostingpackage',
|
model_name="customerhostingpackage",
|
||||||
name='osuser',
|
name="osuser",
|
||||||
field=models.ForeignKey(
|
field=models.ForeignKey(
|
||||||
verbose_name='Operating system user', blank=True,
|
verbose_name="Operating system user",
|
||||||
to='osusers.User', null=True, on_delete=models.CASCADE),
|
blank=True,
|
||||||
|
to="osusers.User",
|
||||||
|
null=True,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
preserve_default=True,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import django.utils.timezone
|
import django.utils.timezone
|
||||||
import model_utils.fields
|
import model_utils.fields
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
@ -8,33 +6,59 @@ from django.db import migrations, models
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('domains', '0002_auto_20150124_1909'),
|
("domains", "0002_auto_20150124_1909"),
|
||||||
('hostingpackages', '0003_auto_20150118_1407'),
|
("hostingpackages", "0003_auto_20150118_1407"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='CustomerHostingPackageDomain',
|
name="CustomerHostingPackageDomain",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(
|
(
|
||||||
verbose_name='ID', serialize=False, auto_created=True,
|
"id",
|
||||||
primary_key=True)),
|
models.AutoField(
|
||||||
('created', model_utils.fields.AutoCreatedField(
|
verbose_name="ID",
|
||||||
default=django.utils.timezone.now, verbose_name='created',
|
serialize=False,
|
||||||
editable=False)),
|
auto_created=True,
|
||||||
('modified', model_utils.fields.AutoLastModifiedField(
|
primary_key=True,
|
||||||
default=django.utils.timezone.now, verbose_name='modified',
|
),
|
||||||
editable=False)),
|
),
|
||||||
('domain', models.OneToOneField(
|
(
|
||||||
verbose_name='hosting domain', to='domains.HostingDomain',
|
"created",
|
||||||
on_delete=models.CASCADE)),
|
model_utils.fields.AutoCreatedField(
|
||||||
('hosting_package', models.ForeignKey(
|
default=django.utils.timezone.now,
|
||||||
related_name='domains', verbose_name='hosting package',
|
verbose_name="created",
|
||||||
to='hostingpackages.CustomerHostingPackage',
|
editable=False,
|
||||||
on_delete=models.CASCADE)),
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"modified",
|
||||||
|
model_utils.fields.AutoLastModifiedField(
|
||||||
|
default=django.utils.timezone.now,
|
||||||
|
verbose_name="modified",
|
||||||
|
editable=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"domain",
|
||||||
|
models.OneToOneField(
|
||||||
|
verbose_name="hosting domain",
|
||||||
|
to="domains.HostingDomain",
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"hosting_package",
|
||||||
|
models.ForeignKey(
|
||||||
|
related_name="domains",
|
||||||
|
verbose_name="hosting package",
|
||||||
|
to="hostingpackages.CustomerHostingPackage",
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'abstract': False,
|
"abstract": False,
|
||||||
},
|
},
|
||||||
bases=(models.Model,),
|
bases=(models.Model,),
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,21 +1,23 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('hostingpackages', '0004_customerhostingpackage_osuser'),
|
("hostingpackages", "0004_customerhostingpackage_osuser"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='customerhostingpackage',
|
model_name="customerhostingpackage",
|
||||||
name='osuser',
|
name="osuser",
|
||||||
field=models.OneToOneField(
|
field=models.OneToOneField(
|
||||||
null=True, blank=True, to='osusers.User',
|
null=True,
|
||||||
verbose_name='Operating system user', on_delete=models.CASCADE),
|
blank=True,
|
||||||
|
to="osusers.User",
|
||||||
|
verbose_name="Operating system user",
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
),
|
||||||
preserve_default=True,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,22 +1,19 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from django.db import migrations
|
||||||
|
|
||||||
from django.db import models, migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('hostingpackages', '0004_customerhostingpackagedomain'),
|
("hostingpackages", "0004_customerhostingpackagedomain"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name='diskspaceoption',
|
name="diskspaceoption",
|
||||||
options={},
|
options={},
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='customerdiskspaceoption',
|
name="customerdiskspaceoption",
|
||||||
unique_together=set([]),
|
unique_together=set([]),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,22 +1,19 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from django.db import migrations
|
||||||
|
|
||||||
from django.db import models, migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('hostingpackages', '0005_auto_20150125_1508'),
|
("hostingpackages", "0005_auto_20150125_1508"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name='userdatabaseoption',
|
name="userdatabaseoption",
|
||||||
options={},
|
options={},
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='customeruserdatabaseoption',
|
name="customeruserdatabaseoption",
|
||||||
unique_together=set([]),
|
unique_together=set([]),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
# Generated by Django 4.2.3 on 2023-07-22 17:31
|
||||||
|
|
||||||
|
import django.utils.timezone
|
||||||
|
import model_utils.fields
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("hostingpackages", "0006_auto_20150125_1510"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="CustomerPackageDiskUsage",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"created",
|
||||||
|
model_utils.fields.AutoCreatedField(
|
||||||
|
default=django.utils.timezone.now,
|
||||||
|
editable=False,
|
||||||
|
verbose_name="created",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"modified",
|
||||||
|
model_utils.fields.AutoLastModifiedField(
|
||||||
|
default=django.utils.timezone.now,
|
||||||
|
editable=False,
|
||||||
|
verbose_name="modified",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"source",
|
||||||
|
models.CharField(
|
||||||
|
choices=[
|
||||||
|
("disk", "disk"),
|
||||||
|
("mysql", "mysql"),
|
||||||
|
("pgsql", "pgsql"),
|
||||||
|
],
|
||||||
|
verbose_name="data source",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("item", models.CharField(verbose_name="data item")),
|
||||||
|
(
|
||||||
|
"used_kb",
|
||||||
|
models.PositiveBigIntegerField(
|
||||||
|
default=0, verbose_name="space used in KiB"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"package",
|
||||||
|
models.ForeignKey(
|
||||||
|
help_text="The hosting package",
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="hostingpackages.customerhostingpackage",
|
||||||
|
verbose_name="hosting package",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"unique_together": {("package", "source", "item")},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,53 @@
|
||||||
|
# Generated by Django 4.2.3 on 2023-07-23 07:24
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("managemails", "0004_auto_20150125_1825"),
|
||||||
|
("websites", "0001_initial"),
|
||||||
|
("hostingpackages", "0007_add_disk_usage_table"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="customerpackagediskusage",
|
||||||
|
name="email_address",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
help_text="Assigned mail address",
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="managemails.mailaddress",
|
||||||
|
verbose_name="mail address",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="customerpackagediskusage",
|
||||||
|
name="website",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
help_text="Assigned web site",
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="websites.website",
|
||||||
|
verbose_name="website",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.RunSQL(
|
||||||
|
"DELETE FROM hostingpackages_customerpackagediskusage WHERE source='disk'"
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="customerpackagediskusage",
|
||||||
|
name="source",
|
||||||
|
field=models.CharField(
|
||||||
|
choices=[
|
||||||
|
("mail", "Mailbox"),
|
||||||
|
("web", "Website"),
|
||||||
|
("mysql", "MariaDB database"),
|
||||||
|
("pgsql", "PostgreSQL database"),
|
||||||
|
],
|
||||||
|
verbose_name="data source",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -2,21 +2,21 @@
|
||||||
This module contains the hosting package models.
|
This module contains the hosting package models.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import transaction
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.db import models
|
from django.db import models, transaction
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.utils.translation import ugettext_lazy as _, ungettext
|
from django.utils.translation import ngettext
|
||||||
|
|
||||||
from model_utils import Choices
|
from model_utils import Choices
|
||||||
from model_utils.models import TimeStampedModel
|
from model_utils.models import TimeStampedModel
|
||||||
|
|
||||||
from domains.models import HostingDomain
|
from domains.models import HostingDomain
|
||||||
from managemails.models import Mailbox
|
from managemails.models import Mailbox
|
||||||
from osusers.models import AdditionalGroup, Group, User as OsUser
|
from osusers.models import AdditionalGroup, Group
|
||||||
|
from osusers.models import User as OsUser
|
||||||
from userdbs.models import DB_TYPES, UserDatabase
|
from userdbs.models import DB_TYPES, UserDatabase
|
||||||
|
|
||||||
DISK_SPACE_UNITS = Choices((0, "M", _("MiB")), (1, "G", _("GiB")), (2, "T", _("TiB")))
|
DISK_SPACE_UNITS = Choices((0, "M", _("MiB")), (1, "G", _("GiB")), (2, "T", _("TiB")))
|
||||||
|
@ -24,7 +24,6 @@ DISK_SPACE_UNITS = Choices((0, "M", _("MiB")), (1, "G", _("GiB")), (2, "T", _("T
|
||||||
DISK_SPACE_FACTORS = ((1, None, None), (1024, 1, None), (1024 * 1024, 1024, 1))
|
DISK_SPACE_FACTORS = ((1, None, None), (1024, 1, None), (1024 * 1024, 1024, 1))
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class HostingPackageBase(TimeStampedModel):
|
class HostingPackageBase(TimeStampedModel):
|
||||||
description = models.TextField(_("description"), blank=True)
|
description = models.TextField(_("description"), blank=True)
|
||||||
mailboxcount = models.PositiveIntegerField(_("mailbox count"))
|
mailboxcount = models.PositiveIntegerField(_("mailbox count"))
|
||||||
|
@ -57,7 +56,6 @@ class HostingOption(TimeStampedModel):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class DiskSpaceOptionBase(models.Model):
|
class DiskSpaceOptionBase(models.Model):
|
||||||
diskspace = models.PositiveIntegerField(_("disk space"))
|
diskspace = models.PositiveIntegerField(_("disk space"))
|
||||||
diskspace_unit = models.PositiveSmallIntegerField(
|
diskspace_unit = models.PositiveSmallIntegerField(
|
||||||
|
@ -87,7 +85,6 @@ class DiskSpaceOption(DiskSpaceOptionBase, HostingOption):
|
||||||
unique_together = ["diskspace", "diskspace_unit"]
|
unique_together = ["diskspace", "diskspace_unit"]
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class UserDatabaseOptionBase(models.Model):
|
class UserDatabaseOptionBase(models.Model):
|
||||||
number = models.PositiveIntegerField(_("number of databases"), default=1)
|
number = models.PositiveIntegerField(_("number of databases"), default=1)
|
||||||
db_type = models.PositiveSmallIntegerField(_("database type"), choices=DB_TYPES)
|
db_type = models.PositiveSmallIntegerField(_("database type"), choices=DB_TYPES)
|
||||||
|
@ -99,7 +96,7 @@ class UserDatabaseOptionBase(models.Model):
|
||||||
verbose_name_plural = _("Database options")
|
verbose_name_plural = _("Database options")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return ungettext(
|
return ngettext(
|
||||||
"{type} database", "{count} {type} databases", self.number
|
"{type} database", "{count} {type} databases", self.number
|
||||||
).format(type=self.get_db_type_display(), count=self.number)
|
).format(type=self.get_db_type_display(), count=self.number)
|
||||||
|
|
||||||
|
@ -115,7 +112,6 @@ class UserDatabaseOption(UserDatabaseOptionBase, HostingOption):
|
||||||
unique_together = ["number", "db_type"]
|
unique_together = ["number", "db_type"]
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class MailboxOptionBase(models.Model):
|
class MailboxOptionBase(models.Model):
|
||||||
"""
|
"""
|
||||||
Base class for mailbox options.
|
Base class for mailbox options.
|
||||||
|
@ -131,7 +127,7 @@ class MailboxOptionBase(models.Model):
|
||||||
verbose_name_plural = _("Mailbox options")
|
verbose_name_plural = _("Mailbox options")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return ungettext(
|
return ngettext(
|
||||||
"{count} additional mailbox", "{count} additional mailboxes", self.number
|
"{count} additional mailbox", "{count} additional mailboxes", self.number
|
||||||
).format(count=self.number)
|
).format(count=self.number)
|
||||||
|
|
||||||
|
@ -177,7 +173,6 @@ class CustomerHostingPackageManager(models.Manager):
|
||||||
return package
|
return package
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class CustomerHostingPackage(HostingPackageBase):
|
class CustomerHostingPackage(HostingPackageBase):
|
||||||
"""
|
"""
|
||||||
This class defines customer specific hosting packages.
|
This class defines customer specific hosting packages.
|
||||||
|
@ -269,11 +264,38 @@ class CustomerHostingPackage(HostingPackageBase):
|
||||||
) + option.diskspace
|
) + option.diskspace
|
||||||
min_unit = option.diskspace_unit
|
min_unit = option.diskspace_unit
|
||||||
if unit is None:
|
if unit is None:
|
||||||
return DISK_SPACE_FACTORS[min_unit][0] * diskspace * 1024 ** 2
|
return DISK_SPACE_FACTORS[min_unit][0] * diskspace * 1024**2
|
||||||
if unit > min_unit:
|
if unit > min_unit:
|
||||||
return DISK_SPACE_FACTORS[unit][min_unit] * diskspace
|
return DISK_SPACE_FACTORS[unit][min_unit] * diskspace
|
||||||
return DISK_SPACE_FACTORS[min_unit][unit] * diskspace
|
return DISK_SPACE_FACTORS[min_unit][unit] * diskspace
|
||||||
|
|
||||||
|
disk_space = property(get_disk_space)
|
||||||
|
|
||||||
|
def get_used_disk_space_sum(self, unit=None):
|
||||||
|
"""
|
||||||
|
Get the used disk space of this hosting package from submitted disk space statistics.
|
||||||
|
|
||||||
|
:param unit: value from :py:data:`DISK_SPACE_UNITS` or :py:const:`None`
|
||||||
|
:return: disk space in unit or bytes (if parameter unit is :py:const:`None`)
|
||||||
|
:rtype: int
|
||||||
|
|
||||||
|
"""
|
||||||
|
sum = 0
|
||||||
|
for usage in self.customerpackagediskusage_set.all():
|
||||||
|
sum += usage.size_in_bytes
|
||||||
|
if unit is None:
|
||||||
|
return sum
|
||||||
|
return DISK_SPACE_FACTORS[0][unit] * sum
|
||||||
|
|
||||||
|
used_disk_space_sum = property(get_used_disk_space_sum)
|
||||||
|
|
||||||
|
def get_space_level(self):
|
||||||
|
if not self.diskspace:
|
||||||
|
return 100.0
|
||||||
|
return self.used_disk_space_sum / self.disk_space * 100.0
|
||||||
|
|
||||||
|
space_level = property(get_space_level)
|
||||||
|
|
||||||
def get_package_space(self, unit=None):
|
def get_package_space(self, unit=None):
|
||||||
"""
|
"""
|
||||||
Get the total disk space reserved for this package without looking at
|
Get the total disk space reserved for this package without looking at
|
||||||
|
@ -287,7 +309,7 @@ class CustomerHostingPackage(HostingPackageBase):
|
||||||
"""
|
"""
|
||||||
if unit is None:
|
if unit is None:
|
||||||
return (
|
return (
|
||||||
DISK_SPACE_FACTORS[self.diskspace_unit][0] * self.diskspace * 1024 ** 2
|
DISK_SPACE_FACTORS[self.diskspace_unit][0] * self.diskspace * 1024**2
|
||||||
)
|
)
|
||||||
if unit > self.diskspace_unit:
|
if unit > self.diskspace_unit:
|
||||||
return DISK_SPACE_FACTORS[unit][self.diskspace_unit] * self.diskspace
|
return DISK_SPACE_FACTORS[unit][self.diskspace_unit] * self.diskspace
|
||||||
|
@ -376,13 +398,17 @@ class CustomerHostingPackage(HostingPackageBase):
|
||||||
self.copy_template_attributes()
|
self.copy_template_attributes()
|
||||||
self.osuser = OsUser.objects.create_user(self.customer)
|
self.osuser = OsUser.objects.create_user(self.customer)
|
||||||
for group in settings.OSUSER_DEFAULT_GROUPS:
|
for group in settings.OSUSER_DEFAULT_GROUPS:
|
||||||
AdditionalGroup.objects.create(
|
try:
|
||||||
user=self.osuser, group=Group.objects.get(groupname=group)
|
AdditionalGroup.objects.create(
|
||||||
)
|
user=self.osuser, group=Group.objects.get(groupname=group)
|
||||||
|
)
|
||||||
|
except Group.DoesNotExist as e:
|
||||||
|
raise ImproperlyConfigured(
|
||||||
|
f"group {group} has not been defined"
|
||||||
|
) from e
|
||||||
return super(CustomerHostingPackage, self).save(*args, **kwargs)
|
return super(CustomerHostingPackage, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class CustomerHostingPackageDomain(TimeStampedModel):
|
class CustomerHostingPackageDomain(TimeStampedModel):
|
||||||
"""
|
"""
|
||||||
This class defines the relationship from a hosting package to a hosting
|
This class defines the relationship from a hosting package to a hosting
|
||||||
|
@ -475,3 +501,53 @@ class CustomerMailboxOption(MailboxOptionBase, CustomerHostingPackageOption):
|
||||||
help_text=_("The mailbox option template that this mailbox option is based on"),
|
help_text=_("The mailbox option template that this mailbox option is based on"),
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerPackageDiskUsage(TimeStampedModel):
|
||||||
|
"""
|
||||||
|
This class represents disk usage statistics for a customer hosting package.
|
||||||
|
"""
|
||||||
|
|
||||||
|
package = models.ForeignKey(
|
||||||
|
CustomerHostingPackage,
|
||||||
|
verbose_name=_("hosting package"),
|
||||||
|
help_text=_("The hosting package"),
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
)
|
||||||
|
source = models.CharField(
|
||||||
|
verbose_name=_("data source"),
|
||||||
|
choices=(
|
||||||
|
("mail", _("Mailbox")),
|
||||||
|
("web", _("Website")),
|
||||||
|
("mysql", _("MariaDB database")),
|
||||||
|
("pgsql", _("PostgreSQL database")),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
item = models.CharField(verbose_name=_("data item"))
|
||||||
|
used_kb = models.PositiveBigIntegerField(
|
||||||
|
verbose_name=_("space used in KiB"), default=0
|
||||||
|
)
|
||||||
|
email_address = models.ForeignKey(
|
||||||
|
"managemails.MailAddress",
|
||||||
|
verbose_name=_("mail address"),
|
||||||
|
help_text=_("Assigned mail address"),
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
null=True,
|
||||||
|
)
|
||||||
|
website = models.ForeignKey(
|
||||||
|
"websites.Website",
|
||||||
|
verbose_name=_("website"),
|
||||||
|
help_text=_("Assigned web site"),
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
null=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = ("package", "source", "item")
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "%s %s = %d KiB" % (self.source, self.item, self.used_kb)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def size_in_bytes(self):
|
||||||
|
return self.used_kb * 1024
|
||||||
|
|
7
gnuviechadmin/hostingpackages/serializers.py
Normal file
7
gnuviechadmin/hostingpackages/serializers.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from hostingpackages.models import CustomerPackageDiskUsage
|
||||||
|
|
||||||
|
|
||||||
|
class DiskUsageSerializer(serializers.Serializer):
|
||||||
|
user = serializers.CharField()
|
|
@ -0,0 +1,13 @@
|
||||||
|
{% extends "hostingpackages/base.html" %}
|
||||||
|
{% load i18n crispy_forms_tags %}
|
||||||
|
{% block title %}{{ block.super }} -
|
||||||
|
{% blocktranslate with package=hostingpackage.name full_name=customer.get_full_name trimmed %}
|
||||||
|
Add Option to Hosting Package {{ package }} of Customer {{ full_name }}
|
||||||
|
{% endblocktranslate %}{% endblock title %}
|
||||||
|
{% block page_title %}{% blocktranslate with package=hostingpackage.name full_name=customer.get_full_name trimmed %}
|
||||||
|
Add Option to Hosting Package {{ package }} of Customer {{ full_name }}
|
||||||
|
{% endblocktranslate %}{% endblock page_title %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% crispy form %}
|
||||||
|
{% endblock content %}
|
|
@ -0,0 +1,53 @@
|
||||||
|
{% extends "hostingpackages/base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block title %}{{ block.super }} - {% translate "All hosting packages" %}{% endblock title %}
|
||||||
|
{% block page_title %}{% translate "All hosting packages" %}{% endblock page_title %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% if customerhostingpackage_list %}
|
||||||
|
<table class="table table-condensed">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% translate "Name" %}</th>
|
||||||
|
<th>{% translate "Customer" %}</th>
|
||||||
|
<th>{% translate "OS User" %}</th>
|
||||||
|
<th>{% translate "Disk space" %}</th>
|
||||||
|
<th>{% translate "Mailboxes" %}</th>
|
||||||
|
<th>{% translate "Databases" %}</th>
|
||||||
|
<th>{% translate "Setup date" %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for package in customerhostingpackage_list %}
|
||||||
|
<tr>
|
||||||
|
<td><a href="{{ package.get_absolute_url }}">{{ package.name }}</a></td>
|
||||||
|
<td>{{ package.customer }}</td>
|
||||||
|
<td>{{ package.osuser.username }}</td>
|
||||||
|
<td>
|
||||||
|
{% with diskspace=package.get_disk_space %}
|
||||||
|
<span title="{% blocktranslate trimmed %}
|
||||||
|
The reserved disk space for your hosting package is {{ diskspace }} bytes.
|
||||||
|
{% endblocktranslate %}">{{ diskspace|filesizeformat }}</span>
|
||||||
|
{% endwith %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% blocktranslate with num=package.used_mailbox_count total=package.mailbox_count trimmed %}
|
||||||
|
used {{ num }} of {{ total }}
|
||||||
|
{% endblocktranslate %}</td>
|
||||||
|
<td>{% for dbtype in package.get_databases %}
|
||||||
|
{{ dbtype.number }}
|
||||||
|
{% include "userdbs/snippets/db_type.html" with db_type=dbtype.db_type %}
|
||||||
|
{% if not forloop.last %} / {% endif %}
|
||||||
|
{% endfor %}</td>
|
||||||
|
<td>{{ package.created }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<p class="text-info">{% translate "No hosting packages have been setup yet." %}</p>
|
||||||
|
{% endif %}
|
||||||
|
<p>
|
||||||
|
<a href="{% url 'create_hosting_package' %}" class="btn btn-primary">{% translate "Add hosting package" %}</a>
|
||||||
|
</p>
|
||||||
|
{% endblock content %}
|
|
@ -0,0 +1,12 @@
|
||||||
|
{% extends "hostingpackages/base.html" %}
|
||||||
|
{% load i18n crispy_forms_tags %}
|
||||||
|
{% block title %}{{ block.super }} - {% blocktranslate with full_name=customer.get_full_name trimmed %}
|
||||||
|
Add hosting package for Customer {{ full_name }}
|
||||||
|
{% endblocktranslate %}{% endblock title %}
|
||||||
|
{% block page_title %}{% blocktranslate with full_name=customer.get_full_name trimmed %}
|
||||||
|
Add Hosting Package for Customer {{ full_name }}
|
||||||
|
{% endblocktranslate %}{% endblock page_title %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% crispy form %}
|
||||||
|
{% endblock content %}
|
|
@ -0,0 +1,282 @@
|
||||||
|
{% extends "hostingpackages/base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}{{ block.super }} - {% spaceless %}
|
||||||
|
{% if user == customer %}
|
||||||
|
{% blocktranslate with package=hostingpackage.name trimmed %}
|
||||||
|
Details for your Hosting Package {{ package }}
|
||||||
|
{% endblocktranslate %}
|
||||||
|
{% else %}
|
||||||
|
{% blocktranslate with package=hostingpackage.name full_name=customer.get_full_name trimmed %}
|
||||||
|
Details for Hosting Package {{ package }} of {{ full_name }}
|
||||||
|
{% endblocktranslate %}
|
||||||
|
{% endif %}
|
||||||
|
{% endspaceless %}{% endblock title %}
|
||||||
|
|
||||||
|
{% block page_title %}{% blocktranslate with package=hostingpackage.name trimmed %}
|
||||||
|
Details of Hosting Package {{ package }}
|
||||||
|
{% endblocktranslate %}{% endblock page_title %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
|
||||||
|
<div class="col">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
{% translate "Hosting Package Information" %}
|
||||||
|
{% if user.is_staff %}
|
||||||
|
<div class="float-end">
|
||||||
|
<a class="panel-title" href="#"
|
||||||
|
title="{% translate "Edit Hosting Package Information" %}"><i
|
||||||
|
class="bi-gear"></i></a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<dl class="dl-horizontal">
|
||||||
|
<dt>{% translate "Name" %}</dt>
|
||||||
|
<dd>{{ hostingpackage.name }}</dd>
|
||||||
|
<dt>{% translate "Description" %}</dt>
|
||||||
|
<dd>{{ hostingpackage.description|default:"-" }}</dd>
|
||||||
|
<dt>{% translate "Disk space" %}</dt>
|
||||||
|
{% with used_space=hostingpackage.get_used_disk_space_sum|filesizeformat disk_space=hostingpackage.get_disk_space|filesizeformat package_space=hostingpackage.get_package_space|filesizeformat space_level=hostingpackage.space_level %}
|
||||||
|
<dd>
|
||||||
|
<span title="{% blocktranslate trimmed %}
|
||||||
|
You use {{ used_space }} of the reserved disk space of {{ disk_space }} for your hosting package
|
||||||
|
{% endblocktranslate %}" class="text-{% if space_level > 90.0 %}danger{% elif space_level > 80.0 %}warning{% else %}success{% endif %}">{% blocktranslate with space_level_percent=space_level|floatformat:1 trimmed%}
|
||||||
|
{{ used_space }} of {{ disk_space }} ({{ space_level_percent }}%)
|
||||||
|
{% endblocktranslate %} <a title="{% translate "Disk usage details" %}" href="{% url "disk_usage_details" user=hostingpackage.customer.username package=hostingpackage.id %}">{% translate "Details" %}</a></span>
|
||||||
|
<i class="bi-info-circle"
|
||||||
|
title="{% blocktranslate trimmed %}
|
||||||
|
The package contributes {{ package_space }} the difference comes from disk space options
|
||||||
|
{% endblocktranslate %}"></i>
|
||||||
|
</dd>
|
||||||
|
{% endwith %}
|
||||||
|
<dt>{% translate "Mailboxes" %}</dt>
|
||||||
|
<dd>
|
||||||
|
{% blocktranslate with num=hostingpackage.used_mailbox_count total=hostingpackage.mailbox_count trimmed %}
|
||||||
|
{{ num }} of {{ total }} in use{% endblocktranslate %}
|
||||||
|
<i class="bi-info-circle"
|
||||||
|
title="{% blocktranslate with mailboxcount=hostingpackage.mailboxcount trimmed %}The package provides {{ mailboxcount }} mailboxes the difference comes from mailbox options.{% endblocktranslate %}"></i>
|
||||||
|
</dd>
|
||||||
|
<dt>{% if osuser.is_sftp_user %}{% translate "SFTP username" %}{% else %}
|
||||||
|
{% translate "SSH/SFTP username" %}{% endif %}</dt>
|
||||||
|
<dd>{{ osuser.username }}{% if sshkeys %}
|
||||||
|
<a href="{% url 'list_ssh_keys' package=hostingpackage.id %}" class="badge"
|
||||||
|
title="{% blocktranslate count counter=sshkeys|length trimmed %}There is an SSH public key set for this user.{% plural %}There are {{ counter }} SSH public keys set for this user.{% endblocktranslate %}"><i
|
||||||
|
class="bi-key"></i> {{ sshkeys|length }}</a>{% endif %}</dd>
|
||||||
|
<dt>{% translate "Upload server" %}</dt>
|
||||||
|
<dd>{{ uploadserver }}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">{% translate "Hosting Package Options" %}</div>
|
||||||
|
{% if hostingoptions %}
|
||||||
|
<ul class="list-group list-group-flush">
|
||||||
|
{% for opt in hostingoptions %}
|
||||||
|
<li class="list-group-item">{{ opt }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% else %}
|
||||||
|
<div class="card-body text-info">{% translate "No options booked" %}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if user.is_staff %}
|
||||||
|
<div class="card-footer"><a
|
||||||
|
class="btn btn-primary"
|
||||||
|
href="{% url 'hosting_option_choices' pk=hostingpackage.id %}"
|
||||||
|
title="{% translate "Add another hosting option" %}">{% translate "Add option" %}</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">{% translate "Hosting Package Actions" %}</div>
|
||||||
|
<ul class="list-group list-group-flush">
|
||||||
|
<li class="list-group-item"><a
|
||||||
|
href="#"
|
||||||
|
title="{% translate "Edit Hosting Package Description" %}">{% translate "Edit description" %}</a>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item"><a href="{% url "set_osuser_password" slug=osuser.username %}">
|
||||||
|
{% if osuser.is_sftp_user %}{% translate "Set SFTP password" %}{% else %}
|
||||||
|
{% translate "Set SSH/SFTP password" %}{% endif %}</a></li>
|
||||||
|
<li class="list-group-item"><a
|
||||||
|
href="{% url "add_ssh_key" package=hostingpackage.id %}"
|
||||||
|
title="{% blocktranslate trimmed %}
|
||||||
|
Add an SSH public key that can be used as an alternative for password
|
||||||
|
{% endblocktranslate %}">{% translate "Add SSH public key" %}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row row-cols-1 mt-4">
|
||||||
|
<div class="col">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">{% translate "Domains" %}</div>
|
||||||
|
{% if domains %}
|
||||||
|
<table class="table table-condensed">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="name-column">{% translate "Domain name" %}</th>
|
||||||
|
<th>{% translate "Mail addresses" %}</th>
|
||||||
|
<th>{% translate "Websites" %}</th>
|
||||||
|
<th title="{% translate "Domain actions" %}" class="actions-column"><span
|
||||||
|
class="visually-hidden">{% translate "Actions" %}</span></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for domain in domains %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ domain.domain }}</td>
|
||||||
|
{% if domain.domain.maildomain.mailaddress_set.exists %}
|
||||||
|
<td>
|
||||||
|
{% with maildomain=domain.domain.maildomain %}
|
||||||
|
{% for mailaddress in maildomain.mailaddresses %}{% spaceless %}
|
||||||
|
<a href="{% url 'edit_mailaddress' package=hostingpackage.id domain=maildomain.domain pk=mailaddress.id %}"
|
||||||
|
title="{% translate "Edit mail address targets" %}">{{ mailaddress }}</a>
|
||||||
|
<a href="{% url 'delete_mailaddress' package=hostingpackage.id domain=maildomain.domain pk=mailaddress.id %}"
|
||||||
|
title="{% translate "Delete mail address" %}"><i
|
||||||
|
class="bi-trash"></i><span
|
||||||
|
class="visually-hidden"> {% translate "Delete mail address" %}</span></a>
|
||||||
|
{% endspaceless %}{% if not forloop.last %}, {% endif %}{% endfor %}
|
||||||
|
{% endwith %}
|
||||||
|
</td>
|
||||||
|
{% else %}
|
||||||
|
<td class="text-info">{% translate "None" %}</td>
|
||||||
|
{% endif %}
|
||||||
|
{% if domain.domain.website_set.exists %}
|
||||||
|
<td>
|
||||||
|
{% with domain=domain.domain %}
|
||||||
|
{% for website in domain.website_set.all %}{% spaceless %}
|
||||||
|
{{ website }}
|
||||||
|
<a href="{% url 'delete_website' package=hostingpackage.id domain=domain.domain pk=website.id %}"
|
||||||
|
titel="{% translate "Delete website" %}"><i
|
||||||
|
class="bi-trash"></i><span
|
||||||
|
class="visually-hidden"> {% translate "Delete website" %}</span></a>
|
||||||
|
{% endspaceless %}{% if not forloop.last %}, {% endif %}{% endfor %}
|
||||||
|
{% endwith %}
|
||||||
|
</td>
|
||||||
|
{% else %}
|
||||||
|
<td class="text-info">{% translate "None" %}</td>
|
||||||
|
{% endif %}
|
||||||
|
<td>
|
||||||
|
{% if domain.domain.maildomain %}
|
||||||
|
{% with maildomain=domain.domain.maildomain %}
|
||||||
|
<a href="{% url 'add_mailaddress' package=hostingpackage.id domain=maildomain.domain %}"
|
||||||
|
title="{% translate "Add mail address" %}"><i
|
||||||
|
class="bi-envelope-plus"></i><span
|
||||||
|
class="visually-hidden"> {% translate "Add mail address" %}</span></a>
|
||||||
|
{% endwith %}
|
||||||
|
{% endif %}
|
||||||
|
{% with hostingdomain=domain.domain %}
|
||||||
|
<a href="{% url 'add_website' package=hostingpackage.id domain=hostingdomain.domain %}"
|
||||||
|
title="{% translate "Add website" %}"><i class="bi-globe"></i><span
|
||||||
|
class="visually-hidden"> {% translate "Add website" %}</span></a>
|
||||||
|
{% endwith %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<div class="card-body text-info">{% translate "There are no domains assigned to this hosting package yet." %}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if user.is_staff %}
|
||||||
|
<div class="card-footer"><a href="{% url 'create_hosting_domain' package=hostingpackage.id %}"
|
||||||
|
class="btn btn-primary">{% translate "Add domain" %}</a></div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row row-cols-1 row-cols-lg-2 mt-4">
|
||||||
|
<div class="col">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">{% translate "E-Mail-Accounts" %}</div>
|
||||||
|
{% if mailboxes %}
|
||||||
|
<table class="table table-condensed">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="name-column">{% translate "Mailbox" %}</th>
|
||||||
|
<th>{% translate "Mail addresses" %}</th>
|
||||||
|
<th class="status-column">{% translate "Active" %}</th>
|
||||||
|
<th title="{% translate "Mailbox actions" %}" class="actions-column"><span
|
||||||
|
class="visually-hidden">{% translate "Actions" %}</span></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for mailbox in mailboxes %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ mailbox.username }}</td>
|
||||||
|
<td>{{ mailbox.mailaddresses|join:", " }}</td>
|
||||||
|
<td>
|
||||||
|
<i class="{% if mailbox.active %}bi-check-circle{% else %}bi-dash-circle{% endif %}"></i><span
|
||||||
|
class="visually-hidden"> {% if mailbox.active %}{% translate "Active" %}{% else %}
|
||||||
|
{% translate "inactive" %}{% endif %}</span></td>
|
||||||
|
<td>
|
||||||
|
<a href="{% url 'change_mailbox_password' package=hostingpackage.id slug=mailbox.username %}"><i
|
||||||
|
class="bi-lock" title="{% translate "Set mailbox password" %}"></i><span
|
||||||
|
class="visually-hidden"> {% translate "Set mailbox password" %}</span></a>
|
||||||
|
</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<div class="card-body text-info">{% translate "There are no mailboxes assigned to this hosting package yet." %}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if hostingpackage.may_add_mailbox %}
|
||||||
|
<div class="card-footer"><a
|
||||||
|
href="{% url 'create_mailbox' package=hostingpackage.id %}"
|
||||||
|
class="btn btn-primary">{% translate "Add mailbox" %}</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">{% translate "Databases" %}</div>
|
||||||
|
{% if databases %}
|
||||||
|
<table class="table table-condensed">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="name-column">{% translate "Database name" %}</th>
|
||||||
|
<th class="name-column">{% translate "Database user" %}</th>
|
||||||
|
<th title="{% translate "Database type" %}"><span
|
||||||
|
class="visually-hidden">{% translate "Type" %}</span></th>
|
||||||
|
<th title="{% translate "Database actions" %}" class="actions-column"><span
|
||||||
|
class="visually-hidden">{% translate "Actions" %}</span></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for database in databases %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ database.db_name }}</td>
|
||||||
|
<td>{{ database.db_user.name }}</td>
|
||||||
|
<td>{% include "userdbs/snippets/db_type.html" with db_type=database.db_user.db_type %}</td>
|
||||||
|
<td>
|
||||||
|
<a href="{% url 'change_dbuser_password' package=hostingpackage.id slug=database.db_user.name %}"
|
||||||
|
title="{% translate "Set database user password" %}"><i
|
||||||
|
class="bi-database-gear"></i><span
|
||||||
|
class="visually-hidden"> {% translate "Set database user password" %}</span></a>
|
||||||
|
<a href="{% url 'delete_userdatabase' package=hostingpackage.id slug=database.db_name %}"
|
||||||
|
title="{% translate "Delete database" %}"><i class="bi-database-dash"></i><span
|
||||||
|
class="visually-hidden">{% translate "Delete database" %}</span></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<div class="card-body text-info">{% translate "There are no databases assigned to this hosting package yet." %}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if hostingpackage.may_add_database %}
|
||||||
|
<div class="card-footer"><a
|
||||||
|
href="{% url 'add_userdatabase' package=hostingpackage.id %}"
|
||||||
|
class="btn btn-primary">{% translate "Add database" %}</a></div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock content %}
|
|
@ -0,0 +1,117 @@
|
||||||
|
{% extends "hostingpackages/base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}{{ block.super }} - {% spaceless %}
|
||||||
|
{% if user == customer %}
|
||||||
|
{% blocktranslate with package=hostingpackage.name trimmed %}
|
||||||
|
Disk usage of your Hosting Package {{ package }}
|
||||||
|
{% endblocktranslate %}
|
||||||
|
{% else %}
|
||||||
|
{% blocktranslate with package=hostingpackage.name full_name=customer.get_full_name trimmed %}
|
||||||
|
Disk usage of Hosting Package {{ package }} of {{ full_name }}
|
||||||
|
{% endblocktranslate %}
|
||||||
|
{% endif %}
|
||||||
|
{% endspaceless %}{% endblock title %}
|
||||||
|
|
||||||
|
{% block page_title %}
|
||||||
|
{% blocktranslate with package=hostingpackage.name package_url=hostingpackage.get_absolute_url trimmed %}
|
||||||
|
Disk usage of Hosting Package <a href="{{ package_url }}">{{ package }}</a>
|
||||||
|
{% endblocktranslate %}{% endblock page_title %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% with used_space=hostingpackage.get_used_disk_space_sum|filesizeformat disk_space=hostingpackage.get_disk_space|filesizeformat package_space=hostingpackage.get_package_space|filesizeformat space_level=hostingpackage.space_level %}
|
||||||
|
<p>{% blocktranslate trimmed %}
|
||||||
|
You use {{ used_space }} of the reserved disk space of {{ disk_space }} for your hosting package.
|
||||||
|
{% endblocktranslate %}</p>
|
||||||
|
<p class="lead"><span
|
||||||
|
class="text-{% if space_level > 90.0 %}danger{% elif space_level > 80.0 %}warning{% else %}success{% endif %}">
|
||||||
|
{% blocktranslate with space_level_percent=space_level|floatformat:1 trimmed %}
|
||||||
|
{{ used_space }} of {{ disk_space }} ({{ space_level_percent }}%)
|
||||||
|
{% endblocktranslate %}</span>
|
||||||
|
<i class="bi-info-circle"
|
||||||
|
title="{% blocktranslate trimmed %}
|
||||||
|
The package contributes {{ package_space }} the difference comes from disk space options
|
||||||
|
{% endblocktranslate %}"></i>
|
||||||
|
</p>
|
||||||
|
<h2>{% trans "Breakdown by usage" %}</h2>
|
||||||
|
{% if mail_usage %}
|
||||||
|
<h3>{% trans "Mailbox usage" %}</h3>
|
||||||
|
<table class="table table-condensed table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% translate "Mailbox" %}</th>
|
||||||
|
<th>{% translate "Primary email address" %}</th>
|
||||||
|
<th class="text-end">{% translate "Used space" %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for line in mail_usage %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ line.item }}</td>
|
||||||
|
<td>{% if line.email_address %}{{ line.email_address }}{% else %}-{% endif %}</td>
|
||||||
|
<td class="text-end">{{ line.size_in_bytes|filesizeformat }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
{% if web_usage %}
|
||||||
|
<h3>{% trans "Website usage" %}</h3>
|
||||||
|
<table class="table table-condensed table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% translate "Website / Directory" %}</th>
|
||||||
|
<th class="text-end">{% translate "Used space" %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for line in web_usage %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ line.item }}</td>
|
||||||
|
<td class="text-end">{{ line.size_in_bytes|filesizeformat }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
{% if mysql_usage %}
|
||||||
|
<h3>{% trans "MySQL/MariaDB database usage" %}</h3>
|
||||||
|
<table class="table table-condensed table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% translate "Database" %}</th>
|
||||||
|
<th class="text-end">{% translate "Used space" %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for line in mysql_usage %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ line.item }}</td>
|
||||||
|
<td class="text-end">{{ line.size_in_bytes|filesizeformat }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
{% if pgsql_usage %}
|
||||||
|
<h3>{% trans "PostgreSQL database usage" %}</h3>
|
||||||
|
<table class="table table-condensed table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% translate "Database" %}</th>
|
||||||
|
<th class="text-end">{% translate "Used space" %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for line in pgsql_usage %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ line.item }}</td>
|
||||||
|
<td class="text-end">{{ line.size_in_bytes|filesizeformat }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
{% endblock content %}
|
|
@ -0,0 +1,29 @@
|
||||||
|
{% extends "hostingpackages/base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}{{ block.super }} -
|
||||||
|
{% blocktranslate with package=hostingpackage.name full_name=customer.get_full_name trimmed %}Choose new Option for
|
||||||
|
Hosting Package {{ package }} of Customer {{ full_name }}{% endblocktranslate %}{% endblock title %}
|
||||||
|
|
||||||
|
{% block page_title %}{% blocktranslate with package=hostingpackage.name full_name=customer.get_full_name trimmed %}
|
||||||
|
Choose new Option for Hosting Package {{ package }} of Customer {{ full_name }}
|
||||||
|
{% endblocktranslate %}{% endblock page_title %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
|
||||||
|
{% for label, items in hosting_options %}
|
||||||
|
<div class="col">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">{{ label }}</div>
|
||||||
|
<ul class="list-group list-group-flush">
|
||||||
|
{% for item, option_type in items %}
|
||||||
|
<li class="list-group-item"><a
|
||||||
|
href="{% url 'add_hosting_option' package=hostingpackage.id type=option_type optionid=item.id %}"><i
|
||||||
|
class="bi-plus-circle"></i> {{ item }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -2,27 +2,68 @@
|
||||||
Test for models.
|
Test for models.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
from django.test import TestCase, override_settings
|
||||||
|
|
||||||
from django.test import TestCase
|
from hostingpackages.models import (
|
||||||
|
DISK_SPACE_UNITS,
|
||||||
|
CustomerHostingPackage,
|
||||||
|
HostingPackageTemplate,
|
||||||
|
)
|
||||||
|
|
||||||
from hostingpackages.models import DISK_SPACE_UNITS, CustomerHostingPackage
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
class CustomerHostingPackageTest(TestCase):
|
class CustomerHostingPackageTest(TestCase):
|
||||||
def test_get_disk_space_bytes(self):
|
def setUp(self) -> None:
|
||||||
package = CustomerHostingPackage(
|
self.user = User.objects.create(username="test")
|
||||||
diskspace=10, diskspace_unit=DISK_SPACE_UNITS.G
|
self.template = HostingPackageTemplate.objects.create(
|
||||||
|
mailboxcount=10,
|
||||||
|
diskspace=100,
|
||||||
|
diskspace_unit=DISK_SPACE_UNITS.M,
|
||||||
|
description="Test package 1 - Description",
|
||||||
|
name="Test package 1",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@override_settings(OSUSER_DEFAULT_GROUPS=[])
|
||||||
|
def test_get_disk_space_bytes(self):
|
||||||
|
self.template.diskspace = 10
|
||||||
|
self.template.diskspace_unit = DISK_SPACE_UNITS.G
|
||||||
|
self.template.save()
|
||||||
|
package = CustomerHostingPackage.objects.create_from_template(
|
||||||
|
customer=self.user, template=self.template, name="customer",
|
||||||
|
)
|
||||||
|
package.save()
|
||||||
self.assertEqual(package.get_disk_space(), 10 * 1024 ** 3)
|
self.assertEqual(package.get_disk_space(), 10 * 1024 ** 3)
|
||||||
|
|
||||||
|
@override_settings(OSUSER_DEFAULT_GROUPS=[])
|
||||||
def test_get_disk_space_mib(self):
|
def test_get_disk_space_mib(self):
|
||||||
package = CustomerHostingPackage(
|
self.template.diskspace = 10
|
||||||
diskspace=10, diskspace_unit=DISK_SPACE_UNITS.G
|
self.template.diskspace_unit = DISK_SPACE_UNITS.G
|
||||||
|
self.template.save()
|
||||||
|
package = CustomerHostingPackage.objects.create_from_template(
|
||||||
|
customer=self.user, template=self.template, name="customer",
|
||||||
)
|
)
|
||||||
|
package.save()
|
||||||
self.assertEqual(package.get_disk_space(DISK_SPACE_UNITS.M), 10 * 1024)
|
self.assertEqual(package.get_disk_space(DISK_SPACE_UNITS.M), 10 * 1024)
|
||||||
|
|
||||||
|
@override_settings(OSUSER_DEFAULT_GROUPS=[])
|
||||||
def test_get_quota(self):
|
def test_get_quota(self):
|
||||||
package = CustomerHostingPackage(
|
self.template.diskspace = 256
|
||||||
diskspace=256, diskspace_unit=DISK_SPACE_UNITS.M
|
self.template.diskspace_unit = DISK_SPACE_UNITS.M
|
||||||
|
self.template.save()
|
||||||
|
package = CustomerHostingPackage.objects.create_from_template(
|
||||||
|
customer=self.user, template=self.template, name="customer",
|
||||||
)
|
)
|
||||||
|
package.save()
|
||||||
self.assertEqual(package.get_quota(), (262144, 275251))
|
self.assertEqual(package.get_quota(), (262144, 275251))
|
||||||
|
|
||||||
|
@override_settings(OSUSER_DEFAULT_GROUPS=["testgroup"])
|
||||||
|
def test_additional_group_not_defined(self):
|
||||||
|
with self.assertRaises(ImproperlyConfigured) as ctx:
|
||||||
|
package = CustomerHostingPackage.objects.create_from_template(
|
||||||
|
customer=self.user, template=self.template, name="Test customer package",
|
||||||
|
)
|
||||||
|
package.save()
|
||||||
|
self.assertIn("testgroup", str(ctx.exception))
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
This module defines the URL patterns for hosting package related views.
|
This module defines the URL patterns for hosting package related views.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from django.conf.urls import url
|
from django.urls import path, re_path
|
||||||
|
|
||||||
from .views import (
|
from .views import (
|
||||||
AddHostingOption,
|
AddHostingOption,
|
||||||
|
@ -12,26 +12,46 @@ from .views import (
|
||||||
CreateCustomerHostingPackage,
|
CreateCustomerHostingPackage,
|
||||||
CreateHostingPackage,
|
CreateHostingPackage,
|
||||||
CustomerHostingPackageDetails,
|
CustomerHostingPackageDetails,
|
||||||
CustomerHostingPackageList,
|
CustomerHostingPackageDiskUsageDetails,
|
||||||
HostingOptionChoices,
|
HostingOptionChoices,
|
||||||
|
UploadCustomerPackageDiskUsage,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^create$', CreateHostingPackage.as_view(),
|
re_path(r"^create$", CreateHostingPackage.as_view(), name="create_hosting_package"),
|
||||||
name='create_hosting_package'),
|
re_path(
|
||||||
url(r'^allpackages/',
|
r"^allpackages/",
|
||||||
AllCustomerHostingPackageList.as_view(), name='all_hosting_packages'),
|
AllCustomerHostingPackageList.as_view(),
|
||||||
url(r'^(?P<user>[-\w0-9@.+_]+)/$',
|
name="all_hosting_packages",
|
||||||
CustomerHostingPackageList.as_view(), name='hosting_packages'),
|
),
|
||||||
url(r'^(?P<user>[-\w0-9@.+_]+)/create$',
|
re_path(
|
||||||
|
r"^(?P<user>[-\w0-9@.+_]+)/create$",
|
||||||
CreateCustomerHostingPackage.as_view(),
|
CreateCustomerHostingPackage.as_view(),
|
||||||
name='create_customer_hosting_package'),
|
name="create_customer_hosting_package",
|
||||||
url(r'^(?P<user>[-\w0-9@.+_]+)/(?P<pk>\d+)/$',
|
),
|
||||||
|
re_path(
|
||||||
|
r"^(?P<user>[-\w0-9@.+_]+)/(?P<pk>\d+)/$",
|
||||||
CustomerHostingPackageDetails.as_view(),
|
CustomerHostingPackageDetails.as_view(),
|
||||||
name='hosting_package_details'),
|
name="hosting_package_details",
|
||||||
url(r'^(?P<pk>\d+)/option-choices$',
|
),
|
||||||
HostingOptionChoices.as_view(), name='hosting_option_choices'),
|
re_path(
|
||||||
url(r'^(?P<package>\d+)/add-option/(?P<type>\w+)/(?P<optionid>\d+)$',
|
r"^(?P<pk>\d+)/option-choices$",
|
||||||
AddHostingOption.as_view(), name='add_hosting_option'),
|
HostingOptionChoices.as_view(),
|
||||||
|
name="hosting_option_choices",
|
||||||
|
),
|
||||||
|
re_path(
|
||||||
|
r"^(?P<package>\d+)/add-option/(?P<type>\w+)/(?P<optionid>\d+)$",
|
||||||
|
AddHostingOption.as_view(),
|
||||||
|
name="add_hosting_option",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"<str:user>/<int:package>/disk-usage/",
|
||||||
|
CustomerHostingPackageDiskUsageDetails.as_view(),
|
||||||
|
name="disk_usage_details",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"upload-disk-usage/",
|
||||||
|
UploadCustomerPackageDiskUsage.as_view(),
|
||||||
|
name="upload_disk_usage",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -2,30 +2,31 @@
|
||||||
This module defines views related to hosting packages.
|
This module defines views related to hosting packages.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
import http
|
||||||
|
import logging
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.http import Http404
|
|
||||||
from django.shortcuts import redirect, get_object_or_404
|
|
||||||
from django.utils.translation import ugettext as _
|
|
||||||
from django.views.generic import (
|
|
||||||
DetailView,
|
|
||||||
ListView,
|
|
||||||
)
|
|
||||||
from django.views.generic.edit import (
|
|
||||||
CreateView,
|
|
||||||
FormView,
|
|
||||||
)
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.contrib.auth.mixins import PermissionRequiredMixin, UserPassesTestMixin
|
||||||
|
from django.http import Http404
|
||||||
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
from django.views.generic import DetailView, ListView
|
||||||
|
from django.views.generic.edit import CreateView, FormView
|
||||||
|
|
||||||
from braces.views import (
|
import rest_framework.request
|
||||||
LoginRequiredMixin,
|
from rest_framework.permissions import BasePermission
|
||||||
StaffuserRequiredMixin,
|
from rest_framework.response import Response
|
||||||
)
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin
|
from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin
|
||||||
|
|
||||||
|
from managemails.models import Mailbox
|
||||||
from .forms import (
|
from .forms import (
|
||||||
AddDiskspaceOptionForm,
|
AddDiskspaceOptionForm,
|
||||||
AddMailboxOptionForm,
|
AddMailboxOptionForm,
|
||||||
|
@ -35,32 +36,37 @@ from .forms import (
|
||||||
)
|
)
|
||||||
from .models import (
|
from .models import (
|
||||||
CustomerHostingPackage,
|
CustomerHostingPackage,
|
||||||
|
CustomerPackageDiskUsage,
|
||||||
DiskSpaceOption,
|
DiskSpaceOption,
|
||||||
MailboxOption,
|
MailboxOption,
|
||||||
UserDatabaseOption,
|
UserDatabaseOption,
|
||||||
)
|
)
|
||||||
|
from .serializers import DiskUsageSerializer
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class CreateHostingPackage(
|
class CreateHostingPackage(PermissionRequiredMixin, CreateView):
|
||||||
LoginRequiredMixin, StaffuserRequiredMixin, CreateView
|
|
||||||
):
|
|
||||||
"""
|
"""
|
||||||
Create a hosting package.
|
Create a hosting package.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
model = CustomerHostingPackage
|
model = CustomerHostingPackage
|
||||||
raise_exception = True
|
raise_exception = True
|
||||||
template_name_suffix = '_create'
|
permission_required = "domains.add_customerhostingpackage"
|
||||||
|
template_name_suffix = "_create"
|
||||||
form_class = CreateHostingPackageForm
|
form_class = CreateHostingPackageForm
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
hostingpackage = form.save()
|
hosting_package = form.save()
|
||||||
messages.success(
|
messages.success(
|
||||||
self.request,
|
self.request,
|
||||||
_('Started setup of new hosting package {name}.').format(
|
_("Started setup of new hosting package {name}.").format(
|
||||||
name=hostingpackage.name)
|
name=hosting_package.name
|
||||||
|
),
|
||||||
)
|
)
|
||||||
return redirect(hostingpackage)
|
return redirect(hosting_package)
|
||||||
|
|
||||||
|
|
||||||
class CreateCustomerHostingPackage(CreateHostingPackage):
|
class CreateCustomerHostingPackage(CreateHostingPackage):
|
||||||
|
@ -68,6 +74,7 @@ class CreateCustomerHostingPackage(CreateHostingPackage):
|
||||||
Create a hosting package for a selected customer.
|
Create a hosting package for a selected customer.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
form_class = CreateCustomerHostingPackageForm
|
form_class = CreateCustomerHostingPackageForm
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
|
@ -76,25 +83,24 @@ class CreateCustomerHostingPackage(CreateHostingPackage):
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def get_customer_object(self):
|
def get_customer_object(self):
|
||||||
return get_object_or_404(
|
return get_object_or_404(get_user_model(), username=self.kwargs["user"])
|
||||||
get_user_model(), username=self.kwargs['user'])
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(
|
context = super(CreateCustomerHostingPackage, self).get_context_data(**kwargs)
|
||||||
CreateCustomerHostingPackage, self).get_context_data(**kwargs)
|
context["customer"] = self.get_customer_object()
|
||||||
context['customer'] = self.get_customer_object()
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
hostingpackage = form.save(commit=False)
|
hosting_package = form.save(commit=False)
|
||||||
hostingpackage.customer = self.get_customer_object()
|
hosting_package.customer = self.get_customer_object()
|
||||||
hostingpackage.save()
|
hosting_package.save()
|
||||||
messages.success(
|
messages.success(
|
||||||
self.request,
|
self.request,
|
||||||
_('Started setup of new hosting package {name}.').format(
|
_("Started setup of new hosting package {name}.").format(
|
||||||
name=hostingpackage.name)
|
name=hosting_package.name
|
||||||
|
),
|
||||||
)
|
)
|
||||||
return redirect(hostingpackage)
|
return redirect(hosting_package)
|
||||||
|
|
||||||
|
|
||||||
class CustomerHostingPackageDetails(StaffOrSelfLoginRequiredMixin, DetailView):
|
class CustomerHostingPackageDetails(StaffOrSelfLoginRequiredMixin, DetailView):
|
||||||
|
@ -102,156 +108,161 @@ class CustomerHostingPackageDetails(StaffOrSelfLoginRequiredMixin, DetailView):
|
||||||
This view is for showing details of a customer hosting package.
|
This view is for showing details of a customer hosting package.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
model = CustomerHostingPackage
|
model = CustomerHostingPackage
|
||||||
context_object_name = 'hostingpackage'
|
context_object_name = "hostingpackage"
|
||||||
customer = None
|
customer = None
|
||||||
|
|
||||||
def get_customer_object(self):
|
def get_customer_object(self):
|
||||||
if self.customer is None:
|
if self.customer is None:
|
||||||
self.customer = get_object_or_404(
|
self.customer = get_object_or_404(
|
||||||
get_user_model(), username=self.kwargs['user'])
|
get_user_model(), username=self.kwargs["user"]
|
||||||
|
)
|
||||||
return self.customer
|
return self.customer
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(CustomerHostingPackageDetails, self).get_context_data(
|
context = super(CustomerHostingPackageDetails, self).get_context_data(**kwargs)
|
||||||
**kwargs)
|
context.update(
|
||||||
context.update({
|
{
|
||||||
'customer': self.get_customer_object(),
|
"customer": self.get_customer_object(),
|
||||||
'uploadserver': settings.OSUSER_UPLOAD_SERVER,
|
"uploadserver": settings.OSUSER_UPLOAD_SERVER,
|
||||||
'databases': context['hostingpackage'].databases,
|
"databases": context["hostingpackage"].databases,
|
||||||
'osuser': context['hostingpackage'].osuser,
|
"osuser": context["hostingpackage"].osuser,
|
||||||
'hostingoptions':
|
"hostingoptions": context["hostingpackage"].get_hostingoptions(),
|
||||||
context['hostingpackage'].get_hostingoptions(),
|
"domains": context["hostingpackage"].domains.all(),
|
||||||
'domains': context['hostingpackage'].domains.all(),
|
"mailboxes": context["hostingpackage"].mailboxes,
|
||||||
'mailboxes': context['hostingpackage'].mailboxes,
|
}
|
||||||
})
|
)
|
||||||
context['sshkeys'] = context['osuser'].sshpublickey_set.all()
|
context["sshkeys"] = context["osuser"].sshpublickey_set.all()
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class AllCustomerHostingPackageList(
|
class StaffUserRequiredMixin(UserPassesTestMixin):
|
||||||
LoginRequiredMixin, StaffuserRequiredMixin, ListView
|
"""
|
||||||
):
|
Mixin to make views available to staff members only.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def test_func(self):
|
||||||
|
return self.request.user.is_staff
|
||||||
|
|
||||||
|
|
||||||
|
class AllCustomerHostingPackageList(StaffUserRequiredMixin, ListView):
|
||||||
"""
|
"""
|
||||||
This view is used for showing a list of all hosting packages.
|
This view is used for showing a list of all hosting packages.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
model = CustomerHostingPackage
|
model = CustomerHostingPackage
|
||||||
template_name_suffix = '_admin_list'
|
template_name_suffix = "_admin_list"
|
||||||
|
|
||||||
|
|
||||||
class CustomerHostingPackageList(StaffOrSelfLoginRequiredMixin, ListView):
|
|
||||||
"""
|
|
||||||
This view is used for showing a list of a customer's hosting packages.
|
|
||||||
|
|
||||||
"""
|
|
||||||
model = CustomerHostingPackage
|
|
||||||
customer = None
|
|
||||||
|
|
||||||
def get_customer_object(self):
|
|
||||||
if self.customer is None:
|
|
||||||
self.customer = get_object_or_404(
|
|
||||||
get_user_model(), username=self.kwargs['user'])
|
|
||||||
return self.customer
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super(CustomerHostingPackageList, self).get_context_data(
|
|
||||||
**kwargs)
|
|
||||||
context['customer'] = self.get_customer_object()
|
|
||||||
return context
|
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return super(CustomerHostingPackageList, self).get_queryset().filter(
|
return (
|
||||||
customer__username=self.kwargs['user'])
|
super()
|
||||||
|
.get_queryset()
|
||||||
|
.select_related("osuser", "customer")
|
||||||
|
.only("name", "pk", "created", "customer__username", "osuser__username")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class HostingOptionChoices(
|
class HostingOptionChoices(StaffUserRequiredMixin, DetailView):
|
||||||
LoginRequiredMixin, StaffuserRequiredMixin, DetailView
|
|
||||||
):
|
|
||||||
"""
|
"""
|
||||||
This view displays choices of hosting options for a customer hosting
|
This view displays choices of hosting options for a customer hosting
|
||||||
package.
|
package.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
model = CustomerHostingPackage
|
model = CustomerHostingPackage
|
||||||
context_object_name = 'hostingpackage'
|
context_object_name = "hostingpackage"
|
||||||
template_name_suffix = '_option_choices'
|
template_name_suffix = "_option_choices"
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(HostingOptionChoices, self).get_context_data(
|
context = super(HostingOptionChoices, self).get_context_data(**kwargs)
|
||||||
**kwargs)
|
context.update(
|
||||||
context.update({
|
{
|
||||||
'customer': self.get_object().customer,
|
"customer": self.get_object().customer,
|
||||||
'hosting_options': (
|
"hosting_options": (
|
||||||
(_('Disk space'),
|
(
|
||||||
[(option, 'diskspace') for option in
|
_("Disk space"),
|
||||||
DiskSpaceOption.objects.all()]),
|
[
|
||||||
(_('Mailboxes'),
|
(option, "diskspace")
|
||||||
[(option, 'mailboxes') for option in
|
for option in DiskSpaceOption.objects.all()
|
||||||
MailboxOption.objects.all()]),
|
],
|
||||||
(_('Databases'),
|
),
|
||||||
[(option, 'databases') for option in
|
(
|
||||||
UserDatabaseOption.objects.all()]),
|
_("Mailboxes"),
|
||||||
),
|
[
|
||||||
})
|
(option, "mailboxes")
|
||||||
|
for option in MailboxOption.objects.all()
|
||||||
|
],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
_("Databases"),
|
||||||
|
[
|
||||||
|
(option, "databases")
|
||||||
|
for option in UserDatabaseOption.objects.all()
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class AddHostingOption(
|
class AddHostingOption(StaffUserRequiredMixin, FormView):
|
||||||
LoginRequiredMixin, StaffuserRequiredMixin, FormView
|
template_name = "hostingpackages/add_hosting_option.html"
|
||||||
):
|
|
||||||
template_name = 'hostingpackages/add_hosting_option.html'
|
|
||||||
|
|
||||||
def get_form_class(self):
|
def get_form_class(self):
|
||||||
optiontype = self.kwargs['type']
|
optiontype = self.kwargs["type"]
|
||||||
if optiontype == 'diskspace':
|
if optiontype == "diskspace":
|
||||||
return AddDiskspaceOptionForm
|
return AddDiskspaceOptionForm
|
||||||
elif optiontype == 'mailboxes':
|
elif optiontype == "mailboxes":
|
||||||
return AddMailboxOptionForm
|
return AddMailboxOptionForm
|
||||||
elif optiontype == 'databases':
|
elif optiontype == "databases":
|
||||||
return AddUserDatabaseOptionForm
|
return AddUserDatabaseOptionForm
|
||||||
raise Http404()
|
raise Http404()
|
||||||
|
|
||||||
def get_hosting_package(self):
|
def get_hosting_package(self):
|
||||||
return get_object_or_404(
|
return get_object_or_404(CustomerHostingPackage, pk=int(self.kwargs["package"]))
|
||||||
CustomerHostingPackage, pk=int(self.kwargs['package']))
|
|
||||||
|
|
||||||
def get_option_template(self):
|
def get_option_template(self):
|
||||||
optiontype = self.kwargs['type']
|
optiontype = self.kwargs["type"]
|
||||||
optionid = int(self.kwargs['optionid'])
|
optionid = int(self.kwargs["optionid"])
|
||||||
if optiontype == 'diskspace':
|
if optiontype == "diskspace":
|
||||||
return get_object_or_404(DiskSpaceOption, pk=optionid)
|
return get_object_or_404(DiskSpaceOption, pk=optionid)
|
||||||
elif optiontype == 'mailboxes':
|
elif optiontype == "mailboxes":
|
||||||
return get_object_or_404(MailboxOption, pk=optionid)
|
return get_object_or_404(MailboxOption, pk=optionid)
|
||||||
elif optiontype == 'databases':
|
elif optiontype == "databases":
|
||||||
return get_object_or_404(UserDatabaseOption, pk=optionid)
|
return get_object_or_404(UserDatabaseOption, pk=optionid)
|
||||||
raise Http404()
|
raise Http404()
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
kwargs = super(AddHostingOption, self).get_form_kwargs()
|
kwargs = super(AddHostingOption, self).get_form_kwargs()
|
||||||
kwargs['hostingpackage'] = self.get_hosting_package()
|
kwargs["hostingpackage"] = self.get_hosting_package()
|
||||||
kwargs['option_template'] = self.get_option_template()
|
kwargs["option_template"] = self.get_option_template()
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
initial = super(AddHostingOption, self).get_initial()
|
initial = super(AddHostingOption, self).get_initial()
|
||||||
template = self.get_option_template()
|
template = self.get_option_template()
|
||||||
if type(template) == DiskSpaceOption:
|
if type(template) == DiskSpaceOption:
|
||||||
initial.update({
|
initial.update(
|
||||||
'diskspace': template.diskspace,
|
{
|
||||||
'diskspace_unit': template.diskspace_unit,
|
"diskspace": template.diskspace,
|
||||||
})
|
"diskspace_unit": template.diskspace_unit,
|
||||||
|
}
|
||||||
|
)
|
||||||
elif type(template) == MailboxOption:
|
elif type(template) == MailboxOption:
|
||||||
initial['number'] = template.number
|
initial["number"] = template.number
|
||||||
elif type(template) == UserDatabaseOption:
|
elif type(template) == UserDatabaseOption:
|
||||||
initial['number'] = template.number
|
initial["number"] = template.number
|
||||||
else:
|
else:
|
||||||
raise Http404()
|
raise Http404()
|
||||||
return initial
|
return initial
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(AddHostingOption, self).get_context_data(**kwargs)
|
context = super(AddHostingOption, self).get_context_data(**kwargs)
|
||||||
context['option_template'] = self.get_option_template()
|
context["option_template"] = self.get_option_template()
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
|
@ -259,8 +270,126 @@ class AddHostingOption(
|
||||||
hosting_package = self.get_hosting_package()
|
hosting_package = self.get_hosting_package()
|
||||||
messages.success(
|
messages.success(
|
||||||
self.request,
|
self.request,
|
||||||
_("Successfully added option {option} to hosting package "
|
_(
|
||||||
"{package}.").format(
|
"Successfully added option {option} to hosting package " "{package}."
|
||||||
option=option, package=hosting_package.name)
|
).format(option=option, package=hosting_package.name),
|
||||||
)
|
)
|
||||||
return redirect(hosting_package)
|
return redirect(hosting_package)
|
||||||
|
|
||||||
|
|
||||||
|
class HasDiskUsageUploadPermission(BasePermission):
|
||||||
|
def has_permission(self, request, view):
|
||||||
|
return (
|
||||||
|
request.user.has_perm("hostingpackages.add_customerpackagediskusage")
|
||||||
|
and request.method == "POST"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class UploadCustomerPackageDiskUsage(APIView):
|
||||||
|
permission_classes = [HasDiskUsageUploadPermission]
|
||||||
|
allowed_methods = ("POST",)
|
||||||
|
serializer = DiskUsageSerializer(many=True)
|
||||||
|
|
||||||
|
def post(self, request: rest_framework.request.Request, format=None):
|
||||||
|
if request.content_type != "application/json":
|
||||||
|
return Response("Unacceptable", status=http.HTTPStatus.BAD_REQUEST)
|
||||||
|
|
||||||
|
submitted_sources = set()
|
||||||
|
|
||||||
|
for row in request.data:
|
||||||
|
user = row["user"]
|
||||||
|
for key in row:
|
||||||
|
if key == "user":
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
submitted_sources.add(key)
|
||||||
|
for item, size in row[key].items():
|
||||||
|
try:
|
||||||
|
package = CustomerHostingPackage.objects.get(
|
||||||
|
osuser__username=user
|
||||||
|
)
|
||||||
|
(
|
||||||
|
metric,
|
||||||
|
created,
|
||||||
|
) = CustomerPackageDiskUsage.objects.get_or_create(
|
||||||
|
package=package,
|
||||||
|
source=key,
|
||||||
|
item=item,
|
||||||
|
)
|
||||||
|
metric.used_kb = size
|
||||||
|
|
||||||
|
if key == "mail":
|
||||||
|
try:
|
||||||
|
ma_mb = package.mailboxes.get(
|
||||||
|
username=item
|
||||||
|
).mailaddressmailbox_set.first()
|
||||||
|
if ma_mb:
|
||||||
|
metric.email_address_id = ma_mb.mailaddress_id
|
||||||
|
except Mailbox.DoesNotExist:
|
||||||
|
logger.warning("mail box %s does not exist", item)
|
||||||
|
|
||||||
|
metric.save()
|
||||||
|
except CustomerHostingPackage.DoesNotExist:
|
||||||
|
logger.warning(
|
||||||
|
"hosting package for user %s does not exist", user
|
||||||
|
)
|
||||||
|
|
||||||
|
if submitted_sources:
|
||||||
|
CustomerPackageDiskUsage.objects.filter(
|
||||||
|
source__in=submitted_sources,
|
||||||
|
modified__lt=timezone.now() - timedelta(minutes=30),
|
||||||
|
).delete()
|
||||||
|
|
||||||
|
logger.info("usage data submitted by %s", request.user)
|
||||||
|
|
||||||
|
return Response("Accepted", status=http.HTTPStatus.ACCEPTED)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerHostingPackageDiskUsageDetails(StaffOrSelfLoginRequiredMixin, DetailView):
|
||||||
|
template_name_suffix = "_disk_usage_details"
|
||||||
|
model = CustomerHostingPackage
|
||||||
|
pk_url_kwarg = "package"
|
||||||
|
context_object_name = "hostingpackage"
|
||||||
|
|
||||||
|
customer = None
|
||||||
|
|
||||||
|
def get_customer_object(self):
|
||||||
|
if self.customer is None:
|
||||||
|
self.customer = get_object_or_404(
|
||||||
|
get_user_model(), username=self.kwargs["user"]
|
||||||
|
)
|
||||||
|
return self.customer
|
||||||
|
|
||||||
|
def get_queryset(self, queryset=None):
|
||||||
|
return super().get_queryset().prefetch_related("customerpackagediskusage_set")
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context_data = super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
mail_usage, web_usage, mysql_usage, pgsql_usage = [], [], [], []
|
||||||
|
|
||||||
|
for usage in self.get_object().customerpackagediskusage_set.order_by(
|
||||||
|
"-used_kb"
|
||||||
|
):
|
||||||
|
if usage.used_kb <= 0:
|
||||||
|
continue
|
||||||
|
if usage.source == "mail":
|
||||||
|
mail_usage.append(usage)
|
||||||
|
elif usage.source == "web":
|
||||||
|
web_usage.append(usage)
|
||||||
|
elif usage.source == "mysql":
|
||||||
|
mysql_usage.append(usage)
|
||||||
|
elif usage.source == "pgsql":
|
||||||
|
pgsql_usage.append(usage)
|
||||||
|
|
||||||
|
context_data.update(
|
||||||
|
{
|
||||||
|
"customer": self.get_customer_object(),
|
||||||
|
"mail_usage": mail_usage,
|
||||||
|
"web_usage": web_usage,
|
||||||
|
"mysql_usage": mysql_usage,
|
||||||
|
"pgsql_usage": pgsql_usage,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return context_data
|
||||||
|
|
0
gnuviechadmin/invoices/__init__.py
Normal file
0
gnuviechadmin/invoices/__init__.py
Normal file
14
gnuviechadmin/invoices/admin.py
Normal file
14
gnuviechadmin/invoices/admin.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from invoices.models import Invoice
|
||||||
|
|
||||||
|
|
||||||
|
class InvoiceAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ["invoice_number", "customer"]
|
||||||
|
readonly_fields = ["customer"]
|
||||||
|
|
||||||
|
def has_add_permission(self, request):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(Invoice, InvoiceAdmin)
|
8
gnuviechadmin/invoices/apps.py
Normal file
8
gnuviechadmin/invoices/apps.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class InvoiceConfig(AppConfig):
|
||||||
|
default_auto_field = "django.db.models.BigAutoField"
|
||||||
|
name = "invoices"
|
||||||
|
verbose_name = _("Invoices")
|
69
gnuviechadmin/invoices/locale/de/LC_MESSAGES/django.po
Normal file
69
gnuviechadmin/invoices/locale/de/LC_MESSAGES/django.po
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: gnuviechadmin invoice\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2023-07-22 19:45+0200\n"
|
||||||
|
"PO-Revision-Date: 2023-04-23 14:35+0200\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
|
||||||
|
"Language: de\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"X-Generator: Poedit 3.2.2\n"
|
||||||
|
"X-Poedit-SourceCharset: UTF-8\n"
|
||||||
|
|
||||||
|
#: invoices/apps.py:8
|
||||||
|
msgid "Invoices"
|
||||||
|
msgstr "Rechnungen"
|
||||||
|
|
||||||
|
#: invoices/models.py:26
|
||||||
|
msgid "customer"
|
||||||
|
msgstr "Kunde"
|
||||||
|
|
||||||
|
#: invoices/models.py:33
|
||||||
|
msgid "invoice number"
|
||||||
|
msgstr "Rechnungsnummer"
|
||||||
|
|
||||||
|
#: invoices/models.py:35
|
||||||
|
msgid "invoice date"
|
||||||
|
msgstr "Rechnungsdatum"
|
||||||
|
|
||||||
|
#: invoices/models.py:37
|
||||||
|
msgid "amount"
|
||||||
|
msgstr "Betrag"
|
||||||
|
|
||||||
|
#: invoices/models.py:40
|
||||||
|
msgid "currency"
|
||||||
|
msgstr "Währung"
|
||||||
|
|
||||||
|
#: invoices/models.py:42
|
||||||
|
msgid "due date"
|
||||||
|
msgstr "Fälligkeit"
|
||||||
|
|
||||||
|
#: invoices/models.py:44
|
||||||
|
msgid "payment date"
|
||||||
|
msgstr "Zahlungsdatum"
|
||||||
|
|
||||||
|
#: invoices/models.py:47
|
||||||
|
msgid "payment variant"
|
||||||
|
msgstr "Zahlungsart"
|
||||||
|
|
||||||
|
#: invoices/models.py:51
|
||||||
|
msgid "invoice"
|
||||||
|
msgstr "Rechnung"
|
||||||
|
|
||||||
|
#: invoices/models.py:52
|
||||||
|
msgid "invoices"
|
||||||
|
msgstr "Rechnungen"
|
||||||
|
|
||||||
|
#: invoices/models.py:56
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Invoice {0}"
|
||||||
|
msgstr "Rechnung {0}"
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue