Compare commits
	
		
			No commits in common. "4577ec4896fef2f2d697478857ed3cc6dfb539f6" and "df3628499d4e6bdb88289a161ad1f08590bbbe17" have entirely different histories.
		
	
	
		
			4577ec4896
			...
			df3628499d
		
	
		
					 270 changed files with 8659 additions and 17948 deletions
				
			
		|  | @ -1,18 +0,0 @@ | ||||||
| **/*.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,11 +50,9 @@ coverage-report/ | ||||||
| .idea/ | .idea/ | ||||||
| 
 | 
 | ||||||
| .env | .env | ||||||
| .envrc |  | ||||||
| 
 | 
 | ||||||
| /docker/django_media | /docker/django_media | ||||||
| /docker/django_static | /docker/django_static | ||||||
| !/docker/django_media/.empty | !/docker/django_media/.empty | ||||||
| !/docker/django_static/.empty | !/docker/django_static/.empty | ||||||
| /media/ |  | ||||||
| /static/ | /static/ | ||||||
|  |  | ||||||
|  | @ -1,7 +0,0 @@ | ||||||
| [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 |  | ||||||
							
								
								
									
										86
									
								
								Dockerfile
									
										
									
									
									
								
							
							
						
						
									
										86
									
								
								Dockerfile
									
										
									
									
									
								
							|  | @ -1,70 +1,56 @@ | ||||||
| ARG DEBIAN_RELEASE=buster | 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 | FROM debian:$DEBIAN_RELEASE | ||||||
| LABEL maintainer="Jan Dittberner <jan@dittberner.info>" | LABEL maintainer="Jan Dittberner <jan@dittberner.info>" | ||||||
| 
 | 
 | ||||||
| ENV LC_ALL=C.UTF-8 | ENV LC_ALL=C.UTF-8 | ||||||
| ENV LANG=C.UTF-8 | ENV LANG=C.UTF-8 | ||||||
| ENV DEBIAN_FRONTEND=noninteractive |  | ||||||
| 
 | 
 | ||||||
| RUN apt-get update \ | RUN apt-get update \ | ||||||
|     && apt-get install -y --no-install-recommends \ |     && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ | ||||||
|        ca-certificates \ |     build-essential \ | ||||||
|        dumb-init \ |     dumb-init \ | ||||||
|        gettext \ |     gettext \ | ||||||
|        postgresql-client \ |     git \ | ||||||
|        python3 \ |     python3-dev \ | ||||||
|        python3-pip \ |     python3-pip \ | ||||||
|        python3-wheel \ |     python3-setuptools \ | ||||||
|  |     python3-virtualenv \ | ||||||
|  |     python3-wheel \ | ||||||
|     && apt-get clean \ |     && apt-get clean \ | ||||||
|     && rm -rf /var/cache/apt/archives /var/lib/apt/lists/* |     && 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/*.* | ||||||
| 
 | 
 | ||||||
| ARG GVAAPP=gva |  | ||||||
| ARG GVAGID=2000 | ARG GVAGID=2000 | ||||||
| ARG GVAUID=2000 | ARG GVAUID=2000 | ||||||
| 
 | 
 | ||||||
| RUN addgroup --gid $GVAGID $GVAAPP ; \ | ARG GVAAPP=gva | ||||||
|     adduser --home /home/$GVAAPP --shell /bin/bash --uid $GVAUID --gid $GVAGID --disabled-password \ |  | ||||||
|             --gecos "User for gnuviechadmin component $GVAAPP" $GVAAPP |  | ||||||
| 
 |  | ||||||
| COPY --chown=$GVAAPP:$GVAAPP --from=builder /srv/$GVAAPP/.venv /srv/$GVAAPP/.venv |  | ||||||
| 
 |  | ||||||
| WORKDIR /srv/$GVAAPP |  | ||||||
| 
 | 
 | ||||||
| VOLUME /srv/$GVAAPP/media /srv/$GVAAPP/static | VOLUME /srv/$GVAAPP/media /srv/$GVAAPP/static | ||||||
| 
 | 
 | ||||||
| VOLUME /srv/$GVAAPP/gnuviechadmin | WORKDIR /srv/$GVAAPP | ||||||
|  | 
 | ||||||
|  | COPY Pipfile Pipfile.lock /srv/$GVAAPP/ | ||||||
|  | 
 | ||||||
|  | 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 | ||||||
| 
 | 
 | ||||||
| EXPOSE 8000 | EXPOSE 8000 | ||||||
| 
 | 
 | ||||||
| COPY ${GVAAPP}.sh entrypoint.sh /srv/ | COPY gva.sh /srv/ | ||||||
| 
 | 
 | ||||||
| ENTRYPOINT ["dumb-init", "/srv/entrypoint.sh"] | ENTRYPOINT ["dumb-init", "/srv/gva.sh"] | ||||||
|  |  | ||||||
							
								
								
									
										33
									
								
								Pipfile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								Pipfile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | ||||||
|  | [[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
									
									
									
										Normal file
									
								
							
							
						
						
									
										617
									
								
								Pipfile.lock
									
										
									
										generated
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,617 @@ | ||||||
|  | { | ||||||
|  |     "_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,4 +1,3 @@ | ||||||
| --- |  | ||||||
| version: "3" | version: "3" | ||||||
| services: | services: | ||||||
|   db: |   db: | ||||||
|  | @ -37,9 +36,9 @@ services: | ||||||
|       GVA_DOMAIN_NAME: localhost |       GVA_DOMAIN_NAME: localhost | ||||||
|       GVA_SITE_NAME: localhost |       GVA_SITE_NAME: localhost | ||||||
|     volumes: |     volumes: | ||||||
|       - "django_media:/srv/gva/media" |       - "./docker/django_media:/srv/gva/media" | ||||||
|       - "django_static:/srv/gva/static" |       - "./docker/django_static:/srv/gva/static" | ||||||
|       - "./gnuviechadmin:/srv/gva/gnuviechadmin" |       - ".:/srv/gva" | ||||||
|   web: |   web: | ||||||
|     image: gnuviech/gvaweb:buster |     image: gnuviech/gvaweb:buster | ||||||
|     build: |     build: | ||||||
|  | @ -52,7 +51,7 @@ services: | ||||||
|       - redis |       - redis | ||||||
|     env_file: ../gvaweb/.env |     env_file: ../gvaweb/.env | ||||||
|     volumes: |     volumes: | ||||||
|       - "../gvaweb/gvaweb:/srv/gvaweb/gvaweb" |       - "../gvaweb:/srv/gvaweb" | ||||||
|   ldap: |   ldap: | ||||||
|     image: gnuviech/gvaldap:buster |     image: gnuviech/gvaldap:buster | ||||||
|     build: |     build: | ||||||
|  | @ -65,7 +64,7 @@ services: | ||||||
|       - redis |       - redis | ||||||
|     env_file: ../gvaldap/.env |     env_file: ../gvaldap/.env | ||||||
|     volumes: |     volumes: | ||||||
|       - "../gvaldap/gvaldap:/srv/gvaldap/gvaldap" |       - "../gvaldap:/srv/gvaldap" | ||||||
|   file: |   file: | ||||||
|     image: gnuviech/gvafile:buster |     image: gnuviech/gvafile:buster | ||||||
|     build: |     build: | ||||||
|  | @ -78,7 +77,7 @@ services: | ||||||
|       - redis |       - redis | ||||||
|     env_file: ../gvafile/.env |     env_file: ../gvafile/.env | ||||||
|     volumes: |     volumes: | ||||||
|       - "../gvafile/gvafile:/srv/gvafile/gvafile" |       - "../gvafile:/srv/gvafile" | ||||||
|   pgsql: |   pgsql: | ||||||
|     image: gnuviech/gvapgsql:buster |     image: gnuviech/gvapgsql:buster | ||||||
|     build: |     build: | ||||||
|  | @ -91,7 +90,7 @@ services: | ||||||
|       - redis |       - redis | ||||||
|     env_file: ../gvapgsql/.env |     env_file: ../gvapgsql/.env | ||||||
|     volumes: |     volumes: | ||||||
|       - "../gvapgsql/gvapgsql:/srv/gvapgsql/gvapgsql" |       - "../gvapgsql:/srv/gvapgsql" | ||||||
|   mysql: |   mysql: | ||||||
|     image: gnuviech/gvamysql:buster |     image: gnuviech/gvamysql:buster | ||||||
|     build: |     build: | ||||||
|  | @ -104,7 +103,7 @@ services: | ||||||
|       - redis |       - redis | ||||||
|     env_file: ../gvamysql/.env |     env_file: ../gvamysql/.env | ||||||
|     volumes: |     volumes: | ||||||
|       - "../gvamysql/gvamysql:/srv/gvamysql/gvamysql" |       - "../gvamysql:/srv/gvamysql" | ||||||
| volumes: | volumes: | ||||||
|   django_media: |   django_media: | ||||||
|   django_static: |   django_static: | ||||||
|  |  | ||||||
|  | @ -1,16 +1,6 @@ | ||||||
| Changelog | 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>` | * :release:`0.12.1 <2020-04-13>` | ||||||
| * :bug:`7` fix handling of undefined mail domains in customer hosting package | * :bug:`7` fix handling of undefined mail domains in customer hosting package | ||||||
|   detail template |   detail template | ||||||
|  |  | ||||||
|  | @ -1,7 +0,0 @@ | ||||||
| #!/bin/sh |  | ||||||
| 
 |  | ||||||
| set -e |  | ||||||
| 
 |  | ||||||
| chown -Rc gva.gva /srv/gva/media /srv/gva/static |  | ||||||
| 
 |  | ||||||
| su -c /srv/gva.sh gva |  | ||||||
							
								
								
									
										1
									
								
								frontend/.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								frontend/.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -1 +0,0 @@ | ||||||
| node_modules/ |  | ||||||
							
								
								
									
										46
									
								
								frontend/package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										46
									
								
								frontend/package-lock.json
									
										
									
										generated
									
									
									
								
							|  | @ -1,46 +0,0 @@ | ||||||
| { |  | ||||||
|   "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==" |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | @ -1,6 +0,0 @@ | ||||||
| { |  | ||||||
|   "dependencies": { |  | ||||||
|     "bootstrap": "^5.2.3", |  | ||||||
|     "bootstrap-icons": "^1.10.4" |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | @ -4,16 +4,19 @@ This module contains the form class for the contact_form app. | ||||||
| """ | """ | ||||||
| from __future__ import absolute_import, unicode_literals | from __future__ import absolute_import, unicode_literals | ||||||
| 
 | 
 | ||||||
| from crispy_forms.helper import FormHelper |  | ||||||
| from crispy_forms.layout import Submit |  | ||||||
| from django import forms | from django import forms | ||||||
| from django.conf import settings | 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.models import Site | ||||||
| from django.contrib.sites.requests import RequestSite | from django.contrib.sites.requests import RequestSite | ||||||
| from django.core.mail import send_mail | 
 | ||||||
| from django.template import RequestContext, loader | from crispy_forms.helper import FormHelper | ||||||
| from django.urls import reverse | from crispy_forms.layout import Submit | ||||||
| from django.utils.translation import gettext_lazy as _ |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class ContactForm(forms.Form): | class ContactForm(forms.Form): | ||||||
|  | @ -21,42 +24,45 @@ class ContactForm(forms.Form): | ||||||
|     This is the contact form class. |     This is the contact form class. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |     name = forms.CharField(max_length=100, label=_('Your name')) | ||||||
|     name = forms.CharField(max_length=100, label=_("Your name")) |     email = forms.EmailField(max_length=200, label=_('Your email address')) | ||||||
|     email = forms.EmailField(max_length=200, label=_("Your email address")) |     body = forms.CharField(widget=forms.Textarea, label=_('Your message')) | ||||||
|     body = forms.CharField(widget=forms.Textarea, label=_("Your message")) |  | ||||||
| 
 | 
 | ||||||
|     subject_template_name = "contact_form/contact_form_subject.txt" |     subject_template_name = "contact_form/contact_form_subject.txt" | ||||||
|     template_name = "contact_form/contact_form.txt" |     template_name = 'contact_form/contact_form.txt' | ||||||
|     from_email = settings.DEFAULT_FROM_EMAIL |     from_email = settings.DEFAULT_FROM_EMAIL | ||||||
|     recipient_list = [mail_tuple[1] for mail_tuple in settings.MANAGERS] |     recipient_list = [mail_tuple[1] for mail_tuple in settings.MANAGERS] | ||||||
| 
 | 
 | ||||||
|     def __init__(self, **kwargs): |     def __init__(self, **kwargs): | ||||||
|         self.request = kwargs.pop("request") |         self.request = kwargs.pop('request') | ||||||
|         super(ContactForm, self).__init__(**kwargs) |         super(ContactForm, self).__init__(**kwargs) | ||||||
|         self.helper = FormHelper() |         self.helper = FormHelper() | ||||||
|         self.helper.form_action = reverse("contact_form") |         self.helper.form_action = reverse('contact_form') | ||||||
|         self.helper.add_input(Submit("submit", _("Send message"))) |         self.helper.add_input(Submit('submit', _('Send message'))) | ||||||
| 
 | 
 | ||||||
|     def get_context(self): |     def get_context(self): | ||||||
|         if not self.is_valid(): |         if not self.is_valid(): | ||||||
|             raise ValueError("Cannot generate context from invalid contact form") |             raise ValueError( | ||||||
|  |                 'Cannot generate context from invalid contact form') | ||||||
|         if Site._meta.installed: |         if Site._meta.installed: | ||||||
|             site = Site.objects.get_current() |             site = Site.objects.get_current() | ||||||
|         else: |         else: | ||||||
|             site = RequestSite(self.request) |             site = RequestSite(self.request) | ||||||
|         return RequestContext(self.request, dict(self.cleaned_data, site=site)) |         return RequestContext( | ||||||
|  |             self.request, dict(self.cleaned_data, site=site)) | ||||||
| 
 | 
 | ||||||
|     def message(self): |     def message(self): | ||||||
|         context = self.get_context() |         context = self.get_context() | ||||||
|         template_context = context.flatten() |         template_context = context.flatten() | ||||||
|         template_context.update({"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) |         return loader.render_to_string(self.template_name, template_context) | ||||||
| 
 | 
 | ||||||
|     def subject(self): |     def subject(self): | ||||||
|         context = self.get_context().flatten() |         context = self.get_context().flatten() | ||||||
|         subject = loader.render_to_string(self.subject_template_name, context) |         subject = loader.render_to_string(self.subject_template_name, context) | ||||||
|         return "".join(subject.splitlines()) |         return ''.join(subject.splitlines()) | ||||||
| 
 | 
 | ||||||
|     def save(self, fail_silently=False): |     def save(self, fail_silently=False): | ||||||
|         """ |         """ | ||||||
|  | @ -68,5 +74,5 @@ class ContactForm(forms.Form): | ||||||
|             from_email=self.from_email, |             from_email=self.from_email, | ||||||
|             recipient_list=self.recipient_list, |             recipient_list=self.recipient_list, | ||||||
|             subject=self.subject(), |             subject=self.subject(), | ||||||
|             message=self.message(), |             message=self.message() | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  | @ -7,8 +7,8 @@ msgid "" | ||||||
| msgstr "" | msgstr "" | ||||||
| "Project-Id-Version: contact_form\n" | "Project-Id-Version: contact_form\n" | ||||||
| "Report-Msgid-Bugs-To: \n" | "Report-Msgid-Bugs-To: \n" | ||||||
| "POT-Creation-Date: 2023-04-22 13:01+0200\n" | "POT-Creation-Date: 2016-01-29 11:04+0100\n" | ||||||
| "PO-Revision-Date: 2023-04-22 13:01+0200\n" | "PO-Revision-Date: 2015-02-01 19:03+0100\n" | ||||||
| "Last-Translator: Jan Dittberner <jan@dittberner.info>\n" | "Last-Translator: Jan Dittberner <jan@dittberner.info>\n" | ||||||
| "Language-Team: Jan Dittberner <jan@dittberner.info>\n" | "Language-Team: Jan Dittberner <jan@dittberner.info>\n" | ||||||
| "Language: de\n" | "Language: de\n" | ||||||
|  | @ -16,32 +16,21 @@ msgstr "" | ||||||
| "Content-Type: text/plain; charset=UTF-8\n" | "Content-Type: text/plain; charset=UTF-8\n" | ||||||
| "Content-Transfer-Encoding: 8bit\n" | "Content-Transfer-Encoding: 8bit\n" | ||||||
| "Plural-Forms: nplurals=2; plural=(n != 1);\n" | "Plural-Forms: nplurals=2; plural=(n != 1);\n" | ||||||
| "X-Generator: Poedit 3.2.2\n" | "X-Generator: Poedit 1.6.10\n" | ||||||
| "X-Poedit-SourceCharset: UTF-8\n" | "X-Poedit-SourceCharset: UTF-8\n" | ||||||
| 
 | 
 | ||||||
| #: contact_form/forms.py:25 | #: contact_form/forms.py:27 | ||||||
| msgid "Your name" | msgid "Your name" | ||||||
| msgstr "Ihr Name" | msgstr "Ihr Name" | ||||||
| 
 | 
 | ||||||
| #: contact_form/forms.py:26 | #: contact_form/forms.py:28 | ||||||
| msgid "Your email address" | msgid "Your email address" | ||||||
| msgstr "Ihre E-Mail-Adresse" | msgstr "Ihre E-Mailadresse" | ||||||
| 
 | 
 | ||||||
| #: contact_form/forms.py:27 | #: contact_form/forms.py:29 | ||||||
| msgid "Your message" | msgid "Your message" | ||||||
| msgstr "Ihre Nachricht" | msgstr "Ihre Nachricht" | ||||||
| 
 | 
 | ||||||
| #: contact_form/forms.py:39 | #: contact_form/forms.py:41 | ||||||
| msgid "Send message" | msgid "Send message" | ||||||
| msgstr "Nachricht senden" | msgstr "Nachricht senden" | ||||||
| 
 |  | ||||||
| #: contact_form/templates/contact_form/contact_form.html:4 |  | ||||||
| #: contact_form/templates/contact_form/contact_form.html:5 |  | ||||||
| #: contact_form/templates/contact_form/contact_success.html:4 |  | ||||||
| #: contact_form/templates/contact_form/contact_success.html:5 |  | ||||||
| msgid "Contact" |  | ||||||
| msgstr "Kontakt" |  | ||||||
| 
 |  | ||||||
| #: contact_form/templates/contact_form/contact_success.html:8 |  | ||||||
| msgid "Your message has been sent successfully." |  | ||||||
| msgstr "Ihre Nachricht wurde erfolgreich übermittelt." |  | ||||||
|  |  | ||||||
|  | @ -1,23 +0,0 @@ | ||||||
| {% 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 %} |  | ||||||
|  | @ -1,9 +0,0 @@ | ||||||
| {% 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,13 +2,17 @@ | ||||||
| URL patterns for the contact_form views. | URL patterns for the contact_form views. | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
| from __future__ import absolute_import | from __future__ import absolute_import, unicode_literals | ||||||
| 
 | 
 | ||||||
| from django.urls import re_path | from django.conf.urls import url | ||||||
|  | 
 | ||||||
|  | from .views import ( | ||||||
|  |     ContactFormView, | ||||||
|  |     ContactSuccessView, | ||||||
|  | ) | ||||||
| 
 | 
 | ||||||
| from .views import ContactFormView, ContactSuccessView |  | ||||||
| 
 | 
 | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
|     re_path(r"^$", ContactFormView.as_view(), name="contact_form"), |     url(r'^$', ContactFormView.as_view(), name='contact_form'), | ||||||
|     re_path(r"^success/$", ContactSuccessView.as_view(), name="contact_success"), |     url(r'^success/$', ContactSuccessView.as_view(), name='contact_success'), | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | @ -2,11 +2,14 @@ | ||||||
| This module defines the views of the contact_form app. | This module defines the views of the contact_form app. | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
| from __future__ import absolute_import | from __future__ import absolute_import, unicode_literals | ||||||
| 
 | 
 | ||||||
| from django.shortcuts import redirect | from django.shortcuts import redirect | ||||||
| from django.urls import reverse_lazy | from django.urls import reverse_lazy | ||||||
| from django.views.generic import FormView, TemplateView | from django.views.generic import ( | ||||||
|  |     FormView, | ||||||
|  |     TemplateView, | ||||||
|  | ) | ||||||
| 
 | 
 | ||||||
| from .forms import ContactForm | from .forms import ContactForm | ||||||
| 
 | 
 | ||||||
|  | @ -16,22 +19,22 @@ class ContactFormView(FormView): | ||||||
|     This is the contact form view. |     This is the contact form view. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |  | ||||||
|     form_class = ContactForm |     form_class = ContactForm | ||||||
|     template_name = "contact_form/contact_form.html" |     template_name = 'contact_form/contact_form.html' | ||||||
|     success_url = reverse_lazy("contact_success") |     success_url = reverse_lazy('contact_success') | ||||||
| 
 | 
 | ||||||
|     def get_form_kwargs(self, **kwargs): |     def get_form_kwargs(self, **kwargs): | ||||||
|         kwargs = super(ContactFormView, self).get_form_kwargs(**kwargs) |         kwargs = super(ContactFormView, self).get_form_kwargs(**kwargs) | ||||||
|         kwargs["request"] = self.request |         kwargs['request'] = self.request | ||||||
|         return kwargs |         return kwargs | ||||||
| 
 | 
 | ||||||
|     def get_initial(self): |     def get_initial(self): | ||||||
|         initial = super(ContactFormView, self).get_initial() |         initial = super(ContactFormView, self).get_initial() | ||||||
|         currentuser = self.request.user |         currentuser = self.request.user | ||||||
|         if currentuser.is_authenticated: |         if currentuser.is_authenticated: | ||||||
|             initial["name"] = currentuser.get_full_name() or currentuser.username |             initial['name'] = ( | ||||||
|             initial["email"] = currentuser.email |                 currentuser.get_full_name() or currentuser.username) | ||||||
|  |             initial['email'] = currentuser.email | ||||||
|         return initial |         return initial | ||||||
| 
 | 
 | ||||||
|     def form_valid(self, form): |     def form_valid(self, form): | ||||||
|  | @ -44,5 +47,4 @@ class ContactSuccessView(TemplateView): | ||||||
|     This view is shown after successful contact form sending. |     This view is shown after successful contact form sending. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |     template_name = 'contact_form/contact_success.html' | ||||||
|     template_name = "contact_form/contact_success.html" |  | ||||||
|  |  | ||||||
|  | @ -7,92 +7,18 @@ msgid "" | ||||||
| msgstr "" | msgstr "" | ||||||
| "Project-Id-Version: gnuviechadmin dashboard\n" | "Project-Id-Version: gnuviechadmin dashboard\n" | ||||||
| "Report-Msgid-Bugs-To: \n" | "Report-Msgid-Bugs-To: \n" | ||||||
| "POT-Creation-Date: 2023-04-16 22:07+0200\n" | "POT-Creation-Date: 2015-01-17 15:59+0100\n" | ||||||
| "PO-Revision-Date: 2023-04-16 18:31+0200\n" | "PO-Revision-Date: 2015-01-17 16:01+0100\n" | ||||||
| "Last-Translator: Jan Dittberner <jan@dittberner.info>\n" | "Last-Translator: Jan Dittberner <jan@dittberner.info>\n" | ||||||
| "Language-Team: Jan Dittberner <jan@dittberner.info>\n" | "Language-Team: Jan Dittberner <de@li.org>\n" | ||||||
| "Language: de\n" | "Language: de\n" | ||||||
| "MIME-Version: 1.0\n" | "MIME-Version: 1.0\n" | ||||||
| "Content-Type: text/plain; charset=UTF-8\n" | "Content-Type: text/plain; charset=UTF-8\n" | ||||||
| "Content-Transfer-Encoding: 8bit\n" | "Content-Transfer-Encoding: 8bit\n" | ||||||
| "Plural-Forms: nplurals=2; plural=(n != 1);\n" | "Plural-Forms: nplurals=2; plural=(n != 1);\n" | ||||||
| "X-Generator: Poedit 3.2.2\n" | "X-Generator: Poedit 1.6.10\n" | ||||||
| "X-Poedit-SourceCharset: UTF-8\n" | "X-Poedit-SourceCharset: UTF-8\n" | ||||||
| 
 | 
 | ||||||
| #: dashboard/templates/dashboard/index.html:3 | #: dashboard/views.py:43 | ||||||
| msgid "Welcome" | msgid "You are not allowed to view this page." | ||||||
| msgstr "Willkommen" | msgstr "Sie haben nicht die nötigen Berechtigungen um diese Seite zu sehen." | ||||||
| 
 |  | ||||||
| #: 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" |  | ||||||
|  |  | ||||||
|  | @ -1,47 +0,0 @@ | ||||||
| {% extends "base.html" %} |  | ||||||
| {% load i18n %} |  | ||||||
| {% block title %}{{ block.super }} - {% blocktranslate with full_name=request.user.get_full_name trimmed %} |  | ||||||
|     Dashboard for {{ full_name }} |  | ||||||
| {% endblocktranslate %}{% endblock title %} |  | ||||||
| {% block page_title %}{% blocktranslate with full_name=request.user.get_full_name trimmed %} |  | ||||||
|     Dashboard for {{ full_name }} |  | ||||||
| {% endblocktranslate %}{% endblock page_title %} |  | ||||||
| {% block content %} |  | ||||||
|     <h2>{% translate "Hosting packages" %}</h2> |  | ||||||
|     <div class="row"> |  | ||||||
|         <div class="col-12"> |  | ||||||
|             {% if hosting_packages %} |  | ||||||
|                 <table class="table"> |  | ||||||
|                     <thead> |  | ||||||
|                     <tr> |  | ||||||
|                         <th>{% translate "Name" %}</th> |  | ||||||
|                         <th>{% translate "Setup date" %}</th> |  | ||||||
|                         <th>{% translate "Actions" %}</th> |  | ||||||
|                     </tr> |  | ||||||
|                     </thead> |  | ||||||
|                     <tbody> |  | ||||||
|                     {% for package in hosting_packages %} |  | ||||||
|                         <tr> |  | ||||||
|                             <td><a href="{{ package.get_absolute_url }}" |  | ||||||
|                                    title="{% blocktranslate with packagename=package.name trimmed %} |  | ||||||
|                                            Show details for {{ packagename }} |  | ||||||
| {% endblocktranslate %}">{{ package.name }}</a> |  | ||||||
|                             </td> |  | ||||||
|                             <td>{{ package.created }}</td> |  | ||||||
|                             <td></td> |  | ||||||
|                         </tr> |  | ||||||
|                     {% endfor %} |  | ||||||
|                     </tbody> |  | ||||||
|                 </table> |  | ||||||
|             {% else %} |  | ||||||
|                 <p class="text-info"> |  | ||||||
|                     {% if user == object %}{% translate "You have no hosting packages yet." %}{% else %} |  | ||||||
|                         {% translate "This user has no hosting packages assigned yet." %}{% endif %}</p> |  | ||||||
|             {% endif %} |  | ||||||
|             {% if user.is_staff %} |  | ||||||
|                 <a href="{% url "create_customer_hosting_package" user=request.user.username %}" |  | ||||||
|                    class="btn btn-primary">{% translate "Add hosting package" %}</a> |  | ||||||
|             {% endif %} |  | ||||||
|         </div> |  | ||||||
|     </div> |  | ||||||
| {% endblock content %} |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| Tests for :py:mod:`dashboard.views`. | Tests for :py:mod:`dashboard.views`. | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
| from django import http | 
 | ||||||
| from django.contrib.auth import get_user_model | from django.contrib.auth import get_user_model | ||||||
| from django.test import TestCase | from django.test import TestCase | ||||||
| from django.urls import reverse | from django.urls import reverse | ||||||
|  | @ -13,35 +13,55 @@ TEST_USER = "test" | ||||||
| TEST_PASSWORD = "secret" | TEST_PASSWORD = "secret" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | class IndexViewTest(TestCase): | ||||||
|  |     def test_index_view(self): | ||||||
|  |         response = self.client.get(reverse("dashboard")) | ||||||
|  |         self.assertEqual(response.status_code, 200) | ||||||
|  |         self.assertTemplateUsed(response, "dashboard/index.html") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class UserDashboardViewTest(TestCase): | class UserDashboardViewTest(TestCase): | ||||||
|     def _create_test_user(self): |     def _create_test_user(self): | ||||||
|         self.user = User.objects.create(username=TEST_USER) |         self.user = User.objects.create(username=TEST_USER) | ||||||
|         self.user.set_password(TEST_PASSWORD) |         self.user.set_password(TEST_PASSWORD) | ||||||
|         self.user.save() |         self.user.save() | ||||||
| 
 | 
 | ||||||
|  |     def test_user_dashboard_view_no_user(self): | ||||||
|  |         response = self.client.get( | ||||||
|  |             reverse("customer_dashboard", kwargs={"slug": TEST_USER}) | ||||||
|  |         ) | ||||||
|  |         self.assertEqual(response.status_code, 404) | ||||||
|  | 
 | ||||||
|     def test_user_dashboard_view_anonymous(self): |     def test_user_dashboard_view_anonymous(self): | ||||||
|         User.objects.create(username=TEST_USER) |         User.objects.create(username=TEST_USER) | ||||||
|         response = self.client.get(reverse("customer_dashboard")) |         response = self.client.get( | ||||||
|         self.assertEqual(response.status_code, 302) |             reverse("customer_dashboard", kwargs={"slug": TEST_USER}) | ||||||
|         self.assertRedirects(response, "/accounts/login/?next=/") |         ) | ||||||
|  |         self.assertEqual(response.status_code, 403) | ||||||
| 
 | 
 | ||||||
|     def test_user_dashboard_view_logged_in_ok(self): |     def test_user_dashboard_view_logged_in_ok(self): | ||||||
|         self._create_test_user() |         self._create_test_user() | ||||||
|         self.assertTrue(self.client.login(username=TEST_USER, password=TEST_PASSWORD)) |         self.assertTrue(self.client.login(username=TEST_USER, password=TEST_PASSWORD)) | ||||||
|         response = self.client.get(reverse("customer_dashboard")) |         response = self.client.get( | ||||||
|  |             reverse("customer_dashboard", kwargs={"slug": TEST_USER}) | ||||||
|  |         ) | ||||||
|         self.assertEqual(response.status_code, 200) |         self.assertEqual(response.status_code, 200) | ||||||
| 
 | 
 | ||||||
|     def test_user_dashboard_view_logged_in_template(self): |     def test_user_dashboard_view_logged_in_template(self): | ||||||
|         self._create_test_user() |         self._create_test_user() | ||||||
|         self.assertTrue(self.client.login(username=TEST_USER, password=TEST_PASSWORD)) |         self.assertTrue(self.client.login(username=TEST_USER, password=TEST_PASSWORD)) | ||||||
|         response = self.client.get( |         response = self.client.get( | ||||||
|             reverse("customer_dashboard") |             reverse("customer_dashboard", kwargs={"slug": TEST_USER}) | ||||||
|         ) |         ) | ||||||
|         self.assertTemplateUsed(response, "dashboard/user_dashboard.html") |         self.assertTemplateUsed(response, "dashboard/user_dashboard.html") | ||||||
| 
 | 
 | ||||||
|     def test_user_dashboard_view_logged_in_context_fresh(self): |     def test_user_dashboard_view_logged_in_context_fresh(self): | ||||||
|         self._create_test_user() |         self._create_test_user() | ||||||
|         self.assertTrue(self.client.login(username=TEST_USER, password=TEST_PASSWORD)) |         self.assertTrue(self.client.login(username=TEST_USER, password=TEST_PASSWORD)) | ||||||
|         response = self.client.get(reverse("customer_dashboard")) |         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"]) | ||||||
|         self.assertIn("hosting_packages", response.context) |         self.assertIn("hosting_packages", response.context) | ||||||
|         self.assertEqual(len(response.context["hosting_packages"]), 0) |         self.assertEqual(len(response.context["hosting_packages"]), 0) | ||||||
|  |  | ||||||
|  | @ -1,9 +1,15 @@ | ||||||
| from __future__ import absolute_import | from __future__ import absolute_import, unicode_literals | ||||||
| 
 | 
 | ||||||
| from django.urls import path | from django.conf.urls import url | ||||||
|  | 
 | ||||||
|  | from .views import ( | ||||||
|  |     IndexView, | ||||||
|  |     UserDashboardView, | ||||||
|  | ) | ||||||
| 
 | 
 | ||||||
| from .views import UserDashboardView |  | ||||||
| 
 | 
 | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
|     path("", UserDashboardView.as_view(), name="customer_dashboard"), |     url(r'^$', IndexView.as_view(), name='dashboard'), | ||||||
|  |     url(r'^user/(?P<slug>[\w0-9@.+-_]+)/$', | ||||||
|  |         UserDashboardView.as_view(), name='customer_dashboard'), | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | @ -2,26 +2,47 @@ | ||||||
| This module defines the views for the gnuviechadmin customer dashboard. | This module defines the views for the gnuviechadmin customer dashboard. | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | 
 | ||||||
|  | from django.views.generic import ( | ||||||
|  |     DetailView, | ||||||
|  |     TemplateView, | ||||||
|  | ) | ||||||
| from django.contrib.auth import get_user_model | from django.contrib.auth import get_user_model | ||||||
| from django.contrib.auth.mixins import LoginRequiredMixin | 
 | ||||||
| from django.shortcuts import redirect |  | ||||||
| from django.views.generic import DetailView, TemplateView |  | ||||||
| from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin | from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin | ||||||
| 
 | 
 | ||||||
| from hostingpackages.models import CustomerHostingPackage | from hostingpackages.models import CustomerHostingPackage | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class UserDashboardView(LoginRequiredMixin, TemplateView): | class IndexView(TemplateView): | ||||||
|  |     """ | ||||||
|  |     This is the dashboard view. | ||||||
|  | 
 | ||||||
|  |     """ | ||||||
|  |     template_name = 'dashboard/index.html' | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class UserDashboardView(StaffOrSelfLoginRequiredMixin, DetailView): | ||||||
|     """ |     """ | ||||||
|     This is the user dashboard view. |     This is the user dashboard view. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |     model = get_user_model() | ||||||
|     template_name = "dashboard/user_dashboard.html" |     context_object_name = 'dashboard_user' | ||||||
|  |     slug_field = 'username' | ||||||
|  |     template_name = 'dashboard/user_dashboard.html' | ||||||
| 
 | 
 | ||||||
|     def get_context_data(self, **kwargs): |     def get_context_data(self, **kwargs): | ||||||
|         context = super(UserDashboardView, self).get_context_data(**kwargs) |         context = super(UserDashboardView, self).get_context_data(**kwargs) | ||||||
|         context["hosting_packages"] = CustomerHostingPackage.objects.filter( |         context['hosting_packages'] = CustomerHostingPackage.objects.filter( | ||||||
|             customer=self.request.user |             customer=self.object | ||||||
|         ) |         ) | ||||||
|         return context |         return context | ||||||
|  | 
 | ||||||
|  |     def get_customer_object(self): | ||||||
|  |         """ | ||||||
|  |         Returns the customer object. | ||||||
|  | 
 | ||||||
|  |         """ | ||||||
|  |         return self.get_object() | ||||||
|  |  | ||||||
|  | @ -2,3 +2,4 @@ | ||||||
| This app takes care of domains. | This app takes care of domains. | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
|  | default_app_config = 'domains.apps.DomainAppConfig' | ||||||
|  |  | ||||||
|  | @ -5,7 +5,24 @@ with the django admin site. | ||||||
| """ | """ | ||||||
| from django.contrib import admin | from django.contrib import admin | ||||||
| 
 | 
 | ||||||
| from domains.models import HostingDomain, MailDomain | from .models import ( | ||||||
|  |     DNSComment, | ||||||
|  |     DNSCryptoKey, | ||||||
|  |     DNSDomain, | ||||||
|  |     DNSDomainMetadata, | ||||||
|  |     DNSRecord, | ||||||
|  |     DNSSupermaster, | ||||||
|  |     DNSTSIGKey, | ||||||
|  |     HostingDomain, | ||||||
|  |     MailDomain, | ||||||
|  | ) | ||||||
| 
 | 
 | ||||||
| admin.site.register(MailDomain) | admin.site.register(MailDomain) | ||||||
| admin.site.register(HostingDomain) | admin.site.register(HostingDomain) | ||||||
|  | admin.site.register(DNSComment) | ||||||
|  | admin.site.register(DNSCryptoKey) | ||||||
|  | admin.site.register(DNSDomain) | ||||||
|  | admin.site.register(DNSDomainMetadata) | ||||||
|  | admin.site.register(DNSRecord) | ||||||
|  | admin.site.register(DNSSupermaster) | ||||||
|  | admin.site.register(DNSTSIGKey) | ||||||
|  |  | ||||||
|  | @ -3,8 +3,9 @@ This module contains the :py:class:`django.apps.AppConfig` instance for the | ||||||
| :py:mod:`domains` app. | :py:mod:`domains` app. | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
|  | from __future__ import unicode_literals | ||||||
| from django.apps import AppConfig | from django.apps import AppConfig | ||||||
| from django.utils.translation import gettext_lazy as _ | from django.utils.translation import ugettext_lazy as _ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class DomainAppConfig(AppConfig): | class DomainAppConfig(AppConfig): | ||||||
|  | @ -12,6 +13,5 @@ class DomainAppConfig(AppConfig): | ||||||
|     AppConfig for the :py:mod:`domains` app. |     AppConfig for the :py:mod:`domains` app. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |     name = 'domains' | ||||||
|     name = "domains" |     verbose_name = _('Domains') | ||||||
|     verbose_name = _("Domains") |  | ||||||
|  |  | ||||||
|  | @ -2,15 +2,19 @@ | ||||||
| This module defines form classes for domain editing. | This module defines form classes for domain editing. | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
| from __future__ import absolute_import | from __future__ import absolute_import, unicode_literals | ||||||
| 
 | 
 | ||||||
| import re | import re | ||||||
| 
 | 
 | ||||||
| from crispy_forms.helper import FormHelper |  | ||||||
| from crispy_forms.layout import Layout, Submit |  | ||||||
| from django import forms | from django import forms | ||||||
| from django.urls import reverse | from django.urls import reverse | ||||||
| from django.utils.translation import gettext as _ | from django.utils.translation import ugettext as _ | ||||||
|  | 
 | ||||||
|  | from crispy_forms.helper import FormHelper | ||||||
|  | from crispy_forms.layout import ( | ||||||
|  |     Layout, | ||||||
|  |     Submit, | ||||||
|  | ) | ||||||
| 
 | 
 | ||||||
| from .models import HostingDomain | from .models import HostingDomain | ||||||
| 
 | 
 | ||||||
|  | @ -22,10 +26,11 @@ def relative_domain_validator(value): | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
|     if len(value) > 254: |     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}(?<!-)$") |     allowed = re.compile(r"(?!-)[a-z\d-]{1,63}(?<!-)$") | ||||||
|     if not all(allowed.match(x) for x in value.split(".")): |     if not all(allowed.match(x) for x in value.split('.')): | ||||||
|         raise forms.ValidationError(_("invalid domain name")) |         raise forms.ValidationError(_('invalid domain name')) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class CreateHostingDomainForm(forms.ModelForm): | class CreateHostingDomainForm(forms.ModelForm): | ||||||
|  | @ -33,32 +38,31 @@ class CreateHostingDomainForm(forms.ModelForm): | ||||||
|     This form is used to create new HostingDomain instances. |     This form is used to create new HostingDomain instances. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |  | ||||||
|     class Meta: |     class Meta: | ||||||
|         model = HostingDomain |         model = HostingDomain | ||||||
|         fields = ["domain"] |         fields = ['domain'] | ||||||
| 
 | 
 | ||||||
|     def __init__(self, instance, *args, **kwargs): |     def __init__(self, instance, *args, **kwargs): | ||||||
|         self.hosting_package = kwargs.pop("hostingpackage") |         self.hosting_package = kwargs.pop('hostingpackage') | ||||||
|         super(CreateHostingDomainForm, self).__init__(*args, **kwargs) |         super(CreateHostingDomainForm, self).__init__(*args, **kwargs) | ||||||
|         self.fields["domain"].validators.append(relative_domain_validator) |         self.fields['domain'].validators.append(relative_domain_validator) | ||||||
|         self.helper = FormHelper() |         self.helper = FormHelper() | ||||||
|         self.helper.form_action = reverse( |         self.helper.form_action = reverse( | ||||||
|             "create_hosting_domain", kwargs={"package": self.hosting_package.id} |             'create_hosting_domain', kwargs={ | ||||||
|         ) |                 'package': self.hosting_package.id | ||||||
|  |             }) | ||||||
|         self.helper.layout = Layout( |         self.helper.layout = Layout( | ||||||
|             "domain", |             'domain', | ||||||
|             Submit("submit", _("Add Hosting Domain")), |             Submit('submit', _('Add Hosting Domain')), | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|     def clean(self): |     def clean(self): | ||||||
|         self.cleaned_data = super(CreateHostingDomainForm, self).clean() |         self.cleaned_data = super(CreateHostingDomainForm, self).clean() | ||||||
|         self.cleaned_data["hosting_package"] = self.hosting_package |         self.cleaned_data['hosting_package'] = self.hosting_package | ||||||
| 
 | 
 | ||||||
|     def save(self, commit=True): |     def save(self, commit=True): | ||||||
|         return HostingDomain.objects.create_for_hosting_package( |         return HostingDomain.objects.create_for_hosting_package( | ||||||
|             commit=commit, **self.cleaned_data |             commit=commit, **self.cleaned_data) | ||||||
|         ) |  | ||||||
| 
 | 
 | ||||||
|     def save_m2m(self): |     def save_m2m(self): | ||||||
|         pass |         pass | ||||||
|  |  | ||||||
|  | @ -7,8 +7,8 @@ msgid "" | ||||||
| msgstr "" | msgstr "" | ||||||
| "Project-Id-Version: gnuviechadmin domains\n" | "Project-Id-Version: gnuviechadmin domains\n" | ||||||
| "Report-Msgid-Bugs-To: \n" | "Report-Msgid-Bugs-To: \n" | ||||||
| "POT-Creation-Date: 2023-04-16 22:07+0200\n" | "POT-Creation-Date: 2016-01-29 11:04+0100\n" | ||||||
| "PO-Revision-Date: 2023-04-16 18:20+0200\n" | "PO-Revision-Date: 2015-11-08 12:02+0100\n" | ||||||
| "Last-Translator: Jan Dittberner <jan@dittberner.info>\n" | "Last-Translator: Jan Dittberner <jan@dittberner.info>\n" | ||||||
| "Language-Team: Jan Dittberner <jan@dittberner.info>\n" | "Language-Team: Jan Dittberner <jan@dittberner.info>\n" | ||||||
| "Language: de\n" | "Language: de\n" | ||||||
|  | @ -16,60 +16,152 @@ msgstr "" | ||||||
| "Content-Type: text/plain; charset=UTF-8\n" | "Content-Type: text/plain; charset=UTF-8\n" | ||||||
| "Content-Transfer-Encoding: 8bit\n" | "Content-Transfer-Encoding: 8bit\n" | ||||||
| "Plural-Forms: nplurals=2; plural=(n != 1);\n" | "Plural-Forms: nplurals=2; plural=(n != 1);\n" | ||||||
| "X-Generator: Poedit 3.2.2\n" | "X-Generator: Poedit 1.8.6\n" | ||||||
| "X-Poedit-SourceCharset: UTF-8\n" | "X-Poedit-SourceCharset: UTF-8\n" | ||||||
| 
 | 
 | ||||||
| #: domains/apps.py:17 | #: domains/apps.py:17 | ||||||
| msgid "Domains" | msgid "Domains" | ||||||
| msgstr "Domains" | msgstr "Domains" | ||||||
| 
 | 
 | ||||||
| #: domains/forms.py:25 domains/tests/test_forms.py:20 | #: domains/forms.py:30 domains/tests/test_forms.py:24 | ||||||
| msgid "host name too long" | msgid "host name too long" | ||||||
| msgstr "zu langer Hostname" | msgstr "zu langer Hostname" | ||||||
| 
 | 
 | ||||||
| #: domains/forms.py:28 domains/tests/test_forms.py:24 | #: domains/forms.py:33 domains/tests/test_forms.py:29 | ||||||
| #: domains/tests/test_forms.py:28 domains/tests/test_forms.py:32 | #: domains/tests/test_forms.py:34 domains/tests/test_forms.py:39 | ||||||
| #: domains/tests/test_forms.py:36 | #: domains/tests/test_forms.py:44 | ||||||
| msgid "invalid domain name" | msgid "invalid domain name" | ||||||
| msgstr "ungültiger Domainname" | msgstr "ungültiger Domainname" | ||||||
| 
 | 
 | ||||||
| #: domains/forms.py:51 | #: domains/forms.py:56 | ||||||
| msgid "Add Hosting Domain" | msgid "Add Hosting Domain" | ||||||
| msgstr "Hostingdomain hinzufügen" | msgstr "Hostingdomain hinzufügen" | ||||||
| 
 | 
 | ||||||
|  | #: domains/models.py:17 | ||||||
|  | msgid "Master" | ||||||
|  | msgstr "Master" | ||||||
|  | 
 | ||||||
|  | #: domains/models.py:18 | ||||||
|  | msgid "Slave" | ||||||
|  | msgstr "Slave" | ||||||
|  | 
 | ||||||
| #: domains/models.py:19 | #: domains/models.py:19 | ||||||
|  | msgid "Native" | ||||||
|  | msgstr "Native" | ||||||
|  | 
 | ||||||
|  | #: domains/models.py:44 | ||||||
|  | msgid "HMAC MD5" | ||||||
|  | msgstr "HMAC MD5" | ||||||
|  | 
 | ||||||
|  | #: domains/models.py:45 | ||||||
|  | msgid "HMAC SHA1" | ||||||
|  | msgstr "HMAC SHA1" | ||||||
|  | 
 | ||||||
|  | #: domains/models.py:46 | ||||||
|  | msgid "HMAC SHA224" | ||||||
|  | msgstr "HMAC SHA224" | ||||||
|  | 
 | ||||||
|  | #: domains/models.py:47 | ||||||
|  | msgid "HMAC SHA256" | ||||||
|  | msgstr "HMAC SHA256" | ||||||
|  | 
 | ||||||
|  | #: domains/models.py:48 | ||||||
|  | msgid "HMAC SHA384" | ||||||
|  | msgstr "HMAC SHA384" | ||||||
|  | 
 | ||||||
|  | #: domains/models.py:49 | ||||||
|  | msgid "HMAC SHA512" | ||||||
|  | msgstr "HMAC SHA512" | ||||||
|  | 
 | ||||||
|  | #: domains/models.py:58 | ||||||
| msgid "domain name" | msgid "domain name" | ||||||
| msgstr "Domainname" | msgstr "Domainname" | ||||||
| 
 | 
 | ||||||
| #: domains/models.py:22 | #: domains/models.py:60 domains/models.py:258 domains/models.py:308 | ||||||
| msgid "customer" | msgid "customer" | ||||||
| msgstr "Kunde" | msgstr "Kunde" | ||||||
| 
 | 
 | ||||||
| #: domains/models.py:41 | #: domains/models.py:76 | ||||||
| msgid "Mail domain" | msgid "Mail domain" | ||||||
| msgstr "E-Maildomain" | msgstr "E-Maildomain" | ||||||
| 
 | 
 | ||||||
| #: domains/models.py:42 | #: domains/models.py:77 | ||||||
| msgid "Mail domains" | msgid "Mail domains" | ||||||
| msgstr "E-Maildomains" | msgstr "E-Maildomains" | ||||||
| 
 | 
 | ||||||
| #: domains/models.py:91 | #: domains/models.py:121 | ||||||
| msgid "mail domain" | msgid "mail domain" | ||||||
| msgstr "E-Maildomain" | msgstr "E-Maildomain" | ||||||
| 
 | 
 | ||||||
| #: domains/models.py:94 | #: domains/models.py:122 | ||||||
| msgid "assigned mail domain for this domain" | msgid "assigned mail domain for this domain" | ||||||
| msgstr "zugeordnete E-Maildomain für diese Domain" | msgstr "zugeordnete E-Maildomain für diese Domain" | ||||||
| 
 | 
 | ||||||
| #: domains/models.py:101 | #: domains/models.py:128 | ||||||
| msgid "Hosting domain" | msgid "Hosting domain" | ||||||
| msgstr "Hostingdomain" | msgstr "Hostingdomain" | ||||||
| 
 | 
 | ||||||
| #: domains/models.py:102 | #: domains/models.py:129 | ||||||
| msgid "Hosting domains" | msgid "Hosting domains" | ||||||
| msgstr "Hostingdomains" | msgstr "Hostingdomains" | ||||||
| 
 | 
 | ||||||
| #: domains/views.py:51 | #: 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 | ||||||
| #, python-brace-format | #, python-brace-format | ||||||
| msgid "Successfully created domain {domainname}" | msgid "Successfully created domain {domainname}" | ||||||
| msgstr "Domain {domainname} erfolgreich angelegt" | msgstr "Domain {domainname} erfolgreich angelegt" | ||||||
|  |  | ||||||
|  | @ -1,46 +1,28 @@ | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | 
 | ||||||
|  | from django.db import models, migrations | ||||||
| import django.utils.timezone | import django.utils.timezone | ||||||
| import model_utils.fields | import model_utils.fields | ||||||
| from django.db import migrations, models |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
|     dependencies = [] | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |     ] | ||||||
| 
 | 
 | ||||||
|     operations = [ |     operations = [ | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="MailDomain", |             name='MailDomain', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | ||||||
|                     "id", |                 ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, verbose_name='created', editable=False)), | ||||||
|                     models.AutoField( |                 ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, verbose_name='modified', editable=False)), | ||||||
|                         verbose_name="ID", |                 ('domain', models.CharField(unique=True, max_length=128)), | ||||||
|                         serialize=False, |  | ||||||
|                         auto_created=True, |  | ||||||
|                         primary_key=True, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "created", |  | ||||||
|                     model_utils.fields.AutoCreatedField( |  | ||||||
|                         default=django.utils.timezone.now, |  | ||||||
|                         verbose_name="created", |  | ||||||
|                         editable=False, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "modified", |  | ||||||
|                     model_utils.fields.AutoLastModifiedField( |  | ||||||
|                         default=django.utils.timezone.now, |  | ||||||
|                         verbose_name="modified", |  | ||||||
|                         editable=False, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ("domain", models.CharField(unique=True, max_length=128)), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "verbose_name": "Mail domain", |                 'verbose_name': 'Mail domain', | ||||||
|                 "verbose_name_plural": "Mail domains", |                 'verbose_name_plural': 'Mail domains', | ||||||
|             }, |             }, | ||||||
|             bases=(models.Model,), |             bases=(models.Model,), | ||||||
|         ), |         ), | ||||||
|  |  | ||||||
|  | @ -1,97 +1,68 @@ | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | 
 | ||||||
|  | from django.db import models, migrations | ||||||
| import django.utils.timezone | import django.utils.timezone | ||||||
| import model_utils.fields |  | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| from django.db import migrations, models | import model_utils.fields | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
|     dependencies = [ |     dependencies = [ | ||||||
|         migrations.swappable_dependency(settings.AUTH_USER_MODEL), |         migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||||||
|         ("domains", "0001_initial"), |         ('domains', '0001_initial'), | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     operations = [ |     operations = [ | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="HostingDomain", |             name='HostingDomain', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('id', | ||||||
|                     "id", |                  models.AutoField(verbose_name='ID', serialize=False, | ||||||
|                     models.AutoField( |                                   auto_created=True, primary_key=True)), | ||||||
|                         verbose_name="ID", |                 ('created', | ||||||
|                         serialize=False, |                  model_utils.fields.AutoCreatedField( | ||||||
|                         auto_created=True, |                      default=django.utils.timezone.now, verbose_name='created', | ||||||
|                         primary_key=True, |                      editable=False)), | ||||||
|                     ), |                 ('modified', | ||||||
|                 ), |                  model_utils.fields.AutoLastModifiedField( | ||||||
|                 ( |                      default=django.utils.timezone.now, verbose_name='modified', | ||||||
|                     "created", |                      editable=False)), | ||||||
|                     model_utils.fields.AutoCreatedField( |                 ('domain', | ||||||
|                         default=django.utils.timezone.now, |                  models.CharField( | ||||||
|                         verbose_name="created", |                      unique=True, max_length=128, verbose_name='domain name')), | ||||||
|                         editable=False, |                 ('customer', | ||||||
|                     ), |                  models.ForeignKey( | ||||||
|                 ), |                      verbose_name='customer', blank=True, | ||||||
|                 ( |                      to=settings.AUTH_USER_MODEL, null=True, | ||||||
|                     "modified", |                      on_delete=models.CASCADE)), | ||||||
|                     model_utils.fields.AutoLastModifiedField( |                 ('maildomain', | ||||||
|                         default=django.utils.timezone.now, |                  models.OneToOneField( | ||||||
|                         verbose_name="modified", |                      null=True, to='domains.MailDomain', blank=True, | ||||||
|                         editable=False, |                      help_text='assigned mail domain for this domain', | ||||||
|                     ), |                      verbose_name='mail domain', | ||||||
|                 ), |                      on_delete=models.CASCADE)), | ||||||
|                 ( |  | ||||||
|                     "domain", |  | ||||||
|                     models.CharField( |  | ||||||
|                         unique=True, max_length=128, verbose_name="domain name" |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "customer", |  | ||||||
|                     models.ForeignKey( |  | ||||||
|                         verbose_name="customer", |  | ||||||
|                         blank=True, |  | ||||||
|                         to=settings.AUTH_USER_MODEL, |  | ||||||
|                         null=True, |  | ||||||
|                         on_delete=models.CASCADE, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "maildomain", |  | ||||||
|                     models.OneToOneField( |  | ||||||
|                         null=True, |  | ||||||
|                         to="domains.MailDomain", |  | ||||||
|                         blank=True, |  | ||||||
|                         help_text="assigned mail domain for this domain", |  | ||||||
|                         verbose_name="mail domain", |  | ||||||
|                         on_delete=models.CASCADE, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "verbose_name": "Hosting domain", |                 'verbose_name': 'Hosting domain', | ||||||
|                 "verbose_name_plural": "Hosting domains", |                 'verbose_name_plural': 'Hosting domains', | ||||||
|             }, |             }, | ||||||
|             bases=(models.Model,), |             bases=(models.Model,), | ||||||
|         ), |         ), | ||||||
|         migrations.AddField( |         migrations.AddField( | ||||||
|             model_name="maildomain", |             model_name='maildomain', | ||||||
|             name="customer", |             name='customer', | ||||||
|             field=models.ForeignKey( |             field=models.ForeignKey( | ||||||
|                 verbose_name="customer", |                 verbose_name='customer', blank=True, | ||||||
|                 blank=True, |                 to=settings.AUTH_USER_MODEL, null=True, | ||||||
|                 to=settings.AUTH_USER_MODEL, |                 on_delete=models.CASCADE), | ||||||
|                 null=True, |  | ||||||
|                 on_delete=models.CASCADE, |  | ||||||
|             ), |  | ||||||
|             preserve_default=True, |             preserve_default=True, | ||||||
|         ), |         ), | ||||||
|         migrations.AlterField( |         migrations.AlterField( | ||||||
|             model_name="maildomain", |             model_name='maildomain', | ||||||
|             name="domain", |             name='domain', | ||||||
|             field=models.CharField( |             field=models.CharField( | ||||||
|                 unique=True, max_length=128, verbose_name="domain name" |                 unique=True, max_length=128, verbose_name='domain name'), | ||||||
|             ), |  | ||||||
|             preserve_default=True, |             preserve_default=True, | ||||||
|         ), |         ), | ||||||
|     ] |     ] | ||||||
|  |  | ||||||
|  | @ -1,285 +1,199 @@ | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
| import django.utils.timezone | from __future__ import unicode_literals | ||||||
| import model_utils.fields | 
 | ||||||
| from django.conf import settings |  | ||||||
| from django.db import migrations, models | from django.db import migrations, models | ||||||
|  | import django.utils.timezone | ||||||
|  | from django.conf import settings | ||||||
|  | import model_utils.fields | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
|     dependencies = [ |     dependencies = [ | ||||||
|         migrations.swappable_dependency(settings.AUTH_USER_MODEL), |         migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||||||
|         ("domains", "0002_auto_20150124_1909"), |         ('domains', '0002_auto_20150124_1909'), | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     operations = [ |     operations = [ | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="DNSComment", |             name='DNSComment', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('id', models.AutoField( | ||||||
|                     "id", |                     verbose_name='ID', serialize=False, | ||||||
|                     models.AutoField( |                     auto_created=True, primary_key=True)), | ||||||
|                         verbose_name="ID", |                 ('name', models.CharField(max_length=255)), | ||||||
|                         serialize=False, |                 ('commenttype', | ||||||
|                         auto_created=True, |                  models.CharField(max_length=10, db_column='type')), | ||||||
|                         primary_key=True, |                 ('modified_at', models.IntegerField()), | ||||||
|                     ), |                 ('comment', models.CharField(max_length=65535)), | ||||||
|                 ), |                 ('customer', models.ForeignKey( | ||||||
|                 ("name", models.CharField(max_length=255)), |                     verbose_name='customer', | ||||||
|                 ("commenttype", models.CharField(max_length=10, db_column="type")), |                     to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), | ||||||
|                 ("modified_at", models.IntegerField()), |  | ||||||
|                 ("comment", models.CharField(max_length=65535)), |  | ||||||
|                 ( |  | ||||||
|                     "customer", |  | ||||||
|                     models.ForeignKey( |  | ||||||
|                         verbose_name="customer", |  | ||||||
|                         to=settings.AUTH_USER_MODEL, |  | ||||||
|                         on_delete=models.CASCADE, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|         ), |         ), | ||||||
|         migrations.RunSQL( |         migrations.RunSQL( | ||||||
|             """ALTER TABLE domains_dnscomment ADD CONSTRAINT c_lowercase_name |             '''ALTER TABLE domains_dnscomment ADD CONSTRAINT c_lowercase_name | ||||||
|              CHECK (((name)::TEXT = LOWER((name)::TEXT)))""" |              CHECK (((name)::TEXT = LOWER((name)::TEXT)))''' | ||||||
|         ), |         ), | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="DNSCryptoKey", |             name='DNSCryptoKey', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('id', models.AutoField( | ||||||
|                     "id", |                     verbose_name='ID', serialize=False, | ||||||
|                     models.AutoField( |                     auto_created=True, primary_key=True)), | ||||||
|                         verbose_name="ID", |                 ('flags', models.IntegerField()), | ||||||
|                         serialize=False, |                 ('active', models.BooleanField(default=True)), | ||||||
|                         auto_created=True, |                 ('content', models.TextField()), | ||||||
|                         primary_key=True, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ("flags", models.IntegerField()), |  | ||||||
|                 ("active", models.BooleanField(default=True)), |  | ||||||
|                 ("content", models.TextField()), |  | ||||||
|             ], |             ], | ||||||
|         ), |         ), | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="DNSDomain", |             name='DNSDomain', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('id', models.AutoField( | ||||||
|                     "id", |                     verbose_name='ID', serialize=False, | ||||||
|                     models.AutoField( |                     auto_created=True, primary_key=True)), | ||||||
|                         verbose_name="ID", |                 ('created', model_utils.fields.AutoCreatedField( | ||||||
|                         serialize=False, |                     default=django.utils.timezone.now, verbose_name='created', | ||||||
|                         auto_created=True, |                     editable=False)), | ||||||
|                         primary_key=True, |                 ('modified', model_utils.fields.AutoLastModifiedField( | ||||||
|                     ), |                     default=django.utils.timezone.now, verbose_name='modified', | ||||||
|                 ), |                     editable=False)), | ||||||
|                 ( |                 ('domain', models.CharField( | ||||||
|                     "created", |                     unique=True, max_length=255, verbose_name='domain name')), | ||||||
|                     model_utils.fields.AutoCreatedField( |                 ('master', | ||||||
|                         default=django.utils.timezone.now, |                  models.CharField(max_length=128, null=True, blank=True)), | ||||||
|                         verbose_name="created", |                 ('last_check', models.IntegerField(null=True)), | ||||||
|                         editable=False, |                 ('domaintype', models.CharField( | ||||||
|                     ), |                     max_length=6, db_column='type', | ||||||
|                 ), |                     choices=[('MASTER', 'Master'), | ||||||
|                 ( |                              ('SLAVE', 'Slave'), | ||||||
|                     "modified", |                              ('NATIVE', 'Native')])), | ||||||
|                     model_utils.fields.AutoLastModifiedField( |                 ('notified_serial', models.IntegerField(null=True)), | ||||||
|                         default=django.utils.timezone.now, |                 ('customer', models.ForeignKey( | ||||||
|                         verbose_name="modified", |                     verbose_name='customer', blank=True, | ||||||
|                         editable=False, |                     to=settings.AUTH_USER_MODEL, null=True, | ||||||
|                     ), |                     on_delete=models.CASCADE)), | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "domain", |  | ||||||
|                     models.CharField( |  | ||||||
|                         unique=True, max_length=255, verbose_name="domain name" |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ("master", models.CharField(max_length=128, null=True, blank=True)), |  | ||||||
|                 ("last_check", models.IntegerField(null=True)), |  | ||||||
|                 ( |  | ||||||
|                     "domaintype", |  | ||||||
|                     models.CharField( |  | ||||||
|                         max_length=6, |  | ||||||
|                         db_column="type", |  | ||||||
|                         choices=[ |  | ||||||
|                             ("MASTER", "Master"), |  | ||||||
|                             ("SLAVE", "Slave"), |  | ||||||
|                             ("NATIVE", "Native"), |  | ||||||
|                         ], |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ("notified_serial", models.IntegerField(null=True)), |  | ||||||
|                 ( |  | ||||||
|                     "customer", |  | ||||||
|                     models.ForeignKey( |  | ||||||
|                         verbose_name="customer", |  | ||||||
|                         blank=True, |  | ||||||
|                         to=settings.AUTH_USER_MODEL, |  | ||||||
|                         null=True, |  | ||||||
|                         on_delete=models.CASCADE, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "verbose_name": "DNS domain", |                 'verbose_name': 'DNS domain', | ||||||
|                 "verbose_name_plural": "DNS domains", |                 'verbose_name_plural': 'DNS domains', | ||||||
|             }, |             }, | ||||||
|         ), |         ), | ||||||
|         migrations.RunSQL( |         migrations.RunSQL( | ||||||
|             """ALTER TABLE domains_dnsdomain ADD CONSTRAINT c_lowercase_name |             '''ALTER TABLE domains_dnsdomain ADD CONSTRAINT c_lowercase_name | ||||||
|             CHECK (((domain)::TEXT = LOWER((domain)::TEXT)))""" |             CHECK (((domain)::TEXT = LOWER((domain)::TEXT)))''' | ||||||
|         ), |         ), | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="DNSDomainMetadata", |             name='DNSDomainMetadata', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('id', models.AutoField( | ||||||
|                     "id", |                     verbose_name='ID', serialize=False, | ||||||
|                     models.AutoField( |                     auto_created=True, primary_key=True)), | ||||||
|                         verbose_name="ID", |                 ('kind', models.CharField(max_length=32)), | ||||||
|                         serialize=False, |                 ('content', models.TextField()), | ||||||
|                         auto_created=True, |                 ('domain', models.ForeignKey( | ||||||
|                         primary_key=True, |                     to='domains.DNSDomain', on_delete=models.CASCADE)), | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ("kind", models.CharField(max_length=32)), |  | ||||||
|                 ("content", models.TextField()), |  | ||||||
|                 ( |  | ||||||
|                     "domain", |  | ||||||
|                     models.ForeignKey(to="domains.DNSDomain", on_delete=models.CASCADE), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|         ), |         ), | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="DNSRecord", |             name='DNSRecord', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('id', models.AutoField( | ||||||
|                     "id", |                     verbose_name='ID', serialize=False, | ||||||
|                     models.AutoField( |                     auto_created=True, primary_key=True)), | ||||||
|                         verbose_name="ID", |                 ('name', models.CharField( | ||||||
|                         serialize=False, |                     db_index=True, max_length=255, null=True, blank=True)), | ||||||
|                         auto_created=True, |                 ('recordtype', models.CharField( | ||||||
|                         primary_key=True, |                     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)), | ||||||
|                     "name", |                 ('prio', models.IntegerField(null=True)), | ||||||
|                     models.CharField( |                 ('change_date', models.IntegerField(null=True)), | ||||||
|                         db_index=True, max_length=255, null=True, blank=True |                 ('disabled', models.BooleanField(default=False)), | ||||||
|                     ), |                 ('ordername', models.CharField(max_length=255)), | ||||||
|                 ), |                 ('auth', models.BooleanField(default=True)), | ||||||
|                 ( |                 ('domain', models.ForeignKey( | ||||||
|                     "recordtype", |                     to='domains.DNSDomain', on_delete=models.CASCADE)), | ||||||
|                     models.CharField( |  | ||||||
|                         max_length=10, null=True, db_column="type", blank=True |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ("content", models.CharField(max_length=65535, null=True, blank=True)), |  | ||||||
|                 ("ttl", models.IntegerField(null=True)), |  | ||||||
|                 ("prio", models.IntegerField(null=True)), |  | ||||||
|                 ("change_date", models.IntegerField(null=True)), |  | ||||||
|                 ("disabled", models.BooleanField(default=False)), |  | ||||||
|                 ("ordername", models.CharField(max_length=255)), |  | ||||||
|                 ("auth", models.BooleanField(default=True)), |  | ||||||
|                 ( |  | ||||||
|                     "domain", |  | ||||||
|                     models.ForeignKey(to="domains.DNSDomain", on_delete=models.CASCADE), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "verbose_name": "DNS record", |                 'verbose_name': 'DNS record', | ||||||
|                 "verbose_name_plural": "DNS records", |                 'verbose_name_plural': 'DNS records', | ||||||
|             }, |             }, | ||||||
|         ), |         ), | ||||||
|         migrations.RunSQL( |         migrations.RunSQL( | ||||||
|             """ALTER TABLE domains_dnsrecord ADD CONSTRAINT c_lowercase_name |             '''ALTER TABLE domains_dnsrecord ADD CONSTRAINT c_lowercase_name | ||||||
|             CHECK (((name)::TEXT = LOWER((name)::TEXT)))""" |             CHECK (((name)::TEXT = LOWER((name)::TEXT)))''' | ||||||
|         ), |         ), | ||||||
|         migrations.RunSQL( |         migrations.RunSQL( | ||||||
|             """CREATE INDEX recordorder ON domains_dnsrecord (domain_id, |             '''CREATE INDEX recordorder ON domains_dnsrecord (domain_id, | ||||||
|             ordername text_pattern_ops)""" |             ordername text_pattern_ops)''' | ||||||
|         ), |         ), | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="DNSSupermaster", |             name='DNSSupermaster', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('id', models.AutoField( | ||||||
|                     "id", |                     verbose_name='ID', serialize=False, | ||||||
|                     models.AutoField( |                     auto_created=True, primary_key=True)), | ||||||
|                         verbose_name="ID", |                 ('ip', models.GenericIPAddressField()), | ||||||
|                         serialize=False, |                 ('nameserver', models.CharField(max_length=255)), | ||||||
|                         auto_created=True, |                 ('customer', models.ForeignKey( | ||||||
|                         primary_key=True, |                     verbose_name='customer', to=settings.AUTH_USER_MODEL, | ||||||
|                     ), |                     on_delete=models.CASCADE)), | ||||||
|                 ), |  | ||||||
|                 ("ip", models.GenericIPAddressField()), |  | ||||||
|                 ("nameserver", models.CharField(max_length=255)), |  | ||||||
|                 ( |  | ||||||
|                     "customer", |  | ||||||
|                     models.ForeignKey( |  | ||||||
|                         verbose_name="customer", |  | ||||||
|                         to=settings.AUTH_USER_MODEL, |  | ||||||
|                         on_delete=models.CASCADE, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|         ), |         ), | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="DNSTSIGKey", |             name='DNSTSIGKey', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('id', models.AutoField( | ||||||
|                     "id", |                     verbose_name='ID', serialize=False, | ||||||
|                     models.AutoField( |                     auto_created=True, primary_key=True)), | ||||||
|                         verbose_name="ID", |                 ('name', models.CharField(max_length=255)), | ||||||
|                         serialize=False, |                 ('algorithm', models.CharField(max_length=50)), | ||||||
|                         auto_created=True, |                 ('secret', models.CharField(max_length=255)), | ||||||
|                         primary_key=True, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ("name", models.CharField(max_length=255)), |  | ||||||
|                 ("algorithm", models.CharField(max_length=50)), |  | ||||||
|                 ("secret", models.CharField(max_length=255)), |  | ||||||
|             ], |             ], | ||||||
|         ), |         ), | ||||||
|         migrations.RunSQL( |         migrations.RunSQL( | ||||||
|             """ALTER TABLE domains_dnstsigkey ADD CONSTRAINT c_lowercase_name |             '''ALTER TABLE domains_dnstsigkey ADD CONSTRAINT c_lowercase_name | ||||||
|             CHECK (((name)::TEXT = LOWER((name)::TEXT)))""" |             CHECK (((name)::TEXT = LOWER((name)::TEXT)))''' | ||||||
|         ), |         ), | ||||||
|         migrations.AlterField( |         migrations.AlterField( | ||||||
|             model_name="hostingdomain", |             model_name='hostingdomain', | ||||||
|             name="domain", |             name='domain', | ||||||
|             field=models.CharField( |             field=models.CharField( | ||||||
|                 unique=True, max_length=255, verbose_name="domain name" |                 unique=True, max_length=255, verbose_name='domain name'), | ||||||
|             ), |  | ||||||
|         ), |         ), | ||||||
|         migrations.AlterField( |         migrations.AlterField( | ||||||
|             model_name="maildomain", |             model_name='maildomain', | ||||||
|             name="domain", |             name='domain', | ||||||
|             field=models.CharField( |             field=models.CharField( | ||||||
|                 unique=True, max_length=255, verbose_name="domain name" |                 unique=True, max_length=255, verbose_name='domain name'), | ||||||
|             ), |  | ||||||
|         ), |         ), | ||||||
|         migrations.AddField( |         migrations.AddField( | ||||||
|             model_name="dnscryptokey", |             model_name='dnscryptokey', | ||||||
|             name="domain", |             name='domain', | ||||||
|             field=models.ForeignKey(to="domains.DNSDomain", on_delete=models.CASCADE), |             field=models.ForeignKey( | ||||||
|  |                 to='domains.DNSDomain', on_delete=models.CASCADE), | ||||||
|         ), |         ), | ||||||
|         migrations.AddField( |         migrations.AddField( | ||||||
|             model_name="dnscomment", |             model_name='dnscomment', | ||||||
|             name="domain", |             name='domain', | ||||||
|             field=models.ForeignKey(to="domains.DNSDomain", on_delete=models.CASCADE), |             field=models.ForeignKey( | ||||||
|  |                 to='domains.DNSDomain', on_delete=models.CASCADE), | ||||||
|         ), |         ), | ||||||
|         migrations.AlterUniqueTogether( |         migrations.AlterUniqueTogether( | ||||||
|             name="dnssupermaster", |             name='dnssupermaster', | ||||||
|             unique_together=set([("ip", "nameserver")]), |             unique_together=set([('ip', 'nameserver')]), | ||||||
|         ), |         ), | ||||||
|         migrations.AlterUniqueTogether( |         migrations.AlterUniqueTogether( | ||||||
|             name="dnstsigkey", |             name='dnstsigkey', | ||||||
|             unique_together=set([("name", "algorithm")]), |             unique_together=set([('name', 'algorithm')]), | ||||||
|         ), |         ), | ||||||
|         migrations.AlterIndexTogether( |         migrations.AlterIndexTogether( | ||||||
|             name="dnsrecord", |             name='dnsrecord', | ||||||
|             index_together=set([("name", "recordtype")]), |             index_together=set([('name', 'recordtype')]), | ||||||
|         ), |         ), | ||||||
|         migrations.AlterIndexTogether( |         migrations.AlterIndexTogether( | ||||||
|             name="dnscomment", |             name='dnscomment', | ||||||
|             index_together={("name", "commenttype"), ("domain", "modified_at")}, |             index_together={('name', 'commenttype'), ('domain', 'modified_at')}, | ||||||
|         ), |         ), | ||||||
|     ] |     ] | ||||||
|  |  | ||||||
|  | @ -1,87 +1,44 @@ | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | 
 | ||||||
| from django.db import migrations, models | from django.db import migrations, models | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|     dependencies = [ |     dependencies = [ | ||||||
|         ("domains", "0003_auto_20151105_2133"), |         ('domains', '0003_auto_20151105_2133'), | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     operations = [ |     operations = [ | ||||||
|         migrations.AlterModelOptions( |         migrations.AlterModelOptions( | ||||||
|             name="dnscomment", |             name='dnscomment', | ||||||
|             options={ |             options={'verbose_name': 'DNS comment', 'verbose_name_plural': 'DNS comments'}, | ||||||
|                 "verbose_name": "DNS comment", |  | ||||||
|                 "verbose_name_plural": "DNS comments", |  | ||||||
|             }, |  | ||||||
|         ), |         ), | ||||||
|         migrations.AlterModelOptions( |         migrations.AlterModelOptions( | ||||||
|             name="dnscryptokey", |             name='dnscryptokey', | ||||||
|             options={ |             options={'verbose_name': 'DNS crypto key', 'verbose_name_plural': 'DNS crypto keys'}, | ||||||
|                 "verbose_name": "DNS crypto key", |  | ||||||
|                 "verbose_name_plural": "DNS crypto keys", |  | ||||||
|             }, |  | ||||||
|         ), |         ), | ||||||
|         migrations.AlterModelOptions( |         migrations.AlterModelOptions( | ||||||
|             name="dnsdomainmetadata", |             name='dnsdomainmetadata', | ||||||
|             options={ |             options={'verbose_name': 'DNS domain metadata item', 'verbose_name_plural': 'DNS domain metadata items'}, | ||||||
|                 "verbose_name": "DNS domain metadata item", |  | ||||||
|                 "verbose_name_plural": "DNS domain metadata items", |  | ||||||
|             }, |  | ||||||
|         ), |         ), | ||||||
|         migrations.AlterModelOptions( |         migrations.AlterModelOptions( | ||||||
|             name="dnssupermaster", |             name='dnssupermaster', | ||||||
|             options={ |             options={'verbose_name': 'DNS supermaster', 'verbose_name_plural': 'DNS supermasters'}, | ||||||
|                 "verbose_name": "DNS supermaster", |  | ||||||
|                 "verbose_name_plural": "DNS supermasters", |  | ||||||
|             }, |  | ||||||
|         ), |         ), | ||||||
|         migrations.AlterModelOptions( |         migrations.AlterModelOptions( | ||||||
|             name="dnstsigkey", |             name='dnstsigkey', | ||||||
|             options={ |             options={'verbose_name': 'DNS TSIG key', 'verbose_name_plural': 'DNS TSIG keys'}, | ||||||
|                 "verbose_name": "DNS TSIG key", |  | ||||||
|                 "verbose_name_plural": "DNS TSIG keys", |  | ||||||
|             }, |  | ||||||
|         ), |         ), | ||||||
|         migrations.AlterField( |         migrations.AlterField( | ||||||
|             model_name="dnsdomainmetadata", |             model_name='dnsdomainmetadata', | ||||||
|             name="kind", |             name='kind', | ||||||
|             field=models.CharField( |             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')]), | ||||||
|                 max_length=32, |  | ||||||
|                 choices=[ |  | ||||||
|                     ("ALLOW-DNSUPDATE-FROM", "ALLOW-DNSUPDATE-FROM"), |  | ||||||
|                     ("ALSO-NOTIFY", "ALSO-NOTIFY"), |  | ||||||
|                     ("AXFR-MASTER-TSIG", "AXFR-MASTER-TSIG"), |  | ||||||
|                     ("AXFR-SOURCE", "AXFR-SOURCE"), |  | ||||||
|                     ("FORWARD-DNSUPDATE", "FORWARD-DNSUPDATE"), |  | ||||||
|                     ("GSS-ACCEPTOR-PRINCIPAL", "GSS-ACCEPTOR-PRINCIPAL"), |  | ||||||
|                     ("GSS-ALLOW-AXFR-PRINCIPAL", "GSS-ALLOW-AXFR-PRINCIPAL"), |  | ||||||
|                     ("LUA-AXFR-SCRIPT", "LUA-AXFR-SCRIPT"), |  | ||||||
|                     ("NSEC3NARROW", "NSEC3NARROW"), |  | ||||||
|                     ("NSEC3PARAM", "NSEC3PARAM"), |  | ||||||
|                     ("PRESIGNED", "PRESIGNED"), |  | ||||||
|                     ("PUBLISH_CDNSKEY", "PUBLISH_CDNSKEY"), |  | ||||||
|                     ("PUBLISH_CDS", "PUBLISH_CDS"), |  | ||||||
|                     ("SOA-EDIT", "SOA-EDIT"), |  | ||||||
|                     ("SOA-EDIT-DNSUPDATE", "SOA-EDIT-DNSUPDATE"), |  | ||||||
|                     ("TSIG-ALLOW-AXFR", "TSIG-ALLOW-AXFR"), |  | ||||||
|                     ("TSIG-ALLOW-DNSUPDATE", "TSIG-ALLOW-DNSUPDATE"), |  | ||||||
|                 ], |  | ||||||
|             ), |  | ||||||
|         ), |         ), | ||||||
|         migrations.AlterField( |         migrations.AlterField( | ||||||
|             model_name="dnstsigkey", |             model_name='dnstsigkey', | ||||||
|             name="algorithm", |             name='algorithm', | ||||||
|             field=models.CharField( |             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')]), | ||||||
|                 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"), |  | ||||||
|                 ], |  | ||||||
|             ), |  | ||||||
|         ), |         ), | ||||||
|     ] |     ] | ||||||
|  |  | ||||||
|  | @ -1,74 +0,0 @@ | ||||||
| # 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,12 +2,52 @@ | ||||||
| This module contains models related to domain names. | This module contains models related to domain names. | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
| from __future__ import absolute_import | from __future__ import absolute_import, unicode_literals | ||||||
| 
 | 
 | ||||||
| from django.conf import settings |  | ||||||
| from django.db import models, transaction | from django.db import models, transaction | ||||||
| from django.utils.translation import gettext as _ | from django.conf import settings | ||||||
|  | from django.utils.encoding import python_2_unicode_compatible | ||||||
|  | from django.utils.translation import ugettext as _ | ||||||
|  | 
 | ||||||
| from model_utils.models import TimeStampedModel | from model_utils.models import TimeStampedModel | ||||||
|  | from model_utils import Choices | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | DNS_DOMAIN_TYPES = Choices( | ||||||
|  |     ('MASTER', _('Master')), | ||||||
|  |     ('SLAVE', _('Slave')), | ||||||
|  |     ('NATIVE', _('Native')), | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | # see https://doc.powerdns.com/md/authoritative/domainmetadata/ | ||||||
|  | DNS_DOMAIN_METADATA_KINDS = Choices( | ||||||
|  |     'ALLOW-DNSUPDATE-FROM', | ||||||
|  |     'ALSO-NOTIFY', | ||||||
|  |     'AXFR-MASTER-TSIG', | ||||||
|  |     'AXFR-SOURCE', | ||||||
|  |     'FORWARD-DNSUPDATE', | ||||||
|  |     'GSS-ACCEPTOR-PRINCIPAL', | ||||||
|  |     'GSS-ALLOW-AXFR-PRINCIPAL', | ||||||
|  |     'LUA-AXFR-SCRIPT', | ||||||
|  |     'NSEC3NARROW', | ||||||
|  |     'NSEC3PARAM', | ||||||
|  |     'PRESIGNED', | ||||||
|  |     'PUBLISH_CDNSKEY', | ||||||
|  |     'PUBLISH_CDS', | ||||||
|  |     'SOA-EDIT', | ||||||
|  |     'SOA-EDIT-DNSUPDATE', | ||||||
|  |     'TSIG-ALLOW-AXFR', | ||||||
|  |     'TSIG-ALLOW-DNSUPDATE', | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | DNS_TSIG_KEY_ALGORITHMS = Choices( | ||||||
|  |     ('hmac-md5', _('HMAC MD5')), | ||||||
|  |     ('hmac-sha1', _('HMAC SHA1')), | ||||||
|  |     ('hmac-sha224', _('HMAC SHA224')), | ||||||
|  |     ('hmac-sha256', _('HMAC SHA256')), | ||||||
|  |     ('hmac-sha384', _('HMAC SHA384')), | ||||||
|  |     ('hmac-sha512', _('HMAC SHA512')), | ||||||
|  | ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class DomainBase(TimeStampedModel): | class DomainBase(TimeStampedModel): | ||||||
|  | @ -15,20 +55,16 @@ class DomainBase(TimeStampedModel): | ||||||
|     This is the base model for domains. |     This is the base model for domains. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |     domain = models.CharField(_('domain name'), max_length=255, unique=True) | ||||||
|     domain = models.CharField(_("domain name"), max_length=255, unique=True) |  | ||||||
|     customer = models.ForeignKey( |     customer = models.ForeignKey( | ||||||
|         settings.AUTH_USER_MODEL, |         settings.AUTH_USER_MODEL, verbose_name=_('customer'), blank=True, | ||||||
|         verbose_name=_("customer"), |         null=True, on_delete=models.CASCADE) | ||||||
|         blank=True, |  | ||||||
|         null=True, |  | ||||||
|         on_delete=models.CASCADE, |  | ||||||
|     ) |  | ||||||
| 
 | 
 | ||||||
|     class Meta: |     class Meta: | ||||||
|         abstract = True |         abstract = True | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @python_2_unicode_compatible | ||||||
| class MailDomain(DomainBase): | class MailDomain(DomainBase): | ||||||
|     """ |     """ | ||||||
|     This is the model for mail domains. Mail domains are used to configure the |     This is the model for mail domains. Mail domains are used to configure the | ||||||
|  | @ -36,10 +72,9 @@ class MailDomain(DomainBase): | ||||||
|     domains. |     domains. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |     class Meta: | ||||||
|     class Meta(DomainBase.Meta): |         verbose_name = _('Mail domain') | ||||||
|         verbose_name = _("Mail domain") |         verbose_name_plural = _('Mail domains') | ||||||
|         verbose_name_plural = _("Mail domains") |  | ||||||
| 
 | 
 | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
|         return self.domain |         return self.domain | ||||||
|  | @ -50,7 +85,6 @@ class MailDomain(DomainBase): | ||||||
| 
 | 
 | ||||||
|         """ |         """ | ||||||
|         return self.mailaddress_set.all() |         return self.mailaddress_set.all() | ||||||
| 
 |  | ||||||
|     mailaddresses = property(get_mailaddresses) |     mailaddresses = property(get_mailaddresses) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -59,47 +93,339 @@ class HostingDomainManager(models.Manager): | ||||||
|     Default Manager for :py:class:`HostingDomain`. |     Default Manager for :py:class:`HostingDomain`. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |  | ||||||
|     @transaction.atomic |     @transaction.atomic | ||||||
|     def create_for_hosting_package(self, hosting_package, domain, commit, **kwargs): |     def create_for_hosting_package( | ||||||
|  |         self, hosting_package, domain, commit, **kwargs | ||||||
|  |     ): | ||||||
|         from hostingpackages.models import CustomerHostingPackageDomain |         from hostingpackages.models import CustomerHostingPackageDomain | ||||||
| 
 |  | ||||||
|         hostingdomain = self.create( |         hostingdomain = self.create( | ||||||
|             customer=hosting_package.customer, domain=domain, **kwargs |             customer=hosting_package.customer, domain=domain, **kwargs) | ||||||
|         ) |  | ||||||
|         hostingdomain.maildomain = MailDomain.objects.create( |         hostingdomain.maildomain = MailDomain.objects.create( | ||||||
|             customer=hosting_package.customer, domain=domain |             customer=hosting_package.customer, domain=domain) | ||||||
|         ) |  | ||||||
|         custdomain = CustomerHostingPackageDomain.objects.create( |         custdomain = CustomerHostingPackageDomain.objects.create( | ||||||
|             hosting_package=hosting_package, domain=hostingdomain |             hosting_package=hosting_package, domain=hostingdomain) | ||||||
|         ) |  | ||||||
|         if commit: |         if commit: | ||||||
|             hostingdomain.save() |             hostingdomain.save() | ||||||
|             custdomain.save() |             custdomain.save() | ||||||
|         return hostingdomain |         return hostingdomain | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @python_2_unicode_compatible | ||||||
| class HostingDomain(DomainBase): | class HostingDomain(DomainBase): | ||||||
|     """ |     """ | ||||||
|     This is the model for hosting domains. A hosting domain is linked to a |     This is the model for hosting domains. A hosting domain is linked to a | ||||||
|     customer hosting account. |     customer hosting account. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |  | ||||||
|     maildomain = models.OneToOneField( |     maildomain = models.OneToOneField( | ||||||
|         MailDomain, |         MailDomain, verbose_name=_('mail domain'), blank=True, null=True, | ||||||
|         verbose_name=_("mail domain"), |         help_text=_('assigned mail domain for this domain'), | ||||||
|         blank=True, |  | ||||||
|         null=True, |  | ||||||
|         help_text=_("assigned mail domain for this domain"), |  | ||||||
|         on_delete=models.CASCADE, |         on_delete=models.CASCADE, | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     objects = HostingDomainManager() |     objects = HostingDomainManager() | ||||||
| 
 | 
 | ||||||
|     class Meta: |     class Meta: | ||||||
|         verbose_name = _("Hosting domain") |         verbose_name = _('Hosting domain') | ||||||
|         verbose_name_plural = _("Hosting domains") |         verbose_name_plural = _('Hosting domains') | ||||||
| 
 | 
 | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
|         return self.domain |         return self.domain | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @python_2_unicode_compatible | ||||||
|  | class DNSDomain(DomainBase): | ||||||
|  |     """ | ||||||
|  |     This model represents a DNS zone. The model is similar to the domain table | ||||||
|  |     in the PowerDNS schema specified in | ||||||
|  |     https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/. | ||||||
|  | 
 | ||||||
|  |     .. code-block:: sql | ||||||
|  | 
 | ||||||
|  |        CREATE TABLE domains ( | ||||||
|  |          id                    SERIAL PRIMARY KEY, | ||||||
|  |          name                  VARCHAR(255) NOT NULL, | ||||||
|  |          master                VARCHAR(128) DEFAULT NULL, | ||||||
|  |          last_check            INT DEFAULT NULL, | ||||||
|  |          type                  VARCHAR(6) NOT NULL, | ||||||
|  |          notified_serial       INT DEFAULT NULL, | ||||||
|  |          account               VARCHAR(40) DEFAULT NULL, | ||||||
|  |          CONSTRAINT c_lowercase_name CHECK ( | ||||||
|  |              ((name)::TEXT = LOWER((name)::TEXT))) | ||||||
|  |        ); | ||||||
|  | 
 | ||||||
|  |        CREATE UNIQUE INDEX name_index ON domains(name); | ||||||
|  | 
 | ||||||
|  |     """ | ||||||
|  |     # name is represented by domain | ||||||
|  |     master = models.CharField(max_length=128, blank=True, null=True) | ||||||
|  |     last_check = models.IntegerField(null=True) | ||||||
|  |     domaintype = models.CharField( | ||||||
|  |         max_length=6, choices=DNS_DOMAIN_TYPES, db_column='type') | ||||||
|  |     notified_serial = models.IntegerField(null=True) | ||||||
|  |     # account is represented by customer_id | ||||||
|  |     # check constraint is added via RunSQL in migration | ||||||
|  | 
 | ||||||
|  |     class Meta: | ||||||
|  |         verbose_name = _('DNS domain') | ||||||
|  |         verbose_name_plural = _('DNS domains') | ||||||
|  | 
 | ||||||
|  |     def __str__(self): | ||||||
|  |         return self.domain | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @python_2_unicode_compatible | ||||||
|  | class DNSRecord(models.Model): | ||||||
|  |     """ | ||||||
|  |     This model represents a DNS record. The model is similar to the record | ||||||
|  |     table in the PowerDNS schema specified in | ||||||
|  |     https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/. | ||||||
|  | 
 | ||||||
|  |     .. code-block:: sql | ||||||
|  | 
 | ||||||
|  |        CREATE TABLE records ( | ||||||
|  |          id                    SERIAL PRIMARY KEY, | ||||||
|  |          domain_id             INT DEFAULT NULL, | ||||||
|  |          name                  VARCHAR(255) DEFAULT NULL, | ||||||
|  |          type                  VARCHAR(10) DEFAULT NULL, | ||||||
|  |          content               VARCHAR(65535) DEFAULT NULL, | ||||||
|  |          ttl                   INT DEFAULT NULL, | ||||||
|  |          prio                  INT DEFAULT NULL, | ||||||
|  |          change_date           INT DEFAULT NULL, | ||||||
|  |          disabled              BOOL DEFAULT 'f', | ||||||
|  |          ordername             VARCHAR(255), | ||||||
|  |          auth                  BOOL DEFAULT 't', | ||||||
|  |          CONSTRAINT domain_exists | ||||||
|  |          FOREIGN KEY(domain_id) REFERENCES domains(id) | ||||||
|  |          ON DELETE CASCADE, | ||||||
|  |          CONSTRAINT c_lowercase_name CHECK ( | ||||||
|  |              ((name)::TEXT = LOWER((name)::TEXT))) | ||||||
|  |        ); | ||||||
|  | 
 | ||||||
|  |        CREATE INDEX rec_name_index ON records(name); | ||||||
|  |        CREATE INDEX nametype_index ON records(name,type); | ||||||
|  |        CREATE INDEX domain_id ON records(domain_id); | ||||||
|  |        CREATE INDEX recordorder ON records ( | ||||||
|  |            domain_id, ordername text_pattern_ops); | ||||||
|  | 
 | ||||||
|  |     """ | ||||||
|  |     domain = models.ForeignKey('DNSDomain', on_delete=models.CASCADE) | ||||||
|  |     name = models.CharField( | ||||||
|  |         max_length=255, blank=True, null=True, db_index=True) | ||||||
|  |     recordtype = models.CharField( | ||||||
|  |         max_length=10, blank=True, null=True, db_column='type') | ||||||
|  |     content = models.CharField(max_length=65535, blank=True, null=True) | ||||||
|  |     ttl = models.IntegerField(null=True) | ||||||
|  |     prio = models.IntegerField(null=True) | ||||||
|  |     change_date = models.IntegerField(null=True) | ||||||
|  |     disabled = models.BooleanField(default=False) | ||||||
|  |     ordername = models.CharField(max_length=255) | ||||||
|  |     auth = models.BooleanField(default=True) | ||||||
|  |     # check constraint and index recordorder are added via RunSQL in migration | ||||||
|  | 
 | ||||||
|  |     class Meta: | ||||||
|  |         verbose_name = _('DNS record') | ||||||
|  |         verbose_name_plural = _('DNS records') | ||||||
|  |         index_together = [ | ||||||
|  |             ['name', 'recordtype'] | ||||||
|  |         ] | ||||||
|  | 
 | ||||||
|  |     def __str__(self): | ||||||
|  |         return "{name} IN {type} {content}".format( | ||||||
|  |             name=self.name, type=self.recordtype, content=self.content) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @python_2_unicode_compatible | ||||||
|  | class DNSSupermaster(models.Model): | ||||||
|  |     """ | ||||||
|  |     This model represents the supermasters table in the PowerDNS schema | ||||||
|  |     specified in | ||||||
|  |     https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/. | ||||||
|  | 
 | ||||||
|  |     .. code-block:: sql | ||||||
|  | 
 | ||||||
|  |        CREATE TABLE supermasters ( | ||||||
|  |          ip                    INET NOT NULL, | ||||||
|  |          nameserver            VARCHAR(255) NOT NULL, | ||||||
|  |          account               VARCHAR(40) NOT NULL, | ||||||
|  |          PRIMARY KEY(ip, nameserver) | ||||||
|  |        ); | ||||||
|  | 
 | ||||||
|  |     """ | ||||||
|  |     ip = models.GenericIPAddressField() | ||||||
|  |     nameserver = models.CharField(max_length=255) | ||||||
|  |     # account is replaced by customer | ||||||
|  |     customer = models.ForeignKey( | ||||||
|  |         settings.AUTH_USER_MODEL, verbose_name=_('customer'), | ||||||
|  |         on_delete=models.CASCADE) | ||||||
|  | 
 | ||||||
|  |     class Meta: | ||||||
|  |         verbose_name = _('DNS supermaster') | ||||||
|  |         verbose_name_plural = _('DNS supermasters') | ||||||
|  |         unique_together = ( | ||||||
|  |             ('ip', 'nameserver') | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     def __str__(self): | ||||||
|  |         return "{ip} {nameserver}".format( | ||||||
|  |             ip=self.ip, nameserver=self.nameserver) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @python_2_unicode_compatible | ||||||
|  | class DNSComment(models.Model): | ||||||
|  |     """ | ||||||
|  |     This model represents the comments table in the PowerDNS schema specified | ||||||
|  |     in https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/. The | ||||||
|  |     comments table is used to store user comments related to individual DNS | ||||||
|  |     records. | ||||||
|  | 
 | ||||||
|  |     .. code-block:: sql | ||||||
|  | 
 | ||||||
|  |        CREATE TABLE comments ( | ||||||
|  |          id                    SERIAL PRIMARY KEY, | ||||||
|  |          domain_id             INT NOT NULL, | ||||||
|  |          name                  VARCHAR(255) NOT NULL, | ||||||
|  |          type                  VARCHAR(10) NOT NULL, | ||||||
|  |          modified_at           INT NOT NULL, | ||||||
|  |          account               VARCHAR(40) DEFAULT NULL, | ||||||
|  |          comment               VARCHAR(65535) NOT NULL, | ||||||
|  |          CONSTRAINT domain_exists | ||||||
|  |          FOREIGN KEY(domain_id) REFERENCES domains(id) | ||||||
|  |          ON DELETE CASCADE, | ||||||
|  |          CONSTRAINT c_lowercase_name CHECK ( | ||||||
|  |              ((name)::TEXT = LOWER((name)::TEXT))) | ||||||
|  |        ); | ||||||
|  | 
 | ||||||
|  |        CREATE INDEX comments_domain_id_idx ON comments (domain_id); | ||||||
|  |        CREATE INDEX comments_name_type_idx ON comments (name, type); | ||||||
|  |        CREATE INDEX comments_order_idx ON comments (domain_id, modified_at); | ||||||
|  | 
 | ||||||
|  |     """ | ||||||
|  |     domain = models.ForeignKey('DNSDomain', on_delete=models.CASCADE) | ||||||
|  |     name = models.CharField(max_length=255) | ||||||
|  |     commenttype = models.CharField(max_length=10, db_column='type') | ||||||
|  |     modified_at = models.IntegerField() | ||||||
|  |     # account is replaced by customer | ||||||
|  |     customer = models.ForeignKey( | ||||||
|  |         settings.AUTH_USER_MODEL, verbose_name=_('customer'), | ||||||
|  |         on_delete=models.CASCADE) | ||||||
|  |     comment = models.CharField(max_length=65535) | ||||||
|  |     # check constraint is added via RunSQL in migration | ||||||
|  | 
 | ||||||
|  |     class Meta: | ||||||
|  |         verbose_name = _('DNS comment') | ||||||
|  |         verbose_name_plural = _('DNS comments') | ||||||
|  |         index_together = [ | ||||||
|  |             ['name', 'commenttype'], | ||||||
|  |             ['domain', 'modified_at'] | ||||||
|  |         ] | ||||||
|  | 
 | ||||||
|  |     def __str__(self): | ||||||
|  |         return "{name} IN {type}: {comment}".format( | ||||||
|  |             name=self.name, type=self.commenttype, comment=self.comment) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @python_2_unicode_compatible | ||||||
|  | class DNSDomainMetadata(models.Model): | ||||||
|  |     """ | ||||||
|  |     This model represents the domainmetadata table in the PowerDNS schema | ||||||
|  |     specified in | ||||||
|  |     https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/. | ||||||
|  |     The domainmetadata table is used to store domain meta data as described in | ||||||
|  |     https://doc.powerdns.com/md/authoritative/domainmetadata/. | ||||||
|  | 
 | ||||||
|  |     .. code-block:: sql | ||||||
|  | 
 | ||||||
|  |        CREATE TABLE domainmetadata ( | ||||||
|  |          id                    SERIAL PRIMARY KEY, | ||||||
|  |          domain_id             INT REFERENCES domains(id) ON DELETE CASCADE, | ||||||
|  |          kind                  VARCHAR(32), | ||||||
|  |          content               TEXT | ||||||
|  |        ); | ||||||
|  | 
 | ||||||
|  |        CREATE INDEX domainidmetaindex ON domainmetadata(domain_id); | ||||||
|  | 
 | ||||||
|  |     """ | ||||||
|  |     domain = models.ForeignKey('DNSDomain', on_delete=models.CASCADE) | ||||||
|  |     kind = models.CharField(max_length=32, choices=DNS_DOMAIN_METADATA_KINDS) | ||||||
|  |     content = models.TextField() | ||||||
|  | 
 | ||||||
|  |     class Meta: | ||||||
|  |         verbose_name = _('DNS domain metadata item') | ||||||
|  |         verbose_name_plural = _('DNS domain metadata items') | ||||||
|  | 
 | ||||||
|  |     def __str__(self): | ||||||
|  |         return "{domain} {kind} {content}".format( | ||||||
|  |             domain=self.domain.domain, kind=self.kind, content=self.content) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @python_2_unicode_compatible | ||||||
|  | class DNSCryptoKey(models.Model): | ||||||
|  |     """ | ||||||
|  |     This model represents the cryptokeys table in the PowerDNS schema | ||||||
|  |     specified in | ||||||
|  |     https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/. | ||||||
|  | 
 | ||||||
|  |     .. code-block:: sql | ||||||
|  | 
 | ||||||
|  |        CREATE TABLE cryptokeys ( | ||||||
|  |          id                    SERIAL PRIMARY KEY, | ||||||
|  |          domain_id             INT REFERENCES domains(id) ON DELETE CASCADE, | ||||||
|  |          flags                 INT NOT NULL, | ||||||
|  |          active                BOOL, | ||||||
|  |          content               TEXT | ||||||
|  |        ); | ||||||
|  | 
 | ||||||
|  |        CREATE INDEX domainidindex ON cryptokeys(domain_id); | ||||||
|  | 
 | ||||||
|  |     """ | ||||||
|  |     domain = models.ForeignKey('DNSDomain', on_delete=models.CASCADE) | ||||||
|  |     flags = models.IntegerField() | ||||||
|  |     active = models.BooleanField(default=True) | ||||||
|  |     content = models.TextField() | ||||||
|  | 
 | ||||||
|  |     class Meta: | ||||||
|  |         verbose_name = _('DNS crypto key') | ||||||
|  |         verbose_name_plural = _('DNS crypto keys') | ||||||
|  | 
 | ||||||
|  |     def __str__(self): | ||||||
|  |         return "{domain} {content}".format( | ||||||
|  |             domain=self.domain.domain, content=self.content) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @python_2_unicode_compatible | ||||||
|  | class DNSTSIGKey(models.Model): | ||||||
|  |     """ | ||||||
|  |     This model represents the tsigkeys table in the PowerDNS schema specified | ||||||
|  |     in https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/. | ||||||
|  | 
 | ||||||
|  |     .. code-block:: sql | ||||||
|  | 
 | ||||||
|  |        CREATE TABLE tsigkeys ( | ||||||
|  |          id                    SERIAL PRIMARY KEY, | ||||||
|  |          name                  VARCHAR(255), | ||||||
|  |          algorithm             VARCHAR(50), | ||||||
|  |          secret                VARCHAR(255), | ||||||
|  |          CONSTRAINT c_lowercase_name CHECK ( | ||||||
|  |              ((name)::TEXT = LOWER((name)::TEXT))) | ||||||
|  |        ); | ||||||
|  | 
 | ||||||
|  |        CREATE UNIQUE INDEX namealgoindex ON tsigkeys(name, algorithm); | ||||||
|  | 
 | ||||||
|  |     """ | ||||||
|  |     name = models.CharField(max_length=255) | ||||||
|  |     algorithm = models.CharField( | ||||||
|  |         max_length=50, choices=DNS_TSIG_KEY_ALGORITHMS) | ||||||
|  |     secret = models.CharField(max_length=255) | ||||||
|  |     # check constraint is added via RunSQL in migration | ||||||
|  | 
 | ||||||
|  |     class Meta: | ||||||
|  |         verbose_name = _('DNS TSIG key') | ||||||
|  |         verbose_name_plural = _('DNS TSIG keys') | ||||||
|  |         unique_together = [ | ||||||
|  |             ['name', 'algorithm'] | ||||||
|  |         ] | ||||||
|  | 
 | ||||||
|  |     def __str__(self): | ||||||
|  |         return "{name} {algorithm} XXXX".format( | ||||||
|  |             name=self.name, algorithm=self.algorithm) | ||||||
|  |  | ||||||
|  | @ -7,9 +7,9 @@ from unittest.mock import MagicMock, Mock, patch | ||||||
| from django.forms import ValidationError | from django.forms import ValidationError | ||||||
| from django.test import TestCase | from django.test import TestCase | ||||||
| from django.urls import reverse | from django.urls import reverse | ||||||
| from django.utils.translation import gettext as _ | from django.utils.translation import ugettext as _ | ||||||
| 
 | 
 | ||||||
| from domains.forms import CreateHostingDomainForm, relative_domain_validator | from domains.forms import relative_domain_validator, CreateHostingDomainForm | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class RelativeDomainValidatorTest(TestCase): | class RelativeDomainValidatorTest(TestCase): | ||||||
|  |  | ||||||
|  | @ -7,10 +7,20 @@ from unittest.mock import patch | ||||||
| from django.test import TestCase | from django.test import TestCase | ||||||
| from django.contrib.auth import get_user_model | from django.contrib.auth import get_user_model | ||||||
| 
 | 
 | ||||||
| from domains.models import HostingDomain, MailDomain | from domains.models import ( | ||||||
| 
 |     DNSComment, | ||||||
|  |     DNSCryptoKey, | ||||||
|  |     DNSDomain, | ||||||
|  |     DNSDomainMetadata, | ||||||
|  |     DNSRecord, | ||||||
|  |     DNSSupermaster, | ||||||
|  |     DNSTSIGKey, | ||||||
|  |     HostingDomain, | ||||||
|  |     MailDomain, | ||||||
|  | ) | ||||||
| from hostingpackages.models import CustomerHostingPackage, HostingPackageTemplate | from hostingpackages.models import CustomerHostingPackage, HostingPackageTemplate | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| User = get_user_model() | User = get_user_model() | ||||||
| 
 | 
 | ||||||
| TEST_USER = "test" | TEST_USER = "test" | ||||||
|  | @ -67,3 +77,49 @@ class HostingDomainTest(TestCase): | ||||||
|     def test___str__(self): |     def test___str__(self): | ||||||
|         hostingdomain = HostingDomain(domain="test") |         hostingdomain = HostingDomain(domain="test") | ||||||
|         self.assertEqual(str(hostingdomain), "test") |         self.assertEqual(str(hostingdomain), "test") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class DNSDomainTest(TestCase): | ||||||
|  |     def test___str__(self): | ||||||
|  |         dnsdomain = DNSDomain(domain="test") | ||||||
|  |         self.assertEqual(str(dnsdomain), "test") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class DNSRecordTest(TestCase): | ||||||
|  |     def test___str__(self): | ||||||
|  |         dnsrecord = DNSRecord(name="localhost", recordtype="A", content="127.0.0.1") | ||||||
|  |         self.assertEqual(str(dnsrecord), "localhost IN A 127.0.0.1") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class DNSSupermasterTest(TestCase): | ||||||
|  |     def test___str__(self): | ||||||
|  |         dnssupermaster = DNSSupermaster(ip="127.0.0.1", nameserver="dns.example.org") | ||||||
|  |         self.assertEqual(str(dnssupermaster), "127.0.0.1 dns.example.org") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class DNSCommentTest(TestCase): | ||||||
|  |     def test___str__(self): | ||||||
|  |         dnscomment = DNSComment(name="localhost", commenttype="A", comment="good stuff") | ||||||
|  |         self.assertEqual(str(dnscomment), "localhost IN A: good stuff") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class DNSDomainMetadataTest(TestCase): | ||||||
|  |     def test___str__(self): | ||||||
|  |         dnsdomain = DNSDomain(domain="test") | ||||||
|  |         dnsdomainmetadata = DNSDomainMetadata( | ||||||
|  |             domain=dnsdomain, kind="SOA-EDIT", content="INCEPTION" | ||||||
|  |         ) | ||||||
|  |         self.assertEqual(str(dnsdomainmetadata), "test SOA-EDIT INCEPTION") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class DNSCryptoKeyTest(TestCase): | ||||||
|  |     def test___str__(self): | ||||||
|  |         dnsdomain = DNSDomain(domain="test") | ||||||
|  |         dnscryptokey = DNSCryptoKey(domain=dnsdomain, content="testvalue") | ||||||
|  |         self.assertEqual(str(dnscryptokey), "test testvalue") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class DNSTSIGKeyTest(TestCase): | ||||||
|  |     def test___str__(self): | ||||||
|  |         dnstsigkey = DNSTSIGKey(name="testkey", algorithm="hmac-md5", secret="dummykey") | ||||||
|  |         self.assertEqual(str(dnstsigkey), "testkey hmac-md5 XXXX") | ||||||
|  |  | ||||||
|  | @ -2,16 +2,14 @@ | ||||||
| This module defines the URL patterns for domain related views. | This module defines the URL patterns for domain related views. | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
| from __future__ import absolute_import | from __future__ import absolute_import, unicode_literals | ||||||
| 
 | 
 | ||||||
| from django.urls import re_path | from django.conf.urls import url | ||||||
| 
 | 
 | ||||||
| from .views import CreateHostingDomain | from .views import CreateHostingDomain | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
|     re_path( |     url(r'^(?P<package>\d+)/create$', CreateHostingDomain.as_view(), | ||||||
|         r"^(?P<package>\d+)/create$", |         name='create_hosting_domain'), | ||||||
|         CreateHostingDomain.as_view(), |  | ||||||
|         name="create_hosting_domain", |  | ||||||
|     ), |  | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | @ -2,21 +2,20 @@ | ||||||
| This module defines views related to domains. | This module defines views related to domains. | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
| from __future__ import absolute_import | from __future__ import absolute_import, unicode_literals | ||||||
| 
 | 
 | ||||||
|  | from braces.views import StaffuserRequiredMixin | ||||||
| from django.contrib import messages | from django.contrib import messages | ||||||
| from django.contrib.auth.mixins import PermissionRequiredMixin |  | ||||||
| from django.shortcuts import get_object_or_404, redirect | from django.shortcuts import get_object_or_404, redirect | ||||||
| from django.utils.translation import gettext as _ | from django.utils.translation import ugettext as _ | ||||||
| from django.views.generic.edit import CreateView | from django.views.generic.edit import CreateView | ||||||
| 
 | 
 | ||||||
| from hostingpackages.models import CustomerHostingPackage | from hostingpackages.models import CustomerHostingPackage | ||||||
| 
 |  | ||||||
| from .forms import CreateHostingDomainForm | from .forms import CreateHostingDomainForm | ||||||
| from .models import HostingDomain | from .models import HostingDomain | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class CreateHostingDomain(PermissionRequiredMixin, CreateView): | class CreateHostingDomain(StaffuserRequiredMixin, CreateView): | ||||||
|     """ |     """ | ||||||
|     This view is used for creating a new HostingDomain instance for an existing |     This view is used for creating a new HostingDomain instance for an existing | ||||||
|     hosting package. |     hosting package. | ||||||
|  | @ -24,7 +23,6 @@ class CreateHostingDomain(PermissionRequiredMixin, CreateView): | ||||||
| 
 | 
 | ||||||
|     model = HostingDomain |     model = HostingDomain | ||||||
|     raise_exception = True |     raise_exception = True | ||||||
|     permission_required = 'domains.add_hostingdomain' |  | ||||||
|     template_name_suffix = "_create" |     template_name_suffix = "_create" | ||||||
|     form_class = CreateHostingDomainForm |     form_class = CreateHostingDomainForm | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| # import celery_app to initialize it | # import celery_app to initialize it | ||||||
| from gnuviechadmin.celery import app as celery_app  # NOQA | from gnuviechadmin.celery import app as celery_app  # NOQA | ||||||
| 
 | 
 | ||||||
| __version__ = "0.13.0" | __version__ = '0.12.1' | ||||||
|  |  | ||||||
|  | @ -1,11 +0,0 @@ | ||||||
| from allauth.account.adapter import DefaultAccountAdapter |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class NoNewUsersAccountAdapter(DefaultAccountAdapter): |  | ||||||
|     """ |  | ||||||
|     Adapter to disable allauth new signups |  | ||||||
| 
 |  | ||||||
|     """ |  | ||||||
| 
 |  | ||||||
|     def is_open_for_signup(self, request): |  | ||||||
|         return False |  | ||||||
|  | @ -6,15 +6,15 @@ from celery import Celery | ||||||
| 
 | 
 | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| 
 | 
 | ||||||
| os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gnuviechadmin.settings") | os.environ.setdefault('DJANGO_SETTINGS_MODULE', | ||||||
|  |                       'gnuviechadmin.settings.production') | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| app = Celery("gnuviechadmin") | app = Celery('gnuviechadmin') | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def get_installed_apps(): | def get_installed_apps(): | ||||||
|     return settings.INSTALLED_APPS |     return settings.INSTALLED_APPS | ||||||
| 
 | 
 | ||||||
| 
 | app.config_from_object('django.conf:settings') | ||||||
| app.config_from_object("django.conf:settings") |  | ||||||
| app.autodiscover_tasks(get_installed_apps) | app.autodiscover_tasks(get_installed_apps) | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| This module provides context processor implementations for gnuviechadmin. | This module provides context processor implementations for gnuviechadmin. | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
| from __future__ import absolute_import | from __future__ import absolute_import, unicode_literals | ||||||
| 
 | 
 | ||||||
| import logging | import logging | ||||||
| 
 | 
 | ||||||
|  | @ -22,42 +22,38 @@ def navigation(request): | ||||||
|     :rtype: dict |     :rtype: dict | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
|     if request.headers.get("x-requested-with") == "XMLHttpRequest": |     if request.is_ajax(): | ||||||
|         return {} |         return {} | ||||||
|     context = { |     context = { | ||||||
|         "webmail_url": settings.GVA_LINK_WEBMAIL, |         'webmail_url': settings.GVA_LINK_WEBMAIL, | ||||||
|         "phpmyadmin_url": settings.GVA_LINK_PHPMYADMIN, |         'phpmyadmin_url': settings.GVA_LINK_PHPMYADMIN, | ||||||
|         "phppgadmin_url": settings.GVA_LINK_PHPPGADMIN, |         'phppgadmin_url': settings.GVA_LINK_PHPPGADMIN, | ||||||
|         "active_item": "dashboard", |         'active_item': 'dashboard', | ||||||
|     } |     } | ||||||
|     if request.resolver_match: |     if request.resolver_match: | ||||||
|         viewfunc = request.resolver_match.func |         viewfunc = request.resolver_match.func | ||||||
|         viewmodule = viewfunc.__module__ |         viewmodule = viewfunc.__module__ | ||||||
|         if viewmodule == "contact_form.views": |         if viewmodule == 'contact_form.views': | ||||||
|             context["active_item"] = "contact" |             context['active_item'] = 'contact' | ||||||
|         elif viewmodule in ( |         elif viewmodule in ( | ||||||
|             "hostingpackages.views", |             'hostingpackages.views', 'osusers.views', 'userdbs.views', | ||||||
|             "osusers.views", |             'managemails.views', 'websites.views', 'domains.views', | ||||||
|             "userdbs.views", |  | ||||||
|             "managemails.views", |  | ||||||
|             "websites.views", |  | ||||||
|             "domains.views", |  | ||||||
|         ): |         ): | ||||||
|             context["active_item"] = "hostingpackage" |             context['active_item'] = 'hostingpackage' | ||||||
|         elif viewmodule in ("allauth.account.views", "allauth.socialaccount.views"): |         elif viewmodule in ( | ||||||
|             context["active_item"] = "account" |             'allauth.account.views', 'allauth.socialaccount.views' | ||||||
|         elif viewmodule == "django.contrib.flatpages.views" and request.path.endswith( |  | ||||||
|             "/impressum/" |  | ||||||
|         ): |         ): | ||||||
|             context["active_item"] = "imprint" |             context['active_item'] = 'account' | ||||||
|         elif not viewmodule.startswith("django.contrib.admin"): |         elif ( | ||||||
|  |             viewmodule == 'django.contrib.flatpages.views' and | ||||||
|  |             request.path.endswith('/impressum/') | ||||||
|  |         ): | ||||||
|  |             context['active_item'] = 'imprint' | ||||||
|  |         elif not viewmodule.startswith('django.contrib.admin'): | ||||||
|             _LOGGER.debug( |             _LOGGER.debug( | ||||||
|                 "no special handling for view %s in module %s, fallback to " |                 'no special handling for view %s in module %s, fallback to ' | ||||||
|                 "default active menu item %s", |                 'default active menu item %s', | ||||||
|                 viewfunc.__name__, |                 viewfunc.__name__, viewmodule, context['active_item']) | ||||||
|                 viewmodule, |  | ||||||
|                 context["active_item"], |  | ||||||
|             ) |  | ||||||
|     return context |     return context | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -68,6 +64,6 @@ def version_info(request): | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
|     context = { |     context = { | ||||||
|         "gnuviechadmin_version": gvaversion, |         'gnuviechadmin_version': gvaversion, | ||||||
|     } |     } | ||||||
|     return context |     return context | ||||||
|  |  | ||||||
|  | @ -8,8 +8,10 @@ Common settings and globals. | ||||||
| from os.path import abspath, basename, dirname, join, normpath | from os.path import abspath, basename, dirname, join, normpath | ||||||
| 
 | 
 | ||||||
| from django.contrib.messages import constants as messages | from django.contrib.messages import constants as messages | ||||||
|  | 
 | ||||||
| from gvacommon.settings_utils import get_env_variable | from gvacommon.settings_utils import get_env_variable | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| # ######### PATH CONFIGURATION | # ######### PATH CONFIGURATION | ||||||
| # Absolute filesystem path to the Django project directory: | # Absolute filesystem path to the Django project directory: | ||||||
| DJANGO_ROOT = dirname(dirname(abspath(__file__))) | DJANGO_ROOT = dirname(dirname(abspath(__file__))) | ||||||
|  | @ -22,11 +24,10 @@ SITE_NAME = basename(DJANGO_ROOT) | ||||||
| 
 | 
 | ||||||
| # ######### END PATH CONFIGURATION | # ######### END PATH CONFIGURATION | ||||||
| 
 | 
 | ||||||
| GVA_ENVIRONMENT = get_env_variable("GVA_ENVIRONMENT", default="prod") |  | ||||||
| 
 | 
 | ||||||
| # ######### DEBUG CONFIGURATION | # ######### DEBUG CONFIGURATION | ||||||
| # See: https://docs.djangoproject.com/en/dev/ref/settings/#debug | # See: https://docs.djangoproject.com/en/dev/ref/settings/#debug | ||||||
| DEBUG = GVA_ENVIRONMENT == "local" | DEBUG = False | ||||||
| # ######### END DEBUG CONFIGURATION | # ######### END DEBUG CONFIGURATION | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -56,8 +57,6 @@ DATABASES = { | ||||||
|         "PORT": get_env_variable("GVA_PGSQL_PORT", int, default=5432), |         "PORT": get_env_variable("GVA_PGSQL_PORT", int, default=5432), | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 |  | ||||||
| DEFAULT_AUTO_FIELD = "django.db.models.AutoField" |  | ||||||
| # ######### END DATABASE CONFIGURATION | # ######### END DATABASE CONFIGURATION | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -86,6 +85,7 @@ USE_TZ = True | ||||||
| 
 | 
 | ||||||
| LOCALE_PATHS = (normpath(join(SITE_ROOT, "gnuviechadmin", "locale")),) | LOCALE_PATHS = (normpath(join(SITE_ROOT, "gnuviechadmin", "locale")),) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| # ######### MEDIA CONFIGURATION | # ######### MEDIA CONFIGURATION | ||||||
| # See: https://docs.djangoproject.com/en/dev/ref/settings/#media-root | # See: https://docs.djangoproject.com/en/dev/ref/settings/#media-root | ||||||
| MEDIA_ROOT = normpath(join(SITE_ROOT, "media")) | MEDIA_ROOT = normpath(join(SITE_ROOT, "media")) | ||||||
|  | @ -179,6 +179,7 @@ AUTHENTICATION_BACKENDS = ( | ||||||
|     "allauth.account.auth_backends.AuthenticationBackend", |     "allauth.account.auth_backends.AuthenticationBackend", | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| # ######### URL CONFIGURATION | # ######### URL CONFIGURATION | ||||||
| # See: https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf | # See: https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf | ||||||
| ROOT_URLCONF = "%s.urls" % SITE_NAME | ROOT_URLCONF = "%s.urls" % SITE_NAME | ||||||
|  | @ -206,10 +207,6 @@ DJANGO_APPS = ( | ||||||
|     # Flatpages for about page |     # Flatpages for about page | ||||||
|     "django.contrib.flatpages", |     "django.contrib.flatpages", | ||||||
|     "crispy_forms", |     "crispy_forms", | ||||||
|     "crispy_bootstrap5", |  | ||||||
|     "impersonate", |  | ||||||
|     "rest_framework", |  | ||||||
|     "rest_framework.authtoken", |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| ALLAUTH_APPS = ( | ALLAUTH_APPS = ( | ||||||
|  | @ -218,6 +215,7 @@ ALLAUTH_APPS = ( | ||||||
|     "allauth.socialaccount", |     "allauth.socialaccount", | ||||||
|     "allauth.socialaccount.providers.google", |     "allauth.socialaccount.providers.google", | ||||||
|     "allauth.socialaccount.providers.linkedin_oauth2", |     "allauth.socialaccount.providers.linkedin_oauth2", | ||||||
|  |     "allauth.socialaccount.providers.twitter", | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| # Apps specific for this project go here. | # Apps specific for this project go here. | ||||||
|  | @ -235,8 +233,6 @@ LOCAL_APPS = ( | ||||||
|     "userdbs", |     "userdbs", | ||||||
|     "hostingpackages", |     "hostingpackages", | ||||||
|     "websites", |     "websites", | ||||||
|     "help", |  | ||||||
|     "invoices", |  | ||||||
|     "contact_form", |     "contact_form", | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -254,38 +250,18 @@ MESSAGE_TAGS = { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # ######### ALLAUTH CONFIGURATION | # ######### ALLAUTH CONFIGURATION | ||||||
| ACCOUNT_ADAPTER = "gnuviechadmin.auth.NoNewUsersAccountAdapter" |  | ||||||
| ACCOUNT_EMAIL_REQUIRED = True | ACCOUNT_EMAIL_REQUIRED = True | ||||||
| ACCOUNT_EMAIL_VERIFICATION = "mandatory" | ACCOUNT_EMAIL_VERIFICATION = "mandatory" | ||||||
| LOGIN_REDIRECT_URL = "/" | LOGIN_REDIRECT_URL = "/" | ||||||
| SOCIALACCOUNT_AUTO_SIGNUP = False |  | ||||||
| SOCIALACCOUNT_QUERY_EMAIL = True | SOCIALACCOUNT_QUERY_EMAIL = True | ||||||
| # ######### END ALLAUTH CONFIGURATION | # ######### END ALLAUTH CONFIGURATION | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # ######### CRISPY FORMS CONFIGURATION | # ######### CRISPY FORMS CONFIGURATION | ||||||
| CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5" | CRISPY_TEMPLATE_PACK = "bootstrap3" | ||||||
| CRISPY_TEMPLATE_PACK = "bootstrap5" |  | ||||||
| # ######### END CRISPY_FORMS CONFIGURATION | # ######### END CRISPY_FORMS CONFIGURATION | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # ######### REST FRAMEWORK CONFIGURATION |  | ||||||
| REST_FRAMEWORK = { |  | ||||||
|     "DEFAULT_AUTHENTICATION_CLASSES": [ |  | ||||||
|         "rest_framework.authentication.BasicAuthentication", |  | ||||||
|         "rest_framework.authentication.SessionAuthentication", |  | ||||||
|         "rest_framework.authentication.TokenAuthentication", |  | ||||||
|     ], |  | ||||||
|     "DEFAULT_RENDERER_CLASSES": [ |  | ||||||
|         "rest_framework.renderers.JSONRenderer", |  | ||||||
|     ], |  | ||||||
|     "DEFAULT_PERMISSION_CLASSES": [ |  | ||||||
|         "rest_framework.permissions.IsAdminUser", |  | ||||||
|     ], |  | ||||||
| } |  | ||||||
| # ######### END REST FRAMEWORK CONFIGURATION |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # ######### LOGGING CONFIGURATION | # ######### LOGGING CONFIGURATION | ||||||
| # See: https://docs.djangoproject.com/en/dev/ref/settings/#logging | # See: https://docs.djangoproject.com/en/dev/ref/settings/#logging | ||||||
| # A sample logging configuration. The only tangible logging | # A sample logging configuration. The only tangible logging | ||||||
|  | @ -305,45 +281,20 @@ LOGGING = { | ||||||
|     }, |     }, | ||||||
|     "filters": {"require_debug_false": {"()": "django.utils.log.RequireDebugFalse"}}, |     "filters": {"require_debug_false": {"()": "django.utils.log.RequireDebugFalse"}}, | ||||||
|     "handlers": { |     "handlers": { | ||||||
|         "console": { |  | ||||||
|             "class": "logging.StreamHandler", |  | ||||||
|         }, |  | ||||||
|         "logfile": { |  | ||||||
|             "level": "INFO", |  | ||||||
|             "class": "logging.FileHandler", |  | ||||||
|             "filename": get_env_variable("GVA_LOG_FILE", default="gva.log"), |  | ||||||
|             "formatter": "verbose", |  | ||||||
|         }, |  | ||||||
|         "mail_admins": { |         "mail_admins": { | ||||||
|             "level": "ERROR", |             "level": "ERROR", | ||||||
|             "filters": ["require_debug_false"], |             "filters": ["require_debug_false"], | ||||||
|             "class": "django.utils.log.AdminEmailHandler", |             "class": "django.utils.log.AdminEmailHandler", | ||||||
|         }, |         } | ||||||
|     }, |  | ||||||
|     "root": { |  | ||||||
|         "handlers": ["console"], |  | ||||||
|         "level": "WARNING", |  | ||||||
|     }, |     }, | ||||||
|     "loggers": { |     "loggers": { | ||||||
|         "django.request": { |         "django.request": { | ||||||
|             "handlers": ["mail_admins"], |             "handlers": ["mail_admins"], | ||||||
|             "level": "ERROR", |             "level": "ERROR", | ||||||
|             "propagate": True, |             "propagate": True, | ||||||
|         }, |         } | ||||||
|         "django": { |  | ||||||
|             "handlers": ["logfile"], |  | ||||||
|             "level": "INFO", |  | ||||||
|             "propagate": False, |  | ||||||
|         }, |  | ||||||
|     }, |     }, | ||||||
| } | } | ||||||
| 
 |  | ||||||
| for app in LOCAL_APPS: |  | ||||||
|     LOGGING["loggers"][app] = { |  | ||||||
|         "handlers": ["logfile"], |  | ||||||
|         "level": "INFO", |  | ||||||
|         "propagate": False, |  | ||||||
|     } |  | ||||||
| # ######### END LOGGING CONFIGURATION | # ######### END LOGGING CONFIGURATION | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -400,6 +351,8 @@ GVA_LINK_PHPPGADMIN = get_env_variable( | ||||||
| ) | ) | ||||||
| # ######### END CUSTOM APP CONFIGURATION | # ######### END CUSTOM APP CONFIGURATION | ||||||
| 
 | 
 | ||||||
|  | GVA_ENVIRONMENT = get_env_variable("GVA_ENVIRONMENT", default="prod") | ||||||
|  | 
 | ||||||
| # ######### STATIC FILE CONFIGURATION | # ######### STATIC FILE CONFIGURATION | ||||||
| # See: https://docs.djangoproject.com/en/dev/ref/settings/#static-root | # See: https://docs.djangoproject.com/en/dev/ref/settings/#static-root | ||||||
| STATIC_ROOT = "/srv/gva/static/" | STATIC_ROOT = "/srv/gva/static/" | ||||||
|  | @ -409,22 +362,11 @@ def show_debug_toolbar(request): | ||||||
|     return DEBUG and GVA_ENVIRONMENT == "local" |     return DEBUG and GVA_ENVIRONMENT == "local" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # ######### 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": | if GVA_ENVIRONMENT == "local": | ||||||
|  |     # ######### DEBUG CONFIGURATION | ||||||
|  |     # See: https://docs.djangoproject.com/en/dev/ref/settings/#debug | ||||||
|  |     DEBUG = True | ||||||
|  | 
 | ||||||
|     # See: https://docs.djangoproject.com/en/dev/ref/settings/#template-debug |     # See: https://docs.djangoproject.com/en/dev/ref/settings/#template-debug | ||||||
|     TEMPLATES[0]["OPTIONS"]["debug"] = DEBUG |     TEMPLATES[0]["OPTIONS"]["debug"] = DEBUG | ||||||
|     # ######### END DEBUG CONFIGURATION |     # ######### END DEBUG CONFIGURATION | ||||||
|  | @ -439,6 +381,12 @@ if GVA_ENVIRONMENT == "local": | ||||||
|     CACHES = {"default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}} |     CACHES = {"default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}} | ||||||
|     # ######### END CACHE CONFIGURATION |     # ######### END CACHE CONFIGURATION | ||||||
| 
 | 
 | ||||||
|  |     # ######### TOOLBAR CONFIGURATION | ||||||
|  |     # See: http://django-debug-toolbar.readthedocs.org/en/latest/installation.html#explicit-setup # noqa | ||||||
|  |     INSTALLED_APPS += ("debug_toolbar",) | ||||||
|  | 
 | ||||||
|  |     MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"] | ||||||
|  | 
 | ||||||
|     LOGGING["handlers"].update( |     LOGGING["handlers"].update( | ||||||
|         { |         { | ||||||
|             "console": { |             "console": { | ||||||
|  | @ -452,10 +400,32 @@ if GVA_ENVIRONMENT == "local": | ||||||
|         dict( |         dict( | ||||||
|             [ |             [ | ||||||
|                 (key, {"handlers": ["console"], "level": "DEBUG", "propagate": True}) |                 (key, {"handlers": ["console"], "level": "DEBUG", "propagate": True}) | ||||||
|                 for key in LOCAL_APPS |                 for key in [ | ||||||
|             ], |                     "dashboard", | ||||||
|  |                     "domains", | ||||||
|  |                     "fileservertasks", | ||||||
|  |                     "gvacommon", | ||||||
|  |                     "gvawebcore", | ||||||
|  |                     "hostingpackages", | ||||||
|  |                     "ldaptasks", | ||||||
|  |                     "managemails", | ||||||
|  |                     "mysqltasks", | ||||||
|  |                     "osusers", | ||||||
|  |                     "pgsqltasks", | ||||||
|  |                     "taskresults", | ||||||
|  |                     "userdbs", | ||||||
|  |                     "websites", | ||||||
|  |                 ] | ||||||
|  |             ] | ||||||
|         ) |         ) | ||||||
|     ) |     ) | ||||||
|  | 
 | ||||||
|  |     DEBUG_TOOLBAR_PATCH_SETTINGS = False | ||||||
|  |     DEBUG_TOOLBAR_CONFIG = { | ||||||
|  |         "SHOW_TOOLBAR_CALLBACK": "gnuviechadmin.settings.show_debug_toolbar" | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     # ######### END TOOLBAR CONFIGURATION | ||||||
| elif GVA_ENVIRONMENT == "test": | elif GVA_ENVIRONMENT == "test": | ||||||
|     ALLOWED_HOSTS = ["localhost"] |     ALLOWED_HOSTS = ["localhost"] | ||||||
|     PASSWORD_HASHERS = ("django.contrib.auth.hashers.MD5PasswordHasher",) |     PASSWORD_HASHERS = ("django.contrib.auth.hashers.MD5PasswordHasher",) | ||||||
|  | @ -472,15 +442,25 @@ elif GVA_ENVIRONMENT == "test": | ||||||
|         dict( |         dict( | ||||||
|             [ |             [ | ||||||
|                 (key, {"handlers": ["console"], "level": "ERROR", "propagate": True}) |                 (key, {"handlers": ["console"], "level": "ERROR", "propagate": True}) | ||||||
|                 for key in LOCAL_APPS |                 for key in [ | ||||||
|  |                     "dashboard", | ||||||
|  |                     "domains", | ||||||
|  |                     "fileservertasks", | ||||||
|  |                     "gvacommon", | ||||||
|  |                     "gvawebcore", | ||||||
|  |                     "hostingpackages", | ||||||
|  |                     "ldaptasks", | ||||||
|  |                     "managemails", | ||||||
|  |                     "mysqltasks", | ||||||
|  |                     "osusers", | ||||||
|  |                     "pgsqltasks", | ||||||
|  |                     "taskresults", | ||||||
|  |                     "userdbs", | ||||||
|  |                     "websites", | ||||||
|  |                 ] | ||||||
|             ] |             ] | ||||||
|         ) |         ) | ||||||
|     ) |     ) | ||||||
|     LOGGING["loggers"]["django"] = { |  | ||||||
|         "handlers": ["console"], |  | ||||||
|         "level": "CRITICAL", |  | ||||||
|         "propagate": True, |  | ||||||
|     } |  | ||||||
|     BROKER_URL = BROKER_URL + "_test" |     BROKER_URL = BROKER_URL + "_test" | ||||||
|     CELERY_RESULT_PERSISTENT = False |     CELERY_RESULT_PERSISTENT = False | ||||||
| else: | else: | ||||||
|  |  | ||||||
|  | @ -18,16 +18,13 @@ from gnuviechadmin.context_processors import navigation | ||||||
| 
 | 
 | ||||||
| User = get_user_model() | User = get_user_model() | ||||||
| 
 | 
 | ||||||
| TEST_USER = "test" |  | ||||||
| TEST_PASSWORD = "secret" |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| class NavigationContextProcessorTest(TestCase): | class NavigationContextProcessorTest(TestCase): | ||||||
| 
 | 
 | ||||||
|     EXPECTED_ITEMS = ("webmail_url", "phpmyadmin_url", "phppgadmin_url", "active_item") |     EXPECTED_ITEMS = ("webmail_url", "phpmyadmin_url", "phppgadmin_url", "active_item") | ||||||
| 
 | 
 | ||||||
|     def test_ajax_request(self): |     def test_ajax_request(self): | ||||||
|         response = self.client.get("/accounts/login/", HTTP_X_REQUESTED_WITH="XMLHttpRequest") |         response = self.client.get("/", HTTP_X_REQUESTED_WITH="XMLHttpRequest") | ||||||
|         for item in self.EXPECTED_ITEMS: |         for item in self.EXPECTED_ITEMS: | ||||||
|             self.assertNotIn(item, response.context) |             self.assertNotIn(item, response.context) | ||||||
| 
 | 
 | ||||||
|  | @ -37,12 +34,6 @@ class NavigationContextProcessorTest(TestCase): | ||||||
|         self.assertEqual(context["phppgadmin_url"], settings.GVA_LINK_PHPPGADMIN) |         self.assertEqual(context["phppgadmin_url"], settings.GVA_LINK_PHPPGADMIN) | ||||||
| 
 | 
 | ||||||
|     def test_index_page_context(self): |     def test_index_page_context(self): | ||||||
|         user = User.objects.create(username=TEST_USER) |  | ||||||
|         user.set_password(TEST_PASSWORD) |  | ||||||
|         user.save() |  | ||||||
| 
 |  | ||||||
|         self.client.login(username=TEST_USER, password=TEST_PASSWORD) |  | ||||||
| 
 |  | ||||||
|         response = self.client.get("/") |         response = self.client.get("/") | ||||||
|         for item in self.EXPECTED_ITEMS: |         for item in self.EXPECTED_ITEMS: | ||||||
|             self.assertIn(item, response.context) |             self.assertIn(item, response.context) | ||||||
|  | @ -56,6 +47,15 @@ class NavigationContextProcessorTest(TestCase): | ||||||
|         self._check_static_urls(response.context) |         self._check_static_urls(response.context) | ||||||
|         self.assertEqual(response.context["active_item"], "contact") |         self.assertEqual(response.context["active_item"], "contact") | ||||||
| 
 | 
 | ||||||
|  |     def test_hostingpackage_page_context(self): | ||||||
|  |         User.objects.create_user("test", password="test") | ||||||
|  |         self.client.login(username="test", password="test") | ||||||
|  |         response = self.client.get(reverse("hosting_packages", kwargs={"user": "test"})) | ||||||
|  |         for item in self.EXPECTED_ITEMS: | ||||||
|  |             self.assertIn(item, response.context) | ||||||
|  |         self._check_static_urls(response.context) | ||||||
|  |         self.assertEqual(response.context["active_item"], "hostingpackage") | ||||||
|  | 
 | ||||||
|     def _test_page_context_by_viewmodule(self, viewmodule, expecteditem): |     def _test_page_context_by_viewmodule(self, viewmodule, expecteditem): | ||||||
|         request = HttpRequest() |         request = HttpRequest() | ||||||
|         request.resolver_match = MagicMock() |         request.resolver_match = MagicMock() | ||||||
|  | @ -106,6 +106,6 @@ class NavigationContextProcessorTest(TestCase): | ||||||
| 
 | 
 | ||||||
| class VersionInfoContextProcessorTest(TestCase): | class VersionInfoContextProcessorTest(TestCase): | ||||||
|     def test_version_info_in_context(self): |     def test_version_info_in_context(self): | ||||||
|         response = self.client.get("/accounts/login/") |         response = self.client.get("/") | ||||||
|         self.assertIn("gnuviechadmin_version", response.context) |         self.assertIn("gnuviechadmin_version", response.context) | ||||||
|         self.assertEqual(response.context["gnuviechadmin_version"], gvaversion) |         self.assertEqual(response.context["gnuviechadmin_version"], gvaversion) | ||||||
|  |  | ||||||
|  | @ -1,49 +1,36 @@ | ||||||
| from __future__ import absolute_import | from __future__ import absolute_import | ||||||
| 
 | 
 | ||||||
| import debug_toolbar | from django.conf.urls import include, url | ||||||
| from django.conf.urls import include | from django.conf import settings | ||||||
|  | 
 | ||||||
| from django.contrib import admin | from django.contrib import admin | ||||||
| from django.contrib.flatpages import views | from django.contrib.flatpages import views | ||||||
| from django.contrib.staticfiles.urls import staticfiles_urlpatterns | from django.contrib.staticfiles.urls import staticfiles_urlpatterns | ||||||
| from django.urls import path |  | ||||||
| 
 |  | ||||||
| from help import views as help_views |  | ||||||
| from invoices import views as invoice_views |  | ||||||
| 
 | 
 | ||||||
| admin.autodiscover() | admin.autodiscover() | ||||||
| 
 | 
 | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
|     path("", include("dashboard.urls")), |     url(r'', include('dashboard.urls')), | ||||||
|     path("api/users/", help_views.ListHelpUserAPIView.as_view()), |     url(r'^accounts/', include('allauth.urls')), | ||||||
|     path( |     url(r'^database/', include('userdbs.urls')), | ||||||
|         "api/users/<int:pk>/", |     url(r'^domains/', include('domains.urls')), | ||||||
|         help_views.HelpUserAPIView.as_view(), |     url(r'^hosting/', include('hostingpackages.urls')), | ||||||
|         name="helpuser-detail", |     url(r'^website/', include('websites.urls')), | ||||||
|     ), |     url(r'^mail/', include('managemails.urls')), | ||||||
|     path("api/invoices/", invoice_views.ListInvoiceAPIView.as_view()), |     url(r'^osuser/', include('osusers.urls')), | ||||||
|     path( |     url(r'^admin/', admin.site.urls), | ||||||
|         "api/invoices/<invoice_number>/", |     url(r'^contact/', include('contact_form.urls')), | ||||||
|         invoice_views.InvoiceAPIView.as_view(), |     url(r'^impressum/$', views.flatpage, { | ||||||
|         name="invoice-detail", |         'url': '/impressum/' | ||||||
|     ), |     }, name='imprint'), | ||||||
|     path("admin/", admin.site.urls), |  | ||||||
|     path("impersonate/", include("impersonate.urls")), |  | ||||||
|     path("accounts/", include("allauth.urls")), |  | ||||||
|     path("database/", include("userdbs.urls")), |  | ||||||
|     path("domains/", include("domains.urls")), |  | ||||||
|     path("hosting/", include("hostingpackages.urls")), |  | ||||||
|     path("website/", include("websites.urls")), |  | ||||||
|     path("mail/", include("managemails.urls")), |  | ||||||
|     path("osuser/", include("osusers.urls")), |  | ||||||
|     path("contact/", include("contact_form.urls")), |  | ||||||
|     path("impressum/", views.flatpage, {"url": "/impressum/"}, name="imprint"), |  | ||||||
|     path("datenschutz/", views.flatpage, {"url": "/datenschutz/"}, name="privacy"), |  | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| # Uncomment the next line to serve media files in dev. | # Uncomment the next line to serve media files in dev. | ||||||
| # urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) | # urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) | ||||||
| 
 | 
 | ||||||
| urlpatterns += staticfiles_urlpatterns() | if settings.DEBUG:  # pragma: no cover | ||||||
| urlpatterns += [ |     import debug_toolbar | ||||||
|     path("__debug__/", include(debug_toolbar.urls)), | 
 | ||||||
| ] |     urlpatterns = [ | ||||||
|  |         url(r'^__debug__/', include(debug_toolbar.urls)), | ||||||
|  |     ] + staticfiles_urlpatterns() + urlpatterns | ||||||
|  |  | ||||||
|  | @ -3,10 +3,11 @@ This module defines form classes that can be extended by other gnuviechadmin | ||||||
| apps' forms. | apps' forms. | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
| from __future__ import absolute_import | from __future__ import absolute_import, unicode_literals | ||||||
| 
 | 
 | ||||||
| from django import forms | from django import forms | ||||||
| from django.utils.translation import gettext_lazy as _ | from django.utils.translation import ugettext_lazy as _ | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| PASSWORD_MISMATCH_ERROR = _("Passwords don't match") | PASSWORD_MISMATCH_ERROR = _("Passwords don't match") | ||||||
| """ | """ | ||||||
|  | @ -20,14 +21,11 @@ class PasswordModelFormMixin(forms.Form): | ||||||
|     whether both fields contain the same string. |     whether both fields contain the same string. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |  | ||||||
|     password1 = forms.CharField( |     password1 = forms.CharField( | ||||||
|         label=_("Password"), |         label=_('Password'), widget=forms.PasswordInput, | ||||||
|         widget=forms.PasswordInput, |  | ||||||
|     ) |     ) | ||||||
|     password2 = forms.CharField( |     password2 = forms.CharField( | ||||||
|         label=_("Password (again)"), |         label=_('Password (again)'), widget=forms.PasswordInput, | ||||||
|         widget=forms.PasswordInput, |  | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     def clean_password2(self): |     def clean_password2(self): | ||||||
|  | @ -38,8 +36,8 @@ class PasswordModelFormMixin(forms.Form): | ||||||
|         :rtype: str or None |         :rtype: str or None | ||||||
| 
 | 
 | ||||||
|         """ |         """ | ||||||
|         password1 = self.cleaned_data.get("password1") |         password1 = self.cleaned_data.get('password1') | ||||||
|         password2 = self.cleaned_data.get("password2") |         password2 = self.cleaned_data.get('password2') | ||||||
|         if password1 and password2 and password1 != password2: |         if password1 and password2 and password1 != password2: | ||||||
|             raise forms.ValidationError(PASSWORD_MISMATCH_ERROR) |             raise forms.ValidationError(PASSWORD_MISMATCH_ERROR) | ||||||
|         return password2 |         return password2 | ||||||
|  |  | ||||||
|  | @ -7,8 +7,8 @@ msgid "" | ||||||
| msgstr "" | msgstr "" | ||||||
| "Project-Id-Version: gvawebcore\n" | "Project-Id-Version: gvawebcore\n" | ||||||
| "Report-Msgid-Bugs-To: \n" | "Report-Msgid-Bugs-To: \n" | ||||||
| "POT-Creation-Date: 2023-04-16 22:07+0200\n" | "POT-Creation-Date: 2016-01-29 11:04+0100\n" | ||||||
| "PO-Revision-Date: 2023-04-16 18:21+0200\n" | "PO-Revision-Date: 2015-01-25 11:49+0100\n" | ||||||
| "Last-Translator: Jan Dittberner <jan@dittberner.info>\n" | "Last-Translator: Jan Dittberner <jan@dittberner.info>\n" | ||||||
| "Language-Team: Jan Dittberner <jan@dittberner.info>\n" | "Language-Team: Jan Dittberner <jan@dittberner.info>\n" | ||||||
| "Language: de\n" | "Language: de\n" | ||||||
|  | @ -16,17 +16,17 @@ msgstr "" | ||||||
| "Content-Type: text/plain; charset=UTF-8\n" | "Content-Type: text/plain; charset=UTF-8\n" | ||||||
| "Content-Transfer-Encoding: 8bit\n" | "Content-Transfer-Encoding: 8bit\n" | ||||||
| "Plural-Forms: nplurals=2; plural=(n != 1);\n" | "Plural-Forms: nplurals=2; plural=(n != 1);\n" | ||||||
| "X-Generator: Poedit 3.2.2\n" | "X-Generator: Poedit 1.6.10\n" | ||||||
| "X-Poedit-SourceCharset: UTF-8\n" | "X-Poedit-SourceCharset: UTF-8\n" | ||||||
| 
 | 
 | ||||||
| #: gvawebcore/forms.py:11 | #: gvawebcore/forms.py:12 | ||||||
| msgid "Passwords don't match" | msgid "Passwords don't match" | ||||||
| msgstr "Passwörter stimmen nicht überein" | msgstr "Passwörter stimmen nicht überein" | ||||||
| 
 | 
 | ||||||
| #: gvawebcore/forms.py:25 | #: gvawebcore/forms.py:25 | ||||||
| msgid "Password" | msgid "Password" | ||||||
| msgstr "Passwort" | msgstr "Passwort: " | ||||||
| 
 | 
 | ||||||
| #: gvawebcore/forms.py:29 | #: gvawebcore/forms.py:28 | ||||||
| msgid "Password (again)" | msgid "Password (again)" | ||||||
| msgstr "Passwortwiederholung" | msgstr "Passwortwiederholung" | ||||||
|  |  | ||||||
|  | @ -2,10 +2,9 @@ | ||||||
| This module defines common view code to be used by multiple gnuviechadmin apps. | This module defines common view code to be used by multiple gnuviechadmin apps. | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
| from __future__ import absolute_import | from __future__ import absolute_import, unicode_literals | ||||||
| 
 | 
 | ||||||
| from django.shortcuts import get_object_or_404 | from django.shortcuts import get_object_or_404 | ||||||
| 
 |  | ||||||
| from hostingpackages.models import CustomerHostingPackage | from hostingpackages.models import CustomerHostingPackage | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -15,8 +14,7 @@ class HostingPackageAndCustomerMixin(object): | ||||||
|     keyword argument 'package'. |     keyword argument 'package'. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |     hosting_package_kwarg = 'package' | ||||||
|     hosting_package_kwarg = "package" |  | ||||||
|     """Keyword argument used to find the hosting package in the URL.""" |     """Keyword argument used to find the hosting package in the URL.""" | ||||||
| 
 | 
 | ||||||
|     hostingpackage = None |     hostingpackage = None | ||||||
|  | @ -24,8 +22,8 @@ class HostingPackageAndCustomerMixin(object): | ||||||
|     def get_hosting_package(self): |     def get_hosting_package(self): | ||||||
|         if self.hostingpackage is None: |         if self.hostingpackage is None: | ||||||
|             self.hostingpackage = get_object_or_404( |             self.hostingpackage = get_object_or_404( | ||||||
|                 CustomerHostingPackage, pk=int(self.kwargs[self.hosting_package_kwarg]) |                 CustomerHostingPackage, | ||||||
|             ) |                 pk=int(self.kwargs[self.hosting_package_kwarg])) | ||||||
|         return self.hostingpackage |         return self.hostingpackage | ||||||
| 
 | 
 | ||||||
|     def get_customer_object(self): |     def get_customer_object(self): | ||||||
|  |  | ||||||
|  | @ -1,22 +0,0 @@ | ||||||
| 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) |  | ||||||
|  | @ -1,8 +0,0 @@ | ||||||
| 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") |  | ||||||
|  | @ -1,36 +0,0 @@ | ||||||
| # 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" |  | ||||||
|  | @ -1,17 +0,0 @@ | ||||||
| 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}.") |  | ||||||
|  | @ -1,29 +0,0 @@ | ||||||
| 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}") |  | ||||||
|  | @ -1,55 +0,0 @@ | ||||||
| # 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, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |  | ||||||
|         ), |  | ||||||
|     ] |  | ||||||
|  | @ -1,17 +0,0 @@ | ||||||
| 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()) |  | ||||||
|  | @ -1,22 +0,0 @@ | ||||||
| """ |  | ||||||
| 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"] |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| from django.test import TestCase |  | ||||||
| 
 |  | ||||||
| # Create your tests here. |  | ||||||
|  | @ -1,26 +0,0 @@ | ||||||
| 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,3 +2,4 @@ | ||||||
| This app takes care of hosting packages. | This app takes care of hosting packages. | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
|  | default_app_config = 'hostingpackages.apps.HostingPackagesAppConfig' | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| This module contains the admin site interface for hosting packages. | This module contains the admin site interface for hosting packages. | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
| from __future__ import absolute_import | from __future__ import absolute_import, unicode_literals | ||||||
| 
 | 
 | ||||||
| from django import forms | from django import forms | ||||||
| from django.contrib import admin | from django.contrib import admin | ||||||
|  | @ -25,10 +25,9 @@ class CustomerHostingPackageCreateForm(forms.ModelForm): | ||||||
|     This is the form class for creating new customer hosting packages. |     This is the form class for creating new customer hosting packages. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |  | ||||||
|     class Meta: |     class Meta: | ||||||
|         model = CustomerHostingPackage |         model = CustomerHostingPackage | ||||||
|         fields = ["customer", "template", "name"] |         fields = ['customer', 'template', 'name'] | ||||||
| 
 | 
 | ||||||
|     def save(self, **kwargs): |     def save(self, **kwargs): | ||||||
|         """ |         """ | ||||||
|  | @ -40,11 +39,10 @@ class CustomerHostingPackageCreateForm(forms.ModelForm): | ||||||
| 
 | 
 | ||||||
|         """ |         """ | ||||||
|         hostinpackages = CustomerHostingPackage.objects.create_from_template( |         hostinpackages = CustomerHostingPackage.objects.create_from_template( | ||||||
|             customer=self.cleaned_data["customer"], |             customer=self.cleaned_data['customer'], | ||||||
|             template=self.cleaned_data["template"], |             template=self.cleaned_data['template'], | ||||||
|             name=self.cleaned_data["name"], |             name=self.cleaned_data['name'], | ||||||
|             **kwargs |             **kwargs) | ||||||
|         ) |  | ||||||
|         return hostinpackages |         return hostinpackages | ||||||
| 
 | 
 | ||||||
|     def save_m2m(self): |     def save_m2m(self): | ||||||
|  | @ -57,7 +55,6 @@ class CustomerDiskSpaceOptionInline(admin.TabularInline): | ||||||
|     space options. |     space options. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |  | ||||||
|     model = CustomerDiskSpaceOption |     model = CustomerDiskSpaceOption | ||||||
|     extra = 0 |     extra = 0 | ||||||
| 
 | 
 | ||||||
|  | @ -68,7 +65,6 @@ class CustomerMailboxOptionInline(admin.TabularInline): | ||||||
|     mailbox options. |     mailbox options. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |  | ||||||
|     model = CustomerMailboxOption |     model = CustomerMailboxOption | ||||||
|     extra = 0 |     extra = 0 | ||||||
| 
 | 
 | ||||||
|  | @ -79,7 +75,6 @@ class CustomerUserDatabaseOptionInline(admin.TabularInline): | ||||||
|     database options. |     database options. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |  | ||||||
|     model = CustomerUserDatabaseOption |     model = CustomerUserDatabaseOption | ||||||
|     extra = 0 |     extra = 0 | ||||||
| 
 | 
 | ||||||
|  | @ -90,7 +85,6 @@ class CustomerHostingPackageDomainInline(admin.TabularInline): | ||||||
|     hosting packages. |     hosting packages. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |  | ||||||
|     model = CustomerHostingPackageDomain |     model = CustomerHostingPackageDomain | ||||||
|     extra = 0 |     extra = 0 | ||||||
| 
 | 
 | ||||||
|  | @ -101,9 +95,12 @@ class CustomerHostingPackageAdmin(admin.ModelAdmin): | ||||||
|     :py:class:`CustomerHostingPackage`. |     :py:class:`CustomerHostingPackage`. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |  | ||||||
|     add_form = CustomerHostingPackageCreateForm |     add_form = CustomerHostingPackageCreateForm | ||||||
|     add_fieldsets = ((None, {"fields": ("customer", "template", "name")}),) |     add_fieldsets = ( | ||||||
|  |         (None, { | ||||||
|  |             'fields': ('customer', 'template', 'name') | ||||||
|  |         }), | ||||||
|  |     ) | ||||||
| 
 | 
 | ||||||
|     inlines = [ |     inlines = [ | ||||||
|         CustomerDiskSpaceOptionInline, |         CustomerDiskSpaceOptionInline, | ||||||
|  | @ -111,7 +108,7 @@ class CustomerHostingPackageAdmin(admin.ModelAdmin): | ||||||
|         CustomerUserDatabaseOptionInline, |         CustomerUserDatabaseOptionInline, | ||||||
|         CustomerHostingPackageDomainInline, |         CustomerHostingPackageDomainInline, | ||||||
|     ] |     ] | ||||||
|     list_display = ["name", "customer", "osuser"] |     list_display = ['name', 'customer', 'osuser'] | ||||||
| 
 | 
 | ||||||
|     def get_form(self, request, obj=None, **kwargs): |     def get_form(self, request, obj=None, **kwargs): | ||||||
|         """ |         """ | ||||||
|  | @ -128,16 +125,13 @@ class CustomerHostingPackageAdmin(admin.ModelAdmin): | ||||||
|         """ |         """ | ||||||
|         defaults = {} |         defaults = {} | ||||||
|         if obj is None: |         if obj is None: | ||||||
|             defaults.update( |             defaults.update({ | ||||||
|                 { |                 'form': self.add_form, | ||||||
|                     "form": self.add_form, |                 'fields': admin.options.flatten_fieldsets(self.add_fieldsets), | ||||||
|                     "fields": admin.options.flatten_fieldsets(self.add_fieldsets), |             }) | ||||||
|                 } |  | ||||||
|             ) |  | ||||||
|         defaults.update(kwargs) |         defaults.update(kwargs) | ||||||
|         return super(CustomerHostingPackageAdmin, self).get_form( |         return super(CustomerHostingPackageAdmin, self).get_form( | ||||||
|             request, obj, **defaults |             request, obj, **defaults) | ||||||
|         ) |  | ||||||
| 
 | 
 | ||||||
|     def get_readonly_fields(self, request, obj=None): |     def get_readonly_fields(self, request, obj=None): | ||||||
|         """ |         """ | ||||||
|  | @ -153,7 +147,7 @@ class CustomerHostingPackageAdmin(admin.ModelAdmin): | ||||||
| 
 | 
 | ||||||
|         """ |         """ | ||||||
|         if obj: |         if obj: | ||||||
|             return ["customer", "template"] |             return ['customer', 'template'] | ||||||
|         return [] |         return [] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,8 +3,9 @@ This module contains the :py:class:`django.apps.AppConfig` instance for the | ||||||
| :py:mod:`hostingpackages` app. | :py:mod:`hostingpackages` app. | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
|  | from __future__ import unicode_literals | ||||||
| from django.apps import AppConfig | from django.apps import AppConfig | ||||||
| from django.utils.translation import gettext_lazy as _ | from django.utils.translation import ugettext_lazy as _ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class HostingPackagesAppConfig(AppConfig): | class HostingPackagesAppConfig(AppConfig): | ||||||
|  | @ -12,6 +13,5 @@ class HostingPackagesAppConfig(AppConfig): | ||||||
|     AppConfig for the :py:mod:`hostingpackages` app. |     AppConfig for the :py:mod:`hostingpackages` app. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |     name = 'hostingpackages' | ||||||
|     name = "hostingpackages" |     verbose_name = _('Hosting Packages and Options') | ||||||
|     verbose_name = _("Hosting Packages and Options") |  | ||||||
|  |  | ||||||
|  | @ -2,13 +2,17 @@ | ||||||
| This module contains the form classes related to hosting packages. | This module contains the form classes related to hosting packages. | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
| from __future__ import absolute_import | from __future__ import absolute_import, unicode_literals | ||||||
| 
 | 
 | ||||||
| from crispy_forms.helper import FormHelper |  | ||||||
| from crispy_forms.layout import Layout, Submit |  | ||||||
| from django import forms | from django import forms | ||||||
| from django.urls import reverse | from django.urls import reverse | ||||||
| from django.utils.translation import gettext as _ | from django.utils.translation import ugettext as _ | ||||||
|  | 
 | ||||||
|  | from crispy_forms.helper import FormHelper | ||||||
|  | from crispy_forms.layout import ( | ||||||
|  |     Layout, | ||||||
|  |     Submit, | ||||||
|  | ) | ||||||
| 
 | 
 | ||||||
| from .models import ( | from .models import ( | ||||||
|     CustomerDiskSpaceOption, |     CustomerDiskSpaceOption, | ||||||
|  | @ -24,24 +28,25 @@ class CreateCustomerHostingPackageForm(forms.ModelForm): | ||||||
|     a preselected customer. |     a preselected customer. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |  | ||||||
|     class Meta: |     class Meta: | ||||||
|         model = CustomerHostingPackage |         model = CustomerHostingPackage | ||||||
|         fields = ["template", "name", "description"] |         fields = ['template', 'name', 'description'] | ||||||
| 
 | 
 | ||||||
|     def __init__(self, instance, *args, **kwargs): |     def __init__(self, instance, *args, **kwargs): | ||||||
|         username = kwargs.pop("user") |         username = kwargs.pop('user') | ||||||
|         super(CreateCustomerHostingPackageForm, self).__init__(*args, **kwargs) |         super(CreateCustomerHostingPackageForm, self).__init__( | ||||||
|         self.fields["description"].widget.attrs["rows"] = 2 |             *args, **kwargs | ||||||
|  |         ) | ||||||
|  |         self.fields['description'].widget.attrs['rows'] = 2 | ||||||
|         self.helper = FormHelper() |         self.helper = FormHelper() | ||||||
|         self.helper.form_action = reverse( |         self.helper.form_action = reverse( | ||||||
|             "create_customer_hosting_package", kwargs={"user": username} |             'create_customer_hosting_package', kwargs={'user': username} | ||||||
|         ) |         ) | ||||||
|         self.helper.layout = Layout( |         self.helper.layout = Layout( | ||||||
|             "template", |             'template', | ||||||
|             "name", |             'name', | ||||||
|             "description", |             'description', | ||||||
|             Submit("submit", _("Add Hosting Package")), |             Submit('submit', _('Add Hosting Package')), | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -50,44 +55,44 @@ class CreateHostingPackageForm(forms.ModelForm): | ||||||
|     This form class is used for creating new customer hosting packages. |     This form class is used for creating new customer hosting packages. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |  | ||||||
|     class Meta: |     class Meta: | ||||||
|         model = CustomerHostingPackage |         model = CustomerHostingPackage | ||||||
|         fields = ["customer", "template", "name", "description"] |         fields = ['customer', 'template', 'name', 'description'] | ||||||
| 
 | 
 | ||||||
|     def __init__(self, instance, *args, **kwargs): |     def __init__(self, instance, *args, **kwargs): | ||||||
|         super(CreateHostingPackageForm, self).__init__(*args, **kwargs) |         super(CreateHostingPackageForm, self).__init__( | ||||||
|         self.fields["description"].widget.attrs["rows"] = 2 |             *args, **kwargs | ||||||
|  |         ) | ||||||
|  |         self.fields['description'].widget.attrs['rows'] = 2 | ||||||
|         self.helper = FormHelper() |         self.helper = FormHelper() | ||||||
|         self.helper.form_action = reverse("create_hosting_package") |         self.helper.form_action = reverse('create_hosting_package') | ||||||
|         self.helper.layout = Layout( |         self.helper.layout = Layout( | ||||||
|             "customer", |             'customer', | ||||||
|             "template", |             'template', | ||||||
|             "name", |             'name', | ||||||
|             "description", |             'description', | ||||||
|             Submit("submit", _("Add Hosting Package")), |             Submit('submit', _('Add Hosting Package')), | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class AddDiskspaceOptionForm(forms.ModelForm): | class AddDiskspaceOptionForm(forms.ModelForm): | ||||||
|     class Meta: |     class Meta: | ||||||
|         model = CustomerDiskSpaceOption |         model = CustomerDiskSpaceOption | ||||||
|         fields = ["diskspace", "diskspace_unit"] |         fields = ['diskspace', 'diskspace_unit'] | ||||||
| 
 | 
 | ||||||
|     def __init__(self, *args, **kwargs): |     def __init__(self, *args, **kwargs): | ||||||
|         self.hostingpackage = kwargs.pop("hostingpackage") |         self.hostingpackage = kwargs.pop('hostingpackage') | ||||||
|         self.option_template = kwargs.pop("option_template") |         self.option_template = kwargs.pop('option_template') | ||||||
|         super(AddDiskspaceOptionForm, self).__init__(*args, **kwargs) |         super(AddDiskspaceOptionForm, self).__init__(*args, **kwargs) | ||||||
|         self.helper = FormHelper() |         self.helper = FormHelper() | ||||||
|         self.helper.form_action = reverse( |         self.helper.form_action = reverse( | ||||||
|             "add_hosting_option", |             'add_hosting_option', | ||||||
|             kwargs={ |             kwargs={ | ||||||
|                 "package": self.hostingpackage.id, |                 'package': self.hostingpackage.id, | ||||||
|                 "type": "diskspace", |                 'type': 'diskspace', | ||||||
|                 "optionid": self.option_template.id, |                 'optionid': self.option_template.id, | ||||||
|             }, |             }) | ||||||
|         ) |         self.helper.add_input(Submit('submit', _('Add disk space option'))) | ||||||
|         self.helper.add_input(Submit("submit", _("Add disk space option"))) |  | ||||||
| 
 | 
 | ||||||
|     def save(self, commit=True): |     def save(self, commit=True): | ||||||
|         self.instance.hosting_package = self.hostingpackage |         self.instance.hosting_package = self.hostingpackage | ||||||
|  | @ -98,22 +103,21 @@ class AddDiskspaceOptionForm(forms.ModelForm): | ||||||
| class AddMailboxOptionForm(forms.ModelForm): | class AddMailboxOptionForm(forms.ModelForm): | ||||||
|     class Meta: |     class Meta: | ||||||
|         model = CustomerMailboxOption |         model = CustomerMailboxOption | ||||||
|         fields = ["number"] |         fields = ['number'] | ||||||
| 
 | 
 | ||||||
|     def __init__(self, *args, **kwargs): |     def __init__(self, *args, **kwargs): | ||||||
|         self.hostingpackage = kwargs.pop("hostingpackage") |         self.hostingpackage = kwargs.pop('hostingpackage') | ||||||
|         self.option_template = kwargs.pop("option_template") |         self.option_template = kwargs.pop('option_template') | ||||||
|         super(AddMailboxOptionForm, self).__init__(*args, **kwargs) |         super(AddMailboxOptionForm, self).__init__(*args, **kwargs) | ||||||
|         self.helper = FormHelper() |         self.helper = FormHelper() | ||||||
|         self.helper.form_action = reverse( |         self.helper.form_action = reverse( | ||||||
|             "add_hosting_option", |             'add_hosting_option', | ||||||
|             kwargs={ |             kwargs={ | ||||||
|                 "package": self.hostingpackage.id, |                 'package': self.hostingpackage.id, | ||||||
|                 "type": "mailboxes", |                 'type': 'mailboxes', | ||||||
|                 "optionid": self.option_template.id, |                 'optionid': self.option_template.id, | ||||||
|             }, |             }) | ||||||
|         ) |         self.helper.add_input(Submit('submit', _('Add mailbox option'))) | ||||||
|         self.helper.add_input(Submit("submit", _("Add mailbox option"))) |  | ||||||
| 
 | 
 | ||||||
|     def save(self, commit=True): |     def save(self, commit=True): | ||||||
|         self.instance.hosting_package = self.hostingpackage |         self.instance.hosting_package = self.hostingpackage | ||||||
|  | @ -124,22 +128,21 @@ class AddMailboxOptionForm(forms.ModelForm): | ||||||
| class AddUserDatabaseOptionForm(forms.ModelForm): | class AddUserDatabaseOptionForm(forms.ModelForm): | ||||||
|     class Meta: |     class Meta: | ||||||
|         model = CustomerUserDatabaseOption |         model = CustomerUserDatabaseOption | ||||||
|         fields = ["number"] |         fields = ['number'] | ||||||
| 
 | 
 | ||||||
|     def __init__(self, *args, **kwargs): |     def __init__(self, *args, **kwargs): | ||||||
|         self.hostingpackage = kwargs.pop("hostingpackage") |         self.hostingpackage = kwargs.pop('hostingpackage') | ||||||
|         self.option_template = kwargs.pop("option_template") |         self.option_template = kwargs.pop('option_template') | ||||||
|         super(AddUserDatabaseOptionForm, self).__init__(*args, **kwargs) |         super(AddUserDatabaseOptionForm, self).__init__(*args, **kwargs) | ||||||
|         self.helper = FormHelper() |         self.helper = FormHelper() | ||||||
|         self.helper.form_action = reverse( |         self.helper.form_action = reverse( | ||||||
|             "add_hosting_option", |             'add_hosting_option', | ||||||
|             kwargs={ |             kwargs={ | ||||||
|                 "package": self.hostingpackage.id, |                 'package': self.hostingpackage.id, | ||||||
|                 "type": "databases", |                 'type': 'databases', | ||||||
|                 "optionid": self.option_template.id, |                 'optionid': self.option_template.id, | ||||||
|             }, |             }) | ||||||
|         ) |         self.helper.add_input(Submit('submit', _('Add database option'))) | ||||||
|         self.helper.add_input(Submit("submit", _("Add database option"))) |  | ||||||
| 
 | 
 | ||||||
|     def save(self, commit=True): |     def save(self, commit=True): | ||||||
|         self.instance.hosting_package = self.hostingpackage |         self.instance.hosting_package = self.hostingpackage | ||||||
|  |  | ||||||
|  | @ -7,8 +7,8 @@ msgid "" | ||||||
| msgstr "" | msgstr "" | ||||||
| "Project-Id-Version: gnuviechadmin hostingpackages\n" | "Project-Id-Version: gnuviechadmin hostingpackages\n" | ||||||
| "Report-Msgid-Bugs-To: \n" | "Report-Msgid-Bugs-To: \n" | ||||||
| "POT-Creation-Date: 2023-04-22 13:14+0200\n" | "POT-Creation-Date: 2016-01-29 11:04+0100\n" | ||||||
| "PO-Revision-Date: 2023-04-22 13:15+0200\n" | "PO-Revision-Date: 2015-01-25 15:49+0100\n" | ||||||
| "Last-Translator: Jan Dittberner <jan@dittberner.info>\n" | "Last-Translator: Jan Dittberner <jan@dittberner.info>\n" | ||||||
| "Language-Team: Jan Dittberner <jan@dittberner.info>\n" | "Language-Team: Jan Dittberner <jan@dittberner.info>\n" | ||||||
| "Language: de\n" | "Language: de\n" | ||||||
|  | @ -16,561 +16,221 @@ msgstr "" | ||||||
| "Content-Type: text/plain; charset=UTF-8\n" | "Content-Type: text/plain; charset=UTF-8\n" | ||||||
| "Content-Transfer-Encoding: 8bit\n" | "Content-Transfer-Encoding: 8bit\n" | ||||||
| "Plural-Forms: nplurals=2; plural=(n != 1);\n" | "Plural-Forms: nplurals=2; plural=(n != 1);\n" | ||||||
| "X-Generator: Poedit 3.2.2\n" | "X-Generator: Poedit 1.6.10\n" | ||||||
| "X-Poedit-SourceCharset: UTF-8\n" | "X-Poedit-SourceCharset: UTF-8\n" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/apps.py:17 | #: hostingpackages/apps.py:17 | ||||||
| msgid "Hosting Packages and Options" | msgid "Hosting Packages and Options" | ||||||
| msgstr "Hostingpakete und -Optionen" | msgstr "Hostingpakete und -Optionen" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/forms.py:44 hostingpackages/forms.py:68 | #: hostingpackages/forms.py:49 hostingpackages/forms.py:74 | ||||||
| msgid "Add Hosting Package" | msgid "Add Hosting Package" | ||||||
| msgstr "Hostingpaket anlegen" | msgstr "Hostingpaket anlegen" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/forms.py:90 | #: hostingpackages/forms.py:95 | ||||||
| msgid "Add disk space option" | msgid "Add disk space option" | ||||||
| msgstr "Speicherplatzoption hinzufügen" | msgstr "Speicherplatzoption hinzufügen" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/forms.py:116 | #: hostingpackages/forms.py:120 | ||||||
| msgid "Add mailbox option" | msgid "Add mailbox option" | ||||||
| msgstr "Postfachoption hinzufügen" | msgstr "Postfachoption hinzufügen" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/forms.py:142 | #: hostingpackages/forms.py:145 | ||||||
| msgid "Add database option" | msgid "Add database option" | ||||||
| msgstr "Datenbankoption hinzufügen" | msgstr "Datenbankoption hinzufügen" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:21 | #: hostingpackages/models.py:31 | ||||||
| msgid "MiB" | msgid "MiB" | ||||||
| msgstr "MiB" | msgstr "MiB" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:21 | #: hostingpackages/models.py:32 | ||||||
| msgid "GiB" | msgid "GiB" | ||||||
| msgstr "GiB" | msgstr "GiB" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:21 | #: hostingpackages/models.py:33 | ||||||
| msgid "TiB" | msgid "TiB" | ||||||
| msgstr "TiB" | msgstr "TiB" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:27 | #: hostingpackages/models.py:45 | ||||||
| msgid "description" | msgid "description" | ||||||
| msgstr "Beschreibung" | msgstr "Beschreibung" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:28 | #: hostingpackages/models.py:46 | ||||||
| msgid "mailbox count" | msgid "mailbox count" | ||||||
| msgstr "Anzahl Postfächer" | msgstr "Anzahl Postfächer" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:30 hostingpackages/models.py:59 | #: hostingpackages/models.py:48 hostingpackages/models.py:76 | ||||||
| msgid "disk space" | msgid "disk space" | ||||||
| msgstr "Speicherplatz" | msgstr "Speicherplatz" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:30 | #: hostingpackages/models.py:48 | ||||||
| msgid "disk space for the hosting package" | msgid "disk space for the hosting package" | ||||||
| msgstr "Speicherplatz für das Hostingpaket" | msgstr "Speicherplatz für das Hostingpaket" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:33 hostingpackages/models.py:61 | #: hostingpackages/models.py:50 hostingpackages/models.py:78 | ||||||
| msgid "unit of disk space" | msgid "unit of disk space" | ||||||
| msgstr "Maßeinheit für den Speicherplatz" | msgstr "Maßeinheit für den Speicherplatz" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:44 hostingpackages/models.py:192 | #: hostingpackages/models.py:60 hostingpackages/models.py:213 | ||||||
| msgid "name" | msgid "name" | ||||||
| msgstr "Name" | msgstr "Name" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:47 | #: hostingpackages/models.py:63 | ||||||
| msgid "Hosting package" | msgid "Hosting package" | ||||||
| msgstr "Hostingpaket" | msgstr "Hostingpaket" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:48 | #: hostingpackages/models.py:64 | ||||||
| msgid "Hosting packages" | msgid "Hosting packages" | ||||||
| msgstr "Hostingpakete" | msgstr "Hostingpakete" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:67 | #: hostingpackages/models.py:83 | ||||||
| msgid "Disk space option" | msgid "Disk space option" | ||||||
| msgstr "Speicherplatzoption" | msgstr "Speicherplatzoption" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:68 | #: hostingpackages/models.py:84 | ||||||
| msgid "Disk space options" | msgid "Disk space options" | ||||||
| msgstr "Speicherplatzoptionen" | msgstr "Speicherplatzoptionen" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:71 | #: hostingpackages/models.py:87 | ||||||
| #, python-brace-format | #, python-brace-format | ||||||
| msgid "Additional disk space {space} {unit}" | msgid "Additional disk space {space} {unit}" | ||||||
| msgstr "Zusätzlicher Speicherplatz {space} {unit}" | msgstr "Zusätzlicher Speicherplatz {space} {unit}" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:88 | #: hostingpackages/models.py:104 | ||||||
| msgid "number of databases" | msgid "number of databases" | ||||||
| msgstr "Anzahl von Datenbanken" | msgstr "Anzahl von Datenbanken" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:89 | #: hostingpackages/models.py:106 | ||||||
| msgid "database type" | msgid "database type" | ||||||
| msgstr "Datenbanktyp" | msgstr "Datenbanktyp" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:94 | #: hostingpackages/models.py:111 | ||||||
| msgid "Database option" | msgid "Database option" | ||||||
| msgstr "Datenbankoption" | msgstr "Datenbankoption" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:95 | #: hostingpackages/models.py:112 | ||||||
| msgid "Database options" | msgid "Database options" | ||||||
| msgstr "Datenbankoptionen" | msgstr "Datenbankoptionen" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:99 | #: hostingpackages/models.py:116 | ||||||
| #, python-brace-format | #, python-brace-format | ||||||
| msgid "{type} database" | msgid "{type} database" | ||||||
| msgid_plural "{count} {type} databases" | msgid_plural "{count} {type} databases" | ||||||
| msgstr[0] "{type}-Datenbank" | msgstr[0] "{type}-Datenbank" | ||||||
| msgstr[1] "{count} {type}-Datenbanken" | msgstr[1] "{count} {type}-Datenbanken" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:120 | #: hostingpackages/models.py:141 | ||||||
| msgid "number of mailboxes" | msgid "number of mailboxes" | ||||||
| msgstr "Anzahl von Postfächern" | msgstr "Anzahl von Postfächern" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:125 | #: hostingpackages/models.py:146 | ||||||
| msgid "Mailbox option" | msgid "Mailbox option" | ||||||
| msgstr "Postfachoption" | msgstr "Postfachoption" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:126 | #: hostingpackages/models.py:147 | ||||||
| msgid "Mailbox options" | msgid "Mailbox options" | ||||||
| msgstr "Postfachoptionen" | msgstr "Postfachoptionen" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:130 | #: hostingpackages/models.py:151 | ||||||
| #, python-brace-format | #, python-brace-format | ||||||
| msgid "{count} additional mailbox" | msgid "{count} additional mailbox" | ||||||
| msgid_plural "{count} additional mailboxes" | msgid_plural "{count} additional mailboxes" | ||||||
| msgstr[0] "{count} zusätzliches Postfach" | msgstr[0] "{count} zusätzliches Postfach" | ||||||
| msgstr[1] "{count} zusätzliche Postfächer" | msgstr[1] "{count} zusätzliche Postfächer" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:182 | #: hostingpackages/models.py:206 | ||||||
| msgid "customer" | msgid "customer" | ||||||
| msgstr "Kunde" | msgstr "Kunde" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:186 | #: hostingpackages/models.py:208 | ||||||
| msgid "hosting package template" | msgid "hosting package template" | ||||||
| msgstr "Hostingpaketvorlage" | msgstr "Hostingpaketvorlage" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:188 | #: hostingpackages/models.py:210 | ||||||
| msgid "The hosting package template that this hosting package is based on" | msgid "The hosting package template that this hosting package is based on" | ||||||
| msgstr "Die Hostingpaketvorlage, auf der dieses Hostingpaket aufgebaut ist" | msgstr "Die Hostingpaketvorlage, auf der dieses Hostingpaket aufgebaut ist" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:195 | #: hostingpackages/models.py:215 | ||||||
| msgid "Operating system user" | msgid "Operating system user" | ||||||
| msgstr "Betriebssystemnutzer" | msgstr "Betriebssystemnutzer" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:205 | #: hostingpackages/models.py:222 | ||||||
| msgid "customer hosting package" | msgid "customer hosting package" | ||||||
| msgstr "Kundenhostingpaket" | msgstr "Kundenhostingpaket" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:206 | #: hostingpackages/models.py:223 | ||||||
| msgid "customer hosting packages" | msgid "customer hosting packages" | ||||||
| msgstr "Kundenhostingpakete" | msgstr "Kundenhostingpakete" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:209 | #: hostingpackages/models.py:226 | ||||||
| #, python-brace-format | #, python-brace-format | ||||||
| msgid "{name} for {customer}" | msgid "{name} for {customer}" | ||||||
| msgstr "{name} für {customer}" | msgstr "{name} für {customer}" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:388 hostingpackages/models.py:415 | #: hostingpackages/models.py:404 hostingpackages/models.py:426 | ||||||
| msgid "hosting package" | msgid "hosting package" | ||||||
| msgstr "Hostingpaket" | msgstr "Hostingpaket" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:393 | #: hostingpackages/models.py:407 | ||||||
| msgid "hosting domain" | msgid "hosting domain" | ||||||
| msgstr "Hostingdomain" | msgstr "Hostingdomain" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:420 | #: hostingpackages/models.py:429 | ||||||
| msgid "customer hosting option" | msgid "customer hosting option" | ||||||
| msgstr "kundenspezifische Hostingoption" | msgstr "kundenspezifische Hostingoption" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:421 | #: hostingpackages/models.py:430 | ||||||
| msgid "customer hosting options" | msgid "customer hosting options" | ||||||
| msgstr "kundenspezifische Hostingoptionen" | msgstr "kundenspezifische Hostingoptionen" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:433 | #: hostingpackages/models.py:442 | ||||||
| msgid "disk space option template" | msgid "disk space option template" | ||||||
| msgstr "Speicherplatzoptionsvorlage" | msgstr "Speicherplatzoptionsvorlage" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:435 | #: hostingpackages/models.py:444 | ||||||
| msgid "The disk space option template that this disk space option is based on" | msgid "The disk space option template that this disk space option is based on" | ||||||
| msgstr "" | msgstr "" | ||||||
| "Die Speicherplatzoptionsvorlage auf der diese Speicherplatzoption aufgebaut " | "Die Speicherplatzoptionsvorlage auf der diese Speicherplatzoption aufgebaut " | ||||||
| "ist" | "ist" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:450 | #: hostingpackages/models.py:458 | ||||||
| msgid "user database option template" | msgid "user database option template" | ||||||
| msgstr "Nutzerdatenbankoptionsvorlage" | msgstr "Nutzerdatenbankoptionsvorlage" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:452 | #: hostingpackages/models.py:460 | ||||||
| msgid "The user database option template that this database option is based on" | msgid "The user database option template that this database option is based on" | ||||||
| msgstr "" | msgstr "" | ||||||
| "Die Nutzerdatenbankoptionsvorlage auf der diese Datenbankoption aufgebaut ist" | "Die Nutzerdatenbankoptionsvorlage auf der diese Datenbankoption aufgebaut ist" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:467 | #: hostingpackages/models.py:474 | ||||||
| msgid "mailbox option template" | msgid "mailbox option template" | ||||||
| msgstr "Postfachoptionsvorlage" | msgstr "Postfachoptionsvorlage" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/models.py:468 | #: hostingpackages/models.py:476 | ||||||
| msgid "The mailbox option template that this mailbox option is based on" | msgid "The mailbox option template that this mailbox option is based on" | ||||||
| msgstr "Die Postfachoptionsvorlage auf der diese Postfachoption aufgebaut ist" | msgstr "Die Postfachoptionsvorlage auf der diese Postfachoption aufgebaut ist" | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/templates/hostingpackages/add_hosting_option.html:4 | #: hostingpackages/views.py:60 hostingpackages/views.py:94 | ||||||
| #: 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 | #, python-brace-format | ||||||
| msgid "Started setup of new hosting package {name}." | msgid "Started setup of new hosting package {name}." | ||||||
| msgstr "Einrichtung des Hostingpakets {name} wurde gestartet." | msgstr "Einrichtung des Hostingpakets {name} wurde gestartet." | ||||||
| 
 | 
 | ||||||
| #: hostingpackages/views.py:287 | #: 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 | ||||||
| #, python-brace-format | #, python-brace-format | ||||||
| msgid "Successfully added option {option} to hosting package {package}." | msgid "Successfully added option {option} to hosting package {package}." | ||||||
| msgstr "Option {option} erfolgreich zum Hostingpaket {package} hinzugefügt." | msgstr "Option {option} erfolgreich zum Hostingpaket {package} hinzugefügt." | ||||||
|  | 
 | ||||||
|  | #~ msgid "Hosting options" | ||||||
|  | #~ msgstr "Hostingoptionen" | ||||||
|  |  | ||||||
|  | @ -1,4 +1,6 @@ | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | 
 | ||||||
| import django.utils.timezone | import django.utils.timezone | ||||||
| import model_utils.fields | import model_utils.fields | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
|  | @ -12,469 +14,301 @@ class Migration(migrations.Migration): | ||||||
| 
 | 
 | ||||||
|     operations = [ |     operations = [ | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="CustomerHostingPackage", |             name='CustomerHostingPackage', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('id', models.AutoField( | ||||||
|                     "id", |                     verbose_name='ID', serialize=False, auto_created=True, | ||||||
|                     models.AutoField( |                     primary_key=True)), | ||||||
|                         verbose_name="ID", |                 ('created', model_utils.fields.AutoCreatedField( | ||||||
|                         serialize=False, |                     default=django.utils.timezone.now, verbose_name='created', | ||||||
|                         auto_created=True, |                     editable=False)), | ||||||
|                         primary_key=True, |                 ('modified', model_utils.fields.AutoLastModifiedField( | ||||||
|                     ), |                     default=django.utils.timezone.now, verbose_name='modified', | ||||||
|                 ), |                     editable=False)), | ||||||
|                 ( |                 ('name', models.CharField( | ||||||
|                     "created", |                     unique=True, max_length=128, verbose_name='name')), | ||||||
|                     model_utils.fields.AutoCreatedField( |                 ('description', models.TextField( | ||||||
|                         default=django.utils.timezone.now, |                     verbose_name='description', blank=True)), | ||||||
|                         verbose_name="created", |                 ('mailboxcount', models.PositiveIntegerField( | ||||||
|                         editable=False, |                     verbose_name='mailbox count')), | ||||||
|                     ), |                 ('diskspace', models.PositiveIntegerField( | ||||||
|                 ), |                     help_text='disk space for the hosting package', | ||||||
|                 ( |                     verbose_name='disk space')), | ||||||
|                     "modified", |                 ('diskspace_unit', models.PositiveSmallIntegerField( | ||||||
|                     model_utils.fields.AutoLastModifiedField( |                     verbose_name='unit of disk space', | ||||||
|                         default=django.utils.timezone.now, |                     choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])), | ||||||
|                         verbose_name="modified", |                 ('customer', models.ForeignKey( | ||||||
|                         editable=False, |                     verbose_name='customer', to=settings.AUTH_USER_MODEL, | ||||||
|                     ), |                     on_delete=models.CASCADE)), | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "name", |  | ||||||
|                     models.CharField(unique=True, max_length=128, verbose_name="name"), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "description", |  | ||||||
|                     models.TextField(verbose_name="description", blank=True), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "mailboxcount", |  | ||||||
|                     models.PositiveIntegerField(verbose_name="mailbox count"), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "diskspace", |  | ||||||
|                     models.PositiveIntegerField( |  | ||||||
|                         help_text="disk space for the hosting package", |  | ||||||
|                         verbose_name="disk space", |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "diskspace_unit", |  | ||||||
|                     models.PositiveSmallIntegerField( |  | ||||||
|                         verbose_name="unit of disk space", |  | ||||||
|                         choices=[(0, "MiB"), (1, "GiB"), (2, "TiB")], |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "customer", |  | ||||||
|                     models.ForeignKey( |  | ||||||
|                         verbose_name="customer", |  | ||||||
|                         to=settings.AUTH_USER_MODEL, |  | ||||||
|                         on_delete=models.CASCADE, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "verbose_name": "customer hosting package", |                 'verbose_name': 'customer hosting package', | ||||||
|                 "verbose_name_plural": "customer hosting packages", |                 'verbose_name_plural': 'customer hosting packages', | ||||||
|             }, |             }, | ||||||
|             bases=(models.Model,), |             bases=(models.Model,), | ||||||
|         ), |         ), | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="CustomerHostingPackageOption", |             name='CustomerHostingPackageOption', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('id', models.AutoField( | ||||||
|                     "id", |                     verbose_name='ID', serialize=False, auto_created=True, | ||||||
|                     models.AutoField( |                     primary_key=True)), | ||||||
|                         verbose_name="ID", |                 ('created', model_utils.fields.AutoCreatedField( | ||||||
|                         serialize=False, |                     default=django.utils.timezone.now, verbose_name='created', | ||||||
|                         auto_created=True, |                     editable=False)), | ||||||
|                         primary_key=True, |                 ('modified', model_utils.fields.AutoLastModifiedField( | ||||||
|                     ), |                     default=django.utils.timezone.now, verbose_name='modified', | ||||||
|                 ), |                     editable=False)), | ||||||
|                 ( |  | ||||||
|                     "created", |  | ||||||
|                     model_utils.fields.AutoCreatedField( |  | ||||||
|                         default=django.utils.timezone.now, |  | ||||||
|                         verbose_name="created", |  | ||||||
|                         editable=False, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "modified", |  | ||||||
|                     model_utils.fields.AutoLastModifiedField( |  | ||||||
|                         default=django.utils.timezone.now, |  | ||||||
|                         verbose_name="modified", |  | ||||||
|                         editable=False, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "verbose_name": "customer hosting option", |                 'verbose_name': 'customer hosting option', | ||||||
|                 "verbose_name_plural": "customer hosting options", |                 'verbose_name_plural': 'customer hosting options', | ||||||
|             }, |             }, | ||||||
|             bases=(models.Model,), |             bases=(models.Model,), | ||||||
|         ), |         ), | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="CustomerDiskSpaceOption", |             name='CustomerDiskSpaceOption', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('customerhostingpackageoption_ptr', models.OneToOneField( | ||||||
|                     "customerhostingpackageoption_ptr", |                     parent_link=True, auto_created=True, primary_key=True, | ||||||
|                     models.OneToOneField( |                     serialize=False, | ||||||
|                         parent_link=True, |                     to='hostingpackages.CustomerHostingPackageOption', | ||||||
|                         auto_created=True, |                     on_delete=models.CASCADE)), | ||||||
|                         primary_key=True, |                 ('diskspace', models.PositiveIntegerField( | ||||||
|                         serialize=False, |                     verbose_name='disk space')), | ||||||
|                         to="hostingpackages.CustomerHostingPackageOption", |                 ('diskspace_unit', models.PositiveSmallIntegerField( | ||||||
|                         on_delete=models.CASCADE, |                     verbose_name='unit of disk space', | ||||||
|                     ), |                     choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])), | ||||||
|                 ), |  | ||||||
|                 ("diskspace", models.PositiveIntegerField(verbose_name="disk space")), |  | ||||||
|                 ( |  | ||||||
|                     "diskspace_unit", |  | ||||||
|                     models.PositiveSmallIntegerField( |  | ||||||
|                         verbose_name="unit of disk space", |  | ||||||
|                         choices=[(0, "MiB"), (1, "GiB"), (2, "TiB")], |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "ordering": ["diskspace_unit", "diskspace"], |                 'ordering': ['diskspace_unit', 'diskspace'], | ||||||
|                 "abstract": False, |                 'abstract': False, | ||||||
|                 "verbose_name": "Disk space option", |                 'verbose_name': 'Disk space option', | ||||||
|                 "verbose_name_plural": "Disk space options", |                 'verbose_name_plural': 'Disk space options', | ||||||
|             }, |             }, | ||||||
|             bases=("hostingpackages.customerhostingpackageoption", models.Model), |             bases=( | ||||||
|  |                 'hostingpackages.customerhostingpackageoption', models.Model), | ||||||
|         ), |         ), | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="CustomerMailboxOption", |             name='CustomerMailboxOption', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('customerhostingpackageoption_ptr', models.OneToOneField( | ||||||
|                     "customerhostingpackageoption_ptr", |                     parent_link=True, auto_created=True, primary_key=True, | ||||||
|                     models.OneToOneField( |                     serialize=False, | ||||||
|                         parent_link=True, |                     to='hostingpackages.CustomerHostingPackageOption', | ||||||
|                         auto_created=True, |                     on_delete=models.CASCADE)), | ||||||
|                         primary_key=True, |                 ('number', models.PositiveIntegerField( | ||||||
|                         serialize=False, |                     unique=True, verbose_name='number of mailboxes')), | ||||||
|                         to="hostingpackages.CustomerHostingPackageOption", |  | ||||||
|                         on_delete=models.CASCADE, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "number", |  | ||||||
|                     models.PositiveIntegerField( |  | ||||||
|                         unique=True, verbose_name="number of mailboxes" |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "ordering": ["number"], |                 'ordering': ['number'], | ||||||
|                 "abstract": False, |                 'abstract': False, | ||||||
|                 "verbose_name": "Mailbox option", |                 'verbose_name': 'Mailbox option', | ||||||
|                 "verbose_name_plural": "Mailbox options", |                 'verbose_name_plural': 'Mailbox options', | ||||||
|             }, |             }, | ||||||
|             bases=("hostingpackages.customerhostingpackageoption", models.Model), |             bases=( | ||||||
|  |                 'hostingpackages.customerhostingpackageoption', models.Model), | ||||||
|         ), |         ), | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="CustomerUserDatabaseOption", |             name='CustomerUserDatabaseOption', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('customerhostingpackageoption_ptr', models.OneToOneField( | ||||||
|                     "customerhostingpackageoption_ptr", |                     parent_link=True, auto_created=True, primary_key=True, | ||||||
|                     models.OneToOneField( |                     serialize=False, | ||||||
|                         parent_link=True, |                     to='hostingpackages.CustomerHostingPackageOption', | ||||||
|                         auto_created=True, |                     on_delete=models.CASCADE)), | ||||||
|                         primary_key=True, |                 ('number', models.PositiveIntegerField( | ||||||
|                         serialize=False, |                     default=1, verbose_name='number of databases')), | ||||||
|                         to="hostingpackages.CustomerHostingPackageOption", |                 ('db_type', models.PositiveSmallIntegerField( | ||||||
|                         on_delete=models.CASCADE, |                     verbose_name='database type', | ||||||
|                     ), |                     choices=[(0, 'PostgreSQL'), (1, 'MySQL')])), | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "number", |  | ||||||
|                     models.PositiveIntegerField( |  | ||||||
|                         default=1, verbose_name="number of databases" |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "db_type", |  | ||||||
|                     models.PositiveSmallIntegerField( |  | ||||||
|                         verbose_name="database type", |  | ||||||
|                         choices=[(0, "PostgreSQL"), (1, "MySQL")], |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "ordering": ["db_type", "number"], |                 'ordering': ['db_type', 'number'], | ||||||
|                 "abstract": False, |                 'abstract': False, | ||||||
|                 "verbose_name": "Database option", |                 'verbose_name': 'Database option', | ||||||
|                 "verbose_name_plural": "Database options", |                 'verbose_name_plural': 'Database options', | ||||||
|             }, |             }, | ||||||
|             bases=("hostingpackages.customerhostingpackageoption", models.Model), |             bases=( | ||||||
|  |                 'hostingpackages.customerhostingpackageoption', models.Model), | ||||||
|         ), |         ), | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="HostingOption", |             name='HostingOption', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('id', models.AutoField( | ||||||
|                     "id", |                     verbose_name='ID', serialize=False, auto_created=True, | ||||||
|                     models.AutoField( |                     primary_key=True)), | ||||||
|                         verbose_name="ID", |                 ('created', model_utils.fields.AutoCreatedField( | ||||||
|                         serialize=False, |                     default=django.utils.timezone.now, verbose_name='created', | ||||||
|                         auto_created=True, |                     editable=False)), | ||||||
|                         primary_key=True, |                 ('modified', model_utils.fields.AutoLastModifiedField( | ||||||
|                     ), |                     default=django.utils.timezone.now, verbose_name='modified', | ||||||
|                 ), |                     editable=False)), | ||||||
|                 ( |  | ||||||
|                     "created", |  | ||||||
|                     model_utils.fields.AutoCreatedField( |  | ||||||
|                         default=django.utils.timezone.now, |  | ||||||
|                         verbose_name="created", |  | ||||||
|                         editable=False, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "modified", |  | ||||||
|                     model_utils.fields.AutoLastModifiedField( |  | ||||||
|                         default=django.utils.timezone.now, |  | ||||||
|                         verbose_name="modified", |  | ||||||
|                         editable=False, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "verbose_name": "Hosting option", |                 'verbose_name': 'Hosting option', | ||||||
|                 "verbose_name_plural": "Hosting options", |                 'verbose_name_plural': 'Hosting options', | ||||||
|             }, |             }, | ||||||
|             bases=(models.Model,), |             bases=(models.Model,), | ||||||
|         ), |         ), | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="DiskSpaceOption", |             name='DiskSpaceOption', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('hostingoption_ptr', models.OneToOneField( | ||||||
|                     "hostingoption_ptr", |                     parent_link=True, auto_created=True, primary_key=True, | ||||||
|                     models.OneToOneField( |                     serialize=False, to='hostingpackages.HostingOption', | ||||||
|                         parent_link=True, |                     on_delete=models.CASCADE)), | ||||||
|                         auto_created=True, |                 ('diskspace', models.PositiveIntegerField( | ||||||
|                         primary_key=True, |                     verbose_name='disk space')), | ||||||
|                         serialize=False, |                 ('diskspace_unit', models.PositiveSmallIntegerField( | ||||||
|                         to="hostingpackages.HostingOption", |                     verbose_name='unit of disk space', | ||||||
|                         on_delete=models.CASCADE, |                     choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])), | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ("diskspace", models.PositiveIntegerField(verbose_name="disk space")), |  | ||||||
|                 ( |  | ||||||
|                     "diskspace_unit", |  | ||||||
|                     models.PositiveSmallIntegerField( |  | ||||||
|                         verbose_name="unit of disk space", |  | ||||||
|                         choices=[(0, "MiB"), (1, "GiB"), (2, "TiB")], |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "ordering": ["diskspace_unit", "diskspace"], |                 'ordering': ['diskspace_unit', 'diskspace'], | ||||||
|                 "abstract": False, |                 'abstract': False, | ||||||
|                 "verbose_name": "Disk space option", |                 'verbose_name': 'Disk space option', | ||||||
|                 "verbose_name_plural": "Disk space options", |                 'verbose_name_plural': 'Disk space options', | ||||||
|             }, |             }, | ||||||
|             bases=("hostingpackages.hostingoption", models.Model), |             bases=('hostingpackages.hostingoption', models.Model), | ||||||
|         ), |         ), | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="HostingPackageTemplate", |             name='HostingPackageTemplate', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('id', models.AutoField( | ||||||
|                     "id", |                     verbose_name='ID', serialize=False, auto_created=True, | ||||||
|                     models.AutoField( |                     primary_key=True)), | ||||||
|                         verbose_name="ID", |                 ('created', model_utils.fields.AutoCreatedField( | ||||||
|                         serialize=False, |                     default=django.utils.timezone.now, verbose_name='created', | ||||||
|                         auto_created=True, |                     editable=False)), | ||||||
|                         primary_key=True, |                 ('modified', model_utils.fields.AutoLastModifiedField( | ||||||
|                     ), |                     default=django.utils.timezone.now, verbose_name='modified', | ||||||
|                 ), |                     editable=False)), | ||||||
|                 ( |                 ('name', models.CharField( | ||||||
|                     "created", |                     unique=True, max_length=128, verbose_name='name')), | ||||||
|                     model_utils.fields.AutoCreatedField( |                 ('description', models.TextField( | ||||||
|                         default=django.utils.timezone.now, |                     verbose_name='description', blank=True)), | ||||||
|                         verbose_name="created", |                 ('mailboxcount', models.PositiveIntegerField( | ||||||
|                         editable=False, |                     verbose_name='mailbox count')), | ||||||
|                     ), |                 ('diskspace', models.PositiveIntegerField( | ||||||
|                 ), |                     help_text='disk space for the hosting package', | ||||||
|                 ( |                     verbose_name='disk space')), | ||||||
|                     "modified", |                 ('diskspace_unit', models.PositiveSmallIntegerField( | ||||||
|                     model_utils.fields.AutoLastModifiedField( |                     verbose_name='unit of disk space', | ||||||
|                         default=django.utils.timezone.now, |                     choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])), | ||||||
|                         verbose_name="modified", |  | ||||||
|                         editable=False, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "name", |  | ||||||
|                     models.CharField(unique=True, max_length=128, verbose_name="name"), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "description", |  | ||||||
|                     models.TextField(verbose_name="description", blank=True), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "mailboxcount", |  | ||||||
|                     models.PositiveIntegerField(verbose_name="mailbox count"), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "diskspace", |  | ||||||
|                     models.PositiveIntegerField( |  | ||||||
|                         help_text="disk space for the hosting package", |  | ||||||
|                         verbose_name="disk space", |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "diskspace_unit", |  | ||||||
|                     models.PositiveSmallIntegerField( |  | ||||||
|                         verbose_name="unit of disk space", |  | ||||||
|                         choices=[(0, "MiB"), (1, "GiB"), (2, "TiB")], |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "verbose_name": "Hosting package", |                 'verbose_name': 'Hosting package', | ||||||
|                 "verbose_name_plural": "Hosting packages", |                 'verbose_name_plural': 'Hosting packages', | ||||||
|             }, |             }, | ||||||
|             bases=(models.Model,), |             bases=(models.Model,), | ||||||
|         ), |         ), | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="MailboxOption", |             name='MailboxOption', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('hostingoption_ptr', models.OneToOneField( | ||||||
|                     "hostingoption_ptr", |                     parent_link=True, auto_created=True, primary_key=True, | ||||||
|                     models.OneToOneField( |                     serialize=False, to='hostingpackages.HostingOption', | ||||||
|                         parent_link=True, |                     on_delete=models.CASCADE)), | ||||||
|                         auto_created=True, |                 ('number', models.PositiveIntegerField( | ||||||
|                         primary_key=True, |                     unique=True, verbose_name='number of mailboxes')), | ||||||
|                         serialize=False, |  | ||||||
|                         to="hostingpackages.HostingOption", |  | ||||||
|                         on_delete=models.CASCADE, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "number", |  | ||||||
|                     models.PositiveIntegerField( |  | ||||||
|                         unique=True, verbose_name="number of mailboxes" |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "ordering": ["number"], |                 'ordering': ['number'], | ||||||
|                 "abstract": False, |                 'abstract': False, | ||||||
|                 "verbose_name": "Mailbox option", |                 'verbose_name': 'Mailbox option', | ||||||
|                 "verbose_name_plural": "Mailbox options", |                 'verbose_name_plural': 'Mailbox options', | ||||||
|             }, |             }, | ||||||
|             bases=("hostingpackages.hostingoption", models.Model), |             bases=('hostingpackages.hostingoption', models.Model), | ||||||
|         ), |         ), | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="UserDatabaseOption", |             name='UserDatabaseOption', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('hostingoption_ptr', models.OneToOneField( | ||||||
|                     "hostingoption_ptr", |                     parent_link=True, auto_created=True, primary_key=True, | ||||||
|                     models.OneToOneField( |                     serialize=False, to='hostingpackages.HostingOption', | ||||||
|                         parent_link=True, |                     on_delete=models.CASCADE)), | ||||||
|                         auto_created=True, |                 ('number', models.PositiveIntegerField( | ||||||
|                         primary_key=True, |                     default=1, verbose_name='number of databases')), | ||||||
|                         serialize=False, |                 ('db_type', models.PositiveSmallIntegerField( | ||||||
|                         to="hostingpackages.HostingOption", |                     verbose_name='database type', | ||||||
|                         on_delete=models.CASCADE, |                     choices=[(0, 'PostgreSQL'), (1, 'MySQL')])), | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "number", |  | ||||||
|                     models.PositiveIntegerField( |  | ||||||
|                         default=1, verbose_name="number of databases" |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "db_type", |  | ||||||
|                     models.PositiveSmallIntegerField( |  | ||||||
|                         verbose_name="database type", |  | ||||||
|                         choices=[(0, "PostgreSQL"), (1, "MySQL")], |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "ordering": ["db_type", "number"], |                 'ordering': ['db_type', 'number'], | ||||||
|                 "abstract": False, |                 'abstract': False, | ||||||
|                 "verbose_name": "Database option", |                 'verbose_name': 'Database option', | ||||||
|                 "verbose_name_plural": "Database options", |                 'verbose_name_plural': 'Database options', | ||||||
|             }, |             }, | ||||||
|             bases=("hostingpackages.hostingoption", models.Model), |             bases=('hostingpackages.hostingoption', models.Model), | ||||||
|         ), |         ), | ||||||
|         migrations.AlterUniqueTogether( |         migrations.AlterUniqueTogether( | ||||||
|             name="userdatabaseoption", |             name='userdatabaseoption', | ||||||
|             unique_together={("number", "db_type")}, |             unique_together={('number', 'db_type')}, | ||||||
|         ), |         ), | ||||||
|         migrations.AlterUniqueTogether( |         migrations.AlterUniqueTogether( | ||||||
|             name="diskspaceoption", |             name='diskspaceoption', | ||||||
|             unique_together={("diskspace", "diskspace_unit")}, |             unique_together={('diskspace', 'diskspace_unit')}, | ||||||
|         ), |         ), | ||||||
|         migrations.AddField( |         migrations.AddField( | ||||||
|             model_name="customeruserdatabaseoption", |             model_name='customeruserdatabaseoption', | ||||||
|             name="template", |             name='template', | ||||||
|             field=models.ForeignKey( |             field=models.ForeignKey( | ||||||
|                 verbose_name="user database option template", |                 verbose_name='user database option template', | ||||||
|                 to="hostingpackages.UserDatabaseOption", |                 to='hostingpackages.UserDatabaseOption', | ||||||
|                 help_text="The user database option template that this " |                 help_text='The user database option template that this ' | ||||||
|                 "hosting option is based on", |                           'hosting option is based on', | ||||||
|                 on_delete=models.CASCADE, |                 on_delete=models.CASCADE), | ||||||
|             ), |  | ||||||
|             preserve_default=True, |             preserve_default=True, | ||||||
|         ), |         ), | ||||||
|         migrations.AlterUniqueTogether( |         migrations.AlterUniqueTogether( | ||||||
|             name="customeruserdatabaseoption", |             name='customeruserdatabaseoption', | ||||||
|             unique_together={("number", "db_type")}, |             unique_together={('number', 'db_type')}, | ||||||
|         ), |         ), | ||||||
|         migrations.AddField( |         migrations.AddField( | ||||||
|             model_name="customermailboxoption", |             model_name='customermailboxoption', | ||||||
|             name="template", |             name='template', | ||||||
|             field=models.ForeignKey( |             field=models.ForeignKey( | ||||||
|                 verbose_name="mailbox option template", |                 verbose_name='mailbox option template', | ||||||
|                 to="hostingpackages.UserDatabaseOption", |                 to='hostingpackages.UserDatabaseOption', | ||||||
|                 help_text="The mailbox option template that this hosting " |                 help_text='The mailbox option template that this hosting ' | ||||||
|                 "option is based on", |                           'option is based on', | ||||||
|                 on_delete=models.CASCADE, |                 on_delete=models.CASCADE), | ||||||
|             ), |  | ||||||
|             preserve_default=True, |             preserve_default=True, | ||||||
|         ), |         ), | ||||||
|         migrations.AddField( |         migrations.AddField( | ||||||
|             model_name="customerhostingpackageoption", |             model_name='customerhostingpackageoption', | ||||||
|             name="hosting_package", |             name='hosting_package', | ||||||
|             field=models.ForeignKey( |             field=models.ForeignKey( | ||||||
|                 verbose_name="hosting package", |                 verbose_name='hosting package', | ||||||
|                 to="hostingpackages.CustomerHostingPackage", |                 to='hostingpackages.CustomerHostingPackage', | ||||||
|                 on_delete=models.CASCADE, |                 on_delete=models.CASCADE), | ||||||
|             ), |  | ||||||
|             preserve_default=True, |             preserve_default=True, | ||||||
|         ), |         ), | ||||||
|         migrations.AddField( |         migrations.AddField( | ||||||
|             model_name="customerhostingpackage", |             model_name='customerhostingpackage', | ||||||
|             name="template", |             name='template', | ||||||
|             field=models.ForeignKey( |             field=models.ForeignKey( | ||||||
|                 verbose_name="hosting package template", |                 verbose_name='hosting package template', | ||||||
|                 to="hostingpackages.HostingPackageTemplate", |                 to='hostingpackages.HostingPackageTemplate', | ||||||
|                 help_text="The hosting package template that this hosting " |                 help_text='The hosting package template that this hosting ' | ||||||
|                 "package is based on.", |                           'package is based on.', | ||||||
|                 on_delete=models.CASCADE, |                 on_delete=models.CASCADE), | ||||||
|             ), |  | ||||||
|             preserve_default=True, |             preserve_default=True, | ||||||
|         ), |         ), | ||||||
|         migrations.AddField( |         migrations.AddField( | ||||||
|             model_name="customerdiskspaceoption", |             model_name='customerdiskspaceoption', | ||||||
|             name="template", |             name='template', | ||||||
|             field=models.ForeignKey( |             field=models.ForeignKey( | ||||||
|                 verbose_name="disk space option template", |                 verbose_name='disk space option template', | ||||||
|                 to="hostingpackages.DiskSpaceOption", |                 to='hostingpackages.DiskSpaceOption', | ||||||
|                 help_text="The disk space option template that this hosting " |                 help_text='The disk space option template that this hosting ' | ||||||
|                 "option is based on", |                           'option is based on', | ||||||
|                 on_delete=models.CASCADE, |                 on_delete=models.CASCADE), | ||||||
|             ), |  | ||||||
|             preserve_default=True, |             preserve_default=True, | ||||||
|         ), |         ), | ||||||
|         migrations.AlterUniqueTogether( |         migrations.AlterUniqueTogether( | ||||||
|             name="customerdiskspaceoption", |             name='customerdiskspaceoption', | ||||||
|             unique_together={("diskspace", "diskspace_unit")}, |             unique_together={('diskspace', 'diskspace_unit')}, | ||||||
|         ), |         ), | ||||||
|     ] |     ] | ||||||
|  |  | ||||||
|  | @ -1,4 +1,6 @@ | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | 
 | ||||||
| import django.utils.timezone | import django.utils.timezone | ||||||
| import model_utils.fields | import model_utils.fields | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
|  | @ -6,530 +8,361 @@ from django.db import migrations, models | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
|     replaces = [ |     replaces = [('hostingpackages', '0001_initial'), | ||||||
|         ("hostingpackages", "0001_initial"), |                 ('hostingpackages', '0002_auto_20150118_1149'), | ||||||
|         ("hostingpackages", "0002_auto_20150118_1149"), |                 ('hostingpackages', '0003_auto_20150118_1221'), | ||||||
|         ("hostingpackages", "0003_auto_20150118_1221"), |                 ('hostingpackages', '0004_customerhostingpackage_osuser'), | ||||||
|         ("hostingpackages", "0004_customerhostingpackage_osuser"), |                 ('hostingpackages', '0005_auto_20150118_1303')] | ||||||
|         ("hostingpackages", "0005_auto_20150118_1303"), |  | ||||||
|     ] |  | ||||||
| 
 | 
 | ||||||
|     dependencies = [ |     dependencies = [ | ||||||
|         migrations.swappable_dependency(settings.AUTH_USER_MODEL), |         migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||||||
|         ("osusers", "0004_auto_20150104_1751"), |         ('osusers', '0004_auto_20150104_1751'), | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     operations = [ |     operations = [ | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="CustomerHostingPackage", |             name='CustomerHostingPackage', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('id', models.AutoField( | ||||||
|                     "id", |                     verbose_name='ID', serialize=False, auto_created=True, | ||||||
|                     models.AutoField( |                     primary_key=True)), | ||||||
|                         verbose_name="ID", |                 ('created', model_utils.fields.AutoCreatedField( | ||||||
|                         serialize=False, |                     default=django.utils.timezone.now, verbose_name='created', | ||||||
|                         auto_created=True, |                     editable=False)), | ||||||
|                         primary_key=True, |                 ('modified', model_utils.fields.AutoLastModifiedField( | ||||||
|                     ), |                     default=django.utils.timezone.now, verbose_name='modified', | ||||||
|                 ), |                     editable=False)), | ||||||
|                 ( |                 ('name', models.CharField( | ||||||
|                     "created", |                     unique=True, max_length=128, verbose_name='name')), | ||||||
|                     model_utils.fields.AutoCreatedField( |                 ('description', models.TextField( | ||||||
|                         default=django.utils.timezone.now, |                     verbose_name='description', blank=True)), | ||||||
|                         verbose_name="created", |                 ('mailboxcount', models.PositiveIntegerField( | ||||||
|                         editable=False, |                     verbose_name='mailbox count')), | ||||||
|                     ), |                 ('diskspace', models.PositiveIntegerField( | ||||||
|                 ), |                     help_text='disk space for the hosting package', | ||||||
|                 ( |                     verbose_name='disk space')), | ||||||
|                     "modified", |                 ('diskspace_unit', models.PositiveSmallIntegerField( | ||||||
|                     model_utils.fields.AutoLastModifiedField( |                     verbose_name='unit of disk space', | ||||||
|                         default=django.utils.timezone.now, |                     choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])), | ||||||
|                         verbose_name="modified", |                 ('customer', models.ForeignKey( | ||||||
|                         editable=False, |                     verbose_name='customer', | ||||||
|                     ), |                     to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "name", |  | ||||||
|                     models.CharField(unique=True, max_length=128, verbose_name="name"), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "description", |  | ||||||
|                     models.TextField(verbose_name="description", blank=True), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "mailboxcount", |  | ||||||
|                     models.PositiveIntegerField(verbose_name="mailbox count"), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "diskspace", |  | ||||||
|                     models.PositiveIntegerField( |  | ||||||
|                         help_text="disk space for the hosting package", |  | ||||||
|                         verbose_name="disk space", |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "diskspace_unit", |  | ||||||
|                     models.PositiveSmallIntegerField( |  | ||||||
|                         verbose_name="unit of disk space", |  | ||||||
|                         choices=[(0, "MiB"), (1, "GiB"), (2, "TiB")], |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "customer", |  | ||||||
|                     models.ForeignKey( |  | ||||||
|                         verbose_name="customer", |  | ||||||
|                         to=settings.AUTH_USER_MODEL, |  | ||||||
|                         on_delete=models.CASCADE, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "verbose_name": "customer hosting package", |                 'verbose_name': 'customer hosting package', | ||||||
|                 "verbose_name_plural": "customer hosting packages", |                 'verbose_name_plural': 'customer hosting packages', | ||||||
|             }, |             }, | ||||||
|             bases=(models.Model,), |             bases=(models.Model,), | ||||||
|         ), |         ), | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="CustomerHostingPackageOption", |             name='CustomerHostingPackageOption', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('id', models.AutoField( | ||||||
|                     "id", |                     verbose_name='ID', serialize=False, auto_created=True, | ||||||
|                     models.AutoField( |                     primary_key=True)), | ||||||
|                         verbose_name="ID", |                 ('created', model_utils.fields.AutoCreatedField( | ||||||
|                         serialize=False, |                     default=django.utils.timezone.now, verbose_name='created', | ||||||
|                         auto_created=True, |                     editable=False)), | ||||||
|                         primary_key=True, |                 ('modified', model_utils.fields.AutoLastModifiedField( | ||||||
|                     ), |                     default=django.utils.timezone.now, verbose_name='modified', | ||||||
|                 ), |                     editable=False)), | ||||||
|                 ( |  | ||||||
|                     "created", |  | ||||||
|                     model_utils.fields.AutoCreatedField( |  | ||||||
|                         default=django.utils.timezone.now, |  | ||||||
|                         verbose_name="created", |  | ||||||
|                         editable=False, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "modified", |  | ||||||
|                     model_utils.fields.AutoLastModifiedField( |  | ||||||
|                         default=django.utils.timezone.now, |  | ||||||
|                         verbose_name="modified", |  | ||||||
|                         editable=False, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "verbose_name": "customer hosting option", |                 'verbose_name': 'customer hosting option', | ||||||
|                 "verbose_name_plural": "customer hosting options", |                 'verbose_name_plural': 'customer hosting options', | ||||||
|             }, |             }, | ||||||
|             bases=(models.Model,), |             bases=(models.Model,), | ||||||
|         ), |         ), | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="CustomerDiskSpaceOption", |             name='CustomerDiskSpaceOption', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('customerhostingpackageoption_ptr', | ||||||
|                     "customerhostingpackageoption_ptr", |                  models.OneToOneField( | ||||||
|                     models.OneToOneField( |                      parent_link=True, auto_created=True, primary_key=True, | ||||||
|                         parent_link=True, |                      serialize=False, | ||||||
|                         auto_created=True, |                      to='hostingpackages.CustomerHostingPackageOption', | ||||||
|                         primary_key=True, |                      on_delete=models.CASCADE)), | ||||||
|                         serialize=False, |                 ('diskspace', models.PositiveIntegerField( | ||||||
|                         to="hostingpackages.CustomerHostingPackageOption", |                     verbose_name='disk space')), | ||||||
|                         on_delete=models.CASCADE, |                 ('diskspace_unit', models.PositiveSmallIntegerField( | ||||||
|                     ), |                     verbose_name='unit of disk space', | ||||||
|                 ), |                     choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])), | ||||||
|                 ("diskspace", models.PositiveIntegerField(verbose_name="disk space")), |  | ||||||
|                 ( |  | ||||||
|                     "diskspace_unit", |  | ||||||
|                     models.PositiveSmallIntegerField( |  | ||||||
|                         verbose_name="unit of disk space", |  | ||||||
|                         choices=[(0, "MiB"), (1, "GiB"), (2, "TiB")], |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "ordering": ["diskspace_unit", "diskspace"], |                 'ordering': ['diskspace_unit', 'diskspace'], | ||||||
|                 "abstract": False, |                 'abstract': False, | ||||||
|                 "verbose_name": "Disk space option", |                 'verbose_name': 'Disk space option', | ||||||
|                 "verbose_name_plural": "Disk space options", |                 'verbose_name_plural': 'Disk space options', | ||||||
|             }, |             }, | ||||||
|             bases=("hostingpackages.customerhostingpackageoption", models.Model), |             bases=( | ||||||
|  |                 'hostingpackages.customerhostingpackageoption', models.Model), | ||||||
|         ), |         ), | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="CustomerMailboxOption", |             name='CustomerMailboxOption', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('customerhostingpackageoption_ptr', | ||||||
|                     "customerhostingpackageoption_ptr", |                  models.OneToOneField( | ||||||
|                     models.OneToOneField( |                      parent_link=True, auto_created=True, primary_key=True, | ||||||
|                         parent_link=True, |                      serialize=False, | ||||||
|                         auto_created=True, |                      to='hostingpackages.CustomerHostingPackageOption', | ||||||
|                         primary_key=True, |                      on_delete=models.CASCADE)), | ||||||
|                         serialize=False, |                 ('number', models.PositiveIntegerField( | ||||||
|                         to="hostingpackages.CustomerHostingPackageOption", |                     unique=True, verbose_name='number of mailboxes')), | ||||||
|                         on_delete=models.CASCADE, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "number", |  | ||||||
|                     models.PositiveIntegerField( |  | ||||||
|                         unique=True, verbose_name="number of mailboxes" |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "ordering": ["number"], |                 'ordering': ['number'], | ||||||
|                 "abstract": False, |                 'abstract': False, | ||||||
|                 "verbose_name": "Mailbox option", |                 'verbose_name': 'Mailbox option', | ||||||
|                 "verbose_name_plural": "Mailbox options", |                 'verbose_name_plural': 'Mailbox options', | ||||||
|             }, |             }, | ||||||
|             bases=("hostingpackages.customerhostingpackageoption", models.Model), |             bases=( | ||||||
|  |                 'hostingpackages.customerhostingpackageoption', models.Model), | ||||||
|         ), |         ), | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="CustomerUserDatabaseOption", |             name='CustomerUserDatabaseOption', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('customerhostingpackageoption_ptr', | ||||||
|                     "customerhostingpackageoption_ptr", |                  models.OneToOneField( | ||||||
|                     models.OneToOneField( |                      parent_link=True, auto_created=True, primary_key=True, | ||||||
|                         parent_link=True, |                      serialize=False, | ||||||
|                         auto_created=True, |                      to='hostingpackages.CustomerHostingPackageOption', | ||||||
|                         primary_key=True, |                      on_delete=models.CASCADE)), | ||||||
|                         serialize=False, |                 ('number', models.PositiveIntegerField( | ||||||
|                         to="hostingpackages.CustomerHostingPackageOption", |                     default=1, verbose_name='number of databases')), | ||||||
|                         on_delete=models.CASCADE, |                 ('db_type', models.PositiveSmallIntegerField( | ||||||
|                     ), |                     verbose_name='database type', | ||||||
|                 ), |                     choices=[(0, 'PostgreSQL'), (1, 'MySQL')])), | ||||||
|                 ( |  | ||||||
|                     "number", |  | ||||||
|                     models.PositiveIntegerField( |  | ||||||
|                         default=1, verbose_name="number of databases" |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "db_type", |  | ||||||
|                     models.PositiveSmallIntegerField( |  | ||||||
|                         verbose_name="database type", |  | ||||||
|                         choices=[(0, "PostgreSQL"), (1, "MySQL")], |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "ordering": ["db_type", "number"], |                 'ordering': ['db_type', 'number'], | ||||||
|                 "abstract": False, |                 'abstract': False, | ||||||
|                 "verbose_name": "Database option", |                 'verbose_name': 'Database option', | ||||||
|                 "verbose_name_plural": "Database options", |                 'verbose_name_plural': 'Database options', | ||||||
|             }, |             }, | ||||||
|             bases=("hostingpackages.customerhostingpackageoption", models.Model), |             bases=( | ||||||
|  |                 'hostingpackages.customerhostingpackageoption', models.Model), | ||||||
|         ), |         ), | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="HostingOption", |             name='HostingOption', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('id', models.AutoField( | ||||||
|                     "id", |                     verbose_name='ID', serialize=False, auto_created=True, | ||||||
|                     models.AutoField( |                     primary_key=True)), | ||||||
|                         verbose_name="ID", |                 ('created', model_utils.fields.AutoCreatedField( | ||||||
|                         serialize=False, |                     default=django.utils.timezone.now, verbose_name='created', | ||||||
|                         auto_created=True, |                     editable=False)), | ||||||
|                         primary_key=True, |                 ('modified', model_utils.fields.AutoLastModifiedField( | ||||||
|                     ), |                     default=django.utils.timezone.now, verbose_name='modified', | ||||||
|                 ), |                     editable=False)), | ||||||
|                 ( |  | ||||||
|                     "created", |  | ||||||
|                     model_utils.fields.AutoCreatedField( |  | ||||||
|                         default=django.utils.timezone.now, |  | ||||||
|                         verbose_name="created", |  | ||||||
|                         editable=False, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "modified", |  | ||||||
|                     model_utils.fields.AutoLastModifiedField( |  | ||||||
|                         default=django.utils.timezone.now, |  | ||||||
|                         verbose_name="modified", |  | ||||||
|                         editable=False, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "verbose_name": "Hosting option", |                 'verbose_name': 'Hosting option', | ||||||
|                 "verbose_name_plural": "Hosting options", |                 'verbose_name_plural': 'Hosting options', | ||||||
|             }, |             }, | ||||||
|             bases=(models.Model,), |             bases=(models.Model,), | ||||||
|         ), |         ), | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="DiskSpaceOption", |             name='DiskSpaceOption', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('hostingoption_ptr', | ||||||
|                     "hostingoption_ptr", |                  models.OneToOneField( | ||||||
|                     models.OneToOneField( |                      parent_link=True, auto_created=True, primary_key=True, | ||||||
|                         parent_link=True, |                      serialize=False, to='hostingpackages.HostingOption', | ||||||
|                         auto_created=True, |                      on_delete=models.CASCADE)), | ||||||
|                         primary_key=True, |                 ('diskspace', models.PositiveIntegerField( | ||||||
|                         serialize=False, |                     verbose_name='disk space')), | ||||||
|                         to="hostingpackages.HostingOption", |                 ('diskspace_unit', models.PositiveSmallIntegerField( | ||||||
|                         on_delete=models.CASCADE, |                     verbose_name='unit of disk space', | ||||||
|                     ), |                     choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])), | ||||||
|                 ), |  | ||||||
|                 ("diskspace", models.PositiveIntegerField(verbose_name="disk space")), |  | ||||||
|                 ( |  | ||||||
|                     "diskspace_unit", |  | ||||||
|                     models.PositiveSmallIntegerField( |  | ||||||
|                         verbose_name="unit of disk space", |  | ||||||
|                         choices=[(0, "MiB"), (1, "GiB"), (2, "TiB")], |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "ordering": ["diskspace_unit", "diskspace"], |                 'ordering': ['diskspace_unit', 'diskspace'], | ||||||
|                 "abstract": False, |                 'abstract': False, | ||||||
|                 "verbose_name": "Disk space option", |                 'verbose_name': 'Disk space option', | ||||||
|                 "verbose_name_plural": "Disk space options", |                 'verbose_name_plural': 'Disk space options', | ||||||
|             }, |             }, | ||||||
|             bases=("hostingpackages.hostingoption", models.Model), |             bases=('hostingpackages.hostingoption', models.Model), | ||||||
|         ), |         ), | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="HostingPackageTemplate", |             name='HostingPackageTemplate', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('id', models.AutoField( | ||||||
|                     "id", |                     verbose_name='ID', serialize=False, auto_created=True, | ||||||
|                     models.AutoField( |                     primary_key=True)), | ||||||
|                         verbose_name="ID", |                 ('created', model_utils.fields.AutoCreatedField( | ||||||
|                         serialize=False, |                     default=django.utils.timezone.now, verbose_name='created', | ||||||
|                         auto_created=True, |                     editable=False)), | ||||||
|                         primary_key=True, |                 ('modified', model_utils.fields.AutoLastModifiedField( | ||||||
|                     ), |                     default=django.utils.timezone.now, verbose_name='modified', | ||||||
|                 ), |                     editable=False)), | ||||||
|                 ( |                 ('name', models.CharField( | ||||||
|                     "created", |                     unique=True, max_length=128, verbose_name='name')), | ||||||
|                     model_utils.fields.AutoCreatedField( |                 ('description', models.TextField( | ||||||
|                         default=django.utils.timezone.now, |                     verbose_name='description', blank=True)), | ||||||
|                         verbose_name="created", |                 ('mailboxcount', models.PositiveIntegerField( | ||||||
|                         editable=False, |                     verbose_name='mailbox count')), | ||||||
|                     ), |                 ('diskspace', models.PositiveIntegerField( | ||||||
|                 ), |                     help_text='disk space for the hosting package', | ||||||
|                 ( |                     verbose_name='disk space')), | ||||||
|                     "modified", |                 ('diskspace_unit', models.PositiveSmallIntegerField( | ||||||
|                     model_utils.fields.AutoLastModifiedField( |                     verbose_name='unit of disk space', | ||||||
|                         default=django.utils.timezone.now, |                     choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])), | ||||||
|                         verbose_name="modified", |  | ||||||
|                         editable=False, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "name", |  | ||||||
|                     models.CharField(unique=True, max_length=128, verbose_name="name"), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "description", |  | ||||||
|                     models.TextField(verbose_name="description", blank=True), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "mailboxcount", |  | ||||||
|                     models.PositiveIntegerField(verbose_name="mailbox count"), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "diskspace", |  | ||||||
|                     models.PositiveIntegerField( |  | ||||||
|                         help_text="disk space for the hosting package", |  | ||||||
|                         verbose_name="disk space", |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "diskspace_unit", |  | ||||||
|                     models.PositiveSmallIntegerField( |  | ||||||
|                         verbose_name="unit of disk space", |  | ||||||
|                         choices=[(0, "MiB"), (1, "GiB"), (2, "TiB")], |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "verbose_name": "Hosting package", |                 'verbose_name': 'Hosting package', | ||||||
|                 "verbose_name_plural": "Hosting packages", |                 'verbose_name_plural': 'Hosting packages', | ||||||
|             }, |             }, | ||||||
|             bases=(models.Model,), |             bases=(models.Model,), | ||||||
|         ), |         ), | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="MailboxOption", |             name='MailboxOption', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('hostingoption_ptr', | ||||||
|                     "hostingoption_ptr", |                  models.OneToOneField( | ||||||
|                     models.OneToOneField( |                      parent_link=True, auto_created=True, primary_key=True, | ||||||
|                         parent_link=True, |                      serialize=False, to='hostingpackages.HostingOption', | ||||||
|                         auto_created=True, |                      on_delete=models.CASCADE)), | ||||||
|                         primary_key=True, |                 ('number', models.PositiveIntegerField( | ||||||
|                         serialize=False, |                     unique=True, verbose_name='number of mailboxes')), | ||||||
|                         to="hostingpackages.HostingOption", |  | ||||||
|                         on_delete=models.CASCADE, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "number", |  | ||||||
|                     models.PositiveIntegerField( |  | ||||||
|                         unique=True, verbose_name="number of mailboxes" |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "ordering": ["number"], |                 'ordering': ['number'], | ||||||
|                 "abstract": False, |                 'abstract': False, | ||||||
|                 "verbose_name": "Mailbox option", |                 'verbose_name': 'Mailbox option', | ||||||
|                 "verbose_name_plural": "Mailbox options", |                 'verbose_name_plural': 'Mailbox options', | ||||||
|             }, |             }, | ||||||
|             bases=("hostingpackages.hostingoption", models.Model), |             bases=('hostingpackages.hostingoption', models.Model), | ||||||
|         ), |         ), | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="UserDatabaseOption", |             name='UserDatabaseOption', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('hostingoption_ptr', | ||||||
|                     "hostingoption_ptr", |                  models.OneToOneField( | ||||||
|                     models.OneToOneField( |                      parent_link=True, auto_created=True, primary_key=True, | ||||||
|                         parent_link=True, |                      serialize=False, to='hostingpackages.HostingOption', | ||||||
|                         auto_created=True, |                      on_delete=models.CASCADE)), | ||||||
|                         primary_key=True, |                 ('number', models.PositiveIntegerField( | ||||||
|                         serialize=False, |                     default=1, verbose_name='number of databases')), | ||||||
|                         to="hostingpackages.HostingOption", |                 ('db_type', | ||||||
|                         on_delete=models.CASCADE, |                  models.PositiveSmallIntegerField( | ||||||
|                     ), |                      verbose_name='database type', | ||||||
|                 ), |                      choices=[(0, 'PostgreSQL'), (1, 'MySQL')])), | ||||||
|                 ( |  | ||||||
|                     "number", |  | ||||||
|                     models.PositiveIntegerField( |  | ||||||
|                         default=1, verbose_name="number of databases" |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "db_type", |  | ||||||
|                     models.PositiveSmallIntegerField( |  | ||||||
|                         verbose_name="database type", |  | ||||||
|                         choices=[(0, "PostgreSQL"), (1, "MySQL")], |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "ordering": ["db_type", "number"], |                 'ordering': ['db_type', 'number'], | ||||||
|                 "abstract": False, |                 'abstract': False, | ||||||
|                 "verbose_name": "Database option", |                 'verbose_name': 'Database option', | ||||||
|                 "verbose_name_plural": "Database options", |                 'verbose_name_plural': 'Database options', | ||||||
|             }, |             }, | ||||||
|             bases=("hostingpackages.hostingoption", models.Model), |             bases=('hostingpackages.hostingoption', models.Model), | ||||||
|         ), |         ), | ||||||
|         migrations.AlterUniqueTogether( |         migrations.AlterUniqueTogether( | ||||||
|             name="userdatabaseoption", |             name='userdatabaseoption', | ||||||
|             unique_together={("number", "db_type")}, |             unique_together={('number', 'db_type')}, | ||||||
|         ), |         ), | ||||||
|         migrations.AlterUniqueTogether( |         migrations.AlterUniqueTogether( | ||||||
|             name="diskspaceoption", |             name='diskspaceoption', | ||||||
|             unique_together={("diskspace", "diskspace_unit")}, |             unique_together={('diskspace', 'diskspace_unit')}, | ||||||
|         ), |         ), | ||||||
|         migrations.AddField( |         migrations.AddField( | ||||||
|             model_name="customeruserdatabaseoption", |             model_name='customeruserdatabaseoption', | ||||||
|             name="template", |             name='template', | ||||||
|             field=models.ForeignKey( |             field=models.ForeignKey( | ||||||
|                 verbose_name="user database option template", |                 verbose_name='user database option template', | ||||||
|                 to="hostingpackages.UserDatabaseOption", |                 to='hostingpackages.UserDatabaseOption', | ||||||
|                 help_text="The user database option template that this " |                 help_text='The user database option template that this ' | ||||||
|                 "hosting option is based on", |                           'hosting option is based on', | ||||||
|                 on_delete=models.CASCADE, |                 on_delete=models.CASCADE), | ||||||
|             ), |  | ||||||
|             preserve_default=True, |             preserve_default=True, | ||||||
|         ), |         ), | ||||||
|         migrations.AlterUniqueTogether( |         migrations.AlterUniqueTogether( | ||||||
|             name="customeruserdatabaseoption", |             name='customeruserdatabaseoption', | ||||||
|             unique_together={("number", "db_type")}, |             unique_together={('number', 'db_type')}, | ||||||
|         ), |         ), | ||||||
|         migrations.AddField( |         migrations.AddField( | ||||||
|             model_name="customermailboxoption", |             model_name='customermailboxoption', | ||||||
|             name="template", |             name='template', | ||||||
|             field=models.ForeignKey( |             field=models.ForeignKey( | ||||||
|                 verbose_name="mailbox option template", |                 verbose_name='mailbox option template', | ||||||
|                 to="hostingpackages.UserDatabaseOption", |                 to='hostingpackages.UserDatabaseOption', | ||||||
|                 help_text="The mailbox option template that this mailbox " |                 help_text='The mailbox option template that this mailbox ' | ||||||
|                 "option is based on", |                           'option is based on', | ||||||
|                 on_delete=models.CASCADE, |                 on_delete=models.CASCADE), | ||||||
|             ), |  | ||||||
|             preserve_default=True, |             preserve_default=True, | ||||||
|         ), |         ), | ||||||
|         migrations.AddField( |         migrations.AddField( | ||||||
|             model_name="customerhostingpackageoption", |             model_name='customerhostingpackageoption', | ||||||
|             name="hosting_package", |             name='hosting_package', | ||||||
|             field=models.ForeignKey( |             field=models.ForeignKey( | ||||||
|                 verbose_name="hosting package", |                 verbose_name='hosting package', | ||||||
|                 to="hostingpackages.CustomerHostingPackage", |                 to='hostingpackages.CustomerHostingPackage', | ||||||
|                 on_delete=models.CASCADE, |                 on_delete=models.CASCADE), | ||||||
|             ), |  | ||||||
|             preserve_default=True, |             preserve_default=True, | ||||||
|         ), |         ), | ||||||
|         migrations.AddField( |         migrations.AddField( | ||||||
|             model_name="customerhostingpackage", |             model_name='customerhostingpackage', | ||||||
|             name="template", |             name='template', | ||||||
|             field=models.ForeignKey( |             field=models.ForeignKey( | ||||||
|                 verbose_name="hosting package template", |                 verbose_name='hosting package template', | ||||||
|                 to="hostingpackages.HostingPackageTemplate", |                 to='hostingpackages.HostingPackageTemplate', | ||||||
|                 help_text="The hosting package template that this hosting " |                 help_text='The hosting package template that this hosting ' | ||||||
|                 "package is based on", |                           'package is based on', | ||||||
|                 on_delete=models.CASCADE, |                 on_delete=models.CASCADE), | ||||||
|             ), |  | ||||||
|             preserve_default=True, |             preserve_default=True, | ||||||
|         ), |         ), | ||||||
|         migrations.AddField( |         migrations.AddField( | ||||||
|             model_name="customerdiskspaceoption", |             model_name='customerdiskspaceoption', | ||||||
|             name="template", |             name='template', | ||||||
|             field=models.ForeignKey( |             field=models.ForeignKey( | ||||||
|                 verbose_name="disk space option template", |                 verbose_name='disk space option template', | ||||||
|                 to="hostingpackages.DiskSpaceOption", |                 to='hostingpackages.DiskSpaceOption', | ||||||
|                 help_text="The disk space option template that this hosting " |                 help_text='The disk space option template that this hosting ' | ||||||
|                 "option is based on", |                           'option is based on', | ||||||
|                 on_delete=models.CASCADE, |                 on_delete=models.CASCADE), | ||||||
|             ), |  | ||||||
|             preserve_default=True, |             preserve_default=True, | ||||||
|         ), |         ), | ||||||
|         migrations.AlterUniqueTogether( |         migrations.AlterUniqueTogether( | ||||||
|             name="customerdiskspaceoption", |             name='customerdiskspaceoption', | ||||||
|             unique_together={("diskspace", "diskspace_unit")}, |             unique_together={('diskspace', 'diskspace_unit')}, | ||||||
|         ), |         ), | ||||||
|         migrations.AlterField( |         migrations.AlterField( | ||||||
|             model_name="customerdiskspaceoption", |             model_name='customerdiskspaceoption', | ||||||
|             name="template", |             name='template', | ||||||
|             field=models.ForeignKey( |             field=models.ForeignKey( | ||||||
|                 verbose_name="disk space option template", |                 verbose_name='disk space option template', | ||||||
|                 to="hostingpackages.DiskSpaceOption", |                 to='hostingpackages.DiskSpaceOption', | ||||||
|                 help_text="The disk space option template that this disk " |                 help_text='The disk space option template that this disk ' | ||||||
|                 "space option is based on", |                           'space option is based on', | ||||||
|                 on_delete=models.CASCADE, |                 on_delete=models.CASCADE), | ||||||
|             ), |  | ||||||
|             preserve_default=True, |             preserve_default=True, | ||||||
|         ), |         ), | ||||||
|         migrations.AlterField( |         migrations.AlterField( | ||||||
|             model_name="customeruserdatabaseoption", |             model_name='customeruserdatabaseoption', | ||||||
|             name="template", |             name='template', | ||||||
|             field=models.ForeignKey( |             field=models.ForeignKey( | ||||||
|                 verbose_name="user database option template", |                 verbose_name='user database option template', | ||||||
|                 to="hostingpackages.UserDatabaseOption", |                 to='hostingpackages.UserDatabaseOption', | ||||||
|                 help_text="The user database option template that this " |                 help_text='The user database option template that this ' | ||||||
|                 "database option is based on", |                           'database option is based on', | ||||||
|                 on_delete=models.CASCADE, |                 on_delete=models.CASCADE), | ||||||
|             ), |  | ||||||
|             preserve_default=True, |             preserve_default=True, | ||||||
|         ), |         ), | ||||||
|         migrations.AlterField( |         migrations.AlterField( | ||||||
|             model_name="customerhostingpackage", |             model_name='customerhostingpackage', | ||||||
|             name="name", |             name='name', | ||||||
|             field=models.CharField(max_length=128, verbose_name="name"), |             field=models.CharField(max_length=128, verbose_name='name'), | ||||||
|             preserve_default=True, |             preserve_default=True, | ||||||
|         ), |         ), | ||||||
|         migrations.AlterUniqueTogether( |         migrations.AlterUniqueTogether( | ||||||
|             name="customerhostingpackage", |             name='customerhostingpackage', | ||||||
|             unique_together={("customer", "name")}, |             unique_together={('customer', 'name')}, | ||||||
|         ), |         ), | ||||||
|         migrations.AddField( |         migrations.AddField( | ||||||
|             model_name="customerhostingpackage", |             model_name='customerhostingpackage', | ||||||
|             name="osuser", |             name='osuser', | ||||||
|             field=models.OneToOneField( |             field=models.OneToOneField( | ||||||
|                 null=True, |                 null=True, blank=True, to='osusers.User', | ||||||
|                 blank=True, |                 verbose_name='Operating system user', on_delete=models.CASCADE), | ||||||
|                 to="osusers.User", |  | ||||||
|                 verbose_name="Operating system user", |  | ||||||
|                 on_delete=models.CASCADE, |  | ||||||
|             ), |  | ||||||
|             preserve_default=True, |             preserve_default=True, | ||||||
|         ), |         ), | ||||||
|     ] |     ] | ||||||
|  |  | ||||||
|  | @ -1,59 +1,57 @@ | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | 
 | ||||||
| from django.db import migrations, models | from django.db import migrations, models | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
|     dependencies = [ |     dependencies = [ | ||||||
|         ("hostingpackages", "0001_initial"), |         ('hostingpackages', '0001_initial'), | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     operations = [ |     operations = [ | ||||||
|         migrations.AlterField( |         migrations.AlterField( | ||||||
|             model_name="customerdiskspaceoption", |             model_name='customerdiskspaceoption', | ||||||
|             name="template", |             name='template', | ||||||
|             field=models.ForeignKey( |             field=models.ForeignKey( | ||||||
|                 verbose_name="disk space option template", |                 verbose_name='disk space option template', | ||||||
|                 to="hostingpackages.DiskSpaceOption", |                 to='hostingpackages.DiskSpaceOption', | ||||||
|                 help_text="The disk space option template that this disk " |                 help_text='The disk space option template that this disk ' | ||||||
|                 "space option is based on", |                           'space option is based on', | ||||||
|                 on_delete=models.CASCADE, |                 on_delete=models.CASCADE), | ||||||
|             ), |  | ||||||
|             preserve_default=True, |             preserve_default=True, | ||||||
|         ), |         ), | ||||||
|         migrations.AlterField( |         migrations.AlterField( | ||||||
|             model_name="customerhostingpackage", |             model_name='customerhostingpackage', | ||||||
|             name="template", |             name='template', | ||||||
|             field=models.ForeignKey( |             field=models.ForeignKey( | ||||||
|                 verbose_name="hosting package template", |                 verbose_name='hosting package template', | ||||||
|                 to="hostingpackages.HostingPackageTemplate", |                 to='hostingpackages.HostingPackageTemplate', | ||||||
|                 help_text="The hosting package template that this hosting " |                 help_text='The hosting package template that this hosting ' | ||||||
|                 "package is based on", |                           'package is based on', | ||||||
|                 on_delete=models.CASCADE, |                 on_delete=models.CASCADE), | ||||||
|             ), |  | ||||||
|             preserve_default=True, |             preserve_default=True, | ||||||
|         ), |         ), | ||||||
|         migrations.AlterField( |         migrations.AlterField( | ||||||
|             model_name="customermailboxoption", |             model_name='customermailboxoption', | ||||||
|             name="template", |             name='template', | ||||||
|             field=models.ForeignKey( |             field=models.ForeignKey( | ||||||
|                 verbose_name="mailbox option template", |                 verbose_name='mailbox option template', | ||||||
|                 to="hostingpackages.UserDatabaseOption", |                 to='hostingpackages.UserDatabaseOption', | ||||||
|                 help_text="The mailbox option template that this mailbox " |                 help_text='The mailbox option template that this mailbox ' | ||||||
|                 "option is based on", |                           'option is based on', | ||||||
|                 on_delete=models.CASCADE, |                 on_delete=models.CASCADE), | ||||||
|             ), |  | ||||||
|             preserve_default=True, |             preserve_default=True, | ||||||
|         ), |         ), | ||||||
|         migrations.AlterField( |         migrations.AlterField( | ||||||
|             model_name="customeruserdatabaseoption", |             model_name='customeruserdatabaseoption', | ||||||
|             name="template", |             name='template', | ||||||
|             field=models.ForeignKey( |             field=models.ForeignKey( | ||||||
|                 verbose_name="user database option template", |                 verbose_name='user database option template', | ||||||
|                 to="hostingpackages.UserDatabaseOption", |                 to='hostingpackages.UserDatabaseOption', | ||||||
|                 help_text="The user database option template that this " |                 help_text='The user database option template that this ' | ||||||
|                 "database option is based on", |                           'database option is based on', | ||||||
|                 on_delete=models.CASCADE, |                 on_delete=models.CASCADE), | ||||||
|             ), |  | ||||||
|             preserve_default=True, |             preserve_default=True, | ||||||
|         ), |         ), | ||||||
|     ] |     ] | ||||||
|  |  | ||||||
|  | @ -1,15 +1,18 @@ | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
| from django.db import migrations | from __future__ import unicode_literals | ||||||
|  | 
 | ||||||
|  | from django.db import models, migrations | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|     dependencies = [ |     dependencies = [ | ||||||
|         ("hostingpackages", "0001_squashed_0005_auto_20150118_1303"), |         ('hostingpackages', '0001_squashed_0005_auto_20150118_1303'), | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     operations = [ |     operations = [ | ||||||
|         migrations.AlterModelOptions( |         migrations.AlterModelOptions( | ||||||
|             name="hostingoption", |             name='hostingoption', | ||||||
|             options={}, |             options={}, | ||||||
|         ), |         ), | ||||||
|     ] |     ] | ||||||
|  |  | ||||||
|  | @ -1,21 +1,24 @@ | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
| from django.db import migrations, models | from __future__ import unicode_literals | ||||||
|  | 
 | ||||||
|  | from django.db import models, migrations | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|     dependencies = [ |     dependencies = [ | ||||||
|         ("hostingpackages", "0002_auto_20150118_1149"), |         ('hostingpackages', '0002_auto_20150118_1149'), | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     operations = [ |     operations = [ | ||||||
|         migrations.AlterField( |         migrations.AlterField( | ||||||
|             model_name="customerhostingpackage", |             model_name='customerhostingpackage', | ||||||
|             name="name", |             name='name', | ||||||
|             field=models.CharField(max_length=128, verbose_name="name"), |             field=models.CharField(max_length=128, verbose_name='name'), | ||||||
|             preserve_default=True, |             preserve_default=True, | ||||||
|         ), |         ), | ||||||
|         migrations.AlterUniqueTogether( |         migrations.AlterUniqueTogether( | ||||||
|             name="customerhostingpackage", |             name='customerhostingpackage', | ||||||
|             unique_together=set([("customer", "name")]), |             unique_together=set([('customer', 'name')]), | ||||||
|         ), |         ), | ||||||
|     ] |     ] | ||||||
|  |  | ||||||
|  | @ -1,23 +1,24 @@ | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | 
 | ||||||
| from django.db import migrations, models | from django.db import migrations, models | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
|     dependencies = [ |     dependencies = [ | ||||||
|         ("hostingpackages", "0002_auto_20150118_1319"), |         ('hostingpackages', '0002_auto_20150118_1319'), | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     operations = [ |     operations = [ | ||||||
|         migrations.AlterField( |         migrations.AlterField( | ||||||
|             model_name="customermailboxoption", |             model_name='customermailboxoption', | ||||||
|             name="template", |             name='template', | ||||||
|             field=models.ForeignKey( |             field=models.ForeignKey( | ||||||
|                 verbose_name="mailbox option template", |                 verbose_name='mailbox option template', | ||||||
|                 to="hostingpackages.MailboxOption", |                 to='hostingpackages.MailboxOption', | ||||||
|                 help_text="The mailbox option template that this mailbox " |                 help_text='The mailbox option template that this mailbox ' | ||||||
|                 "option is based on", |                           'option is based on', | ||||||
|                 on_delete=models.CASCADE, |                 on_delete=models.CASCADE), | ||||||
|             ), |  | ||||||
|             preserve_default=True, |             preserve_default=True, | ||||||
|         ), |         ), | ||||||
|     ] |     ] | ||||||
|  |  | ||||||
|  | @ -1,24 +1,22 @@ | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | 
 | ||||||
| from django.db import migrations, models | from django.db import migrations, models | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
|     dependencies = [ |     dependencies = [ | ||||||
|         ("osusers", "0004_auto_20150104_1751"), |         ('osusers', '0004_auto_20150104_1751'), | ||||||
|         ("hostingpackages", "0003_auto_20150118_1221"), |         ('hostingpackages', '0003_auto_20150118_1221'), | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     operations = [ |     operations = [ | ||||||
|         migrations.AddField( |         migrations.AddField( | ||||||
|             model_name="customerhostingpackage", |             model_name='customerhostingpackage', | ||||||
|             name="osuser", |             name='osuser', | ||||||
|             field=models.ForeignKey( |             field=models.ForeignKey( | ||||||
|                 verbose_name="Operating system user", |                 verbose_name='Operating system user', blank=True, | ||||||
|                 blank=True, |                 to='osusers.User', null=True, on_delete=models.CASCADE), | ||||||
|                 to="osusers.User", |  | ||||||
|                 null=True, |  | ||||||
|                 on_delete=models.CASCADE, |  | ||||||
|             ), |  | ||||||
|             preserve_default=True, |             preserve_default=True, | ||||||
|         ), |         ), | ||||||
|     ] |     ] | ||||||
|  |  | ||||||
|  | @ -1,4 +1,6 @@ | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | 
 | ||||||
| import django.utils.timezone | import django.utils.timezone | ||||||
| import model_utils.fields | import model_utils.fields | ||||||
| from django.db import migrations, models | from django.db import migrations, models | ||||||
|  | @ -6,59 +8,33 @@ from django.db import migrations, models | ||||||
| 
 | 
 | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
|     dependencies = [ |     dependencies = [ | ||||||
|         ("domains", "0002_auto_20150124_1909"), |         ('domains', '0002_auto_20150124_1909'), | ||||||
|         ("hostingpackages", "0003_auto_20150118_1407"), |         ('hostingpackages', '0003_auto_20150118_1407'), | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     operations = [ |     operations = [ | ||||||
|         migrations.CreateModel( |         migrations.CreateModel( | ||||||
|             name="CustomerHostingPackageDomain", |             name='CustomerHostingPackageDomain', | ||||||
|             fields=[ |             fields=[ | ||||||
|                 ( |                 ('id', models.AutoField( | ||||||
|                     "id", |                     verbose_name='ID', serialize=False, auto_created=True, | ||||||
|                     models.AutoField( |                     primary_key=True)), | ||||||
|                         verbose_name="ID", |                 ('created', model_utils.fields.AutoCreatedField( | ||||||
|                         serialize=False, |                     default=django.utils.timezone.now, verbose_name='created', | ||||||
|                         auto_created=True, |                     editable=False)), | ||||||
|                         primary_key=True, |                 ('modified', model_utils.fields.AutoLastModifiedField( | ||||||
|                     ), |                     default=django.utils.timezone.now, verbose_name='modified', | ||||||
|                 ), |                     editable=False)), | ||||||
|                 ( |                 ('domain', models.OneToOneField( | ||||||
|                     "created", |                     verbose_name='hosting domain', to='domains.HostingDomain', | ||||||
|                     model_utils.fields.AutoCreatedField( |                     on_delete=models.CASCADE)), | ||||||
|                         default=django.utils.timezone.now, |                 ('hosting_package', models.ForeignKey( | ||||||
|                         verbose_name="created", |                     related_name='domains', verbose_name='hosting package', | ||||||
|                         editable=False, |                     to='hostingpackages.CustomerHostingPackage', | ||||||
|                     ), |                     on_delete=models.CASCADE)), | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "modified", |  | ||||||
|                     model_utils.fields.AutoLastModifiedField( |  | ||||||
|                         default=django.utils.timezone.now, |  | ||||||
|                         verbose_name="modified", |  | ||||||
|                         editable=False, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "domain", |  | ||||||
|                     models.OneToOneField( |  | ||||||
|                         verbose_name="hosting domain", |  | ||||||
|                         to="domains.HostingDomain", |  | ||||||
|                         on_delete=models.CASCADE, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|                 ( |  | ||||||
|                     "hosting_package", |  | ||||||
|                     models.ForeignKey( |  | ||||||
|                         related_name="domains", |  | ||||||
|                         verbose_name="hosting package", |  | ||||||
|                         to="hostingpackages.CustomerHostingPackage", |  | ||||||
|                         on_delete=models.CASCADE, |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             ], |             ], | ||||||
|             options={ |             options={ | ||||||
|                 "abstract": False, |                 'abstract': False, | ||||||
|             }, |             }, | ||||||
|             bases=(models.Model,), |             bases=(models.Model,), | ||||||
|         ), |         ), | ||||||
|  |  | ||||||
|  | @ -1,23 +1,21 @@ | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | 
 | ||||||
| from django.db import migrations, models | from django.db import migrations, models | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
|     dependencies = [ |     dependencies = [ | ||||||
|         ("hostingpackages", "0004_customerhostingpackage_osuser"), |         ('hostingpackages', '0004_customerhostingpackage_osuser'), | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     operations = [ |     operations = [ | ||||||
|         migrations.AlterField( |         migrations.AlterField( | ||||||
|             model_name="customerhostingpackage", |             model_name='customerhostingpackage', | ||||||
|             name="osuser", |             name='osuser', | ||||||
|             field=models.OneToOneField( |             field=models.OneToOneField( | ||||||
|                 null=True, |                 null=True, blank=True, to='osusers.User', | ||||||
|                 blank=True, |                 verbose_name='Operating system user', on_delete=models.CASCADE), | ||||||
|                 to="osusers.User", |  | ||||||
|                 verbose_name="Operating system user", |  | ||||||
|                 on_delete=models.CASCADE, |  | ||||||
|             ), |  | ||||||
|             preserve_default=True, |             preserve_default=True, | ||||||
|         ), |         ), | ||||||
|     ] |     ] | ||||||
|  |  | ||||||
|  | @ -1,19 +1,22 @@ | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
| from django.db import migrations | from __future__ import unicode_literals | ||||||
|  | 
 | ||||||
|  | from django.db import models, migrations | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|     dependencies = [ |     dependencies = [ | ||||||
|         ("hostingpackages", "0004_customerhostingpackagedomain"), |         ('hostingpackages', '0004_customerhostingpackagedomain'), | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     operations = [ |     operations = [ | ||||||
|         migrations.AlterModelOptions( |         migrations.AlterModelOptions( | ||||||
|             name="diskspaceoption", |             name='diskspaceoption', | ||||||
|             options={}, |             options={}, | ||||||
|         ), |         ), | ||||||
|         migrations.AlterUniqueTogether( |         migrations.AlterUniqueTogether( | ||||||
|             name="customerdiskspaceoption", |             name='customerdiskspaceoption', | ||||||
|             unique_together=set([]), |             unique_together=set([]), | ||||||
|         ), |         ), | ||||||
|     ] |     ] | ||||||
|  |  | ||||||
|  | @ -1,19 +1,22 @@ | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
| from django.db import migrations | from __future__ import unicode_literals | ||||||
|  | 
 | ||||||
|  | from django.db import models, migrations | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Migration(migrations.Migration): | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|     dependencies = [ |     dependencies = [ | ||||||
|         ("hostingpackages", "0005_auto_20150125_1508"), |         ('hostingpackages', '0005_auto_20150125_1508'), | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     operations = [ |     operations = [ | ||||||
|         migrations.AlterModelOptions( |         migrations.AlterModelOptions( | ||||||
|             name="userdatabaseoption", |             name='userdatabaseoption', | ||||||
|             options={}, |             options={}, | ||||||
|         ), |         ), | ||||||
|         migrations.AlterUniqueTogether( |         migrations.AlterUniqueTogether( | ||||||
|             name="customeruserdatabaseoption", |             name='customeruserdatabaseoption', | ||||||
|             unique_together=set([]), |             unique_together=set([]), | ||||||
|         ), |         ), | ||||||
|     ] |     ] | ||||||
|  |  | ||||||
|  | @ -2,21 +2,21 @@ | ||||||
| This module contains the hosting package models. | This module contains the hosting package models. | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
| from __future__ import absolute_import | from __future__ import absolute_import, unicode_literals | ||||||
| 
 | 
 | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| from django.core.exceptions import ImproperlyConfigured | from django.db import transaction | ||||||
| from django.db import models, transaction | from django.db import models | ||||||
| from django.urls import reverse | from django.urls import reverse | ||||||
| from django.utils.translation import gettext_lazy as _ | from django.utils.encoding import python_2_unicode_compatible | ||||||
| from django.utils.translation import ngettext | from django.utils.translation import ugettext_lazy as _, ungettext | ||||||
|  | 
 | ||||||
| from model_utils import Choices | from model_utils import Choices | ||||||
| from model_utils.models import TimeStampedModel | from model_utils.models import TimeStampedModel | ||||||
| 
 | 
 | ||||||
| from domains.models import HostingDomain | from domains.models import HostingDomain | ||||||
| from managemails.models import Mailbox | from managemails.models import Mailbox | ||||||
| from osusers.models import AdditionalGroup, Group | from osusers.models import AdditionalGroup, Group, User as OsUser | ||||||
| from osusers.models import User as OsUser |  | ||||||
| from userdbs.models import DB_TYPES, UserDatabase | from userdbs.models import DB_TYPES, UserDatabase | ||||||
| 
 | 
 | ||||||
| DISK_SPACE_UNITS = Choices((0, "M", _("MiB")), (1, "G", _("GiB")), (2, "T", _("TiB"))) | DISK_SPACE_UNITS = Choices((0, "M", _("MiB")), (1, "G", _("GiB")), (2, "T", _("TiB"))) | ||||||
|  | @ -24,6 +24,7 @@ DISK_SPACE_UNITS = Choices((0, "M", _("MiB")), (1, "G", _("GiB")), (2, "T", _("T | ||||||
| DISK_SPACE_FACTORS = ((1, None, None), (1024, 1, None), (1024 * 1024, 1024, 1)) | DISK_SPACE_FACTORS = ((1, None, None), (1024, 1, None), (1024 * 1024, 1024, 1)) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @python_2_unicode_compatible | ||||||
| class HostingPackageBase(TimeStampedModel): | class HostingPackageBase(TimeStampedModel): | ||||||
|     description = models.TextField(_("description"), blank=True) |     description = models.TextField(_("description"), blank=True) | ||||||
|     mailboxcount = models.PositiveIntegerField(_("mailbox count")) |     mailboxcount = models.PositiveIntegerField(_("mailbox count")) | ||||||
|  | @ -56,6 +57,7 @@ class HostingOption(TimeStampedModel): | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @python_2_unicode_compatible | ||||||
| class DiskSpaceOptionBase(models.Model): | class DiskSpaceOptionBase(models.Model): | ||||||
|     diskspace = models.PositiveIntegerField(_("disk space")) |     diskspace = models.PositiveIntegerField(_("disk space")) | ||||||
|     diskspace_unit = models.PositiveSmallIntegerField( |     diskspace_unit = models.PositiveSmallIntegerField( | ||||||
|  | @ -85,6 +87,7 @@ class DiskSpaceOption(DiskSpaceOptionBase, HostingOption): | ||||||
|         unique_together = ["diskspace", "diskspace_unit"] |         unique_together = ["diskspace", "diskspace_unit"] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @python_2_unicode_compatible | ||||||
| class UserDatabaseOptionBase(models.Model): | class UserDatabaseOptionBase(models.Model): | ||||||
|     number = models.PositiveIntegerField(_("number of databases"), default=1) |     number = models.PositiveIntegerField(_("number of databases"), default=1) | ||||||
|     db_type = models.PositiveSmallIntegerField(_("database type"), choices=DB_TYPES) |     db_type = models.PositiveSmallIntegerField(_("database type"), choices=DB_TYPES) | ||||||
|  | @ -96,7 +99,7 @@ class UserDatabaseOptionBase(models.Model): | ||||||
|         verbose_name_plural = _("Database options") |         verbose_name_plural = _("Database options") | ||||||
| 
 | 
 | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
|         return ngettext( |         return ungettext( | ||||||
|             "{type} database", "{count} {type} databases", self.number |             "{type} database", "{count} {type} databases", self.number | ||||||
|         ).format(type=self.get_db_type_display(), count=self.number) |         ).format(type=self.get_db_type_display(), count=self.number) | ||||||
| 
 | 
 | ||||||
|  | @ -112,6 +115,7 @@ class UserDatabaseOption(UserDatabaseOptionBase, HostingOption): | ||||||
|         unique_together = ["number", "db_type"] |         unique_together = ["number", "db_type"] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @python_2_unicode_compatible | ||||||
| class MailboxOptionBase(models.Model): | class MailboxOptionBase(models.Model): | ||||||
|     """ |     """ | ||||||
|     Base class for mailbox options. |     Base class for mailbox options. | ||||||
|  | @ -127,7 +131,7 @@ class MailboxOptionBase(models.Model): | ||||||
|         verbose_name_plural = _("Mailbox options") |         verbose_name_plural = _("Mailbox options") | ||||||
| 
 | 
 | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
|         return ngettext( |         return ungettext( | ||||||
|             "{count} additional mailbox", "{count} additional mailboxes", self.number |             "{count} additional mailbox", "{count} additional mailboxes", self.number | ||||||
|         ).format(count=self.number) |         ).format(count=self.number) | ||||||
| 
 | 
 | ||||||
|  | @ -173,6 +177,7 @@ class CustomerHostingPackageManager(models.Manager): | ||||||
|         return package |         return package | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @python_2_unicode_compatible | ||||||
| class CustomerHostingPackage(HostingPackageBase): | class CustomerHostingPackage(HostingPackageBase): | ||||||
|     """ |     """ | ||||||
|     This class defines customer specific hosting packages. |     This class defines customer specific hosting packages. | ||||||
|  | @ -264,7 +269,7 @@ class CustomerHostingPackage(HostingPackageBase): | ||||||
|                 ) + option.diskspace |                 ) + option.diskspace | ||||||
|                 min_unit = option.diskspace_unit |                 min_unit = option.diskspace_unit | ||||||
|         if unit is None: |         if unit is None: | ||||||
|             return DISK_SPACE_FACTORS[min_unit][0] * diskspace * 1024**2 |             return DISK_SPACE_FACTORS[min_unit][0] * diskspace * 1024 ** 2 | ||||||
|         if unit > min_unit: |         if unit > min_unit: | ||||||
|             return DISK_SPACE_FACTORS[unit][min_unit] * diskspace |             return DISK_SPACE_FACTORS[unit][min_unit] * diskspace | ||||||
|         return DISK_SPACE_FACTORS[min_unit][unit] * diskspace |         return DISK_SPACE_FACTORS[min_unit][unit] * diskspace | ||||||
|  | @ -282,7 +287,7 @@ class CustomerHostingPackage(HostingPackageBase): | ||||||
|         """ |         """ | ||||||
|         if unit is None: |         if unit is None: | ||||||
|             return ( |             return ( | ||||||
|                 DISK_SPACE_FACTORS[self.diskspace_unit][0] * self.diskspace * 1024**2 |                 DISK_SPACE_FACTORS[self.diskspace_unit][0] * self.diskspace * 1024 ** 2 | ||||||
|             ) |             ) | ||||||
|         if unit > self.diskspace_unit: |         if unit > self.diskspace_unit: | ||||||
|             return DISK_SPACE_FACTORS[unit][self.diskspace_unit] * self.diskspace |             return DISK_SPACE_FACTORS[unit][self.diskspace_unit] * self.diskspace | ||||||
|  | @ -371,17 +376,13 @@ class CustomerHostingPackage(HostingPackageBase): | ||||||
|             self.copy_template_attributes() |             self.copy_template_attributes() | ||||||
|             self.osuser = OsUser.objects.create_user(self.customer) |             self.osuser = OsUser.objects.create_user(self.customer) | ||||||
|             for group in settings.OSUSER_DEFAULT_GROUPS: |             for group in settings.OSUSER_DEFAULT_GROUPS: | ||||||
|                 try: |                 AdditionalGroup.objects.create( | ||||||
|                     AdditionalGroup.objects.create( |                     user=self.osuser, group=Group.objects.get(groupname=group) | ||||||
|                         user=self.osuser, group=Group.objects.get(groupname=group) |                 ) | ||||||
|                     ) |  | ||||||
|                 except Group.DoesNotExist as e: |  | ||||||
|                     raise ImproperlyConfigured( |  | ||||||
|                         f"group {group} has not been defined" |  | ||||||
|                     ) from e |  | ||||||
|         return super(CustomerHostingPackage, self).save(*args, **kwargs) |         return super(CustomerHostingPackage, self).save(*args, **kwargs) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @python_2_unicode_compatible | ||||||
| class CustomerHostingPackageDomain(TimeStampedModel): | class CustomerHostingPackageDomain(TimeStampedModel): | ||||||
|     """ |     """ | ||||||
|     This class defines the relationship from a hosting package to a hosting |     This class defines the relationship from a hosting package to a hosting | ||||||
|  |  | ||||||
|  | @ -1,13 +0,0 @@ | ||||||
| {% 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 %} |  | ||||||
|  | @ -1,53 +0,0 @@ | ||||||
| {% 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 %} |  | ||||||
|  | @ -1,12 +0,0 @@ | ||||||
| {% 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 %} |  | ||||||
|  | @ -1,280 +0,0 @@ | ||||||
| {% 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 %} |  | ||||||
|  | @ -1,29 +0,0 @@ | ||||||
| {% 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,17 +2,10 @@ | ||||||
| Test for models. | Test for models. | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
| from django.contrib.auth import get_user_model |  | ||||||
| from django.core.exceptions import ImproperlyConfigured |  | ||||||
| from django.test import TestCase, override_settings |  | ||||||
| 
 | 
 | ||||||
| from hostingpackages.models import ( | from django.test import TestCase | ||||||
|     DISK_SPACE_UNITS, |  | ||||||
|     CustomerHostingPackage, |  | ||||||
|     HostingPackageTemplate, |  | ||||||
| ) |  | ||||||
| 
 | 
 | ||||||
| User = get_user_model() | from hostingpackages.models import DISK_SPACE_UNITS, CustomerHostingPackage | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class CustomerHostingPackageTest(TestCase): | class CustomerHostingPackageTest(TestCase): | ||||||
|  | @ -20,7 +13,7 @@ class CustomerHostingPackageTest(TestCase): | ||||||
|         package = CustomerHostingPackage( |         package = CustomerHostingPackage( | ||||||
|             diskspace=10, diskspace_unit=DISK_SPACE_UNITS.G |             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): |     def test_get_disk_space_mib(self): | ||||||
|         package = CustomerHostingPackage( |         package = CustomerHostingPackage( | ||||||
|  | @ -33,20 +26,3 @@ class CustomerHostingPackageTest(TestCase): | ||||||
|             diskspace=256, diskspace_unit=DISK_SPACE_UNITS.M |             diskspace=256, diskspace_unit=DISK_SPACE_UNITS.M | ||||||
|         ) |         ) | ||||||
|         self.assertEqual(package.get_quota(), (262144, 275251)) |         self.assertEqual(package.get_quota(), (262144, 275251)) | ||||||
| 
 |  | ||||||
|     @override_settings(OSUSER_DEFAULT_GROUPS=["testgroup"]) |  | ||||||
|     def test_additional_group_not_defined(self): |  | ||||||
|         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. | This module defines the URL patterns for hosting package related views. | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
| from __future__ import absolute_import | from __future__ import absolute_import, unicode_literals | ||||||
| 
 | 
 | ||||||
| from django.urls import re_path | from django.conf.urls import url | ||||||
| 
 | 
 | ||||||
| from .views import ( | from .views import ( | ||||||
|     AddHostingOption, |     AddHostingOption, | ||||||
|  | @ -12,34 +12,26 @@ from .views import ( | ||||||
|     CreateCustomerHostingPackage, |     CreateCustomerHostingPackage, | ||||||
|     CreateHostingPackage, |     CreateHostingPackage, | ||||||
|     CustomerHostingPackageDetails, |     CustomerHostingPackageDetails, | ||||||
|  |     CustomerHostingPackageList, | ||||||
|     HostingOptionChoices, |     HostingOptionChoices, | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
|     re_path(r"^create$", CreateHostingPackage.as_view(), name="create_hosting_package"), |     url(r'^create$', CreateHostingPackage.as_view(), | ||||||
|     re_path( |         name='create_hosting_package'), | ||||||
|         r"^allpackages/", |     url(r'^allpackages/', | ||||||
|         AllCustomerHostingPackageList.as_view(), |         AllCustomerHostingPackageList.as_view(), name='all_hosting_packages'), | ||||||
|         name="all_hosting_packages", |     url(r'^(?P<user>[-\w0-9@.+_]+)/$', | ||||||
|     ), |         CustomerHostingPackageList.as_view(), name='hosting_packages'), | ||||||
|     re_path( |     url(r'^(?P<user>[-\w0-9@.+_]+)/create$', | ||||||
|         r"^(?P<user>[-\w0-9@.+_]+)/create$", |  | ||||||
|         CreateCustomerHostingPackage.as_view(), |         CreateCustomerHostingPackage.as_view(), | ||||||
|         name="create_customer_hosting_package", |         name='create_customer_hosting_package'), | ||||||
|     ), |     url(r'^(?P<user>[-\w0-9@.+_]+)/(?P<pk>\d+)/$', | ||||||
|     re_path( |  | ||||||
|         r"^(?P<user>[-\w0-9@.+_]+)/(?P<pk>\d+)/$", |  | ||||||
|         CustomerHostingPackageDetails.as_view(), |         CustomerHostingPackageDetails.as_view(), | ||||||
|         name="hosting_package_details", |         name='hosting_package_details'), | ||||||
|     ), |     url(r'^(?P<pk>\d+)/option-choices$', | ||||||
|     re_path( |         HostingOptionChoices.as_view(), name='hosting_option_choices'), | ||||||
|         r"^(?P<pk>\d+)/option-choices$", |     url(r'^(?P<package>\d+)/add-option/(?P<type>\w+)/(?P<optionid>\d+)$', | ||||||
|         HostingOptionChoices.as_view(), |         AddHostingOption.as_view(), name='add_hosting_option'), | ||||||
|         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,17 +2,28 @@ | ||||||
| This module defines views related to hosting packages. | This module defines views related to hosting packages. | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
| from __future__ import absolute_import | from __future__ import absolute_import, unicode_literals | ||||||
| 
 | 
 | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
|  | from django.http import Http404 | ||||||
|  | from django.shortcuts import redirect, get_object_or_404 | ||||||
|  | from django.utils.translation import ugettext as _ | ||||||
|  | from django.views.generic import ( | ||||||
|  |     DetailView, | ||||||
|  |     ListView, | ||||||
|  | ) | ||||||
|  | from django.views.generic.edit import ( | ||||||
|  |     CreateView, | ||||||
|  |     FormView, | ||||||
|  | ) | ||||||
| from django.contrib import messages | from django.contrib import messages | ||||||
| from django.contrib.auth import get_user_model | from django.contrib.auth import get_user_model | ||||||
| from django.contrib.auth.mixins import PermissionRequiredMixin, UserPassesTestMixin | 
 | ||||||
| from django.http import Http404 | from braces.views import ( | ||||||
| from django.shortcuts import get_object_or_404, redirect |     LoginRequiredMixin, | ||||||
| from django.utils.translation import gettext as _ |     StaffuserRequiredMixin, | ||||||
| from django.views.generic import DetailView, ListView | ) | ||||||
| from django.views.generic.edit import CreateView, FormView | 
 | ||||||
| from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin | from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin | ||||||
| 
 | 
 | ||||||
| from .forms import ( | from .forms import ( | ||||||
|  | @ -30,27 +41,26 @@ from .models import ( | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class CreateHostingPackage(PermissionRequiredMixin, CreateView): | class CreateHostingPackage( | ||||||
|  |     LoginRequiredMixin, StaffuserRequiredMixin, CreateView | ||||||
|  | ): | ||||||
|     """ |     """ | ||||||
|     Create a hosting package. |     Create a hosting package. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |  | ||||||
|     model = CustomerHostingPackage |     model = CustomerHostingPackage | ||||||
|     raise_exception = True |     raise_exception = True | ||||||
|     permission_required = "domains.add_customerhostingpackage" |     template_name_suffix = '_create' | ||||||
|     template_name_suffix = "_create" |  | ||||||
|     form_class = CreateHostingPackageForm |     form_class = CreateHostingPackageForm | ||||||
| 
 | 
 | ||||||
|     def form_valid(self, form): |     def form_valid(self, form): | ||||||
|         hosting_package = form.save() |         hostingpackage = form.save() | ||||||
|         messages.success( |         messages.success( | ||||||
|             self.request, |             self.request, | ||||||
|             _("Started setup of new hosting package {name}.").format( |             _('Started setup of new hosting package {name}.').format( | ||||||
|                 name=hosting_package.name |                 name=hostingpackage.name) | ||||||
|             ), |  | ||||||
|         ) |         ) | ||||||
|         return redirect(hosting_package) |         return redirect(hostingpackage) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class CreateCustomerHostingPackage(CreateHostingPackage): | class CreateCustomerHostingPackage(CreateHostingPackage): | ||||||
|  | @ -58,7 +68,6 @@ class CreateCustomerHostingPackage(CreateHostingPackage): | ||||||
|     Create a hosting package for a selected customer. |     Create a hosting package for a selected customer. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |  | ||||||
|     form_class = CreateCustomerHostingPackageForm |     form_class = CreateCustomerHostingPackageForm | ||||||
| 
 | 
 | ||||||
|     def get_form_kwargs(self): |     def get_form_kwargs(self): | ||||||
|  | @ -67,24 +76,25 @@ class CreateCustomerHostingPackage(CreateHostingPackage): | ||||||
|         return kwargs |         return kwargs | ||||||
| 
 | 
 | ||||||
|     def get_customer_object(self): |     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): |     def get_context_data(self, **kwargs): | ||||||
|         context = super(CreateCustomerHostingPackage, self).get_context_data(**kwargs) |         context = super( | ||||||
|         context["customer"] = self.get_customer_object() |             CreateCustomerHostingPackage, self).get_context_data(**kwargs) | ||||||
|  |         context['customer'] = self.get_customer_object() | ||||||
|         return context |         return context | ||||||
| 
 | 
 | ||||||
|     def form_valid(self, form): |     def form_valid(self, form): | ||||||
|         hosting_package = form.save(commit=False) |         hostingpackage = form.save(commit=False) | ||||||
|         hosting_package.customer = self.get_customer_object() |         hostingpackage.customer = self.get_customer_object() | ||||||
|         hosting_package.save() |         hostingpackage.save() | ||||||
|         messages.success( |         messages.success( | ||||||
|             self.request, |             self.request, | ||||||
|             _("Started setup of new hosting package {name}.").format( |             _('Started setup of new hosting package {name}.').format( | ||||||
|                 name=hosting_package.name |                 name=hostingpackage.name) | ||||||
|             ), |  | ||||||
|         ) |         ) | ||||||
|         return redirect(hosting_package) |         return redirect(hostingpackage) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class CustomerHostingPackageDetails(StaffOrSelfLoginRequiredMixin, DetailView): | class CustomerHostingPackageDetails(StaffOrSelfLoginRequiredMixin, DetailView): | ||||||
|  | @ -92,161 +102,156 @@ class CustomerHostingPackageDetails(StaffOrSelfLoginRequiredMixin, DetailView): | ||||||
|     This view is for showing details of a customer hosting package. |     This view is for showing details of a customer hosting package. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |  | ||||||
|     model = CustomerHostingPackage |     model = CustomerHostingPackage | ||||||
|     context_object_name = "hostingpackage" |     context_object_name = 'hostingpackage' | ||||||
|     customer = None |     customer = None | ||||||
| 
 | 
 | ||||||
|     def get_customer_object(self): |     def get_customer_object(self): | ||||||
|         if self.customer is None: |         if self.customer is None: | ||||||
|             self.customer = get_object_or_404( |             self.customer = get_object_or_404( | ||||||
|                 get_user_model(), username=self.kwargs["user"] |                 get_user_model(), username=self.kwargs['user']) | ||||||
|             ) |  | ||||||
|         return self.customer |         return self.customer | ||||||
| 
 | 
 | ||||||
|     def get_context_data(self, **kwargs): |     def get_context_data(self, **kwargs): | ||||||
|         context = super(CustomerHostingPackageDetails, self).get_context_data(**kwargs) |         context = super(CustomerHostingPackageDetails, self).get_context_data( | ||||||
|         context.update( |             **kwargs) | ||||||
|             { |         context.update({ | ||||||
|                 "customer": self.get_customer_object(), |             'customer': self.get_customer_object(), | ||||||
|                 "uploadserver": settings.OSUSER_UPLOAD_SERVER, |             'uploadserver': settings.OSUSER_UPLOAD_SERVER, | ||||||
|                 "databases": context["hostingpackage"].databases, |             'databases': context['hostingpackage'].databases, | ||||||
|                 "osuser": context["hostingpackage"].osuser, |             'osuser': context['hostingpackage'].osuser, | ||||||
|                 "hostingoptions": context["hostingpackage"].get_hostingoptions(), |             'hostingoptions': | ||||||
|                 "domains": context["hostingpackage"].domains.all(), |                 context['hostingpackage'].get_hostingoptions(), | ||||||
|                 "mailboxes": context["hostingpackage"].mailboxes, |             'domains': context['hostingpackage'].domains.all(), | ||||||
|             } |             'mailboxes': context['hostingpackage'].mailboxes, | ||||||
|         ) |         }) | ||||||
|         context["sshkeys"] = context["osuser"].sshpublickey_set.all() |         context['sshkeys'] = context['osuser'].sshpublickey_set.all() | ||||||
|         return context |         return context | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class StaffUserRequiredMixin(UserPassesTestMixin): | class AllCustomerHostingPackageList( | ||||||
|     """ |     LoginRequiredMixin, StaffuserRequiredMixin, ListView | ||||||
|     Mixin to make views available to staff members only. | ): | ||||||
| 
 |  | ||||||
|     """ |  | ||||||
| 
 |  | ||||||
|     def test_func(self): |  | ||||||
|         return self.request.user.is_staff |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class AllCustomerHostingPackageList(StaffUserRequiredMixin, ListView): |  | ||||||
|     """ |     """ | ||||||
|     This view is used for showing a list of all hosting packages. |     This view is used for showing a list of all hosting packages. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |  | ||||||
|     model = CustomerHostingPackage |     model = CustomerHostingPackage | ||||||
|     template_name_suffix = "_admin_list" |     template_name_suffix = '_admin_list' | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class CustomerHostingPackageList(StaffOrSelfLoginRequiredMixin, ListView): | ||||||
|  |     """ | ||||||
|  |     This view is used for showing a list of a customer's hosting packages. | ||||||
|  | 
 | ||||||
|  |     """ | ||||||
|  |     model = CustomerHostingPackage | ||||||
|  |     customer = None | ||||||
|  | 
 | ||||||
|  |     def get_customer_object(self): | ||||||
|  |         if self.customer is None: | ||||||
|  |             self.customer = get_object_or_404( | ||||||
|  |                 get_user_model(), username=self.kwargs['user']) | ||||||
|  |         return self.customer | ||||||
|  | 
 | ||||||
|  |     def get_context_data(self, **kwargs): | ||||||
|  |         context = super(CustomerHostingPackageList, self).get_context_data( | ||||||
|  |             **kwargs) | ||||||
|  |         context['customer'] = self.get_customer_object() | ||||||
|  |         return context | ||||||
| 
 | 
 | ||||||
|     def get_queryset(self): |     def get_queryset(self): | ||||||
|         return ( |         return super(CustomerHostingPackageList, self).get_queryset().filter( | ||||||
|             super() |             customer__username=self.kwargs['user']) | ||||||
|             .get_queryset() |  | ||||||
|             .select_related("osuser", "customer") |  | ||||||
|             .only("name", "pk", "created", "customer__username", "osuser__username") |  | ||||||
|         ) |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class HostingOptionChoices(StaffUserRequiredMixin, DetailView): | class HostingOptionChoices( | ||||||
|  |     LoginRequiredMixin, StaffuserRequiredMixin, DetailView | ||||||
|  | ): | ||||||
|     """ |     """ | ||||||
|     This view displays choices of hosting options for a customer hosting |     This view displays choices of hosting options for a customer hosting | ||||||
|     package. |     package. | ||||||
| 
 | 
 | ||||||
|     """ |     """ | ||||||
| 
 |  | ||||||
|     model = CustomerHostingPackage |     model = CustomerHostingPackage | ||||||
|     context_object_name = "hostingpackage" |     context_object_name = 'hostingpackage' | ||||||
|     template_name_suffix = "_option_choices" |     template_name_suffix = '_option_choices' | ||||||
| 
 | 
 | ||||||
|     def get_context_data(self, **kwargs): |     def get_context_data(self, **kwargs): | ||||||
|         context = super(HostingOptionChoices, self).get_context_data(**kwargs) |         context = super(HostingOptionChoices, self).get_context_data( | ||||||
|         context.update( |             **kwargs) | ||||||
|             { |         context.update({ | ||||||
|                 "customer": self.get_object().customer, |             'customer': self.get_object().customer, | ||||||
|                 "hosting_options": ( |             'hosting_options': ( | ||||||
|                     ( |                 (_('Disk space'), | ||||||
|                         _("Disk space"), |                  [(option, 'diskspace') for option in | ||||||
|                         [ |                   DiskSpaceOption.objects.all()]), | ||||||
|                             (option, "diskspace") |                 (_('Mailboxes'), | ||||||
|                             for option in DiskSpaceOption.objects.all() |                  [(option, 'mailboxes') for option in | ||||||
|                         ], |                   MailboxOption.objects.all()]), | ||||||
|                     ), |                 (_('Databases'), | ||||||
|                     ( |                  [(option, 'databases') for option in | ||||||
|                         _("Mailboxes"), |                   UserDatabaseOption.objects.all()]), | ||||||
|                         [ |             ), | ||||||
|                             (option, "mailboxes") |         }) | ||||||
|                             for option in MailboxOption.objects.all() |  | ||||||
|                         ], |  | ||||||
|                     ), |  | ||||||
|                     ( |  | ||||||
|                         _("Databases"), |  | ||||||
|                         [ |  | ||||||
|                             (option, "databases") |  | ||||||
|                             for option in UserDatabaseOption.objects.all() |  | ||||||
|                         ], |  | ||||||
|                     ), |  | ||||||
|                 ), |  | ||||||
|             } |  | ||||||
|         ) |  | ||||||
|         return context |         return context | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class AddHostingOption(StaffUserRequiredMixin, FormView): | class AddHostingOption( | ||||||
|     template_name = "hostingpackages/add_hosting_option.html" |     LoginRequiredMixin, StaffuserRequiredMixin, FormView | ||||||
|  | ): | ||||||
|  |     template_name = 'hostingpackages/add_hosting_option.html' | ||||||
| 
 | 
 | ||||||
|     def get_form_class(self): |     def get_form_class(self): | ||||||
|         optiontype = self.kwargs["type"] |         optiontype = self.kwargs['type'] | ||||||
|         if optiontype == "diskspace": |         if optiontype == 'diskspace': | ||||||
|             return AddDiskspaceOptionForm |             return AddDiskspaceOptionForm | ||||||
|         elif optiontype == "mailboxes": |         elif optiontype == 'mailboxes': | ||||||
|             return AddMailboxOptionForm |             return AddMailboxOptionForm | ||||||
|         elif optiontype == "databases": |         elif optiontype == 'databases': | ||||||
|             return AddUserDatabaseOptionForm |             return AddUserDatabaseOptionForm | ||||||
|         raise Http404() |         raise Http404() | ||||||
| 
 | 
 | ||||||
|     def get_hosting_package(self): |     def get_hosting_package(self): | ||||||
|         return get_object_or_404(CustomerHostingPackage, pk=int(self.kwargs["package"])) |         return get_object_or_404( | ||||||
|  |             CustomerHostingPackage, pk=int(self.kwargs['package'])) | ||||||
| 
 | 
 | ||||||
|     def get_option_template(self): |     def get_option_template(self): | ||||||
|         optiontype = self.kwargs["type"] |         optiontype = self.kwargs['type'] | ||||||
|         optionid = int(self.kwargs["optionid"]) |         optionid = int(self.kwargs['optionid']) | ||||||
|         if optiontype == "diskspace": |         if optiontype == 'diskspace': | ||||||
|             return get_object_or_404(DiskSpaceOption, pk=optionid) |             return get_object_or_404(DiskSpaceOption, pk=optionid) | ||||||
|         elif optiontype == "mailboxes": |         elif optiontype == 'mailboxes': | ||||||
|             return get_object_or_404(MailboxOption, pk=optionid) |             return get_object_or_404(MailboxOption, pk=optionid) | ||||||
|         elif optiontype == "databases": |         elif optiontype == 'databases': | ||||||
|             return get_object_or_404(UserDatabaseOption, pk=optionid) |             return get_object_or_404(UserDatabaseOption, pk=optionid) | ||||||
|         raise Http404() |         raise Http404() | ||||||
| 
 | 
 | ||||||
|     def get_form_kwargs(self): |     def get_form_kwargs(self): | ||||||
|         kwargs = super(AddHostingOption, self).get_form_kwargs() |         kwargs = super(AddHostingOption, self).get_form_kwargs() | ||||||
|         kwargs["hostingpackage"] = self.get_hosting_package() |         kwargs['hostingpackage'] = self.get_hosting_package() | ||||||
|         kwargs["option_template"] = self.get_option_template() |         kwargs['option_template'] = self.get_option_template() | ||||||
|         return kwargs |         return kwargs | ||||||
| 
 | 
 | ||||||
|     def get_initial(self): |     def get_initial(self): | ||||||
|         initial = super(AddHostingOption, self).get_initial() |         initial = super(AddHostingOption, self).get_initial() | ||||||
|         template = self.get_option_template() |         template = self.get_option_template() | ||||||
|         if type(template) == DiskSpaceOption: |         if type(template) == DiskSpaceOption: | ||||||
|             initial.update( |             initial.update({ | ||||||
|                 { |                 'diskspace': template.diskspace, | ||||||
|                     "diskspace": template.diskspace, |                 'diskspace_unit': template.diskspace_unit, | ||||||
|                     "diskspace_unit": template.diskspace_unit, |             }) | ||||||
|                 } |  | ||||||
|             ) |  | ||||||
|         elif type(template) == MailboxOption: |         elif type(template) == MailboxOption: | ||||||
|             initial["number"] = template.number |             initial['number'] = template.number | ||||||
|         elif type(template) == UserDatabaseOption: |         elif type(template) == UserDatabaseOption: | ||||||
|             initial["number"] = template.number |             initial['number'] = template.number | ||||||
|         else: |         else: | ||||||
|             raise Http404() |             raise Http404() | ||||||
|         return initial |         return initial | ||||||
| 
 | 
 | ||||||
|     def get_context_data(self, **kwargs): |     def get_context_data(self, **kwargs): | ||||||
|         context = super(AddHostingOption, self).get_context_data(**kwargs) |         context = super(AddHostingOption, self).get_context_data(**kwargs) | ||||||
|         context["option_template"] = self.get_option_template() |         context['option_template'] = self.get_option_template() | ||||||
|         return context |         return context | ||||||
| 
 | 
 | ||||||
|     def form_valid(self, form): |     def form_valid(self, form): | ||||||
|  | @ -254,8 +259,8 @@ class AddHostingOption(StaffUserRequiredMixin, FormView): | ||||||
|         hosting_package = self.get_hosting_package() |         hosting_package = self.get_hosting_package() | ||||||
|         messages.success( |         messages.success( | ||||||
|             self.request, |             self.request, | ||||||
|             _( |             _("Successfully added option {option} to hosting package " | ||||||
|                 "Successfully added option {option} to hosting package " "{package}." |               "{package}.").format( | ||||||
|             ).format(option=option, package=hosting_package.name), |                   option=option, package=hosting_package.name) | ||||||
|         ) |         ) | ||||||
|         return redirect(hosting_package) |         return redirect(hosting_package) | ||||||
|  |  | ||||||
|  | @ -1,14 +0,0 @@ | ||||||
| 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) |  | ||||||
|  | @ -1,8 +0,0 @@ | ||||||
| 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") |  | ||||||
|  | @ -1,69 +0,0 @@ | ||||||
| # 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}" |  | ||||||
|  | @ -1,38 +0,0 @@ | ||||||
| # 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'], |  | ||||||
|             }, |  | ||||||
|         ), |  | ||||||
|     ] |  | ||||||
|  | @ -1,59 +0,0 @@ | ||||||
| # 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'), |  | ||||||
|         ), |  | ||||||
|     ] |  | ||||||
|  | @ -1,56 +0,0 @@ | ||||||
| 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) |  | ||||||
|  | @ -1,33 +0,0 @@ | ||||||
| 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", |  | ||||||
|             }, |  | ||||||
|         } |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| from django.test import TestCase |  | ||||||
| 
 |  | ||||||
| # Create your tests here. |  | ||||||
|  | @ -1,28 +0,0 @@ | ||||||
| from django.contrib.auth import get_user_model |  | ||||||
| from rest_framework import generics |  | ||||||
| 
 |  | ||||||
| from invoices.models import Invoice |  | ||||||
| from invoices.serializers import InvoiceSerializer |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Create your views here. |  | ||||||
| class ListInvoiceAPIView(generics.ListCreateAPIView): |  | ||||||
|     """ |  | ||||||
|     API endpoint that allows invoice to be viewed or edited. |  | ||||||
| 
 |  | ||||||
|     """ |  | ||||||
| 
 |  | ||||||
|     queryset = Invoice.objects.all().order_by("-invoice_date", "customer__username") |  | ||||||
|     serializer_class = InvoiceSerializer |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class InvoiceAPIView(generics.RetrieveUpdateAPIView): |  | ||||||
|     """ |  | ||||||
|     API endpoint for retrieving and updating invoices. |  | ||||||
| 
 |  | ||||||
|     """ |  | ||||||
| 
 |  | ||||||
|     queryset = Invoice.objects.all() |  | ||||||
|     serializer_class = InvoiceSerializer |  | ||||||
|     lookup_field = "invoice_number" |  | ||||||
|     lookup_url_kwarg = "invoice_number" |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -2,3 +2,4 @@ | ||||||
| This app takes care of mailboxes and mail addresses. | This app takes care of mailboxes and mail addresses. | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
|  | default_app_config = 'managemails.apps.ManageMailsAppConfig' | ||||||
|  |  | ||||||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue