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