Compare commits
48 commits
df3628499d
...
4577ec4896
Author | SHA1 | Date | |
---|---|---|---|
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 | |||
b12a891fd7 |
270 changed files with 17933 additions and 8644 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/
|
||||
|
||||
.env
|
||||
.envrc
|
||||
|
||||
/docker/django_media
|
||||
/docker/django_static
|
||||
!/docker/django_media/.empty
|
||||
!/docker/django_static/.empty
|
||||
/media/
|
||||
/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
|
82
Dockerfile
82
Dockerfile
|
@ -1,56 +1,70 @@
|
|||
ARG DEBIAN_RELEASE=buster
|
||||
FROM debian:$DEBIAN_RELEASE AS builder
|
||||
|
||||
ARG GVAAPP=gva
|
||||
ARG POETRY_VERSION=1.3.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
|
||||
|
||||
FROM debian:$DEBIAN_RELEASE
|
||||
LABEL maintainer="Jan Dittberner <jan@dittberner.info>"
|
||||
|
||||
ENV LC_ALL=C.UTF-8
|
||||
ENV LANG=C.UTF-8
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
dumb-init \
|
||||
gettext \
|
||||
git \
|
||||
python3-dev \
|
||||
python3-pip \
|
||||
python3-setuptools \
|
||||
python3-virtualenv \
|
||||
python3-wheel \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
dumb-init \
|
||||
gettext \
|
||||
postgresql-client \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-wheel \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /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/*.*
|
||||
&& rm -rf /var/cache/apt/archives /var/lib/apt/lists/*
|
||||
|
||||
ARG GVAAPP=gva
|
||||
ARG GVAGID=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
|
||||
|
||||
COPY Pipfile Pipfile.lock /srv/$GVAAPP/
|
||||
VOLUME /srv/$GVAAPP/media /srv/$GVAAPP/static
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
VOLUME /srv/$GVAAPP/gnuviechadmin
|
||||
|
||||
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"
|
||||
services:
|
||||
db:
|
||||
|
@ -36,9 +37,9 @@ services:
|
|||
GVA_DOMAIN_NAME: localhost
|
||||
GVA_SITE_NAME: localhost
|
||||
volumes:
|
||||
- "./docker/django_media:/srv/gva/media"
|
||||
- "./docker/django_static:/srv/gva/static"
|
||||
- ".:/srv/gva"
|
||||
- "django_media:/srv/gva/media"
|
||||
- "django_static:/srv/gva/static"
|
||||
- "./gnuviechadmin:/srv/gva/gnuviechadmin"
|
||||
web:
|
||||
image: gnuviech/gvaweb:buster
|
||||
build:
|
||||
|
@ -51,7 +52,7 @@ services:
|
|||
- redis
|
||||
env_file: ../gvaweb/.env
|
||||
volumes:
|
||||
- "../gvaweb:/srv/gvaweb"
|
||||
- "../gvaweb/gvaweb:/srv/gvaweb/gvaweb"
|
||||
ldap:
|
||||
image: gnuviech/gvaldap:buster
|
||||
build:
|
||||
|
@ -64,7 +65,7 @@ services:
|
|||
- redis
|
||||
env_file: ../gvaldap/.env
|
||||
volumes:
|
||||
- "../gvaldap:/srv/gvaldap"
|
||||
- "../gvaldap/gvaldap:/srv/gvaldap/gvaldap"
|
||||
file:
|
||||
image: gnuviech/gvafile:buster
|
||||
build:
|
||||
|
@ -77,7 +78,7 @@ services:
|
|||
- redis
|
||||
env_file: ../gvafile/.env
|
||||
volumes:
|
||||
- "../gvafile:/srv/gvafile"
|
||||
- "../gvafile/gvafile:/srv/gvafile/gvafile"
|
||||
pgsql:
|
||||
image: gnuviech/gvapgsql:buster
|
||||
build:
|
||||
|
@ -90,7 +91,7 @@ services:
|
|||
- redis
|
||||
env_file: ../gvapgsql/.env
|
||||
volumes:
|
||||
- "../gvapgsql:/srv/gvapgsql"
|
||||
- "../gvapgsql/gvapgsql:/srv/gvapgsql/gvapgsql"
|
||||
mysql:
|
||||
image: gnuviech/gvamysql:buster
|
||||
build:
|
||||
|
@ -103,7 +104,7 @@ services:
|
|||
- redis
|
||||
env_file: ../gvamysql/.env
|
||||
volumes:
|
||||
- "../gvamysql:/srv/gvamysql"
|
||||
- "../gvamysql/gvamysql:/srv/gvamysql/gvamysql"
|
||||
volumes:
|
||||
django_media:
|
||||
django_static:
|
||||
|
|
|
@ -1,6 +1,16 @@
|
|||
Changelog
|
||||
=========
|
||||
|
||||
* :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
|
||||
|
|
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,16 @@ This module contains the form class for the contact_form app.
|
|||
"""
|
||||
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.layout import Submit
|
||||
from django import forms
|
||||
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):
|
||||
|
@ -24,45 +21,42 @@ class ContactForm(forms.Form):
|
|||
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'))
|
||||
body = forms.CharField(widget=forms.Textarea, label=_('Your message'))
|
||||
|
||||
name = forms.CharField(max_length=100, label=_("Your name"))
|
||||
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"
|
||||
template_name = 'contact_form/contact_form.txt'
|
||||
template_name = "contact_form/contact_form.txt"
|
||||
from_email = settings.DEFAULT_FROM_EMAIL
|
||||
recipient_list = [mail_tuple[1] for mail_tuple in settings.MANAGERS]
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.request = kwargs.pop('request')
|
||||
self.request = kwargs.pop("request")
|
||||
super(ContactForm, self).__init__(**kwargs)
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_action = reverse('contact_form')
|
||||
self.helper.add_input(Submit('submit', _('Send message')))
|
||||
self.helper.form_action = reverse("contact_form")
|
||||
self.helper.add_input(Submit("submit", _("Send message")))
|
||||
|
||||
def get_context(self):
|
||||
if not self.is_valid():
|
||||
raise ValueError(
|
||||
'Cannot generate context from invalid contact form')
|
||||
raise ValueError("Cannot generate context from invalid contact form")
|
||||
if Site._meta.installed:
|
||||
site = Site.objects.get_current()
|
||||
else:
|
||||
site = RequestSite(self.request)
|
||||
return RequestContext(
|
||||
self.request, dict(self.cleaned_data, site=site))
|
||||
return RequestContext(self.request, dict(self.cleaned_data, site=site))
|
||||
|
||||
def message(self):
|
||||
context = self.get_context()
|
||||
template_context = context.flatten()
|
||||
template_context.update({
|
||||
'remote_ip': context.request.META['REMOTE_ADDR']
|
||||
})
|
||||
template_context.update({"remote_ip": context.request.META["REMOTE_ADDR"]})
|
||||
return loader.render_to_string(self.template_name, template_context)
|
||||
|
||||
def subject(self):
|
||||
context = self.get_context().flatten()
|
||||
subject = loader.render_to_string(self.subject_template_name, context)
|
||||
return ''.join(subject.splitlines())
|
||||
return "".join(subject.splitlines())
|
||||
|
||||
def save(self, fail_silently=False):
|
||||
"""
|
||||
|
@ -74,5 +68,5 @@ class ContactForm(forms.Form):
|
|||
from_email=self.from_email,
|
||||
recipient_list=self.recipient_list,
|
||||
subject=self.subject(),
|
||||
message=self.message()
|
||||
message=self.message(),
|
||||
)
|
||||
|
|
|
@ -7,8 +7,8 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: contact_form\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-01-29 11:04+0100\n"
|
||||
"PO-Revision-Date: 2015-02-01 19:03+0100\n"
|
||||
"POT-Creation-Date: 2023-04-22 13:01+0200\n"
|
||||
"PO-Revision-Date: 2023-04-22 13:01+0200\n"
|
||||
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
|
||||
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
|
||||
"Language: de\n"
|
||||
|
@ -16,21 +16,32 @@ msgstr ""
|
|||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\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"
|
||||
|
||||
#: contact_form/forms.py:27
|
||||
#: contact_form/forms.py:25
|
||||
msgid "Your name"
|
||||
msgstr "Ihr Name"
|
||||
|
||||
#: contact_form/forms.py:28
|
||||
#: contact_form/forms.py:26
|
||||
msgid "Your email address"
|
||||
msgstr "Ihre E-Mailadresse"
|
||||
msgstr "Ihre E-Mail-Adresse"
|
||||
|
||||
#: contact_form/forms.py:29
|
||||
#: contact_form/forms.py:27
|
||||
msgid "Your message"
|
||||
msgstr "Ihre Nachricht"
|
||||
|
||||
#: contact_form/forms.py:41
|
||||
#: contact_form/forms.py:39
|
||||
msgid "Send message"
|
||||
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.
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from .views import (
|
||||
ContactFormView,
|
||||
ContactSuccessView,
|
||||
)
|
||||
from django.urls import re_path
|
||||
|
||||
from .views import ContactFormView, ContactSuccessView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', ContactFormView.as_view(), name='contact_form'),
|
||||
url(r'^success/$', ContactSuccessView.as_view(), name='contact_success'),
|
||||
re_path(r"^$", ContactFormView.as_view(), name="contact_form"),
|
||||
re_path(r"^success/$", ContactSuccessView.as_view(), name="contact_success"),
|
||||
]
|
||||
|
|
|
@ -2,14 +2,11 @@
|
|||
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.urls import reverse_lazy
|
||||
from django.views.generic import (
|
||||
FormView,
|
||||
TemplateView,
|
||||
)
|
||||
from django.views.generic import FormView, TemplateView
|
||||
|
||||
from .forms import ContactForm
|
||||
|
||||
|
@ -19,22 +16,22 @@ class ContactFormView(FormView):
|
|||
This is the contact form view.
|
||||
|
||||
"""
|
||||
|
||||
form_class = ContactForm
|
||||
template_name = 'contact_form/contact_form.html'
|
||||
success_url = reverse_lazy('contact_success')
|
||||
template_name = "contact_form/contact_form.html"
|
||||
success_url = reverse_lazy("contact_success")
|
||||
|
||||
def get_form_kwargs(self, **kwargs):
|
||||
kwargs = super(ContactFormView, self).get_form_kwargs(**kwargs)
|
||||
kwargs['request'] = self.request
|
||||
kwargs["request"] = self.request
|
||||
return kwargs
|
||||
|
||||
def get_initial(self):
|
||||
initial = super(ContactFormView, self).get_initial()
|
||||
currentuser = self.request.user
|
||||
if currentuser.is_authenticated:
|
||||
initial['name'] = (
|
||||
currentuser.get_full_name() or currentuser.username)
|
||||
initial['email'] = currentuser.email
|
||||
initial["name"] = currentuser.get_full_name() or currentuser.username
|
||||
initial["email"] = currentuser.email
|
||||
return initial
|
||||
|
||||
def form_valid(self, form):
|
||||
|
@ -47,4 +44,5 @@ class ContactSuccessView(TemplateView):
|
|||
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,92 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: gnuviechadmin dashboard\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 15:59+0100\n"
|
||||
"PO-Revision-Date: 2015-01-17 16:01+0100\n"
|
||||
"POT-Creation-Date: 2023-04-16 22:07+0200\n"
|
||||
"PO-Revision-Date: 2023-04-16 18:31+0200\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"
|
||||
"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 1.6.10\n"
|
||||
"X-Generator: Poedit 3.2.2\n"
|
||||
"X-Poedit-SourceCharset: UTF-8\n"
|
||||
|
||||
#: dashboard/views.py:43
|
||||
msgid "You are not allowed to view this page."
|
||||
msgstr "Sie haben nicht die nötigen Berechtigungen um diese Seite zu sehen."
|
||||
#: dashboard/templates/dashboard/index.html:3
|
||||
msgid "Welcome"
|
||||
msgstr "Willkommen"
|
||||
|
||||
#: dashboard/templates/dashboard/index.html:4
|
||||
msgid "Welcome to our customer self service"
|
||||
msgstr "Willkommen in unserem Selbstservice-System"
|
||||
|
||||
#: dashboard/templates/dashboard/index.html:7
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Hello %(full_name)s,<br/> You can visit your <a "
|
||||
"href=\"%(dashboard_url)s\">Dashboard</a> to view and modify your hosting "
|
||||
"options."
|
||||
msgstr ""
|
||||
"Hallo %(full_name)s,<br /> Sie können Ihre <a "
|
||||
"href=\"%(dashboard_url)s\">Startseite</a> besuchen, um Ihre "
|
||||
"Hostingeinstellungen anzusehen und zu bearbeiten."
|
||||
|
||||
#: dashboard/templates/dashboard/user_dashboard.html:3
|
||||
#: dashboard/templates/dashboard/user_dashboard.html:6
|
||||
#, 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 "Disk space"
|
||||
msgstr "Speicherplatz"
|
||||
|
||||
#: dashboard/templates/dashboard/user_dashboard.html:19
|
||||
msgid "Mailboxes"
|
||||
msgstr "Postfächer"
|
||||
|
||||
#: dashboard/templates/dashboard/user_dashboard.html:20
|
||||
msgid "Databases"
|
||||
msgstr "Datenbanken"
|
||||
|
||||
#: dashboard/templates/dashboard/user_dashboard.html:21
|
||||
msgid "Actions"
|
||||
msgstr "Aktionen"
|
||||
|
||||
#: dashboard/templates/dashboard/user_dashboard.html:28
|
||||
#, python-format
|
||||
msgid "Show details for %(packagename)s"
|
||||
msgstr "Details für %(packagename)s anzeigen"
|
||||
|
||||
#: dashboard/templates/dashboard/user_dashboard.html:34
|
||||
#, python-format
|
||||
msgid ""
|
||||
"The reserved disk space for your hosting package is %(diskspace)s bytes."
|
||||
msgstr ""
|
||||
"Der für Ihr Hostingpaket reservierte Speicherplatz sind %(diskspace)s Bytes."
|
||||
|
||||
#: dashboard/templates/dashboard/user_dashboard.html:40
|
||||
#, python-format
|
||||
msgid "used %(num)s of %(total)s"
|
||||
msgstr "%(num)s von %(total)s genutzt"
|
||||
|
||||
#: dashboard/templates/dashboard/user_dashboard.html:55
|
||||
msgid "You have no hosting packages yet."
|
||||
msgstr "Sie haben noch keine Hostingpakete."
|
||||
|
||||
#: dashboard/templates/dashboard/user_dashboard.html:56
|
||||
msgid "This user has no hosting packages assigned yet."
|
||||
msgstr "Diesem Benutzer sind noch keine Hostingpakete zugewiesen."
|
||||
|
||||
#: dashboard/templates/dashboard/user_dashboard.html:60
|
||||
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`.
|
||||
|
||||
"""
|
||||
|
||||
from django import http
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
@ -13,55 +13,35 @@ TEST_USER = "test"
|
|||
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):
|
||||
def _create_test_user(self):
|
||||
self.user = User.objects.create(username=TEST_USER)
|
||||
self.user.set_password(TEST_PASSWORD)
|
||||
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):
|
||||
User.objects.create(username=TEST_USER)
|
||||
response = self.client.get(
|
||||
reverse("customer_dashboard", kwargs={"slug": TEST_USER})
|
||||
)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
response = self.client.get(reverse("customer_dashboard"))
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertRedirects(response, "/accounts/login/?next=/")
|
||||
|
||||
def test_user_dashboard_view_logged_in_ok(self):
|
||||
self._create_test_user()
|
||||
self.assertTrue(self.client.login(username=TEST_USER, password=TEST_PASSWORD))
|
||||
response = self.client.get(
|
||||
reverse("customer_dashboard", kwargs={"slug": TEST_USER})
|
||||
)
|
||||
response = self.client.get(reverse("customer_dashboard"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_user_dashboard_view_logged_in_template(self):
|
||||
self._create_test_user()
|
||||
self.assertTrue(self.client.login(username=TEST_USER, password=TEST_PASSWORD))
|
||||
response = self.client.get(
|
||||
reverse("customer_dashboard", kwargs={"slug": TEST_USER})
|
||||
reverse("customer_dashboard")
|
||||
)
|
||||
self.assertTemplateUsed(response, "dashboard/user_dashboard.html")
|
||||
|
||||
def test_user_dashboard_view_logged_in_context_fresh(self):
|
||||
self._create_test_user()
|
||||
self.assertTrue(self.client.login(username=TEST_USER, password=TEST_PASSWORD))
|
||||
response = self.client.get(
|
||||
reverse("customer_dashboard", kwargs={"slug": TEST_USER})
|
||||
)
|
||||
self.assertIn("dashboard_user", response.context)
|
||||
self.assertEqual(self.user, response.context["dashboard_user"])
|
||||
response = self.client.get(reverse("customer_dashboard"))
|
||||
self.assertIn("hosting_packages", response.context)
|
||||
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 .views import (
|
||||
IndexView,
|
||||
UserDashboardView,
|
||||
)
|
||||
from django.urls import path
|
||||
|
||||
from .views import UserDashboardView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', IndexView.as_view(), name='dashboard'),
|
||||
url(r'^user/(?P<slug>[\w0-9@.+-_]+)/$',
|
||||
UserDashboardView.as_view(), name='customer_dashboard'),
|
||||
path("", UserDashboardView.as_view(), name="customer_dashboard"),
|
||||
]
|
||||
|
|
|
@ -2,47 +2,26 @@
|
|||
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.mixins import LoginRequiredMixin
|
||||
from django.shortcuts import redirect
|
||||
from django.views.generic import DetailView, TemplateView
|
||||
from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin
|
||||
|
||||
from hostingpackages.models import CustomerHostingPackage
|
||||
|
||||
|
||||
class IndexView(TemplateView):
|
||||
"""
|
||||
This is the dashboard view.
|
||||
|
||||
"""
|
||||
template_name = 'dashboard/index.html'
|
||||
|
||||
|
||||
class UserDashboardView(StaffOrSelfLoginRequiredMixin, DetailView):
|
||||
class UserDashboardView(LoginRequiredMixin, TemplateView):
|
||||
"""
|
||||
This is the user dashboard view.
|
||||
|
||||
"""
|
||||
model = get_user_model()
|
||||
context_object_name = 'dashboard_user'
|
||||
slug_field = 'username'
|
||||
template_name = 'dashboard/user_dashboard.html'
|
||||
|
||||
template_name = "dashboard/user_dashboard.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UserDashboardView, self).get_context_data(**kwargs)
|
||||
context['hosting_packages'] = CustomerHostingPackage.objects.filter(
|
||||
customer=self.object
|
||||
context["hosting_packages"] = CustomerHostingPackage.objects.filter(
|
||||
customer=self.request.user
|
||||
)
|
||||
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.
|
||||
|
||||
"""
|
||||
default_app_config = 'domains.apps.DomainAppConfig'
|
||||
|
|
|
@ -5,24 +5,7 @@ with the django admin site.
|
|||
"""
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import (
|
||||
DNSComment,
|
||||
DNSCryptoKey,
|
||||
DNSDomain,
|
||||
DNSDomainMetadata,
|
||||
DNSRecord,
|
||||
DNSSupermaster,
|
||||
DNSTSIGKey,
|
||||
HostingDomain,
|
||||
MailDomain,
|
||||
)
|
||||
from domains.models import HostingDomain, MailDomain
|
||||
|
||||
admin.site.register(MailDomain)
|
||||
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.
|
||||
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
from django.apps import AppConfig
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class DomainAppConfig(AppConfig):
|
||||
|
@ -13,5 +12,6 @@ class DomainAppConfig(AppConfig):
|
|||
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.
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
from __future__ import absolute_import
|
||||
|
||||
import re
|
||||
|
||||
from crispy_forms.helper import FormHelper
|
||||
from crispy_forms.layout import Layout, Submit
|
||||
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.layout import (
|
||||
Layout,
|
||||
Submit,
|
||||
)
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from .models import HostingDomain
|
||||
|
||||
|
@ -26,11 +22,10 @@ def relative_domain_validator(value):
|
|||
|
||||
"""
|
||||
if len(value) > 254:
|
||||
raise forms.ValidationError(
|
||||
_('host name too long'), code='too-long')
|
||||
raise forms.ValidationError(_("host name too long"), code="too-long")
|
||||
allowed = re.compile(r"(?!-)[a-z\d-]{1,63}(?<!-)$")
|
||||
if not all(allowed.match(x) for x in value.split('.')):
|
||||
raise forms.ValidationError(_('invalid domain name'))
|
||||
if not all(allowed.match(x) for x in value.split(".")):
|
||||
raise forms.ValidationError(_("invalid domain name"))
|
||||
|
||||
|
||||
class CreateHostingDomainForm(forms.ModelForm):
|
||||
|
@ -38,31 +33,32 @@ class CreateHostingDomainForm(forms.ModelForm):
|
|||
This form is used to create new HostingDomain instances.
|
||||
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
model = HostingDomain
|
||||
fields = ['domain']
|
||||
fields = ["domain"]
|
||||
|
||||
def __init__(self, instance, *args, **kwargs):
|
||||
self.hosting_package = kwargs.pop('hostingpackage')
|
||||
self.hosting_package = kwargs.pop("hostingpackage")
|
||||
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.form_action = reverse(
|
||||
'create_hosting_domain', kwargs={
|
||||
'package': self.hosting_package.id
|
||||
})
|
||||
"create_hosting_domain", kwargs={"package": self.hosting_package.id}
|
||||
)
|
||||
self.helper.layout = Layout(
|
||||
'domain',
|
||||
Submit('submit', _('Add Hosting Domain')),
|
||||
"domain",
|
||||
Submit("submit", _("Add Hosting Domain")),
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
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):
|
||||
return HostingDomain.objects.create_for_hosting_package(
|
||||
commit=commit, **self.cleaned_data)
|
||||
commit=commit, **self.cleaned_data
|
||||
)
|
||||
|
||||
def save_m2m(self):
|
||||
pass
|
||||
|
|
|
@ -7,8 +7,8 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: gnuviechadmin domains\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-01-29 11:04+0100\n"
|
||||
"PO-Revision-Date: 2015-11-08 12:02+0100\n"
|
||||
"POT-Creation-Date: 2023-04-16 22:07+0200\n"
|
||||
"PO-Revision-Date: 2023-04-16 18:20+0200\n"
|
||||
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
|
||||
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
|
||||
"Language: de\n"
|
||||
|
@ -16,152 +16,60 @@ msgstr ""
|
|||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\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"
|
||||
|
||||
#: domains/apps.py:17
|
||||
msgid "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"
|
||||
msgstr "zu langer Hostname"
|
||||
|
||||
#: domains/forms.py:33 domains/tests/test_forms.py:29
|
||||
#: domains/tests/test_forms.py:34 domains/tests/test_forms.py:39
|
||||
#: domains/tests/test_forms.py:44
|
||||
#: domains/forms.py:28 domains/tests/test_forms.py:24
|
||||
#: domains/tests/test_forms.py:28 domains/tests/test_forms.py:32
|
||||
#: domains/tests/test_forms.py:36
|
||||
msgid "invalid domain name"
|
||||
msgstr "ungültiger Domainname"
|
||||
|
||||
#: domains/forms.py:56
|
||||
#: domains/forms.py:51
|
||||
msgid "Add Hosting Domain"
|
||||
msgstr "Hostingdomain hinzufügen"
|
||||
|
||||
#: domains/models.py:17
|
||||
msgid "Master"
|
||||
msgstr "Master"
|
||||
|
||||
#: domains/models.py:18
|
||||
msgid "Slave"
|
||||
msgstr "Slave"
|
||||
|
||||
#: 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"
|
||||
msgstr "Domainname"
|
||||
|
||||
#: domains/models.py:60 domains/models.py:258 domains/models.py:308
|
||||
#: domains/models.py:22
|
||||
msgid "customer"
|
||||
msgstr "Kunde"
|
||||
|
||||
#: domains/models.py:76
|
||||
#: domains/models.py:41
|
||||
msgid "Mail domain"
|
||||
msgstr "E-Maildomain"
|
||||
|
||||
#: domains/models.py:77
|
||||
#: domains/models.py:42
|
||||
msgid "Mail domains"
|
||||
msgstr "E-Maildomains"
|
||||
|
||||
#: domains/models.py:121
|
||||
#: domains/models.py:91
|
||||
msgid "mail domain"
|
||||
msgstr "E-Maildomain"
|
||||
|
||||
#: domains/models.py:122
|
||||
#: domains/models.py:94
|
||||
msgid "assigned mail domain for this domain"
|
||||
msgstr "zugeordnete E-Maildomain für diese Domain"
|
||||
|
||||
#: domains/models.py:128
|
||||
#: domains/models.py:101
|
||||
msgid "Hosting domain"
|
||||
msgstr "Hostingdomain"
|
||||
|
||||
#: domains/models.py:129
|
||||
#: domains/models.py:102
|
||||
msgid "Hosting domains"
|
||||
msgstr "Hostingdomains"
|
||||
|
||||
#: domains/models.py:169
|
||||
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
|
||||
#: domains/views.py:51
|
||||
#, python-brace-format
|
||||
msgid "Successfully created domain {domainname}"
|
||||
msgstr "Domain {domainname} erfolgreich angelegt"
|
||||
|
|
|
@ -1,28 +1,46 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import django.utils.timezone
|
||||
import model_utils.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
dependencies = []
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='MailDomain',
|
||||
name="MailDomain",
|
||||
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)),
|
||||
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, verbose_name='modified', editable=False)),
|
||||
('domain', models.CharField(unique=True, max_length=128)),
|
||||
(
|
||||
"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,
|
||||
),
|
||||
),
|
||||
(
|
||||
"modified",
|
||||
model_utils.fields.AutoLastModifiedField(
|
||||
default=django.utils.timezone.now,
|
||||
verbose_name="modified",
|
||||
editable=False,
|
||||
),
|
||||
),
|
||||
("domain", models.CharField(unique=True, max_length=128)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Mail domain',
|
||||
'verbose_name_plural': 'Mail domains',
|
||||
"verbose_name": "Mail domain",
|
||||
"verbose_name_plural": "Mail domains",
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
|
|
|
@ -1,68 +1,97 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import django.utils.timezone
|
||||
from django.conf import settings
|
||||
import model_utils.fields
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('domains', '0001_initial'),
|
||||
("domains", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='HostingDomain',
|
||||
name="HostingDomain",
|
||||
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)),
|
||||
('modified',
|
||||
model_utils.fields.AutoLastModifiedField(
|
||||
default=django.utils.timezone.now, verbose_name='modified',
|
||||
editable=False)),
|
||||
('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)),
|
||||
(
|
||||
"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,
|
||||
),
|
||||
),
|
||||
(
|
||||
"modified",
|
||||
model_utils.fields.AutoLastModifiedField(
|
||||
default=django.utils.timezone.now,
|
||||
verbose_name="modified",
|
||||
editable=False,
|
||||
),
|
||||
),
|
||||
(
|
||||
"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={
|
||||
'verbose_name': 'Hosting domain',
|
||||
'verbose_name_plural': 'Hosting domains',
|
||||
"verbose_name": "Hosting domain",
|
||||
"verbose_name_plural": "Hosting domains",
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='maildomain',
|
||||
name='customer',
|
||||
model_name="maildomain",
|
||||
name="customer",
|
||||
field=models.ForeignKey(
|
||||
verbose_name='customer', blank=True,
|
||||
to=settings.AUTH_USER_MODEL, null=True,
|
||||
on_delete=models.CASCADE),
|
||||
verbose_name="customer",
|
||||
blank=True,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
null=True,
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='maildomain',
|
||||
name='domain',
|
||||
model_name="maildomain",
|
||||
name="domain",
|
||||
field=models.CharField(
|
||||
unique=True, max_length=128, verbose_name='domain name'),
|
||||
unique=True, max_length=128, verbose_name="domain name"
|
||||
),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,199 +1,285 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
from django.conf import settings
|
||||
import model_utils.fields
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('domains', '0002_auto_20150124_1909'),
|
||||
("domains", "0002_auto_20150124_1909"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='DNSComment',
|
||||
name="DNSComment",
|
||||
fields=[
|
||||
('id', models.AutoField(
|
||||
verbose_name='ID', serialize=False,
|
||||
auto_created=True, primary_key=True)),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('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)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
verbose_name="ID",
|
||||
serialize=False,
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=255)),
|
||||
("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(
|
||||
'''ALTER TABLE domains_dnscomment ADD CONSTRAINT c_lowercase_name
|
||||
CHECK (((name)::TEXT = LOWER((name)::TEXT)))'''
|
||||
"""ALTER TABLE domains_dnscomment ADD CONSTRAINT c_lowercase_name
|
||||
CHECK (((name)::TEXT = LOWER((name)::TEXT)))"""
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DNSCryptoKey',
|
||||
name="DNSCryptoKey",
|
||||
fields=[
|
||||
('id', models.AutoField(
|
||||
verbose_name='ID', serialize=False,
|
||||
auto_created=True, primary_key=True)),
|
||||
('flags', models.IntegerField()),
|
||||
('active', models.BooleanField(default=True)),
|
||||
('content', models.TextField()),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
verbose_name="ID",
|
||||
serialize=False,
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
),
|
||||
),
|
||||
("flags", models.IntegerField()),
|
||||
("active", models.BooleanField(default=True)),
|
||||
("content", models.TextField()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DNSDomain',
|
||||
name="DNSDomain",
|
||||
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)),
|
||||
('modified', model_utils.fields.AutoLastModifiedField(
|
||||
default=django.utils.timezone.now, verbose_name='modified',
|
||||
editable=False)),
|
||||
('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)),
|
||||
(
|
||||
"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,
|
||||
),
|
||||
),
|
||||
(
|
||||
"modified",
|
||||
model_utils.fields.AutoLastModifiedField(
|
||||
default=django.utils.timezone.now,
|
||||
verbose_name="modified",
|
||||
editable=False,
|
||||
),
|
||||
),
|
||||
(
|
||||
"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={
|
||||
'verbose_name': 'DNS domain',
|
||||
'verbose_name_plural': 'DNS domains',
|
||||
"verbose_name": "DNS domain",
|
||||
"verbose_name_plural": "DNS domains",
|
||||
},
|
||||
),
|
||||
migrations.RunSQL(
|
||||
'''ALTER TABLE domains_dnsdomain ADD CONSTRAINT c_lowercase_name
|
||||
CHECK (((domain)::TEXT = LOWER((domain)::TEXT)))'''
|
||||
"""ALTER TABLE domains_dnsdomain ADD CONSTRAINT c_lowercase_name
|
||||
CHECK (((domain)::TEXT = LOWER((domain)::TEXT)))"""
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DNSDomainMetadata',
|
||||
name="DNSDomainMetadata",
|
||||
fields=[
|
||||
('id', models.AutoField(
|
||||
verbose_name='ID', serialize=False,
|
||||
auto_created=True, primary_key=True)),
|
||||
('kind', models.CharField(max_length=32)),
|
||||
('content', models.TextField()),
|
||||
('domain', models.ForeignKey(
|
||||
to='domains.DNSDomain', on_delete=models.CASCADE)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
verbose_name="ID",
|
||||
serialize=False,
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
),
|
||||
),
|
||||
("kind", models.CharField(max_length=32)),
|
||||
("content", models.TextField()),
|
||||
(
|
||||
"domain",
|
||||
models.ForeignKey(to="domains.DNSDomain", on_delete=models.CASCADE),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DNSRecord',
|
||||
name="DNSRecord",
|
||||
fields=[
|
||||
('id', models.AutoField(
|
||||
verbose_name='ID', serialize=False,
|
||||
auto_created=True, primary_key=True)),
|
||||
('name', models.CharField(
|
||||
db_index=True, max_length=255, null=True, blank=True)),
|
||||
('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)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
verbose_name="ID",
|
||||
serialize=False,
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"name",
|
||||
models.CharField(
|
||||
db_index=True, max_length=255, null=True, blank=True
|
||||
),
|
||||
),
|
||||
(
|
||||
"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={
|
||||
'verbose_name': 'DNS record',
|
||||
'verbose_name_plural': 'DNS records',
|
||||
"verbose_name": "DNS record",
|
||||
"verbose_name_plural": "DNS records",
|
||||
},
|
||||
),
|
||||
migrations.RunSQL(
|
||||
'''ALTER TABLE domains_dnsrecord ADD CONSTRAINT c_lowercase_name
|
||||
CHECK (((name)::TEXT = LOWER((name)::TEXT)))'''
|
||||
"""ALTER TABLE domains_dnsrecord ADD CONSTRAINT c_lowercase_name
|
||||
CHECK (((name)::TEXT = LOWER((name)::TEXT)))"""
|
||||
),
|
||||
migrations.RunSQL(
|
||||
'''CREATE INDEX recordorder ON domains_dnsrecord (domain_id,
|
||||
ordername text_pattern_ops)'''
|
||||
"""CREATE INDEX recordorder ON domains_dnsrecord (domain_id,
|
||||
ordername text_pattern_ops)"""
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DNSSupermaster',
|
||||
name="DNSSupermaster",
|
||||
fields=[
|
||||
('id', models.AutoField(
|
||||
verbose_name='ID', serialize=False,
|
||||
auto_created=True, primary_key=True)),
|
||||
('ip', models.GenericIPAddressField()),
|
||||
('nameserver', models.CharField(max_length=255)),
|
||||
('customer', models.ForeignKey(
|
||||
verbose_name='customer', to=settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
verbose_name="ID",
|
||||
serialize=False,
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
),
|
||||
),
|
||||
("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(
|
||||
name='DNSTSIGKey',
|
||||
name="DNSTSIGKey",
|
||||
fields=[
|
||||
('id', models.AutoField(
|
||||
verbose_name='ID', serialize=False,
|
||||
auto_created=True, primary_key=True)),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('algorithm', models.CharField(max_length=50)),
|
||||
('secret', models.CharField(max_length=255)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
verbose_name="ID",
|
||||
serialize=False,
|
||||
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(
|
||||
'''ALTER TABLE domains_dnstsigkey ADD CONSTRAINT c_lowercase_name
|
||||
CHECK (((name)::TEXT = LOWER((name)::TEXT)))'''
|
||||
"""ALTER TABLE domains_dnstsigkey ADD CONSTRAINT c_lowercase_name
|
||||
CHECK (((name)::TEXT = LOWER((name)::TEXT)))"""
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='hostingdomain',
|
||||
name='domain',
|
||||
model_name="hostingdomain",
|
||||
name="domain",
|
||||
field=models.CharField(
|
||||
unique=True, max_length=255, verbose_name='domain name'),
|
||||
unique=True, max_length=255, verbose_name="domain name"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='maildomain',
|
||||
name='domain',
|
||||
model_name="maildomain",
|
||||
name="domain",
|
||||
field=models.CharField(
|
||||
unique=True, max_length=255, verbose_name='domain name'),
|
||||
unique=True, max_length=255, verbose_name="domain name"
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='dnscryptokey',
|
||||
name='domain',
|
||||
field=models.ForeignKey(
|
||||
to='domains.DNSDomain', on_delete=models.CASCADE),
|
||||
model_name="dnscryptokey",
|
||||
name="domain",
|
||||
field=models.ForeignKey(to="domains.DNSDomain", on_delete=models.CASCADE),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='dnscomment',
|
||||
name='domain',
|
||||
field=models.ForeignKey(
|
||||
to='domains.DNSDomain', on_delete=models.CASCADE),
|
||||
model_name="dnscomment",
|
||||
name="domain",
|
||||
field=models.ForeignKey(to="domains.DNSDomain", on_delete=models.CASCADE),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='dnssupermaster',
|
||||
unique_together=set([('ip', 'nameserver')]),
|
||||
name="dnssupermaster",
|
||||
unique_together=set([("ip", "nameserver")]),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='dnstsigkey',
|
||||
unique_together=set([('name', 'algorithm')]),
|
||||
name="dnstsigkey",
|
||||
unique_together=set([("name", "algorithm")]),
|
||||
),
|
||||
migrations.AlterIndexTogether(
|
||||
name='dnsrecord',
|
||||
index_together=set([('name', 'recordtype')]),
|
||||
name="dnsrecord",
|
||||
index_together=set([("name", "recordtype")]),
|
||||
),
|
||||
migrations.AlterIndexTogether(
|
||||
name='dnscomment',
|
||||
index_together={('name', 'commenttype'), ('domain', 'modified_at')},
|
||||
name="dnscomment",
|
||||
index_together={("name", "commenttype"), ("domain", "modified_at")},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,44 +1,87 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('domains', '0003_auto_20151105_2133'),
|
||||
("domains", "0003_auto_20151105_2133"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='dnscomment',
|
||||
options={'verbose_name': 'DNS comment', 'verbose_name_plural': 'DNS comments'},
|
||||
name="dnscomment",
|
||||
options={
|
||||
"verbose_name": "DNS comment",
|
||||
"verbose_name_plural": "DNS comments",
|
||||
},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='dnscryptokey',
|
||||
options={'verbose_name': 'DNS crypto key', 'verbose_name_plural': 'DNS crypto keys'},
|
||||
name="dnscryptokey",
|
||||
options={
|
||||
"verbose_name": "DNS crypto key",
|
||||
"verbose_name_plural": "DNS crypto keys",
|
||||
},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='dnsdomainmetadata',
|
||||
options={'verbose_name': 'DNS domain metadata item', 'verbose_name_plural': 'DNS domain metadata items'},
|
||||
name="dnsdomainmetadata",
|
||||
options={
|
||||
"verbose_name": "DNS domain metadata item",
|
||||
"verbose_name_plural": "DNS domain metadata items",
|
||||
},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='dnssupermaster',
|
||||
options={'verbose_name': 'DNS supermaster', 'verbose_name_plural': 'DNS supermasters'},
|
||||
name="dnssupermaster",
|
||||
options={
|
||||
"verbose_name": "DNS supermaster",
|
||||
"verbose_name_plural": "DNS supermasters",
|
||||
},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='dnstsigkey',
|
||||
options={'verbose_name': 'DNS TSIG key', 'verbose_name_plural': 'DNS TSIG keys'},
|
||||
name="dnstsigkey",
|
||||
options={
|
||||
"verbose_name": "DNS TSIG key",
|
||||
"verbose_name_plural": "DNS TSIG keys",
|
||||
},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='dnsdomainmetadata',
|
||||
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')]),
|
||||
model_name="dnsdomainmetadata",
|
||||
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"),
|
||||
],
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='dnstsigkey',
|
||||
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')]),
|
||||
model_name="dnstsigkey",
|
||||
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"),
|
||||
],
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -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.
|
||||
|
||||
"""
|
||||
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.utils.encoding import python_2_unicode_compatible
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from django.db import models, transaction
|
||||
from django.utils.translation import gettext as _
|
||||
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):
|
||||
|
@ -55,16 +15,20 @@ class DomainBase(TimeStampedModel):
|
|||
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(
|
||||
settings.AUTH_USER_MODEL, verbose_name=_('customer'), blank=True,
|
||||
null=True, on_delete=models.CASCADE)
|
||||
settings.AUTH_USER_MODEL,
|
||||
verbose_name=_("customer"),
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class MailDomain(DomainBase):
|
||||
"""
|
||||
This is the model for mail domains. Mail domains are used to configure the
|
||||
|
@ -72,9 +36,10 @@ class MailDomain(DomainBase):
|
|||
domains.
|
||||
|
||||
"""
|
||||
class Meta:
|
||||
verbose_name = _('Mail domain')
|
||||
verbose_name_plural = _('Mail domains')
|
||||
|
||||
class Meta(DomainBase.Meta):
|
||||
verbose_name = _("Mail domain")
|
||||
verbose_name_plural = _("Mail domains")
|
||||
|
||||
def __str__(self):
|
||||
return self.domain
|
||||
|
@ -85,6 +50,7 @@ class MailDomain(DomainBase):
|
|||
|
||||
"""
|
||||
return self.mailaddress_set.all()
|
||||
|
||||
mailaddresses = property(get_mailaddresses)
|
||||
|
||||
|
||||
|
@ -93,339 +59,47 @@ class HostingDomainManager(models.Manager):
|
|||
Default Manager for :py:class:`HostingDomain`.
|
||||
|
||||
"""
|
||||
|
||||
@transaction.atomic
|
||||
def create_for_hosting_package(
|
||||
self, hosting_package, domain, commit, **kwargs
|
||||
):
|
||||
def create_for_hosting_package(self, hosting_package, domain, commit, **kwargs):
|
||||
from hostingpackages.models import CustomerHostingPackageDomain
|
||||
|
||||
hostingdomain = self.create(
|
||||
customer=hosting_package.customer, domain=domain, **kwargs)
|
||||
customer=hosting_package.customer, domain=domain, **kwargs
|
||||
)
|
||||
hostingdomain.maildomain = MailDomain.objects.create(
|
||||
customer=hosting_package.customer, domain=domain)
|
||||
customer=hosting_package.customer, domain=domain
|
||||
)
|
||||
custdomain = CustomerHostingPackageDomain.objects.create(
|
||||
hosting_package=hosting_package, domain=hostingdomain)
|
||||
hosting_package=hosting_package, domain=hostingdomain
|
||||
)
|
||||
if commit:
|
||||
hostingdomain.save()
|
||||
custdomain.save()
|
||||
return hostingdomain
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class HostingDomain(DomainBase):
|
||||
"""
|
||||
This is the model for hosting domains. A hosting domain is linked to a
|
||||
customer hosting account.
|
||||
|
||||
"""
|
||||
|
||||
maildomain = models.OneToOneField(
|
||||
MailDomain, verbose_name=_('mail domain'), blank=True, null=True,
|
||||
help_text=_('assigned mail domain for this domain'),
|
||||
MailDomain,
|
||||
verbose_name=_("mail domain"),
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text=_("assigned mail domain for this domain"),
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
|
||||
objects = HostingDomainManager()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Hosting domain')
|
||||
verbose_name_plural = _('Hosting domains')
|
||||
verbose_name = _("Hosting domain")
|
||||
verbose_name_plural = _("Hosting domains")
|
||||
|
||||
def __str__(self):
|
||||
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.test import TestCase
|
||||
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):
|
||||
|
|
|
@ -7,19 +7,9 @@ from unittest.mock import patch
|
|||
from django.test import TestCase
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from domains.models import (
|
||||
DNSComment,
|
||||
DNSCryptoKey,
|
||||
DNSDomain,
|
||||
DNSDomainMetadata,
|
||||
DNSRecord,
|
||||
DNSSupermaster,
|
||||
DNSTSIGKey,
|
||||
HostingDomain,
|
||||
MailDomain,
|
||||
)
|
||||
from hostingpackages.models import CustomerHostingPackage, HostingPackageTemplate
|
||||
from domains.models import HostingDomain, MailDomain
|
||||
|
||||
from hostingpackages.models import CustomerHostingPackage, HostingPackageTemplate
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
@ -77,49 +67,3 @@ class HostingDomainTest(TestCase):
|
|||
def test___str__(self):
|
||||
hostingdomain = HostingDomain(domain="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.
|
||||
|
||||
"""
|
||||
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
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^(?P<package>\d+)/create$', CreateHostingDomain.as_view(),
|
||||
name='create_hosting_domain'),
|
||||
re_path(
|
||||
r"^(?P<package>\d+)/create$",
|
||||
CreateHostingDomain.as_view(),
|
||||
name="create_hosting_domain",
|
||||
),
|
||||
]
|
||||
|
|
|
@ -2,20 +2,21 @@
|
|||
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.auth.mixins import PermissionRequiredMixin
|
||||
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 hostingpackages.models import CustomerHostingPackage
|
||||
|
||||
from .forms import CreateHostingDomainForm
|
||||
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
|
||||
hosting package.
|
||||
|
@ -23,6 +24,7 @@ class CreateHostingDomain(StaffuserRequiredMixin, CreateView):
|
|||
|
||||
model = HostingDomain
|
||||
raise_exception = True
|
||||
permission_required = 'domains.add_hostingdomain'
|
||||
template_name_suffix = "_create"
|
||||
form_class = CreateHostingDomainForm
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# import celery_app to initialize it
|
||||
from gnuviechadmin.celery import app as celery_app # NOQA
|
||||
|
||||
__version__ = '0.12.1'
|
||||
__version__ = "0.13.0"
|
||||
|
|
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
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE',
|
||||
'gnuviechadmin.settings.production')
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gnuviechadmin.settings")
|
||||
|
||||
|
||||
app = Celery('gnuviechadmin')
|
||||
app = Celery("gnuviechadmin")
|
||||
|
||||
|
||||
def get_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)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
This module provides context processor implementations for gnuviechadmin.
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
from __future__ import absolute_import
|
||||
|
||||
import logging
|
||||
|
||||
|
@ -22,38 +22,42 @@ def navigation(request):
|
|||
:rtype: dict
|
||||
|
||||
"""
|
||||
if request.is_ajax():
|
||||
if request.headers.get("x-requested-with") == "XMLHttpRequest":
|
||||
return {}
|
||||
context = {
|
||||
'webmail_url': settings.GVA_LINK_WEBMAIL,
|
||||
'phpmyadmin_url': settings.GVA_LINK_PHPMYADMIN,
|
||||
'phppgadmin_url': settings.GVA_LINK_PHPPGADMIN,
|
||||
'active_item': 'dashboard',
|
||||
"webmail_url": settings.GVA_LINK_WEBMAIL,
|
||||
"phpmyadmin_url": settings.GVA_LINK_PHPMYADMIN,
|
||||
"phppgadmin_url": settings.GVA_LINK_PHPPGADMIN,
|
||||
"active_item": "dashboard",
|
||||
}
|
||||
if request.resolver_match:
|
||||
viewfunc = request.resolver_match.func
|
||||
viewmodule = viewfunc.__module__
|
||||
if viewmodule == 'contact_form.views':
|
||||
context['active_item'] = 'contact'
|
||||
if viewmodule == "contact_form.views":
|
||||
context["active_item"] = "contact"
|
||||
elif viewmodule in (
|
||||
'hostingpackages.views', 'osusers.views', 'userdbs.views',
|
||||
'managemails.views', 'websites.views', 'domains.views',
|
||||
"hostingpackages.views",
|
||||
"osusers.views",
|
||||
"userdbs.views",
|
||||
"managemails.views",
|
||||
"websites.views",
|
||||
"domains.views",
|
||||
):
|
||||
context['active_item'] = 'hostingpackage'
|
||||
elif viewmodule in (
|
||||
'allauth.account.views', 'allauth.socialaccount.views'
|
||||
context["active_item"] = "hostingpackage"
|
||||
elif viewmodule in ("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'
|
||||
elif (
|
||||
viewmodule == 'django.contrib.flatpages.views' and
|
||||
request.path.endswith('/impressum/')
|
||||
):
|
||||
context['active_item'] = 'imprint'
|
||||
elif not viewmodule.startswith('django.contrib.admin'):
|
||||
context["active_item"] = "imprint"
|
||||
elif not viewmodule.startswith("django.contrib.admin"):
|
||||
_LOGGER.debug(
|
||||
'no special handling for view %s in module %s, fallback to '
|
||||
'default active menu item %s',
|
||||
viewfunc.__name__, viewmodule, context['active_item'])
|
||||
"no special handling for view %s in module %s, fallback to "
|
||||
"default active menu item %s",
|
||||
viewfunc.__name__,
|
||||
viewmodule,
|
||||
context["active_item"],
|
||||
)
|
||||
return context
|
||||
|
||||
|
||||
|
@ -64,6 +68,6 @@ def version_info(request):
|
|||
|
||||
"""
|
||||
context = {
|
||||
'gnuviechadmin_version': gvaversion,
|
||||
"gnuviechadmin_version": gvaversion,
|
||||
}
|
||||
return context
|
||||
|
|
|
@ -8,10 +8,8 @@ Common settings and globals.
|
|||
from os.path import abspath, basename, dirname, join, normpath
|
||||
|
||||
from django.contrib.messages import constants as messages
|
||||
|
||||
from gvacommon.settings_utils import get_env_variable
|
||||
|
||||
|
||||
# ######### PATH CONFIGURATION
|
||||
# Absolute filesystem path to the Django project directory:
|
||||
DJANGO_ROOT = dirname(dirname(abspath(__file__)))
|
||||
|
@ -24,10 +22,11 @@ SITE_NAME = basename(DJANGO_ROOT)
|
|||
|
||||
# ######### END PATH CONFIGURATION
|
||||
|
||||
GVA_ENVIRONMENT = get_env_variable("GVA_ENVIRONMENT", default="prod")
|
||||
|
||||
# ######### DEBUG CONFIGURATION
|
||||
# See: https://docs.djangoproject.com/en/dev/ref/settings/#debug
|
||||
DEBUG = False
|
||||
DEBUG = GVA_ENVIRONMENT == "local"
|
||||
# ######### END DEBUG CONFIGURATION
|
||||
|
||||
|
||||
|
@ -57,6 +56,8 @@ DATABASES = {
|
|||
"PORT": get_env_variable("GVA_PGSQL_PORT", int, default=5432),
|
||||
}
|
||||
}
|
||||
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
||||
# ######### END DATABASE CONFIGURATION
|
||||
|
||||
|
||||
|
@ -85,7 +86,6 @@ USE_TZ = True
|
|||
|
||||
LOCALE_PATHS = (normpath(join(SITE_ROOT, "gnuviechadmin", "locale")),)
|
||||
|
||||
|
||||
# ######### MEDIA CONFIGURATION
|
||||
# See: https://docs.djangoproject.com/en/dev/ref/settings/#media-root
|
||||
MEDIA_ROOT = normpath(join(SITE_ROOT, "media"))
|
||||
|
@ -179,7 +179,6 @@ AUTHENTICATION_BACKENDS = (
|
|||
"allauth.account.auth_backends.AuthenticationBackend",
|
||||
)
|
||||
|
||||
|
||||
# ######### URL CONFIGURATION
|
||||
# See: https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf
|
||||
ROOT_URLCONF = "%s.urls" % SITE_NAME
|
||||
|
@ -207,6 +206,10 @@ DJANGO_APPS = (
|
|||
# Flatpages for about page
|
||||
"django.contrib.flatpages",
|
||||
"crispy_forms",
|
||||
"crispy_bootstrap5",
|
||||
"impersonate",
|
||||
"rest_framework",
|
||||
"rest_framework.authtoken",
|
||||
)
|
||||
|
||||
ALLAUTH_APPS = (
|
||||
|
@ -215,7 +218,6 @@ ALLAUTH_APPS = (
|
|||
"allauth.socialaccount",
|
||||
"allauth.socialaccount.providers.google",
|
||||
"allauth.socialaccount.providers.linkedin_oauth2",
|
||||
"allauth.socialaccount.providers.twitter",
|
||||
)
|
||||
|
||||
# Apps specific for this project go here.
|
||||
|
@ -233,6 +235,8 @@ LOCAL_APPS = (
|
|||
"userdbs",
|
||||
"hostingpackages",
|
||||
"websites",
|
||||
"help",
|
||||
"invoices",
|
||||
"contact_form",
|
||||
)
|
||||
|
||||
|
@ -250,18 +254,38 @@ MESSAGE_TAGS = {
|
|||
|
||||
|
||||
# ######### ALLAUTH CONFIGURATION
|
||||
ACCOUNT_ADAPTER = "gnuviechadmin.auth.NoNewUsersAccountAdapter"
|
||||
ACCOUNT_EMAIL_REQUIRED = True
|
||||
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
|
||||
LOGIN_REDIRECT_URL = "/"
|
||||
SOCIALACCOUNT_AUTO_SIGNUP = False
|
||||
SOCIALACCOUNT_QUERY_EMAIL = True
|
||||
# ######### END ALLAUTH CONFIGURATION
|
||||
|
||||
|
||||
# ######### CRISPY FORMS CONFIGURATION
|
||||
CRISPY_TEMPLATE_PACK = "bootstrap3"
|
||||
CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
|
||||
CRISPY_TEMPLATE_PACK = "bootstrap5"
|
||||
# ######### 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
|
||||
# See: https://docs.djangoproject.com/en/dev/ref/settings/#logging
|
||||
# A sample logging configuration. The only tangible logging
|
||||
|
@ -281,20 +305,45 @@ LOGGING = {
|
|||
},
|
||||
"filters": {"require_debug_false": {"()": "django.utils.log.RequireDebugFalse"}},
|
||||
"handlers": {
|
||||
"console": {
|
||||
"class": "logging.StreamHandler",
|
||||
},
|
||||
"logfile": {
|
||||
"level": "INFO",
|
||||
"class": "logging.FileHandler",
|
||||
"filename": get_env_variable("GVA_LOG_FILE", default="gva.log"),
|
||||
"formatter": "verbose",
|
||||
},
|
||||
"mail_admins": {
|
||||
"level": "ERROR",
|
||||
"filters": ["require_debug_false"],
|
||||
"class": "django.utils.log.AdminEmailHandler",
|
||||
}
|
||||
},
|
||||
},
|
||||
"root": {
|
||||
"handlers": ["console"],
|
||||
"level": "WARNING",
|
||||
},
|
||||
"loggers": {
|
||||
"django.request": {
|
||||
"handlers": ["mail_admins"],
|
||||
"level": "ERROR",
|
||||
"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
|
||||
|
||||
|
||||
|
@ -351,8 +400,6 @@ GVA_LINK_PHPPGADMIN = get_env_variable(
|
|||
)
|
||||
# ######### END CUSTOM APP CONFIGURATION
|
||||
|
||||
GVA_ENVIRONMENT = get_env_variable("GVA_ENVIRONMENT", default="prod")
|
||||
|
||||
# ######### STATIC FILE CONFIGURATION
|
||||
# See: https://docs.djangoproject.com/en/dev/ref/settings/#static-root
|
||||
STATIC_ROOT = "/srv/gva/static/"
|
||||
|
@ -362,11 +409,22 @@ def show_debug_toolbar(request):
|
|||
return DEBUG and GVA_ENVIRONMENT == "local"
|
||||
|
||||
|
||||
if GVA_ENVIRONMENT == "local":
|
||||
# ######### DEBUG CONFIGURATION
|
||||
# See: https://docs.djangoproject.com/en/dev/ref/settings/#debug
|
||||
DEBUG = True
|
||||
# ######### TOOLBAR CONFIGURATION
|
||||
# See: http://django-debug-toolbar.readthedocs.org/en/latest/installation.html#explicit-setup # noqa
|
||||
INSTALLED_APPS += ("debug_toolbar",)
|
||||
|
||||
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
|
||||
TEMPLATES[0]["OPTIONS"]["debug"] = DEBUG
|
||||
# ######### END DEBUG CONFIGURATION
|
||||
|
@ -381,12 +439,6 @@ if GVA_ENVIRONMENT == "local":
|
|||
CACHES = {"default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}}
|
||||
# ######### 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(
|
||||
{
|
||||
"console": {
|
||||
|
@ -400,32 +452,10 @@ if GVA_ENVIRONMENT == "local":
|
|||
dict(
|
||||
[
|
||||
(key, {"handlers": ["console"], "level": "DEBUG", "propagate": True})
|
||||
for key in [
|
||||
"dashboard",
|
||||
"domains",
|
||||
"fileservertasks",
|
||||
"gvacommon",
|
||||
"gvawebcore",
|
||||
"hostingpackages",
|
||||
"ldaptasks",
|
||||
"managemails",
|
||||
"mysqltasks",
|
||||
"osusers",
|
||||
"pgsqltasks",
|
||||
"taskresults",
|
||||
"userdbs",
|
||||
"websites",
|
||||
]
|
||||
]
|
||||
for key in LOCAL_APPS
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
DEBUG_TOOLBAR_PATCH_SETTINGS = False
|
||||
DEBUG_TOOLBAR_CONFIG = {
|
||||
"SHOW_TOOLBAR_CALLBACK": "gnuviechadmin.settings.show_debug_toolbar"
|
||||
}
|
||||
|
||||
# ######### END TOOLBAR CONFIGURATION
|
||||
elif GVA_ENVIRONMENT == "test":
|
||||
ALLOWED_HOSTS = ["localhost"]
|
||||
PASSWORD_HASHERS = ("django.contrib.auth.hashers.MD5PasswordHasher",)
|
||||
|
@ -442,25 +472,15 @@ elif GVA_ENVIRONMENT == "test":
|
|||
dict(
|
||||
[
|
||||
(key, {"handlers": ["console"], "level": "ERROR", "propagate": True})
|
||||
for key in [
|
||||
"dashboard",
|
||||
"domains",
|
||||
"fileservertasks",
|
||||
"gvacommon",
|
||||
"gvawebcore",
|
||||
"hostingpackages",
|
||||
"ldaptasks",
|
||||
"managemails",
|
||||
"mysqltasks",
|
||||
"osusers",
|
||||
"pgsqltasks",
|
||||
"taskresults",
|
||||
"userdbs",
|
||||
"websites",
|
||||
]
|
||||
for key in LOCAL_APPS
|
||||
]
|
||||
)
|
||||
)
|
||||
LOGGING["loggers"]["django"] = {
|
||||
"handlers": ["console"],
|
||||
"level": "CRITICAL",
|
||||
"propagate": True,
|
||||
}
|
||||
BROKER_URL = BROKER_URL + "_test"
|
||||
CELERY_RESULT_PERSISTENT = False
|
||||
else:
|
||||
|
|
|
@ -18,13 +18,16 @@ from gnuviechadmin.context_processors import navigation
|
|||
|
||||
User = get_user_model()
|
||||
|
||||
TEST_USER = "test"
|
||||
TEST_PASSWORD = "secret"
|
||||
|
||||
|
||||
class NavigationContextProcessorTest(TestCase):
|
||||
|
||||
EXPECTED_ITEMS = ("webmail_url", "phpmyadmin_url", "phppgadmin_url", "active_item")
|
||||
|
||||
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:
|
||||
self.assertNotIn(item, response.context)
|
||||
|
||||
|
@ -34,6 +37,12 @@ class NavigationContextProcessorTest(TestCase):
|
|||
self.assertEqual(context["phppgadmin_url"], settings.GVA_LINK_PHPPGADMIN)
|
||||
|
||||
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("/")
|
||||
for item in self.EXPECTED_ITEMS:
|
||||
self.assertIn(item, response.context)
|
||||
|
@ -47,15 +56,6 @@ class NavigationContextProcessorTest(TestCase):
|
|||
self._check_static_urls(response.context)
|
||||
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):
|
||||
request = HttpRequest()
|
||||
request.resolver_match = MagicMock()
|
||||
|
@ -106,6 +106,6 @@ class NavigationContextProcessorTest(TestCase):
|
|||
|
||||
class VersionInfoContextProcessorTest(TestCase):
|
||||
def test_version_info_in_context(self):
|
||||
response = self.client.get("/")
|
||||
response = self.client.get("/accounts/login/")
|
||||
self.assertIn("gnuviechadmin_version", response.context)
|
||||
self.assertEqual(response.context["gnuviechadmin_version"], gvaversion)
|
||||
|
|
|
@ -1,36 +1,49 @@
|
|||
from __future__ import absolute_import
|
||||
|
||||
from django.conf.urls import include, url
|
||||
from django.conf import settings
|
||||
|
||||
import debug_toolbar
|
||||
from django.conf.urls import include
|
||||
from django.contrib import admin
|
||||
from django.contrib.flatpages import views
|
||||
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()
|
||||
|
||||
urlpatterns = [
|
||||
url(r'', include('dashboard.urls')),
|
||||
url(r'^accounts/', include('allauth.urls')),
|
||||
url(r'^database/', include('userdbs.urls')),
|
||||
url(r'^domains/', include('domains.urls')),
|
||||
url(r'^hosting/', include('hostingpackages.urls')),
|
||||
url(r'^website/', include('websites.urls')),
|
||||
url(r'^mail/', include('managemails.urls')),
|
||||
url(r'^osuser/', include('osusers.urls')),
|
||||
url(r'^admin/', admin.site.urls),
|
||||
url(r'^contact/', include('contact_form.urls')),
|
||||
url(r'^impressum/$', views.flatpage, {
|
||||
'url': '/impressum/'
|
||||
}, name='imprint'),
|
||||
path("", include("dashboard.urls")),
|
||||
path("api/users/", help_views.ListHelpUserAPIView.as_view()),
|
||||
path(
|
||||
"api/users/<int:pk>/",
|
||||
help_views.HelpUserAPIView.as_view(),
|
||||
name="helpuser-detail",
|
||||
),
|
||||
path("api/invoices/", invoice_views.ListInvoiceAPIView.as_view()),
|
||||
path(
|
||||
"api/invoices/<invoice_number>/",
|
||||
invoice_views.InvoiceAPIView.as_view(),
|
||||
name="invoice-detail",
|
||||
),
|
||||
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"),
|
||||
]
|
||||
|
||||
# Uncomment the next line to serve media files in dev.
|
||||
# urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
||||
if settings.DEBUG: # pragma: no cover
|
||||
import debug_toolbar
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^__debug__/', include(debug_toolbar.urls)),
|
||||
] + staticfiles_urlpatterns() + urlpatterns
|
||||
urlpatterns += staticfiles_urlpatterns()
|
||||
urlpatterns += [
|
||||
path("__debug__/", include(debug_toolbar.urls)),
|
||||
]
|
||||
|
|
|
@ -3,11 +3,10 @@ This module defines form classes that can be extended by other gnuviechadmin
|
|||
apps' forms.
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
from __future__ import absolute_import
|
||||
|
||||
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")
|
||||
"""
|
||||
|
@ -21,11 +20,14 @@ class PasswordModelFormMixin(forms.Form):
|
|||
whether both fields contain the same string.
|
||||
|
||||
"""
|
||||
|
||||
password1 = forms.CharField(
|
||||
label=_('Password'), widget=forms.PasswordInput,
|
||||
label=_("Password"),
|
||||
widget=forms.PasswordInput,
|
||||
)
|
||||
password2 = forms.CharField(
|
||||
label=_('Password (again)'), widget=forms.PasswordInput,
|
||||
label=_("Password (again)"),
|
||||
widget=forms.PasswordInput,
|
||||
)
|
||||
|
||||
def clean_password2(self):
|
||||
|
@ -36,8 +38,8 @@ class PasswordModelFormMixin(forms.Form):
|
|||
:rtype: str or None
|
||||
|
||||
"""
|
||||
password1 = self.cleaned_data.get('password1')
|
||||
password2 = self.cleaned_data.get('password2')
|
||||
password1 = self.cleaned_data.get("password1")
|
||||
password2 = self.cleaned_data.get("password2")
|
||||
if password1 and password2 and password1 != password2:
|
||||
raise forms.ValidationError(PASSWORD_MISMATCH_ERROR)
|
||||
return password2
|
||||
|
|
|
@ -7,8 +7,8 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: gvawebcore\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-01-29 11:04+0100\n"
|
||||
"PO-Revision-Date: 2015-01-25 11:49+0100\n"
|
||||
"POT-Creation-Date: 2023-04-16 22:07+0200\n"
|
||||
"PO-Revision-Date: 2023-04-16 18:21+0200\n"
|
||||
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
|
||||
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
|
||||
"Language: de\n"
|
||||
|
@ -16,17 +16,17 @@ msgstr ""
|
|||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\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"
|
||||
|
||||
#: gvawebcore/forms.py:12
|
||||
#: gvawebcore/forms.py:11
|
||||
msgid "Passwords don't match"
|
||||
msgstr "Passwörter stimmen nicht überein"
|
||||
|
||||
#: gvawebcore/forms.py:25
|
||||
msgid "Password"
|
||||
msgstr "Passwort: "
|
||||
msgstr "Passwort"
|
||||
|
||||
#: gvawebcore/forms.py:28
|
||||
#: gvawebcore/forms.py:29
|
||||
msgid "Password (again)"
|
||||
msgstr "Passwortwiederholung"
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
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 hostingpackages.models import CustomerHostingPackage
|
||||
|
||||
|
||||
|
@ -14,7 +15,8 @@ class HostingPackageAndCustomerMixin(object):
|
|||
keyword argument 'package'.
|
||||
|
||||
"""
|
||||
hosting_package_kwarg = 'package'
|
||||
|
||||
hosting_package_kwarg = "package"
|
||||
"""Keyword argument used to find the hosting package in the URL."""
|
||||
|
||||
hostingpackage = None
|
||||
|
@ -22,8 +24,8 @@ class HostingPackageAndCustomerMixin(object):
|
|||
def get_hosting_package(self):
|
||||
if self.hostingpackage is None:
|
||||
self.hostingpackage = get_object_or_404(
|
||||
CustomerHostingPackage,
|
||||
pk=int(self.kwargs[self.hosting_package_kwarg]))
|
||||
CustomerHostingPackage, pk=int(self.kwargs[self.hosting_package_kwarg])
|
||||
)
|
||||
return self.hostingpackage
|
||||
|
||||
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 ugettext_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 ugettext_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.
|
||||
|
||||
"""
|
||||
default_app_config = 'hostingpackages.apps.HostingPackagesAppConfig'
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
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.contrib import admin
|
||||
|
@ -25,9 +25,10 @@ class CustomerHostingPackageCreateForm(forms.ModelForm):
|
|||
This is the form class for creating new customer hosting packages.
|
||||
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
model = CustomerHostingPackage
|
||||
fields = ['customer', 'template', 'name']
|
||||
fields = ["customer", "template", "name"]
|
||||
|
||||
def save(self, **kwargs):
|
||||
"""
|
||||
|
@ -39,10 +40,11 @@ class CustomerHostingPackageCreateForm(forms.ModelForm):
|
|||
|
||||
"""
|
||||
hostinpackages = CustomerHostingPackage.objects.create_from_template(
|
||||
customer=self.cleaned_data['customer'],
|
||||
template=self.cleaned_data['template'],
|
||||
name=self.cleaned_data['name'],
|
||||
**kwargs)
|
||||
customer=self.cleaned_data["customer"],
|
||||
template=self.cleaned_data["template"],
|
||||
name=self.cleaned_data["name"],
|
||||
**kwargs
|
||||
)
|
||||
return hostinpackages
|
||||
|
||||
def save_m2m(self):
|
||||
|
@ -55,6 +57,7 @@ class CustomerDiskSpaceOptionInline(admin.TabularInline):
|
|||
space options.
|
||||
|
||||
"""
|
||||
|
||||
model = CustomerDiskSpaceOption
|
||||
extra = 0
|
||||
|
||||
|
@ -65,6 +68,7 @@ class CustomerMailboxOptionInline(admin.TabularInline):
|
|||
mailbox options.
|
||||
|
||||
"""
|
||||
|
||||
model = CustomerMailboxOption
|
||||
extra = 0
|
||||
|
||||
|
@ -75,6 +79,7 @@ class CustomerUserDatabaseOptionInline(admin.TabularInline):
|
|||
database options.
|
||||
|
||||
"""
|
||||
|
||||
model = CustomerUserDatabaseOption
|
||||
extra = 0
|
||||
|
||||
|
@ -85,6 +90,7 @@ class CustomerHostingPackageDomainInline(admin.TabularInline):
|
|||
hosting packages.
|
||||
|
||||
"""
|
||||
|
||||
model = CustomerHostingPackageDomain
|
||||
extra = 0
|
||||
|
||||
|
@ -95,12 +101,9 @@ class CustomerHostingPackageAdmin(admin.ModelAdmin):
|
|||
:py:class:`CustomerHostingPackage`.
|
||||
|
||||
"""
|
||||
|
||||
add_form = CustomerHostingPackageCreateForm
|
||||
add_fieldsets = (
|
||||
(None, {
|
||||
'fields': ('customer', 'template', 'name')
|
||||
}),
|
||||
)
|
||||
add_fieldsets = ((None, {"fields": ("customer", "template", "name")}),)
|
||||
|
||||
inlines = [
|
||||
CustomerDiskSpaceOptionInline,
|
||||
|
@ -108,7 +111,7 @@ class CustomerHostingPackageAdmin(admin.ModelAdmin):
|
|||
CustomerUserDatabaseOptionInline,
|
||||
CustomerHostingPackageDomainInline,
|
||||
]
|
||||
list_display = ['name', 'customer', 'osuser']
|
||||
list_display = ["name", "customer", "osuser"]
|
||||
|
||||
def get_form(self, request, obj=None, **kwargs):
|
||||
"""
|
||||
|
@ -125,13 +128,16 @@ class CustomerHostingPackageAdmin(admin.ModelAdmin):
|
|||
"""
|
||||
defaults = {}
|
||||
if obj is None:
|
||||
defaults.update({
|
||||
'form': self.add_form,
|
||||
'fields': admin.options.flatten_fieldsets(self.add_fieldsets),
|
||||
})
|
||||
defaults.update(
|
||||
{
|
||||
"form": self.add_form,
|
||||
"fields": admin.options.flatten_fieldsets(self.add_fieldsets),
|
||||
}
|
||||
)
|
||||
defaults.update(kwargs)
|
||||
return super(CustomerHostingPackageAdmin, self).get_form(
|
||||
request, obj, **defaults)
|
||||
request, obj, **defaults
|
||||
)
|
||||
|
||||
def get_readonly_fields(self, request, obj=None):
|
||||
"""
|
||||
|
@ -147,7 +153,7 @@ class CustomerHostingPackageAdmin(admin.ModelAdmin):
|
|||
|
||||
"""
|
||||
if obj:
|
||||
return ['customer', 'template']
|
||||
return ["customer", "template"]
|
||||
return []
|
||||
|
||||
|
||||
|
|
|
@ -3,9 +3,8 @@ This module contains the :py:class:`django.apps.AppConfig` instance for the
|
|||
:py:mod:`hostingpackages` app.
|
||||
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
from django.apps import AppConfig
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class HostingPackagesAppConfig(AppConfig):
|
||||
|
@ -13,5 +12,6 @@ class HostingPackagesAppConfig(AppConfig):
|
|||
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.
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django import forms
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
from __future__ import absolute_import
|
||||
|
||||
from crispy_forms.helper import FormHelper
|
||||
from crispy_forms.layout import (
|
||||
Layout,
|
||||
Submit,
|
||||
)
|
||||
from crispy_forms.layout import Layout, Submit
|
||||
from django import forms
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from .models import (
|
||||
CustomerDiskSpaceOption,
|
||||
|
@ -28,25 +24,24 @@ class CreateCustomerHostingPackageForm(forms.ModelForm):
|
|||
a preselected customer.
|
||||
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
model = CustomerHostingPackage
|
||||
fields = ['template', 'name', 'description']
|
||||
fields = ["template", "name", "description"]
|
||||
|
||||
def __init__(self, instance, *args, **kwargs):
|
||||
username = kwargs.pop('user')
|
||||
super(CreateCustomerHostingPackageForm, self).__init__(
|
||||
*args, **kwargs
|
||||
)
|
||||
self.fields['description'].widget.attrs['rows'] = 2
|
||||
username = kwargs.pop("user")
|
||||
super(CreateCustomerHostingPackageForm, self).__init__(*args, **kwargs)
|
||||
self.fields["description"].widget.attrs["rows"] = 2
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_action = reverse(
|
||||
'create_customer_hosting_package', kwargs={'user': username}
|
||||
"create_customer_hosting_package", kwargs={"user": username}
|
||||
)
|
||||
self.helper.layout = Layout(
|
||||
'template',
|
||||
'name',
|
||||
'description',
|
||||
Submit('submit', _('Add Hosting Package')),
|
||||
"template",
|
||||
"name",
|
||||
"description",
|
||||
Submit("submit", _("Add Hosting Package")),
|
||||
)
|
||||
|
||||
|
||||
|
@ -55,44 +50,44 @@ class CreateHostingPackageForm(forms.ModelForm):
|
|||
This form class is used for creating new customer hosting packages.
|
||||
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
model = CustomerHostingPackage
|
||||
fields = ['customer', 'template', 'name', 'description']
|
||||
fields = ["customer", "template", "name", "description"]
|
||||
|
||||
def __init__(self, instance, *args, **kwargs):
|
||||
super(CreateHostingPackageForm, self).__init__(
|
||||
*args, **kwargs
|
||||
)
|
||||
self.fields['description'].widget.attrs['rows'] = 2
|
||||
super(CreateHostingPackageForm, self).__init__(*args, **kwargs)
|
||||
self.fields["description"].widget.attrs["rows"] = 2
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_action = reverse('create_hosting_package')
|
||||
self.helper.form_action = reverse("create_hosting_package")
|
||||
self.helper.layout = Layout(
|
||||
'customer',
|
||||
'template',
|
||||
'name',
|
||||
'description',
|
||||
Submit('submit', _('Add Hosting Package')),
|
||||
"customer",
|
||||
"template",
|
||||
"name",
|
||||
"description",
|
||||
Submit("submit", _("Add Hosting Package")),
|
||||
)
|
||||
|
||||
|
||||
class AddDiskspaceOptionForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = CustomerDiskSpaceOption
|
||||
fields = ['diskspace', 'diskspace_unit']
|
||||
fields = ["diskspace", "diskspace_unit"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.hostingpackage = kwargs.pop('hostingpackage')
|
||||
self.option_template = kwargs.pop('option_template')
|
||||
self.hostingpackage = kwargs.pop("hostingpackage")
|
||||
self.option_template = kwargs.pop("option_template")
|
||||
super(AddDiskspaceOptionForm, self).__init__(*args, **kwargs)
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_action = reverse(
|
||||
'add_hosting_option',
|
||||
"add_hosting_option",
|
||||
kwargs={
|
||||
'package': self.hostingpackage.id,
|
||||
'type': 'diskspace',
|
||||
'optionid': self.option_template.id,
|
||||
})
|
||||
self.helper.add_input(Submit('submit', _('Add disk space option')))
|
||||
"package": self.hostingpackage.id,
|
||||
"type": "diskspace",
|
||||
"optionid": self.option_template.id,
|
||||
},
|
||||
)
|
||||
self.helper.add_input(Submit("submit", _("Add disk space option")))
|
||||
|
||||
def save(self, commit=True):
|
||||
self.instance.hosting_package = self.hostingpackage
|
||||
|
@ -103,21 +98,22 @@ class AddDiskspaceOptionForm(forms.ModelForm):
|
|||
class AddMailboxOptionForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = CustomerMailboxOption
|
||||
fields = ['number']
|
||||
fields = ["number"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.hostingpackage = kwargs.pop('hostingpackage')
|
||||
self.option_template = kwargs.pop('option_template')
|
||||
self.hostingpackage = kwargs.pop("hostingpackage")
|
||||
self.option_template = kwargs.pop("option_template")
|
||||
super(AddMailboxOptionForm, self).__init__(*args, **kwargs)
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_action = reverse(
|
||||
'add_hosting_option',
|
||||
"add_hosting_option",
|
||||
kwargs={
|
||||
'package': self.hostingpackage.id,
|
||||
'type': 'mailboxes',
|
||||
'optionid': self.option_template.id,
|
||||
})
|
||||
self.helper.add_input(Submit('submit', _('Add mailbox option')))
|
||||
"package": self.hostingpackage.id,
|
||||
"type": "mailboxes",
|
||||
"optionid": self.option_template.id,
|
||||
},
|
||||
)
|
||||
self.helper.add_input(Submit("submit", _("Add mailbox option")))
|
||||
|
||||
def save(self, commit=True):
|
||||
self.instance.hosting_package = self.hostingpackage
|
||||
|
@ -128,21 +124,22 @@ class AddMailboxOptionForm(forms.ModelForm):
|
|||
class AddUserDatabaseOptionForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = CustomerUserDatabaseOption
|
||||
fields = ['number']
|
||||
fields = ["number"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.hostingpackage = kwargs.pop('hostingpackage')
|
||||
self.option_template = kwargs.pop('option_template')
|
||||
self.hostingpackage = kwargs.pop("hostingpackage")
|
||||
self.option_template = kwargs.pop("option_template")
|
||||
super(AddUserDatabaseOptionForm, self).__init__(*args, **kwargs)
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_action = reverse(
|
||||
'add_hosting_option',
|
||||
"add_hosting_option",
|
||||
kwargs={
|
||||
'package': self.hostingpackage.id,
|
||||
'type': 'databases',
|
||||
'optionid': self.option_template.id,
|
||||
})
|
||||
self.helper.add_input(Submit('submit', _('Add database option')))
|
||||
"package": self.hostingpackage.id,
|
||||
"type": "databases",
|
||||
"optionid": self.option_template.id,
|
||||
},
|
||||
)
|
||||
self.helper.add_input(Submit("submit", _("Add database option")))
|
||||
|
||||
def save(self, commit=True):
|
||||
self.instance.hosting_package = self.hostingpackage
|
||||
|
|
|
@ -7,8 +7,8 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: gnuviechadmin hostingpackages\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-01-29 11:04+0100\n"
|
||||
"PO-Revision-Date: 2015-01-25 15:49+0100\n"
|
||||
"POT-Creation-Date: 2023-04-22 13:14+0200\n"
|
||||
"PO-Revision-Date: 2023-04-22 13:15+0200\n"
|
||||
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
|
||||
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
|
||||
"Language: de\n"
|
||||
|
@ -16,221 +16,561 @@ msgstr ""
|
|||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\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"
|
||||
|
||||
#: hostingpackages/apps.py:17
|
||||
msgid "Hosting Packages and Options"
|
||||
msgstr "Hostingpakete und -Optionen"
|
||||
|
||||
#: hostingpackages/forms.py:49 hostingpackages/forms.py:74
|
||||
#: hostingpackages/forms.py:44 hostingpackages/forms.py:68
|
||||
msgid "Add Hosting Package"
|
||||
msgstr "Hostingpaket anlegen"
|
||||
|
||||
#: hostingpackages/forms.py:95
|
||||
#: hostingpackages/forms.py:90
|
||||
msgid "Add disk space option"
|
||||
msgstr "Speicherplatzoption hinzufügen"
|
||||
|
||||
#: hostingpackages/forms.py:120
|
||||
#: hostingpackages/forms.py:116
|
||||
msgid "Add mailbox option"
|
||||
msgstr "Postfachoption hinzufügen"
|
||||
|
||||
#: hostingpackages/forms.py:145
|
||||
#: hostingpackages/forms.py:142
|
||||
msgid "Add database option"
|
||||
msgstr "Datenbankoption hinzufügen"
|
||||
|
||||
#: hostingpackages/models.py:31
|
||||
#: hostingpackages/models.py:21
|
||||
msgid "MiB"
|
||||
msgstr "MiB"
|
||||
|
||||
#: hostingpackages/models.py:32
|
||||
#: hostingpackages/models.py:21
|
||||
msgid "GiB"
|
||||
msgstr "GiB"
|
||||
|
||||
#: hostingpackages/models.py:33
|
||||
#: hostingpackages/models.py:21
|
||||
msgid "TiB"
|
||||
msgstr "TiB"
|
||||
|
||||
#: hostingpackages/models.py:45
|
||||
#: hostingpackages/models.py:27
|
||||
msgid "description"
|
||||
msgstr "Beschreibung"
|
||||
|
||||
#: hostingpackages/models.py:46
|
||||
#: hostingpackages/models.py:28
|
||||
msgid "mailbox count"
|
||||
msgstr "Anzahl Postfächer"
|
||||
|
||||
#: hostingpackages/models.py:48 hostingpackages/models.py:76
|
||||
#: hostingpackages/models.py:30 hostingpackages/models.py:59
|
||||
msgid "disk space"
|
||||
msgstr "Speicherplatz"
|
||||
|
||||
#: hostingpackages/models.py:48
|
||||
#: hostingpackages/models.py:30
|
||||
msgid "disk space for the hosting package"
|
||||
msgstr "Speicherplatz für das Hostingpaket"
|
||||
|
||||
#: hostingpackages/models.py:50 hostingpackages/models.py:78
|
||||
#: hostingpackages/models.py:33 hostingpackages/models.py:61
|
||||
msgid "unit of disk space"
|
||||
msgstr "Maßeinheit für den Speicherplatz"
|
||||
|
||||
#: hostingpackages/models.py:60 hostingpackages/models.py:213
|
||||
#: hostingpackages/models.py:44 hostingpackages/models.py:192
|
||||
msgid "name"
|
||||
msgstr "Name"
|
||||
|
||||
#: hostingpackages/models.py:63
|
||||
#: hostingpackages/models.py:47
|
||||
msgid "Hosting package"
|
||||
msgstr "Hostingpaket"
|
||||
|
||||
#: hostingpackages/models.py:64
|
||||
#: hostingpackages/models.py:48
|
||||
msgid "Hosting packages"
|
||||
msgstr "Hostingpakete"
|
||||
|
||||
#: hostingpackages/models.py:83
|
||||
#: hostingpackages/models.py:67
|
||||
msgid "Disk space option"
|
||||
msgstr "Speicherplatzoption"
|
||||
|
||||
#: hostingpackages/models.py:84
|
||||
#: hostingpackages/models.py:68
|
||||
msgid "Disk space options"
|
||||
msgstr "Speicherplatzoptionen"
|
||||
|
||||
#: hostingpackages/models.py:87
|
||||
#: hostingpackages/models.py:71
|
||||
#, python-brace-format
|
||||
msgid "Additional disk space {space} {unit}"
|
||||
msgstr "Zusätzlicher Speicherplatz {space} {unit}"
|
||||
|
||||
#: hostingpackages/models.py:104
|
||||
#: hostingpackages/models.py:88
|
||||
msgid "number of databases"
|
||||
msgstr "Anzahl von Datenbanken"
|
||||
|
||||
#: hostingpackages/models.py:106
|
||||
#: hostingpackages/models.py:89
|
||||
msgid "database type"
|
||||
msgstr "Datenbanktyp"
|
||||
|
||||
#: hostingpackages/models.py:111
|
||||
#: hostingpackages/models.py:94
|
||||
msgid "Database option"
|
||||
msgstr "Datenbankoption"
|
||||
|
||||
#: hostingpackages/models.py:112
|
||||
#: hostingpackages/models.py:95
|
||||
msgid "Database options"
|
||||
msgstr "Datenbankoptionen"
|
||||
|
||||
#: hostingpackages/models.py:116
|
||||
#: hostingpackages/models.py:99
|
||||
#, python-brace-format
|
||||
msgid "{type} database"
|
||||
msgid_plural "{count} {type} databases"
|
||||
msgstr[0] "{type}-Datenbank"
|
||||
msgstr[1] "{count} {type}-Datenbanken"
|
||||
|
||||
#: hostingpackages/models.py:141
|
||||
#: hostingpackages/models.py:120
|
||||
msgid "number of mailboxes"
|
||||
msgstr "Anzahl von Postfächern"
|
||||
|
||||
#: hostingpackages/models.py:146
|
||||
#: hostingpackages/models.py:125
|
||||
msgid "Mailbox option"
|
||||
msgstr "Postfachoption"
|
||||
|
||||
#: hostingpackages/models.py:147
|
||||
#: hostingpackages/models.py:126
|
||||
msgid "Mailbox options"
|
||||
msgstr "Postfachoptionen"
|
||||
|
||||
#: hostingpackages/models.py:151
|
||||
#: hostingpackages/models.py:130
|
||||
#, python-brace-format
|
||||
msgid "{count} additional mailbox"
|
||||
msgid_plural "{count} additional mailboxes"
|
||||
msgstr[0] "{count} zusätzliches Postfach"
|
||||
msgstr[1] "{count} zusätzliche Postfächer"
|
||||
|
||||
#: hostingpackages/models.py:206
|
||||
#: hostingpackages/models.py:182
|
||||
msgid "customer"
|
||||
msgstr "Kunde"
|
||||
|
||||
#: hostingpackages/models.py:208
|
||||
#: hostingpackages/models.py:186
|
||||
msgid "hosting package template"
|
||||
msgstr "Hostingpaketvorlage"
|
||||
|
||||
#: hostingpackages/models.py:210
|
||||
#: hostingpackages/models.py:188
|
||||
msgid "The hosting package template that this hosting package is based on"
|
||||
msgstr "Die Hostingpaketvorlage, auf der dieses Hostingpaket aufgebaut ist"
|
||||
|
||||
#: hostingpackages/models.py:215
|
||||
#: hostingpackages/models.py:195
|
||||
msgid "Operating system user"
|
||||
msgstr "Betriebssystemnutzer"
|
||||
|
||||
#: hostingpackages/models.py:222
|
||||
#: hostingpackages/models.py:205
|
||||
msgid "customer hosting package"
|
||||
msgstr "Kundenhostingpaket"
|
||||
|
||||
#: hostingpackages/models.py:223
|
||||
#: hostingpackages/models.py:206
|
||||
msgid "customer hosting packages"
|
||||
msgstr "Kundenhostingpakete"
|
||||
|
||||
#: hostingpackages/models.py:226
|
||||
#: hostingpackages/models.py:209
|
||||
#, python-brace-format
|
||||
msgid "{name} for {customer}"
|
||||
msgstr "{name} für {customer}"
|
||||
|
||||
#: hostingpackages/models.py:404 hostingpackages/models.py:426
|
||||
#: hostingpackages/models.py:388 hostingpackages/models.py:415
|
||||
msgid "hosting package"
|
||||
msgstr "Hostingpaket"
|
||||
|
||||
#: hostingpackages/models.py:407
|
||||
#: hostingpackages/models.py:393
|
||||
msgid "hosting domain"
|
||||
msgstr "Hostingdomain"
|
||||
|
||||
#: hostingpackages/models.py:429
|
||||
#: hostingpackages/models.py:420
|
||||
msgid "customer hosting option"
|
||||
msgstr "kundenspezifische Hostingoption"
|
||||
|
||||
#: hostingpackages/models.py:430
|
||||
#: hostingpackages/models.py:421
|
||||
msgid "customer hosting options"
|
||||
msgstr "kundenspezifische Hostingoptionen"
|
||||
|
||||
#: hostingpackages/models.py:442
|
||||
#: hostingpackages/models.py:433
|
||||
msgid "disk space option template"
|
||||
msgstr "Speicherplatzoptionsvorlage"
|
||||
|
||||
#: hostingpackages/models.py:444
|
||||
#: hostingpackages/models.py:435
|
||||
msgid "The disk space option template that this disk space option is based on"
|
||||
msgstr ""
|
||||
"Die Speicherplatzoptionsvorlage auf der diese Speicherplatzoption aufgebaut "
|
||||
"ist"
|
||||
|
||||
#: hostingpackages/models.py:458
|
||||
#: hostingpackages/models.py:450
|
||||
msgid "user database option template"
|
||||
msgstr "Nutzerdatenbankoptionsvorlage"
|
||||
|
||||
#: hostingpackages/models.py:460
|
||||
#: hostingpackages/models.py:452
|
||||
msgid "The user database option template that this database option is based on"
|
||||
msgstr ""
|
||||
"Die Nutzerdatenbankoptionsvorlage auf der diese Datenbankoption aufgebaut ist"
|
||||
|
||||
#: hostingpackages/models.py:474
|
||||
#: hostingpackages/models.py:467
|
||||
msgid "mailbox option template"
|
||||
msgstr "Postfachoptionsvorlage"
|
||||
|
||||
#: hostingpackages/models.py:476
|
||||
#: hostingpackages/models.py:468
|
||||
msgid "The mailbox option template that this mailbox option is based on"
|
||||
msgstr "Die Postfachoptionsvorlage auf der diese Postfachoption aufgebaut ist"
|
||||
|
||||
#: hostingpackages/views.py:60 hostingpackages/views.py:94
|
||||
#: 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
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:26
|
||||
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_list.html:27
|
||||
msgid "Setup date"
|
||||
msgstr "Einrichtungsdatum"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:31
|
||||
msgid "No hosting packages have been setup yet."
|
||||
msgstr "Es wurden noch keine Hostingpakete eingerichtet."
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:34
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:46
|
||||
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:40
|
||||
#: hostingpackages/views.py:199
|
||||
msgid "Disk space"
|
||||
msgstr "Speicherplatz"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:43
|
||||
#, 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_detail.html:47
|
||||
#, python-format
|
||||
msgid ""
|
||||
"The package contributes %(humanbytes)s (%(packagespace)s bytes) the "
|
||||
"difference comes from disk space options"
|
||||
msgstr ""
|
||||
"Das Paket trägt %(humanbytes)s (%(packagespace)s Bytes) zur Gesamtgröße bei, "
|
||||
"der Unterschied ergibt sich aus Speicherplatzoptionen"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:52
|
||||
#: hostingpackages/views.py:206
|
||||
msgid "Mailboxes"
|
||||
msgstr "Postfächer"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:54
|
||||
#, python-format
|
||||
msgid "%(num)s of %(total)s in use"
|
||||
msgstr "%(num)s von %(total)s genutzt"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:57
|
||||
#, 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:59
|
||||
msgid "SFTP username"
|
||||
msgstr "SFTP-Benutzername"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:60
|
||||
msgid "SSH/SFTP username"
|
||||
msgstr "SSH/SFTP-Benutzername"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:63
|
||||
#, 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:65
|
||||
msgid "Upload server"
|
||||
msgstr "Uploadserver"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:73
|
||||
msgid "Hosting Package Options"
|
||||
msgstr "Hostingpaketoptionen"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:81
|
||||
msgid "No options booked"
|
||||
msgstr "Keine Optionen gebucht"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:87
|
||||
msgid "Add another hosting option"
|
||||
msgstr "Eine weitere Hostingoption hinzufügen"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:87
|
||||
msgid "Add option"
|
||||
msgstr "Option hinzufügen"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:94
|
||||
msgid "Hosting Package Actions"
|
||||
msgstr "Aktionen zum Hostingpaket"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:98
|
||||
msgid "Edit Hosting Package Description"
|
||||
msgstr "Beschreibung des Hostingpakets bearbeiten"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:98
|
||||
msgid "Edit description"
|
||||
msgstr "Beschreibung bearbeiten"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:101
|
||||
msgid "Set SFTP password"
|
||||
msgstr "SFTP-Passwort setzen"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:102
|
||||
msgid "Set SSH/SFTP password"
|
||||
msgstr "SSH/SFTP-Passwort setzen"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:105
|
||||
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:107
|
||||
msgid "Add SSH public key"
|
||||
msgstr "SSH-Schlüssel hinzufügen"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:116
|
||||
msgid "Domains"
|
||||
msgstr "Domains"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:121
|
||||
msgid "Domain name"
|
||||
msgstr "Domainname"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:122
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:201
|
||||
msgid "Mail addresses"
|
||||
msgstr "E-Mailadressen"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:123
|
||||
msgid "Websites"
|
||||
msgstr "Webauftritte"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:124
|
||||
msgid "Domain actions"
|
||||
msgstr "Domainaktionen"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:125
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:204
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:247
|
||||
msgid "Actions"
|
||||
msgstr "Aktionen"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:137
|
||||
msgid "Edit mail address targets"
|
||||
msgstr "E-Mailadressziele bearbeiten"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:139
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:141
|
||||
msgid "Delete mail address"
|
||||
msgstr "E-Mailadresse löschen"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:146
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:161
|
||||
msgid "None"
|
||||
msgstr "Keine"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:154
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:156
|
||||
msgid "Delete website"
|
||||
msgstr "Webauftritt löschen"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:167
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:169
|
||||
msgid "Add mail address"
|
||||
msgstr "E-Mailadresse hinzufügen"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:174
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:175
|
||||
msgid "Add website"
|
||||
msgstr "Webauftritt anlegen"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:183
|
||||
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:187
|
||||
msgid "Add domain"
|
||||
msgstr "Domain hinzufügen"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:195
|
||||
msgid "E-Mail-Accounts"
|
||||
msgstr "E-Mail-Konten"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:200
|
||||
msgid "Mailbox"
|
||||
msgstr "Postfach"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:202
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:214
|
||||
msgid "Active"
|
||||
msgstr "Aktiv"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:203
|
||||
msgid "Mailbox actions"
|
||||
msgstr "Postfachaktionen"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:215
|
||||
msgid "inactive"
|
||||
msgstr "inaktiv"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:218
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:219
|
||||
msgid "Set mailbox password"
|
||||
msgstr "Postfachpasswort setzen"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:225
|
||||
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:230
|
||||
msgid "Add mailbox"
|
||||
msgstr "Postfach hinzufügen"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:237
|
||||
#: hostingpackages/views.py:213
|
||||
msgid "Databases"
|
||||
msgstr "Datenbanken"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:242
|
||||
msgid "Database name"
|
||||
msgstr "Datenbankname"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:243
|
||||
msgid "Database user"
|
||||
msgstr "Datenbanknutzer"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:244
|
||||
msgid "Database type"
|
||||
msgstr "Datenbanktyp"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:245
|
||||
msgid "Type"
|
||||
msgstr "Typ"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:246
|
||||
msgid "Database actions"
|
||||
msgstr "Datenbankaktionen"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:258
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:260
|
||||
msgid "Set database user password"
|
||||
msgstr "Datenbanknutzerpasswort setzen"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:262
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:263
|
||||
msgid "Delete database"
|
||||
msgstr "Datenbank löschen"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:270
|
||||
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:275
|
||||
msgid "Add database"
|
||||
msgstr "Datenbank hinzufügen"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:5
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:14
|
||||
msgid "Your hosting packages"
|
||||
msgstr "Ihre Hostingpakete"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:7
|
||||
#, python-format
|
||||
msgid "Hosting Packages of %(customer)s"
|
||||
msgstr "Hostingpakete des Kunden %(customer)s"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:16
|
||||
#, python-format
|
||||
msgid "Hosting Packages <small>of %(customer)s</small>"
|
||||
msgstr "Hostingpakete <small>des Kunden %(customer)s</small>"
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:41
|
||||
msgid "You have no hosting packages setup yet."
|
||||
msgstr "Es wurden noch keine Hostingpakete für Sie eingerichtet."
|
||||
|
||||
#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:42
|
||||
msgid "There are no hosting packages setup for this customer yet."
|
||||
msgstr "Es wurden noch keine Hostingpakete für diesen Kunden eingerichtet."
|
||||
|
||||
#: 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:49 hostingpackages/views.py:83
|
||||
#, python-brace-format
|
||||
msgid "Started setup of new hosting package {name}."
|
||||
msgstr "Einrichtung des Hostingpakets {name} wurde gestartet."
|
||||
|
||||
#: hostingpackages/views.py:186
|
||||
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
|
||||
#: hostingpackages/views.py:287
|
||||
#, python-brace-format
|
||||
msgid "Successfully added option {option} to hosting package {package}."
|
||||
msgstr "Option {option} erfolgreich zum Hostingpaket {package} hinzugefügt."
|
||||
|
||||
#~ msgid "Hosting options"
|
||||
#~ msgstr "Hostingoptionen"
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.utils.timezone
|
||||
import model_utils.fields
|
||||
from django.conf import settings
|
||||
|
@ -14,301 +12,469 @@ class Migration(migrations.Migration):
|
|||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CustomerHostingPackage',
|
||||
name="CustomerHostingPackage",
|
||||
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)),
|
||||
('modified', model_utils.fields.AutoLastModifiedField(
|
||||
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')])),
|
||||
('customer', models.ForeignKey(
|
||||
verbose_name='customer', to=settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE)),
|
||||
(
|
||||
"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,
|
||||
),
|
||||
),
|
||||
(
|
||||
"modified",
|
||||
model_utils.fields.AutoLastModifiedField(
|
||||
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")],
|
||||
),
|
||||
),
|
||||
(
|
||||
"customer",
|
||||
models.ForeignKey(
|
||||
verbose_name="customer",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'customer hosting package',
|
||||
'verbose_name_plural': 'customer hosting packages',
|
||||
"verbose_name": "customer hosting package",
|
||||
"verbose_name_plural": "customer hosting packages",
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CustomerHostingPackageOption',
|
||||
name="CustomerHostingPackageOption",
|
||||
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)),
|
||||
('modified', model_utils.fields.AutoLastModifiedField(
|
||||
default=django.utils.timezone.now, verbose_name='modified',
|
||||
editable=False)),
|
||||
(
|
||||
"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,
|
||||
),
|
||||
),
|
||||
(
|
||||
"modified",
|
||||
model_utils.fields.AutoLastModifiedField(
|
||||
default=django.utils.timezone.now,
|
||||
verbose_name="modified",
|
||||
editable=False,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'customer hosting option',
|
||||
'verbose_name_plural': 'customer hosting options',
|
||||
"verbose_name": "customer hosting option",
|
||||
"verbose_name_plural": "customer hosting options",
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CustomerDiskSpaceOption',
|
||||
name="CustomerDiskSpaceOption",
|
||||
fields=[
|
||||
('customerhostingpackageoption_ptr', models.OneToOneField(
|
||||
parent_link=True, auto_created=True, primary_key=True,
|
||||
serialize=False,
|
||||
to='hostingpackages.CustomerHostingPackageOption',
|
||||
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')])),
|
||||
(
|
||||
"customerhostingpackageoption_ptr",
|
||||
models.OneToOneField(
|
||||
parent_link=True,
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="hostingpackages.CustomerHostingPackageOption",
|
||||
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={
|
||||
'ordering': ['diskspace_unit', 'diskspace'],
|
||||
'abstract': False,
|
||||
'verbose_name': 'Disk space option',
|
||||
'verbose_name_plural': 'Disk space options',
|
||||
"ordering": ["diskspace_unit", "diskspace"],
|
||||
"abstract": False,
|
||||
"verbose_name": "Disk space option",
|
||||
"verbose_name_plural": "Disk space options",
|
||||
},
|
||||
bases=(
|
||||
'hostingpackages.customerhostingpackageoption', models.Model),
|
||||
bases=("hostingpackages.customerhostingpackageoption", models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CustomerMailboxOption',
|
||||
name="CustomerMailboxOption",
|
||||
fields=[
|
||||
('customerhostingpackageoption_ptr', models.OneToOneField(
|
||||
parent_link=True, auto_created=True, primary_key=True,
|
||||
serialize=False,
|
||||
to='hostingpackages.CustomerHostingPackageOption',
|
||||
on_delete=models.CASCADE)),
|
||||
('number', models.PositiveIntegerField(
|
||||
unique=True, verbose_name='number of mailboxes')),
|
||||
(
|
||||
"customerhostingpackageoption_ptr",
|
||||
models.OneToOneField(
|
||||
parent_link=True,
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="hostingpackages.CustomerHostingPackageOption",
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
),
|
||||
(
|
||||
"number",
|
||||
models.PositiveIntegerField(
|
||||
unique=True, verbose_name="number of mailboxes"
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ['number'],
|
||||
'abstract': False,
|
||||
'verbose_name': 'Mailbox option',
|
||||
'verbose_name_plural': 'Mailbox options',
|
||||
"ordering": ["number"],
|
||||
"abstract": False,
|
||||
"verbose_name": "Mailbox option",
|
||||
"verbose_name_plural": "Mailbox options",
|
||||
},
|
||||
bases=(
|
||||
'hostingpackages.customerhostingpackageoption', models.Model),
|
||||
bases=("hostingpackages.customerhostingpackageoption", models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CustomerUserDatabaseOption',
|
||||
name="CustomerUserDatabaseOption",
|
||||
fields=[
|
||||
('customerhostingpackageoption_ptr', models.OneToOneField(
|
||||
parent_link=True, auto_created=True, primary_key=True,
|
||||
serialize=False,
|
||||
to='hostingpackages.CustomerHostingPackageOption',
|
||||
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')])),
|
||||
(
|
||||
"customerhostingpackageoption_ptr",
|
||||
models.OneToOneField(
|
||||
parent_link=True,
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="hostingpackages.CustomerHostingPackageOption",
|
||||
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={
|
||||
'ordering': ['db_type', 'number'],
|
||||
'abstract': False,
|
||||
'verbose_name': 'Database option',
|
||||
'verbose_name_plural': 'Database options',
|
||||
"ordering": ["db_type", "number"],
|
||||
"abstract": False,
|
||||
"verbose_name": "Database option",
|
||||
"verbose_name_plural": "Database options",
|
||||
},
|
||||
bases=(
|
||||
'hostingpackages.customerhostingpackageoption', models.Model),
|
||||
bases=("hostingpackages.customerhostingpackageoption", models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='HostingOption',
|
||||
name="HostingOption",
|
||||
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)),
|
||||
('modified', model_utils.fields.AutoLastModifiedField(
|
||||
default=django.utils.timezone.now, verbose_name='modified',
|
||||
editable=False)),
|
||||
(
|
||||
"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,
|
||||
),
|
||||
),
|
||||
(
|
||||
"modified",
|
||||
model_utils.fields.AutoLastModifiedField(
|
||||
default=django.utils.timezone.now,
|
||||
verbose_name="modified",
|
||||
editable=False,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Hosting option',
|
||||
'verbose_name_plural': 'Hosting options',
|
||||
"verbose_name": "Hosting option",
|
||||
"verbose_name_plural": "Hosting options",
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DiskSpaceOption',
|
||||
name="DiskSpaceOption",
|
||||
fields=[
|
||||
('hostingoption_ptr', models.OneToOneField(
|
||||
parent_link=True, auto_created=True, primary_key=True,
|
||||
serialize=False, to='hostingpackages.HostingOption',
|
||||
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')])),
|
||||
(
|
||||
"hostingoption_ptr",
|
||||
models.OneToOneField(
|
||||
parent_link=True,
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="hostingpackages.HostingOption",
|
||||
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={
|
||||
'ordering': ['diskspace_unit', 'diskspace'],
|
||||
'abstract': False,
|
||||
'verbose_name': 'Disk space option',
|
||||
'verbose_name_plural': 'Disk space options',
|
||||
"ordering": ["diskspace_unit", "diskspace"],
|
||||
"abstract": False,
|
||||
"verbose_name": "Disk space option",
|
||||
"verbose_name_plural": "Disk space options",
|
||||
},
|
||||
bases=('hostingpackages.hostingoption', models.Model),
|
||||
bases=("hostingpackages.hostingoption", models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='HostingPackageTemplate',
|
||||
name="HostingPackageTemplate",
|
||||
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)),
|
||||
('modified', model_utils.fields.AutoLastModifiedField(
|
||||
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')])),
|
||||
(
|
||||
"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,
|
||||
),
|
||||
),
|
||||
(
|
||||
"modified",
|
||||
model_utils.fields.AutoLastModifiedField(
|
||||
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={
|
||||
'verbose_name': 'Hosting package',
|
||||
'verbose_name_plural': 'Hosting packages',
|
||||
"verbose_name": "Hosting package",
|
||||
"verbose_name_plural": "Hosting packages",
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='MailboxOption',
|
||||
name="MailboxOption",
|
||||
fields=[
|
||||
('hostingoption_ptr', models.OneToOneField(
|
||||
parent_link=True, auto_created=True, primary_key=True,
|
||||
serialize=False, to='hostingpackages.HostingOption',
|
||||
on_delete=models.CASCADE)),
|
||||
('number', models.PositiveIntegerField(
|
||||
unique=True, verbose_name='number of mailboxes')),
|
||||
(
|
||||
"hostingoption_ptr",
|
||||
models.OneToOneField(
|
||||
parent_link=True,
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="hostingpackages.HostingOption",
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
),
|
||||
(
|
||||
"number",
|
||||
models.PositiveIntegerField(
|
||||
unique=True, verbose_name="number of mailboxes"
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ['number'],
|
||||
'abstract': False,
|
||||
'verbose_name': 'Mailbox option',
|
||||
'verbose_name_plural': 'Mailbox options',
|
||||
"ordering": ["number"],
|
||||
"abstract": False,
|
||||
"verbose_name": "Mailbox option",
|
||||
"verbose_name_plural": "Mailbox options",
|
||||
},
|
||||
bases=('hostingpackages.hostingoption', models.Model),
|
||||
bases=("hostingpackages.hostingoption", models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UserDatabaseOption',
|
||||
name="UserDatabaseOption",
|
||||
fields=[
|
||||
('hostingoption_ptr', models.OneToOneField(
|
||||
parent_link=True, auto_created=True, primary_key=True,
|
||||
serialize=False, to='hostingpackages.HostingOption',
|
||||
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')])),
|
||||
(
|
||||
"hostingoption_ptr",
|
||||
models.OneToOneField(
|
||||
parent_link=True,
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="hostingpackages.HostingOption",
|
||||
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={
|
||||
'ordering': ['db_type', 'number'],
|
||||
'abstract': False,
|
||||
'verbose_name': 'Database option',
|
||||
'verbose_name_plural': 'Database options',
|
||||
"ordering": ["db_type", "number"],
|
||||
"abstract": False,
|
||||
"verbose_name": "Database option",
|
||||
"verbose_name_plural": "Database options",
|
||||
},
|
||||
bases=('hostingpackages.hostingoption', models.Model),
|
||||
bases=("hostingpackages.hostingoption", models.Model),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='userdatabaseoption',
|
||||
unique_together={('number', 'db_type')},
|
||||
name="userdatabaseoption",
|
||||
unique_together={("number", "db_type")},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='diskspaceoption',
|
||||
unique_together={('diskspace', 'diskspace_unit')},
|
||||
name="diskspaceoption",
|
||||
unique_together={("diskspace", "diskspace_unit")},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='customeruserdatabaseoption',
|
||||
name='template',
|
||||
model_name="customeruserdatabaseoption",
|
||||
name="template",
|
||||
field=models.ForeignKey(
|
||||
verbose_name='user database option template',
|
||||
to='hostingpackages.UserDatabaseOption',
|
||||
help_text='The user database option template that this '
|
||||
'hosting option is based on',
|
||||
on_delete=models.CASCADE),
|
||||
verbose_name="user database option template",
|
||||
to="hostingpackages.UserDatabaseOption",
|
||||
help_text="The user database option template that this "
|
||||
"hosting option is based on",
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='customeruserdatabaseoption',
|
||||
unique_together={('number', 'db_type')},
|
||||
name="customeruserdatabaseoption",
|
||||
unique_together={("number", "db_type")},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='customermailboxoption',
|
||||
name='template',
|
||||
model_name="customermailboxoption",
|
||||
name="template",
|
||||
field=models.ForeignKey(
|
||||
verbose_name='mailbox option template',
|
||||
to='hostingpackages.UserDatabaseOption',
|
||||
help_text='The mailbox option template that this hosting '
|
||||
'option is based on',
|
||||
on_delete=models.CASCADE),
|
||||
verbose_name="mailbox option template",
|
||||
to="hostingpackages.UserDatabaseOption",
|
||||
help_text="The mailbox option template that this hosting "
|
||||
"option is based on",
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='customerhostingpackageoption',
|
||||
name='hosting_package',
|
||||
model_name="customerhostingpackageoption",
|
||||
name="hosting_package",
|
||||
field=models.ForeignKey(
|
||||
verbose_name='hosting package',
|
||||
to='hostingpackages.CustomerHostingPackage',
|
||||
on_delete=models.CASCADE),
|
||||
verbose_name="hosting package",
|
||||
to="hostingpackages.CustomerHostingPackage",
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='customerhostingpackage',
|
||||
name='template',
|
||||
model_name="customerhostingpackage",
|
||||
name="template",
|
||||
field=models.ForeignKey(
|
||||
verbose_name='hosting package template',
|
||||
to='hostingpackages.HostingPackageTemplate',
|
||||
help_text='The hosting package template that this hosting '
|
||||
'package is based on.',
|
||||
on_delete=models.CASCADE),
|
||||
verbose_name="hosting package template",
|
||||
to="hostingpackages.HostingPackageTemplate",
|
||||
help_text="The hosting package template that this hosting "
|
||||
"package is based on.",
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='customerdiskspaceoption',
|
||||
name='template',
|
||||
model_name="customerdiskspaceoption",
|
||||
name="template",
|
||||
field=models.ForeignKey(
|
||||
verbose_name='disk space option template',
|
||||
to='hostingpackages.DiskSpaceOption',
|
||||
help_text='The disk space option template that this hosting '
|
||||
'option is based on',
|
||||
on_delete=models.CASCADE),
|
||||
verbose_name="disk space option template",
|
||||
to="hostingpackages.DiskSpaceOption",
|
||||
help_text="The disk space option template that this hosting "
|
||||
"option is based on",
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='customerdiskspaceoption',
|
||||
unique_together={('diskspace', 'diskspace_unit')},
|
||||
name="customerdiskspaceoption",
|
||||
unique_together={("diskspace", "diskspace_unit")},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.utils.timezone
|
||||
import model_utils.fields
|
||||
from django.conf import settings
|
||||
|
@ -8,361 +6,530 @@ from django.db import migrations, models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
replaces = [('hostingpackages', '0001_initial'),
|
||||
('hostingpackages', '0002_auto_20150118_1149'),
|
||||
('hostingpackages', '0003_auto_20150118_1221'),
|
||||
('hostingpackages', '0004_customerhostingpackage_osuser'),
|
||||
('hostingpackages', '0005_auto_20150118_1303')]
|
||||
replaces = [
|
||||
("hostingpackages", "0001_initial"),
|
||||
("hostingpackages", "0002_auto_20150118_1149"),
|
||||
("hostingpackages", "0003_auto_20150118_1221"),
|
||||
("hostingpackages", "0004_customerhostingpackage_osuser"),
|
||||
("hostingpackages", "0005_auto_20150118_1303"),
|
||||
]
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('osusers', '0004_auto_20150104_1751'),
|
||||
("osusers", "0004_auto_20150104_1751"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CustomerHostingPackage',
|
||||
name="CustomerHostingPackage",
|
||||
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)),
|
||||
('modified', model_utils.fields.AutoLastModifiedField(
|
||||
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')])),
|
||||
('customer', models.ForeignKey(
|
||||
verbose_name='customer',
|
||||
to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
|
||||
(
|
||||
"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,
|
||||
),
|
||||
),
|
||||
(
|
||||
"modified",
|
||||
model_utils.fields.AutoLastModifiedField(
|
||||
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")],
|
||||
),
|
||||
),
|
||||
(
|
||||
"customer",
|
||||
models.ForeignKey(
|
||||
verbose_name="customer",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'customer hosting package',
|
||||
'verbose_name_plural': 'customer hosting packages',
|
||||
"verbose_name": "customer hosting package",
|
||||
"verbose_name_plural": "customer hosting packages",
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CustomerHostingPackageOption',
|
||||
name="CustomerHostingPackageOption",
|
||||
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)),
|
||||
('modified', model_utils.fields.AutoLastModifiedField(
|
||||
default=django.utils.timezone.now, verbose_name='modified',
|
||||
editable=False)),
|
||||
(
|
||||
"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,
|
||||
),
|
||||
),
|
||||
(
|
||||
"modified",
|
||||
model_utils.fields.AutoLastModifiedField(
|
||||
default=django.utils.timezone.now,
|
||||
verbose_name="modified",
|
||||
editable=False,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'customer hosting option',
|
||||
'verbose_name_plural': 'customer hosting options',
|
||||
"verbose_name": "customer hosting option",
|
||||
"verbose_name_plural": "customer hosting options",
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CustomerDiskSpaceOption',
|
||||
name="CustomerDiskSpaceOption",
|
||||
fields=[
|
||||
('customerhostingpackageoption_ptr',
|
||||
models.OneToOneField(
|
||||
parent_link=True, auto_created=True, primary_key=True,
|
||||
serialize=False,
|
||||
to='hostingpackages.CustomerHostingPackageOption',
|
||||
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')])),
|
||||
(
|
||||
"customerhostingpackageoption_ptr",
|
||||
models.OneToOneField(
|
||||
parent_link=True,
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="hostingpackages.CustomerHostingPackageOption",
|
||||
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={
|
||||
'ordering': ['diskspace_unit', 'diskspace'],
|
||||
'abstract': False,
|
||||
'verbose_name': 'Disk space option',
|
||||
'verbose_name_plural': 'Disk space options',
|
||||
"ordering": ["diskspace_unit", "diskspace"],
|
||||
"abstract": False,
|
||||
"verbose_name": "Disk space option",
|
||||
"verbose_name_plural": "Disk space options",
|
||||
},
|
||||
bases=(
|
||||
'hostingpackages.customerhostingpackageoption', models.Model),
|
||||
bases=("hostingpackages.customerhostingpackageoption", models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CustomerMailboxOption',
|
||||
name="CustomerMailboxOption",
|
||||
fields=[
|
||||
('customerhostingpackageoption_ptr',
|
||||
models.OneToOneField(
|
||||
parent_link=True, auto_created=True, primary_key=True,
|
||||
serialize=False,
|
||||
to='hostingpackages.CustomerHostingPackageOption',
|
||||
on_delete=models.CASCADE)),
|
||||
('number', models.PositiveIntegerField(
|
||||
unique=True, verbose_name='number of mailboxes')),
|
||||
(
|
||||
"customerhostingpackageoption_ptr",
|
||||
models.OneToOneField(
|
||||
parent_link=True,
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="hostingpackages.CustomerHostingPackageOption",
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
),
|
||||
(
|
||||
"number",
|
||||
models.PositiveIntegerField(
|
||||
unique=True, verbose_name="number of mailboxes"
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ['number'],
|
||||
'abstract': False,
|
||||
'verbose_name': 'Mailbox option',
|
||||
'verbose_name_plural': 'Mailbox options',
|
||||
"ordering": ["number"],
|
||||
"abstract": False,
|
||||
"verbose_name": "Mailbox option",
|
||||
"verbose_name_plural": "Mailbox options",
|
||||
},
|
||||
bases=(
|
||||
'hostingpackages.customerhostingpackageoption', models.Model),
|
||||
bases=("hostingpackages.customerhostingpackageoption", models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CustomerUserDatabaseOption',
|
||||
name="CustomerUserDatabaseOption",
|
||||
fields=[
|
||||
('customerhostingpackageoption_ptr',
|
||||
models.OneToOneField(
|
||||
parent_link=True, auto_created=True, primary_key=True,
|
||||
serialize=False,
|
||||
to='hostingpackages.CustomerHostingPackageOption',
|
||||
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')])),
|
||||
(
|
||||
"customerhostingpackageoption_ptr",
|
||||
models.OneToOneField(
|
||||
parent_link=True,
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="hostingpackages.CustomerHostingPackageOption",
|
||||
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={
|
||||
'ordering': ['db_type', 'number'],
|
||||
'abstract': False,
|
||||
'verbose_name': 'Database option',
|
||||
'verbose_name_plural': 'Database options',
|
||||
"ordering": ["db_type", "number"],
|
||||
"abstract": False,
|
||||
"verbose_name": "Database option",
|
||||
"verbose_name_plural": "Database options",
|
||||
},
|
||||
bases=(
|
||||
'hostingpackages.customerhostingpackageoption', models.Model),
|
||||
bases=("hostingpackages.customerhostingpackageoption", models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='HostingOption',
|
||||
name="HostingOption",
|
||||
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)),
|
||||
('modified', model_utils.fields.AutoLastModifiedField(
|
||||
default=django.utils.timezone.now, verbose_name='modified',
|
||||
editable=False)),
|
||||
(
|
||||
"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,
|
||||
),
|
||||
),
|
||||
(
|
||||
"modified",
|
||||
model_utils.fields.AutoLastModifiedField(
|
||||
default=django.utils.timezone.now,
|
||||
verbose_name="modified",
|
||||
editable=False,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Hosting option',
|
||||
'verbose_name_plural': 'Hosting options',
|
||||
"verbose_name": "Hosting option",
|
||||
"verbose_name_plural": "Hosting options",
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DiskSpaceOption',
|
||||
name="DiskSpaceOption",
|
||||
fields=[
|
||||
('hostingoption_ptr',
|
||||
models.OneToOneField(
|
||||
parent_link=True, auto_created=True, primary_key=True,
|
||||
serialize=False, to='hostingpackages.HostingOption',
|
||||
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')])),
|
||||
(
|
||||
"hostingoption_ptr",
|
||||
models.OneToOneField(
|
||||
parent_link=True,
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="hostingpackages.HostingOption",
|
||||
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={
|
||||
'ordering': ['diskspace_unit', 'diskspace'],
|
||||
'abstract': False,
|
||||
'verbose_name': 'Disk space option',
|
||||
'verbose_name_plural': 'Disk space options',
|
||||
"ordering": ["diskspace_unit", "diskspace"],
|
||||
"abstract": False,
|
||||
"verbose_name": "Disk space option",
|
||||
"verbose_name_plural": "Disk space options",
|
||||
},
|
||||
bases=('hostingpackages.hostingoption', models.Model),
|
||||
bases=("hostingpackages.hostingoption", models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='HostingPackageTemplate',
|
||||
name="HostingPackageTemplate",
|
||||
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)),
|
||||
('modified', model_utils.fields.AutoLastModifiedField(
|
||||
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')])),
|
||||
(
|
||||
"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,
|
||||
),
|
||||
),
|
||||
(
|
||||
"modified",
|
||||
model_utils.fields.AutoLastModifiedField(
|
||||
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={
|
||||
'verbose_name': 'Hosting package',
|
||||
'verbose_name_plural': 'Hosting packages',
|
||||
"verbose_name": "Hosting package",
|
||||
"verbose_name_plural": "Hosting packages",
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='MailboxOption',
|
||||
name="MailboxOption",
|
||||
fields=[
|
||||
('hostingoption_ptr',
|
||||
models.OneToOneField(
|
||||
parent_link=True, auto_created=True, primary_key=True,
|
||||
serialize=False, to='hostingpackages.HostingOption',
|
||||
on_delete=models.CASCADE)),
|
||||
('number', models.PositiveIntegerField(
|
||||
unique=True, verbose_name='number of mailboxes')),
|
||||
(
|
||||
"hostingoption_ptr",
|
||||
models.OneToOneField(
|
||||
parent_link=True,
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="hostingpackages.HostingOption",
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
),
|
||||
(
|
||||
"number",
|
||||
models.PositiveIntegerField(
|
||||
unique=True, verbose_name="number of mailboxes"
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ['number'],
|
||||
'abstract': False,
|
||||
'verbose_name': 'Mailbox option',
|
||||
'verbose_name_plural': 'Mailbox options',
|
||||
"ordering": ["number"],
|
||||
"abstract": False,
|
||||
"verbose_name": "Mailbox option",
|
||||
"verbose_name_plural": "Mailbox options",
|
||||
},
|
||||
bases=('hostingpackages.hostingoption', models.Model),
|
||||
bases=("hostingpackages.hostingoption", models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UserDatabaseOption',
|
||||
name="UserDatabaseOption",
|
||||
fields=[
|
||||
('hostingoption_ptr',
|
||||
models.OneToOneField(
|
||||
parent_link=True, auto_created=True, primary_key=True,
|
||||
serialize=False, to='hostingpackages.HostingOption',
|
||||
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')])),
|
||||
(
|
||||
"hostingoption_ptr",
|
||||
models.OneToOneField(
|
||||
parent_link=True,
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="hostingpackages.HostingOption",
|
||||
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={
|
||||
'ordering': ['db_type', 'number'],
|
||||
'abstract': False,
|
||||
'verbose_name': 'Database option',
|
||||
'verbose_name_plural': 'Database options',
|
||||
"ordering": ["db_type", "number"],
|
||||
"abstract": False,
|
||||
"verbose_name": "Database option",
|
||||
"verbose_name_plural": "Database options",
|
||||
},
|
||||
bases=('hostingpackages.hostingoption', models.Model),
|
||||
bases=("hostingpackages.hostingoption", models.Model),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='userdatabaseoption',
|
||||
unique_together={('number', 'db_type')},
|
||||
name="userdatabaseoption",
|
||||
unique_together={("number", "db_type")},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='diskspaceoption',
|
||||
unique_together={('diskspace', 'diskspace_unit')},
|
||||
name="diskspaceoption",
|
||||
unique_together={("diskspace", "diskspace_unit")},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='customeruserdatabaseoption',
|
||||
name='template',
|
||||
model_name="customeruserdatabaseoption",
|
||||
name="template",
|
||||
field=models.ForeignKey(
|
||||
verbose_name='user database option template',
|
||||
to='hostingpackages.UserDatabaseOption',
|
||||
help_text='The user database option template that this '
|
||||
'hosting option is based on',
|
||||
on_delete=models.CASCADE),
|
||||
verbose_name="user database option template",
|
||||
to="hostingpackages.UserDatabaseOption",
|
||||
help_text="The user database option template that this "
|
||||
"hosting option is based on",
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='customeruserdatabaseoption',
|
||||
unique_together={('number', 'db_type')},
|
||||
name="customeruserdatabaseoption",
|
||||
unique_together={("number", "db_type")},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='customermailboxoption',
|
||||
name='template',
|
||||
model_name="customermailboxoption",
|
||||
name="template",
|
||||
field=models.ForeignKey(
|
||||
verbose_name='mailbox option template',
|
||||
to='hostingpackages.UserDatabaseOption',
|
||||
help_text='The mailbox option template that this mailbox '
|
||||
'option is based on',
|
||||
on_delete=models.CASCADE),
|
||||
verbose_name="mailbox option template",
|
||||
to="hostingpackages.UserDatabaseOption",
|
||||
help_text="The mailbox option template that this mailbox "
|
||||
"option is based on",
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='customerhostingpackageoption',
|
||||
name='hosting_package',
|
||||
model_name="customerhostingpackageoption",
|
||||
name="hosting_package",
|
||||
field=models.ForeignKey(
|
||||
verbose_name='hosting package',
|
||||
to='hostingpackages.CustomerHostingPackage',
|
||||
on_delete=models.CASCADE),
|
||||
verbose_name="hosting package",
|
||||
to="hostingpackages.CustomerHostingPackage",
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='customerhostingpackage',
|
||||
name='template',
|
||||
model_name="customerhostingpackage",
|
||||
name="template",
|
||||
field=models.ForeignKey(
|
||||
verbose_name='hosting package template',
|
||||
to='hostingpackages.HostingPackageTemplate',
|
||||
help_text='The hosting package template that this hosting '
|
||||
'package is based on',
|
||||
on_delete=models.CASCADE),
|
||||
verbose_name="hosting package template",
|
||||
to="hostingpackages.HostingPackageTemplate",
|
||||
help_text="The hosting package template that this hosting "
|
||||
"package is based on",
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='customerdiskspaceoption',
|
||||
name='template',
|
||||
model_name="customerdiskspaceoption",
|
||||
name="template",
|
||||
field=models.ForeignKey(
|
||||
verbose_name='disk space option template',
|
||||
to='hostingpackages.DiskSpaceOption',
|
||||
help_text='The disk space option template that this hosting '
|
||||
'option is based on',
|
||||
on_delete=models.CASCADE),
|
||||
verbose_name="disk space option template",
|
||||
to="hostingpackages.DiskSpaceOption",
|
||||
help_text="The disk space option template that this hosting "
|
||||
"option is based on",
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='customerdiskspaceoption',
|
||||
unique_together={('diskspace', 'diskspace_unit')},
|
||||
name="customerdiskspaceoption",
|
||||
unique_together={("diskspace", "diskspace_unit")},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='customerdiskspaceoption',
|
||||
name='template',
|
||||
model_name="customerdiskspaceoption",
|
||||
name="template",
|
||||
field=models.ForeignKey(
|
||||
verbose_name='disk space option template',
|
||||
to='hostingpackages.DiskSpaceOption',
|
||||
help_text='The disk space option template that this disk '
|
||||
'space option is based on',
|
||||
on_delete=models.CASCADE),
|
||||
verbose_name="disk space option template",
|
||||
to="hostingpackages.DiskSpaceOption",
|
||||
help_text="The disk space option template that this disk "
|
||||
"space option is based on",
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='customeruserdatabaseoption',
|
||||
name='template',
|
||||
model_name="customeruserdatabaseoption",
|
||||
name="template",
|
||||
field=models.ForeignKey(
|
||||
verbose_name='user database option template',
|
||||
to='hostingpackages.UserDatabaseOption',
|
||||
help_text='The user database option template that this '
|
||||
'database option is based on',
|
||||
on_delete=models.CASCADE),
|
||||
verbose_name="user database option template",
|
||||
to="hostingpackages.UserDatabaseOption",
|
||||
help_text="The user database option template that this "
|
||||
"database option is based on",
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='customerhostingpackage',
|
||||
name='name',
|
||||
field=models.CharField(max_length=128, verbose_name='name'),
|
||||
model_name="customerhostingpackage",
|
||||
name="name",
|
||||
field=models.CharField(max_length=128, verbose_name="name"),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='customerhostingpackage',
|
||||
unique_together={('customer', 'name')},
|
||||
name="customerhostingpackage",
|
||||
unique_together={("customer", "name")},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='customerhostingpackage',
|
||||
name='osuser',
|
||||
model_name="customerhostingpackage",
|
||||
name="osuser",
|
||||
field=models.OneToOneField(
|
||||
null=True, blank=True, to='osusers.User',
|
||||
verbose_name='Operating system user', on_delete=models.CASCADE),
|
||||
null=True,
|
||||
blank=True,
|
||||
to="osusers.User",
|
||||
verbose_name="Operating system user",
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,57 +1,59 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('hostingpackages', '0001_initial'),
|
||||
("hostingpackages", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='customerdiskspaceoption',
|
||||
name='template',
|
||||
model_name="customerdiskspaceoption",
|
||||
name="template",
|
||||
field=models.ForeignKey(
|
||||
verbose_name='disk space option template',
|
||||
to='hostingpackages.DiskSpaceOption',
|
||||
help_text='The disk space option template that this disk '
|
||||
'space option is based on',
|
||||
on_delete=models.CASCADE),
|
||||
verbose_name="disk space option template",
|
||||
to="hostingpackages.DiskSpaceOption",
|
||||
help_text="The disk space option template that this disk "
|
||||
"space option is based on",
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='customerhostingpackage',
|
||||
name='template',
|
||||
model_name="customerhostingpackage",
|
||||
name="template",
|
||||
field=models.ForeignKey(
|
||||
verbose_name='hosting package template',
|
||||
to='hostingpackages.HostingPackageTemplate',
|
||||
help_text='The hosting package template that this hosting '
|
||||
'package is based on',
|
||||
on_delete=models.CASCADE),
|
||||
verbose_name="hosting package template",
|
||||
to="hostingpackages.HostingPackageTemplate",
|
||||
help_text="The hosting package template that this hosting "
|
||||
"package is based on",
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='customermailboxoption',
|
||||
name='template',
|
||||
model_name="customermailboxoption",
|
||||
name="template",
|
||||
field=models.ForeignKey(
|
||||
verbose_name='mailbox option template',
|
||||
to='hostingpackages.UserDatabaseOption',
|
||||
help_text='The mailbox option template that this mailbox '
|
||||
'option is based on',
|
||||
on_delete=models.CASCADE),
|
||||
verbose_name="mailbox option template",
|
||||
to="hostingpackages.UserDatabaseOption",
|
||||
help_text="The mailbox option template that this mailbox "
|
||||
"option is based on",
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='customeruserdatabaseoption',
|
||||
name='template',
|
||||
model_name="customeruserdatabaseoption",
|
||||
name="template",
|
||||
field=models.ForeignKey(
|
||||
verbose_name='user database option template',
|
||||
to='hostingpackages.UserDatabaseOption',
|
||||
help_text='The user database option template that this '
|
||||
'database option is based on',
|
||||
on_delete=models.CASCADE),
|
||||
verbose_name="user database option template",
|
||||
to="hostingpackages.UserDatabaseOption",
|
||||
help_text="The user database option template that this "
|
||||
"database option is based on",
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,18 +1,15 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('hostingpackages', '0001_squashed_0005_auto_20150118_1303'),
|
||||
("hostingpackages", "0001_squashed_0005_auto_20150118_1303"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='hostingoption',
|
||||
name="hostingoption",
|
||||
options={},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,24 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('hostingpackages', '0002_auto_20150118_1149'),
|
||||
("hostingpackages", "0002_auto_20150118_1149"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='customerhostingpackage',
|
||||
name='name',
|
||||
field=models.CharField(max_length=128, verbose_name='name'),
|
||||
model_name="customerhostingpackage",
|
||||
name="name",
|
||||
field=models.CharField(max_length=128, verbose_name="name"),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='customerhostingpackage',
|
||||
unique_together=set([('customer', 'name')]),
|
||||
name="customerhostingpackage",
|
||||
unique_together=set([("customer", "name")]),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,24 +1,23 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('hostingpackages', '0002_auto_20150118_1319'),
|
||||
("hostingpackages", "0002_auto_20150118_1319"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='customermailboxoption',
|
||||
name='template',
|
||||
model_name="customermailboxoption",
|
||||
name="template",
|
||||
field=models.ForeignKey(
|
||||
verbose_name='mailbox option template',
|
||||
to='hostingpackages.MailboxOption',
|
||||
help_text='The mailbox option template that this mailbox '
|
||||
'option is based on',
|
||||
on_delete=models.CASCADE),
|
||||
verbose_name="mailbox option template",
|
||||
to="hostingpackages.MailboxOption",
|
||||
help_text="The mailbox option template that this mailbox "
|
||||
"option is based on",
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('osusers', '0004_auto_20150104_1751'),
|
||||
('hostingpackages', '0003_auto_20150118_1221'),
|
||||
("osusers", "0004_auto_20150104_1751"),
|
||||
("hostingpackages", "0003_auto_20150118_1221"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='customerhostingpackage',
|
||||
name='osuser',
|
||||
model_name="customerhostingpackage",
|
||||
name="osuser",
|
||||
field=models.ForeignKey(
|
||||
verbose_name='Operating system user', blank=True,
|
||||
to='osusers.User', null=True, on_delete=models.CASCADE),
|
||||
verbose_name="Operating system user",
|
||||
blank=True,
|
||||
to="osusers.User",
|
||||
null=True,
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.utils.timezone
|
||||
import model_utils.fields
|
||||
from django.db import migrations, models
|
||||
|
@ -8,33 +6,59 @@ from django.db import migrations, models
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('domains', '0002_auto_20150124_1909'),
|
||||
('hostingpackages', '0003_auto_20150118_1407'),
|
||||
("domains", "0002_auto_20150124_1909"),
|
||||
("hostingpackages", "0003_auto_20150118_1407"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CustomerHostingPackageDomain',
|
||||
name="CustomerHostingPackageDomain",
|
||||
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)),
|
||||
('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)),
|
||||
(
|
||||
"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,
|
||||
),
|
||||
),
|
||||
(
|
||||
"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={
|
||||
'abstract': False,
|
||||
"abstract": False,
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
|
|
|
@ -1,21 +1,23 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('hostingpackages', '0004_customerhostingpackage_osuser'),
|
||||
("hostingpackages", "0004_customerhostingpackage_osuser"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='customerhostingpackage',
|
||||
name='osuser',
|
||||
model_name="customerhostingpackage",
|
||||
name="osuser",
|
||||
field=models.OneToOneField(
|
||||
null=True, blank=True, to='osusers.User',
|
||||
verbose_name='Operating system user', on_delete=models.CASCADE),
|
||||
null=True,
|
||||
blank=True,
|
||||
to="osusers.User",
|
||||
verbose_name="Operating system user",
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,22 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('hostingpackages', '0004_customerhostingpackagedomain'),
|
||||
("hostingpackages", "0004_customerhostingpackagedomain"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='diskspaceoption',
|
||||
name="diskspaceoption",
|
||||
options={},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='customerdiskspaceoption',
|
||||
name="customerdiskspaceoption",
|
||||
unique_together=set([]),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,22 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('hostingpackages', '0005_auto_20150125_1508'),
|
||||
("hostingpackages", "0005_auto_20150125_1508"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='userdatabaseoption',
|
||||
name="userdatabaseoption",
|
||||
options={},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='customeruserdatabaseoption',
|
||||
name="customeruserdatabaseoption",
|
||||
unique_together=set([]),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -2,21 +2,21 @@
|
|||
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.db import transaction
|
||||
from django.db import models
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.db import models, transaction
|
||||
from django.urls import reverse
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from django.utils.translation import ugettext_lazy as _, ungettext
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.translation import ngettext
|
||||
from model_utils import Choices
|
||||
from model_utils.models import TimeStampedModel
|
||||
|
||||
from domains.models import HostingDomain
|
||||
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
|
||||
|
||||
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))
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class HostingPackageBase(TimeStampedModel):
|
||||
description = models.TextField(_("description"), blank=True)
|
||||
mailboxcount = models.PositiveIntegerField(_("mailbox count"))
|
||||
|
@ -57,7 +56,6 @@ class HostingOption(TimeStampedModel):
|
|||
"""
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class DiskSpaceOptionBase(models.Model):
|
||||
diskspace = models.PositiveIntegerField(_("disk space"))
|
||||
diskspace_unit = models.PositiveSmallIntegerField(
|
||||
|
@ -87,7 +85,6 @@ class DiskSpaceOption(DiskSpaceOptionBase, HostingOption):
|
|||
unique_together = ["diskspace", "diskspace_unit"]
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class UserDatabaseOptionBase(models.Model):
|
||||
number = models.PositiveIntegerField(_("number of databases"), default=1)
|
||||
db_type = models.PositiveSmallIntegerField(_("database type"), choices=DB_TYPES)
|
||||
|
@ -99,7 +96,7 @@ class UserDatabaseOptionBase(models.Model):
|
|||
verbose_name_plural = _("Database options")
|
||||
|
||||
def __str__(self):
|
||||
return ungettext(
|
||||
return ngettext(
|
||||
"{type} database", "{count} {type} databases", 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"]
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class MailboxOptionBase(models.Model):
|
||||
"""
|
||||
Base class for mailbox options.
|
||||
|
@ -131,7 +127,7 @@ class MailboxOptionBase(models.Model):
|
|||
verbose_name_plural = _("Mailbox options")
|
||||
|
||||
def __str__(self):
|
||||
return ungettext(
|
||||
return ngettext(
|
||||
"{count} additional mailbox", "{count} additional mailboxes", self.number
|
||||
).format(count=self.number)
|
||||
|
||||
|
@ -177,7 +173,6 @@ class CustomerHostingPackageManager(models.Manager):
|
|||
return package
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class CustomerHostingPackage(HostingPackageBase):
|
||||
"""
|
||||
This class defines customer specific hosting packages.
|
||||
|
@ -269,7 +264,7 @@ class CustomerHostingPackage(HostingPackageBase):
|
|||
) + option.diskspace
|
||||
min_unit = option.diskspace_unit
|
||||
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:
|
||||
return DISK_SPACE_FACTORS[unit][min_unit] * diskspace
|
||||
return DISK_SPACE_FACTORS[min_unit][unit] * diskspace
|
||||
|
@ -287,7 +282,7 @@ class CustomerHostingPackage(HostingPackageBase):
|
|||
"""
|
||||
if unit is None:
|
||||
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:
|
||||
return DISK_SPACE_FACTORS[unit][self.diskspace_unit] * self.diskspace
|
||||
|
@ -376,13 +371,17 @@ class CustomerHostingPackage(HostingPackageBase):
|
|||
self.copy_template_attributes()
|
||||
self.osuser = OsUser.objects.create_user(self.customer)
|
||||
for group in settings.OSUSER_DEFAULT_GROUPS:
|
||||
AdditionalGroup.objects.create(
|
||||
user=self.osuser, group=Group.objects.get(groupname=group)
|
||||
)
|
||||
try:
|
||||
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)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class CustomerHostingPackageDomain(TimeStampedModel):
|
||||
"""
|
||||
This class defines the relationship from a hosting package to a hosting
|
||||
|
|
|
@ -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,280 @@
|
|||
{% 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 diskspace=hostingpackage.get_disk_space packagespace=hostingpackage.get_package_space %}
|
||||
<dd>
|
||||
<span title="{% blocktranslate trimmed %}
|
||||
The reserved disk space for your hosting package is {{ diskspace }} bytes
|
||||
{% endblocktranslate %}">{{ diskspace|filesizeformat }}</span>
|
||||
<i class="bi-info-circle"
|
||||
title="{% blocktranslate with humanbytes=packagespace|filesizeformat trimmed %}
|
||||
The package contributes {{ humanbytes }} ({{ packagespace }} bytes) 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,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,10 +2,17 @@
|
|||
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):
|
||||
|
@ -13,7 +20,7 @@ class CustomerHostingPackageTest(TestCase):
|
|||
package = CustomerHostingPackage(
|
||||
diskspace=10, diskspace_unit=DISK_SPACE_UNITS.G
|
||||
)
|
||||
self.assertEqual(package.get_disk_space(), 10 * 1024 ** 3)
|
||||
self.assertEqual(package.get_disk_space(), 10 * 1024**3)
|
||||
|
||||
def test_get_disk_space_mib(self):
|
||||
package = CustomerHostingPackage(
|
||||
|
@ -26,3 +33,20 @@ class CustomerHostingPackageTest(TestCase):
|
|||
diskspace=256, diskspace_unit=DISK_SPACE_UNITS.M
|
||||
)
|
||||
self.assertEqual(package.get_quota(), (262144, 275251))
|
||||
|
||||
@override_settings(OSUSER_DEFAULT_GROUPS=["testgroup"])
|
||||
def test_additional_group_not_defined(self):
|
||||
user = User.objects.create(username="test")
|
||||
template = HostingPackageTemplate.objects.create(
|
||||
description="Test package 1 - Description",
|
||||
mailboxcount=10,
|
||||
diskspace=100,
|
||||
diskspace_unit=DISK_SPACE_UNITS.M,
|
||||
name="Test package 1",
|
||||
)
|
||||
with self.assertRaises(ImproperlyConfigured) as ctx:
|
||||
package = CustomerHostingPackage.objects.create_from_template(
|
||||
customer=user, template=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.
|
||||
|
||||
"""
|
||||
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 (
|
||||
AddHostingOption,
|
||||
|
@ -12,26 +12,34 @@ from .views import (
|
|||
CreateCustomerHostingPackage,
|
||||
CreateHostingPackage,
|
||||
CustomerHostingPackageDetails,
|
||||
CustomerHostingPackageList,
|
||||
HostingOptionChoices,
|
||||
)
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^create$', CreateHostingPackage.as_view(),
|
||||
name='create_hosting_package'),
|
||||
url(r'^allpackages/',
|
||||
AllCustomerHostingPackageList.as_view(), name='all_hosting_packages'),
|
||||
url(r'^(?P<user>[-\w0-9@.+_]+)/$',
|
||||
CustomerHostingPackageList.as_view(), name='hosting_packages'),
|
||||
url(r'^(?P<user>[-\w0-9@.+_]+)/create$',
|
||||
re_path(r"^create$", CreateHostingPackage.as_view(), name="create_hosting_package"),
|
||||
re_path(
|
||||
r"^allpackages/",
|
||||
AllCustomerHostingPackageList.as_view(),
|
||||
name="all_hosting_packages",
|
||||
),
|
||||
re_path(
|
||||
r"^(?P<user>[-\w0-9@.+_]+)/create$",
|
||||
CreateCustomerHostingPackage.as_view(),
|
||||
name='create_customer_hosting_package'),
|
||||
url(r'^(?P<user>[-\w0-9@.+_]+)/(?P<pk>\d+)/$',
|
||||
name="create_customer_hosting_package",
|
||||
),
|
||||
re_path(
|
||||
r"^(?P<user>[-\w0-9@.+_]+)/(?P<pk>\d+)/$",
|
||||
CustomerHostingPackageDetails.as_view(),
|
||||
name='hosting_package_details'),
|
||||
url(r'^(?P<pk>\d+)/option-choices$',
|
||||
HostingOptionChoices.as_view(), name='hosting_option_choices'),
|
||||
url(r'^(?P<package>\d+)/add-option/(?P<type>\w+)/(?P<optionid>\d+)$',
|
||||
AddHostingOption.as_view(), name='add_hosting_option'),
|
||||
name="hosting_package_details",
|
||||
),
|
||||
re_path(
|
||||
r"^(?P<pk>\d+)/option-choices$",
|
||||
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",
|
||||
),
|
||||
]
|
||||
|
|
|
@ -2,28 +2,17 @@
|
|||
This module defines views related to hosting packages.
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
from __future__ import absolute_import
|
||||
|
||||
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.auth import get_user_model
|
||||
|
||||
from braces.views import (
|
||||
LoginRequiredMixin,
|
||||
StaffuserRequiredMixin,
|
||||
)
|
||||
|
||||
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.translation import gettext as _
|
||||
from django.views.generic import DetailView, ListView
|
||||
from django.views.generic.edit import CreateView, FormView
|
||||
from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin
|
||||
|
||||
from .forms import (
|
||||
|
@ -41,26 +30,27 @@ from .models import (
|
|||
)
|
||||
|
||||
|
||||
class CreateHostingPackage(
|
||||
LoginRequiredMixin, StaffuserRequiredMixin, CreateView
|
||||
):
|
||||
class CreateHostingPackage(PermissionRequiredMixin, CreateView):
|
||||
"""
|
||||
Create a hosting package.
|
||||
|
||||
"""
|
||||
|
||||
model = CustomerHostingPackage
|
||||
raise_exception = True
|
||||
template_name_suffix = '_create'
|
||||
permission_required = "domains.add_customerhostingpackage"
|
||||
template_name_suffix = "_create"
|
||||
form_class = CreateHostingPackageForm
|
||||
|
||||
def form_valid(self, form):
|
||||
hostingpackage = form.save()
|
||||
hosting_package = form.save()
|
||||
messages.success(
|
||||
self.request,
|
||||
_('Started setup of new hosting package {name}.').format(
|
||||
name=hostingpackage.name)
|
||||
_("Started setup of new hosting package {name}.").format(
|
||||
name=hosting_package.name
|
||||
),
|
||||
)
|
||||
return redirect(hostingpackage)
|
||||
return redirect(hosting_package)
|
||||
|
||||
|
||||
class CreateCustomerHostingPackage(CreateHostingPackage):
|
||||
|
@ -68,6 +58,7 @@ class CreateCustomerHostingPackage(CreateHostingPackage):
|
|||
Create a hosting package for a selected customer.
|
||||
|
||||
"""
|
||||
|
||||
form_class = CreateCustomerHostingPackageForm
|
||||
|
||||
def get_form_kwargs(self):
|
||||
|
@ -76,25 +67,24 @@ class CreateCustomerHostingPackage(CreateHostingPackage):
|
|||
return kwargs
|
||||
|
||||
def get_customer_object(self):
|
||||
return get_object_or_404(
|
||||
get_user_model(), username=self.kwargs['user'])
|
||||
return get_object_or_404(get_user_model(), username=self.kwargs["user"])
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(
|
||||
CreateCustomerHostingPackage, self).get_context_data(**kwargs)
|
||||
context['customer'] = self.get_customer_object()
|
||||
context = super(CreateCustomerHostingPackage, self).get_context_data(**kwargs)
|
||||
context["customer"] = self.get_customer_object()
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
hostingpackage = form.save(commit=False)
|
||||
hostingpackage.customer = self.get_customer_object()
|
||||
hostingpackage.save()
|
||||
hosting_package = form.save(commit=False)
|
||||
hosting_package.customer = self.get_customer_object()
|
||||
hosting_package.save()
|
||||
messages.success(
|
||||
self.request,
|
||||
_('Started setup of new hosting package {name}.').format(
|
||||
name=hostingpackage.name)
|
||||
_("Started setup of new hosting package {name}.").format(
|
||||
name=hosting_package.name
|
||||
),
|
||||
)
|
||||
return redirect(hostingpackage)
|
||||
return redirect(hosting_package)
|
||||
|
||||
|
||||
class CustomerHostingPackageDetails(StaffOrSelfLoginRequiredMixin, DetailView):
|
||||
|
@ -102,156 +92,161 @@ class CustomerHostingPackageDetails(StaffOrSelfLoginRequiredMixin, DetailView):
|
|||
This view is for showing details of a customer hosting package.
|
||||
|
||||
"""
|
||||
|
||||
model = CustomerHostingPackage
|
||||
context_object_name = 'hostingpackage'
|
||||
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'])
|
||||
get_user_model(), username=self.kwargs["user"]
|
||||
)
|
||||
return self.customer
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(CustomerHostingPackageDetails, self).get_context_data(
|
||||
**kwargs)
|
||||
context.update({
|
||||
'customer': self.get_customer_object(),
|
||||
'uploadserver': settings.OSUSER_UPLOAD_SERVER,
|
||||
'databases': context['hostingpackage'].databases,
|
||||
'osuser': context['hostingpackage'].osuser,
|
||||
'hostingoptions':
|
||||
context['hostingpackage'].get_hostingoptions(),
|
||||
'domains': context['hostingpackage'].domains.all(),
|
||||
'mailboxes': context['hostingpackage'].mailboxes,
|
||||
})
|
||||
context['sshkeys'] = context['osuser'].sshpublickey_set.all()
|
||||
context = super(CustomerHostingPackageDetails, self).get_context_data(**kwargs)
|
||||
context.update(
|
||||
{
|
||||
"customer": self.get_customer_object(),
|
||||
"uploadserver": settings.OSUSER_UPLOAD_SERVER,
|
||||
"databases": context["hostingpackage"].databases,
|
||||
"osuser": context["hostingpackage"].osuser,
|
||||
"hostingoptions": context["hostingpackage"].get_hostingoptions(),
|
||||
"domains": context["hostingpackage"].domains.all(),
|
||||
"mailboxes": context["hostingpackage"].mailboxes,
|
||||
}
|
||||
)
|
||||
context["sshkeys"] = context["osuser"].sshpublickey_set.all()
|
||||
return context
|
||||
|
||||
|
||||
class AllCustomerHostingPackageList(
|
||||
LoginRequiredMixin, StaffuserRequiredMixin, ListView
|
||||
):
|
||||
class StaffUserRequiredMixin(UserPassesTestMixin):
|
||||
"""
|
||||
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.
|
||||
|
||||
"""
|
||||
|
||||
model = CustomerHostingPackage
|
||||
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
|
||||
template_name_suffix = "_admin_list"
|
||||
|
||||
def get_queryset(self):
|
||||
return super(CustomerHostingPackageList, self).get_queryset().filter(
|
||||
customer__username=self.kwargs['user'])
|
||||
return (
|
||||
super()
|
||||
.get_queryset()
|
||||
.select_related("osuser", "customer")
|
||||
.only("name", "pk", "created", "customer__username", "osuser__username")
|
||||
)
|
||||
|
||||
|
||||
class HostingOptionChoices(
|
||||
LoginRequiredMixin, StaffuserRequiredMixin, DetailView
|
||||
):
|
||||
class HostingOptionChoices(StaffUserRequiredMixin, DetailView):
|
||||
"""
|
||||
This view displays choices of hosting options for a customer hosting
|
||||
package.
|
||||
|
||||
"""
|
||||
|
||||
model = CustomerHostingPackage
|
||||
context_object_name = 'hostingpackage'
|
||||
template_name_suffix = '_option_choices'
|
||||
context_object_name = "hostingpackage"
|
||||
template_name_suffix = "_option_choices"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(HostingOptionChoices, self).get_context_data(
|
||||
**kwargs)
|
||||
context.update({
|
||||
'customer': self.get_object().customer,
|
||||
'hosting_options': (
|
||||
(_('Disk space'),
|
||||
[(option, 'diskspace') for option in
|
||||
DiskSpaceOption.objects.all()]),
|
||||
(_('Mailboxes'),
|
||||
[(option, 'mailboxes') for option in
|
||||
MailboxOption.objects.all()]),
|
||||
(_('Databases'),
|
||||
[(option, 'databases') for option in
|
||||
UserDatabaseOption.objects.all()]),
|
||||
),
|
||||
})
|
||||
context = super(HostingOptionChoices, self).get_context_data(**kwargs)
|
||||
context.update(
|
||||
{
|
||||
"customer": self.get_object().customer,
|
||||
"hosting_options": (
|
||||
(
|
||||
_("Disk space"),
|
||||
[
|
||||
(option, "diskspace")
|
||||
for option in DiskSpaceOption.objects.all()
|
||||
],
|
||||
),
|
||||
(
|
||||
_("Mailboxes"),
|
||||
[
|
||||
(option, "mailboxes")
|
||||
for option in MailboxOption.objects.all()
|
||||
],
|
||||
),
|
||||
(
|
||||
_("Databases"),
|
||||
[
|
||||
(option, "databases")
|
||||
for option in UserDatabaseOption.objects.all()
|
||||
],
|
||||
),
|
||||
),
|
||||
}
|
||||
)
|
||||
return context
|
||||
|
||||
|
||||
class AddHostingOption(
|
||||
LoginRequiredMixin, StaffuserRequiredMixin, FormView
|
||||
):
|
||||
template_name = 'hostingpackages/add_hosting_option.html'
|
||||
class AddHostingOption(StaffUserRequiredMixin, FormView):
|
||||
template_name = "hostingpackages/add_hosting_option.html"
|
||||
|
||||
def get_form_class(self):
|
||||
optiontype = self.kwargs['type']
|
||||
if optiontype == 'diskspace':
|
||||
optiontype = self.kwargs["type"]
|
||||
if optiontype == "diskspace":
|
||||
return AddDiskspaceOptionForm
|
||||
elif optiontype == 'mailboxes':
|
||||
elif optiontype == "mailboxes":
|
||||
return AddMailboxOptionForm
|
||||
elif optiontype == 'databases':
|
||||
elif optiontype == "databases":
|
||||
return AddUserDatabaseOptionForm
|
||||
raise Http404()
|
||||
|
||||
def get_hosting_package(self):
|
||||
return get_object_or_404(
|
||||
CustomerHostingPackage, pk=int(self.kwargs['package']))
|
||||
return get_object_or_404(CustomerHostingPackage, pk=int(self.kwargs["package"]))
|
||||
|
||||
def get_option_template(self):
|
||||
optiontype = self.kwargs['type']
|
||||
optionid = int(self.kwargs['optionid'])
|
||||
if optiontype == 'diskspace':
|
||||
optiontype = self.kwargs["type"]
|
||||
optionid = int(self.kwargs["optionid"])
|
||||
if optiontype == "diskspace":
|
||||
return get_object_or_404(DiskSpaceOption, pk=optionid)
|
||||
elif optiontype == 'mailboxes':
|
||||
elif optiontype == "mailboxes":
|
||||
return get_object_or_404(MailboxOption, pk=optionid)
|
||||
elif optiontype == 'databases':
|
||||
elif optiontype == "databases":
|
||||
return get_object_or_404(UserDatabaseOption, pk=optionid)
|
||||
raise Http404()
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super(AddHostingOption, self).get_form_kwargs()
|
||||
kwargs['hostingpackage'] = self.get_hosting_package()
|
||||
kwargs['option_template'] = self.get_option_template()
|
||||
kwargs["hostingpackage"] = self.get_hosting_package()
|
||||
kwargs["option_template"] = self.get_option_template()
|
||||
return kwargs
|
||||
|
||||
def get_initial(self):
|
||||
initial = super(AddHostingOption, self).get_initial()
|
||||
template = self.get_option_template()
|
||||
if type(template) == DiskSpaceOption:
|
||||
initial.update({
|
||||
'diskspace': template.diskspace,
|
||||
'diskspace_unit': template.diskspace_unit,
|
||||
})
|
||||
initial.update(
|
||||
{
|
||||
"diskspace": template.diskspace,
|
||||
"diskspace_unit": template.diskspace_unit,
|
||||
}
|
||||
)
|
||||
elif type(template) == MailboxOption:
|
||||
initial['number'] = template.number
|
||||
initial["number"] = template.number
|
||||
elif type(template) == UserDatabaseOption:
|
||||
initial['number'] = template.number
|
||||
initial["number"] = template.number
|
||||
else:
|
||||
raise Http404()
|
||||
return initial
|
||||
|
||||
def get_context_data(self, **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
|
||||
|
||||
def form_valid(self, form):
|
||||
|
@ -259,8 +254,8 @@ class AddHostingOption(
|
|||
hosting_package = self.get_hosting_package()
|
||||
messages.success(
|
||||
self.request,
|
||||
_("Successfully added option {option} to hosting package "
|
||||
"{package}.").format(
|
||||
option=option, package=hosting_package.name)
|
||||
_(
|
||||
"Successfully added option {option} to hosting package " "{package}."
|
||||
).format(option=option, package=hosting_package.name),
|
||||
)
|
||||
return redirect(hosting_package)
|
||||
|
|
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 ugettext_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-04-23 14:35+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"
|
||||
|
||||
#: invoice/apps.py:8
|
||||
msgid "Invoices"
|
||||
msgstr "Rechnungen"
|
||||
|
||||
#: invoice/models.py:26
|
||||
msgid "customer"
|
||||
msgstr "Kunde"
|
||||
|
||||
#: invoice/models.py:33
|
||||
msgid "invoice number"
|
||||
msgstr "Rechnungsnummer"
|
||||
|
||||
#: invoice/models.py:35
|
||||
msgid "invoice date"
|
||||
msgstr "Rechnungsdatum"
|
||||
|
||||
#: invoice/models.py:37
|
||||
msgid "amount"
|
||||
msgstr "Betrag"
|
||||
|
||||
#: invoice/models.py:40
|
||||
msgid "currency"
|
||||
msgstr "Währung"
|
||||
|
||||
#: invoice/models.py:42
|
||||
msgid "due date"
|
||||
msgstr "Fälligkeit"
|
||||
|
||||
#: invoice/models.py:44
|
||||
msgid "payment date"
|
||||
msgstr "Zahlungsdatum"
|
||||
|
||||
#: invoice/models.py:47
|
||||
msgid "payment variant"
|
||||
msgstr "Zahlungsart"
|
||||
|
||||
#: invoice/models.py:51
|
||||
msgid "invoice"
|
||||
msgstr "Rechnung"
|
||||
|
||||
#: invoice/models.py:52
|
||||
msgid "invoices"
|
||||
msgstr "Rechnungen"
|
||||
|
||||
#: invoice/models.py:56
|
||||
#, python-brace-format
|
||||
msgid "Invoice {0}"
|
||||
msgstr "Rechnung {0}"
|
|
@ -0,0 +1,38 @@
|
|||
# Generated by Django 3.2.18 on 2023-04-23 10:37
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import invoices.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Invoice',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('invoice', models.FileField(upload_to=invoices.models.customer_invoice_path)),
|
||||
('invoice_number', models.SlugField(max_length=10, unique=True, verbose_name='Invoice number')),
|
||||
('invoice_date', models.DateField(verbose_name='Invoice date')),
|
||||
('invoice_value', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Amount')),
|
||||
('invoice_currency', models.PositiveSmallIntegerField(choices=[(1, 'EUR')], verbose_name='Currency')),
|
||||
('due_date', models.DateField(verbose_name='Due date')),
|
||||
('payment_date', models.DateField(blank=True, null=True, verbose_name='Payment date')),
|
||||
('payment_variant', models.TextField(blank=True, null=True, verbose_name='Payment variant')),
|
||||
('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='customer')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Invoice',
|
||||
'verbose_name_plural': 'Invoices',
|
||||
'ordering': ['-invoice_date', 'customer'],
|
||||
},
|
||||
),
|
||||
]
|
|
@ -0,0 +1,59 @@
|
|||
# Generated by Django 3.2.18 on 2023-04-23 12:15
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import invoices.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('invoices', '0001_initial_invoice_model'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='invoice',
|
||||
options={'ordering': ['-invoice_date', 'customer'], 'verbose_name': 'invoice', 'verbose_name_plural': 'invoices'},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='invoice',
|
||||
name='due_date',
|
||||
field=models.DateField(verbose_name='due date'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='invoice',
|
||||
name='invoice',
|
||||
field=models.FileField(upload_to=invoices.models.customer_invoice_path, validators=[invoices.models.validate_pdf, django.core.validators.FileExtensionValidator(allowed_extensions=['pdf'])]),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='invoice',
|
||||
name='invoice_currency',
|
||||
field=models.PositiveSmallIntegerField(choices=[(1, 'EUR')], verbose_name='currency'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='invoice',
|
||||
name='invoice_date',
|
||||
field=models.DateField(verbose_name='invoice date'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='invoice',
|
||||
name='invoice_number',
|
||||
field=models.SlugField(max_length=10, unique=True, verbose_name='invoice number'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='invoice',
|
||||
name='invoice_value',
|
||||
field=models.DecimalField(decimal_places=2, max_digits=10, verbose_name='amount'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='invoice',
|
||||
name='payment_date',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='payment date'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='invoice',
|
||||
name='payment_variant',
|
||||
field=models.TextField(blank=True, null=True, verbose_name='payment variant'),
|
||||
),
|
||||
]
|
0
gnuviechadmin/invoices/migrations/__init__.py
Normal file
0
gnuviechadmin/invoices/migrations/__init__.py
Normal file
56
gnuviechadmin/invoices/models.py
Normal file
56
gnuviechadmin/invoices/models.py
Normal file
|
@ -0,0 +1,56 @@
|
|||
import magic
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.validators import FileExtensionValidator
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
CURRENCIES = [(1, "EUR")]
|
||||
|
||||
|
||||
def customer_invoice_path(instance, filename):
|
||||
return "invoices/{0}/{1}.pdf".format(
|
||||
instance.customer.username, instance.invoice_number
|
||||
)
|
||||
|
||||
|
||||
def validate_pdf(value):
|
||||
valid_mime_types = ["application/pdf"]
|
||||
file_mime_type = magic.from_buffer(value.read(1024), mime=True)
|
||||
if file_mime_type not in valid_mime_types:
|
||||
raise ValidationError("Unsupported file type.")
|
||||
|
||||
|
||||
class Invoice(models.Model):
|
||||
customer = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL, verbose_name=_("customer"), on_delete=models.CASCADE
|
||||
)
|
||||
invoice = models.FileField(
|
||||
upload_to=customer_invoice_path,
|
||||
validators=[validate_pdf, FileExtensionValidator(allowed_extensions=["pdf"])],
|
||||
)
|
||||
invoice_number = models.SlugField(
|
||||
verbose_name=_("invoice number"), max_length=10, unique=True
|
||||
)
|
||||
invoice_date = models.DateField(verbose_name=_("invoice date"))
|
||||
invoice_value = models.DecimalField(
|
||||
verbose_name=_("amount"), decimal_places=2, max_digits=10
|
||||
)
|
||||
invoice_currency = models.PositiveSmallIntegerField(
|
||||
verbose_name=_("currency"), choices=CURRENCIES
|
||||
)
|
||||
due_date = models.DateField(verbose_name=_("due date"))
|
||||
payment_date = models.DateField(
|
||||
verbose_name=_("payment date"), blank=True, null=True
|
||||
)
|
||||
payment_variant = models.TextField(
|
||||
verbose_name=_("payment variant"), blank=True, null=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("invoice")
|
||||
verbose_name_plural = _("invoices")
|
||||
ordering = ["-invoice_date", "customer"]
|
||||
|
||||
def __str__(self):
|
||||
return _("Invoice {0}").format(self.invoice_number)
|
33
gnuviechadmin/invoices/serializers.py
Normal file
33
gnuviechadmin/invoices/serializers.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
from django.contrib.auth import get_user_model
|
||||
from rest_framework import serializers
|
||||
|
||||
from invoices.models import Invoice
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class InvoiceSerializer(serializers.ModelSerializer):
|
||||
customer = serializers.SlugRelatedField(
|
||||
queryset=User.objects.all(), slug_field="username"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Invoice
|
||||
fields = [
|
||||
"url",
|
||||
"customer",
|
||||
"invoice",
|
||||
"invoice_number",
|
||||
"invoice_date",
|
||||
"invoice_value",
|
||||
"invoice_currency",
|
||||
"due_date",
|
||||
"payment_date",
|
||||
"payment_variant",
|
||||
]
|
||||
extra_kwargs = {
|
||||
"url": {
|
||||
"lookup_field": "invoice_number",
|
||||
"lookup_url_kwarg": "invoice_number",
|
||||
},
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue